From 62861e471377fe32d226c71d2a6bfd8ae2168749 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Mon, 2 Jan 2017 20:15:30 +0100 Subject: [PATCH] Initial commit --- .gitignore | 4 + Cargo.toml | 2 + README.md | 2 + couchbase-sys/Cargo.toml | 10 + couchbase-sys/build.rs | 16 + couchbase-sys/libcouchbase-2.7.0/.gitignore | 132 + couchbase-sys/libcouchbase-2.7.0/.travis.yml | 19 + .../libcouchbase-2.7.0/CMakeLists.txt | 429 ++ .../libcouchbase-2.7.0/CONTRIBUTING.md | 124 + couchbase-sys/libcouchbase-2.7.0/LICENSE | 202 + .../libcouchbase-2.7.0/README.markdown | 163 + .../libcouchbase-2.7.0/RELEASE_NOTES.markdown | 2711 +++++++++ .../cmake/Modules/ConfigureDtrace.cmake | 27 + .../cmake/Modules/CopyPDB.cmake | 42 + .../cmake/Modules/DistScript.cmake | 17 + .../cmake/Modules/DownloadLcbDep.cmake | 20 + .../Modules/FindCouchbaseHdrHistogram.cmake | 15 + .../cmake/Modules/FindCouchbaseLibev.cmake | 73 + .../cmake/Modules/FindCouchbaseLibevent.cmake | 52 + .../cmake/Modules/FindCouchbaseLibuv.cmake | 56 + .../cmake/Modules/FindCouchbaseSnappy.cmake | 11 + .../cmake/Modules/GenerateConfigDotH.cmake | 29 + .../cmake/Modules/GetLibcouchbaseFlags.cmake | 134 + .../cmake/Modules/GetPlatformCCInfo.cmake | 45 + .../cmake/Modules/GetVersionInfo.cmake | 70 + .../cmake/config-cmake.h.in | 60 + .../libcouchbase-2.7.0/cmake/configure | 357 ++ .../libcouchbase-2.7.0/cmake/defs.mk.in | 8 + .../cmake/dtrace-instr-link.pl | 38 + .../cmake/source_files.cmake | 78 + couchbase-sys/libcouchbase-2.7.0/configure.pl | 1 + .../libcouchbase-2.7.0/contrib/cJSON/cJSON.c | 624 ++ .../libcouchbase-2.7.0/contrib/cJSON/cJSON.h | 158 + .../contrib/cbsasl/CMakeLists.txt | 9 + .../libcouchbase-2.7.0/contrib/cbsasl/COPYING | 202 + .../contrib/cbsasl/include/cbsasl/cbsasl.h | 217 + .../contrib/cbsasl/src/client.c | 205 + .../contrib/cbsasl/src/common.c | 46 + .../contrib/cbsasl/src/cram-md5/hmac.c | 67 + .../contrib/cbsasl/src/cram-md5/hmac.h | 33 + .../contrib/cbsasl/src/cram-md5/md5.c | 296 + .../contrib/cbsasl/src/cram-md5/md5.h | 45 + .../contrib/cbsasl/src/hash.c | 573 ++ .../contrib/cbsasl/src/hash.h | 15 + .../contrib/cbsasl/src/util.h | 31 + .../contrib/cliopts/CMakeLists.txt | 2 + .../contrib/cliopts/cliopts.c | 778 +++ .../contrib/cliopts/cliopts.h | 501 ++ .../contrib/genhash/genhash.c | 372 ++ .../contrib/genhash/genhash.h | 241 + .../contrib/gtest-1.7.0/CHANGES | 157 + .../contrib/gtest-1.7.0/CMakeLists.txt | 252 + .../contrib/gtest-1.7.0/CONTRIBUTORS | 37 + .../contrib/gtest-1.7.0/LICENSE | 28 + .../contrib/gtest-1.7.0/MINIFY.sh | 15 + .../contrib/gtest-1.7.0/README | 435 ++ .../gtest-1.7.0/cmake/internal_utils.cmake | 227 + .../include/gtest/gtest-death-test.h | 294 + .../gtest-1.7.0/include/gtest/gtest-message.h | 250 + .../include/gtest/gtest-param-test.h | 1421 +++++ .../include/gtest/gtest-param-test.h.pump | 487 ++ .../include/gtest/gtest-printers.h | 855 +++ .../gtest-1.7.0/include/gtest/gtest-spi.h | 232 + .../include/gtest/gtest-test-part.h | 179 + .../include/gtest/gtest-typed-test.h | 259 + .../contrib/gtest-1.7.0/include/gtest/gtest.h | 2291 ++++++++ .../include/gtest/gtest_pred_impl.h | 358 ++ .../gtest-1.7.0/include/gtest/gtest_prod.h | 58 + .../internal/gtest-death-test-internal.h | 319 + .../include/gtest/internal/gtest-filepath.h | 206 + .../include/gtest/internal/gtest-internal.h | 1158 ++++ .../include/gtest/internal/gtest-linked_ptr.h | 233 + .../internal/gtest-param-util-generated.h | 5143 +++++++++++++++++ .../gtest-param-util-generated.h.pump | 301 + .../include/gtest/internal/gtest-param-util.h | 619 ++ .../include/gtest/internal/gtest-port.h | 1947 +++++++ .../include/gtest/internal/gtest-string.h | 167 + .../include/gtest/internal/gtest-tuple.h | 1012 ++++ .../include/gtest/internal/gtest-tuple.h.pump | 339 ++ .../include/gtest/internal/gtest-type-util.h | 3331 +++++++++++ .../gtest/internal/gtest-type-util.h.pump | 297 + .../contrib/gtest-1.7.0/src/gtest-all.cc | 48 + .../gtest-1.7.0/src/gtest-death-test.cc | 1344 +++++ .../contrib/gtest-1.7.0/src/gtest-filepath.cc | 382 ++ .../gtest-1.7.0/src/gtest-internal-inl.h | 1218 ++++ .../contrib/gtest-1.7.0/src/gtest-port.cc | 805 +++ .../contrib/gtest-1.7.0/src/gtest-printers.cc | 363 ++ .../gtest-1.7.0/src/gtest-test-part.cc | 110 + .../gtest-1.7.0/src/gtest-typed-test.cc | 110 + .../contrib/gtest-1.7.0/src/gtest.cc | 5015 ++++++++++++++++ .../contrib/gtest-1.7.0/src/gtest_main.cc | 38 + .../contrib/http_parser/LICENSE-MIT | 23 + .../contrib/http_parser/README.md | 178 + .../contrib/http_parser/http_parser.c | 2060 +++++++ .../contrib/http_parser/http_parser.h | 321 + .../libcouchbase-2.7.0/contrib/jsonsl/LICENSE | 20 + .../contrib/jsonsl/jsonsl.c | 1452 +++++ .../contrib/jsonsl/jsonsl.h | 971 ++++ .../contrib/lcb-jsoncpp/CMakeLists.txt | 6 + .../contrib/lcb-jsoncpp/LICENSE | 55 + .../lcb-jsoncpp/lcb-jsoncpp-forwards.h | 255 + .../contrib/lcb-jsoncpp/lcb-jsoncpp.cpp | 4892 ++++++++++++++++ .../contrib/lcb-jsoncpp/lcb-jsoncpp.h | 1961 +++++++ .../contrib/snappy/CMakeLists.txt | 8 + .../libcouchbase-2.7.0/contrib/snappy/COPYING | 28 + .../contrib/snappy/snappy-c.cc | 90 + .../contrib/snappy/snappy-c.h | 138 + .../contrib/snappy/snappy-internal.h | 150 + .../contrib/snappy/snappy-lcb-msvc.h | 5 + .../contrib/snappy/snappy-sinksource.cc | 71 + .../contrib/snappy/snappy-sinksource.h | 137 + .../contrib/snappy/snappy-stubs-internal.cc | 42 + .../contrib/snappy/snappy-stubs-internal.h | 491 ++ .../contrib/snappy/snappy-stubs-public.h | 98 + .../contrib/snappy/snappy.cc | 1307 +++++ .../contrib/snappy/snappy.h | 184 + .../contrib/win32-defs/iocpdefs.h | 133 + .../contrib/win32-defs/mingwdefs.h | 4396 ++++++++++++++ .../contrib/win32-defs/win_stdint.h | 258 + couchbase-sys/libcouchbase-2.7.0/doc/Doxyfile | 1840 ++++++ .../libcouchbase-2.7.0/doc/DoxygenLayout.xml | 109 + couchbase-sys/libcouchbase-2.7.0/doc/Makefile | 45 + .../libcouchbase-2.7.0/doc/apiattr.h | 89 + .../doc/cbc-n1qlback.markdown | 143 + .../doc/cbc-pillowfight.markdown | 319 + .../libcouchbase-2.7.0/doc/cbc.markdown | 600 ++ .../libcouchbase-2.7.0/doc/cbcrc.markdown | 49 + .../libcouchbase-2.7.0/doc/environment.h | 93 + .../libcouchbase-2.7.0/doc/example/threads.c | 77 + .../libcouchbase-2.7.0/doc/footer.html | 22 + .../libcouchbase-2.7.0/doc/genman.sh | 20 + .../libcouchbase-2.7.0/doc/header.html | 52 + couchbase-sys/libcouchbase-2.7.0/doc/intro.h | 131 + .../libcouchbase-2.7.0/doc/mainpage.h | 98 + .../libcouchbase-2.7.0/doc/man/cbc-admin.1 | 1 + .../doc/man/cbc-bucket-create.1 | 1 + .../doc/man/cbc-bucket-delete.1 | 1 + .../libcouchbase-2.7.0/doc/man/cbc-cat.1 | 1 + .../libcouchbase-2.7.0/doc/man/cbc-connstr.1 | 1 + .../libcouchbase-2.7.0/doc/man/cbc-cp.1 | 1 + .../libcouchbase-2.7.0/doc/man/cbc-create.1 | 1 + .../libcouchbase-2.7.0/doc/man/cbc-dsn.1 | 1 + .../libcouchbase-2.7.0/doc/man/cbc-flush.1 | 1 + .../libcouchbase-2.7.0/doc/man/cbc-hash.1 | 1 + .../libcouchbase-2.7.0/doc/man/cbc-lock.1 | 1 + .../libcouchbase-2.7.0/doc/man/cbc-n1qlback.1 | 142 + .../libcouchbase-2.7.0/doc/man/cbc-observe.1 | 1 + .../doc/man/cbc-pillowfight.1 | 407 ++ .../libcouchbase-2.7.0/doc/man/cbc-rm.1 | 1 + .../libcouchbase-2.7.0/doc/man/cbc-stats.1 | 1 + .../libcouchbase-2.7.0/doc/man/cbc-unlock.1 | 1 + .../doc/man/cbc-verbosity.1 | 1 + .../libcouchbase-2.7.0/doc/man/cbc-version.1 | 1 + .../libcouchbase-2.7.0/doc/man/cbc-view.1 | 1 + .../libcouchbase-2.7.0/doc/man/cbc.1 | 725 +++ .../libcouchbase-2.7.0/doc/man/cbcrc.4 | 67 + .../libcouchbase-2.7.0/doc/style.css | 1162 ++++ .../libcouchbase-2.7.0/example/CMakeLists.txt | 37 + .../example/README.markdown | 47 + .../libcouchbase-2.7.0/example/db/db.c | 167 + .../libcouchbase-2.7.0/example/db/vb.c | 227 + .../example/instancepool/main.cc | 102 + .../example/instancepool/pool.cc | 102 + .../example/instancepool/pool.h | 69 + .../example/libeventdirect/main.c | 148 + .../libcouchbase-2.7.0/example/mcc/mcc.cc | 246 + .../example/minimal/minimal.c | 130 + .../example/observe/observe.c | 146 + .../example/subdoc/subdoc-multi.cc | 132 + .../example/subdoc/subdoc-simple.cc | 191 + .../libcouchbase-2.7.0/example/tick/tick.c | 119 + .../example/views/views-example.cc | 83 + .../include/libcouchbase/_cxxwrap.h | 150 + .../include/libcouchbase/api-legacy.h | 1689 ++++++ .../include/libcouchbase/api3.h | 2 + .../include/libcouchbase/assert.h | 44 + .../include/libcouchbase/auth.h | 101 + .../include/libcouchbase/cbft.h | 109 + .../include/libcouchbase/cntl-private.h | 356 ++ .../include/libcouchbase/cntl.h | 937 +++ .../include/libcouchbase/configuration.h.in | 23 + .../include/libcouchbase/couchbase.h | 3690 ++++++++++++ .../include/libcouchbase/deprecated.h | 300 + .../include/libcouchbase/error.h | 595 ++ .../include/libcouchbase/http.h | 1 + .../include/libcouchbase/iops.h | 1050 ++++ .../include/libcouchbase/ixmgmt.h | 263 + .../include/libcouchbase/kvbuf.h | 132 + .../include/libcouchbase/n1ql.h | 364 ++ .../include/libcouchbase/pktfwd.h | 270 + .../libcouchbase/plugins/io/bsdio-inl.c | 367 ++ .../libcouchbase/plugins/io/wsaerr-inl.c | 76 + .../include/libcouchbase/plugins/io/wsaerr.h | 199 + .../include/libcouchbase/subdoc.h | 312 + .../include/libcouchbase/sysdefs.h | 98 + .../include/libcouchbase/vbucket.h | 643 +++ .../include/libcouchbase/views.h | 298 + .../include/libcouchbase/visibility.h | 65 + .../include/memcached/COPYING | 30 + .../include/memcached/README | 10 + .../include/memcached/protocol_binary.h | 1916 ++++++ .../include/memcached/vbucket.h | 42 + .../libcouchbase-2.7.0/packaging/README | 7 + .../packaging/abicheck/.gitignore | 4 + .../packaging/abicheck/Makefile | 17 + .../packaging/abicheck/README.md | 27 + .../packaging/abicheck/template.xml | 3 + .../libcouchbase-2.7.0/packaging/deb/compat | 1 + .../libcouchbase-2.7.0/packaging/deb/control | 73 + .../packaging/deb/copyright | 10 + .../packaging/deb/libcouchbase-dev.docs | 3 + .../packaging/deb/package.mk | 31 + .../libcouchbase-2.7.0/packaging/deb/rules | 46 + .../packaging/deb/source/format | 1 + .../packaging/distinfo/README | 1 + .../packaging/distinfo/distinfo.cmake.in | 4 + .../packaging/dllversion.rc.in | 39 + .../packaging/libcouchbase.pc.in | 10 + .../packaging/nuget/libcouchbase.autopkg | 76 + .../packaging/parse-git-describe.pl | 166 + .../packaging/rpm/libcouchbase.spec.in | 108 + .../packaging/rpm/package.mk | 40 + .../plugins/io/iocp/CMakeLists.txt | 9 + .../plugins/io/iocp/iocp_iops.c | 466 ++ .../plugins/io/iocp/iocp_iops.h | 217 + .../plugins/io/iocp/iocp_loop.c | 295 + .../plugins/io/iocp/iocp_timer.c | 79 + .../plugins/io/iocp/iocp_util.c | 229 + .../plugins/io/libev/CMakeLists.txt | 29 + .../plugins/io/libev/libev_io_opts.h | 65 + .../plugins/io/libev/plugin-libev.c | 289 + .../plugins/io/libevent/CMakeLists.txt | 29 + .../plugins/io/libevent/libevent_io_opts.h | 67 + .../plugins/io/libevent/plugin-libevent.c | 292 + .../plugins/io/libuv/CMakeLists.txt | 42 + .../plugins/io/libuv/libuv_compat.h | 212 + .../plugins/io/libuv/libuv_io_opts.h | 118 + .../plugins/io/libuv/plugin-internal.h | 148 + .../plugins/io/libuv/plugin-libuv.c | 648 +++ .../plugins/io/select/CMakeLists.txt | 11 + .../plugins/io/select/plugin-select.c | 448 ++ .../plugins/io/select/select_io_opts.h | 39 + .../libcouchbase-2.7.0/src/README.md | 103 + couchbase-sys/libcouchbase-2.7.0/src/aspend.h | 105 + .../libcouchbase-2.7.0/src/auth-priv.h | 34 + couchbase-sys/libcouchbase-2.7.0/src/auth.cc | 99 + .../libcouchbase-2.7.0/src/bootstrap.c | 269 + .../libcouchbase-2.7.0/src/bootstrap.h | 129 + .../src/bucketconfig/bc_cccp.cc | 488 ++ .../src/bucketconfig/bc_file.c | 347 ++ .../src/bucketconfig/bc_http.c | 630 ++ .../src/bucketconfig/bc_http.h | 82 + .../src/bucketconfig/bc_mcraw.c | 150 + .../src/bucketconfig/clconfig.h | 681 +++ .../src/bucketconfig/confmon.c | 474 ++ .../libcouchbase-2.7.0/src/callbacks.c | 378 ++ couchbase-sys/libcouchbase-2.7.0/src/cbft.cc | 210 + couchbase-sys/libcouchbase-2.7.0/src/cntl.cc | 846 +++ .../libcouchbase-2.7.0/src/config_static.h | 159 + .../libcouchbase-2.7.0/src/connspec.cc | 462 ++ .../libcouchbase-2.7.0/src/connspec.h | 118 + .../libcouchbase-2.7.0/src/ctx-log-inl.h | 27 + couchbase-sys/libcouchbase-2.7.0/src/dump.cc | 95 + .../libcouchbase-2.7.0/src/getconfig.cc | 99 + .../libcouchbase-2.7.0/src/gethrtime.c | 109 + .../libcouchbase-2.7.0/src/handler.cc | 936 +++ .../libcouchbase-2.7.0/src/hashtable.c | 75 + .../libcouchbase-2.7.0/src/hdr_timings.c | 92 + .../libcouchbase-2.7.0/src/hostlist.cc | 301 + .../libcouchbase-2.7.0/src/hostlist.h | 171 + .../libcouchbase-2.7.0/src/http/http-priv.h | 307 + .../libcouchbase-2.7.0/src/http/http.cc | 638 ++ .../libcouchbase-2.7.0/src/http/http.h | 34 + .../libcouchbase-2.7.0/src/http/http_io.cc | 307 + .../libcouchbase-2.7.0/src/instance.cc | 728 +++ .../libcouchbase-2.7.0/src/internal.h | 246 + .../libcouchbase-2.7.0/src/iofactory.c | 575 ++ .../libcouchbase-2.7.0/src/jsparse/parser.cc | 519 ++ .../libcouchbase-2.7.0/src/jsparse/parser.h | 173 + .../libcouchbase-2.7.0/src/lcbht/lcbht.c | 282 + .../libcouchbase-2.7.0/src/lcbht/lcbht.h | 199 + .../libcouchbase-2.7.0/src/lcbio/connect.c | 557 ++ .../libcouchbase-2.7.0/src/lcbio/connect.h | 364 ++ .../libcouchbase-2.7.0/src/lcbio/ctx.c | 611 ++ .../libcouchbase-2.7.0/src/lcbio/ctx.h | 405 ++ .../libcouchbase-2.7.0/src/lcbio/iotable.c | 290 + .../libcouchbase-2.7.0/src/lcbio/iotable.h | 84 + .../libcouchbase-2.7.0/src/lcbio/ioutils.c | 350 ++ .../libcouchbase-2.7.0/src/lcbio/ioutils.h | 203 + .../libcouchbase-2.7.0/src/lcbio/lcbio.h | 51 + .../libcouchbase-2.7.0/src/lcbio/manager.c | 584 ++ .../libcouchbase-2.7.0/src/lcbio/manager.h | 156 + .../libcouchbase-2.7.0/src/lcbio/protoctx.c | 84 + .../libcouchbase-2.7.0/src/lcbio/rw-inl.h | 115 + .../libcouchbase-2.7.0/src/lcbio/ssl.h | 149 + .../libcouchbase-2.7.0/src/lcbio/timer-ng.h | 179 + .../libcouchbase-2.7.0/src/lcbio/timer.c | 132 + couchbase-sys/libcouchbase-2.7.0/src/legacy.c | 430 ++ couchbase-sys/libcouchbase-2.7.0/src/list.c | 144 + couchbase-sys/libcouchbase-2.7.0/src/list.h | 127 + .../libcouchbase-2.7.0/src/logging.c | 244 + .../libcouchbase-2.7.0/src/logging.h | 86 + .../libcouchbase-2.7.0/src/mc/compress.c | 90 + .../libcouchbase-2.7.0/src/mc/compress.h | 61 + .../libcouchbase-2.7.0/src/mc/forward.c | 186 + .../libcouchbase-2.7.0/src/mc/forward.h | 90 + .../libcouchbase-2.7.0/src/mc/iovcursor-inl.h | 279 + .../libcouchbase-2.7.0/src/mc/iovcursor.h | 66 + .../src/mc/mcreq-flush-inl.h | 111 + .../libcouchbase-2.7.0/src/mc/mcreq.c | 954 +++ .../libcouchbase-2.7.0/src/mc/mcreq.h | 977 ++++ .../src/mcserver/mcserver.cc | 730 +++ .../src/mcserver/mcserver.h | 218 + .../src/mcserver/negotiate.cc | 582 ++ .../src/mcserver/negotiate.h | 130 + .../libcouchbase-2.7.0/src/n1ql/ixmgmt.cc | 860 +++ .../src/n1ql/n1ql-internal.h | 22 + .../libcouchbase-2.7.0/src/n1ql/n1ql.cc | 730 +++ .../libcouchbase-2.7.0/src/n1ql/params.cc | 215 + .../src/netbuf/netbuf-defs.h | 89 + .../src/netbuf/netbuf-mblock.h | 235 + .../libcouchbase-2.7.0/src/netbuf/netbuf.c | 929 +++ .../libcouchbase-2.7.0/src/netbuf/netbuf.h | 452 ++ .../libcouchbase-2.7.0/src/newconfig.cc | 357 ++ .../libcouchbase-2.7.0/src/nodeinfo.cc | 190 + .../src/operations/cbflush.c | 71 + .../src/operations/counter.c | 116 + .../src/operations/durability-cas.cc | 240 + .../src/operations/durability-seqno.cc | 163 + .../src/operations/durability.cc | 663 +++ .../src/operations/durability_internal.h | 284 + .../libcouchbase-2.7.0/src/operations/get.c | 409 ++ .../src/operations/observe-seqno.cc | 93 + .../src/operations/observe.c | 340 ++ .../src/operations/pktfwd.c | 86 + .../src/operations/remove.c | 83 + .../src/operations/stats.cc | 454 ++ .../libcouchbase-2.7.0/src/operations/store.c | 360 ++ .../src/operations/subdoc.cc | 510 ++ .../libcouchbase-2.7.0/src/operations/touch.c | 81 + .../libcouchbase-2.7.0/src/packetutils.c | 37 + .../libcouchbase-2.7.0/src/packetutils.h | 296 + couchbase-sys/libcouchbase-2.7.0/src/probes.d | 211 + .../libcouchbase-2.7.0/src/rdb/bigalloc.c | 225 + .../libcouchbase-2.7.0/src/rdb/bigalloc.h | 73 + .../libcouchbase-2.7.0/src/rdb/chunkalloc.c | 174 + .../libcouchbase-2.7.0/src/rdb/libcalloc.c | 94 + .../libcouchbase-2.7.0/src/rdb/rope.c | 419 ++ .../libcouchbase-2.7.0/src/rdb/rope.h | 488 ++ .../libcouchbase-2.7.0/src/retrychk.c | 113 + .../libcouchbase-2.7.0/src/retryq.cc | 394 ++ couchbase-sys/libcouchbase-2.7.0/src/retryq.h | 169 + .../libcouchbase-2.7.0/src/ringbuffer.c | 442 ++ .../libcouchbase-2.7.0/src/ringbuffer.h | 100 + .../libcouchbase-2.7.0/src/settings.c | 94 + .../libcouchbase-2.7.0/src/settings.h | 188 + .../libcouchbase-2.7.0/src/simplestring.c | 211 + .../libcouchbase-2.7.0/src/simplestring.h | 228 + .../libcouchbase-2.7.0/src/sllist-inl.h | 197 + couchbase-sys/libcouchbase-2.7.0/src/sllist.h | 76 + .../libcouchbase-2.7.0/src/ssl/CMakeLists.txt | 23 + .../libcouchbase-2.7.0/src/ssl/ssl_c.c | 415 ++ .../libcouchbase-2.7.0/src/ssl/ssl_common.c | 454 ++ .../libcouchbase-2.7.0/src/ssl/ssl_e.c | 408 ++ .../src/ssl/ssl_iot_common.h | 180 + couchbase-sys/libcouchbase-2.7.0/src/ssobuf.h | 82 + .../libcouchbase-2.7.0/src/strcodecs/base64.c | 123 + .../src/strcodecs/strcodecs.h | 285 + .../libcouchbase-2.7.0/src/timings.c | 208 + couchbase-sys/libcouchbase-2.7.0/src/trace.h | 110 + .../libcouchbase-2.7.0/src/utilities.c | 171 + .../src/vbucket/CMakeLists.txt | 2 + .../libcouchbase-2.7.0/src/vbucket/aliases.h | 35 + .../libcouchbase-2.7.0/src/vbucket/crc32.h | 83 + .../libcouchbase-2.7.0/src/vbucket/hash.h | 30 + .../libcouchbase-2.7.0/src/vbucket/json-inl.h | 112 + .../libcouchbase-2.7.0/src/vbucket/ketama.c | 66 + .../src/vbucket/rfc1321/global.h | 32 + .../src/vbucket/rfc1321/md5.h | 35 + .../src/vbucket/rfc1321/md5c-inl.h | 335 ++ .../libcouchbase-2.7.0/src/vbucket/vbucket.c | 1543 +++++ .../libcouchbase-2.7.0/src/views/docreq.c | 194 + .../libcouchbase-2.7.0/src/views/docreq.h | 83 + .../libcouchbase-2.7.0/src/views/viewreq.c | 358 ++ .../libcouchbase-2.7.0/src/views/viewreq.h | 36 + couchbase-sys/libcouchbase-2.7.0/src/wait.cc | 156 + .../libcouchbase-2.7.0/tests/CMakeLists.txt | 140 + .../tests/basic/t_base64.cc | 81 + .../tests/basic/t_ccbc103.cc | 95 + .../tests/basic/t_connstr.cc | 404 ++ .../libcouchbase-2.7.0/tests/basic/t_creds.cc | 54 + .../tests/basic/t_ctlcodes.cc | 92 + .../libcouchbase-2.7.0/tests/basic/t_host.cc | 198 + .../tests/basic/t_jsparse.cc | 137 + .../tests/basic/t_jsparse.h | 589 ++ .../libcouchbase-2.7.0/tests/basic/t_list.cc | 155 + .../tests/basic/t_logger.cc | 65 + .../libcouchbase-2.7.0/tests/basic/t_misc.cc | 24 + .../tests/basic/t_n1qlstrings.cc | 18 + .../tests/basic/t_netbuf.cc | 446 ++ .../tests/basic/t_packet.cc | 215 + .../tests/basic/t_ringbuffer.cc | 278 + .../libcouchbase-2.7.0/tests/basic/t_slist.cc | 429 ++ .../tests/basic/t_strerror.cc | 64 + .../tests/basic/t_string.cc | 112 + .../tests/basic/t_urlencode.cc | 132 + .../libcouchbase-2.7.0/tests/check-all.cc | 608 ++ .../tests/htparse/t_basic.cc | 193 + .../tests/ioserver/connection.cc | 166 + .../tests/ioserver/future.cc | 50 + .../tests/ioserver/ioserver.cc | 104 + .../tests/ioserver/ioserver.h | 478 ++ .../tests/ioserver/socket.cc | 88 + .../tests/ioserver/ssl_connection.cc | 145 + .../tests/ioserver/threads-pthreads.cc | 119 + .../tests/ioserver/threads-win32.cc | 117 + .../tests/ioserver/threads.h | 66 + .../tests/iotests/iotests.h | 15 + .../tests/iotests/mock-environment.cc | 524 ++ .../tests/iotests/mock-environment.h | 385 ++ .../tests/iotests/mock-unit-test.cc | 67 + .../tests/iotests/mock-unit-test.h | 61 + .../tests/iotests/serverparams.h | 76 + .../tests/iotests/t_arithmetic.cc | 143 + .../tests/iotests/t_behavior.cc | 226 + .../tests/iotests/t_configcache.cc | 117 + .../tests/iotests/t_confmon.cc | 241 + .../tests/iotests/t_durability.cc | 1059 ++++ .../tests/iotests/t_forward.cc | 110 + .../libcouchbase-2.7.0/tests/iotests/t_get.cc | 512 ++ .../tests/iotests/t_http.cc | 438 ++ .../tests/iotests/t_iops.cc | 175 + .../tests/iotests/t_lock.cc | 275 + .../tests/iotests/t_misc.cc | 733 +++ .../tests/iotests/t_mutate.cc | 609 ++ .../tests/iotests/t_n1ql.cc | 270 + .../tests/iotests/t_netfail.cc | 654 +++ .../tests/iotests/t_obseqno.cc | 157 + .../tests/iotests/t_regression.cc | 321 + .../tests/iotests/t_sched.cc | 87 + .../tests/iotests/t_serverops.cc | 230 + .../tests/iotests/t_smoke.cc | 528 ++ .../tests/iotests/t_subdoc.cc | 822 +++ .../tests/iotests/t_syncmode.cc | 64 + .../tests/iotests/t_views.cc | 405 ++ .../tests/iotests/testutil.cc | 250 + .../tests/iotests/testutil.h | 163 + .../libcouchbase-2.7.0/tests/mc/mctest.h | 119 + .../libcouchbase-2.7.0/tests/mc/pktmaker.h | 101 + .../libcouchbase-2.7.0/tests/mc/t_alloc.cc | 269 + .../libcouchbase-2.7.0/tests/mc/t_context.cc | 100 + .../libcouchbase-2.7.0/tests/mc/t_flush.cc | 185 + .../libcouchbase-2.7.0/tests/mc/t_forward.cc | 239 + .../libcouchbase-2.7.0/tests/mc/t_ioflush.cc | 102 + .../tests/mc/t_iovcursor.cc | 173 + .../tests/mocksupport/procutil.c | 305 + .../tests/mocksupport/procutil.h | 89 + .../tests/mocksupport/server.c | 391 ++ .../tests/mocksupport/server.h | 72 + .../tests/mocksupport/timeout.c | 69 + .../libcouchbase-2.7.0/tests/nonio_tests.cc | 23 + .../libcouchbase-2.7.0/tests/rdb/rdbtest.h | 133 + .../libcouchbase-2.7.0/tests/rdb/t_basic.cc | 128 + .../tests/rdb/t_bigalloc.cc | 93 + .../libcouchbase-2.7.0/tests/rdb/t_refs.cc | 112 + .../tests/socktests/socktest.cc | 347 ++ .../tests/socktests/socktest.h | 448 ++ .../tests/socktests/t_basic.cc | 143 + .../tests/socktests/t_ctx.cc | 73 + .../tests/socktests/t_manager.cc | 179 + .../tests/socktests/t_putex.cc | 256 + .../tests/socktests/t_read.cc | 187 + .../tests/socktests/t_reentrant.cc | 143 + .../tests/socktests/t_ssl.cc | 80 + .../tests/socktests/t_write.cc | 95 + .../libcouchbase-2.7.0/tests/start_mock.bat | 15 + .../libcouchbase-2.7.0/tests/start_mock.sh | 42 + .../libcouchbase-2.7.0/tests/unit_tests.cc | 43 + .../tests/vbucket/confdata/bad.json | 101 + .../tests/vbucket/confdata/full_25.json | 363 ++ .../tests/vbucket/confdata/memd_25.json | 90 + .../tests/vbucket/confdata/memd_30.json | 1 + .../tests/vbucket/confdata/memd_45.json | 1 + .../tests/vbucket/confdata/terse_25.json | 291 + .../tests/vbucket/confdata/terse_30.json | 1 + .../tests/vbucket/t_config.cc | 341 ++ .../libcouchbase-2.7.0/tools/CMakeLists.txt | 51 + .../libcouchbase-2.7.0/tools/cbc-handlers.h | 462 ++ .../libcouchbase-2.7.0/tools/cbc-n1qlback.cc | 439 ++ .../tools/cbc-pillowfight.cc | 822 +++ couchbase-sys/libcouchbase-2.7.0/tools/cbc.cc | 1550 +++++ .../tools/common/histogram.cc | 43 + .../tools/common/histogram.h | 23 + .../tools/common/my_inttypes.h | 22 + .../tools/common/options.cc | 420 ++ .../libcouchbase-2.7.0/tools/common/options.h | 81 + .../libcouchbase-2.7.0/tools/docgen/docgen.h | 469 ++ .../libcouchbase-2.7.0/tools/docgen/loc.h | 210 + .../tools/docgen/placeholders.h | 211 + .../libcouchbase-2.7.0/tools/docgen/seqgen.h | 94 + couchbase-sys/src/lib.rs | 6 + couchbase/Cargo.toml | 7 + couchbase/src/lib.rs | 1 + 503 files changed, 153866 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 couchbase-sys/Cargo.toml create mode 100644 couchbase-sys/build.rs create mode 100644 couchbase-sys/libcouchbase-2.7.0/.gitignore create mode 100644 couchbase-sys/libcouchbase-2.7.0/.travis.yml create mode 100644 couchbase-sys/libcouchbase-2.7.0/CMakeLists.txt create mode 100644 couchbase-sys/libcouchbase-2.7.0/CONTRIBUTING.md create mode 100644 couchbase-sys/libcouchbase-2.7.0/LICENSE create mode 100644 couchbase-sys/libcouchbase-2.7.0/README.markdown create mode 100644 couchbase-sys/libcouchbase-2.7.0/RELEASE_NOTES.markdown create mode 100644 couchbase-sys/libcouchbase-2.7.0/cmake/Modules/ConfigureDtrace.cmake create mode 100644 couchbase-sys/libcouchbase-2.7.0/cmake/Modules/CopyPDB.cmake create mode 100644 couchbase-sys/libcouchbase-2.7.0/cmake/Modules/DistScript.cmake create mode 100644 couchbase-sys/libcouchbase-2.7.0/cmake/Modules/DownloadLcbDep.cmake create mode 100644 couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseHdrHistogram.cmake create mode 100644 couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseLibev.cmake create mode 100644 couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseLibevent.cmake create mode 100644 couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseLibuv.cmake create mode 100644 couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseSnappy.cmake create mode 100644 couchbase-sys/libcouchbase-2.7.0/cmake/Modules/GenerateConfigDotH.cmake create mode 100644 couchbase-sys/libcouchbase-2.7.0/cmake/Modules/GetLibcouchbaseFlags.cmake create mode 100644 couchbase-sys/libcouchbase-2.7.0/cmake/Modules/GetPlatformCCInfo.cmake create mode 100644 couchbase-sys/libcouchbase-2.7.0/cmake/Modules/GetVersionInfo.cmake create mode 100644 couchbase-sys/libcouchbase-2.7.0/cmake/config-cmake.h.in create mode 100755 couchbase-sys/libcouchbase-2.7.0/cmake/configure create mode 100644 couchbase-sys/libcouchbase-2.7.0/cmake/defs.mk.in create mode 100755 couchbase-sys/libcouchbase-2.7.0/cmake/dtrace-instr-link.pl create mode 100644 couchbase-sys/libcouchbase-2.7.0/cmake/source_files.cmake create mode 120000 couchbase-sys/libcouchbase-2.7.0/configure.pl create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/cJSON/cJSON.c create mode 100755 couchbase-sys/libcouchbase-2.7.0/contrib/cJSON/cJSON.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/CMakeLists.txt create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/COPYING create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/include/cbsasl/cbsasl.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/client.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/common.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/cram-md5/hmac.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/cram-md5/hmac.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/cram-md5/md5.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/cram-md5/md5.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/hash.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/hash.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/util.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/cliopts/CMakeLists.txt create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/cliopts/cliopts.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/cliopts/cliopts.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/genhash/genhash.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/genhash/genhash.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/CHANGES create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/CMakeLists.txt create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/CONTRIBUTORS create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/LICENSE create mode 100755 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/MINIFY.sh create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/README create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/cmake/internal_utils.cmake create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-death-test.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-message.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-param-test.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-param-test.h.pump create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-printers.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-spi.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-test-part.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-typed-test.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest_pred_impl.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest_prod.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-death-test-internal.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-filepath.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-internal.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-linked_ptr.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-param-util-generated.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-param-util-generated.h.pump create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-param-util.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-port.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-string.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-tuple.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-tuple.h.pump create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-type-util.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-type-util.h.pump create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-all.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-death-test.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-filepath.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-internal-inl.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-port.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-printers.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-test-part.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-typed-test.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest_main.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/http_parser/LICENSE-MIT create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/http_parser/README.md create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/http_parser/http_parser.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/http_parser/http_parser.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/jsonsl/LICENSE create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/jsonsl/jsonsl.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/jsonsl/jsonsl.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/CMakeLists.txt create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/LICENSE create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/lcb-jsoncpp-forwards.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/lcb-jsoncpp.cpp create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/lcb-jsoncpp.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/snappy/CMakeLists.txt create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/snappy/COPYING create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-c.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-c.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-internal.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-lcb-msvc.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-sinksource.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-sinksource.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-stubs-internal.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-stubs-internal.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-stubs-public.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/win32-defs/iocpdefs.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/win32-defs/mingwdefs.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/contrib/win32-defs/win_stdint.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/Doxyfile create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/DoxygenLayout.xml create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/Makefile create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/apiattr.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/cbc-n1qlback.markdown create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/cbc-pillowfight.markdown create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/cbc.markdown create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/cbcrc.markdown create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/environment.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/example/threads.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/footer.html create mode 100755 couchbase-sys/libcouchbase-2.7.0/doc/genman.sh create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/header.html create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/intro.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/mainpage.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-admin.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-bucket-create.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-bucket-delete.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-cat.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-connstr.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-cp.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-create.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-dsn.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-flush.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-hash.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-lock.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-n1qlback.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-observe.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-pillowfight.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-rm.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-stats.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-unlock.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-verbosity.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-version.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-view.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbc.1 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/man/cbcrc.4 create mode 100644 couchbase-sys/libcouchbase-2.7.0/doc/style.css create mode 100644 couchbase-sys/libcouchbase-2.7.0/example/CMakeLists.txt create mode 100644 couchbase-sys/libcouchbase-2.7.0/example/README.markdown create mode 100644 couchbase-sys/libcouchbase-2.7.0/example/db/db.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/example/db/vb.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/example/instancepool/main.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/example/instancepool/pool.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/example/instancepool/pool.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/example/libeventdirect/main.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/example/mcc/mcc.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/example/minimal/minimal.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/example/observe/observe.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/example/subdoc/subdoc-multi.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/example/subdoc/subdoc-simple.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/example/tick/tick.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/example/views/views-example.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/_cxxwrap.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/api-legacy.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/api3.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/assert.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/auth.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/cbft.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/cntl-private.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/cntl.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/configuration.h.in create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/couchbase.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/deprecated.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/error.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/http.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/iops.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/ixmgmt.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/kvbuf.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/n1ql.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/pktfwd.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/plugins/io/bsdio-inl.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/plugins/io/wsaerr-inl.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/plugins/io/wsaerr.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/subdoc.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/sysdefs.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/vbucket.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/views.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/visibility.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/memcached/COPYING create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/memcached/README create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/memcached/protocol_binary.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/include/memcached/vbucket.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/packaging/README create mode 100644 couchbase-sys/libcouchbase-2.7.0/packaging/abicheck/.gitignore create mode 100644 couchbase-sys/libcouchbase-2.7.0/packaging/abicheck/Makefile create mode 100644 couchbase-sys/libcouchbase-2.7.0/packaging/abicheck/README.md create mode 100644 couchbase-sys/libcouchbase-2.7.0/packaging/abicheck/template.xml create mode 100644 couchbase-sys/libcouchbase-2.7.0/packaging/deb/compat create mode 100644 couchbase-sys/libcouchbase-2.7.0/packaging/deb/control create mode 100644 couchbase-sys/libcouchbase-2.7.0/packaging/deb/copyright create mode 100644 couchbase-sys/libcouchbase-2.7.0/packaging/deb/libcouchbase-dev.docs create mode 100644 couchbase-sys/libcouchbase-2.7.0/packaging/deb/package.mk create mode 100755 couchbase-sys/libcouchbase-2.7.0/packaging/deb/rules create mode 100644 couchbase-sys/libcouchbase-2.7.0/packaging/deb/source/format create mode 100644 couchbase-sys/libcouchbase-2.7.0/packaging/distinfo/README create mode 100644 couchbase-sys/libcouchbase-2.7.0/packaging/distinfo/distinfo.cmake.in create mode 100644 couchbase-sys/libcouchbase-2.7.0/packaging/dllversion.rc.in create mode 100644 couchbase-sys/libcouchbase-2.7.0/packaging/libcouchbase.pc.in create mode 100644 couchbase-sys/libcouchbase-2.7.0/packaging/nuget/libcouchbase.autopkg create mode 100755 couchbase-sys/libcouchbase-2.7.0/packaging/parse-git-describe.pl create mode 100644 couchbase-sys/libcouchbase-2.7.0/packaging/rpm/libcouchbase.spec.in create mode 100644 couchbase-sys/libcouchbase-2.7.0/packaging/rpm/package.mk create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/CMakeLists.txt create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_iops.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_iops.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_loop.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_timer.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_util.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/libev/CMakeLists.txt create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/libev/libev_io_opts.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/libev/plugin-libev.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/libevent/CMakeLists.txt create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/libevent/libevent_io_opts.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/libevent/plugin-libevent.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/CMakeLists.txt create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/libuv_compat.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/libuv_io_opts.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/plugin-internal.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/plugin-libuv.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/select/CMakeLists.txt create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/select/plugin-select.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/plugins/io/select/select_io_opts.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/README.md create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/aspend.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/auth-priv.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/auth.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/bootstrap.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/bootstrap.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_cccp.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_file.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_http.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_http.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_mcraw.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/clconfig.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/confmon.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/callbacks.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/cbft.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/cntl.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/config_static.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/connspec.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/connspec.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/ctx-log-inl.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/dump.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/getconfig.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/gethrtime.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/handler.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/hashtable.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/hdr_timings.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/hostlist.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/hostlist.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/http/http-priv.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/http/http.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/http/http.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/http/http_io.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/instance.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/internal.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/iofactory.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/jsparse/parser.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/jsparse/parser.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/lcbht/lcbht.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/lcbht/lcbht.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/lcbio/connect.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/lcbio/connect.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/lcbio/ctx.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/lcbio/ctx.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/lcbio/iotable.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/lcbio/iotable.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/lcbio/ioutils.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/lcbio/ioutils.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/lcbio/lcbio.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/lcbio/manager.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/lcbio/manager.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/lcbio/protoctx.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/lcbio/rw-inl.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/lcbio/ssl.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/lcbio/timer-ng.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/lcbio/timer.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/legacy.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/list.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/list.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/logging.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/logging.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/mc/compress.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/mc/compress.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/mc/forward.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/mc/forward.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/mc/iovcursor-inl.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/mc/iovcursor.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/mc/mcreq-flush-inl.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/mc/mcreq.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/mc/mcreq.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/mcserver/mcserver.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/mcserver/mcserver.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/mcserver/negotiate.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/mcserver/negotiate.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/n1ql/ixmgmt.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/n1ql/n1ql-internal.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/n1ql/n1ql.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/n1ql/params.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/netbuf/netbuf-defs.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/netbuf/netbuf-mblock.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/netbuf/netbuf.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/netbuf/netbuf.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/newconfig.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/nodeinfo.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/operations/cbflush.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/operations/counter.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/operations/durability-cas.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/operations/durability-seqno.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/operations/durability.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/operations/durability_internal.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/operations/get.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/operations/observe-seqno.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/operations/observe.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/operations/pktfwd.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/operations/remove.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/operations/stats.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/operations/store.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/operations/subdoc.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/operations/touch.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/packetutils.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/packetutils.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/probes.d create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/rdb/bigalloc.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/rdb/bigalloc.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/rdb/chunkalloc.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/rdb/libcalloc.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/rdb/rope.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/rdb/rope.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/retrychk.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/retryq.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/retryq.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/ringbuffer.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/ringbuffer.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/settings.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/settings.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/simplestring.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/simplestring.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/sllist-inl.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/sllist.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/ssl/CMakeLists.txt create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/ssl/ssl_c.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/ssl/ssl_common.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/ssl/ssl_e.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/ssl/ssl_iot_common.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/ssobuf.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/strcodecs/base64.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/strcodecs/strcodecs.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/timings.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/trace.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/utilities.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/vbucket/CMakeLists.txt create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/vbucket/aliases.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/vbucket/crc32.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/vbucket/hash.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/vbucket/json-inl.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/vbucket/ketama.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/vbucket/rfc1321/global.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/vbucket/rfc1321/md5.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/vbucket/rfc1321/md5c-inl.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/vbucket/vbucket.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/views/docreq.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/views/docreq.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/views/viewreq.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/views/viewreq.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/src/wait.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/CMakeLists.txt create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_base64.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_ccbc103.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_connstr.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_creds.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_ctlcodes.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_host.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_jsparse.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_jsparse.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_list.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_logger.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_misc.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_n1qlstrings.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_netbuf.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_packet.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_ringbuffer.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_slist.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_strerror.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_string.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/basic/t_urlencode.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/check-all.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/htparse/t_basic.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/ioserver/connection.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/ioserver/future.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/ioserver/ioserver.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/ioserver/ioserver.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/ioserver/socket.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/ioserver/ssl_connection.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/ioserver/threads-pthreads.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/ioserver/threads-win32.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/ioserver/threads.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/iotests.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/mock-environment.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/mock-environment.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/mock-unit-test.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/mock-unit-test.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/serverparams.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_arithmetic.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_behavior.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_configcache.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_confmon.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_durability.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_forward.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_get.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_http.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_iops.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_lock.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_misc.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_mutate.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_n1ql.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_netfail.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_obseqno.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_regression.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_sched.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_serverops.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_smoke.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_subdoc.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_syncmode.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_views.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/testutil.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/iotests/testutil.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/mc/mctest.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/mc/pktmaker.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/mc/t_alloc.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/mc/t_context.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/mc/t_flush.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/mc/t_forward.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/mc/t_ioflush.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/mc/t_iovcursor.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/procutil.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/procutil.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/server.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/server.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/timeout.c create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/nonio_tests.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/rdb/rdbtest.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/rdb/t_basic.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/rdb/t_bigalloc.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/rdb/t_refs.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/socktests/socktest.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/socktests/socktest.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_basic.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_ctx.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_manager.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_putex.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_read.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_reentrant.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_ssl.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_write.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/start_mock.bat create mode 100755 couchbase-sys/libcouchbase-2.7.0/tests/start_mock.sh create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/unit_tests.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/bad.json create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/full_25.json create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/memd_25.json create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/memd_30.json create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/memd_45.json create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/terse_25.json create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/terse_30.json create mode 100644 couchbase-sys/libcouchbase-2.7.0/tests/vbucket/t_config.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tools/CMakeLists.txt create mode 100644 couchbase-sys/libcouchbase-2.7.0/tools/cbc-handlers.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tools/cbc-n1qlback.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tools/cbc-pillowfight.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tools/cbc.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tools/common/histogram.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tools/common/histogram.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tools/common/my_inttypes.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tools/common/options.cc create mode 100644 couchbase-sys/libcouchbase-2.7.0/tools/common/options.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tools/docgen/docgen.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tools/docgen/loc.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tools/docgen/placeholders.h create mode 100644 couchbase-sys/libcouchbase-2.7.0/tools/docgen/seqgen.h create mode 100644 couchbase-sys/src/lib.rs create mode 100644 couchbase/Cargo.toml create mode 100644 couchbase/src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..57f4300c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +target/ +Cargo.lock +.idea/ +*.iml diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..df570e13 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["couchbase", "couchbase-sys"] diff --git a/README.md b/README.md new file mode 100644 index 00000000..de3401c5 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# Couchbase Rust SDK +A brand new `libcouchbase` binding for [Rust](https://www.rust-lang.org). diff --git a/couchbase-sys/Cargo.toml b/couchbase-sys/Cargo.toml new file mode 100644 index 00000000..7a4e8589 --- /dev/null +++ b/couchbase-sys/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "couchbase-sys" +version = "0.1.0" +authors = ["Michael Nitschinger "] +build = "build.rs" + +[dependencies] + +[build-dependencies] +libbindgen = "0.1" diff --git a/couchbase-sys/build.rs b/couchbase-sys/build.rs new file mode 100644 index 00000000..4f3fbfab --- /dev/null +++ b/couchbase-sys/build.rs @@ -0,0 +1,16 @@ +extern crate libbindgen; + +use std::env; +use std::path::Path; + +fn main() { + let out_dir = env::var("OUT_DIR").unwrap(); + + let _ = libbindgen::builder() + .header("libcouchbase-2.7.0/include/libcouchbase/couchbase.h") + //.use_core() + .no_unstable_rust() + .generate() + .unwrap() + .write_to_file(Path::new(&out_dir).join("bindings.rs")); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/.gitignore b/couchbase-sys/libcouchbase-2.7.0/.gitignore new file mode 100644 index 00000000..fd0d23c0 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/.gitignore @@ -0,0 +1,132 @@ +# Keep the entries sorted to reduce the risk for a merge conflict +*.[ao] +*.dirstamp +*.dll +*.dobj +*.exe +*.exp +*.gcda +*.gcno +*.gcov +*.l[aon] +*.lib +*.obj +*.orig +*.pdb +*.profile +*.sln +*.swo +*.swp +*.vcxproj +*.vcxproj.filters +*.vcxproj.user +*~ +.DS_Store +._* +.deps/ +.libs/ +/INSTALL +/Makefile +/Makefile.in +/aclocal.m4 +/autom4te.cache +/check-all* +/config.cache +/config.log +/config.status +/config/compile +/config/config.guess +/config/config.sub +/config/depcomp +/config/install-sh +/config/ltmain.sh +/config/missing +/config/mkinstalldirs +/config/plugin.ac +/config/test-driver +/configure +/debug/sizes +/example/db/db +/example/db/vb +/example/debug/debug +/example/instancepool/instancepool +/example/libeventdirect/eventloop +/example/mcc/mcc +/example/minimal/minimal +/example/observe/observe +/example/syncmode/syncmode +/example/yajl/couchview +filelist.mk +/gtest-1.* +/include/libcouchbase/configuration.h +/libcouchbase*.changes +/libcouchbase*.deb +/libcouchbase*.dsc +/libcouchbase*.rpm +/libcouchbase*.tar.gz +/libtool +/config/m4/libtool.m4 +/config/m4/ltoptions.m4 +/config/m4/ltsugar.m4 +/config/m4/ltversion.m4 +/config/m4/lt~obsolete.m4 +/config/m4/version.m4 +/src/config.h +/src/config.h.in +/src/probes.h +/src/stamp-h1 +/test-suite.log +/tests/*-test +/tests/*.log +/tests/*.trs +/tests/CouchbaseMock.jar +/tests/check-all +/tests/htparse-tests +/tests/nonio-tests +/tests/unit-tests +/tests/mc-tests +/tests/sock-tests +/tests/rdb-tests +/tools/cbc +/tools/cbc-pillowfight +/tools/cbc_debug.ilk +/vc100.idb +CMakeCache.txt +CMakeFiles/ +CTestTestfile.cmake +Project.opensdf +Project.sdf +Project.suo +VERSION +build/ +cbc-pillowfight.dir/ +cbc.dir/ +cmake_install.cmake +core* +couchbase.dir/ +debug/cbc-pillowfight.ilk +debug/cbc.ilk +debug/libcouchbase.idb +debug/libcouchbase.ilk +ipch/ +lcbutils.dir/ +libcouchbase.opensdf +libcouchbase.sdf +libcouchbase.suo +unit-tests.dir/ +win32/Debug/ +MANIFEST +doc/public +doc/internal + +# Symlinks +m4 +configure.ac +Makefile.am +packaging/distinfo/distinfo.cmake +packaging/distinfo/MANIFEST +*.tlog +*.nupkg +*.suo +tests/LOCAL +inst diff --git a/couchbase-sys/libcouchbase-2.7.0/.travis.yml b/couchbase-sys/libcouchbase-2.7.0/.travis.yml new file mode 100644 index 00000000..859ef3eb --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/.travis.yml @@ -0,0 +1,19 @@ +language: c + +compiler: + - clang + - gcc + +before_install: + - sudo apt-get -y install python-software-properties + - sudo add-apt-repository -y ppa:mnunberg/cmake + - sudo apt-get update + - sudo apt-get -y install libgtest-dev libssl-dev libev-dev libevent-dev cmake + +script: | + ./cmake/configure --enable-debug --disable-couchbasemock && make && make alltests && cd build && \ + (ctest -V || (cat check*.log; false)) + +notifications: + email: + - mark.nunberg@couchbase.com diff --git a/couchbase-sys/libcouchbase-2.7.0/CMakeLists.txt b/couchbase-sys/libcouchbase-2.7.0/CMakeLists.txt new file mode 100644 index 00000000..3b536e4e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/CMakeLists.txt @@ -0,0 +1,429 @@ +# libcouchbase +# Copyright (C) 2013 Couchbase, Inc +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +CMAKE_MINIMUM_REQUIRED(VERSION 2.8.9) +# Currently this file is only used for Windows + +### BEGIN CONFIGURABLES ### +# These variables can be modified as needed + +# Couchbase mock path to download +SET(COUCHBASE_MOCK_VERSION CouchbaseMock-1.4.3.jar) +# Maven repository where ${COUCHBASE_MOCK_VERSION} is to be found +SET(COUCHBASE_MOCK_DLSERVER http://packages.couchbase.com/clients/c/mock) +project(libcouchbase) + +OPTION(LCB_NO_TESTS "Disable building of tests" OFF) +OPTION(LCB_NO_TOOLS "Disable building of additional tools" OFF) +OPTION(LCB_NO_PLUGINS "Disable the building of IO plugins for external libs" OFF) +OPTION(LCB_BUILD_LIBEVENT "Build the libevent plugin" ON) +OPTION(LCB_BUILD_LIBEV "Build the libev plugin (if available)" ON) +OPTION(LCB_BUILD_LIBUV "Build the libuv plugin (if available)" ON) +OPTION(LCB_MAINTAINER_MODE "Enables maintainer mode" OFF) +OPTION(LCB_NO_SSL "Do not compile SSL support" OFF) +OPTION(LCB_NO_SNAPPY "Do not compile snappy support" ON) +OPTION(LCB_USE_ASAN "Use AddressSanitizer support (Requires Clang)" OFF) +OPTION(LCB_USE_COVERAGE "Build with code coverage support" OFF) +OPTION(LCB_STATIC_SNAPPY "Use the bundled libsnappy. Do not link with system snappy" ${WIN32}) +OPTION(LCB_USE_ARCHLIBDIR "Use architecture-prefixed library installation directory, if possible" OFF) +OPTION(LCB_BUILD_EXAMPLES "Build example applications" OFF) +OPTION(LCB_NO_MOCK "Don't run tests which depend on the mock" OFF) +OPTION(LCB_BUILD_DTRACE "Build DTrace instrumentation, if available on platform" ON) +OPTION(LCB_EMBED_PLUGIN_LIBEVENT "Embed the libevent plugin" OFF) +OPTION(LCB_STATIC_LIBEVENT "Link static libevent (only applicable if EMBED_PLUGIN_LIBEVENT is ON" OFF) +OPTION(LCB_USE_HDR_HISTOGRAM "Use HdrHistogram for statistics recording" OFF) + +### END CONFIGURABLES ### +IF(MSVC) + SET(CMAKE_DEBUG_POSTFIX "_d") +ELSE() + IF(APPLE) + SET(CMAKE_MACOSX_RPATH ON) + ENDIF() + SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + LIST(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir) + IF("${isSystemDir}" STREQUAL "-1") + SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") + ENDIF("${isSystemDir}" STREQUAL "-1") +ENDIF() + +# If building from a configured autotools tree, bail. +IF(EXISTS ${PROJECT_SOURCE_DIR}/src/config.h) + MESSAGE(FATAL_ERROR "config.h found in src/. This will break the CMake build. Remove it manually then rerun") +ENDIF() + +SET(LCB_GENSRCDIR ${PROJECT_BINARY_DIR}/generated) +INCLUDE_DIRECTORIES(${LCB_GENSRCDIR}) +SET(LCB_GENINFODIR ${PROJECT_SOURCE_DIR}/packaging/distinfo) + +INCLUDE(cmake/Modules/GetLibcouchbaseFlags.cmake) +INCLUDE(cmake/Modules/FindCouchbaseLibevent.cmake) +INCLUDE(cmake/Modules/FindCouchbaseLibev.cmake) +INCLUDE(cmake/Modules/FindCouchbaseLibuv.cmake) +INCLUDE(cmake/Modules/GetPlatformCCInfo.cmake) +INCLUDE(cmake/Modules/GetVersionInfo.cmake) +INCLUDE(cmake/Modules/CopyPDB.cmake) +INCLUDE(cmake/Modules/DownloadLcbDep.cmake) +INCLUDE(CheckIncludeFiles) +INCLUDE(cmake/source_files.cmake) + +IF(LCB_USE_HDR_HISTOGRAM) + INCLUDE(cmake/Modules/FindCouchbaseHdrHistogram.cmake) +ENDIF() +IF(HDR_HISTOGRAM_FOUND) + INCLUDE_DIRECTORIES(${HDR_HISTOGRAM_INCLUDES}) + SET(LCB_HDR_HISTOGRAM_LINK "${HDR_HISTOGRAM_LIBRARIES}") + LIST(APPEND LCB_CORE_SRC "src/hdr_timings.c") +ELSE() + SET(LCB_HDR_HISTOGRAM_LINK "") + LIST(APPEND LCB_CORE_SRC "src/timings.c") +ENDIF() + +IF(LIB_INSTALL_DIR) + SET(CMAKE_INSTALL_LIBDIR "${LIB_INSTALL_DIR}") +ENDIF() +IF(NOT LCB_USE_ARCHLIBDIR AND NOT CMAKE_INSTALL_LIBDIR) + SET(CMAKE_INSTALL_LIBDIR "lib") +ENDIF() +INCLUDE(GNUInstallDirs) + + +SET(CPACK_PACKAGE_VERSION_MAJOR "${LCB_VERSION_MAJOR}") +SET(CPACK_PACKAGE_VERSION_MINOR "${LCB_VERSION_MINOR}") +SET(CPACK_PACKAGE_VERSION_PATCH "${LCB_VERSION_PATCH}") +IF(NOT CMAKE_BUILD_TYPE) + SET(CPACK_INSTALL_SCRIPT + "${PROJECT_SOURCE_DIR}/cmake/Modules/DistScript.cmake") +ENDIF() +SET(lcb_package_name "libcouchbase-${LCB_VERSION}_${LCB_ARCH_STRING}_${LCB_CC_STRING}") +SET(CPACK_PACKAGE_FILE_NAME ${lcb_package_name}) + +IF(WIN32) + SET(CPACK_GENERATOR "ZIP") +ELSE() + SET(CPACK_GENERATOR "TGZ") +ENDIF() + +INCLUDE(CPack) + +CONFIGURE_FILE( + ${PROJECT_SOURCE_DIR}/include/libcouchbase/configuration.h.in + ${LCB_GENSRCDIR}/libcouchbase/configuration.h + @ONLY) + +CONFIGURE_FILE( + ${PROJECT_SOURCE_DIR}/cmake/defs.mk.in + ${PROJECT_BINARY_DIR}/defs.mk) + +CONFIGURE_FILE( + ${PROJECT_SOURCE_DIR}/packaging/dllversion.rc.in + ${PROJECT_BINARY_DIR}/dllversion.rc) + +SET(NT_REQUIRED_VERSION 0x0600) + +ADD_DEFINITIONS(-DLIBCOUCHBASE_INTERNAL=1) + +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) +SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) +IF(WIN32) + SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) +ELSE() + SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) +ENDIF() + +SET(SOURCE_ROOT ${PROJECT_SOURCE_DIR}) + +IF(WIN32) + ADD_DEFINITIONS(-DWINVER=${NT_REQUIRED_VERSION}) + ADD_DEFINITIONS(-D_WIN32_WINNT=${NT_REQUIRED_VERSION}) + SET(lcb_plat_includes "${SOURCE_ROOT}/contrib/win32-defs") + SET(lcb_plat_libs ws2_32.lib) + SET(lcb_plat_objs $) +ELSE() + SET(lcb_plat_libs m) + IF(NOT CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + SET(lcb_plat_libs ${lcb_plat_libs} dl) + ELSE() + # BSD _and_ DTrace + IF(LCB_BUILD_DTRACE) + SET(lcb_plat_libs ${lcb_plat_libs} elf) + ENDIF() + ENDIF() + IF(CMAKE_SYSTEM_NAME STREQUAL "SunOS") + SET(lcb_plat_libs ${lcb_plat_libs} nsl socket) + ENDIF() + IF(LCB_EMBED_PLUGIN_LIBEVENT) + SET(lcb_plat_objs ${lcb_plat_objs} $) + SET(lcb_plat_libs ${lcb_plat_libs} ${LIBEVENT_LIBRARIES}) + ADD_DEFINITIONS(-DLCB_EMBED_PLUGIN_LIBEVENT) + ENDIF() +ENDIF() + +INCLUDE_DIRECTORIES(BEFORE ${SOURCE_ROOT}/include + ${SOURCE_ROOT}/contrib + ${SOURCE_ROOT}/contrib/cbsasl/include + ${SOURCE_ROOT}/src + ${lcb_plat_includes} + ${SOURCE_ROOT}) + +# These files are bundled in a static library. +# They export no symbols of their own and may +# be considered as 'static' + +ADD_LIBRARY(couchbase_utils OBJECT ${LCB_UTILS_SRC}) + +MACRO(LCB_UTIL tgt) + SET_TARGET_PROPERTIES(${tgt} + PROPERTIES + COMPILE_FLAGS "${LCB_CORE_CFLAGS}" + POSITION_INDEPENDENT_CODE TRUE) +ENDMACRO() + +MACRO(LCB_CXXUTIL tgt) + SET_TARGET_PROPERTIES(${tgt} + PROPERTIES + COMPILE_FLAGS "${LCB_CORE_CXXFLAGS}" + POSITION_INDEPENDENT_CODE TRUE) +ENDMACRO() + +ADD_LIBRARY(netbuf OBJECT ${LCB_NETBUF_SRC}) +ADD_LIBRARY(netbuf-malloc OBJECT ${LCB_NETBUF_SRC}) +ADD_LIBRARY(mcreq OBJECT ${LCB_MC_SRC}) +ADD_LIBRARY(rdb OBJECT ${LCB_RDB_SRC}) +ADD_LIBRARY(lcbio OBJECT ${LCB_IO_SRC}) +ADD_LIBRARY(lcbht OBJECT ${LCB_HT_SRC}) +ADD_LIBRARY(lcbcore OBJECT ${LCB_CORE_SRC}) +ADD_LIBRARY(lcbcore-cxx OBJECT ${LCB_CORE_CXXSRC}) + +SET_TARGET_PROPERTIES(netbuf-malloc PROPERTIES COMPILE_DEFINITIONS NETBUF_LIBC_PROXY=1) +LCB_UTIL(netbuf-malloc) +LCB_UTIL(netbuf) +LCB_UTIL(rdb) +LCB_UTIL(lcbio) +LCB_UTIL(couchbase_utils) +LCB_UTIL(mcreq) +LCB_UTIL(lcbht) +LCB_UTIL(lcbcore) +LCB_CXXUTIL(lcbcore-cxx) + +ADD_SUBDIRECTORY(src/vbucket) +ADD_SUBDIRECTORY(contrib/cbsasl) +ADD_SUBDIRECTORY(contrib/cliopts) +ADD_SUBDIRECTORY(src/ssl) +ADD_SUBDIRECTORY(contrib/lcb-jsoncpp) +IF(LCB_BUILD_EXAMPLES) + ADD_SUBDIRECTORY(example) +ENDIF() + +IF(LCB_NO_SSL) + MESSAGE(STATUS "SSL support will be disabled") + ADD_DEFINITIONS(-DLCB_NO_SSL=1) +ENDIF() + +IF(LCB_BUILD_STATIC) + SET(_lcb_linkspec STATIC) +ELSE() + SET(_lcb_linkspec SHARED) +ENDIF() + +# CMake configuration +IF(NOT WIN32 AND LCB_BUILD_DTRACE) + INCLUDE(cmake/Modules/ConfigureDtrace.cmake) +ENDIF() + +FILE(GLOB LCB_VIEWS_C_SRC src/views/*.c) +ADD_LIBRARY(lcb_views_c OBJECT ${LCB_VIEWS_C_SRC}) +LCB_UTIL(lcb_views_c) + +FILE(GLOB LCB_JSPARSE_SRC src/jsparse/*.cc) +ADD_LIBRARY(lcb_jsparse OBJECT ${LCB_JSPARSE_SRC}) +LCB_CXXUTIL(lcb_jsparse) + +SET(LCB_CORE_OBJS + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + ${LCB_DTRACE_OBJECT} + ${lcb_plat_objs} + ${lcb_ssl_objs}) + +ADD_LIBRARY(couchbaseS STATIC ${LCB_CORE_OBJS}) +ADD_LIBRARY(couchbase ${_lcb_linkspec} ${LCB_CORE_OBJS} ${PROJECT_BINARY_DIR}/dllversion.rc) + +# For DTrace implementations which need to gain access to all the *.o files first +# we need to hook the linker command to a custom perl script which will intercept +# the object files passed to the linker, run dtrace on them, and inject the generated +# object into the linker commandline. This is a bit fragile but we don't officially +# support any of these platforms anyway +IF(LCB_DTRACE_INSTRO) + SET(_lcb_linkhook "${PROJECT_SOURCE_DIR}/cmake/dtrace-instr-link.pl ${LCB_DTRACE_SRC}") + SET_TARGET_PROPERTIES(couchbase PROPERTIES RULE_LAUNCH_LINK ${_lcb_linkhook}) + SET_TARGET_PROPERTIES(couchbaseS PROPERTIES RULE_LAUNCH_LINK ${_lcb_linkhook}) +ENDIF() + +# This is done to be ABI-compatible with Autotools. On OSX, autotools generates +# a library with a Compatibility and Current versions of 3.x despite our library +# actually being versioned at 2.x. To make things worse, autotools also actually +# provided a _symlink_ of libcouchbase.2.dylib. CMake cannot do this manually +# and thus we disable the SOVERSION appending when on OS X, and revert to +# inserting the version information by hand. +IF(NOT APPLE) + SET_TARGET_PROPERTIES(couchbase PROPERTIES + SOVERSION "${LCB_SONAME_MAJOR}" VERSION "${LCB_SONAME_FULL}") +ELSE() + SET_TARGET_PROPERTIES(couchbase PROPERTIES + LINK_FLAGS "-compatibility_version ${LCB_SONAME_MAJOR} -current_version ${LCB_SONAME_FULL}" + OUTPUT_NAME couchbase.2) + ADD_CUSTOM_COMMAND(TARGET couchbase POST_BUILD + COMMAND rm -f libcouchbase.dylib + COMMAND ln libcouchbase.2.dylib libcouchbase.dylib + WORKING_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) + INSTALL(FILES ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libcouchbase.dylib + DESTINATION ${CMAKE_INSTALL_LIBDIR}) +ENDIF() + + +IF(NOT LCB_NO_SNAPPY) + IF(LCB_STATIC_SNAPPY) + MESSAGE(STATUS "Will use embedded libsnappy") + ADD_SUBDIRECTORY(contrib/snappy) + SET(LCB_SNAPPY_LINK lcbsnappy) + ELSE() + INCLUDE(cmake/Modules/FindCouchbaseSnappy.cmake) + IF(NOT SNAPPY_FOUND) + MESSAGE(FATAL_ERROR "Snappy not found and building with dynamic snappy requested") + ELSE() + SET(LCB_SNAPPY_LINK ${SNAPPY_LIBRARIES}) + INCLUDE_DIRECTORIES(${SNAPPY_INCLUDES}) + ENDIF() + ENDIF() +ELSE() + MESSAGE(STATUS "Compression support will be disabled") + ADD_DEFINITIONS(-DLCB_NO_SNAPPY=1) + SET(LCB_SNAPPY_LINK "") +ENDIF() + +SET_TARGET_PROPERTIES(couchbase PROPERTIES PREFIX "lib") +SET_TARGET_PROPERTIES(couchbase PROPERTIES IMPORT_PREFIX "lib") +SET(LCB_LINK_DEPS ${lcb_plat_libs} ${lcb_ssl_libs} ${LCB_SNAPPY_LINK} ${LCB_HDR_HISTOGRAM_LINK}) +IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") + SET(LCB_LINK_DEPS ${LCB_LINK_DEPS} rt) +ENDIF() + +TARGET_LINK_LIBRARIES(couchbase ${LCB_LINK_DEPS}) +TARGET_LINK_LIBRARIES(couchbaseS ${LCB_LINK_DEPS}) + +ENABLE_TESTING() +IF(NOT LCB_NO_TESTS) + ADD_SUBDIRECTORY(tests tests) +ELSE() + ADD_TEST(NAME dummytest COMMAND echo "No Tests!") +ENDIF(NOT LCB_NO_TESTS) + +IF(NOT LCB_NO_TOOLS) + ADD_SUBDIRECTORY(tools tools) +ENDIF(NOT LCB_NO_TOOLS) + +# Always add this file, as we need the headers for this! +ADD_SUBDIRECTORY(plugins/io/libuv) +IF(NOT LCB_NO_PLUGINS AND LCB_BUILD_LIBUV) +ENDIF() + +IF(NOT LCB_NO_PLUGINS AND NOT WIN32) + IF(LCB_BUILD_LIBEVENT) + ADD_SUBDIRECTORY(plugins/io/libevent) + ENDIF() + IF(LCB_BUILD_LIBEV) + ADD_SUBDIRECTORY(plugins/io/libev) + ENDIF() + IF(NOT (HAVE_LIBEV OR HAVE_LIBEVENT)) + MESSAGE(FATAL_ERROR + "libev or libevent development files missing. + You can disable these dependencies by passing + -DLCB_NO_PLUGINS=1 to Cmake, or + --disable-plugins to the configure script") + ENDIF() +ENDIF() + + +IF(LCB_MAINTAINER_MODE) + IF(NOT (HAVE_LIBEV AND HAVE_LIBEVENT AND HAVE_LIBUV)) + MESSAGE(FATAL_ERROR "Maintainer mode requires all plugins to be present") + ENDIF() +ENDIF() + +ADD_SUBDIRECTORY(plugins/io/select) +ADD_SUBDIRECTORY(plugins/io/iocp) +INSTALL(TARGETS couchbase + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + +INSTALL(DIRECTORY include/libcouchbase ${LCB_GENSRCDIR}/libcouchbase + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING PATTERN *.h PATTERN *.c) + +INSTALL(DIRECTORY doc/man/ DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 FILES_MATCHING PATTERN *.1) +INSTALL(DIRECTORY doc/man/ DESTINATION ${CMAKE_INSTALL_MANDIR}/man4 FILES_MATCHING PATTERN *.4) + +INSTALL_PDBS(couchbase) + +SET(_lcb_tarname "libcouchbase-${LCB_VERSION}") +SET(_lcb_manifest "${LCB_GENINFODIR}/MANIFEST") + +# pkg-config stuff goes with installation +IF(NOT WIN32) + CONFIGURE_FILE(packaging/libcouchbase.pc.in packaging/libcouchbase.pc @ONLY) + INSTALL(FILES ${PROJECT_BINARY_DIR}/packaging/libcouchbase.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +ENDIF() + + +ADD_CUSTOM_TARGET(uninstall + COMMAND xargs rm -vf < ${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt) + +ADD_CUSTOM_TARGET(file_manifest + COMMAND sh -c 'test -e ${_lcb_manifest} || git ls-files > ${_lcb_manifest}' + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + + +ADD_CUSTOM_TARGET(dist + COMMAND rm -rf "${_lcb_tarname}" + COMMAND mkdir "${_lcb_tarname}" + COMMAND tar -cf - -C ${PROJECT_SOURCE_DIR} -T ${_lcb_manifest} | tar xf - -C "${_lcb_tarname}" + COMMAND cp -a "${LCB_GENINFODIR}" "${_lcb_tarname}/packaging" + COMMAND tar -czf "${_lcb_tarname}.tar.gz" "${_lcb_tarname}" + COMMAND rm -rf "${_lcb_tarname}" + DEPENDS file_manifest) + +# Generate our configuration file _after_ we've collected everything +INCLUDE(cmake/Modules/GenerateConfigDotH.cmake) + +# Build any local tests/scripts +IF (EXISTS ${SOURCE_ROOT}/tests/LOCAL) + ADD_SUBDIRECTORY(${SOURCE_ROOT}/tests/LOCAL) +ENDIF() diff --git a/couchbase-sys/libcouchbase-2.7.0/CONTRIBUTING.md b/couchbase-sys/libcouchbase-2.7.0/CONTRIBUTING.md new file mode 100644 index 00000000..3c0988c3 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/CONTRIBUTING.md @@ -0,0 +1,124 @@ +# Contributing + +In addition to filing bugs, you may contribute by submitting patches +to fix bugs in the library. Contributions may be submitting to +. We use Gerrit as our code review system - +and thus submitting a change would require an account there. Note that +pull requests will not be ignored but will be responded to much quicker +once they are converted into Gerrit. + +For something to be accepted into the codebase, it must be formatted +properly and have undergone proper testing. While there are no formatting +guidelines per se, the code should look similar to the existing code +within the library. + +## Branches and Tags + +Released versions of the library are marked as annotated tags inside +the repository. + +* The `release10` contains the older 1.x versions of the library. +* The `release23` contains the current 2.3.x maintenance branch +* The `master` branch represents the mainline branch. The master + branch typically consists of content going into the next release. + +## Contributing Patches + +If you wish to contribute a new feature or a bug fix to the library, try to follow +the following guidelines to help ensure your change gets merged upstream. + +### Before you begin + +For any code change, ensure the new code you write looks similar to the code +surrounding it. We have no strict code style policies, but do request that your +code stand out as little as possible from its surrounding neighborhood (unless +of course your change is stylistic in nature). + +If your change is going to involve a substantial amount of time or effort, please +attempt to discuss it with the project developers first who will provide assistance +and direction where possible. + +Additionally, note that the library uses C89 (AKA "ANSI C") with some extensions +that are known to work on both GCC and Visual Studio. Please ensure your code +conforms to this, unless the new code is specific to a given platform. + +#### For new features + +Ensure the feature you are adding does not already exist, and think about how +this feature may be useful for other users. In general less intrusive changes +are more likely to be accepted. + +#### For fixing bugs + +Ensure the bug you are fixing is actually a bug (and not a usage) error, and +that it has not been fixed in a more recent version. Please read the release +notes as well as the issue tracker to see a list of open and resolved issues. + +### Code Review + +#### Signing up on Gerrit + +Everything that is merged into the library goes through a code review process. +The code review process is done via [Gerrit](http://review.couchbase.org) +(Unfortunately we cannot merge pull requests, though it is fairly simple to +convert a pull request to gerrit, as seen later. If you know a way to integrate +pull requests with Gerrit, please let us know). + +To sign up for a gerrit account, go to http://review.couchbase.org and click +on the _Register_ link at the top right. Once you've signed in you will need +to sign the CLA (Contributor License Agreement) by going you your gerrit account +page and selecting the _Agreements_ link on the left. When you've done that, be +sure to notify us in IRC (at _#libcouchbase_) and/or send an email to +**matt**AT**couchbase**DOTCOM as you will require manual approval before being +able to submit a request for change. + +Once approved, you should add your public SSH key to gerrit. + +#### Setting up your fork with Gerrit + +Assuming you have a repository created like so: + +``` +$ git clone git://github.com/couchbase/libcouchbase.git +``` + +you can simply perform two simple steps to get started with gerrit: + +``` +$ git remote add gerrit ssh://${USERNAME}@review.couchbase.org:29418/libcouchbase +$ scp -P 29418 ${USERNAME}@review.couchbase.org:hooks/commit-msg .git/hooks +$ chmod a+x .git/hooks/commit-msg +``` + +The last change is required for annotating each commit message with a special +header known as `Change-Id`. This allows Gerrit to group together different +revisions of the same patch. + +#### Pushing a changeset + +Now that you have your change and a gerrit account to push to, you need to +upload the change for review. To do so, invoke the following incantation: + +``` +$ git push gerrit HEAD:refs/for/master +``` + +Where `gerrit` is the name of the _remote_ added earlier. You may encounter +some errors when pushing. The most common are: + +* "You are not authorized to push to this repository". You will get this if your + account has not yet been approved. +* "Missing Change-Id". You need to install the `commit-msg` hook as described above. + Note that even once you do this, you will need to ensure that any prior commits + already have this header - this may be done by doing an interactive rebase (e.g. + `git rebase -i origin/master` and selecting `reword` for all the commits; which + will automatically fill in the Change-Id). + + +Once you've pushed your changeset you can add people to review. Currently these +are: + +* Mark Nunberg +* Brett Lawson +* Sergey Avseyev + diff --git a/couchbase-sys/libcouchbase-2.7.0/LICENSE b/couchbase-sys/libcouchbase-2.7.0/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/couchbase-sys/libcouchbase-2.7.0/README.markdown b/couchbase-sys/libcouchbase-2.7.0/README.markdown new file mode 100644 index 00000000..8bb5d798 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/README.markdown @@ -0,0 +1,163 @@ +# Couchbase C Client + +[![Build Status](https://travis-ci.org/couchbase/libcouchbase.png?branch=master)](https://travis-ci.org/couchbase/libcouchbase) + +This is the C client library for [Couchbase](http://www.couchbase.com) +It communicates with the cluster and speaks the relevant protocols +necessary to connect to the cluster and execute data operations. + +## Features + +* Can function as either a synchronous or asynchronous library +* Callback Oriented +* Can integrate with most other asynchronous environments. You can write your + code to integrate it into your environment. Currently support exists for + * [libuv](http://github.com/joyent/libuv) (Windows and POSIX) + * [libev](http://software.schmorp.de/pkg/libev.html) (POSIX) + * [libevent](http://libevent.org/) (POSIX) + * `select` (Windows and POSIX) + * IOCP (Windows Only) +* Support for operation batching +* Cross Platform - Tested on Linux, OS X, and Windows. + +## Building + +Before you build from this repository, please check the +[installation page](http://developer.couchbase.com/server/other-products/release-notes-archives/c-sdk) +to see if there is a binary or release tarball available for your needs. Since the code here is +not part of an official release it has therefore not gone through our +release testing process. + +### Dependencies + +By default the library depends on: + +* _libevent_ (or _libev_) for the primary I/O backend. +* _openssl_ for SSL transport. +* _CMake_ version 2.8.9 or greater (for building) + +On Unix-like systems these dependencies are checked for by default +while on Windows they are not checked by default. + +On Unix, the build system will expect to have _libevent_ or _libev_ installed, +unless building plugins is explicitly disabled (see further). + +### Building on Unix-like systems + +Provided is a convenience script called `cmake/configure`. It is a Perl +script and functions like a normal `autotools` script. + +```shell +$ git clone git://github.com/couchbase/libcouchbase.git +$ cd libcouchbase && mkdir build && cd build +$ ../cmake/configure +$ make +$ ctest +``` + +### Building on Windows + +Assuming `git` and Visual Studio 2010 are installed, from a `CMD` shell, do: + +``` +C:\> git clone git://github.com/couchbase/libcouchbase.git +C:\> mkdir lcb-build +C:\> cd lcb-build +C:\> cmake -G "Visual Studio 10" ..\libcouchbase +C:\> cmake --build . +``` + +This will generate and build a Visual Studio `.sln` file. + +Windows builds are known to work on Visual Studio versions 2008, 2010 and +2012. + +If you wish to link against OpenSSL, you should set the value of +`OPENSSL_ROOT_DIR` to the location of the installation path, as described +[here](https://github.com/Kitware/CMake/blob/master/Modules/FindOpenSSL.cmake) + +## Bugs, Support, Issues +You may report issues in the library in our issue tracked at +. Sign up for an account and file an issue +against the _Couchbase C Client Library_ project. + +The developers of the library hang out in IRC on `#libcouchbase` on +irc.freenode.net. + + +## Examples + +* The `examples` directory +* Official client libraries using libcouchbase + * [node.js](http://github.com/couchbase/couchnode) + * [Python](http://github.com/couchbase/couchbase-python-client) + * [Ruby](http://github.com/couchbase/couchbase-ruby-client) (uses the old < 2.6 API) + * [PHP](http://github.com/couchbase/php-couchbase) (uses the old < 2.6 API) +* Community projects using libcouchbase + * [C++11 wrapper](https://github.com/couchbaselabs/libcouchbase-cxx) + * [cberl - Couchbase NIF](https://github.com/chitika/cberl) + * [Perl client](https://github.com/mnunberg/perl-Couchbase-Client) + +## Documentation + +Documentation is available in guide format (introducing the basic concepts of +Couchbase and the library). It is recommended for first-time users, and can +be accessed at our [Documentation Site](http://developer.couchbase.com/documentation/server/4.5/sdk/c/start-using-sdk.html). + +API documentation is also available and is generated from the library's headers. +It may contain references to more advanced features not found in the guide. + +API documentation may be generated by running `doxygen` within the source root +directory. When this is done, you should have a `doc/html/index.html` page which +may be viewed. + +Doxygen may be downloaded from the +[doxygen downloads page](http://www.stack.nl/~dimitri/doxygen/download.html). Note +however that most Linux distributions as well as Homebrew contain Doxygen in their +repositories. + +``` +$ doxygen +$ xdg-open doc/html/index.html # Linux +$ open doc/html/index.html # OS X +``` + +You may also generate documentation using the `doc/Makefile` which dynamically +inserts version information + +``` +$ make -f doc/Makefile public # for public documentation +$ make -f doc/Makefile internal # for internal documentation +``` + +The generated documentation will be in the `doc/public/html` directory for +public documentation, and in the `doc/internal/html` directory for internal +documentation. + +## Contributors + +The following people contributed to libcouchbase (in alphabetic order) +(last updated Nov. 27 2014) + +* Brett Lawson +* Dave Rigby +* Jan Lehnardt +* Mark Nunberg +* Matt Ingenthron +* Patrick Varley +* Paul Farag +* Pierre Joye +* Sebastian +* Sergey Avseyev +* Subhashni Balakrishnan +* Sundar Sridharan +* Trond Norbye +* Volker Mische +* William Bowers +* Yura Sokolov +* Yury Alioshinov + +## License + +libcouchbase is licensed under the Apache 2.0 License. See `LICENSE` file for +details. diff --git a/couchbase-sys/libcouchbase-2.7.0/RELEASE_NOTES.markdown b/couchbase-sys/libcouchbase-2.7.0/RELEASE_NOTES.markdown new file mode 100644 index 00000000..6ce65109 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/RELEASE_NOTES.markdown @@ -0,0 +1,2711 @@ +# Release Notes + +## 2.7.0 (December 21 2016) + +This release consists mainly of internal refactoring. Many of the internals +have been 'upgraded' to C++ + +## 2.6.4 (November 28 2016) + +* Fix bug in pillowfight where large value sizes would cause a segfault. + * Issues: [CCBC-727](https://issues.couchbase.com/browse/CCBC-727) + +* Allow 64 bit values with `cbc-incr` and `cbc-decr`. + * Issues: [CCBC-716](https://issues.couchbase.com/browse/CCBC-716) + +* Fix encoding in `lcb_n1p_setconsistent_token`. This function would encode + it as `scan_vector` but it should be `scan_vectors`. + +* Refactor negotiation internals to use C++. + This is part of an internal refactoring to move our internals over to C++. + This will make the code more manageable and extendable in the future. + +## 2.6.3 (September 27 2016) + +* Fix memory corruption for some JSON APIs when no rows are returned. + This fixes a bug where the JSON parser would read from garbage memory when + parsing a response that had no rows, but due to a slow network, would be + received in multiple chunks. + This affects N1QL, CBFT, and View APIs. + * Priority: Major + * Issues: [CCBC-721](https://issues.couchbase.com/browse/CCBC-721) + +* Allow to adjust bytes to read per event loop iteration. + This allows applications with high network throughput but low CPU capacity + to prevent the library from oversaturating a specific event callback invocation + or starve other sockets. It may be controlled through the `read_chunk_size` + connection string option or via `lcb_cntl_string`. + * Priority: Major + * Issues: [CCBC-568](https://issues.couchbase.com/browse/CCBC-568) + +* Use `htonll` for CAS values. + This allows a consistent representation of CAS values regardless of underlying + platform. This allows interoperability between other SDKs with respect to + exchanging CAS values. This however may break interoperability with older + versions of the same SDK, if the CAS value is being passed around (which it + shouldn't be). + +* New subdocument additions. + This adds the `LCB_SUBDOC_F_MKDOCUMENT` flag which allows document creation + if the document does not exist, and can be used for mutation operations which + may create new paths or values. The `LCB_SUBDOC_CMD_GET_COUNT` is also added, + which is a new command which retrieves the number of elements (for an array) + or key-value items (within an object/dictionary) of a given path. + Both these features require Couchbase Server 4.6 (or its prereleases). + * Priority: Major + * Issues: [CCBC-718](https://issues.couchbase.com/browse/CCBC-718) + +## 2.6.2 (July 26 2016) + +* Don't crash on high number of FDs with select plugin. Because `select(2)` + can only accomodate up to a certain number of file descriptors in the + application, if opening a socket results in a too-high-numbered FD, the + plugin will return an error rather than silently failing during polling. + * Priority: Major + * Issues: [CCBC-567](https://issues.couchbase.com/browse/CCBC-567) + +* Pillowfight can now set ttl (expiry). This is done via the `-e` or `--expiry` + option. + * Priority: Major + * Issues: [CCBC-637](https://issues.couchbase.com/browse/CCBC-637) + +* Log URLs of HTTP requests. This may make it easier to debug some HTTP-based + APIs. The URLs are printed as part of the `TRACE` logging level. + * Priority: Major + * Issues: [CCBC-641](https://issues.couchbase.com/browse/CCBC-641) + +* Fix crash on shutdown with completion-based I/O. The crash was a result + of dereferencing the `lcb_t` after it had been destroyed. This bug affected + completion-based I/O subsystems such as libuv and IOCP. + * Priority: Major + * Issues: [CCBC-707](https://issues.couchbase.com/browse/CCBC-707) + +* Do not require `operation` field to be set on `lcb_CMDSTORE`. + Starting from this version, a new `lcb_storage_t` constant, `LCB_UPSERT` + has been added with a value of 0. This means that upsert operations no + longer need to explicitly use `LCB_SET`, it being the default. + * Priority: Major + * Issues: [CCBC-545](https://issues.couchbase.com/browse/CCBC-545) + +## 2.6.1 (June 21 2016) + +* Index management API now properly handles 'fields' field. Previously this + was treated as a csv string, when it is in fact a JSON array. + +* `pillowfight` now has a `--populate-only` option, which is useful when simply + trying to populate buckets with large amounts of data. + +* Allow to bypass OpenSSL initialization. This allows applications which already + have OpenSSL intialization code in place to suppress libcouchbase's own + OpenSSL initialization code. You can disable SSL initialization by using + `ssl=no_global_init` in the connection string. + +* Allow to toggle sending of multiple credentials in N1QL queries. + You can use the `LCB_CMD_F_MULTIAUTH` in the `lcb_CMDN1QL::cmdflags` field + to indicate that multiple credentials should be added. Otherwise only the + current bucket's credentials will be sent. + +* Fix infinite loop on completion (UV,nodejs,IOCP) type IO plugins. + This bug would be triggered when only a single server remained in the cluster + and that single server failed. This would result in the client never being + able to perform operations due to a delayed reference count decrement. + * Priority: Major + * Issues: [CCBC-704](https://issues.couchbase.com/browse/CCBC-704) + +## 2.6.0 (May 17 2016) + +* Improve index management API and implementation. The `rawjson` field was + being ignored and the `condition` field was missing as well. + +* Add pillowfight support for subdoc. At the simplest level you can simply + invoke pillowfight as `cbc-pillowfight --subdoc --json `. + Refer to the pillowfight documentation for more details. + +## 2.5.8 (April 19 2016) + +* Fix SSL connectivity errors with views and HTTP bootstrapping. + This would cause network connectivity issues when attempting to bootstrap + via `https://` or using views (`lcb_view_query()`). The fix is a workaround + to not advertise older SSL encryption methods. + * Priority: Major + * Issues: [CCBC-688](https://issues.couchbase.com/browse/CCBC-688) + +* Do not abort when receiving a memcached EINVAL response. + While the client should never end up in a situation where it receives an + `EINVAL` from the server, it should nevertheless not terminate the execution + of the current process. This bug was introduced in 2.5.6 + * Issues: [CCBC-689](https://issues.couchbase.com/browse/CCBC-689) + +* Fix memory leak when using N1QL prepared statements. + Prepared statements would never be freed, even when the client handle was + destroyed (`lcb_destroy()`) causing a slight memory leak. + +* Append CRLF header after host header. + This would sometimes result in odd HTTP headers being sent out. + * Issues: [CCBC-694](https://issues.couchbase.com/browse/CCBC-694) + +* Experimental CBFT (Full-Text search API) + This version adds a new fulltext api. The API is a row-based API similar + to N1QL and MapReduce. See the `` header for more details. + The API is experimental and subject to change. + * Issues: [CCBC-638](https://issues.couchbase.com/browse/CCBC-638) + +* Allow additional client identifier for HELLO command. + The SDK sends a version string to the server when doing initial negotiation. + The server then uses this string in the context of any logging messages + pertaining to that connection. In this version, a new setting has been added + to allow 'user-defined' version strings to be appended to the logs. Note that + this feature is intended only for use with wrapping SDKs such as Python, node.js + and PHP so that their versions can be present in the log messages as well. + This setting is exposed as a string control (in the connection string, or + `lcb_cntl_string()` with the name of `client_string`. + * Issues: [CCBC-693](https://issues.couchbase.com/browse/CCBC-693) + +* vBucket retry logic changes. + The client will now retry at constant 100ms rate when receiving not-my-vbucket + error replies from the server (adjustable using `retry_nmv_interval`). + It will also only use fast-forward map to determine the new location for the + vbucket, and will not use extended hueristics. + + The most noteworthy user-visible change is the 100ms retry interval which + will significantly decrease the network traffic used by the SDK + during a rebalance. + + To restore the pre-2.5.8 behavior (i.e. use extended heuristics and and + exponential retry rate), specify `vb_noguess=false`. + * Priority: Major + * Issues: [CCBC-660](https://issues.couchbase.com/browse/CCBC-660) + +* Add interface for multi-bucket authentication. + A new API has been added to modify and add additional bucket/password + pairs in the library. This is done using `lcb_cntl` and the `LCB_CNTL_BUCKET_CRED` + setting. + + Note that this functionality is not yet used in N1QL queries due to + [MB-16964](https://issues.couchbase.com/browse/MB-16964) + * Priority: Minor + * Issues: [CCBC-661](https://issues.couchbase.com/browse/CCBC-661) + + +## 2.5.7 (March 22 2016) + +* High-level index management operations. + A volatile API for high level index management operations has been added to + assist in common N1QL index operations such as creating the primary index + and removing indexes. + * Priority: Major + * Issues: [CCBC-662](https://issues.couchbase.com/browse/CCBC-662) + +* Fix N1QL mutation token queries. + This fixes some bugs in the previous implementation of the way mutation tokens + were handled with `lcb_N1QLPARAMS`. The bugs and fixes only affect consumers + of that API. Couchbase SDKs do not consume this API + * Priority: Minor + * Issues: [CCBC-658](https://issues.couchbase.com/browse/CCBC-658) + +* Throttle config request retries on empty NMVB responses. + This changes the previous behavior where a new configuration would be + retrieved _immediately_ upon a not-my-vbucket reply if a configuration + was not included within the error reply itself. The new behavior is to + request a delayed retry (i.e. subject to the default throttle settings) + if the current configuration originated from the CCCP (Memcached) provider. + * Priority: Major + * Issues: [CCBC-681](https://issues.couchbase.com/browse/CCBC-681) + +* Rename `LCB_CLIENT_ETMPFAIL` to `LCB_CLIENT_ENOCONF`. + This error code is returned only when there is no current client configuration. + This error condition is _not_ temporary and is actually fatal; a result of + an initial bootstrapping failure. Note that the older name is still valid + in older code for compatibility purposes. + * Priority: Minor + * Issues: [CCBC-679](https://issues.couchbase.com/browse/CCBC-679) + +* Include PID in log messages on OS X. + This makes the library logs (via `LCB_LOGLEVEL` etc.) easier to read on a + mac. Previously this used to display only the thread ID, which was identical + for multiple processes. Now the display reads as _pid/tid_, making it easier + to read the logs in a multi-process environment. + * Priority: Minor + * Issues: [CCBC-677](https://issues.couchbase.com/browse/CCBC-677) + + +## 2.5.6 (February 18 2016) + +* Sub-Document API (_experimental_) + The client-side sub-document API has been implemented. Sub-document is + a feature which vastly reduces network usage when operating on parts + of documents. + The API as it appears in this version is highly experimental and may + (and likely will) change. Examples of use can be found in the `examples/subdoc` + directory. + * Priority: Major + +* Make `lcb_sched_enter` and `lcb_sched_leave` optional. + When scheduling an operation (e.g. `lcb_get3()`), the scheduling function + will implicitly create a scheduling context and submit the operation if + none exists already. A scheduling context is explicitly created by calling + `lcb_sched_enter()` and finished by calling `lcb_sched_leave()` or + `lcb_sched_fail()`. + * Issues: [CCBC-664](https://issues.couchbase.com/browse/CCBC-664) + * Priority: Major + +* API3 is now stable. + The scheduling based API, introduced in version 2.4.0 and known as 'api3', + is now stable and considered the API for use with the library. + The previous API (i.e. 'api2') is considered deprecated. + + While API3 has been promoted to stable in this version, it has been available + in its current form (and in a mostly compatible manner, _except_ the implicit + scheduling feature - CCBC-664) since 2.4.0. + + Storing an item in API2: + + lcb_get_store_t cmd = { 0 }, *cmd_p = &cmd; + cmd.v.v0.key = "key"; + cmd.v.v0.nkey = 3; + cmd.v.v0.bytes = "value"; + cmd.v.v0.nbytes = 5; + cmd.v.v0.operation = LCB_SET; + lcb_store(instance, NULL, 1, &cmd_p); + + Storing an item in API3: + + lcb_CMDSTORE cmd = { 0 }; + LCB_CMD_SET_KEY(&cmd, "key", 3); + LCB_CMD_SET_VALUE(&cmd, "value", 5); + cmd.operation - LCB_SET; + lcb_store3(instance, NULL, &cmd); + + +* Add `libcouchbase/` string to version identification to Memcached + Connections to memcached will now be identified as `libcouchbase/version` + rather than `version`. This increases readability for server logs + * Issues: [CCBC-656](https://issues.couchbase.com/browse/CCBC-656) + * Priority: Minor + +* Hide `mutation_token` field from API3 mutation respones. The `mutation_token` + field has never been part of the API itself (it was previously present when + api3 was marked as "experimental"). + The mutation token for any operation must now be retrieved using the + `lcb_resp_get_mutation_token()` to retrieve the actual mutation token. + * Issues: [CCBC-671](https://issues.couchbase.com/browse/CCBC-671) + * Priority: Minor + +* Server's `PROTOCOL_BINARY_RESPONSE_EINTERNAL` is no longer mapped to + `LCB_EINTERNAL`. `LCB_UNKNOWN_MEMCACHED_ERROR` will be returned instead + +* Allow get-and-touch with an expiry of 0. + Clearing a document's expiry with `get` is now possible, using the new + `LCB_CMDGET_F_CLEAREXP` in `lcb_CMDGET::cmdflags`. + * Issues: [CCBC-667](https://issues.couchbase.com/browse/CCBC-667) + * Priority: Major + +* Allow multiple buckets when using sequence number consistency with N1QL + This uses the new internal `scan_vector` protocol supporting multiple buckets, + each providing their own `lcb_MUTATION_TOKEN` objects. + * Issues: [CCBC-658](https://issues.couchbase.com/browse/CCBC-658) + * Priority: Major + +## 2.5.5 (January 12 2016) + +* Add `retry_interval` string option to adjust retry interval. + This allows the setting to be modified via `lcb_cntl_string()` and specified + in the connection string. + * Priority: Major + * Issues: [CCBC-654](https://issues.couchbase.com/browse/CCBC-654) + +* Handle backslashes in view row ID fields. + This would previously not be handled correctly as the backslashes would not + be removed, for example an ID of `has_a_"quote` would appear in the API as + `has_a_\"quote`. This has been fixed and document IDs are now properly + processed as JSON + * Priority: Major + * Issues: [CCBC-649](https://issues.couchbase.com/browse/CCBC-649) + +* Allow 'file-only' configuration mode. + This allows applications to make the library instance exclusively configured + from a file on the local filesystem rather than through network bootstrap. + This feature is undocumented and unsupported. It may be enabled using the + `bootstrap_on=file_only` connection string directive. + * Priority: Major + * Issues: [CCBC-652](https://issues.couchbase.com/browse/CCBC-652) + +* Log when squashing network errors. + This will make the library log the original error whenever a network error + is translated from a more detailed description into `LCB_NETWORK_ERROR` + (in case `detailed_errcodes` is not enabled), or if an OS-level error is + found which cannot be translated into a more specific library error. + * Priority: Major + +* Fix memcached/ketama hashing + This fixes a bug in the ketama hasing code which caused a key to be mapped + to an effectively arbitrary server for the library instance. In practice the + node a key was mapped to depended on the order in which the hosts were + specified in the connection string. This has been fixed to always use + hashing based on the lexical sort order of each server node. + It is highly recommended that applications upgrade to this version (2.5.5) + for proper memcached (cache) bucket functionality. + * Priority: Critical + * Issues: [CCBC-653](https://issues.couchbase.com/browse/CCBC-653) + +* Add `cbc-touch` subcommand. + This now allows the simple "touching", or modifying expiration time via the + `cbc` command line client. + * Priority: Major + * Issues: [CCBC-651](https://issues.couchbase.com/browse/CCBC-651) + + +## 2.5.4 (November 25 2015) + +* Validate vBucket master nodes for bounds when receiving new configuration. + This ensures that invalid configurations (addressing nodes which do not + exist) do not make their way to KV routing operations. + * Priority: Major + * Issues: [CCBC-643](https://issues.couchbase.com/browse/CCBC-643) + +* Add `lcb_strcbtype` to print the name of the callback type + This small convenience function is added to pretty-print the type + of callback being invoked. The second argument to the callback can be passed + to this function. + * Priority: Minor + +* Disallow using `certpath` connection string option without explicit SSL + (`couchbases://`) scheme. Since the SSL and non-SSL schemes are similar, + a typo can let a user mistakenly think that SSL is being used. This is + fixed by disallowing the other SSL option (`certpath`) when SSL is not + enabled. + * Priority: Minor + * Issues: [CCBC-644](https://issues.couchbase.com/browse/CCBC-644) + +* Add convenience function to retrieve hostname for key. + This is an alternative to retrieving the vBucket configuration (via `lcb_cntl()`) + and mapping the key to an index, and mapping the index to a node. Note that + hostnames are sufficient for most but not all configurations. Those running + multiple server instances on the same host (for example, `cluster_run`) will + still need to use the full set of steps as this function does not return a + port. This function is provided both as a vBucket API (`lcbvb_get_hostname()`) + which retrieves the hostname for an index as well as a top-level instance + (`lcb_t`) function (`lcb_get_keynode()`) which accepts a key buffer and length. + * Priority: Minor + +* Ensure embedded jsoncpp does not throw exceptions. + This caused some build issues with other build systems. This has been fixed + by replacing any exception throwing code with `abort()` and `assert()` + statements. + * Priority: Minor + * Issues: [CCBC-634](https://issues.couchbase.com/browse/CCBC-634) + +* Log vBucket configuration parsing failures. + This logs vBucket configuration parsing failures when a given config received + could not be parsed. Parsing failures include both JSON syntax errors as well + as improper fields or values within the config itself. + * Priority: Major + * Issues: [CCBC-647](https://issues.couchbase.com/browse/CCBC-647) + +* Allow per-request N1QL timeouts to exceed global timeouts. + This scans the `"timeout"` property of the N1QL request and if set, will + make the value of this property the timeout value for the request. A small + parser was implemented which converted the N1QL timeout values (`s`, `h`, etc.) + into microseconds. + * Priority: Major + * Issues: [CCBC-660](https://issues.couchbase.com/browse/CCBC-660) + +* Request configuration refresh when HTTP API redirects are received. + Redirects in Couchbase Server are sent when a node is about to exit the + cluster. We should take this as a sign to refresh the config since it indicates + a node is about to be removed. + * Priority: Major + * Issues: [CCBC-646](https://issues.couchbase.com/browse/CCBC-646) + + +## 2.5.3 (August 27 2015) + +* Add N1QL timeout feature. + This allows an independent timeout setting for N1QL. Previously this would + use the views timeout. + * Priority: Major + * Issues: [CCBC-631](https://issues.couchbase.com/browse/CCBC-631) + +* Add N1QL prepared statements. + This allows prepared statements to be used with N1QL. The library will + maintain an internal "prepared statement cache" which contains cached + responses for internal PREPARE requests. To use a prepared statement, an + application can simply set the `LCB_CMDN1QL_F_PREPCACHE` bit in the + `cmdflags` field within the `lcb_CMDN1QL` structure. All the rest is + handled internally within the library. + * Priority: Major + * Issues: [CCBC-633](https://issues.couchbase.com/browse/CCBC-633) + +## 2.5.2 (July 23 2015) + +* Fix off-by-one error when populating documents with pillowfight. + Previously pillowfight would only populate N-1 documents where N + is the (`-I`, `--num-items`) option. This has been fixed. + * Priority: Minor + +* Don't generate negative keys for pillowfight. + For certain option configurations, pillowfight would generate negative keys + (some keys were in the format of -nnnnnn). + * Priority: Minor + +* Allow in-progress N1QL requests to be cancelled. + This allows in-progress N1QL requests to be cancelled by adding a new API, + `lcb_n1ql_cancel()`. Invoking this function on an `lcb_N1QLHANDLE` handle + (obtained via an _out_ parameter in the command structure) will effectively + stop the request and stop delivering callbacks to the user. + * Priority: Major + * Issues: [CCBC-619](https://issues.couchbase.com/browse/CCBC-619) + +* Rename `lcb_SYNCTOKEN` to `lcb_MUTATION_TOKEN`. + This experimental (volatile) API has been renamed to "Mutation Token" to + better reflect naming conventions found in other client libraries. + * Priority: Minor + +* Implement histogram/timings information for N1QL queries via `cbc-n1qlback`. + This adds support for the (`-T`, `--timings`) option in the + `cbc-n1qlback` benchmark/stress-test utility. These timings reflect the + latency between issuing the query and the receipt of the first row of the + resultset. + * Priority: Major + * Issues: [CCBC-624](https://issues.couchbase.com/browse/CCBC-624) + +* Add (`-M`, `--mode`) option to `cbc-create` to allow for upsert, insert, etc. + This allows `cbc-create` to use the full gamut of storage options available + via the SDK by allowing an insert/upsert/replace mode as an argument to the + new `--mode` option. `append` and `prepend` are now also provided as options, + though not documented. + * Priority: Major + * Issues: [CCBC-625](https://issues.couchbase.com/browse/CCBC-625) + +* Support `CBC_CONFIG` environment variable for command line tools. + This variable specifies a path to an alternate `cbcrc` file which may be + used to provide cluster/bucket settings. This new option allows multiple + configurations to coexist, without forcing any one of them to be inside the + user's home directory. + * Priority: Minor + * Issues: [CCBC-626](https://issues.couchbase.com/browse/CCBC-626) + + +## 2.5.1 (June 17 2015) + +Bug fixes and improvements in 2.5.1 + +* Fix hanging in durability operations if node is not present and constraints + include failed node. This condition may be triggered when only a single node + remains in the broadcast probe and a command sent to it could not be scheduled. + A symptom of this bug was durability operations 'hanging' + * Priority: Major + * Issues: [CCBC-607](http://issues.couchbase.com/browse/CCBC-607) + +* Improved handling of topology changes when non-data (N1QL, Index) nodes are + part of the cluster. This fixes some issues (mainly crashes) when non-data + nodes are found inside the cluster during a topology change. While the library + since version 2.4.8 was able to handle initial bootstrapping with non-data + nodes, it would still crash when such nodes were encountered during + configuration changes. + * Priority: Major + * Issues: [CCBC-609](http://issues.couchbase.com/browse/CCBC-609), + [CCBC-612](http://issues.couchbase.com/browse/CCBC-612) + +* Improved random host selection algorithm for REST services + This new algorithm ensures that the distribution is even among all _eligible_ + nodes for a given service. The old algorithm would only distribute evenly when + the assumption that all nodes contained the same services were true. However + this assumption is no longer necessarily true with Couchbase 4.0. In this case + the algorithm ensures that the random selection inspects only the pool of + nodes which are known to have a given service enabled. + * Priority: Major + * Issues: [CCBC-611](http://issues.couchbase.com/browse/CCBC-611) + +* Ensure ketama/Memcached-bucket hashing works correctly when non-data nodes + are part of the cluster. In previous versions, ketama hashing would incorrectly + consider all nodes as candidates for keys, which would result in some items + being routed to non-data nodes, resulting in odd errors and inaccessible + data. This is only an issue for the still-unreleased Couchbase 4.0. + * Priority: Major + * Issues: [CCBC-613](http://issues.couchbase.com/browse/CCBC-613) + +* Set `TCP_NODELAY` as a server side option, if it's enabled on the client. + This uses the `HELLO` protocol functionality to enable this feature, if + this feature is also enabled on the client (enabled by default). + + +New features in 2.5.1 + +* Add `cmake/configure` option for enabling the embedding of the libevent + plugin. This option, named `--enable-embedded-libevent-plugin`, will cause + the plugin to be linked in with the core library (_libcouchbase_) rather + than built as its own object + * Priority: Minor + +* Add new combined "Store-with-durability" operation. This new API, called + `lcb_storedur3()` allows specifying the storage input options as well as + the associated durability options in a single command. Likewise, the status + of the operation (including durability) is returned in the operation's + callback. + * Priority: Major + * Issues: [CCBC-616](http://issues.couchbase.com/browse/CCBC-616) + + +## 2.5.0 (May 12 2015) + +This change in the major version number signals the addition of new features +for Couchbase Server 4.0; most of the actual new functionality for Couchbase +4.0 has already been included (incrementally) in prior 2.4.x versions. The +new 2.5 version is built on the 2.4.x codebase. + +* Add `cbc-n1qlback` - a simple benchmark for N1QL queries. This functions + by executing a line-delimited file containing query bodies using multiple + threads if possible. + * Priority: Major + * Issues: [CCBC-604](http://issues.couchbase.com/browse/CCBC-604) + +* `TCP_NODELAY` functionality has now been placed into effect. This + functionality was nominally present in prior versions, but would not work + because of a typo. + * Priority: Minor + +* Add 'tick' or 'pump' mode for I/O + As an alternative to `lcb_wait()`, applications may call `lcb_tick_nowait()` + to incrementally perform (non-blocking) I/O. This may provide a performance + boost when batching/scheduling many operations. `lcb_wait()` itself must be + called to guarantee completion of all operations, and the `lcb_tick_nowait()` + functionality is only available on some I/O plugins. See the API docs for + more information. + * Priority: Major + * Issues: [CCBC-598](http://issues.couchbase.com/browse/CCBC-598) + +* Allow "console logger" to log to a file + As a convenience, it is now possible to direct the library to write to + a log file rather than standard error. This is possible using the + `LCB_CNTL_CONLOGGER_FP` (to programmatically set a `FILE*` value via + `lcb_cntl()`) or `console_log_file` to set the path of the file (which + will be overwritten) via `lcb_cntl_string()` or the connection string. + +* Make `lcb_N1QLPARAMS` repeatable/debuggable + This allows the `lcb_n1p_mkcmd()` call to be invoked multiple times without + actually modifying internal state. Previously calling this function twice + would result in corruption of the internal parameter state. In this version, + a new function, `lcb_n1p_encode()` has been added (which `lcb_n1p_mkcmd()` + wraps) which may be used to inspect the encoded form of the query. + +## 2.4.9 (April 14 2015) + +* Disable HTTP provider when any CCCP config is received. + This makes the assumption that CCCP will always be available if even a + single node provides an HTTP configuration. This change may break some + corner-case upgrade scenarios from version 2.2 to 2.5 where a newly added + 2.5 node is subsequently removed. + * Priority: Major + * Issues: [CCBC-526](http://issues.couchbase.com/browse/CCBC-526), + [CCBC-589](http://issues.couchbase.com/browse/CCBC-589) + +* Fix additional missing defines for UV's `EAI_*` symbols + This was not entirely fixed in 2.4.8, since some undefined macros still + remained. + * Priority: Major + * Issues: [CCBC-596](http://issues.couchbase.com/browse/CCBC-596) + +* Make connection string timeout parameters (e.g. `operation_timeout`) always + specify seconds; this will no longer require the decimal point to be used, + but will break any prior usages of this value for microseconds. + * Priority: Minor + * Issues: [CCBC-597](http://issues.couchbase.com/browse/CCBC-597) + +* Add `cbc n1ql` subcommand, which executes N1QL queries. + This subcommand is still a bit rough around the edges, mainly because of + server-side support for "pretty=false" (which makes the rows display rather + weirdly). + * Priority: Major + * Issues: [CCBC-595](http://issues.couchbase.com/browse/CCBC-595) + +* Allow usage of `-D` option in `cbc` and `cbc-pillowfight` tools. + This flag allows specifying connection string options in a more + concise form on the commandline. The `-D` option may be specified + multiple times in the form of `-Doption=value`. + * Priority: Minor + +* Interpret `couchbase://host:8091` connection string as `couchbase://host` + Previously the library would treat `8091` as a memcached port. While technically + correct according to the connection string semantics, would often be a + source of confusion for users migrating from older versions of the library + (or other SDKs) when using the form `http://host:8091`. A special provision + is thus made for such a cas. + * Priority: Major + * Issues: [CCBC-599](http://issues.couchbase.com/browse/CCBC-599) + +* Implement enhanced durability using sequence numbers. + This feature is available in Couchbase 4.0, and uses sequence numbers + (optionally specified in the response packet of each mutation). + sequence-based durability constraints help resolve some ambiguity in + the case of checking the durability of items which have since been + mutated, or in the case of a cluster failover. Using this functionality + requires the `LCB_CNTL_FETCH_SYNCTOKENS` (or `fetch_synctokens`) and the + `LCB_CNTL_DURABILITY_SYNCTOKENS` (or `dur_synctokens`) + settings to be enabled (using `lcb_cntl()` or `lcb_cntl_string()`, or + in the connection string). Enabling `LCB_CNTL_FETCH_SYNCTOKENS` will + cause mutation response packets from the server to return an additional + 16 bytes of sequence data, and enabling `LCB_CNTL_DURABILITY_SYNCTOKENS` + will cause `lcb_durability_poll()` to transparently use this information + (rather than the CAS) to check for persistence/replication. + **Only available in Couchbase 4.0**. As a result of this feature, much + of the durability subsystem itself has been rewritten, making durability + overall more performant, even for CAS-based durability. + * Priority: Major + * Issues: [CCBC-569](http://issues.couchbase.com/browse/CCBC-569) + +* Add `lcb_version_g` extern symbol as alternative to `lcb_get_version()`. + This symbol is an extern global which allows simple runtime checking of + the library version. This is more convenient than `lcb_get_version()` as + it avoids the requirement to create a temporary variable on the stack + (`lcb_get_version()` returns a string, and requires an `lcb_U32` pointer + as its first argument to get the actual numeric version). + * Priority: Minor + + +## 2.4.8 (Mar. 8 2015) + +* Retry next nodes on initial bootstrap, even if first node says bucket does + not exist (or auth error), as this might be a recently removed node + * Priority: Major + * Issues: [CCBC-577](http://issues.couchbase.com/browse/CCBC-577) + +* The `cbc` and `cbc-pillowfight` binaries on Windows are now distributed + in both _release_ and _debug_ variants. Previously they would be clobbered + by one or the other depending on the build host. This fixes some issues in + performance and dependency resolution when using these libraries. + * Priority: Minor + * Issues: [CCBC-581](http://issues.couchbase.com/browse/CCBC-581) + +* Provide Read-Only config cache mode. In this mode the configuration cache + file is read but never updated. Additionally, a missing file in this mode + results in a hard error. + * Priority: Major + * Issues: [CCBC-584](http://issues.couchbase.com/browse/CCBC-584) + +* Keep vBucket heuristic guesses for limited periods of time. + This will allow previously-learned vBucket master locations to persist + over a configuration change, providing these changes were discovered + recently. This allows the reduction of not-my-vbucket responses while + allowing new configs to overwrite our heuristic info, if the heuristic is + too old. + * Priority: Major + +* Fix potential crashes in get-with-replica (`lcb_rget3`, `lcb_get_replica`) + when there are no replicas available, or if there is an error in retrieving + from one of the replicas. + * Priority: Major + * Issues: [CCBC-586](http://issues.couchbase.com/browse/CCBC-586) + +* Do not wait between not-my-vbucket retries + This behavior restores the pre 2.4.0 behavior of retrying not-my-vbucket + responses, with a more intelligent retry/rotation algorithm (see the + release note about "vbucket map heuristics"). Previously a wait-time + was introduced because of potential busy loops in retrying to the same + node. The `LCB_CNTL_RETRY_NMV_IMM` setting can be used to disable this + functionality (by disabling it, i.e. setting it to 0). This may also be + disabled in the connection string via `retry_nmv_imm=0`. + * Priority: Major + * Issues: [CCBC-588](http://issues.couchbase.com/browse/CCBC-588) + +* Fix compilation error with UV when `EAI_BADHINTS` is not defined in the + system. This is primarily an issue with newer UV versions and some versions + of Linux + * Priority: Major + * Issues: [CCBC-590](http://issues.couchbase.com/browse/CCBC-590) + +* Allow means to disable C++ behavior on public library structures, allowing + them to be initialized via C-style static initializers. + This allows the zeroing of structures such as `lcb_get_cmd_t cmd = { 0 }`, + which would ordinarily fail under C++ compilation because of that structure + having a defined C++ constructor. Applications can take advantage of this + feature by defining the `LCB_NO_DEPR_CXX_CTORS` preprocessor macro when + compiling. + * Priority: Major + * Issues: [CCBC-591](http://issues.couchbase.com/browse/CCBC-591) + +* Fix some bugs in timing behavior (`lcb_enable_timings`). Timings between + 1000-2000ms are now reported accurately. Additionally for more common + handling, second timing ranges (between 1-9s) are reported in ms range + (i.e. timings of 4 seconds are reported as 3000-4000ms ). + * Priority: Minor + * Issues: [CCBC-582](http://issues.couchbase.com/browse/CCBC-582) + + +## 2.4.7 (Feb. 17 2015) + +* Fix SSL connection failures with `SSL_UNDEFINED_CONST_FUNCTION`. + This would sometimes cause failures during early connection/negotiation + stages. + * Priority: Major + * Issues: [CCBC-571](http://issues.couchbase.com/browse/CCBC-571) + +* Add _experimental_ support for N1QL queries. + This adds support for contacting N1QL endpoints and retrieving their + result sets. The support at both the client and server components is + still a work in progress. + The API is similar to the view api (see `lcb_view_query()`) added in + version 2.4.6. See details in `` + * Priority: Major + * Issues: [CCBC-572](http://issues.couchbase.com/browse/CCBC-572) + +* Add _experimental_ support for geospatial view queries. + GeoSpatial views are available as an experimental feature in the + current releases of the server. This will soon be offered as a + stable feature in future releases. + Applications may now use the `lcb_RESPVIEWQUERY::geometry` field + and the `LCB_CMDVIEWQUERY_F_SPATIAL` to utilize geospatial views. + * Priority: Major + * Issues: [CCBC-573](http://issues.couchbase.com/browse/CCBC-573) + +* Fix memory leak for retried commands. + In cases where a given command needs to be retried more than once, a + memory leak was fixed in which the previous instance of the pacekt was + not properly freed. + * Priority: Major + * Issues: [CCBC-574](http://issues.couchbase.com/browse/CCBC-574) + +## 2.4.6 (January 20 2015) + +* Fix floating point exception on OS X. + A floating point exception would sometimes be thrown on OS X sytems due + to bad time structure initialization. The installation provided with + homebrew for 2.4.5 fixed this issue. This is completely fixed in 2.4.6 + Priority: Major + +* Improve warning messages when using deprecated options in `cbc`. + This provides less ambiguous help messages when using deprecated options, + showing a full and complete example for proper usage (when possible). + * Priority: Minor + * Issues: [CCBC-562](http://issues.couchbase.com/browse/CCBC-562) + +* Add patch/micro version to DLL information on Windows. + This lets the user see the exact version of the library on windows (via + right clicking on the DLL and inspecting the details). Previously this + information contained only the major and minor versions. + * Priority: Minor + * Issues: [CCBC-563](http://issues.couchbase.com/browse/CCBC-563) + +* Provide _pkgconfig_ (`.pc`) file with installation. + This may help third party applications and libraries link against libcouchbase + in some environments. + +* Provide one-off `unsafe_optimize` option for connection string/`lcb_cntl`. + This provides a shorter way to enable some potentially unsafe optimizations + which may make the client perform better in some scenarios. + * Priority: Minor + +* Allow prompting for password in `cbc`. + The `cbc` and `cbc-pillowfight` utilities will now securely prompt for the + password if the password specified on the commandline is a hyphen (`-`). + * Priority: Minor + * Issues: [CCBC-565](http://issues.couchbase.com/browse/CCBC-565) + +* Fix timeouts in some durability when not all replicas are online. + The library will now fail the operation with `LCB_DURABILITY_ETOOMANY` + rather than allowing the operation to timeout. + * Priority: Major + * Issues: [CCBC-560](http://issues.couchbase.com/browse/CCBC-560) + +* Add high level row-based view functionality. + This adds a new API (currently considered _volatile_) which allows + intelligently querying views. This builds atop the basic HTTP + interface, and exposes a row-based callback API based upon + streaming JSON parsing. The new API is defined in ``. + This API will become more stable over time. + * Priority: Major + * Issues: [CCBC-100](http://issues.couchbase.com/browse/CCBC-100) + +* Parse configuration service locations for experimental services + This exposes the N1QL and indexing services via the _lcbvb_ API. See + `libcouchbase/vbucket.h` for more information. + +## 2.4.5 (December 17 2014) + +* Fix `pillowfight` ignoring `set-ratio` values above 50 + The program would ignore these values and act as if 100 was specified, + thus never issuing any GET operations + * Priority: Minor + * Issues: [CCBC-550](http://couchbase.com/issues/browse/CCBC-550) + +* Building with autotools is no longer supported. + If building the library from source, you _must_ use + [CMake](http://cmake.org/download) version 2.8.9 or greater. If unfamiliar + with CMake, the README describes the process. Included also is a top-level + script called `configure.pl` which functions with an autoconf-like interface. + * Priority: Major + +* Fix customized IOPS crashes in some usage cases + This fixes scenarios where applications assume that the built-in IOPS version + is 0, and attempt to "Subclass" the IOPS structure. The internal version of + the library structure is now 3, with some extra heuristics in place to ensure + that the older code will still function. + This issue was most visible in the Python SDK when using the gevent or Twisted + plugins. + This issue was first introduced with version 2.4.4 + * Priority: Critical + * Issues: [CCBC-557](http://couchbase.com/issues/browse/CCBC-557) + +* Allow raw `certpath` to be passed without need for percent-encoding (in most cases) + This allows for a common pattern fo passing `certpath` in the connection string as + a raw, unencoded path. This allows a user to do + `couchbases://host/bucket?certpath=/foo/bar/baz`. + +* Fix missing installation UV plugin headers and source + In 2.4.4 this was accidentally left out, and would only be installed if the plugin + itself was built and installed. This affected building the Node.JS SDK using an + existing libcouchbase install. + * Priority: Major + * Issues: [CCBC-558](http://couchbase.com/issues/browse/CCBC-558) + +## 2.4.4 (Nov. 19 2014) + +* Detect disconnected pooled sockets + This allows the connection pool to detect dead sockets which were closed + by a server when they were idle. Sometimes servers will close connections + to open idle sockets to save resources, or because of bugs in their + implementations. + This will fix some issues experienced with views where queries would + randomly fail with `LCB_NETWORK_ERROR` or `LCB_ESOCKSHUTDOWN`, by first + checking if the socket is alive before returning it back to the library's + core. + Note that the `libuv` plugin does not implement this functionality yet. + * Priority: Critical + * Issues: [CCBC-546](http://couchbase.com/issues/browse/CCBC-546) + +* Fix _pillowfight_ `--min-size` bug + This fixes a bug where pillowfight would sometimes compare the `min-size` + option to an uninitialized `max-size` option and round it down to that + value; then would set the `max-size` option. + * Priority: Major + * Issues: [CCBC-542](http://couchbase.com/issues/browse/CCBC-542) + +* Don't ignore `LCB_CNTL_DURABILITY_INTERVAL` + Fix a bug where this interval would be ignored, if modified by the user; always + reverting to 100ms. + * Priority: Major + * Issues: [CCBC-543](http://couchbase.com/issues/browse/CCBC-543) + +* Fix memory leak with HTTP requests using a request body + Requests (such as `PUT`, `POST`, etc) which contained a request body + would cause a memory leak as the library forgot to free them when the + request object was destroyed. + * Priority: Major + * Issues: [CCBC-538](http://couchbase.com/issues/browse/CCBC-538) + +* Fix errneous `LCB_SUCCESS` return when passed duplicate keys to + `lcb_durability_poll()`. This would cause applications to mistakenly wait + for a callback to arrive, when in fact the command had failed. + * Priority: Major + * Issues: [CCBC-536](http://couchbase.com/issues/browse/CCBC-535) + +* Add option to preserve vbucket ownership heuristics across config updates + This allows the learned configuration settings to persist between configuration + updates. The default behavior (up to, and including this change) is to + discard any "learned" configuration in favor of the explicitly new config + passed to the server. This new option allows this information to be persisted + when a new configuration update is received. This behavior is considered + experimental, and is primarily intended to reduce the time it takes for the + client to relearn the current node (which is typically under 1-2 seconds). + * Priority: Minor + * Issues: [CCBC-530](http://couchbase.com/issues/browse/CCBC-530) + +* Relocate memcached packets on topology changes for memcached buckets + This enhances the behavior of the client when operating with a memcached + bucket during a topology change. Previously the library would not relocate + packets to new servers, resulting in errors for items which were now + mapped to wrong nodes. The new behavior remaps the key to the new server + using the updated ketama hashing. Note that as a current restriction, the + remapping will be performed based on the key of the item, not any `hashkey` + parameter being employed. + * Priority: Major + * Issues: [CCBC-331](http://couchbase.com/issues/browse/CCBC-331) + +* Return error if ignored/conflicting options are found + This changes the behavior of the library to throw an error if a specific + option field was filled in which did not make sense for a given command, for + example, specifying a `cas` value using a `LCB_ADD` operation with `lcb_store`. + * Priority: Major + * Issues: [CCBC-529](http://couchbase.com/issues/browse/CCBC-529) + +* Fix issue when sending out large _OBSERVE_ command. + This would cause a partial command to be sent out if the size of the output + packet was greater than 512 bytes. This has been fixed by dynamically growing + the output buffer for _OBSERVE_ + * Priority: Minor + * Issues: [CCBC-528](http://couchbase.com/issues/browse/CCBC-528) + +* Fix spurious timeouts when using `lcb_durability_poll` + This fixes an issue where sometimes the durability poll operation would + prematurely time out. + * Priority: Major + * Issues: [CCBC-527](http://couchbase.com/issues/browse/CCBC-527) + +## 2.4.3 (Oct. 21 2014) + +* Disable support for SSLv3 + This works around the _POODLE_ SSLv3 vulnerability by disabling support for + anything below TLSv1. + + * Priority: Critical + * Issues: [CCBC-523](http://couchbase.com/issues/browse/CCBC-523) + +* Pillowfight enhancements + Several behavior changes were made to pillowfight in this version. These are: + * The `-l` or `-c -1` option is in effect by default. This means that by + default `pillowfight` will run an infinite number of cycles. The previous + behavior was to default to a single cycle, requiring an explicit `--loop` + to ensure the workload ran for a considerable amount of time. + + * When multiple threads are used, the workload is divided among the threads, + thus making it that each thread only operates on a subset of the data. + + * A `--sequential` option has been added to allow the workload to operate + in _sequence_ on the total number of items. This is useful when wishing to + load a bucket with many items. + + * A `--start-at` option has been added to allow the workload to specify an + alternate range of keys; effectively allowing resumption of a previous + run. The `--start-at` flag allows to specify the lower bound number which + will be used to generate keys. Thus a `--num-items=20000` and a + `--start-at=10000` will generate keys from 10000 through 30000. + + * The _population_ phase has now been merged with the general workload + implementation. This means that all worker threads will participate in + the population phase. The previous behavior limited the populate phase to + a single thread. + + * If `stdout` is detected to be a terminal, a simple "OPS/SEC" meter will + periodically write the estimated throughput to the screen. + +* Fix memory leak when using large read buffers + In the case where large read buffers are used (and the `iovec` elements + becomes sizable, the library may end up incorrectly caching some memory + blocks for future use. This fix makes the blocks be cached at the allocator + level, so that they are properly (re) utilized. + + * Priority: Major + * Issues: [CCBC-519](http://couchbase.com/issue/browse/CCBC-519) + +* Use forward map (and other heuristics) to get a next node for an item after + a not-my-vbucket reply. Since the server (see bug attached) does not always + guarantee that a given config is the most _correct_, the client must do some + guesswork in order to properly map a node when it gets a not-my-vbucket; + especially if the config says that the node is the correct one. + + * Priority: Major + * Issues: [MB-12268](http://couchbase.com/issues/browse/MB-12268) + +## 2.4.2 (Sep. 23 2014) + +* Mark the `hashkey` fields as being _volatile_. + Usage of this field is not supported in many cluster systems and is thus not + supported functionality. It exists primarily as a legacy from an older API + * Priority: Major + * Issues: [CCBC-508](http://couchbase.com/issues/browse/CCBC-508) + +* Add "key stats" mode to `lcb_CMDDSTATS`. + This adds an additional key stats mode to the `lcb_stats3()` API + which interprets the `key` field as being a document ID for which + information (such as expiry, status) should be retrieved, rather + than a system statistics key. Similar functionality already exists + in the Java client library as `getKeyStats()`. In addition to this + feature, a `cbc stats --keystats` option is also provided to employ + this functionality from the command line. + * Priority: Major + * Issues: [CCBC-318](http://issues.couchbase.com/browse/CCBC-318) + +* Add more details about replica nodes in the `cbc hash` command. + * Priority: Minor + * Issues: [CCBC-504](http://couchbase.com/issues/browse/CCBC-504) + +* Add `lcb_cntl()` setting to retrieve bucket name. + Previously the library did not have a means by which the bucket name + could be retrieved. Using the `LCB_CNTL_BUCKETNAME` setting, the bucket + name will now be returned. + * Priority: Major + * Issues: [CCBC-502](http://issues.couchbase.com/browse/CCBC-502) + +## 2.4.1 + + +* Implement `mcflush` subcommand for `cbc`. This was removed in the cbc + rewrite as the previous `flush` command. + * Priority: Minor + * Issues: [CCBC-486](http://couchbase.com/issues/browse/CCBC-486) + + +* Requests issued to an invalid replica via `lcb_get_replica()` should fail + with the `LCB_NO_MATCHING_SERVER_CODE`. Previously this sometimes went + through due to an incorrect bounds checking in the `lcbvb_vbreplica()` + function. + * Priority: Major + * Issues: [CCBC-488](http://couchbase.com/issues/browse/CCBC-488) + + +* Fixed a memory leak in `lcb_get_replica()` when the operation would fail. + * Priority: Major + * Issues: [CCBC-489](http://couchbase.com/issues/browse/CCBC-489) + [CCBC-490](http://couchbase.com/issues/browse/CCBC-490) + + + +* Fix memory leak in `lcb_sched_fail()` when extended commands are in the + pipeline + * Priority: Major + * Issues: [CCBC-474](http://couchbase.com/issues/browse/CCBC-474) + + + +* Provide `lcb_dump()` function call to dump state information about + a client handle. The function call itself is currently marked as + volatile and the output format is very much likely to change. + * Priority: Minor + * Issues: [CCBC-491](http://couchbase.com/issues/browse/CCBC-490) + + +* Fix `ratio` argument in `cbc-pillowfight`. This ensures that the + `ratio` argument will truly determine the ratio of gets to sets. + * Priority: Minor + +* Fix crash when HTTP request is retried. This may take place during topology + changes + * Priority: Major + * Issues: [CCBC-497](http://couchbase.com/issues/browse/CCBC-497) + +* Allow simple host-port string in connection string, giving it an implicit + `http://` scheme. This allows easier backwards compatibility with some + application + * Priority: Minor + * Issues: [CCBC-500](http://couchbase.com/issues/browse/CCBC-500) + +* Update some SSL options to better reflect server 3.0 functionality + The old `capath` option has been renamed to `certpath` to indicate that the + path is not to the signing authority, but to the self-signed server certificate + generated by the server itself. Additionally the `no_verify` option has been + hidden. + * Priority: Major + * Issues: [CCBC-501](http://couchbase.com/issues/browse/CCBC-501) + +## 2.4.0 GA + +* [major] Attempt to retry items that are mapped to a non-existent node in + a degraded cluster. Rather than returning `LCB_NO_MATCHING_SERVER` the + behavior should be to wait for the item to succeed and attempt to fetch + a new cluster configuration. + + In order to control how such 'orphaned' commands are handled, a new value + has been added to the `lcb_RETRYMODEOPTS` called `LCB_RETRY_ON_MISSINGNODE` + which dictates how commands should be rescheduled if the associated vbucket + has no master. The default is to retry the command until it times out, but + by setting this value to `0` (See `LCB_CNTL_RETRYMODE`) it may only be + attempted once, causing 'fail fast' behavior in such a case. + +* [major] Don't throttle config requests based on initial file-based config. + This allows the client to quickly recover from a stale config cache without + waiting for the `LCB_CNTL_CONFDELAY_THRESH` interval to elapse. Prior to this + fix, a client would appear to "not recover" if bootstrapping from a stale cache. + In reality the client would eventually recover but was waiting for the delay + threshold to elapse. + +* [major] Ignore `NOT_MY_VBUCKET` config payloads if CCCP provider is disabled. + This allows the client to circumvent any possible bugs in the CCCP response + payload and rely entirely on the HTTP config. It also allows 'rewriting' + proxies like confsed to function. + +## 2.4.0-beta + +* [major] Better error reporting for SSL failures. + This adds new error codes (`LCB_SSL_ERROR`, `LCB_SSL_CANTVERIFY`) + which are returned on initialization and verification failures + respectively. + +* [minor] Communication via legacy memcached servers is possible + by using the `memcached://` scheme in the connection string. + +* [minor] Environment variables understood by the library are now + documented in their own section. + +* [major] Add `lcb_get_node()` function to retrieve addresses for + various nodes in the cluster. This deprecates the `lcb_get_host()`, + `lcb_get_port()` and `lcb_get_server_list()` functions as they are + constrained to only return information about the administrative API. + The new function is configurable to return information about various + ports. + +* [major] The `dsn` field in the `lcb_create_st` structure has been + renamed to `connstr`. + +* [major] An HTTP request which has followed redirects will cause the + `lcb_wait()` function to never return. This bug was introduced in + 2.4.0-DP1 and has now been fixed. + +* [minor] `lcb_get_server_list()` function now returns updated information + from the current cluster configuration. Previously this would only return + a node from the list specified during initial creation. + +* [minor] Provide additional error classifiers. Two error classifiers have + been added, they are: + + * `LCB_ERRTYPE_SRVLOAD` which indicates that the server is likely under high load. + * `LCB_ERRTYPE_SRVGEN` which indicates that the error is a direct reply from the + server. This code can help distinguish between client and server generated + return codes. + +* [major] Provide HTTP keepalive and connection pooling for HTTP requests. + This allows the client to reuse an HTTP connection for multiple requests + rather than creating a new connection and closing it for each operation. + + The functionality may be controlled via the `LCB_CNTL_HTTP_POOLSIZE` setting + which limits how many open connections (per server) to maintain inside the + client. Setting this value to `0` will disable pooling and restore old + behavior. + +* [major] Properly schedule next invocations for retry queue. A bug was introduced + in 2.4.0-dp1 which would cause the next tick callback to be invoked in what is + effectively a busy loop. This would be reflected in higher CPU load and less + throughput during topology changes. + +* [major] Return error if empty key is passed to an operation. Empty keys will + cause the server to drop the connection. + The error code returned is the newly added `LCB_EMPTY_KEY` + +* [minor] Provide setting to disable refreshing the configuration when an HTTP + API error is encountered (from one of the HTTP callback functions). This + adds the `LCB_CNTL_HTTP_REFRESH_CONFIG_ON_ERROR` setting. + +* [major] Fix bug where the CCCP provider may prematurely fail, activating the + HTTP provider + + +## 2.4.0-dp1 (2014-06-18) + + +**Changes affecting older APIs** + +* [minor] Make `run_event_loop` and `stop_event_loop` private. + These functions may no longer be used from within an application to + start/stop the event loop. `lcb_wait()` and `lcb_wait3()` should be + used instead. + +* [major] Deprecate the `lcb_set_XXX` functions. `lcb_set_timeout` + and some other calls have been deprecated in favor of the `lcb_cntl()` + interface. These functions will still work but will cause the compiler + to print a deprecation warning. + +* [minor] `lcb_socket_t` is typedefed to a `DWORD` on windows. In + previous versions this was an `int`. + +* [minor] Connecting to a standalone memcached instance is currently no longer + supported. + +* [major] `lcb_set_error_callback()` has been deprecated. Applications should + use the new `lcb_set_bootstrap_callback()` and/or operation callbacks + to determine success/failure status. + +* [major] `lcb_get_last_error()` has been deprecated. Error information is always + returned in the operation callback + +* [major] Disable the sending of `GETQ` packets. The format of this command + is cumbersome to deal with and in most uses cases is actually slightly + _less_ efficient on the network. Note that this does not change the API + of the actual `lcb_get()` call, but simply changes the format of the + packets sent over the wire. + +* [major] The IOPS API has been changed. This is considered volatile interface + and may subsequently change in the future as well. + +**New APIs added in 2.4.0 extending existing functionality** + +These changes extend existing features with enhanced APIs + +* [major] Additional APIs for `lcb_cntl()`. These consist of helper functions + to make it easier to use simple types or strings rather than pointers, if + possible. These functions are `lcb_cntl_string()`, `lcb_cntl_setu32()` and + `lcb_cntl_getu32()` + +* [minor] Provide extended version of `lcb_wait()`. + A new function called `lcb_wait3()` has been added which offers additional + options with respect to running the event loop. Specifically it offers to + bypass the check for pending operations which `lcb_wait()` executes. This + is both more performant and allows us to wait for operations which are + not explicitly scheduled. + +* [major] Provide API to request a configuration refresh. + Sometimes it is necessary to force the client to request a new configuration, + for example in certain failover conditions. A new API called `lcb_config_refresh()` + has been added, and should be used in conjunction with `lcb_wait3()`. + +* [major] Provide bootstrapping notification callback + This provides an explicit `lcb_set_bootstrap_callback()` to definitively + determine whether the client has received its initial configuration (and + thus may now start performing operations) or whether it failed (and thus + must be reinitialized). This deprecates the common use case of + `lcb_set_error_callback()`. + +* [major] New vBucket interface/API. This API is used internally and exposed + as _volatile_ inside the public header files. It provides extended features, + a more concise API, and is compatible with the upcoming Couchbase 3.0 config + format. Note that file-based configuration caches written by this version of + the library are incompatible with previous versions, however this version may + read caches generated by previous versions. This is because this version generates + a stripped-down version of the "terse" configuration style. + +* [major] Extended detailed error codes. + These error codes expose more detail about the `NETWORK_ERROR` and + `CONNECT_ERROR` codes returned by previous versions of the library. The extended + codes are not returned by default and must be explicitly enabled in order to + retain backwards compatibility with applications which rely on the older + error codes. + + +**New Features in 2.4.0** + +* [major] Connection Strings (aka "dsn") feature for instance creation. This adds a new + version of the `lcb_create_st` structure which is passed a URI-like string + rather than a semicolon-delimited list of hosts. This string is used to + provide options and the list of hosts that the library should connect to. + For example, `couchbase://default/localhost&compression=off` + +* [major] SSL transport support for Couchbase 3.0 Enterprise. + Couchbase 3.0 enterprise features the ability to encrypt communications + between the client and the server using the SSL protocol. SSL protocol + support in _libcouchbase_. + +* [major] Retry queue for failed operations. The retry queue is used + as a place to place operations which have failed internally and which + should be retried within a certain amount of time. This also provides + options on which commands should be retried. + +* [minor] Compression/JSON flag (aka Datatype) support + This adds support for a future feature of Couchbase server which will + feature transparent compression. This feature also allows the server + to signal to the library if a document is JSON or not. The compression + feature may be disabled at compile-time, and may also be modified at + runtime by setting `compression=off` in either the connection string + or via `lcb_cntl_setstring(instance, "compression", "off")` + +* [major] Experimental _scheduling_ API. This API replaces most of the older + operation APIs with a scheduling API. These APIs are called with one + command at a time and insert the resultant packet into a pipeline. The + user may "schedule" the commands or "fail" the pipeline if a certain + request has failed to be scheduled. + + This API also provides a common ABI header for commands so that they may + easily be used via type-punning, or wrapped as a class hierarchy in C++. + + This API is currently considered volatile but will be the basis of the + upcoming libcouchbase 3.0 API. The header file is `` + +* [major] Raw memcached packets may be sent to the library and have a callback + invoked when their responses have been received. + This adds an `lcb_pktfwd3()` API. This requires the new scheduling API. + + +**Bug Fixes in 2.4.0** + +* [major] _select_ plugin may endlessly loop in some cases + The plugin may loop if there was a long timeout from the + future . + +* [major] Do not break TCP connections on topology changes unless ejected from + cluster. This ensures that nodes which are still part of the cluster have their + TCP connections remain in tact despite being shifted in their server index values. + Packets which have been sent to the wrong vBucket are silently ignored and + rescheduled to their appropriate destination. This decreases load significantly + on the client, network, and cluster during topology changes. + +* [major] Use new-style "Terse" URI format when requesting a configuration over HTTP. + This uses the HTTP configuration format over the new `/pools/default/bs/default` + rather than the older `/pools/default/bucketsStreaming/default` form. The former + form is much more efficient on the cluster side. If the new URI form is not + supported (i.e. the server responds with an HTTP 404) the older form will be + used instead. You may modify this behavior by setting the `LCB_CNTL_HTCONFIG_URLTYPE` + setting via `lcb_cntl()`. + +* [minor] The `cmake/configure` script now accepts the `LDFLAGS`, `CPPFLAGS`, `CFLAGS`, + `CXXFLAGS`, `CC`, and `CXX` settings both within the environment _and_ the + commandline, so the forms of `CC=clang ./cmake/configure` and + `./cmake/configure CC=clang` are equivalent. + +* [minor] The `pillowfight` tool will now print latencies between 1-10ms in resolutions + of 100us. + + +**Metadata and Packaging Changes in 2.4.0** + +* [major] Use Doxygen for API documentation. + This replaces the _manpages_ for API documentation with Doxygen. Doxygen + is a free and portable documentation system which may be obtained from your + distribution or at [](http://doxygen.org). To generate the documentation + from the source tree, simply run `doxygen` from the source root directory. + To generate internal documentation, run `./docs/gen_internal_apidoc.sh`. + +* [major] Add interface attributes to all API calls + This properly documents all API calls with a certain API stability level + such as _committed_ (for stable APIs), _uncomitted_ for APIs which may, but + are not likely to change, and _volatile_ for APIs which are likely to be + changed or removed. + +* [major] Public header files have been reorganized + This changes the layout of the header files from previous versions. This should + not affect applications as applications should only ever include the main + `` file. + + the following files have been _removed_ from the + `` header directory: + + * `types.h` - Merged into other header files + * `arguments.h` - now a part of `couchbase.h` + * `callbacks.h` - now a part of `couchbase.h` + * `debug.h` - unused and obsolete + * `durability.h` - now a part of `couchbase.h` + * `behavior.h` - Merged into `deprecated.h` + * `sanitycheck.h` - Merged into `deprecated.h` + * `timings.h` - Part of `couchbase.h` + * `compat.h` - Part of `deprecated.h` + + The following files have been _added_ into the `` directory. + Unless otherwise noted, these files are included by ``: + + * `api3.h` - Volatile proposed 3.0 API. **Not included by default** + * `cxxwrap.h` - Contains the implementation for the deprecated C++ wrappers + * `deprecated.h` - Contains deprecated APIs + * `iops.h` - Contains the IO integration APIs + * `pktfwd.h` - Contains the packet forwarding API. **Not included by default** + * `vbucket.h` - Contains the vBucket mapping API. **Not included by default** + +* OpenSSL is now a base dependency for the library. This may be disabled at configure + time via `--enable-ssl=no`. See `./configure --help`. + +* Snappy compression library is bundled and optionally compiled. This is left out by + default as the configure script will search for a system installed `libsnappy`. + Snappy provides the compression feature needed for compressing and inflating data + between client and server. It may be disabled at compile-time via `--enable-snappy=no` + +* [minor] _libvbucket_ has been fully integrated into libcouchbase from the forked + _libvbucket_ package and, lives fully as part of the + library. The public vBucket API may be found in ``. + +* [minor] As an alternative to the cross-platform `lcb_uintNN_t` typedefs, a shorter + (and more standards compliant) alternative `lcb_UNN` typedefs are provided, thus + instead of `lcb_uint32_t` you may use `lcb_U32`. The full listing of cross platform + typdefs may be found inside `` + + + +## 2.3.1 (2014-05-08) + +* [major] CCBC-404: Segfault in `connmgr_invoke_request` + Occasionally a segmentation fault would happen when a connection was being + released as a result of a connection failure. This was because of invalid + list tracking. + +* [major] CCBC-395: Add `lcb_cntl()` interface for configuration cache + Configuration cache options may be set after instantiation using `lcb_cntl()` + with the new `LCB_CNTL_CONFIGCACHE` operation. The old-style `lcb_create_compat` + creation path is deprecated. + +* [major] CCBC-394: Get-with-replica occasionally crashes on Windows and UV + during topology changes. This was due to not allocating a buffer if one did + not exist. + +* [major] CCBC-392: ABI compatibility broken between 2.x and 2.3 for + `lcb_create_compat`. This has been fixed by symbol aliasing between versions. + Developers are recommended to use the `lcb_cntl()` API to set the + configuration cache, as specified in CCBC-395 + +* [major] CCBC-385: Failed assertion on get-with-replica when connection fails. + If a connection fails with a `CMD_GET_REPLICA` command still in the queue an + assertion failure will crash the library. This has been fixed by handling the + opcode in the `failout_single_request` function. + +* [major] CCBC-384: Unknown Winsock error codes crash application. This was fixed + by providing proper handlers for Winsock codes which were not explicitly + converted into their POSIX equivalents. + +* [major] CCBC-376: Fix memory leak in configuration parsing. A leak was + introduced in version 2.3.0 by not freeing the JSON pool structure. This has + been fixed in 2.3.1 + +* [minor] CCBC-370: `lcb_get_host` and `lcb_get_port` may return host-port + combinations from different servers. If multiple servers are listening on + different ports this may result in yielding an invalid endpoint by combining + the output from those two functions. This has been fixed in 2.3.1 by returning + the host and port from the first host, in lieu of a currently-connected REST + endpoint. + +* [minor] CCBC-368: Initial bootstrapping failure may mask `LCB_BUCKET_ENOENT` + calls with `LCB_ETIMEDOUT`. This has been fixed by not retrying configuration + retrieval if an explicit HTTP 404 code is received. Note that when using + bootstrap over memcached, a missing bucket may still be manifest as + `LCB_AUTH_ERROR`. + +* [minor] CCBC-367: Ensure `lcb_get_host` does not return `NULL` when the + associated `lcb_t` is of `LCB_TYPE_CLUSTER`. This would cause crashes in some + applications which relied on this function to not return `NULL`. + +* [major] CCBC-389: Fixed Spurious timeouts being delivered in asynchronous + use cases. + In applications which do not use `lcb_wait()` the library will potentially + time out commands internally triggering an erroneous configuration refresh. + While this issue would not end up failing operations it will cause unnecessary + network traffic for retrieving configurations. Applications using `lcb_wait()` + are not affected as that function resets the timeout handler. + +* [major] CCBC-332, CCBC-364: Compare configuration revision information + for memcached cluster bootstrap. Previously we would refresh the + configuration upon receipt + of any new configuration update from memcached. This is fixed in 2.3.1 where + the configuration will only be applied if it is deemed to be newer than the + current configuration. With memcached bootstrap this is only true if the + configuration's `rev` field is higher than the current one. + + +## 2.3.0 GA (2014-04-07) + +* [major] CCBC-152: Provide a master-only observe option. This adds a new + struct version to the `lcb_observe_cmd_t` which allows to select only the + master node. One can use this to efficiently check if the key exists (without + retrieving it). It also allows one to get the CAS of the item without fetching + it. + +* [major] CCBC-281: Fix partial scheduling during multi operations. Previously + the library would deliver spurious callbacks if multiple operations were + scheduled with a single command and one of the operations could not be mapped + to a server. This fixes this behavior and ensures that callbacks are only + invoked for items if the entire API call succeeded. + +* [major] CCBC-150: Multi-packet commands will no longer deliver spurious + callbacks on failure. Previously these commands would be relocated to the + same server during a configuration change, resulting in multiple callbacks + for the same command. In this case the client would think all the commands + had been completed, and when the next response arrived it would incorrectly + map it to a different request. + +* [minor] CCBC-327: Fix assumption of `vbucket_compare()` only returning if + a diff exists. This function actually returns a non-NULL pointer always + unless it cannot allocate more memory. This bug was introduced with the + _DP1_ release. + +* [minor] CCBC-326: Memcached buckets should use streaming config. This was + left unchecked in the _DP1_ release and has now been fixed. + +* [major] CCBC-351: Enhance performance for configuration parsing. In previous + versions receiving multiple configurations at once would cause CPU spikes on + slower systems. The configuration parser code has been optimized to alleviate + this issue. + +* [minor] CCBC-350: Provide `lcb_cntl()` API to retrieve the SCM changeset used + by the currently loaded binary. This is a more effective way to get the + revision as it does not depend on the specific headers the library was + compiled with. + +* [major] CCBC-340: Correctly parse `""`, `"0"` and `"1"` for environment + variables. In previous versions having the entry set to an empty string + or `0` would still be treated by the library as a true value for various + environment variables. This has been fixed so that clear "False" values + such as the empty string or 0 are treated as such. + + +## 2.3.0-dp1 (2014-02-04) + +* [major] CCBC-234: Implementation of + [Cluster Configuration Carrier Publication][cccp-wiki]. This is the new and + more efficient way to bootstrap from a cluster using the native memcached + protocol and is quicker than the previous HTTP bootstrap mechanism, dramatically + improving startup times and reducing load on the server. This feature is + available in server verions 2.5 and greater. The existing HTTP configuration is + still supported and will be employed as a fallback in the event that `CCCP` + is not supported. + + In conjunction with this, a new struct version has been added to the + `lcb_create_st` parameters structure for use with `lcb_create`. This allows + you to get more control over how the client is initialized: + + lcb_t instance; + struct lcb_create_st options; + lcb_config_transport_t enabled_transports = { + LCB_CONFIG_TRANSPORT_CCCP, + LCB_CONFIG_TRANSPORT_LIST_END + }; + + memset(&options, 0, sizeof(options)); + options.version = 2; + options.v.v2.mchosts = "example.com:11210"; + options.v.v2.transports = enabled_transports; + + lcb_error_t rc = lcb_create(&instance, &options); + if (rc != LCB_SUCCESS) { + fprintf(stderr, "Failed to create instance: %s\n", lcb_strerror(instance, rc)); + } + + The above snippet will configure a client to _always_ use the `CCCP` protocol + and never attempt to fall back on HTTP + + The CCCP implementation required a significant rewrite in how sockets were + created and re-used. Particularly, a connection pooling feature was implemented. + + Additionally, the `cbc` command now has an additional `-C` option which accepts + the preferred configuration mechanism to use. + +* [major] CCBC-305: Implement logging hooks. + + This improvements adds various levels of diagnostic logging with the library + itself. It may be utilized via the environment (by setting the `LCB_LOGLEVEL` + environment variable to a positive integer -- the higher the number the more + verbose the logging). + + Integrators may also use the logging API specified in `` + to proxy the library's logging messages into your own application. + + Current events logged include connection initialization, destruction, connection + pool management, configuration changes, and timeouts. + + By default the library is silent. + +* [major] CCBC-316: Allow per-node bootstrap/config timeouts. + This change allows more finer grained control over how long to wait per-node + to receive updated configuration info. This setting helps adjust the initial + and subsequent bootstrap processes to help ensure each node gets a slice of + time. + +* [major] CCBC-297: Handle spurious EWOULDBLOCK on UV/Win32 + This issue caused odd errors on Windows when large amounts of data + would be received on the socket. + +## 2.2.0 (2013-10-05) + +* [major] CCBC-169 Handle 302 redirects in HTTP (views, administrative + requests). By default the library will follow up to three redirects. + Once the limit reached the request will be terminated with code + `LCB_TOO_MANY_REDIRECTS`. Limit is configurable through + `LCB_CNTL_MAX_REDIRECTS`. If set to -1, it will disable redirect + limit. + + int new_value = 5; + lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_MAX_REDIRECTS, &new_value); + +* [major] CCBC-243 Replace isasl with cbsasl, the latter has + implemented both PLAIN and CRAM-MD5 authentication mechanisms. + + * `LCB_CNTL_MEMDNODE_INFO` command updated to include effective + SASL mechanism: + + cb_cntl_server_t node; + node.version = 1; + node.v.v1.index = 0; /* first node */ + lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_MEMDNODE_INFO, &node); + if (node.v.v1.sasl_mech) { + printf("authenticated via SASL '%s'\n", + node.v.v1.sasl_mech); + } + + * It is also possible to force specific authentication mechanism for + the connection handle using `LCB_CNTL_FORCE_SASL_MECH` command: + + lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_FORCE_SASL_MECH, "PLAIN"); + +* [major] CCBC-286 libuv plugin: use same CRT for free/malloc + +* [major] CCBC-288 Fail `NOT_MY_VBUCKET` responses on timeout + +* [major] CCBC-275 Do a full purge when negotiation times out. In this + case we must purge the server from all commands and not simply pop + individual items. + +* [major] CCBC-275 Reset the server's buffers upon reconnection. This + fixes a crash experienced when requesting a new read with the + previous buffer still in tact. This was exposed by calling + `lcb_failout_server` on a timeout error while maintaining the same + server struct. + +* [major] CCBC-282 Make server buffers reentrant-safe. When purging + implicit commands, we invoke callbacks which may in turn cause other + LCB entry points to be invoked which can shift the contents and/or + positions of the ringbuffers we're reading from. + +* [major] CCBC-204, CCBC-205 Stricter/More inspectable behavior for + config cache. This provides a test and an additional `lcb_cntl` + operation to check the status of the configuration cache. Also it + switches off config cache with memcached buckets. + + int is_loaded; + lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_CONFIG_CACHE_LOADED, &is_loaded); + if (is_loaded) { + printf("Configuration cache saved us a trip to the config server\n"); + } else { + printf("We had to contact the configuration server for some reason\n"); + } + +* [major] CCBC-278 Use common config retry mechanism for bad + configcache. This uses the same error handling mechanism as when a + bad configuration has been received from the network. New + `LCB_CONFIG_CACHE_INVALID` error code to notify the user of such a + situation + +* [major] CCBC-274 Handle getl/unl when purging the server (thanks + Robert Groenenberg) + +* [major] Don't failout all commands on a timeout. Only fail those + commands which are old enough to have timed out already. + +* [major] CCBC-269 Don't record and use TTP/TTR from observe. Just + poll at a fixed interval, as the responses from the server side can + be unreliable. + +* [minor] Allow hooks for mapping server codes to errors. This also + helps handle sane behavior if a new error code is introduced, or + allow user-defined logging when a specific error code is received. + + lcb_errmap_callback default_callback; + + lcb_error_t user_map_error(lcb_t instance, lcb_uint16_t in) + { + if (in == PROTOCOL_BINARY_RESPONSE_ETMPFAIL) { + fprintf(stderr, "temporary failure on server\n"); + } + return default_callback(instance, in); + } + + ... + + default_callback = lcb_set_errmap_callback(conn, user_map_error); + +* [minor] Add an example of a connection pool. See + `example/instancepool` directory + +* [minor] CCBC-279 Force `lcb_wait` return result of wait operation + instead of `lcb_get_last_error`. It returns `last_error` if and only + if the handle is not yet configured + +* [minor] CCBC-284 `cbc-pillowfight`: compute item size correctly + during set If `minSize` and `maxSize` are set to the same value it + can sometimes crash since it may try to read out of memory bounds + from the allocated data buffer. + +* [minor] CCBC-283 Apply key prefix CLI option in cbc-pillowfight + +* [minor] Add `--enable-maintainer-mode`. Maintainer mode enables + `--enable-werror --enable-warnings --enable-debug`, forces all + plugins to be installed and forces all tests, tools, and examples to + be built + +* [minor] CCBC-255 Expose `LCB_MAX_ERROR` to allow user-defined codes + +## 2.1.3 (2013-09-10) + +* [minor] Updated gtest to version 1.7.0. Fixes issue with building + test suite with new XCode 5.0 version being released later this + month. + +* [major] CCBC-265 Do not try to parse config for `LCB_TYPE_CLUSTER` + handles. It fixes timouts for management operations (like 'cbc + bucket-create', 'cbc bucket-flush', 'cbc bucket-delete' and 'cbc + admin') + +* [major] CCBC-263 Skip unfinished SASL commands on rebalance. During + rebalance, it is possible that the newly added server doesn't have + chance to finish SASL auth before the cluster will push config + update, in this case packet relocator messing cookies. Also the + patch makes sure that SASL command/cookie isn't mixing with other + commands + +* [major] Use cluster type connection for cbc-bucket-flush. Although + flush command is accessible for bucket type connections, + cbc-bucket-flush doesn't use provided bucket name to connect to, + therefore it will fail if the bucket name isn't "default". + +* [major] Allow to make connect order deterministic. It allows the + user to toggle between deterministic and random connect order for + the supplied nodes list. By default it will randomize the list. + +* [major] Do not allow to use Administrator account for + `LCB_TYPE_BUCKET` + +* [major] CCBC-258 Fig segmentation faults during tests load of + node.js. Sets `inside_handler` on `socket_connected`. Previously we + were always using SASL auth, and as such, we wouldn't flush packets + from the `cmd_log` using `server_send_packets` (which calls + `apply_want`). `apply_want` shouldn't be called more than once per + event loop entry -- so this sets and unsets the `inside_handler` + flag. + +* [major] Added support of libuv 0.8 + +* [major] Close config connection before trying next node. It will fix + asserts in case of the config node becomes unresponsive, and the + threshold controlled by `LCB_CNTL_CONFERRTHRESH` and `lcb_cntl(3)` + +## 2.1.2 (2013-08-27) + +* [major] CCBC-253, CCBC-254 Use bucket name in SASL if username + omitted. Without this fix, you can may encounter a segmentation + faults for buckets, which are not protected by a password. + +* [major] Preserve IO cookie in `options_from_info` when using v0 + plugins with user-provided IO loop instance. This issue was + introduced in 2.1.0. + +* [minor] Display the effective IO backend in 'cbc-version'. This is + helpful to quickly detect what is the effective IO plugin on a given + system. + +## 2.1.1 (2013-08-22) + +* [minor] Use provided credentials for authenticating to the data + nodes. With this fix, it is no longer possible to use Administrator + credentials with a bucket. If your configuration does so, you must + change the credentials you use before applying this update. No + documentation guides use of Administrator credentials, so this + change is not expected to affect few, if any deployments. + +* [major] CCBC-239 Do not use socket after failout. Fixes segmentation + faults during rebalance. + +* [minor] CCBC-245 Distribute debug information with release binaries + on Windows + +* [minor] CCBC-248 Do not disable config.h on UNIX-like platforms. It + fixes build issue, when application is trying to include plugins + from the tarball. + +* [major] CCBC-192 Skip misconfigured nodes in the list. New + lcb\_cntl(3couchbase) added to control whether the library will skip + nodes in initial node list, which listen on configuration port (8091 + usually) but doesn't meet required parameters (invalid + authentication or missing bucket). By default report this issue and + stop trying nodes from the list, like all previous release. Read + more at man page lcb\_cntl(3couchbase) in section + LCB\_CNTL\_SKIP\_CONFIGURATION\_ERRORS\_ON\_CONNECT + +* [major] CCBC-246 Fallback to 'select' IO plugin if default plugin + cannot be loaded. On UNIX-like systems, default IO backend is + 'libevent', which uses third-party library might be not available + at the run-time. Read in lcb\_cntl(3couchbase) man page in section + LCB\_CNTL\_IOPS\_DEFAULT\_TYPES about how to determine effective IO + plugin, when your code chose to use LCB\_IO\_OPS\_DEFAULT during + connection instantiation. The fallback mode doesn't affect + application which specify IO backend explicitly. + +## 2.1.0 (2013-08-17) + +* [major] New backend `select`. This backend is based on the select(2) + system call and its Windows version. It could be considered the most + portable solution and is available with the libcouchbase core. + +* [major] CCBC-236 New backend `libuv`. This backend previously was + part of the `couchnode` project and is now available as a plugin. + Because libuv doesn't ship binary packages there is no binary + package `libcouchbase2-libuv`. You can build plugin from the source + distribution, or through the `libcouchbase-dev` or + `libcouchbase-devel` package on UNIX like systems. + +* [major] New backend `iocp`. This is a Windows specific backend, + which uses "I/O Completion Ports". As a part of the change, a new + version of plugin API was introduced which is more optimized to this + model of asynchronous IO. + +* [major] CCBC-229 Fixed bug when REPLICA\_FIRST fails if first try + does not return key + +* [major] CCBC-228 Fixed bug when REPLICA\_SELECT didn't invoke + callbacks for negative error codes + +* [major] CCBC-145 API for durability operations. This new API is + based on `lcb_observe(3)` and allows you to monitor keys more + easily. See the man pages `lcb_durability_poll(3)` and + `lcb_set_durability_callback(3)` for more info. + +* [major] New configuration interface lcb\_cntl(3) along with new + tunable options of the library and connection instances. In this + release the following settings are available. See the man page for + more information and examples.: + + * LCB\_CNTL\_OP\_TIMEOUT operation timeout (default 2.5 seconds) + + * LCB\_CNTL\_CONFIGURATION\_TIMEOUT time to fetch cluster + configuration. This is similar to a connection timeout (default 5 + seconds) + + * LCB\_CNTL\_VIEW\_TIMEOUT timeout for couchbase views (default 75 + seconds) + + * LCB\_CNTL\_HTTP\_TIMEOUT timeout for other HTTP operations like + RESTful flush, bucket creating etc. (default 75 seconds) + + * LCB\_CNTL\_RBUFSIZE size of the internal read buffer (default + 32768 bytes) + + * LCB\_CNTL\_WBUFSIZE size of the internal write buffer (default + 32768 bytes) + + * LCB\_CNTL\_HANDLETYPE type of the `lcb\_t` handler (readonly) + + * LCB\_CNTL\_VBCONFIG returns pointer to VBUCKET\_CONFIG\_HANDLE + (readonly) + + * LCB\_CNTL\_IOPS get the implementation of IO (lcb\_io\_opt\_t) + + * LCB\_CNTL\_VBMAP get vBucket ID for a given key + + * LCB\_CNTL\_MEMDNODE\_INFO get memcached node info + + * LCB\_CNTL\_CONFIGNODE\_INFO get config node info + + * LCB\_CNTL\_SYNCMODE control synchronous behaviour (default + LCB\_ASYNCHRONOUS) + + * LCB\_CNTL\_IP6POLICY specify IPv4/IPv6 policy (default + LCB\_IPV6\_DISABLED) + + * LCB\_CNTL\_CONFERRTHRESH control configuration error threshold + (default 100) + + * LCB\_CNTL\_DURABILITY\_TIMEOUT durability timeout (default 5 seconds) + + * LCB\_CNTL\_DURABILITY\_INTERVAL durability polling interval (default + 100 milliseconds) + + * LCB\_CNTL\_IOPS\_DEFAULT\_TYPES get the default IO types + + * LCB\_CNTL\_IOPS\_DLOPEN\_DEBUG control verbose printing of dynamic + loading of IO plugins. + +## 2.0.7 (2013-07-10) + +* [major] CCBC-183 Improve `lcb_get_replica()`. Now it is possible + to choose between three strategies: + + 1. `LCB_REPLICA_FIRST`: Previously accessible and now the default, + the caller will get a reply from the first replica to successfully + reply within the timeout for the operation or will receive an + error. + + 2. `LCB_REPLICA_ALL`: Ask all replicas to send documents/items + back. + + 3. `LCB_REPLICA_SELECT`: Select one replica by the index in the + configuration starting from zero. This approach can more quickly + receive all possible replies for a given topology, but it can + also generate false negatives. + + Note that applications should not assume the order of the + replicas indicates more recent data is at a lower index number. + It is up to the application to determine which version of a + document/item it may wish to use in the case of retrieving data + from a replica. + +## 2.0.6 (2013-05-07) + +* [major] CCBC-188 Fix segfault when rebalancing + When a (!connected) server is reconnected, the tasks in its + "pending" buffer will be moved into "output" buffer. If its + connection is broken again immediately, `relocate_packets()` will go + to wrong path. + +* [major] CCBC-202 Don't try to switch to backup nodes when timeout is + reached + +* [major] CCBC-188 Check if SASL struct is valid before disposing + +* [major] Fix compile error with sun studio + "src/event.c", line 172: error: statement not reached (`E_STATEMENT_NOT_REACHED`) + +* [major] Don't invoke HTTP callbacks after cancellation, because + user code might assume a previously-freed resource is still valid + +* [minor] CCBC-179 Added an example to properly use the bucket + credentials for authentication instead of administrator credentials + +* [minor] example/yajl/couchview.c: pass cookie to the command + Fixes coredump when executing ./examples/yajl/couchview + +* [minor] CCBC-201 Add Host header in http request + http://cbugg.hq.couchbase.com/bug/bug-555 points out that Host is a + required field in HTTP 1.1 + +## 2.0.5 (2013-04-05) + +* [minor] Try to search the --libdir for modules if dlopen + fails to find the module in the default library path + +* [minor] CCBC-190 New compat mode (experimental) for configuration + caching. See man `lcb_create_compat()` + +* [minor] Manpage fixes + +* [minor] Fix build on FreeBSD (http://review.couchbase.org/25289) + +* [minor] Fix reconnecting issues on windows + (http://review.couchbase.org/25170 and + http://review.couchbase.org/25155) + +* [minor] pillowfight example updated to optionally use threads + +## 2.0.4 (2013-03-06) + +* [minor] CCBC-185 The bootstrap URI is not parsed correctly + +* [minor] CCBC-175 Work properly on systems where EWOULDBLOCK != EAGAIN + +* [critical] CCBC-180 Segmentation fault when the hostname resolved + into several addresses and first of them reject couchbase + connections. + +* [major] CCBC-182 The library stops iterating backup nodes list if + the next one isn't accessible. + +* [major] CCBC-147 Fixed illegal memory access in win32 plugin + +* [minor] CCBC-178 Build error on solaris/sparc: -Werror=cast-align + +## 2.0.3 (2013-02-06) + +* [minor] bypass SASL LIST MECH + +* [minor] Shrink internal lookup tables (and reduce the size of + `lcb_t`) + +* [minor] Add a new library: `libcouchbase_debug.so` (see + include/libcouchbase/debug.h) which is a new library that contains + new debug functionality. + +* [minor] Added manual pages for the library + +* [major] CCBC-153 Reset internal state on `lcb_connect()`. Allow caller + to use `lcb_connect()` multiple times to implement reconnecting using + the same `lcb_t` instance. Also it sets up the initial-connection + timer for users who don't use `lcb_wait()` and drive IO loop manually. + +* [major] CCBC-171 Invalid read in libevent plugin, when the plugin + compiled in 1.x mode + +* [critical] CCBC-155 Observe malfunctions in the case of multiple + keys and server failure + +* [major] CCBC-156 The ep-engine renders meaningful body for observe + responses only if status code is 0 (`PROTOCOL_BINARY_RESPONSE_SUCCESS`). + We shouldn't interpret response body in other cases, just decode & + failout request instead. Also we shouldn't retry observe commands on + `PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET`, because it can cause the + client to loop infinitely + +* [major] CCBC-145 KV Durability operation API. Async APIs added to + allow the checking of the durability (replication and persistence) + status of a key, and to notify the user when a specific criteria has + been satisfied. + +## 2.0.2 (2013-01-04) + +* [major] CCBC-150 commands sent to multiple servers fail to detect + the respose if mixed with other commands. + +* [minor] CCBC-143 'cbc version' reports that uses 2.0.0, but really + installed with 2.0.1. Minor but confusing issue. + +* [major] CCBC-151 Cancellation of the HTTP request might lead to + memory leaks or to segfaults (2e3875c2). + +* [minor] Document `LCB_SERVER_BUG` and `LCB_PLUGIN_VERSION_MISMATCH`. + Enhance the the `lcb_strerror()` test to detect undocumented error + codes. + +* [critical] CCBC-153 Under high load the library could generate + `LCB_ETIMEDOUT` errors without reason owing to internal limitations. + +## 2.0.1 (2012-12-11) + +50 files changed, 1009 insertions(+), 274 deletions(-) + +* libev-plugin: delay all timers while the loop isn't active. It will + fix `LCB_ETIMEOUT` in the following scenario: + + * connect the instance + * sleep for time greater than default timeout (e.g. 3 seconds) + * schedule and execute a command (it will be timed out + immediately) + +* libev-plugin: reset IO event on delete. We need to reset it, + because it might be re-used later + +* CCBC-136: do not abort when purging SASL commands + +* Make library C89 friendly again + +* CCBC-132, CCBC-133: Ensure HTTP works even when the network may be + unreliable. This changeset encompasses several issues which had been + found with HTTP requests during network errors and configuration + changes. Specifically some duplicate code paths were removed, and + the process for delivering an HTTP response back to the user is more + streamlined. + +* CCBC-130: Fix a memory leak on the use of http headers + +* CCBC-131: Compensate for cluster nodes lacking couchApiBase + +* Fix possible SEGFAULT. Not-periodic timers are destroyed after + calling user's callback, after that library performed read from + freed pointer. + +* SystemTap and DTrace integration + +## 2.0.0 (2012-11-27) + +12 files changed, 50 insertions(+), 12 deletions(-) + +* Install unlock callback in synchronous mode + +* Add the CAS to the delete callback + +* Minor update of the packaging layout: + + * libcouchbase-all package comes without version + * extract debug symbols from libcouchbase-{bin,core} to + libcouchbase-dbg package + +## 2.0.0beta3 (2012-11-21) + +64 files changed, 3641 insertions(+), 735 deletions(-) + +* CCBC-104 Fix illegal memory access. Reconnect config listener if the + config connection was gone without proper shutdown. + +* Check for EWOULDBLOCK/EINTR on failed send + +* Allow to use gethrtime() from C++ + +* Fix using freed memory (was introduced in 4397181) + +* Use dynamic versioning for plugins + +* Remove libtool version from the plugins + +* Allow to use 'cbc-hash' with files + +* CCBC-120 Purge stale OBSERVE packets + +* CCBC-120 Reformat and refactor `lcb_server_purge_implicit_responses`: + + * move packet allocation out of GET handler + * dropping NOOP command shouldn't return error code + +* CCBC-122 Try to switch another server from backup list on timeout + +* CCBC-119: Allow the user to specify a different hash key. All of the + data operations contains a hashkey and nhashkey field. This allows + you to "group" items together in your cluster. A typical use case + for this is if you're storing lets say data for a single user in + multiple objects. If you want to ensure that either all or none of + the objects are available if a server goes down, it could be a good + idea to locate them on the same server. Do bear in mind that if you + do try to decide where objects is located, you may end up with an + uneven distribution of the number of items on each node. This will + again result in some nodes being more busy than others etc. This is + why some clients doesn't allow you to do this, so bear in mind that + by doing so you might not be able to get your objects from other + clients. + +* Create man pages for cbc and cbcrc + +* CCBC-118 `lcb_error_t` member in the http callbacks shouldn't reflect + the HTTP response code. So the error code will be always `LCB_SUCCESS` + if the library managed to receive the data successfully. + +* Timer in libev uses double for interval. Ref: + [](http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#code_ev_timer_code_relative_and_opti) + +* CCBC-115 Return zero from `do_read_data()` if `operations_per_call` + reached. The `operations_per_call' limit was introduced to prevent + from freezing event loop. But in the function variable rv could + store two different results and in case of reaching this limit it is + returning number of the processed records, which is wrong. The + function should return either zero (success) or non-zero (failure). + +* Do not allow admin operations without authentication + +* Fix cbc-bucket-create. `sasl-password' is misspelled, and it fails + to parse the command line option. + +* CCBC-114 Lookup the plugin symbol also in the current executable + image. + +* CCBC-113 Remove unauthorized asserion (d344037). The + `lcb_server_send_packets()` function later check if the server object + connected and establish connection if not (with raising possible + errors) + +* Try all known plugins for `LCB_IO_OPS_DEFAULT` in run time + +* Don't use the `time_t` for win32. When compiling from php it turns out + that it gets another size of the `time_t` type, causing the struct + offsets to differ. + +* Add `lcb_verify_compiler_setup()`. This function allows the "user" of + the library to verify that the compiler use a compatible struct + packing scheme. + +* CCBC-87: Add documentation about the error codes + +## 2.0.0beta2 (2012-10-12) + +81 files changed, 2822 insertions(+), 1353 deletions(-) + +* Search ev.h also in ${includedir}/libev + +* Fix SEGFAULT if IO struct is allocated not by the `lcb_create()` + +* Allow libcouchbase to connect to an instance without specifying bucket. It is useful + when the bucket not needed, e.g. when performing administration + tasks. + +* Fix memory leak after an unsuccessful connection + +* Fix invalid memory access in cbc tool. Affected command is + cbc-bucket-create + +* `lcb_create`: replace `assert()` with error code + +* CCBC-105 breakout event loop in default `error_callback`. This provides + better default behaviour for users who haven't defined global error + callback. + +* Allow users to build the library without dependencies. For example, + without plugins at all. This may be useful if the plugin is + implemented by or built into the host application. + +* Allow users to install both libraries (2.x and 1.x) on the same system. + +* Make the content type optional for `lcb_make_http_request()` + +* Fix password memory leak in http.c (7e71493) + +* Add support for raw http requests. libcouchase already contains all + the bits to execute a raw http request, except for the possibility + to specify a host:port, username and password. + +* Cleanup HTTP callbacks. Use the same callbacks both for Management + and View commands, and rename them to `lcb_http_complete_callback` and + `lcb_http_data_callback`. + +* Allow users to use environment variables to pick the event plugin + +* Add a new interface version for creating IO objects via plugins + +* Implement a new libev plugin. It is compatible with both libev3 and + libev4. + +* CCBC-103: Fix linked event/timer lists for win32 + +* Allow to disable CXX targets + +* `lcb_connect()` should honor the syncmode setting. Automatically call + `lcb_wait()` when in synchronous mode + +## 2.0.0beta (2012-09-13) + +123 files changed, 13753 insertions(+), 8264 deletions(-) + +* Refactor the API. This is a full redesign of the current + libcouchbase API that'll allow us to extend parts of the API without + breaking binary compatibility. Also it renames all functions to have + `lcb_` prefix instead of `libcouchbase_` and `LCB`/`LIBCOUCHBASE` in macros. + +* Added --enable-fat-binary. Helps to solve issues when linking with + fat binaries on MacOS. + +* Implement getter for number of nodes in the cluster: + `lcb_get_num_nodes()` + +* Implement RESTful flush in the cbc toolset + +* Bundle Windows packages as zip archives + +* CCBC-98 Differentiate between TMPFAILs. This allows a developer + to know if the temporary condition where the request cannot be + handled is due to a constraint on the client or the server. + +* Don't try to put the current node last in the backup list. This may + cause "duplicates" in the list if the REST server returns another + name for the server than you used. Ex: you specify "localhost" and + the REST response contains 127.0.0.1 + +* Fix locking keys in multi-get mode + +* Fix bug where HTTP method is not set + +* CCBC-96 Correct buffer length for POST/PUT headers + +* Add `lcb_get_server_list` + +* Merge `lcb_get_locked` into `lcb_get` function + +* Fix Windows build + +* Include sys/uio.h. Needed by OpenBSD + +* Fix mingw build (c394a1c) + +* CCBC-80: Default to IPv4 only + +* Sync `memcached/protocol_binary.h`. Pull extra + `protocol_binary_datatypes` declarations. + +* Deliver HTTP headers via callbacks + +* Unify HTTP interface. This means massive rename of the symbols + +* CCBC-92 release ringbuffer in `lcb_purge_single_server` + +* CCBC-91 Fix switching to backup node in case of server outage + +* CCBC-91 Reset timer for commands with `NOT_MY_VBUCKET` response + +* Fix alignment for sparc platforms + +* Fix win32 build (Add strings.h) + +* Fix build with libyajl available + +* Bundle libvbucket + +* Fix a problem with allocating too few slots in the `backup_nodes`. Fixes + illegal memory access. + +* CCBC-90 Fix initialization of backup nodes array. The code switching + nodes relies on NULL terminator rather than `nbackup_nodes` variable. + Fixes illegal memory access. + +* CCBC-89: Release the memory allocated by the http parser + +## 1.0.6 (2012-08-30) + +5 files changed, 18 insertions(+), 5 deletions(-) + +* CCBC-92 release ringbuffer in `libcouchbase_purge_single_server` + +## 1.0.5 (2012-08-15) + +6 files changed, 23 insertions(+), 15 deletions(-) + +* CCBC-91 Fix switching to backup node in case of server outage + +* CCBC-91 Reset timer for commands with `NOT_MY_VBUCKET` response + +## 1.1.0dp9 (2012-07-27) + +5 files changed, 18 insertions(+), 11 deletions(-) + +* Render auth credentials for View requests. + `libcouchbase_make_http_request()` won't accept credentials anymore. + It will pick them bucket configuration. + +## 1.1.0dp8 (2012-07-27) + +36 files changed, 2093 insertions(+), 704 deletions(-) + +* Allow the client to specify the verbosity level on the servers using + `lcb_set_verbosity()` function. + +* Bind timeouts to server sockets instead of commands. This means that + from this point timeout interval will be started from the latest IO + activity on the socket. This is a behavior change from the 1.0 series. + +* Allow the user to get the number of replicas using + `libcouchbase_get_num_replicas()` + +* Allow a user to breakout from the event loop in callbacks using + `libcouchbase_breakout()` + +* Make `libcouchbase_wait()` re-entrable + +* Let users detect if the event loop running already using + `libcouchbase_is_waiting()` function. + +* CCBC-77 Use separate error code for ENOMEM on the client + +* CCBC-82 Implement read replica + +* CCBC-85 Implement general purpose timers. It is possible for users + to define their own timers using `libcouchbase_timer_create()` + function. (See headers for more info) + +* Implement multiple timers for windows + +* CCBC-15 Add OBSERVE command + +* Allow users to specify content type for HTTP request + +* Fix to handle the case when View base doesn't have URI schema + +* Separate HTTP callbacks for couch and management requests + +* Claim that server has data in buffers if there are HTTP requests + pending. Without this patch the event loop can be stopped + prematurely. + +* Add new cbc commands and options: + + * cbc-view (remove couchview example) + * cbc-verbosity + * cbc-admin + * cbc-bucket-delete + * cbc-bucket-create + * Add -p and -r options to cbc-cp to control persistence (uses + OBSERVE internally) + +## 1.1.0dp7 (2012-06-19) + +18 files changed, 266 insertions(+), 115 deletions(-) + +* Add support for notification callbacks for configuration changes. + Now it is possible to install a hook using function + `libcouchbase_set_configuration_callback()`, and be notified about all + configuration changes. + +* Implement function to execution management requests. Using + `libcouchbase_make_management_request()` function you can configure + the cluster, add/remove buckets, rebalance etc. It behaves like + `libcouchbase_make_couch_request()` but works with another endpoint. + +* Extract HTTP client. Backward incompatible change in Couchbase View + subsystem + +## 1.1.0dp6 (2012-06-13) + +20 files changed, 201 insertions(+), 127 deletions(-) + +* CCBC-70 Close dynamic libraries. Fixes small memory leak + +* CCBC-72 Fix compilation on macosx with gtest from homebrew + +* CCBC-71 Implement 'help' command for cbc tool + +* Undefine NDEBUG to avoid asserts to be optimized out + +* Fix win32 builds: + + * Add suffix to cbc command implementations + * Fix guards for socket errno macros + * Define `size_t` types to fix MSVC 9 build + * MSVC 9 isn't C99, but has stddef.h, so just include it + +* CCBC-63 Include types definitions for POSIX systems. Fixes C++ + builds on some systems. + +## 1.1.0dp5 (2012-06-06) + +7 files changed, 65 insertions(+), 9 deletions(-) + +* The library doesn't depend on pthreads (eliminates package lint + warnings) + +* Implement 'cbc-hash' to match server/vbucket for given key + +## 1.1.0dp4 (2012-06-05) + +8 files changed, 54 insertions(+), 7 deletions(-) + +* cbc: strtoull doesn't exist on win32, therefore use C++ equivalent. + +* integration with Travis-CI + +## 1.1.0dp3 (2012-06-03) + +54 files changed, 1874 insertions(+), 824 deletions(-) + +* CCBC-68 Implement `UNLOCK_KEY` (`UNL`) command + +* CCBC-68 Implement `GET_LOCKED` (`GETL`) command + +* hashset.c: iterate over whole set on rehashing. Fixes memory leaks + related to hash collisions (905ef95) + +* Destroy view requests items when server get destroyed + +* Do not call View callbacks for cancelled requests + +* Fix `ringbuffer_memcpy()` (36afdb2) + +* CCBC-62 A hang could occur in `libcouchbase_wait()` after the timeout + period. Check for breakout condition after purging servers + +* CCBC-65 A small memory leak can occur with frequent calls to + `libcouchbase_create()` and `libcouchbase_destroy()` + +* CCBC-64. Timeouts can occur during topology changes, rather than be + correctly retried. Send the retry-packet to new server + +* `vbucket_found_incorrect_master()` returns server index + +* Fix `ringbuffer_is_continous()` + +* Pick up cookies from pending buffer unless node connected + +* RCBC-33 A fix for a buffer overflow with the supplied password as + has been integrated. While it is a buffer overflow issue, this is + not considered to be a possible security issue because the password + to the bucket is not commonly supplied by an untrusted source + +## 1.0.4 (2012-06-01) + +15 files changed, 330 insertions(+), 76 deletions(-) + +* CCBC-65 A small memory leak can occur with frequent calls to + `libcouchbase_create()` and `libcouchbase_destroy()` + +* CCBC-62 A hang could occur in `libcouchbase_wait()` after the timeout + period. Check for breakout condition after purging servers + +* CCBC-64. Timeouts can occur during topology changes, rather than be + correctly retried. Send the retry-packet to new server + +* [backport] `vbucket_found_incorrect_master()` returns server index. + (orig: c32fdae) + +## 1.0.3 (2012-05-02) + +6 files changed, 44 insertions(+), 7 deletions(-) + +* [backport] Fix `ringbuffer_is_continous()` (orig: 9cfda9d) + +* [backport] Pick up cookies from pending buffer unless node connected + (orig: 463958d) + +* RCBC-33 A fix for a buffer overflow with the supplied password as + has been integrated. While it is a buffer overflow issue, this is + not considered to be a possible security issue because the password + to the bucket is not commonly supplied by an untrusted source + +## 1.1.0dp2 (2012-04-10) + +10 files changed, 54 insertions(+), 20 deletions(-) + +* CCBC-59 Don't wait for empty buffers. If called with no operations + queued, `libcouchbase_wait()` will block forever. This means that a + single threaded application that calls `libcouchbase_wait()` at + different times to make sure operations are sent to the server runs + the risk of stalling indefinitely. This is a very likely scenario. + +* Don't define `size_t` and `ssize_t` for VS2008 + +* Fix segfault while authorizing on protected buckets (211bb04) + +## 1.1.0dp (2012-04-05) + +59 files changed, 4374 insertions(+), 1205 deletions(-) + +* This release adds new functionality to directly access Couchbase + Server views using the `libcouchbase_make_couch_request()` function. + See the associated documentation and header files for more details + +* Check for newer libvbucket + +* MB-4834: Request the tap bytes in a known byte order (adf2b30) + +## 1.0.2 (2012-03-06) + +83 files changed, 4095 insertions(+), 654 deletions(-) + +* Implement VERSION command from binary protocol + +* Allow use of libcouchbase to pure memcached clusters by using + `libcouchbase_create_compat()` function + +* Always sign deb packages and allow to pass PGP key + +* Bundle the protocol definitions for memcached + (`memcached/protocol_binary.h` and `memcached/vbucket.h`) to make it + easier to build + +* Bundle sasl client implementation + +* Fix windows build for MS Visual Studio 9 + + * define `E*` if missing + * stdint header + +* Add support for multiple hosts for the bootstrap URL. A list of + hosts:port separated by ';' to the administration port of the + couchbase cluster. (ex: "host1;host2:9000;host3" would try to + connect to host1 on port 8091, if that fails it'll connect to host2 + on port 9000 etc) + +* Raise error if missing + +* Add JSON support for cbc-cp command + +* Add option to set timeout for cbc + +* Added support for '-' to cp + +* Added cbc-verify: verify content in cache with files + +* Now cbc supports better usage messages + +## 1.0.1 (2012-02-13) + +65 files changed, 3275 insertions(+), 1329 deletions(-) + +* CCBC-38 Use alternate nodes when current is dead. A fix to allow the + client library to failover automatically to other nodes when the + initial bootstrap node becomes unavailable has been added. All users + are recommended to upgrade for this fix. + +* Fix connect timeouts. Timeouts are per-operation and only set if + there is any I/O. The special exception to this is initial + connections, which do not necessarily have a data stream or write + buffer associated wiht them yet. + +* Update to new MT-safe libvbucket API + +* Add option for embedding libevent IO plugin + +* Fix multi-{get,touch} requests handling when nkeys > 1 + +* Allow to build without tools which require C++ compiler + +* Destroy event base if we created it + +* CCBC-51 Check server index before using + +* Handle `PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET` and retry it until + success or another error, which can be handled by caller + +* Do not attempt SASL when SASL already in progress + +* Finer grained error reporting for basic REST errors: + + * return `LIBCOUCHBASE_AUTH_ERROR` on HTTP 401 + * return `LIBCOUCHBASE_BUCKET_ENOENT` on HTTP 404 + * event loop is stopped (via `maybe_breakout`) on REST error + +* Fixed segfaults and memory access errors on libevent1.4 + +* Allow for notification on initial vbucket config. This makes + libcouchbase `stop_event_loop` and libcouchbase maybe breakout work + properly in cooperative asynchronous event loops. the wait flag is + set by `libcouchbase_wait()` and unset by `maybe_breakout`. + Additionally, `breakout_vbucket_state_listener` will call + `maybe_breakout` as well, instead of having synchronous behavior + assumed by `libcouchbase_wait()` + +* Fix `sasl_list_mech_response_handler()`. `sasl_client_start()` expects + null-terminated string + +* Refactor: use `libcouchbase_xxxx` for the datatypes + +* Do not notify user about the same error twice. Use command callback + when it's possible. (e.g. where the `libcouchbase_server_t` is + accessible and we can `libcouchbase_failout_server()`) + +* Install configuration.h for win32 + +* CCBC-20 Implement operation timeouts. Timeouts applies for all + operations, and the timer starts running from the moment you call + the libcouchbase operation you want. The timer includes times for + connect/send/ receive, and all of the time our application spend + before letting the event loop call callbacks into libcouchbase. + +* Fix double free() error when reading key from packet in handler.c + (b5d485a) + +## 1.0.0 (2012-01-22) + +170 files changed, 6048 insertions(+), 7553 deletions(-) + +* Allow the user to specify sync mode on an instance + +* Empty string as bucket name should be treated as NULL + +* Bail out if you can't find memcached/vbucket.h and + libvbucket/vbucket.h + +* New command cbc. This command intended as the analog of `mem*` + tools from libmemcached distribution. Supported commands: + + * cbc-cat + * cbc-cp + * cbc-create + * cbc-flush + * cbc-rm + * cbc-stats + * cbc-send + * cbc-receive + +* CCBC-37 allow config for cbc tool to be read from .cbcrc + +* Convert flags to network byte order + +* Remove dependency + +* Use the error handler instead of printing to stderr + +* Disable Views code + +* Don't accept NULL as a valid "callback" + +* Add make targets to build RPM and DEB packages + +* Allow download memcached headers from remote host + +* Added docbook-based manual pages + +* Gracefully update vbucket configuration. This means that the + connection listener, could reconfigure data sockets on the fly + +* Allow libcouchbase build with libevent 1.x (verified for 1.4.14) + +* Aggregate flush responses + +* Add stats command + +## 0.3.0 (2011-11-02) + +102 files changed, 6188 insertions(+), 1531 deletions(-) + +* Add flush command from binary protocol + +* Remove packet filter + +* Use ringbuffers instead `buffer_t` + +* Win32 build fixes + +* Allow to specify IO framework but using IO plugins + +* CCBC-11 The interface to access views + +* Initial man pages + +* Extend the test suite + +## 0.2.0 (2011-09-01) + +85 files changed, 12144 insertions(+) + +* Simple bootstapping which builds HTTP packet and listens + /pools/default/buckets/BUCKETNAME directly. Allowed usage of + defaults (bucket name, password) + +* Support basic set of binary protocol commands: + + * get (get and touch) + * set + * increment/decrement + * remove + * touch + +* MB-3294 Added `_by_key` functions + +* CCBC-5 Fixed abort in `do_read_data` (c=0x7b09bf0) at src/event.c:105 + +* Added timings API. It might be possible to turn on timing collection + using `libcouchbase_enable_timings()`/`libcouchbase_disable_timings()`, + and receive the data in timings callback. + +* Basic TAP protocol implementation + +* Initial win32 support + + diff --git a/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/ConfigureDtrace.cmake b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/ConfigureDtrace.cmake new file mode 100644 index 00000000..31af440a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/ConfigureDtrace.cmake @@ -0,0 +1,27 @@ +FIND_PROGRAM(DTRACE dtrace) +IF(DTRACE) + SET(LCB_DTRACE_HEADER "${LCB_GENSRCDIR}/probes.h") + SET(LCB_DTRACE_SRC "${PROJECT_SOURCE_DIR}/src/probes.d") + + # Generate probes.h + EXECUTE_PROCESS(COMMAND ${DTRACE} -C -h -s ${LCB_DTRACE_SRC} -o ${LCB_DTRACE_HEADER} + RESULT_VARIABLE _rv) + IF(NOT ${_rv} EQUAL 0) + MESSAGE(WARNING "Could not execute DTrace. DTrace support will be disabled!") + RETURN() + ENDIF() + + ADD_DEFINITIONS(-DHAVE_DTRACE) + IF(NOT APPLE) + SET(LCB_DTRACE_OBJECT "${LCB_GENSRCDIR}/probes.o") + # Generate probes.o + IF(CMAKE_SYSTEM_NAME STREQUAL "SunOS" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + SET(LCB_DTRACE_INSTRO ON) + UNSET(LCB_DTRACE_OBJECT) + ELSE() + ADD_CUSTOM_COMMAND(OUTPUT ${LCB_DTRACE_OBJECT} + DEPENDS ${LCB_DTRACE_SRC} + COMMAND ${DTRACE} -C -G ${LCB_DTRACE_OPTIONS} -s ${LCB_DTRACE_SRC} -o ${LCB_DTRACE_OBJECT}) + ENDIF() + ENDIF() +ENDIF() diff --git a/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/CopyPDB.cmake b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/CopyPDB.cmake new file mode 100644 index 00000000..4a9dc1b1 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/CopyPDB.cmake @@ -0,0 +1,42 @@ +IF(POLICY CMP0026) + CMAKE_POLICY(SET CMP0026 OLD) +ENDIF() +MACRO(TRANSFORM_TARGET tname output_pdb output_exp) + # Base extension name + GET_FILENAME_COMPONENT(_base "${tname}" NAME_WE) + GET_FILENAME_COMPONENT(_type "${tname}" EXT) + + # Path, e.g. 'Debug' + GET_FILENAME_COMPONENT(_bindir "${tname}" PATH) + GET_FILENAME_COMPONENT(_config "${_bindir}" NAME) + + # e.g. the build directory itself + IF(CMAKE_BUILD_TYPE) + GET_FILENAME_COMPONENT(_basedir "${_bindir}/../" ABSOLUTE) + SET(_config "") + ELSE() + GET_FILENAME_COMPONENT(_basedir "${_bindir}/../../" ABSOLUTE) + ENDIF() + + SET(${output_pdb} "${_basedir}/bin/${_config}/${_base}.pdb") + IF( ".dll" STREQUAL "${_type}") + SET(${output_exp} "${_basedir}/lib/${_config}/${_base}.exp") + ENDIF() +ENDMACRO() + +MACRO(INSTALL_PDBS target) + IF(MSVC) + GET_TARGET_PROPERTY(_BIN_DEBUG ${target} LOCATION_DEBUG) + GET_TARGET_PROPERTY(_BIN_RDB ${target} LOCATION_RelWithDebInfo) + TRANSFORM_TARGET(${_BIN_DEBUG} _DEBUG_PDB _DEBUG_EXP) + TRANSFORM_TARGET(${_BIN_RDB} _RDB_PDB _RDB_EXP) + INSTALL(FILES ${_DEBUG_PDB} DESTINATION bin CONFIGURATIONS DEBUG) + INSTALL(FILES ${_RDB_PDB} DESTINATION bin CONFIGURATIONS RelWithDebInfo) + IF(_DEBUG_EXP) + INSTALL(FILES ${_DEBUG_EXP} DESTINATION lib CONFIGURATIONS DEBUG) + ENDIF() + IF(_RDB_EXP) + INSTALL(FILES ${_RDB_EXP} DESTINATION lib CONFIGURATIONS RelWithDebInfo) + ENDIF() + ENDIF(MSVC) +ENDMACRO() diff --git a/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/DistScript.cmake b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/DistScript.cmake new file mode 100644 index 00000000..9198993a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/DistScript.cmake @@ -0,0 +1,17 @@ +EXECUTE_PROCESS( + COMMAND ${CMAKE_COMMAND} --build . --config Debug) + +EXECUTE_PROCESS( + COMMAND ${CMAKE_COMMAND} --build . --config RelWithDebInfo) + +EXECUTE_PROCESS( + COMMAND ${CMAKE_COMMAND} + -DCMAKE_INSTALL_CONFIG_NAME=Debug + -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} + -P cmake_install.cmake) + +EXECUTE_PROCESS( + COMMAND ${CMAKE_COMMAND} + -DCMAKE_INSTALL_CONFIG_NAME=RelWithDebInfo + -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} + -P cmake_install.cmake) diff --git a/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/DownloadLcbDep.cmake b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/DownloadLcbDep.cmake new file mode 100644 index 00000000..b829e402 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/DownloadLcbDep.cmake @@ -0,0 +1,20 @@ +# DownloadLcbDeb(url location) +FIND_PROGRAM(WGET wget) +FIND_PROGRAM(CURL curl) +MACRO(DOWNLOAD_LCB_DEP url dest) + IF(WIN32) + EXECUTE_PROCESS(COMMAND powershell -Command + "(New-Object Net.WebClient).DownloadFile('${url}', '${dest}')" RESULT_VARIABLE DLRC) + ELSEIF(CURL) + EXECUTE_PROCESS(COMMAND "${CURL}" "${url}" -o "${dest}" RESULT_VARIABLE DLRC) + ELSEIF(WGET) + EXECUTE_PROCESS(COMMAND "${WGET}" "${url}" -O "${dest}" RESULT_VARIABLE DLRC) + ELSE() + MESSAGE(WARNING "Using buggy built-in CMake downloader") + FILE(DOWNLOAD ${url} ${dest} INACTIVITY_TIMEOUT 30 SHOW_PROGRESS) + SET(DLRC 0) + ENDIF() + IF(NOT DLRC EQUAL 0) + MESSAGE(FATAL_ERROR "Failed to download ${url}") + ENDIF() +ENDMACRO() diff --git a/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseHdrHistogram.cmake b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseHdrHistogram.cmake new file mode 100644 index 00000000..7e990274 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseHdrHistogram.cmake @@ -0,0 +1,15 @@ +FIND_PATH(HDR_HISTOGRAM_INCLUDES hdr_histogram.h + HINTS ENV HDR_HISTOGRAM_DIR + PATH_SUFFIXES include/hdr + PATHS + /usr/local) +FIND_LIBRARY(HDR_HISTOGRAM_LIBRARIES NAMES hdr_histogram + HINTS ENV HDR_HISTOGRAM_DIR + PATH_SUFFIXES lib + PATHS + /usr/local) +IF(HDR_HISTOGRAM_LIBRARIES AND HDR_HISTOGRAM_INCLUDES) + SET(HDR_HISTOGRAM_FOUND TRUE) +ELSE() + SET(HDR_HISTOGRAM_FOUND FALSE) +ENDIF() diff --git a/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseLibev.cmake b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseLibev.cmake new file mode 100644 index 00000000..659c0896 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseLibev.cmake @@ -0,0 +1,73 @@ +# Locate libev library +# This module defines +# HAVE_LIBEV, if false, do not try to link with libev +# LIBEV_LIBRARIES, Library path and libs +# LIBEV_INCLUDE_DIR, where to find the libev headers + +FIND_PATH(LIBEV_INCLUDE_DIR ev.h + PATHS ${LIBEV_ROOT} + PATH_SUFFIXES include libev + NO_DEFAULT_PATH) + +FIND_LIBRARY(LIBEV_LIBRARIES + NAMES ev libev + PATHS ${LIBEV_ROOT} + PATH_SUFFIXES lib libev + NO_DEFAULT_PATH) + +FIND_PATH(LIBEV_INCLUDE_DIR ev.h + HINTS + ENV LIBEV_DIR + PATH_SUFFIXES include include/libev + PATHS + ${DEPS_INCLUDE_DIR} + ~/Library/Frameworks + /Library/Frameworks + /opt/local + /opt/csw + /opt/libev + /opt) + +FIND_LIBRARY(LIBEV_LIBRARIES + NAMES ev libev + HINTS + ENV LIBEV_DIR + PATH_SUFFIXES lib libev + PATHS + ${DEPS_LIB_DIR} + ~/Library/Frameworks + /Library/Frameworks + /opt/local + /opt/csw + /opt/libev + /opt) + +IF (LIBEV_LIBRARIES AND LIBEV_INCLUDE_DIR) + SET(HAVE_LIBEV true) + MESSAGE(STATUS "Found libev in ${LIBEV_INCLUDE_DIR} : ${LIBEV_LIBRARIES}") +ELSE (LIBEV_LIBRARIES) + SET(HAVE_LIBEV false) +ENDIF (LIBEV_LIBRARIES AND LIBEV_INCLUDE_DIR) + +INCLUDE(CMakePushCheckState) +INCLUDE(CheckFunctionExists) +IF(HAVE_LIBEV) + CMAKE_PUSH_CHECK_STATE() + SET(CMAKE_REQUIRED_FLAGS "-I${LIBEV_INCLUDE_DIR}") + SET(CMAKE_REQUIRED_LIBRARIES ${LIBEV_LIBRARIES}) + SET(CMAKE_REQUIRED_INCLUDES "ev.h") + CHECK_FUNCTION_EXISTS(ev_loop HAVE_LIBEV3) + IF(NOT HAVE_LIBEV3) + CHECK_FUNCTION_EXISTS(ev_run HAVE_LIBEV4) + ENDIF() + CMAKE_POP_CHECK_STATE() + IF(HAVE_LIBEV3) + MESSAGE(STATUS "libev3 found") + ELSEIF(HAVE_LIBEV4) + MESSAGE(STATUS "libev4 found") + ELSE() + MESSAGE(STATUS "libev not found") + ENDIF() +ENDIF() + +MARK_AS_ADVANCED(HAVE_LIBEV LIBEV_INCLUDE_DIR LIBEV_LIBRARIES) diff --git a/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseLibevent.cmake b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseLibevent.cmake new file mode 100644 index 00000000..e91fa105 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseLibevent.cmake @@ -0,0 +1,52 @@ +# Locate libevent library +# This module defines +# HAVE_LIBEVENT, if false, do not try to link with libevent +# LIBEVENT_LIBRARIES, Library path and libs +# LIBEVENT_INCLUDE_DIR, where to find the ICU headers +# HAVE_LIBEVENT +# HAVE_LIBEVENT2 + +FIND_PATH(LIBEVENT_INCLUDE_DIR evutil.h + HINTS + ENV LIBEVENT_DIR + PATH_SUFFIXES include + PATHS + ${DEPS_INCLUDE_DIR} + ~/Library/Frameworks + /Library/Frameworks + /opt/local + /opt/csw + /opt/libevent + /opt) + +FIND_LIBRARY(LIBEVENT_LIBRARIES + NAMES event_core libevent_core + HINTS + ENV LIBEVENT_DIR + PATHS + ${DEPS_LIB_DIR} + ~/Library/Frameworks + /Library/Frameworks + /opt/local + /opt/csw + /opt/libevent + /opt) + +INCLUDE(CMakePushCheckState) +INCLUDE(CheckFunctionExists) + +IF (LIBEVENT_LIBRARIES AND LIBEVENT_INCLUDE_DIR) + SET(HAVE_LIBEVENT true) + MESSAGE(STATUS "Found libevent in ${LIBEVENT_INCLUDE_DIR} : ${LIBEVENT_LIBRARIES}") + CMAKE_PUSH_CHECK_STATE() + SET(CMAKE_REQUIRED_FLAGS "-I${LIBEVENT_INCLUDE_DIR}") + SET(CMAKE_REQUIRED_INCLUDES "event2/event.h") + SET(CMAKE_REQUIRED_LIBRARIES ${LIBEVENT_LIBRARIES}) + CHECK_FUNCTION_EXISTS(event_new HAVE_LIBEVENT2) + CMAKE_POP_CHECK_STATE() + +ELSE (LIBEVENT_LIBRARIES) + SET(HAVE_LIBEVENT false) +ENDIF (LIBEVENT_LIBRARIES AND LIBEVENT_INCLUDE_DIR) + +MARK_AS_ADVANCED(HAVE_LIBEVENT LIBEVENT_INCLUDE_DIR LIBEVENT_LIBRARIES) diff --git a/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseLibuv.cmake b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseLibuv.cmake new file mode 100644 index 00000000..b72ecee9 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseLibuv.cmake @@ -0,0 +1,56 @@ +# Locate libuvent library +# This module defines +# HAVE_LIBUV, if false, do not try to link with libuvent +# LIBUV_LIBRARIES, Library path and libs +# LIBUV_INCLUDE_DIR, where to find the ICU headers + +FIND_PATH(LIBUV_INCLUDE_DIR uv.h + HINTS ${LIBUV_ROOT} + PATH_SUFFIXES include + PATHS + ~/Library/Frameworks + /Library/Frameworks + /opt/local + /opt/csw + /opt/libuv + /opt) +MESSAGE(STATUS "Found UV include dir: ${LIBUV_INCLUDE_DIR}") + +IF(WIN32) + FIND_LIBRARY(LIBUV_LIBRARIES_DEBUG + NAMES uv libuv + HINTS ${LIBUV_ROOT} + PATH_SUFFIXES Debug Debug/lib) + + FIND_LIBRARY(LIBUV_LIBRARIES_OPTIMIZED + NAMES uv libuv + HINTS ${LIBUV_ROOT} + PATH_SUFFIXES Release Release/lib) + + SET(LIBUV_LIBRARIES + OPTIMIZED ${LIBUV_LIBRARIES_OPTIMIZED} + DEBUG ${LIBUV_LIBRARIES_DEBUG}) + +ELSE() + FIND_LIBRARY(LIBUV_LIBRARIES + NAMES uv + HINTS ${LIBUV_ROOT} + PATH_SUFFIXES lib + PATHS + ~/Library/Frameworks + /Library/Frameworks + /opt/local + /opt/csw + /opt/libuv + /opt) +ENDIF(WIN32) + + +IF (LIBUV_LIBRARIES) + SET(HAVE_LIBUV true) + MESSAGE(STATUS "Found libuv in ${LIBUV_INCLUDE_DIR} : ${LIBUV_LIBRARIES}") +ELSE (LIBUV_LIBRARIES) + SET(HAVE_LIBUV false) +ENDIF (LIBUV_LIBRARIES) + +MARK_AS_ADVANCED(HAVE_LIBUV LIBUV_INCLUDE_DIR LIBUV_LIBRARIES LIBUV_ROOT) diff --git a/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseSnappy.cmake b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseSnappy.cmake new file mode 100644 index 00000000..d8cb3601 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/FindCouchbaseSnappy.cmake @@ -0,0 +1,11 @@ +FIND_PATH(SNAPPY_INCLUDES snappy-c.h + HINTS ENV SNAPPY_DIR ${SNAPPY_DIR} + PATH_SUFFIXES include) +FIND_LIBRARY(SNAPPY_LIBRARIES NAMES snappy + HINTS ENV SNAPPY_DIR ${SNAPPY_DIR} + PATH_SUFFIXES lib) +IF(SNAPPY_LIBRARIES AND SNAPPY_INCLUDES) + SET(SNAPPY_FOUND TRUE) +ELSE() + SET(SNAPPY_FOUND FALSE) +ENDIF() diff --git a/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/GenerateConfigDotH.cmake b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/GenerateConfigDotH.cmake new file mode 100644 index 00000000..3d41401c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/GenerateConfigDotH.cmake @@ -0,0 +1,29 @@ +# Writes the 'config.h' header.. +INCLUDE(CheckFunctionExists) +INCLUDE(CheckIncludeFiles) +INCLUDE(CheckSymbolExists) + +IF(UNIX) + CHECK_FUNCTION_EXISTS(gethrtime HAVE_GETHRTIME) + CHECK_FUNCTION_EXISTS(gettimeofday HAVE_GETTIMEOFDAY) + CHECK_FUNCTION_EXISTS(clock_gettime HAVE_CLOCK_GETTIME) + CHECK_FUNCTION_EXISTS(setitimer HAVE_SETITIMER) + CHECK_SYMBOL_EXISTS(htonll arpa/inet.h HAVE_HTONLL) + CHECK_INCLUDE_FILES(dlfcn.h HAVE_DLFCN_H) + CHECK_INCLUDE_FILES(netdb.h HAVE_NETDB_H) + CHECK_INCLUDE_FILES(stdint.h HAVE_STDINT_H) + CHECK_INCLUDE_FILES(strings.h HAVE_STRINGS_H) + CHECK_INCLUDE_FILES(sys/socket.h HAVE_SYS_SOCKET_H) + CHECK_INCLUDE_FILES(sys/stat.h HAVE_SYS_STAT_H) + CHECK_INCLUDE_FILES(sys/types.h HAVE_SYS_TYPES_H) + CHECK_INCLUDE_FILES(unistd.h HAVE_UNISTD_H) + CHECK_INCLUDE_FILES(sys/uio.h HAVE_SYS_UIO_H) + CHECK_INCLUDE_FILES(fcntl.h HAVE_FCNTL_H) + CHECK_INCLUDE_FILES(sys/time.h HAVE_SYS_TIME_H) + CHECK_INCLUDE_FILES(arpa/inet.h HAVE_ARPA_INET_H) + CHECK_INCLUDE_FILES(inttypes.h HAVE_INTTYPES_H) +ENDIF() + +CONFIGURE_FILE( + ${PROJECT_SOURCE_DIR}/cmake/config-cmake.h.in + ${LCB_GENSRCDIR}/config.h) diff --git a/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/GetLibcouchbaseFlags.cmake b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/GetLibcouchbaseFlags.cmake new file mode 100644 index 00000000..19e81446 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/GetLibcouchbaseFlags.cmake @@ -0,0 +1,134 @@ +# Common flags for libcouchbase modules. This defines the specific flags +# required for various compilation modes +# Exports: +# LCB_CORE_CFLAGS: +# C flags to be used by our "Core" modules. This contains +# many warnings. +# LCB_CORE_CXXFLAGS: +# Like LCB_CORE_CFLAGS, but for C++ +# +# LCB_BASIC_CFLAGS +# Basic C flags without extra warnings +# LCB_BASIC_CXXFLAGS +# Basic C++ flags without extra warnings. +# +# Note that global flags will still be modified for debug settings and the +# like. + +MACRO(list2args VAR) + STRING(REPLACE ";" " " _tmp "${${VAR}}") + SET("${VAR}" "${_tmp}") +ENDMACRO(list2args) + +LIST(APPEND LCB_GNUC_CPP_WARNINGS + -Wall -pedantic -Wshadow -fdiagnostics-show-option -Wformat + -Wno-strict-aliasing -Wextra -Winit-self -Wno-missing-field-initializers) + +IF("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") + LIST(APPEND LCB_GNUC_CPP_WARNINGS -Wno-cast-align -Wno-dollar-in-identifier-extension) +ENDIF() + +IF(LCB_USE_ASAN) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer -fsanitize=address") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -fsanitize=address") + SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -undefined dynamic_lookup -fsanitize=address") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") +ENDIF() + +IF(LCB_USE_COVERAGE) + SET(_covflags "-fprofile-arcs -ftest-coverage") + IF(CMAKE_COMPILER_IS_GNUCC) + SET(_covflags "--coverage ${_covflags}") + ENDIF() + + LIST(APPEND LCB_GNUC_CPP_WARNINGS ${_covflags}) + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${_covflags}") + SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${_covflags}") +ENDIF() + +list2args(LCB_GNUC_CPP_WARNINGS) + +LIST(APPEND LCB_GNUC_C_WARNINGS + ${LCB_GNUC_CPP_WARNINGS} + -std=gnu99 + -Wundef -Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls + -Wmissing-declarations) +list2args(LCB_GNUC_C_WARNINGS) + +LIST(APPEND LCB_GNUC_CXX_WARNINGS + ${LCB_GNUC_CPP_WARNINGS} + -std=gnu++98 -Woverloaded-virtual -Wnon-virtual-dtor -Wctor-dtor-privacy + -Wno-long-long -Wredundant-decls) +list2args(LCB_GNUC_CXX_WARNINGS) + +#MSVC-specific flags for C/C++ +LIST(APPEND LCB_CL_CPPFLAGS /nologo /W3 /MP /EHsc) +LIST(APPEND LCB_CL_CPPFLAGS /wd4800 /wd4244 /wd4267) +list2args(LCB_CL_CPPFLAGS) + +# Common flags for DEBUG +LIST(APPEND LCB_CL_CPPFLAGS_DEBUG /RTC1) +list2args( LCB_CL_CPPFLAGS_DEBUG) + +# Common flags for RELEASE +LIST(APPEND LCB_CL_CPPFLAGS_REL /O2) +list2args(LCB_CL_CPPFLAGS_REL) + +MACRO(SET_ALL_FLAGS extra_flags) + FOREACH(variant C CXX) + FOREACH(config RELEASE DEBUG RELWITHDEBINFO) + SET(varname "CMAKE_${variant}_FLAGS_${config}") + SET(existing ${${varname}}) + SET(${varname} "${existing} ${extra_flags}") + ENDFOREACH() + SET(CMAKE_${variant}_FLAGS "${CMAKE_${variant}_FLAGS} ${extra_flags}") + ENDFOREACH() +ENDMACRO() + +IF(MSVC) + ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS) + # Don't warn about "deprecated POSIX names" + ADD_DEFINITIONS(-D_CRT_NONSTDC_NO_DEPRECATE) + + # Need this for VS 2012 for googletest and C++ + IF(MSVC_VERSION EQUAL 1700 OR MSVC_VERSION GREATER 1700) + ADD_DEFINITIONS(-D_VARIADIC_MAX=10) + ENDIF() + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /TC ${LCB_CL_CPPFLAGS}") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LCB_CL_CPPFLAGS}") + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${LCB_CL_CPPFLAGS_DEBUG}") + SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${LCB_CL_CPPFLAGS_DEBUG}") + SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${LCB_CL_CPPFLAGS_REL}") + SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${LCB_CL_CPPFLAGS_REL}") + + # put debug info into release build and revert /OPT defaults after + # /DEBUG so that it won't degrade performance and size + # http://msdn.microsoft.com/en-us/library/xe4t6fc1(v=vs.80).aspx + # Since CMake for some odd reason strips 'incremental' and 'INCREMENTAL', we'll + # use weird casing here + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /OPT:REF /OPT:ICF /IncReMenTal:no") + SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /OPT:REF /OPT:ICF /InCreMenTal:no") + SET(LCB_CORE_CXXFLAGS "") + SET(LCB_CORE_CFLAGS "") + SET(LCB_BASIC_CFLAGS "") + SET(LCB_BASIC_CXXFLAGS "") + +ELSE() + # GCC + SET_ALL_FLAGS("-fno-strict-aliasing") + IF(WIN32) + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gstabs") + SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gstabs") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++") + SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libgcc -static-libstdc++") + ELSE() + SET_ALL_FLAGS("-pthread") + ENDIF() + SET(LCB_CORE_CFLAGS "${LCB_GNUC_C_WARNINGS} -DHAVE_VISIBILITY -fvisibility=hidden") + SET(LCB_CORE_CXXFLAGS "${LCB_GNUC_CXX_WARNINGS} -DHAVE_VISIBILITY -fvisibility=hidden") +ENDIF() + +IF(LCB_UNIVERSAL_BINARY AND (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")) + SET(CMAKE_C_FLAGS + "${CMAKE_C_FLAGS} -force_cpusubtype_ALL -arch i386 -arch x86_64") +ENDIF() diff --git a/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/GetPlatformCCInfo.cmake b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/GetPlatformCCInfo.cmake new file mode 100644 index 00000000..e2dd99fc --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/GetPlatformCCInfo.cmake @@ -0,0 +1,45 @@ +# Gets string/platform information about the specific compiler +# Defines: +# LCB_CC_STRING for the C compiler string (i.e. "msvc", "mingw") +# LCB_ARCH_STRING for the target architecture, e.g. "x86" + +# Figure out how we're building! +IF(MSVC) + IF(CMAKE_CL_64) + SET(LCB_ARCH_STRING "amd64") + ELSE() + SET(LCB_ARCH_STRING "x86") + ENDIF(CMAKE_CL_64) + + IF(MSVC80) + SET(LCB_CC_STRING "vs8") + ELSEIF(MSVC90) + SET(LCB_CC_STRING "vc9") + ELSEIF(MSVC_VERSION EQUAL 1600) + SET(LCB_CC_STRING "vc10") + ELSEIF(MSVC_VERSION EQUAL 1700) + SET(LCB_CC_STRING "vc11") + ELSEIF(MSVC_VERSION EQUAL 1800) + SET(LCB_CC_STRING "vc12") + ELSEIF(MSVC_VERSION EQUAL 1900) + SET(LCB_CC_STRING "vc14") + ENDIF() +ELSE() + IF(UNIX) + SET(LCB_CC_STRING "gcc") + EXECUTE_PROCESS( + COMMAND + uname -m + COMMAND + tr -d '\n' + OUTPUT_VARIABLE + LCB_ARCH_STRING) + ELSE() + IF(CMAKE_SIZEOF_VOID_P EQUAL 8) + SET(LCB_ARCH_STRING "amd64") + ELSE() + SET(LCB_ARCH_STRING "x86") + ENDIF() + SET(LCB_CC_STRING "mingw") + ENDIF() +ENDIF() diff --git a/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/GetVersionInfo.cmake b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/GetVersionInfo.cmake new file mode 100644 index 00000000..c8ac55a3 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/cmake/Modules/GetVersionInfo.cmake @@ -0,0 +1,70 @@ +# Gets the libcouchbase version +# Sets: +# LCB_VERSION: Version string +# LCB_CHANGESET: SCM Revision number +# LCB_VERSION_HEX Numeric hex version +# LCB_VERSION_MAJOR +# LCB_VERSION_MINOR +# LCB_VERSION_PATCH + +## Try git first ## +FIND_PROGRAM(GIT_EXECUTABLE NAMES git git.exe) +MACRO(RUNGIT outvar) + EXECUTE_PROCESS(COMMAND git ${ARGN} + OUTPUT_VARIABLE ${outvar} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + OUTPUT_STRIP_TRAILING_WHITESPACE) +ENDMACRO() + +if (GIT_EXECUTABLE) + RUNGIT(LCB_REVDESCRIBE describe --long) + RUNGIT(LCB_VERSION describe) + STRING(REPLACE "-" "_" LCB_VERSION "${LCB_VERSION}") + MESSAGE(STATUS "Sanitized VERSION=${LCB_VERSION}") + RUNGIT(LCB_VERSION_CHANGESET rev-parse HEAD) + + EXECUTE_PROCESS( + COMMAND echo ${LCB_VERSION} + COMMAND awk -F. "{printf \"0x%0.2d%0.2d%0.2d\", $1, $2, $3}" + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + OUTPUT_VARIABLE LCB_VERSION_HEX) +ENDIF() + +IF(LCB_VERSION) + # Have the version information + CONFIGURE_FILE(${LCB_GENINFODIR}/distinfo.cmake.in ${LCB_GENINFODIR}/distinfo.cmake) +ENDIF() + +# library version +IF(NOT LCB_VERSION AND EXISTS ${LCB_GENINFODIR}/distinfo.cmake) + INCLUDE(${LCB_GENINFODIR}/distinfo.cmake) +ENDIF() + +IF (NOT LCB_VERSION) + SET(LCB_NOGITVERSION ON) + SET(LCB_VERSION "2.7.0") +ENDIF() +IF (NOT LCB_VERSION_CHANGESET) + SET(LCB_VERSION_CHANGESET "0xdeadbeef") +ENDIF() +IF (NOT LCB_VERSION_HEX) + SET(LCB_VERSION_HEX 0x020700) +ENDIF() + +# Now parse the version string +STRING(REPLACE "." ";" LCB_VERSION_LIST "${LCB_VERSION}") +LIST(GET LCB_VERSION_LIST 0 LCB_VERSION_MAJOR) +LIST(GET LCB_VERSION_LIST 1 LCB_VERSION_MINOR) +LIST(GET LCB_VERSION_LIST 2 LCB_VERSION_PATCH) + +# Determine the SONAME for the library +IF(APPLE) + SET(LCB_SONAME_MAJOR "3") +ELSE() + SET(LCB_SONAME_MAJOR "2") +ENDIF() +SET(LCB_SONAME_FULL "${LCB_SONAME_MAJOR}.0.40") + +MESSAGE(STATUS + "libcouchbase ${LCB_VERSION_MAJOR},${LCB_VERSION_MINOR},${LCB_VERSION_PATCH}") +MESSAGE(STATUS "Building libcouchbase ${LCB_VERSION}/${LCB_VERSION_CHANGESET}") diff --git a/couchbase-sys/libcouchbase-2.7.0/cmake/config-cmake.h.in b/couchbase-sys/libcouchbase-2.7.0/cmake/config-cmake.h.in new file mode 100644 index 00000000..64669081 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/cmake/config-cmake.h.in @@ -0,0 +1,60 @@ +#ifdef _WIN32 +#define CONFIG_H +#define HAVE_QUERYPERFORMANCECOUNTER +#include "config_static.h" +#endif + +#ifndef CONFIG_H +#define CONFIG_H + +/* This file checks for platform-specific includes which may or may + * not be available during build time. The config_static.h file includes + * files mentioned here as well as other files whose availability can be + * inferred from compile-time macros + */ + +#cmakedefine HAVE_CLOCK_GETTIME +#cmakedefine HAVE_DLFCN_H +#cmakedefine HAVE_GETHRTIME +#cmakedefine HAVE_GETTIMEOFDAY +#cmakedefine HAVE_INTTYPES_H +#cmakedefine HAVE_NETDB_H +#cmakedefine HAVE_SETITIMER +#cmakedefine HAVE_STDINT_H +#cmakedefine HAVE_FCNTL_H +#cmakedefine HAVE_HTONLL +#cmakedefine HAVE_STRINGS_H +#cmakedefine HAVE_SYS_SOCKET_H +#cmakedefine HAVE_SYS_STAT_H +#cmakedefine HAVE_SYS_TIME_H +#cmakedefine HAVE_SYS_TYPES_H +#cmakedefine HAVE_SYS_UIO_H +#cmakedefine HAVE_UNISTD_H +#cmakedefine HAVE_ARPA_INET_H + +#ifndef HAVE_LIBEVENT +#cmakedefine HAVE_LIBEVENT +#endif + +#ifndef HAVE_LIBEVENT2 +#cmakedefine HAVE_LIBEVENT2 +#endif + +#ifndef HAVE_LIBEV3 +#cmakedefine HAVE_LIBEV3 +#endif + +#ifndef HAVE_LIBEV4 +#cmakedefine HAVE_LIBEV4 +#endif + +#ifndef HAVE_LIBUV +#cmakedefine HAVE_LIBUV +#endif + +#cmakedefine LCB_USE_HDR_HISTOGRAM + +#define LCB_LIBDIR "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}" + +#include "config_static.h" +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/cmake/configure b/couchbase-sys/libcouchbase-2.7.0/cmake/configure new file mode 100755 index 00000000..ea50464d --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/cmake/configure @@ -0,0 +1,357 @@ +#!/usr/bin/env perl + +# This script will eventually become the top-level configure directory ONCE +# we've completely migrated away from autotools + +use strict; +use warnings; +use Getopt::Long; +use File::Path; +use File::Basename qw(dirname); +use Cwd qw(getcwd realpath); +use FindBin qw($Bin $RealBin); + +# Format here is: +# [ Argument Name, +# Option format string, +# Option description, +# Default format string, +# Default value (ref) +# ] + +my @HELP_DESCRIPTIONS = ( + [ "enable-debug", "", + "Create a debug build", + "%d", \(my $DEBUG = 0) ], + + [ "with-ninja", "s", + "Generate Ninja Makefiles", + "%s", \(my $USE_NINJA = "") ], + + [ "prefix", "s", + "installation prefix", + "%s", \(my $PREFIX = "/usr/local") ], + + [ "disable-tools", "", + "Don't build tools", + "%d", \(my $NO_TOOLS = 0)], + + [ "disable-tests", "", + "Don't build tests", + "%d", \(my $NO_TESTS = 0)], + + [ "with-libuv", "s", + "Path to LIBUV installation", + "%s", \(my $LIBUV_ROOT = "")], + + [ "with-libev", "s", + "Path to LIBEV installation", + "%s", \(my $LIBEV_ROOT = "")], + + [ "with-libevent", "s", + "Path to LIBEVENT installation", + "%s", \(my $LIBEVENT_ROOT = "")], + + [ "with-cmake", "s", + "Executable to use as cmake", + "%s", \(my $CMAKE_EXE = "cmake")], + + [ "with-openssl", "s", + "Path to OpenSSL installation", + "%s", \(my $OPENSSL_ROOT = "")], + + [ "with-snappy", "s", + "Path to snappy installation", + "%s", \(my $SNAPPY_ROOT = "")], + + [ "disable-snappy", "", + "Do not build static snappy", + "%d", \(my $NO_SNAPPY = 0)], + [ "enable-snappy", "", + "Enable experimental snappy support", + "%d", \(my $ENABLE_SNAPPY = 0)], + [ "enable-static-snappy", "", + "Build the bundled snappy and do not link against the system version", + "%d", \(my $STATIC_SNAPPY = 0) ], + + [ "disable-plugins", "", + "Disable building of external plugins", + "%d", \(my $NO_PLUGINS = 0)], + + [ "disable-cxx", "", + "Disable C++ targets", + "%d", \(my $NO_CXX = 0) ], + [ "disable-couchbasemock", "", + "Don't run tests with Mock", + "%d", \(my $NO_MOCK = 0) ], + [ "enable-maintainer-mode", "", + "Maintainer mode. Everything is strict", + "%d", \(my $MAINTAINER_MODE = 0)], + + [ "enable-fat-binary", "", + "Build universal binaries on OS X", + "%d", \(my $OSX_UNIVERSAL_BUILD = 0)], + + [ "cmake-verbose", "", + "Verbose CMake output", + "%d", \(my $CMAKE_VERBOSE = 0)], + + [ "host", "s", + "Set host target for Win32 cross compilation", + "%s", \(my $CMAKE_HOST = "")], + + [ "enable-static", "", + "Build static libraries", + "%d", \(my $STATIC_LIBS = 0)], + [ "enable-embedded-libevent-plugin", "", + "Embed the libevent plugin into the library's core", + "%d", \(my $EMBED_LIBEVENT_PLUGIN = 0) ], + [ "cc", "s", + "C Compiler", + "%s", \(my $CC = "") ], + + [ "cxx", "s", + "C++ Compiler", + "%s", \(my $CXX = "") ], + [ "disable-ssl", "", + "Do not build SSL support", + "%d", \(my $NO_SSL = 0)], + [ "enable-asan", "", + "Enable LLVM AddressSanitizer support", + "%d", \(my $LCB_USE_ASAN = 0)], + [ "enable-coverage", "", + "Compile with code coverage instrumentation", + "%d", \(my $LCB_USE_COVERAGE = 0)], + [ "enable-examples", "", + "Build examples", + "%s", \(my $BUILD_EXAMPLES = 0)], + [ "build-dir", "s", + "Directory to build in", + "%s", \(my $BUILD_DIR = "build") ], + [ "help", "", + "This message", "%s", \(my $WANT_HELP = 0) ] +); + +my @HELPLINES = (); +my %GETOPT_ARGS = (); + +foreach my $optdesc (@HELP_DESCRIPTIONS) { + my ($name, $vfmt, $desc, $deflfmt, $vref) = @$optdesc; + my $go_key = $name; + if ($vfmt) { + $go_key .= "=$vfmt"; + } + $GETOPT_ARGS{$go_key} = $vref; +} + +GetOptions(%GETOPT_ARGS); + +if ($WANT_HELP) { + printf("configure.pl ... options\n"); + foreach my $optdesc (@HELP_DESCRIPTIONS) { + my ($name, $vfmt, $desc, $fmt, $deflref) = @$optdesc; + printf(" --%-20s", $name); + if (length($name) > 18) { + printf("\n"); + printf("%-20s%s\n","", $desc); + } else { + printf("%s.", $desc); + } + printf(" Default=".$fmt."\n", ${$deflref}); + } + + exit(0); +} + +my $origdir = getcwd(); +my $srcdir = realpath("$RealBin/.."); + +if ($origdir eq $srcdir) { + printf("Detected in-source build. Making '$BUILD_DIR' directory\n"); + mkpath($BUILD_DIR); + chdir($BUILD_DIR); +} + +if ($NO_CXX) { + $NO_TESTS=1; + $NO_TOOLS=1; +} + +my @CM_OPTIONS = (); + +my @VARNAMES = (qw(CC CXX CFLAGS CPPFLAGS CXXFLAGS LDFLAGS LIBS)); +my %ENVOPTIONS = (); +foreach my $var (@VARNAMES) { + $ENVOPTIONS{$var} = $ENV{$var}; +} + +foreach my $arg (@ARGV) { + my ($k,$v) = $arg =~ m/([^=]+)=(.*)/; + unless ($k && $v && exists $ENVOPTIONS{$k}) { + die("Invalid variable $arg"); + } + $ENVOPTIONS{$k} = $v; +} + +my $flags_common = ""; +my $flags_cxx = ""; +my $flags_cc = ""; + +if ($ENVOPTIONS{CC}) { $CC = $ENVOPTIONS{CC}; } +if ($ENVOPTIONS{CXX}) { $CXX = $ENVOPTIONS{CXX}; } +if ($ENVOPTIONS{CPPFLAGS}) { + $flags_common = $ENVOPTIONS{CPPFLAGS}; +} +$flags_cxx = $flags_common; +$flags_cc = $flags_common; +if ($ENVOPTIONS{CFLAGS}) { + $flags_cc .= " " . $ENVOPTIONS{CFLAGS}; +} +if ($ENVOPTIONS{CXXFLAGS}) { + $flags_cxx .= " ".$ENVOPTIONS{CXXFLAGS}; +} +if ($flags_cc) { + push @CM_OPTIONS, "-DCMAKE_C_FLAGS=$flags_cc"; +} +if ($flags_cxx) { + push @CM_OPTIONS, "-DCMAKE_CXX_FLAGS=$flags_cxx"; +} +if ($ENVOPTIONS{LDFLAGS}) { + # Flags for the linker + my $flags = $ENVOPTIONS{LDFLAGS}; + foreach my $var (qw(EXE_LINKER_FLAGS SHARED_LINKER_FLAGS)) { + push @CM_OPTIONS, "-DCMAKE_${var}=$flags"; + } +} +if ($ENVOPTIONS{LIBS}) { + warn("Don't know how to convert autotools 'LIBS' into CMake"); +} + +if ($DEBUG) { + push @CM_OPTIONS, "-DCMAKE_BUILD_TYPE=DEBUG"; +} else { + push @CM_OPTIONS, "-DCMAKE_BUILD_TYPE=RelWithDebInfo" +} + +if ($PREFIX) { + push @CM_OPTIONS, "-DCMAKE_INSTALL_PREFIX=$PREFIX"; +} +if ($NO_TESTS) { + push @CM_OPTIONS, "-DLCB_NO_TESTS=1"; +} +if ($NO_TOOLS) { + push @CM_OPTIONS, "-DLCB_NO_TOOLS=1"; +} + +if ($NO_PLUGINS) { + push @CM_OPTIONS, "-DLCB_NO_PLUGINS=1"; +} +if ($LIBUV_ROOT) { + push @CM_OPTIONS, "-DLIBUV_ROOT=$LIBUV_ROOT"; +} +if ($LIBEV_ROOT) { + push @CM_OPTIONS, "-DLIBEV_ROOT=$LIBEV_ROOT"; +} +if ($MAINTAINER_MODE) { + push @CM_OPTIONS, "-DLCB_MAINTAINER_MODE=1"; +} +if ($OSX_UNIVERSAL_BUILD) { + push @CM_OPTIONS, "-DLCB_UNIVERSAL_BINARY=1"; +} +if ($CMAKE_VERBOSE) { + push @CM_OPTIONS, "--debug-output"; +} +if ($STATIC_LIBS) { + push @CM_OPTIONS, "-DBUILD_SHARED_LIBS=OFF", "-DLCB_BUILD_STATIC=1"; +} +if ($CC) { + push @CM_OPTIONS, "-DCMAKE_C_COMPILER=$CC"; +} +if ($CXX) { + push @CM_OPTIONS, "-DCMAKE_CXX_COMPILER=$CXX"; +} +if ($USE_NINJA) { + push @CM_OPTIONS, "-DCMAKE_MAKE_PROGRAM=$USE_NINJA"; + push @CM_OPTIONS, "-GNinja"; +} else { + push @CM_OPTIONS, "-GUnix Makefiles"; +} +if ($NO_SSL) { + push @CM_OPTIONS, "-DLCB_NO_SSL=$NO_SSL"; +} +if ($OPENSSL_ROOT) { + push @CM_OPTIONS, "-DOPENSSL_ROOT_DIR=$OPENSSL_ROOT"; +} +if ($SNAPPY_ROOT) { + push @CM_OPTIONS, "-DSNAPPY_ROOT=$SNAPPY_ROOT"; +} +if ($NO_SNAPPY) { + push @CM_OPTIONS, "-DLCB_NO_SNAPPY=1"; +} +if ($ENABLE_SNAPPY) { + push @CM_OPTIONS, "-DLCB_NO_SNAPPY=0"; +} +if ($STATIC_SNAPPY) { + push @CM_OPTIONS, "-DLCB_STATIC_SNAPPY"; +} +if ($LCB_USE_ASAN) { + push @CM_OPTIONS, "-DLCB_USE_ASAN=1"; +} +if ($LCB_USE_COVERAGE) { + push @CM_OPTIONS, "-DLCB_USE_COVERAGE=1"; +} +if ($NO_MOCK) { + push @CM_OPTIONS, "-DLCB_NO_MOCK=1"; +} +if ($EMBED_LIBEVENT_PLUGIN) { + push @CM_OPTIONS, "-DLCB_EMBED_PLUGIN_LIBEVENT=1"; +} +if ($BUILD_EXAMPLES) { + push @CM_OPTIONS, "-DLCB_BUILD_EXAMPLES=1"; +} + +if ($CMAKE_HOST) { + # Write the toolchain file + my $fname = "TOOLCHAIN-$CMAKE_HOST.cmake"; + open my $fp, ">", $fname; + print $fp <", "Makefile"); + print $fh < +#include +#include +#include +#include +#include +#include +#include +#include "cJSON.h" + +static int cJSON_strcasecmp(const char *s1,const char *s2) +{ + if (!s1) return (s1==s2)?0:1;if (!s2) return 1; + for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; + return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); +} + +static void *(*cJSON_malloc)(size_t sz) = malloc; +static void (*cJSON_free)(void *ptr) = free; + +static char* cJSON_strdup(const char* str) +{ + size_t len; + char* copy; + + len = strlen(str) + 1; + if (!(copy = (char*)cJSON_malloc(len))) return 0; + memcpy(copy,str,len); + return copy; +} + +void cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (!hooks) { /* Reset hooks */ + cJSON_malloc = malloc; + cJSON_free = free; + return; + } + + cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; + cJSON_free = (hooks->free_fn)?hooks->free_fn:free; +} + +/* The size of pool allocation groups. Note that the allocation + pools are only used during parsing, and not generation. + Additionally, this default value is based on empirical + evidence of best performance with Couchbase configuration + data */ +#ifndef CJSON_POOL_ALLOC_SIZE + #define CJSON_POOL_ALLOC_SIZE 1024 +#endif + +/* Creates a new allocation pool. */ +static cJSON_PoolBlock *cJSON_New_PoolBlock(cJSON_Pool *pool) +{ + int i; + cJSON *item, *new_alloc; + cJSON_PoolBlock *new_block; + + if (CJSON_POOL_ALLOC_SIZE <= 0) { + return 0; + } + + new_block = (cJSON_PoolBlock*)cJSON_malloc( + sizeof(cJSON_PoolBlock) + sizeof(cJSON)*CJSON_POOL_ALLOC_SIZE); + if (!new_block) { + return 0; + } + + new_alloc = (cJSON*)(new_block+1); + + memset(new_block, 0, + sizeof(cJSON_PoolBlock) + sizeof(cJSON)*CJSON_POOL_ALLOC_SIZE); + + new_block->next = pool->blocks; + pool->blocks = new_block; + + for (i=0, item=new_alloc; inext = pool->free_items; + pool->free_items = item; + } + + return new_block; +} + +/* This will create an allocation pool */ +static cJSON_Pool * cJSON_New_Pool(void) +{ + if (CJSON_POOL_ALLOC_SIZE <= 0) { + return 0; + } else { + cJSON_Pool * pool = (cJSON_Pool*)cJSON_malloc(sizeof(cJSON_Pool)); + if (!pool) { + return 0; + } + + memset(pool, 0, sizeof(cJSON_Pool)); + pool->free_items = 0; + pool->blocks = 0; + pool->refcount = 0; + + return pool; + } +} + +/* This will destroy an allocation pool */ +static void cJSON_Destroy_Pool(cJSON_Pool *pool) +{ + cJSON_PoolBlock *cur = pool->blocks, *next; + while (cur) { + next = cur->next; + /* this frees all the allocations as well */ + cJSON_free(cur); + cur = next; + } + cJSON_free(pool); +} + +/* allocate new item from a pool */ +static cJSON *cJSON_Pool_New_Item(cJSON_Pool *pool) +{ + if (!pool->free_items) { + cJSON_New_PoolBlock(pool); + } + if (pool->free_items) { + cJSON *node = pool->free_items; + pool->free_items = node->next; + pool->refcount++; + node->next = 0; + node->alloc_pool = pool; + /* all items in the pool are already zeroed */ + return node; + } + return 0; +} +static void cJSON_Pool_Free_Item(cJSON_Pool *pool, cJSON *node) +{ + /* Note that this code intentially does not return the item + to the pool if we are destroying the pool anyways. */ + assert(pool->refcount); + if (--pool->refcount == 0) { + cJSON_Destroy_Pool(pool); + } else { + memset(node, 0, sizeof(cJSON)); + node->next = pool->free_items; + pool->free_items = node; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(cJSON *parent) +{ + cJSON* node = 0; + if (parent && parent->alloc_pool) { + node = cJSON_Pool_New_Item(parent->alloc_pool); + } + if (!node) { + node = (cJSON*)cJSON_malloc(sizeof(cJSON)); + if (node) { + memset(node,0,sizeof(cJSON)); + } + } + return node; +} + +static void cJSON_Free_Item(cJSON *node) +{ + cJSON_Pool *pool = node->alloc_pool; + if (pool) { + cJSON_Pool_Free_Item(pool, node); + } else { + cJSON_free(node); + } +} + +/* Delete a cJSON structure. */ +void cJSON_Delete(cJSON *c) +{ + cJSON *next; + while (c) + { + next=c->next; + if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); + if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); + if (c->string) cJSON_free(c->string); + cJSON_Free_Item(c); + c=next; + } +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static const char *parse_number(cJSON *item,const char *num) +{ + double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; + + /* Could use sscanf for this? */ + if (*num=='-') sign=-1,num++; /* Has sign? */ + if (*num=='0') num++; /* is zero */ + if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ + if (*num=='.') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ + if (*num=='e' || *num=='E') /* Exponent? */ + { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ + while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ + } + + n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ + + item->valuedouble=n; + item->valueint=(int)n; + item->type=cJSON_Number; + return num; +} + +/* Render the number nicely from the given item into a string. */ +static char *print_number(cJSON *item) +{ + char *str; + double d=item->valuedouble; + if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) + { + str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ + sprintf(str,"%d",item->valueint); + } + else + { + str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ + if (fabs(floor(d)-d)<=DBL_EPSILON) sprintf(str,"%.0f",d); + else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); + else sprintf(str,"%f",d); + } + return str; +} + +/* Parse the input text into an unescaped cstring, and populate item. */ +static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; +static const char *parse_string(cJSON *item,const char *str) +{ + const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc; + if (*str!='\"') return 0; /* not a string! */ + + while (*ptr!='\"' && (unsigned char)*ptr>31 && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ + + out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ + if (!out) return 0; + + ptr=str+1;ptr2=out; + while (*ptr!='\"' && (unsigned char)*ptr>31) + { + if (*ptr!='\\') *ptr2++=*ptr++; + else + { + ptr++; + switch (*ptr) + { + case 'b': *ptr2++='\b'; break; + case 'f': *ptr2++='\f'; break; + case 'n': *ptr2++='\n'; break; + case 'r': *ptr2++='\r'; break; + case 't': *ptr2++='\t'; break; + case 'u': /* transcode utf16 to utf8. DOES NOT SUPPORT SURROGATE PAIRS CORRECTLY. */ + sscanf(ptr+1,"%4x",&uc); /* get the unicode char. */ + len=3;if (uc<0x80) len=1;else if (uc<0x800) len=2;ptr2+=len; + + switch (len) { + case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 1: *--ptr2 =(uc | firstByteMark[len]); + } + ptr2+=len;ptr+=4; + break; + default: *ptr2++=*ptr; break; + } + ptr++; + } + } + *ptr2=0; + if (*ptr=='\"') ptr++; + item->valuestring=out; + item->type=cJSON_String; + return ptr; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static char *print_string_ptr(const char *str) +{ + const char *ptr;char *ptr2,*out;int len=0; + + if (!str) return cJSON_strdup(""); + ptr=str;while (*ptr && ++len) {if ((unsigned char)*ptr<32 || *ptr=='\"' || *ptr=='\\') len++;ptr++;} + + out=(char*)cJSON_malloc(len+3); + ptr2=out;ptr=str; + *ptr2++='\"'; + while (*ptr) + { + if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; + else + { + *ptr2++='\\'; + switch (*ptr++) + { + case '\\': *ptr2++='\\'; break; + case '\"': *ptr2++='\"'; break; + case '\b': *ptr2++='b'; break; + case '\f': *ptr2++='f'; break; + case '\n': *ptr2++='n'; break; + case '\r': *ptr2++='r'; break; + case '\t': *ptr2++='t'; break; + default: ptr2--; break; /* eviscerate with prejudice. */ + } + } + } + *ptr2++='\"';*ptr2++=0; + return out; +} +/* Invote print_string_ptr (which is useful) on an item. */ +static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} + +/* Predeclare these prototypes. */ +static const char *parse_value(cJSON *item,const char *value); +static char *print_value(cJSON *item,int depth,int fmt); +static const char *parse_array(cJSON *item,const char *value); +static char *print_array(cJSON *item,int depth,int fmt); +static const char *parse_object(cJSON *item,const char *value); +static char *print_object(cJSON *item,int depth,int fmt); + +/* Utility to jump whitespace and cr/lf */ +static const char *skip(const char *in) {while (in && (unsigned char)*in<=32) in++; return in;} + +/* Parse an object - create a new root, and populate. */ +cJSON *cJSON_Parse(const char *value) +{ + cJSON_Pool *pool = cJSON_New_Pool(); + cJSON *c = 0; + if (pool) { + c = cJSON_Pool_New_Item(pool); + } + if (!c) { + c = cJSON_New_Item(0); + } + if (!c) return 0; /* memory fail */ + + c->alloc_pool = pool; + + if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;} + return c; +} + +/* Render a cJSON item/entity/structure to text. */ +char *cJSON_Print(cJSON *item) {return print_value(item,0,1);} +char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} + +/* Parser core - when encountering text, process appropriately. */ +static const char *parse_value(cJSON *item,const char *value) +{ + if (!value) return 0; /* Fail on null. */ + if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } + if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } + if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } + if (*value=='\"') { return parse_string(item,value); } + if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } + if (*value=='[') { return parse_array(item,value); } + if (*value=='{') { return parse_object(item,value); } + + return 0; /* failure. */ +} + +/* Render a value to text. */ +static char *print_value(cJSON *item,int depth,int fmt) +{ + char *out=0; + if (!item) return 0; + switch ((item->type)&255) + { + case cJSON_NULL: out=cJSON_strdup("null"); break; + case cJSON_False: out=cJSON_strdup("false");break; + case cJSON_True: out=cJSON_strdup("true"); break; + case cJSON_Number: out=print_number(item);break; + case cJSON_String: out=print_string(item);break; + case cJSON_Array: out=print_array(item,depth,fmt);break; + case cJSON_Object: out=print_object(item,depth,fmt);break; + } + return out; +} + +/* Build an array from input text. */ +static const char *parse_array(cJSON *item,const char *value) +{ + cJSON *child; + if (*value!='[') return 0; /* not an array! */ + + item->type=cJSON_Array; + value=skip(value+1); + if (*value==']') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(item); + if (!item->child) return 0; /* memory fail */ + value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item(item))) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_value(child,skip(value+1))); + if (!value) return 0; /* memory fail */ + } + + if (*value==']') return value+1; /* end of array */ + return 0; /* malformed. */ +} + +/* Render an array to text */ +static char *print_array(cJSON *item,int depth,int fmt) +{ + char **entries; + char *out=0,*ptr,*ret;size_t len=5; + cJSON *child=item->child; + int numentries=0,i=0,fail=0; + + /* How many entries in the array? */ + while (child) numentries++,child=child->next; + /* Allocate an array to hold the values for each */ + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + memset(entries,0,numentries*sizeof(char*)); + /* Retrieve all the results: */ + child=item->child; + while (child && !fail) + { + ret=print_value(child,depth+1,fmt); + entries[i++]=ret; + if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; + child=child->next; + } + + /* If we didn't fail, try to malloc the output string */ + if (!fail) out=cJSON_malloc(len); + /* If that fails, we fail. */ + if (!out) fail=1; + + /* Handle failure. */ + if (fail) + { + for (i=0;itype=cJSON_Object; + value=skip(value+1); + if (*value=='}') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(item); + value=skip(parse_string(child,skip(value))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') return 0; /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item(item))) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_string(child,skip(value+1))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') return 0; /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + } + + if (*value=='}') return value+1; /* end of array */ + return 0; /* malformed. */ +} + +/* Render an object to text. */ +static char *print_object(cJSON *item,int depth,int fmt) +{ + char **entries=0,**names=0; + char *out=0,*ptr,*ret,*str;size_t len=7; int i=0,j; + cJSON *child=item->child; + int numentries=0,fail=0; + /* Count the number of entries. */ + while (child) numentries++,child=child->next; + /* Allocate space for the names and the objects */ + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + names=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!names) {cJSON_free(entries);return 0;} + memset(entries,0,sizeof(char*)*numentries); + memset(names,0,sizeof(char*)*numentries); + + /* Collect all the results into our arrays: */ + child=item->child;depth++;if (fmt) len+=depth; + while (child) + { + names[i]=str=print_string_ptr(child->string); + entries[i++]=ret=print_value(child,depth,fmt); + if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; + child=child->next; + } + + /* Try to allocate the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + if (!out) fail=1; + + /* Handle failure */ + if (fail) + { + for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} +cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} +cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} +/* Utility for handling references. */ +static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item(item);memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} + +/* Add item to array/object. */ +void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} +void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} +void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} +void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} + +cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; + if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} +void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} +cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} +void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} + +/* Replace array/object items with new ones. */ +void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; + newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; + if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} +void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} + +/* Create basic types: */ +cJSON *cJSON_CreateNull() {cJSON *item=cJSON_New_Item(0);item->type=cJSON_NULL;return item;} +cJSON *cJSON_CreateTrue() {cJSON *item=cJSON_New_Item(0);item->type=cJSON_True;return item;} +cJSON *cJSON_CreateFalse() {cJSON *item=cJSON_New_Item(0);item->type=cJSON_False;return item;} +cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item(0);item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;return item;} +cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item(0);item->type=cJSON_String;item->valuestring=cJSON_strdup(string);return item;} +cJSON *cJSON_CreateArray() {cJSON *item=cJSON_New_Item(0);item->type=cJSON_Array;return item;} +cJSON *cJSON_CreateObject() {cJSON *item=cJSON_New_Item(0);item->type=cJSON_Object;return item;} + +/* Create Arrays: */ +cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;ichild=n;else suffix_object(p,n);p=n;}return a;} diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/cJSON/cJSON.h b/couchbase-sys/libcouchbase-2.7.0/contrib/cJSON/cJSON.h new file mode 100755 index 00000000..acf25a1d --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/cJSON/cJSON.h @@ -0,0 +1,158 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* cJSON Types: */ +#define cJSON_False 0 +#define cJSON_True 1 +#define cJSON_NULL 2 +#define cJSON_Number 3 +#define cJSON_String 4 +#define cJSON_Array 5 +#define cJSON_Object 6 + +#define cJSON_IsReference 256 + +typedef struct cJSON_PoolBlock { + struct cJSON_PoolBlock * next; + + /* Not visible here, however the allocation system + will allocate this structure along with all the + elements at once, where the elements will follow + this structure */ +} cJSON_PoolBlock; + +typedef struct cJSON_Pool { + /* linked list of allocation blocks */ + cJSON_PoolBlock * blocks; + + /* the number of references to this pool */ + unsigned int refcount; + + /* linked list of available cJSON allocations */ + struct cJSON * free_items; +} cJSON_Pool; + +/* The cJSON structure: */ +typedef struct cJSON { + struct cJSON *next,*prev; /* next/prev allow you to walk + array/object + chains. Alternatively, use + GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *child; /* An array or object item will have a + child pointer pointing to a chain of + the items in the array/object. */ + + int type; /* The type of the item, as above. */ + + char *valuestring; /* The item's string, if type==cJSON_String */ + int valueint; /* The item's number, if type==cJSON_Number */ + double valuedouble; /* The item's number, if type==cJSON_Number */ + + char *string; /* The item's name string, if this item is the + child of, or is in the list of subitems of an + object. */ + + struct cJSON_Pool *alloc_pool; +} cJSON; + +typedef struct cJSON_Hooks { + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +/* Supply malloc, realloc and free functions to cJSON */ +extern void cJSON_InitHooks(cJSON_Hooks* hooks); + + +/* Supply a block of JSON, and this returns a cJSON object you can + interrogate. Call cJSON_Delete when finished. */ +extern cJSON *cJSON_Parse(const char *value); +/* Render a cJSON entity to text for transfer/storage. Free the char* + when finished. */ +extern char *cJSON_Print(cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any + formatting. Free the char* when finished. */ +extern char *cJSON_PrintUnformatted(cJSON *item); +/* Delete a cJSON entity and all subentities. */ +extern void cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +extern int cJSON_GetArraySize(cJSON *array); +/* Retrieve item number "item" from array "array". Returns NULL if + unsuccessful. */ +extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); +/* Get item "string" from object. Case insensitive. */ +extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); + +/* These calls create a cJSON item of the appropriate type. */ +extern cJSON *cJSON_CreateNull(void); +extern cJSON *cJSON_CreateTrue(void); +extern cJSON *cJSON_CreateFalse(void); +extern cJSON *cJSON_CreateNumber(double num); +extern cJSON *cJSON_CreateString(const char *string); +extern cJSON *cJSON_CreateArray(void); +extern cJSON *cJSON_CreateObject(void); + +/* These utilities create an Array of count items. */ +extern cJSON *cJSON_CreateIntArray(int *numbers,int count); +extern cJSON *cJSON_CreateFloatArray(float *numbers,int count); +extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count); +extern cJSON *cJSON_CreateStringArray(const char **strings,int count); + +/* Append item to the specified array/object. */ +extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); +/* Append reference to item to the specified array/object. Use this + when you want to add an existing cJSON to a new cJSON, but don't + want to corrupt your existing cJSON. */ +extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); +extern void cJSON_DeleteItemFromArray(cJSON *array,int which); +extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); +extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); + +/* Update array items. */ +extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); +extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); + +#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) +#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) +#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/CMakeLists.txt b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/CMakeLists.txt new file mode 100644 index 00000000..961a0047 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/CMakeLists.txt @@ -0,0 +1,9 @@ +INCLUDE_DIRECTORIES(src) +FILE(GLOB CBSASL_SRC src/*.c) +FILE(GLOB CRAM_SRC src/cram-md5/*.c) + +ADD_LIBRARY(cbsasl OBJECT ${CBSASL_SRC} ${CRAM_SRC}) +SET_TARGET_PROPERTIES(cbsasl + PROPERTIES + POSITION_INDEPENDENT_CODE TRUE + COMPILE_FLAGS "${LCB_CORE_CFLAGS}") diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/COPYING b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/COPYING new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/COPYING @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/include/cbsasl/cbsasl.h b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/include/cbsasl/cbsasl.h new file mode 100644 index 00000000..49617212 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/include/cbsasl/cbsasl.h @@ -0,0 +1,217 @@ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INCLUDE_CBSASL_CBSASL_H_ +#define INCLUDE_CBSASL_CBSASL_H_ 1 +#define CBSASL_PUBLIC_API + +#ifdef __cplusplus +extern "C" { +#endif + + typedef enum cbsasl_error { + SASL_OK, + SASL_CONTINUE, + SASL_FAIL, + SASL_NOMEM, + SASL_BADPARAM, + SASL_NOMECH, + SASL_NOUSER + } + cbsasl_error_t; + + typedef struct { + unsigned long len; + unsigned char data[1]; + } cbsasl_secret_t; + + typedef struct { + unsigned long id; + int (*proc)(void); + void *context; + } cbsasl_callback_t; + + typedef struct cbsasl_conn_st cbsasl_conn_t; + + typedef cbsasl_error_t (*cbsasl_init_fn)(void); + typedef cbsasl_error_t (*cbsasl_start_fn)(cbsasl_conn_t *); + typedef cbsasl_error_t (*cbsasl_step_fn)(cbsasl_conn_t *, const char *, + unsigned, const char **, unsigned *); + + typedef struct cbsasl_mechs { + const char *name; + cbsasl_init_fn init; + cbsasl_start_fn start; + cbsasl_step_fn step; + } cbsasl_mechs_t; + + struct cbsasl_client_conn_t { + char *userdata; + int plain; + int (*get_username)(void *context, int id, const char **result, + unsigned int *len); + void *get_username_ctx; + int (*get_password)(cbsasl_conn_t *conn, void *context, int id, + cbsasl_secret_t **psecret); + void *get_password_ctx; + }; + + struct cbsasl_server_conn_t { + char *username; + char *config; + char *sasl_data; + unsigned int sasl_data_len; + cbsasl_mechs_t mech; + }; + + struct cbsasl_conn_st { + int client; + union { + struct cbsasl_client_conn_t client; + struct cbsasl_server_conn_t server; + } c; + }; + + /** + * Lists all of the mechanisms this sasl server supports + * + * @param mechs A string containing all supported mechanism names + * @param mechslen The length of the mechs string + * + * @return Whether or not an error occured while getting the mechanism list + */ + CBSASL_PUBLIC_API + cbsasl_error_t cbsasl_list_mechs(const char **mechs, + unsigned *mechslen); + + /** + * Initializes the sasl server + * + * This function initializes the server by loading passwords from the cbsasl + * password file. This function should only be called once. + * + * @return Whether or not the sasl server initialization was successful + */ + CBSASL_PUBLIC_API + cbsasl_error_t cbsasl_server_init(void); + + /** + * Creates a sasl connection and begins authentication + * + * When a client receives a request for sasl authentication this function is + * called in order to initialize the sasl connection based on the mechanism + * specified. + * + * @param conn The connection context for this session + * @param mechanism The mechanism that will be used for authentication + * + * @return Whether or not the mecahnism initialization was successful + */ + CBSASL_PUBLIC_API + cbsasl_error_t cbsasl_server_start(cbsasl_conn_t **conn, + const char *mech, + const char *clientin, + unsigned int clientinlen, + unsigned char **serverout, + unsigned int *serveroutlen); + + /** + * Does username/password authentication + * + * After the sasl connection is initialized the step function is called to + * check credentials. + * + * @return Whether or not the sasl step was successful + */ + CBSASL_PUBLIC_API + cbsasl_error_t cbsasl_server_step(cbsasl_conn_t *conn, + const char *input, + unsigned inputlen, + const char **output, + unsigned *outputlen); + + /** + * Frees up funushed sasl connections + * + * @param conn The sasl connection to free + */ + CBSASL_PUBLIC_API + void cbsasl_dispose(cbsasl_conn_t **pconn); + + /** + * Refresh the internal data (this may result in loading password + * databases etc) + * + * @return Whether or not the operation was successful + */ + CBSASL_PUBLIC_API + cbsasl_error_t cbsasl_server_refresh(void); + + typedef enum { + CBSASL_USERNAME = 0, + CBSASL_CONFIG = 1 + } cbsasl_prop_t; + + CBSASL_PUBLIC_API + cbsasl_error_t cbsasl_getprop(cbsasl_conn_t *conn, + cbsasl_prop_t propnum, + const void **pvalue); + + CBSASL_PUBLIC_API + cbsasl_error_t cbsasl_setprop(cbsasl_conn_t *conn, + cbsasl_prop_t propnum, + const void *pvalue); + + /* Client API */ + + + /* define the different callback id's we support */ +#define CBSASL_CB_USER 1 +#define CBSASL_CB_AUTHNAME 2 +#define CBSASL_CB_PASS 3 +#define CBSASL_CB_LIST_END 4 + + CBSASL_PUBLIC_API + cbsasl_error_t cbsasl_client_new(const char *service, + const char *serverFQDN, + const char *iplocalport, + const char *ipremoteport, + const cbsasl_callback_t *prompt_supp, + unsigned int flags, + cbsasl_conn_t **pconn); + + CBSASL_PUBLIC_API + cbsasl_error_t cbsasl_client_start(cbsasl_conn_t *conn, + const char *mechlist, + void **prompt_need, + const char **clientout, + unsigned int *clientoutlen, + const char **mech); + + CBSASL_PUBLIC_API + cbsasl_error_t cbsasl_client_step(cbsasl_conn_t *conn, + const char *serverin, + unsigned int serverinlen, + void **not_used, + const char **clientout, + unsigned int *clientoutlen); + +#ifdef __cplusplus +} +#endif + + +#endif /* INCLUDE_CBSASL_CBSASL_H_ */ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/client.c b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/client.c new file mode 100644 index 00000000..06225019 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/client.c @@ -0,0 +1,205 @@ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cbsasl/cbsasl.h" +#include "cram-md5/hmac.h" +#include "util.h" +#include +#include +#include + +CBSASL_PUBLIC_API +cbsasl_error_t cbsasl_client_new(const char *service, + const char *serverFQDN, + const char *iplocalport, + const char *ipremoteport, + const cbsasl_callback_t *prompt_supp, + unsigned flags, + cbsasl_conn_t **pconn) +{ + cbsasl_conn_t *conn; + cbsasl_callback_t *callbacks = (cbsasl_callback_t*)prompt_supp; + int ii; + + if (prompt_supp == NULL) { + return SASL_BADPARAM; + } + + conn = calloc(1, sizeof(*conn)); + if (conn == NULL) { + return SASL_NOMEM; + } + + conn->client = 1; + + ii = 0; + /* Locate the callbacks */ + while (callbacks[ii].id != CBSASL_CB_LIST_END) { + if (callbacks[ii].id == CBSASL_CB_USER || callbacks[ii].id == CBSASL_CB_AUTHNAME) { + union { + int (*get)(void *, int, const char **, unsigned int *); + int (*proc)(void); + } hack; + hack.proc = callbacks[ii].proc; + conn->c.client.get_username = hack.get; + conn->c.client.get_username_ctx = callbacks[ii].context; + } else if (callbacks[ii].id == CBSASL_CB_PASS) { + union { + int (*get)(cbsasl_conn_t *, void *, int, cbsasl_secret_t **); + int (*proc)(void); + } hack; + hack.proc = callbacks[ii].proc; + conn->c.client.get_password = hack.get; + conn->c.client.get_password_ctx = callbacks[ii].context; + } + ++ii; + } + + if (conn->c.client.get_username == NULL || conn->c.client.get_password == NULL) { + cbsasl_dispose(&conn); + return SASL_NOUSER; + } + + *pconn = conn; + + (void)service; + (void)serverFQDN; + (void)iplocalport; + (void)ipremoteport; + (void)flags; + + return SASL_OK; +} + +CBSASL_PUBLIC_API +cbsasl_error_t cbsasl_client_start(cbsasl_conn_t *conn, + const char *mechlist, + void **prompt_need, + const char **clientout, + unsigned int *clientoutlen, + const char **mech) +{ + if (conn->client == 0) { + return SASL_BADPARAM; + } + + if (strstr(mechlist, "CRAM-MD5") == NULL) { + if (strstr(mechlist, "PLAIN") == NULL) { + return SASL_NOMECH; + } + + *mech = "PLAIN"; + conn->c.client.plain = 1; + } else { + *mech = "CRAM-MD5"; + conn->c.client.plain = 0; + } + + + if (conn->c.client.plain) { + const char *usernm = NULL; + unsigned int usernmlen; + cbsasl_secret_t *pass; + + cbsasl_error_t ret; + ret = conn->c.client.get_username(conn->c.client.get_username_ctx, + CBSASL_CB_USER, + &usernm, &usernmlen); + if (ret != SASL_OK) { + return ret; + } + + ret = conn->c.client.get_password(conn, conn->c.client.get_password_ctx, + CBSASL_CB_PASS, + &pass); + if (ret != SASL_OK) { + return ret; + } + + conn->c.client.userdata = calloc(usernmlen + 1 + pass->len + 1, 1); + if (conn->c.client.userdata == NULL) { + return SASL_NOMEM; + } + + memcpy(conn->c.client.userdata + 1, usernm, usernmlen); + memcpy(conn->c.client.userdata + usernmlen + 2, pass->data, pass->len); + *clientout = conn->c.client.userdata; + *clientoutlen = (unsigned int)(usernmlen + 2 + pass->len); + } else { + /* CRAM-MD5 */ + *clientout = NULL; + *clientoutlen = 0; + } + + (void)prompt_need; + return SASL_OK; +} + +CBSASL_PUBLIC_API +cbsasl_error_t cbsasl_client_step(cbsasl_conn_t *conn, + const char *serverin, + unsigned int serverinlen, + void **not_used, + const char **clientout, + unsigned int *clientoutlen) +{ + unsigned char digest[DIGEST_LENGTH]; + char md5string[DIGEST_LENGTH * 2]; + const char *usernm = NULL; + unsigned int usernmlen; + cbsasl_secret_t *pass; + cbsasl_error_t ret; + + if (conn->client == 0) { + return SASL_BADPARAM; + } + + if (conn->c.client.plain) { + /* Shouldn't be called during plain auth */ + return SASL_BADPARAM; + } + + ret = conn->c.client.get_username(conn->c.client.get_username_ctx, + CBSASL_CB_USER, &usernm, &usernmlen); + if (ret != SASL_OK) { + return ret; + } + + ret = conn->c.client.get_password(conn, conn->c.client.get_password_ctx, + CBSASL_CB_PASS, &pass); + if (ret != SASL_OK) { + return ret; + } + + free(conn->c.client.userdata); + conn->c.client.userdata = calloc(usernmlen + 1 + sizeof(md5string) + 1, 1); + if (conn->c.client.userdata == NULL) { + return SASL_NOMEM; + } + + cbsasl_hmac_md5((unsigned char*)serverin, serverinlen, pass->data, + pass->len, digest); + cbsasl_hex_encode(md5string, (char *) digest, DIGEST_LENGTH); + memcpy(conn->c.client.userdata, usernm, usernmlen); + conn->c.client.userdata[usernmlen] = ' '; + memcpy(conn->c.client.userdata + usernmlen + 1, md5string, + sizeof(md5string)); + + *clientout = conn->c.client.userdata; + *clientoutlen = strlen(conn->c.client.userdata); + + return SASL_CONTINUE; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/common.c b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/common.c new file mode 100644 index 00000000..47186b38 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/common.c @@ -0,0 +1,46 @@ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cbsasl/cbsasl.h" +#include "util.h" +#include + +CBSASL_PUBLIC_API +void cbsasl_dispose(cbsasl_conn_t **conn) +{ + if (*conn != NULL) { + if ((*conn)->client) { + free((*conn)->c.client.userdata); + } else { + free((*conn)->c.server.username); + free((*conn)->c.server.config); + free((*conn)->c.server.sasl_data); + } + + free(*conn); + *conn = NULL; + } +} + +static const char *hexchar = "0123456789abcdef"; +void cbsasl_hex_encode(char *dest, const char *src, size_t srclen) +{ + size_t i; + for (i = 0; i < srclen; i++) { + dest[i * 2] = hexchar[(src[i] >> 4) & 0xF]; + dest[i * 2 + 1] = hexchar[src[i] & 0xF]; + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/cram-md5/hmac.c b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/cram-md5/hmac.c new file mode 100644 index 00000000..1def0fd1 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/cram-md5/hmac.c @@ -0,0 +1,67 @@ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hmac.h" +#include "md5.h" +#include + +/** + * The code in this function is based on the code provided in rfc 2104. + * http://www.ietf.org/rfc/rfc2104.txt + */ +void cbsasl_hmac_md5(unsigned char *text, + int textlen, + unsigned char *key, + int keylen, + unsigned char *digest) +{ + MD5_CTX context; + unsigned char k_ipad[65]; + unsigned char k_opad[65]; + unsigned char tk[16]; + int i; + + if (keylen > 64) { + MD5_CTX ctx; + cbsasl_MD5_Init(&ctx); + cbsasl_MD5_Update(&ctx, key, keylen); + cbsasl_MD5_Final(tk, &ctx); + key = tk; + keylen = 16; + } + + memset(k_ipad, 0, sizeof(k_ipad)); + memset(k_opad, 0, sizeof(k_opad)); + memcpy(k_ipad, key, keylen); + memcpy(k_opad, key, keylen); + + for (i = 0; i < 64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + /* Perform inner md5 */ + cbsasl_MD5_Init(&context); + cbsasl_MD5_Update(&context, k_ipad, 64); + cbsasl_MD5_Update(&context, text, textlen); + cbsasl_MD5_Final(digest, &context); + + /* Perform outer md5 */ + cbsasl_MD5_Init(&context); + cbsasl_MD5_Update(&context, k_opad, 64); + cbsasl_MD5_Update(&context, digest, 16); + cbsasl_MD5_Final(digest, &context); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/cram-md5/hmac.h b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/cram-md5/hmac.h new file mode 100644 index 00000000..e8fdeaef --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/cram-md5/hmac.h @@ -0,0 +1,33 @@ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SRC_CRAM_MD5_HMAC_H_ +#define SRC_CRAM_MD5_HMAC_H_ 1 +#define DIGEST_LENGTH 16 + +/** + * Perform hmac on md5 + * + * The code in this function is based on the code provided in rfc 2104. + * http://www.ietf.org/rfc/rfc2104.txt + */ +void cbsasl_hmac_md5(unsigned char *text, + int text_len, + unsigned char *key, + int keylen, + unsigned char *digest); + +#endif /* SRC_CRAM_MD5_HMAC_H_ */ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/cram-md5/md5.c b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/cram-md5/md5.c new file mode 100644 index 00000000..95acf900 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/cram-md5/md5.c @@ -0,0 +1,296 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there's no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. + */ + +#ifndef HAVE_OPENSSL + +#include + +#include "md5.h" + +/* + * The basic MD5 functions. + * + * F and G are optimized compared to their RFC 1321 definitions for + * architectures that lack an AND-NOT instruction, just like in Colin Plumb's + * implementation. + */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +/* + * The MD5 transformation for all four rounds. + */ +#define STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ + (a) += (b); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them + * in a properly aligned word in host byte order. + * + * The check for little-endian architectures that tolerate unaligned + * memory accesses is just an optimization. Nothing will break if it + * doesn't work. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +#define SET(n) \ + (*(MD5_u32plus *)&ptr[(n) * 4]) +#define GET(n) \ + SET(n) +#else +#define SET(n) \ + (ctx->block[(n)] = \ + (MD5_u32plus)ptr[(n) * 4] | \ + ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ + ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ + ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) +#define GET(n) \ + (ctx->block[(n)]) +#endif + +/* + * This processes one or more 64-byte data blocks, but does NOT update + * the bit counters. There are no alignment requirements. + */ +static void *body(MD5_CTX *ctx, void *data, unsigned long size) +{ + unsigned char *ptr; + MD5_u32plus a, b, c, d; + MD5_u32plus saved_a, saved_b, saved_c, saved_d; + + ptr = data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + + /* Round 1 */ + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) + STEP(F, c, d, a, b, SET(2), 0x242070db, 17) + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + + /* Round 2 */ + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) + STEP(G, d, a, b, c, GET(10), 0x02441453, 9) + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + + /* Round 3 */ + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) + STEP(H, d, a, b, c, GET(8), 0x8771f681, 11) + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) + STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23) + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) + STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11) + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) + STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23) + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) + STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11) + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) + STEP(H, b, c, d, a, GET(6), 0x04881d05, 23) + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) + STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11) + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) + STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23) + + /* Round 4 */ + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +void cbsasl_MD5_Init(MD5_CTX *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; +} + +void cbsasl_MD5_Update(MD5_CTX *ctx, void *data, unsigned long size) +{ + MD5_u32plus saved_lo; + unsigned long used, free; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) { + ctx->hi++; + } + ctx->hi += size >> 29; + + used = saved_lo & 0x3f; + + if (used) { + free = 64 - used; + + if (size < free) { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, free); + data = (unsigned char *)data + free; + size -= free; + body(ctx, ctx->buffer, 64); + } + + if (size >= 64) { + data = body(ctx, data, size & ~(unsigned long)0x3f); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); +} + +void cbsasl_MD5_Final(unsigned char *result, MD5_CTX *ctx) +{ + unsigned long used, free; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + free = 64 - used; + + if (free < 8) { + memset(&ctx->buffer[used], 0, free); + body(ctx, ctx->buffer, 64); + used = 0; + free = 64; + } + + memset(&ctx->buffer[used], 0, free - 8); + + ctx->lo <<= 3; + ctx->buffer[56] = ctx->lo; + ctx->buffer[57] = ctx->lo >> 8; + ctx->buffer[58] = ctx->lo >> 16; + ctx->buffer[59] = ctx->lo >> 24; + ctx->buffer[60] = ctx->hi; + ctx->buffer[61] = ctx->hi >> 8; + ctx->buffer[62] = ctx->hi >> 16; + ctx->buffer[63] = ctx->hi >> 24; + + body(ctx, ctx->buffer, 64); + + result[0] = ctx->a; + result[1] = ctx->a >> 8; + result[2] = ctx->a >> 16; + result[3] = ctx->a >> 24; + result[4] = ctx->b; + result[5] = ctx->b >> 8; + result[6] = ctx->b >> 16; + result[7] = ctx->b >> 24; + result[8] = ctx->c; + result[9] = ctx->c >> 8; + result[10] = ctx->c >> 16; + result[11] = ctx->c >> 24; + result[12] = ctx->d; + result[13] = ctx->d >> 8; + result[14] = ctx->d >> 16; + result[15] = ctx->d >> 24; + + memset(ctx, 0, sizeof(*ctx)); +} + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/cram-md5/md5.h b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/cram-md5/md5.h new file mode 100644 index 00000000..adfa863e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/cram-md5/md5.h @@ -0,0 +1,45 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * See md5.c for more information. + */ + +#ifdef HAVE_OPENSSL +#include +#elif !defined(_MD5_H) +#define _MD5_H + +/* Any 32-bit or wider unsigned integer data type will do */ +typedef unsigned int MD5_u32plus; + +typedef struct { + MD5_u32plus lo, hi; + MD5_u32plus a, b, c, d; + unsigned char buffer[64]; + MD5_u32plus block[16]; +} MD5_CTX; + +extern void cbsasl_MD5_Init(MD5_CTX *ctx); +extern void cbsasl_MD5_Update(MD5_CTX *ctx, void *data, unsigned long size); +extern void cbsasl_MD5_Final(unsigned char *result, MD5_CTX *ctx); + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/hash.c b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/hash.c new file mode 100644 index 00000000..0f1e7e80 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/hash.c @@ -0,0 +1,573 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Hash table + * + * The hash function used here is by Bob Jenkins, 1996: + * + * "By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. + * You may use this code any way you wish, private, educational, + * or commercial. It's free." + * + */ +#include "hash.h" + +/* + * Since the hash function does bit manipulation, it needs to know + * whether it's big or little-endian. ENDIAN_LITTLE and ENDIAN_BIG + * are set in the configure script. + */ +#ifdef WORDS_BIGENDIAN +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#endif + +#define rot(x,k) (((x)<<(k)) ^ ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +#if HASH_LITTLE_ENDIAN == 1 +uint32_t cbsasl_hash( + const void *key, /* the key to hash */ + size_t length, /* length of the key */ + const uint32_t initval) /* initval */ +{ + uint32_t a, b, c; /* internal state */ + union { + const void *ptr; + size_t i; + } u; /* needed for Mac Powerbook G4 */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + + u.ptr = key; + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = key; /* read 32-bit chunks */ +#ifdef VALGRIND + const uint8_t *k8; +#endif /* ifdef VALGRIND */ + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a, b, c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch (length) { + case 12: + c += k[2]; + b += k[1]; + a += k[0]; + break; + case 11: + c += k[2] & 0xffffff; + b += k[1]; + a += k[0]; + break; + case 10: + c += k[2] & 0xffff; + b += k[1]; + a += k[0]; + break; + case 9 : + c += k[2] & 0xff; + b += k[1]; + a += k[0]; + break; + case 8 : + b += k[1]; + a += k[0]; + break; + case 7 : + b += k[1] & 0xffffff; + a += k[0]; + break; + case 6 : + b += k[1] & 0xffff; + a += k[0]; + break; + case 5 : + b += k[1] & 0xff; + a += k[0]; + break; + case 4 : + a += k[0]; + break; + case 3 : + a += k[0] & 0xffffff; + break; + case 2 : + a += k[0] & 0xffff; + break; + case 1 : + a += k[0] & 0xff; + break; + case 0 : + return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch (length) { + case 12: + c += k[2]; + b += k[1]; + a += k[0]; + break; + case 11: + c += ((uint32_t)k8[10]) << 16; /* fall through */ + case 10: + c += ((uint32_t)k8[9]) << 8; /* fall through */ + case 9 : + c += k8[8]; /* fall through */ + case 8 : + b += k[1]; + a += k[0]; + break; + case 7 : + b += ((uint32_t)k8[6]) << 16; /* fall through */ + case 6 : + b += ((uint32_t)k8[5]) << 8; /* fall through */ + case 5 : + b += k8[4]; /* fall through */ + case 4 : + a += k[0]; + break; + case 3 : + a += ((uint32_t)k8[2]) << 16; /* fall through */ + case 2 : + a += ((uint32_t)k8[1]) << 8; /* fall through */ + case 1 : + a += k8[0]; + break; + case 0 : + return c; /* zero length strings require no mixing */ + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) { + a += k[0] + (((uint32_t)k[1]) << 16); + b += k[2] + (((uint32_t)k[3]) << 16); + c += k[4] + (((uint32_t)k[5]) << 16); + mix(a, b, c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch (length) { + case 12: + c += k[4] + (((uint32_t)k[5]) << 16); + b += k[2] + (((uint32_t)k[3]) << 16); + a += k[0] + (((uint32_t)k[1]) << 16); + break; + case 11: + c += ((uint32_t)k8[10]) << 16; /* @fallthrough */ + case 10: + c += k[4]; /* @fallthrough@ */ + b += k[2] + (((uint32_t)k[3]) << 16); + a += k[0] + (((uint32_t)k[1]) << 16); + break; + case 9 : + c += k8[8]; /* @fallthrough */ + case 8 : + b += k[2] + (((uint32_t)k[3]) << 16); + a += k[0] + (((uint32_t)k[1]) << 16); + break; + case 7 : + b += ((uint32_t)k8[6]) << 16; /* @fallthrough */ + case 6 : + b += k[2]; + a += k[0] + (((uint32_t)k[1]) << 16); + break; + case 5 : + b += k8[4]; /* @fallthrough */ + case 4 : + a += k[0] + (((uint32_t)k[1]) << 16); + break; + case 3 : + a += ((uint32_t)k8[2]) << 16; /* @fallthrough */ + case 2 : + a += k[0]; + break; + case 1 : + a += k8[0]; + break; + case 0 : + return c; /* zero length strings require no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) { + a += k[0]; + a += ((uint32_t)k[1]) << 8; + a += ((uint32_t)k[2]) << 16; + a += ((uint32_t)k[3]) << 24; + b += k[4]; + b += ((uint32_t)k[5]) << 8; + b += ((uint32_t)k[6]) << 16; + b += ((uint32_t)k[7]) << 24; + c += k[8]; + c += ((uint32_t)k[9]) << 8; + c += ((uint32_t)k[10]) << 16; + c += ((uint32_t)k[11]) << 24; + mix(a, b, c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch (length) { /* all the case statements fall through */ + case 12: + c += ((uint32_t)k[11]) << 24; + case 11: + c += ((uint32_t)k[10]) << 16; + case 10: + c += ((uint32_t)k[9]) << 8; + case 9 : + c += k[8]; + case 8 : + b += ((uint32_t)k[7]) << 24; + case 7 : + b += ((uint32_t)k[6]) << 16; + case 6 : + b += ((uint32_t)k[5]) << 8; + case 5 : + b += k[4]; + case 4 : + a += ((uint32_t)k[3]) << 24; + case 3 : + a += ((uint32_t)k[2]) << 16; + case 2 : + a += ((uint32_t)k[1]) << 8; + case 1 : + a += k[0]; + break; + case 0 : + return c; /* zero length strings require no mixing */ + } + } + + final(a, b, c); + return c; /* zero length strings require no mixing */ +} + +#elif HASH_BIG_ENDIAN == 1 +/* + * hashbig(): + * This is the same as hashword() on big-endian machines. It is different + * from hashlittle() on all machines. hashbig() takes advantage of + * big-endian byte ordering. + */ +uint32_t cbsasl_hash(const void *key, size_t length, const uint32_t initval) +{ + uint32_t a, b, c; + union { + const void *ptr; + size_t i; + } u; /* to cast key to (size_t) happily */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + + u.ptr = key; + if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = key; /* read 32-bit chunks */ +#ifdef VALGRIND + const uint8_t *k8; +#endif /* ifdef VALGRIND */ + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a, b, c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]<<8" actually reads beyond the end of the string, but + * then shifts out the part it's not allowed to read. Because the + * string is aligned, the illegal read is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch (length) { + case 12: + c += k[2]; + b += k[1]; + a += k[0]; + break; + case 11: + c += k[2] & 0xffffff00; + b += k[1]; + a += k[0]; + break; + case 10: + c += k[2] & 0xffff0000; + b += k[1]; + a += k[0]; + break; + case 9 : + c += k[2] & 0xff000000; + b += k[1]; + a += k[0]; + break; + case 8 : + b += k[1]; + a += k[0]; + break; + case 7 : + b += k[1] & 0xffffff00; + a += k[0]; + break; + case 6 : + b += k[1] & 0xffff0000; + a += k[0]; + break; + case 5 : + b += k[1] & 0xff000000; + a += k[0]; + break; + case 4 : + a += k[0]; + break; + case 3 : + a += k[0] & 0xffffff00; + break; + case 2 : + a += k[0] & 0xffff0000; + break; + case 1 : + a += k[0] & 0xff000000; + break; + case 0 : + return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch (length) { /* all the case statements fall through */ + case 12: + c += k[2]; + b += k[1]; + a += k[0]; + break; + case 11: + c += ((uint32_t)k8[10]) << 8; /* fall through */ + case 10: + c += ((uint32_t)k8[9]) << 16; /* fall through */ + case 9 : + c += ((uint32_t)k8[8]) << 24; /* fall through */ + case 8 : + b += k[1]; + a += k[0]; + break; + case 7 : + b += ((uint32_t)k8[6]) << 8; /* fall through */ + case 6 : + b += ((uint32_t)k8[5]) << 16; /* fall through */ + case 5 : + b += ((uint32_t)k8[4]) << 24; /* fall through */ + case 4 : + a += k[0]; + break; + case 3 : + a += ((uint32_t)k8[2]) << 8; /* fall through */ + case 2 : + a += ((uint32_t)k8[1]) << 16; /* fall through */ + case 1 : + a += ((uint32_t)k8[0]) << 24; + break; + case 0 : + return c; + } + +#endif /* !VALGRIND */ + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) { + a += ((uint32_t)k[0]) << 24; + a += ((uint32_t)k[1]) << 16; + a += ((uint32_t)k[2]) << 8; + a += ((uint32_t)k[3]); + b += ((uint32_t)k[4]) << 24; + b += ((uint32_t)k[5]) << 16; + b += ((uint32_t)k[6]) << 8; + b += ((uint32_t)k[7]); + c += ((uint32_t)k[8]) << 24; + c += ((uint32_t)k[9]) << 16; + c += ((uint32_t)k[10]) << 8; + c += ((uint32_t)k[11]); + mix(a, b, c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch (length) { /* all the case statements fall through */ + case 12: + c += k[11]; + case 11: + c += ((uint32_t)k[10]) << 8; + case 10: + c += ((uint32_t)k[9]) << 16; + case 9 : + c += ((uint32_t)k[8]) << 24; + case 8 : + b += k[7]; + case 7 : + b += ((uint32_t)k[6]) << 8; + case 6 : + b += ((uint32_t)k[5]) << 16; + case 5 : + b += ((uint32_t)k[4]) << 24; + case 4 : + a += k[3]; + case 3 : + a += ((uint32_t)k[2]) << 8; + case 2 : + a += ((uint32_t)k[1]) << 16; + case 1 : + a += ((uint32_t)k[0]) << 24; + break; + case 0 : + return c; + } + } + + final(a, b, c); + return c; +} +#else /* HASH_XXX_ENDIAN == 1 */ +#error Must define HASH_BIG_ENDIAN or HASH_LITTLE_ENDIAN +#endif /* HASH_XXX_ENDIAN == 1 */ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/hash.h b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/hash.h new file mode 100644 index 00000000..368f9e8c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/hash.h @@ -0,0 +1,15 @@ +#ifndef SRC_HASH_H_ +#define SRC_HASH_H_ 1 +#include "config.h" + +#ifdef __cplusplus +extern "C" { +#endif + + uint32_t cbsasl_hash(const void *key, size_t length, const uint32_t initval); + +#ifdef __cplusplus +} +#endif + +#endif /* SRC_HASH_H_ */ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/util.h b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/util.h new file mode 100644 index 00000000..4e48f462 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/cbsasl/src/util.h @@ -0,0 +1,31 @@ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CBSASL_UTIL_H_ +#define CBSASL_UTIL_H_ 1 +#include + +/* Encode hexadecimal representation of bytes from src into dest. + * Will write srclen * 2 bytes. */ +void cbsasl_hex_encode(char *dest, const char *src, size_t srclen); + +/* Compare a and b without revealing their content by short-circuiting */ +int cbsasl_secure_compare(const char *a, size_t alen, const char *b, size_t blen); + +cbsasl_error_t cbsasl_secure_random(char *dest, size_t len); + + +#endif /* CBSASL_UTIL_H_ */ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/cliopts/CMakeLists.txt b/couchbase-sys/libcouchbase-2.7.0/contrib/cliopts/CMakeLists.txt new file mode 100644 index 00000000..577fdb2c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/cliopts/CMakeLists.txt @@ -0,0 +1,2 @@ +ADD_LIBRARY(cliopts OBJECT cliopts.c) +SET_TARGET_PROPERTIES(cbsasl PROPERTIES COMPILE_FLAGS "${LCB_CORE_CFLAGS}") diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/cliopts/cliopts.c b/couchbase-sys/libcouchbase-2.7.0/contrib/cliopts/cliopts.c new file mode 100644 index 00000000..37d82024 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/cliopts/cliopts.c @@ -0,0 +1,778 @@ +#ifndef _WIN32 +#include +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "cliopts.h" + + +enum { + CLIOPTS_ERR_SUCCESS, + CLIOPTS_ERR_NEED_ARG, + CLIOPTS_ERR_ISSWITCH, + CLIOPTS_ERR_BADOPT, + CLIOPTS_ERR_BAD_VALUE, + CLIOPTS_ERR_UNRECOGNIZED +}; + +struct cliopts_priv { + cliopts_entry *entries; + + cliopts_entry *prev; + cliopts_entry *current; + struct cliopts_extra_settings *settings; + + char *errstr; + int errnum; + + int argsplit; + int wanted; + + char current_key[4096]; + char current_value[4096]; +}; + +enum { + WANT_OPTION, + WANT_VALUE, + + MODE_ERROR, + MODE_RESTARGS, + MODE_HELP +}; + +#define INDENT " " + +#ifdef CLIOPTS_DEBUG + +#define cliopt_debug(...) \ + fprintf(stderr, "(%s:%d) ", __func__, __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n") + +#else +/** variadic macros not c89 */ +static void cliopt_debug(void *unused, ...) { (void)unused; } +#endif /* CLIOPT_DEBUG */ + +static int +parse_option(struct cliopts_priv *ctx, const char *key); + + +static int +parse_value(struct cliopts_priv *ctx, const char *value); + +static void +add_list_value(const char *src, size_t nsrc, cliopts_list *l) +{ + char *cp = malloc(nsrc + 1); + + if (!l->nalloc) { + l->nalloc = 2; + l->values = malloc(l->nalloc * sizeof(*l->values)); + } else { + l->nalloc *= 1.5; + l->values = realloc(l->values, sizeof(*l->values) * l->nalloc); + } + + l->values[l->nvalues++] = cp; + cp[nsrc] = '\0'; + memcpy(cp, src, nsrc); +} + +CLIOPTS_API +void +cliopts_list_clear(cliopts_list *l) +{ + size_t ii; + for (ii = 0; ii < l->nvalues; ii++) { + free(l->values[ii]); + } + free(l->values); + l->values = NULL; + l->nvalues = 0; + l->nalloc = 0; +} + +/** + * Various extraction/conversion functions for numerics + */ + +#define _VERIFY_INT_COMMON(m1, m2) \ + if (value == m1 || value > m2) { *errp = "Value too large"; return -1; } \ + if (*endptr != '\0') { *errp = "Trailing garbage"; return -1; } + +static int +extract_int(const char *s, void *dest, char **errp) +{ + long int value; + char *endptr = NULL; + value = strtol(s, &endptr, 10); + _VERIFY_INT_COMMON(LONG_MAX, INT_MAX) + *(int*)dest = value; + return 0; +} + +static int +extract_uint(const char *s, void *dest, char **errp) +{ + unsigned long int value; + char *endptr = NULL; + value = strtoul(s, &endptr, 10); + _VERIFY_INT_COMMON(ULONG_MAX, UINT_MAX) + *(unsigned int*)dest = value; + return 0; +} + +#ifdef ULLONG_MAX +static int +extract_ulonglong(const char *s, void *dest, char **errp) +{ + unsigned long long value; + char *endptr = NULL; +#ifdef _WIN32 + value = _strtoui64(s, &endptr, 10); +#else + value = strtoull(s, &endptr, 10); +#endif + _VERIFY_INT_COMMON(ULLONG_MAX, ULLONG_MAX); + *(unsigned long long *)dest = value; + return 0; +} +#else +static int extract_ulonglong(const char *s, void *dest, char **errp) +{ + *errp = "long long not available"; + return -1; +} +#endif /* ULLONG_MAX */ + +static int +extract_hex(const char *s, void *dest, char **errp) +{ + unsigned long value; + char *endptr = NULL; + value = strtoul(s, &endptr, 16); + _VERIFY_INT_COMMON(ULONG_MAX, UINT_MAX); + *(unsigned int*)dest = value; + return 0; +} + +#undef _VERIFY_INT_COMMON + +static int +extract_float(const char *s, void *dest, char **errp) +{ + char dummy_buf[4096]; + float value; + if (sscanf(s, "%f%s", &value, dummy_buf) != 1) { + *errp = "Found trailing garbage"; + return -1; + } + *(float*)dest = value; + return 0; +} + +typedef int(*cliopts_extractor_func)(const char*, void*, char**); + + +/** + * This function tries to extract a single value for an option key. + * If it successfully has extracted a value, it returns MODE_VALUE. + * If the entry takes no arguments, then the current string is a key, + * and it will return MODE_OPTION. On error, MODE_ERROR is set, and errp + * will point to a string. + * + * @param entry The current entry + * @param value the string which might be a value + * @errp a pointer which will be populated with the address of the error, if any + * + * @return a MODE_* type + */ +static int +parse_value(struct cliopts_priv *ctx, + const char *value) +{ + cliopts_entry *entry = ctx->current; + + size_t vlen = strlen(value); + cliopts_extractor_func exfn = NULL; + int exret; + int is_option = 0; + + cliopt_debug("Called with %s, want=%d", value, ctx->wanted); + + if (ctx->argsplit) { + if (vlen > 2 && strncmp(value, "--", 2) == 0) { + is_option = 1; + } else if (*value == '-') { + is_option = 1; + } + } + + if (is_option) { + ctx->errstr = "Expected option. Got '-' or '--' prefixed value " + "(use = if this is really a value)"; + ctx->errnum = CLIOPTS_ERR_NEED_ARG; + return MODE_ERROR; + } + + if (entry->ktype == CLIOPTS_ARGT_STRING) { + char *vp = malloc(vlen+1); + vp[vlen] = 0; + strcpy(vp, value); + *(char**)entry->dest = vp; + return WANT_OPTION; + } + + if (entry->ktype == CLIOPTS_ARGT_LIST) { + add_list_value(value, vlen, (cliopts_list *)entry->dest); + return WANT_OPTION; + } + + if (entry->ktype == CLIOPTS_ARGT_FLOAT) { + exfn = extract_float; + } else if (entry->ktype == CLIOPTS_ARGT_HEX) { + exfn = extract_hex; + } else if (entry->ktype == CLIOPTS_ARGT_INT) { + exfn = extract_int; + } else if (entry->ktype == CLIOPTS_ARGT_UINT) { + exfn = extract_uint; + } else if (entry->ktype == CLIOPTS_ARGT_ULONGLONG) { + exfn = extract_ulonglong; + } else { + fprintf(stderr, "Unrecognized type %d.\n", entry->ktype); + return MODE_ERROR; + } + + exret = exfn(value, entry->dest, &ctx->errstr); + if (exret == 0) { + return WANT_OPTION; + } else { + ctx->errnum = CLIOPTS_ERR_BAD_VALUE; + } + + return MODE_ERROR; +} + +/** + * Like parse_value, except for keys. + * + * @param entries all option entries + * @param key the current string from argv + * @param errp a pointer which will be populated with the address of an error + * string + * + * @param found_entry a pointer to be populated with the relevant entry + * structure + * @param kp a pointer which will be poplated with the address of the 'sanitized' + * key string + * + * @param valp if the string is actually a key-value pair (i.e. --foo=bar) then + * this will be populated with the address of that string + * + * @return MODE_OPTION if an option was found, MODE_VALUE if the current option + * is a value, or MODE_ERROR on error + */ +static int +parse_option(struct cliopts_priv *ctx, + const char *key) +{ + cliopts_entry *cur = NULL; + int prefix_len = 0; + unsigned ii = 0; + const char *valp = NULL; + size_t klen; + + klen = strlen(key); + ctx->errstr = NULL; + ctx->prev = ctx->current; + ctx->current = NULL; + + cliopt_debug("Called with %s, want=%d", key, ctx->wanted); + if (klen == 0) { + ctx->errstr = "Got an empty string"; + ctx->errnum = CLIOPTS_ERR_BADOPT; + return MODE_ERROR; + } + + /** + * figure out what type of option it is.. + * it can either be a -c, --long, or --long=value + */ + while (*key == '-') { + key++; + prefix_len++; + klen--; + } + + for (ii = 0; ii < klen; ii++) { + if (key[ii] == '"' || key[ii] == '\'') { + ii = klen; + break; + + } else if (key[ii] == '=' && prefix_len == 2) { + /* only split on '=' if we're called as '--' */ + valp = key + (ii + 1); + klen = ii; + break; + } + } + + GT_PARSEOPT: + memset(ctx->current_value, 0, sizeof(ctx->current_value)); + memcpy(ctx->current_key, key, klen); + ctx->current_key[ii] = '\0'; + + if (valp) { + strcpy(ctx->current_value, valp); + } + + if (prefix_len == 0 || prefix_len > 2) { + if (ctx->settings->restargs) { + key -= prefix_len; + ctx->settings->restargs[ctx->settings->nrestargs++] = key; + return WANT_OPTION; + } else if (ctx->prev && ctx->prev->ktype == CLIOPTS_ARGT_NONE) { + ctx->errstr = "Option does not accept a value"; + ctx->errnum = CLIOPTS_ERR_ISSWITCH; + } else { + ctx->errstr = "Options must begin with either '-' or '--'"; + ctx->errnum = CLIOPTS_ERR_BADOPT; + } + return MODE_ERROR; + } + + /** + * --help or -? + */ + + if ( (prefix_len == 1 && *key == '?') || + (prefix_len == 2 && strcmp(key, "help") == 0)) { + return MODE_HELP; + } + + /** + * Bare -- + */ + if (prefix_len == 2 && *key == '\0') { + if (ctx->settings->restargs) { + + } + if (ctx->wanted == WANT_VALUE) { + ctx->errnum = CLIOPTS_ERR_NEED_ARG; + ctx->errstr = "Found bare '--', but value wanted"; + return MODE_ERROR; + } + + return MODE_RESTARGS; + } + + for (cur = ctx->entries; cur->dest; cur++) { + int optlen; + if (prefix_len == 1) { + if (cur->kshort == ctx->current_key[0]) { + ctx->current = cur; + break; + } + continue; + } + /** else, prefix_len is 2 */ + if (cur->klong == NULL || + (optlen = strlen(cur->klong) != klen) || + strncmp(cur->klong, ctx->current_key, klen) != 0) { + + continue; + } + + ctx->current = cur; + break; + } + + if (!ctx->current) { + ctx->errstr = "Unknown option"; + ctx->errnum = CLIOPTS_ERR_UNRECOGNIZED; + return MODE_ERROR; + } + + ctx->current->found++; + if (ctx->current->ktype != CLIOPTS_ARGT_NONE) { + ctx->wanted = WANT_VALUE; + } + + if (ctx->current_value[0]) { + /* --foo=bar */ + if (ctx->current->ktype == CLIOPTS_ARGT_NONE) { + ctx->errnum = CLIOPTS_ERR_ISSWITCH; + ctx->errstr = "Option takes no arguments"; + return MODE_ERROR; + } else { + return parse_value(ctx, ctx->current_value); + } + } + + if (ctx->current->ktype == CLIOPTS_ARGT_NONE) { + *(char*)ctx->current->dest = 1; + + if (prefix_len == 1 && klen > 1) { + /** + * e.g. ls -lsh + */ + klen--; + key++; + + /** + * While we can also possibly recurse, this may be a security risk + * as it wouldn't take much to cause a deep recursion on the stack + * which will cause all sorts of nasties. + */ + goto GT_PARSEOPT; + } + return WANT_OPTION; + + } else if (prefix_len == 1 && klen > 1) { + + /* e.g. patch -p0 */ + ctx->wanted = WANT_VALUE; + return parse_value(ctx, key + 1); + } + return WANT_VALUE; +} + +static char * +get_option_name(cliopts_entry *entry, char *buf) +{ + /* [-s,--option] */ + char *bufp = buf; + bufp += sprintf(buf, "["); + if (entry->kshort) { + bufp += sprintf(bufp, "-%c", entry->kshort); + } + if (entry->klong) { + if (entry->kshort) { + bufp += sprintf(bufp, ","); + } + bufp += sprintf(bufp, "--%s", entry->klong); + } + sprintf(bufp, "]"); + return buf; +} + +static int get_terminal_width(void) +{ +#ifndef _WIN32 + struct winsize max; + if (ioctl(0, TIOCGWINSZ, &max) != -1) { + return max.ws_col; + } else { + return 80; + } +#else + CONSOLE_SCREEN_BUFFER_INFO cbsi; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cbsi); + return cbsi.srWindow.Right - cbsi.srWindow.Left; +#endif +} + +static char* +format_option_help(cliopts_entry *entry, + char *buf, + struct cliopts_extra_settings *settings) +{ + char *bufp = buf; + if (entry->kshort) { + bufp += sprintf(bufp, " -%c ", entry->kshort); + } + +#define _advance_margin(offset) \ + while(bufp-buf < offset || *bufp) { \ + if (!*bufp) { \ + *bufp = ' '; \ + } \ + bufp++; \ + } + + _advance_margin(4) + + if (entry->klong) { + bufp += sprintf(bufp, " --%s ", entry->klong); + } + + if (entry->vdesc) { + bufp += sprintf(bufp, " <%s> ", entry->vdesc); + } + + _advance_margin(35) +#undef _advance_margin + + if (entry->help) { + unsigned initial_indent = bufp - buf + 1; + int curpos = initial_indent; + const char *help_p = entry->help; + + for (; *help_p; help_p++, curpos++, bufp++) { + + if (curpos >= settings->line_max) { + unsigned ii; + if (!isspace(*help_p) && !isspace(*(help_p-1))) { + *bufp = '-'; + bufp++; + } + *bufp = '\n'; + bufp++; + + for (ii = 0; ii < initial_indent+1; ii++, bufp++) { + *bufp = ' '; + } + + curpos = initial_indent; + if (isspace(*help_p)) { + bufp--; + continue; + } + } + *bufp = *help_p; + } + } + + *bufp = '\0'; + return buf; +} + +static void +print_help(struct cliopts_priv *ctx, struct cliopts_extra_settings *settings) +{ + cliopts_entry *cur; + cliopts_entry helpent = { 0 }; + char helpbuf[1024] = { 0 }; + + helpent.klong = "help"; + helpent.kshort = '?'; + helpent.help = "this message"; + + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s %s\n\n", settings->progname, settings->argstring); + if (settings->shortdesc) { + fprintf(stderr, "%s", settings->shortdesc); + fprintf(stderr, "\n"); + } + + + for (cur = ctx->entries; cur->dest; cur++) { + if (cur->hidden) { + continue; + } + + memset(helpbuf, 0, sizeof(helpbuf)); + format_option_help(cur, helpbuf, settings); + fprintf(stderr, INDENT "%s", helpbuf); + + + if (settings->show_defaults) { + fprintf(stderr, " [Default="); + + switch (cur->ktype) { + case CLIOPTS_ARGT_STRING: + fprintf(stderr, "'%s'", (cur->dest && *(char **)cur->dest) ? + *(char**)cur->dest : ""); + break; + case CLIOPTS_ARGT_LIST: { + size_t ii; + cliopts_list *l = (cliopts_list *)cur->dest; + for (ii = 0; ii < l->nvalues; ii++) { + fprintf(stderr, "'%s'", l->values[ii]); + if (ii != l->nvalues-1) { + fprintf(stderr, ", "); + } + } + break; + } + case CLIOPTS_ARGT_FLOAT: + fprintf(stderr, "%0.2f", *(float*)cur->dest); + break; + case CLIOPTS_ARGT_HEX: + fprintf(stderr, "0x%x", *(int*)cur->dest); + break; + case CLIOPTS_ARGT_INT: + fprintf(stderr, "%d", *(int*)cur->dest); + break; + case CLIOPTS_ARGT_UINT: + fprintf(stderr, "%u", *(unsigned int*)cur->dest); + break; +#ifdef ULLONG_MAX + case CLIOPTS_ARGT_ULONGLONG: + fprintf(stderr, "%llu", *(unsigned long long*)cur->dest); + break; +#endif + case CLIOPTS_ARGT_NONE: + fprintf(stderr, "%s", *(int*)cur->dest ? "TRUE" : "FALSE"); + break; + default: + fprintf(stderr, "Unknown option type '%d'", (int)cur->ktype); + break; + } + fprintf(stderr, "]"); + } + fprintf(stderr, "\n"); + } + memset(helpbuf, 0, sizeof(helpbuf)); + fprintf(stderr, INDENT "%s\n", + format_option_help(&helpent, helpbuf, settings)); + +} + +static void +dump_error(struct cliopts_priv *ctx) +{ + fprintf(stderr, "Couldn't parse options: %s\n", ctx->errstr); + if (ctx->errnum == CLIOPTS_ERR_BADOPT) { + fprintf(stderr, "Bad option: %s", ctx->current_key); + } else if (ctx->errnum == CLIOPTS_ERR_BAD_VALUE) { + fprintf(stderr, "Bad value '%s' for %s", + ctx->current_value, + ctx->current_key); + } else if (ctx->errnum == CLIOPTS_ERR_UNRECOGNIZED) { + fprintf(stderr, "No such option: %s", ctx->current_key); + } else if (ctx->errnum == CLIOPTS_ERR_ISSWITCH) { + char optbuf[64] = { 0 }; + fprintf(stderr, "Option %s takes no arguments", + get_option_name(ctx->prev, optbuf)); + } + fprintf(stderr, "\n"); + +} + +CLIOPTS_API +int +cliopts_parse_options(cliopts_entry *entries, + int argc, + char **argv, + int *lastidx, + struct cliopts_extra_settings *settings) +{ + /** + * Now let's build ourselves a + */ + int curmode; + int ii, ret = 0, lastidx_s = 0; + struct cliopts_priv ctx = { 0 }; + struct cliopts_extra_settings default_settings = { 0 }; + + if (!lastidx) { + lastidx = &lastidx_s; + } + + ctx.entries = entries; + + if (!settings) { + settings = &default_settings; + settings->show_defaults = 1; + } + if (!settings->progname) { + settings->progname = argv[0]; + } + if (!settings->argstring) { + settings->argstring = "[OPTIONS...]"; + } + settings->nrestargs = 0; + + if (!settings->line_max) { + settings->line_max = get_terminal_width() - 3; + } + + ii = (settings->argv_noskip) ? 0 : 1; + + if (ii >= argc) { + *lastidx = 0; + ret = 0; + goto GT_CHECK_REQ; + return 0; + } + + curmode = WANT_OPTION; + ctx.wanted = curmode; + ctx.settings = settings; + + for (; ii < argc; ii++) { + + if (curmode == WANT_OPTION) { + curmode = parse_option(&ctx, argv[ii]); + } else if (curmode == WANT_VALUE) { + curmode = parse_value(&ctx, argv[ii]); + } + + if (curmode == MODE_ERROR) { + if (settings->error_nohelp == 0) { + dump_error(&ctx); + } + ret = -1; + break; + } else if (curmode == MODE_HELP) { + if (settings->help_noflag) { + /* ignore it ? */ + continue; + } + + print_help(&ctx, settings); + exit(0); + + } else if (curmode == MODE_RESTARGS) { + ii++; + break; + } else { + ctx.wanted = curmode; + } + } + + *lastidx = ii; + + if (curmode == WANT_VALUE) { + ret = -1; + + if (settings->error_nohelp == 0) { + fprintf(stderr, + "Option %s requires argument\n", + ctx.current_key); + } + goto GT_RET; + } + + GT_CHECK_REQ: + { + cliopts_entry *cur_ent; + for (cur_ent = entries; cur_ent->dest; cur_ent++) { + char entbuf[128] = { 0 }; + if (cur_ent->found || cur_ent->required == 0) { + continue; + } + + ret = -1; + if (settings->error_nohelp) { + goto GT_RET; + } + + fprintf(stderr, "Required option %s missing\n", + get_option_name(cur_ent, entbuf)); + } + } + + GT_RET: + if (ret == -1) { + if (settings->error_nohelp == 0) { + print_help(&ctx, settings); + } + if (settings->error_noexit == 0) { + exit(EXIT_FAILURE); + } + } + return ret; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/cliopts/cliopts.h b/couchbase-sys/libcouchbase-2.7.0/contrib/cliopts/cliopts.h new file mode 100644 index 00000000..2b930a4c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/cliopts/cliopts.h @@ -0,0 +1,501 @@ +#ifndef CLIOPTS_H_ +#define CLIOPTS_H_ + +#include /* size_t */ +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#if defined(_WIN32) && defined(CLIOPTS_BUILDING_DLL) +#define CLIOPTS_API __declspec( dllexport ) + +#else +#define CLIOPTS_API +#endif + + +/** + * Various option types + */ +typedef enum { + /** takes no argument, dest should be anything big enough to hold a boolean*/ + CLIOPTS_ARGT_NONE, + + /** simple int type, dest should be an 'int' */ + CLIOPTS_ARGT_INT, + + /** dest should be an unsigned int */ + CLIOPTS_ARGT_UINT, + + /** dest should be an unsigned long long */ + CLIOPTS_ARGT_ULONGLONG, + + /** dest should be an unsigned int, but command line format is hex */ + CLIOPTS_ARGT_HEX, + + /** dest should be a char**. Note that the string is allocated, so you should + * free() it when done */ + CLIOPTS_ARGT_STRING, + + /** dest should be a float* */ + CLIOPTS_ARGT_FLOAT, + + /** + * Destination should be cliopts_list. Argument type is assumed to be a + * string. You can use this option type to build -Doption=value style + * options which can be processed later on. + */ + CLIOPTS_ARGT_LIST +} cliopts_argtype_t; + +typedef struct { + /** + * Input parameters + */ + + /** Short option, i.e. -v (0 for none) */ + char kshort; + + /** long option, i.e. --verbose, NULL for none */ + const char *klong; + + /** type of value */ + cliopts_argtype_t ktype; + + /** destination pointer for value */ + void *dest; + + /** help string for this option */ + const char *help; + + /** description of the value, e.g. --file=FILE */ + const char *vdesc; + + + /** set this to true if the user must provide this option */ + int required; + + /** set this to true to disable showing the option in the help text */ + int hidden; + + /** + * Output parameters + */ + + /** whether this option was encountered on the command line */ + int found; + +} cliopts_entry; + +struct cliopts_extra_settings { + /** Assume actual arguments start from argv[0], not argv[1] */ + int argv_noskip; + /** Don't exit on error */ + int error_noexit; + /** Don't print help on error */ + int error_nohelp; + /** Don't interpret --help or -? as help flags */ + int help_noflag; + /** Program name (defaults to argv[0]) */ + const char *progname; + /** Usage string (defaults to "[OPTIONS..]") */ + const char *argstring; + /** Short description (empty by default) */ + const char *shortdesc; + /** Print default values as well */ + int show_defaults; + /** + * Maximum length of a line when printing help. This may be detected + * using the $COLUMNS environment variable + */ + int line_max; + + /** Positional parameters (if found). If this array is non-NULL on input + * then parameters which are not recognized will be placed here. Otherwise + * the parser will return with an error. This array must be large enough + * to contain `argc` count strings. + */ + const char **restargs; + + /** Number of positional parameters (if found) */ + unsigned nrestargs; +}; + +typedef struct { + /** Array of string pointers. Allocated via standard malloc functions */ + char **values; + /** Number of valid entries */ + size_t nvalues; + /** Number of entries allocated */ + size_t nalloc; +} cliopts_list; + +/** + * Clear a list of its contents + * @param l The list + */ +CLIOPTS_API +void +cliopts_list_clear(cliopts_list *l); + +/** + * Parse options. + * + * @param entries an array of cliopts_entry structures. The list should be + * terminated with a structure which has its dest field set to NULL + * + * @param argc the count of arguments + * @param argv the actual list of arguments + * @param lastidx populated with the amount of elements from argv actually read + * @params setting a structure defining extra settings for the argument parser. + * May be NULL + * + * @return 0 for success, -1 on error. + */ +CLIOPTS_API +int +cliopts_parse_options(cliopts_entry *entries, + int argc, + char **argv, + int *lastidx, + struct cliopts_extra_settings *settings); +#ifdef __cplusplus +} + +#ifdef CLIOPTS_ENABLE_CXX +#include +#include +#include +#include +#include +#include + +namespace cliopts { +class Parser; + +/** + * This class should typically not be used directly. It is a simple wrapper + * around the C-based ::cliopts_entry class for further wrapping by the + * cliopts::TOption template class. + */ +class Option : protected cliopts_entry { +public: + bool passed() const { return found != 0; } + void setPassed(bool val = true) { found = val ? 1 : 0; } + int numSpecified() const { return found; } + Option() { memset(this, 0, sizeof (cliopts_entry)); } +private: + friend class Parser; +}; + +class EmptyPriv {}; + +/** + * Option template class. This class is not meant to be used by applications + * directly. Applications should use one of the template instantiations + * below (e.g. cliopts::StringOption) + * + * @param T type returned to the application + * @param Targ integer constant indicating the type of the C argument + * @param Taccum raw destination type which will store the parsed value + * @param Tpriv type of private data to be stored for type-specific processing + */ +template < + typename T, + cliopts_argtype_t Targ, + typename Taccum, + typename Tpriv = EmptyPriv + > +class TOption : public Option { + +private: + typedef TOption Ttype; + Taccum innerVal; /**< Pointer for cliopts_entry destination */ + Tpriv priv; /**< Type-specific data */ +public: + + /** + * Construct a new option + * @param shortname abbreviated short name + * @param longname long ("GNU-style" name) + * @param deflval default value to be used + * @param helpstr Text explaining the option + */ + TOption(char shortname, const char *longname = NULL, + T deflval = createDefault(), const char *helpstr = NULL) { + + memset((cliopts_entry *)this, 0, sizeof(cliopts_entry)); + ktype = Targ; + klong = longname; + dest = &innerVal; + + abbrev(shortname); + description(helpstr); + setDefault(deflval); + } + + /** + * Construct a new option + * @param longname the long ("GNU-Style") name. + */ + TOption(const char *longname) { + memset((cliopts_entry *)this, 0, sizeof(cliopts_entry)); + ktype = Targ; + klong = longname; + innerVal = createDefault(); + dest = &innerVal; + } + + /** + * Copy constructor. This mainly exists to allow chaining (See example) + * @param other the source option to copy + */ + TOption(TOption& other) { + *(cliopts_entry*)this = *(cliopts_entry*) &other; + innerVal = other.innerVal; + dest = &innerVal; + other.dest = NULL; + doCopy(other); + } + + /** + * Set the default value for the option + * @param val the default value + * @return the option object, for method chaining. + */ + inline Ttype& setDefault(const T& val) { + innerVal = val; + return *this; + } + + /** + * Set the single-character switch + * @param val the switch character, e.g. '-v' + * @return the option object, for method chaining + */ + inline Ttype& abbrev(char val) { kshort = val; return *this; } + + /** + * Set the description (or help string) for the option. + * @param msg The help string e.g. "Increases verbosity" + * @return the obtion object, for method chaining. + */ + inline Ttype& description(const char *msg) { help = msg; return *this; } + + /** + * Set whether this option must appear + * @param val boolean, set to true if required, false if optional + * @return the option object, for method chaining + */ + inline Ttype& mandatory(bool val = true) { required = val; return *this; } + + /** + * Set the value description string for the option value. + * @param desc The short description string, e.g. "RETRIES" + * @return the option object, for method chaining + */ + inline Ttype& argdesc(const char *desc) { vdesc = desc; return *this; } + + /** + * Whether to hide this option in the help output + * @param val true if the option should be hidden + * @return the object object, for method chaining. + */ + inline Ttype& hide(bool val = true) { hidden = val; return *this; } + + /** + * Returns the result object + * @return a copy of the result object + */ + inline T result() { return (T)innerVal; } + + /** + * Returns a reference to the result object + * @return a reference to the result object. + */ + inline T& const_result() { return (T)innerVal; } + + operator T() { return result(); } + +protected: + /** Called from within copy constructor */ + inline void doCopy(TOption&) {} + + /** Create the default value for the option */ + static inline Taccum createDefault() { return Taccum(); } +}; + +typedef TOption StringOption; + +typedef TOption, + CLIOPTS_ARGT_LIST, + cliopts_list, + std::vector > ListOption; + +typedef TOption BoolOption; + +typedef TOption UIntOption; + +typedef TOption ULongLongOption; + +typedef TOption IntOption; + +typedef TOption HexOption; + +typedef TOption FloatOption; + +// STRING ROUTINES +template<> inline std::string& StringOption::const_result() { + if (innerVal && passed()) { + priv = innerVal; + } + return priv; +} +template<> inline std::string StringOption::result() { + return const_result(); +} +template<> inline StringOption& StringOption::setDefault(const std::string& s) { + priv = s; + innerVal = priv.c_str(); + return *this; +} +template<> inline void StringOption::doCopy(StringOption& other) { + priv = other.priv; + if (other.innerVal == other.priv.c_str()) { + innerVal = priv.c_str(); + } +} +template<> inline const char* StringOption::createDefault() { return ""; } + +// LIST ROUTINES +template<> inline std::vector& ListOption::const_result() { + if (priv.empty()) { + for (size_t ii = 0; ii < innerVal.nvalues; ii++) { + priv.push_back(innerVal.values[ii]); + } + } + return priv; +} +template<> inline std::vector ListOption::result() { + return const_result(); +} + +// BOOL ROUTINES +template<> inline BoolOption& BoolOption::setDefault(const bool& b) { + innerVal = b ? 1 : 0; return *this; +} +template<> inline bool BoolOption::result() { + return innerVal != 0 ? true : false; +} + +/** + * Parser class which contains one or more cliopts::Option objects. Options + * should be added via the #addOption() member function. + */ +class Parser { +public: + /** + * Construct a new parser + * @param name the "program name" which is printed at the top of the + * help message. + */ + Parser(const char *name = NULL) { + memset(&default_settings, 0, sizeof default_settings); + default_settings.progname = name; + } + + /** + * Adds an option to the parser. The option is then checked for presence + * on the commandline (in #parse()). + * @param opt the option to add. Note that the application is responsible + * for keeping the option in valid memory. + */ + void addOption(Option *opt) { options.push_back(opt); } + + void addOption(Option& opt) { options.push_back(&opt); } + + /** + * Parses the options from the commandline + * @param argc number of arguments + * @param argv list of arguments + * @param standalone_args whether to accept (and store) positional arguments + * (after all named options are processed). + * @return true on parse success, false on parse failure + */ + bool parse(int argc, char **argv, bool standalone_args = false) { + std::vector ents; + cliopts_extra_settings settings = default_settings; + int lastix; + + for (unsigned ii = 0; ii < options.size(); ++ii) { + ents.push_back(*options[ii]); + } + + if (ents.empty()) { return false; } + ents.push_back(Option()); + const char **tmpargs = NULL; + if (standalone_args) { + tmpargs = new const char*[argc]; + settings.restargs = tmpargs; + settings.nrestargs = 0; + } + settings.show_defaults = 1; + + int rv = cliopts_parse_options(&ents[0], argc, argv, &lastix, &settings); + + if (tmpargs != NULL) { + for (unsigned ii = 0; ii < settings.nrestargs; ii++) { + restargs.push_back(tmpargs[ii]); + } + delete[] tmpargs; + } + + // Copy the options back + for (unsigned ii = 0; ii < options.size(); ii++) { + *(cliopts_entry *)options[ii] = ents[ii]; + } + + if (rv == 0 && lastix != 0) { + for (; lastix < argc; lastix++) { + restargs.push_back(argv[lastix]); + } + } + + return rv == 0; + } + + /** + * Get the list of any positional arguments found on the commandline + * @return A list of positional arguments found. + */ + const std::vector& getRestArgs() { return restargs; } + + cliopts_extra_settings default_settings; +private: + std::vector options; + std::vector restargs; + Parser(Parser&); +}; +} // namespace +#endif /* CLIOPTS_ENABLE_CXX */ + +#endif /* __cplusplus */ + +#endif /* CLIOPTS_H_ */ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/genhash/genhash.c b/couchbase-sys/libcouchbase-2.7.0/contrib/genhash/genhash.c new file mode 100644 index 00000000..41a8b85f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/genhash/genhash.c @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2006 Dustin Sallings + */ + +#include +#include "internal.h" +#include "genhash.h" + +/* Table of 32 primes by their distance from the nearest power of two */ +static lcb_size_t prime_size_table[] = { + 3, 7, 13, 23, 47, 97, 193, 383, 769, 1531, 3067, 6143, 12289, 24571, 49157, + 98299, 196613, 393209, 786433, 1572869, 3145721, 6291449, 12582917, + 25165813, 50331653, 100663291, 201326611, 402653189, 805306357, + 1610612741 +}; + +#define TABLE_SIZE ((int)(sizeof(prime_size_table) / sizeof(int))) + +struct genhash_entry_t { + /** The key for this entry */ + void *key; + /** Size of the key */ + lcb_size_t nkey; + /** The value for this entry */ + void *value; + /** Size of the value */ + lcb_size_t nvalue; + /** Pointer to the next entry */ + struct genhash_entry_t *next; +}; + +struct _genhash { + lcb_size_t size; + struct lcb_hash_ops ops; + struct genhash_entry_t *buckets[1]; +}; + +static lcb_size_t estimate_table_size(lcb_size_t est); + + +static void *dup_key(genhash_t *h, const void *key, lcb_size_t klen) +{ + if (h->ops.dup_key != NULL) { + return h->ops.dup_key(key, klen); + } else { + return (void *)key; + } +} + +static void *dup_value(genhash_t *h, const void *value, lcb_size_t vlen) +{ + if (h->ops.dup_value != NULL) { + return h->ops.dup_value(value, vlen); + } else { + return (void *)value; + } +} + +static void free_key(genhash_t *h, void *key) +{ + if (h->ops.free_key != NULL) { + h->ops.free_key(key); + } +} + +static void free_value(genhash_t *h, void *value) +{ + if (h->ops.free_value != NULL) { + h->ops.free_value(value); + } +} + +static lcb_size_t estimate_table_size(lcb_size_t est) +{ + lcb_size_t rv = 0; + while (prime_size_table[rv] < est && rv + 1 < TABLE_SIZE) { + rv++; + } + return prime_size_table[rv]; +} + +genhash_t *genhash_init(lcb_size_t est, struct lcb_hash_ops ops) +{ + genhash_t *rv = NULL; + lcb_size_t size = 0; + if (est < 1) { + return NULL; + } + + lcb_assert(ops.hashfunc != NULL); + lcb_assert(ops.hasheq != NULL); + lcb_assert((ops.dup_key != NULL && ops.free_key != NULL) || ops.free_key == NULL); + lcb_assert((ops.dup_value != NULL && ops.free_value != NULL) || ops.free_value == NULL); + + size = estimate_table_size(est); + rv = calloc(1, sizeof(genhash_t) + + (size * sizeof(struct genhash_entry_t *))); + if (rv == NULL) { + return NULL; + } + rv->size = size; + rv->ops = ops; + + return rv; +} + +void genhash_free(genhash_t *h) +{ + if (h != NULL) { + genhash_clear(h); + free(h); + } +} + +int genhash_store(genhash_t *h, const void *k, lcb_size_t klen, + const void *v, lcb_size_t vlen) +{ + lcb_size_t n = 0; + struct genhash_entry_t *p; + + lcb_assert(h != NULL); + + n = h->ops.hashfunc(k, klen) % h->size; + lcb_assert(n < h->size); + + p = calloc(1, sizeof(struct genhash_entry_t)); + if (!p) { + return -1; + } + + p->key = dup_key(h, k, klen); + p->nkey = klen; + p->value = dup_value(h, v, vlen); + p->nvalue = vlen; + + p->next = h->buckets[n]; + h->buckets[n] = p; + return 0; +} + +static struct genhash_entry_t *genhash_find_entry(genhash_t *h, + const void *k, + lcb_size_t klen) +{ + lcb_size_t n = 0; + struct genhash_entry_t *p; + + lcb_assert(h != NULL); + n = h->ops.hashfunc(k, klen) % h->size; + lcb_assert(n < h->size); + + p = h->buckets[n]; + for (p = h->buckets[n]; p && !h->ops.hasheq(k, klen, p->key, p->nkey); p = p->next); + return p; +} + +void *genhash_find(genhash_t *h, const void *k, lcb_size_t klen) +{ + struct genhash_entry_t *p; + void *rv = NULL; + + p = genhash_find_entry(h, k, klen); + + if (p) { + rv = p->value; + } + return rv; +} + +enum update_type genhash_update(genhash_t *h, const void *k, lcb_size_t klen, + const void *v, lcb_size_t vlen) +{ + struct genhash_entry_t *p; + enum update_type rv = 0; + + p = genhash_find_entry(h, k, klen); + + if (p) { + free_value(h, p->value); + p->value = dup_value(h, v, vlen); + rv = MODIFICATION; + } else { + if (-1 == genhash_store(h, k, klen, v, vlen)) { + rv = ALLOC_FAILURE; + } + rv = NEW; + } + + return rv; +} + +enum update_type genhash_fun_update(genhash_t *h, + const void *k, + lcb_size_t klen, + void * (*upd)(const void *, + const void *, + lcb_size_t *, + void *), + void (*fr)(void *), + void *arg, + const void *def, + lcb_size_t deflen) +{ + struct genhash_entry_t *p; + enum update_type rv = 0; + lcb_size_t newSize = 0; + + p = genhash_find_entry(h, k, klen); + + if (p) { + void *newValue = upd(k, p->value, &newSize, arg); + free_value(h, p->value); + p->value = dup_value(h, newValue, newSize); + fr(newValue); + rv = MODIFICATION; + } else { + void *newValue = upd(k, def, &newSize, arg); + genhash_store(h, k, klen, newValue, newSize); + fr(newValue); + rv = NEW; + } + + (void)deflen; + return rv; +} + +static void free_item(genhash_t *h, struct genhash_entry_t *i) +{ + lcb_assert(i); + free_key(h, i->key); + free_value(h, i->value); + free(i); +} + +int genhash_delete(genhash_t *h, const void *k, lcb_size_t klen) +{ + struct genhash_entry_t *deleteme = NULL; + lcb_size_t n = 0; + int rv = 0; + + lcb_assert(h != NULL); + n = h->ops.hashfunc(k, klen) % h->size; + lcb_assert(n < h->size); + + if (h->buckets[n] != NULL) { + /* Special case the first one */ + if (h->ops.hasheq(h->buckets[n]->key, h->buckets[n]->nkey, k, klen)) { + deleteme = h->buckets[n]; + h->buckets[n] = deleteme->next; + } else { + struct genhash_entry_t *p = NULL; + for (p = h->buckets[n]; deleteme == NULL && p->next != NULL; p = p->next) { + if (h->ops.hasheq(p->next->key, p->next->nkey, k, klen)) { + deleteme = p->next; + p->next = deleteme->next; + } + } + } + } + if (deleteme != NULL) { + free_item(h, deleteme); + rv++; + } + + return rv; +} + +int genhash_delete_all(genhash_t *h, const void *k, lcb_size_t klen) +{ + int rv = 0; + while (genhash_delete(h, k, klen) == 1) { + rv++; + } + return rv; +} + +void genhash_iter(genhash_t *h, + void (*iterfunc)(const void *key, lcb_size_t nkey, + const void *val, lcb_size_t nval, + void *arg), void *arg) +{ + lcb_size_t i = 0; + struct genhash_entry_t *p = NULL; + lcb_assert(h != NULL); + + for (i = 0; i < h->size; i++) { + for (p = h->buckets[i]; p != NULL; p = p->next) { + iterfunc(p->key, p->nkey, p->value, p->nvalue, arg); + } + } +} + +int genhash_clear(genhash_t *h) +{ + lcb_size_t i = 0; + int rv = 0; + lcb_assert(h != NULL); + + for (i = 0; i < h->size; i++) { + while (h->buckets[i]) { + struct genhash_entry_t *p = NULL; + p = h->buckets[i]; + h->buckets[i] = p->next; + free_item(h, p); + } + } + + return rv; +} + +static void count_entries(const void *key, + lcb_size_t klen, + const void *val, + lcb_size_t vlen, + void *arg) +{ + int *count = (int *)arg; + (*count)++; + (void)key; + (void)klen; + (void)val; + (void)vlen; +} + +int genhash_size(genhash_t *h) +{ + int rv = 0; + lcb_assert(h != NULL); + genhash_iter(h, count_entries, &rv); + return rv; +} + +int genhash_size_for_key(genhash_t *h, const void *k, lcb_size_t klen) +{ + int rv = 0; + lcb_assert(h != NULL); + genhash_iter_key(h, k, klen, count_entries, &rv); + return rv; +} + +void genhash_iter_key(genhash_t *h, const void *key, lcb_size_t klen, + void (*iterfunc)(const void *key, lcb_size_t klen, + const void *val, lcb_size_t vlen, + void *arg), void *arg) +{ + lcb_size_t n = 0; + struct genhash_entry_t *p = NULL; + + lcb_assert(h != NULL); + n = h->ops.hashfunc(key, klen) % h->size; + lcb_assert(n < h->size); + + for (p = h->buckets[n]; p != NULL; p = p->next) { + if (h->ops.hasheq(key, klen, p->key, p->nkey)) { + iterfunc(p->key, p->nkey, p->value, p->nvalue, arg); + } + } +} + +int genhash_string_hash(const void *p, lcb_size_t nkey) +{ + int rv = 5381; + int i = 0; + char *str = (char *)p; + + for (i = 0; i < (int)nkey; i++) { + lcb_assert(str[i]); + rv = ((rv << 5) + rv) ^ str[i]; + } + + return rv; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/genhash/genhash.h b/couchbase-sys/libcouchbase-2.7.0/contrib/genhash/genhash.h new file mode 100644 index 00000000..490a946b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/genhash/genhash.h @@ -0,0 +1,241 @@ +/* + * Generic hash table implementation. + * + * Copyright (c) 2006 Dustin Sallings + */ + +#ifndef GENHASH_H +#define GENHASH_H 1 +#ifdef __cplusplus +extern "C" { +#endif + +/*! \mainpage genhash + * + * \section intro_sec Introduction + * + * genhash is a generic hash table implementation in C. It's + * well-tested, freely available (MIT-license) and does what you need. + * + * \section docs_sec API Documentation + * + * Jump right into the API docs to get started. + */ + +/** + * \defgroup Core genhash core + */ + +/** + * \addtogroup Core + * @{ + */ + +/** + * Operations on keys and values in the hash table. + */ +struct lcb_hash_ops { + /** + * Function to compute a hash for the given value. + */ + int (*hashfunc)(const void *, lcb_size_t); + /** + * Function that returns true if the given keys are equal. + */ + int (*hasheq)(const void *, lcb_size_t, const void *, lcb_size_t); + /** + * Function to duplicate a key for storage. + */ + void *(*dup_key)(const void *, lcb_size_t); + /** + * Function to duplicate a value for storage. + */ + void *(*dup_value)(const void *, lcb_size_t); + /** + * Function to free a key. + */ + void (*free_key)(void *); + /** + * Function to free a value. + */ + void (*free_value)(void *); +}; + +/** + * The hash table structure. + */ +typedef struct _genhash genhash_t ; + +/** + * Type of update performed by an update function. + */ +enum update_type { + MODIFICATION, /**< This update is modifying an existing entry */ + NEW, /**< This update is creating a new entry */ + ALLOC_FAILURE +}; + +/** + * Create a new generic hashtable. + * + * @param est the estimated number of items to store (must be > 0) + * @param ops the key and value operations + * + * @return the new genhash_t or NULL if one cannot be created + */ +genhash_t *genhash_init(lcb_size_t est, struct lcb_hash_ops ops); + +/** + * Free a gen hash. + * + * @param h the genhash to free (may be NULL) + */ +void genhash_free(genhash_t *h); + +/** + * Store an item. + * + * @param h the genhash + * @param k the key + * @param v the value + */ +int genhash_store(genhash_t *h, const void *k, lcb_size_t klen, + const void *v, lcb_size_t vlen); + +/** + * Get the most recent value stored for the given key. + * + * @param h the genhash + * @param k the key + * + * @return the value, or NULL if one cannot be found + */ +void *genhash_find(genhash_t *h, const void *k, lcb_size_t klen); + +/** + * Delete the most recent value stored for a key. + * + * @param h the genhash + * @param k the key + * + * @return the number of items deleted + */ +int genhash_delete(genhash_t *h, const void *k, lcb_size_t klen); + +/** + * Delete all mappings of a given key. + * + * @param h the genhash + * @param k the key + * + * @return the number of items deleted + */ +int genhash_delete_all(genhash_t *h, const void *k, lcb_size_t klen); + +/** + * Create or update an item in-place. + * + * @param h the genhash + * @param k the key + * @param v the new value to store for this key + * + * @return an indicator of whether this created a new item or updated + * an existing one + */ +enum update_type genhash_update(genhash_t *h, const void *k, lcb_size_t klen, + const void *v, lcb_size_t vlen); + +/** + * Create or update an item in-place with a function. + * + * @param h hashtable + * @param key the key of the item + * @param upd function that will be called with the key and current + * value. Should return the new value. + * @param fr function to free the return value returned by the update + * function + * @param def default value + * + * @return an indicator of whether this created a new item or updated + * an existing one + */ +enum update_type genhash_fun_update(genhash_t *h, const void *key, lcb_size_t klen, + void * (*upd)(const void *k, const void *oldv, + lcb_size_t *ns, void *a), + void (*fr)(void *), + void *arg, + const void *def, lcb_size_t deflen); + +/** + * Iterate all keys and values in a hash table. + * + * @param h the genhash + * @param iterfunc a function that will be called once for every k/v pair + * @param arg an argument to be passed to the iterfunc on each iteration + */ +void genhash_iter(genhash_t *h, + void (*iterfunc)(const void *key, lcb_size_t nkey, + const void *val, lcb_size_t nval, + void *arg), + void *arg); + +/** + * Iterate all values for a given key in a hash table. + * + * @param h the genhash + * @param key the key to iterate + * @param iterfunc a function that will be called once for every k/v pair + * @param arg an argument to be passed to the iterfunc on each iteration + */ +void genhash_iter_key(genhash_t *h, const void *key, lcb_size_t nkey, + void (*iterfunc)(const void *key, lcb_size_t inkey, + const void *val, lcb_size_t inval, + void *arg), + void *arg); + +/** + * Get the total number of entries in this hash table. + * + * @param h the genhash + * + * @return the number of entries in the hash table + */ +int genhash_size(genhash_t *h); + +/** + * Remove all items from a genhash. + * + * @param h the genhash + * + * @return the number of items removed + */ +int genhash_clear(genhash_t *h); + +/** + * Get the total number of entries in this hash table that map to the given + * key. + * + * @param h the genhash + * @param k a key + * + * @return the number of entries keyed with the given key + */ +int genhash_size_for_key(genhash_t *h, const void *k, lcb_size_t nkey); + +/** + * Convenient hash function for strings. + * + * @param k a null-terminated string key. + * + * @return a hash value for this string. + */ +int genhash_string_hash(const void *k, lcb_size_t nkey); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif +#endif /* GENHASH_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/CHANGES b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/CHANGES new file mode 100644 index 00000000..05521324 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/CHANGES @@ -0,0 +1,157 @@ +Changes for 1.7.0: + +* New feature: death tests are supported on OpenBSD and in iOS + simulator now. +* New feature: Google Test now implements a protocol to allow + a test runner to detect that a test program has exited + prematurely and report it as a failure (before it would be + falsely reported as a success if the exit code is 0). +* New feature: Test::RecordProperty() can now be used outside of the + lifespan of a test method, in which case it will be attributed to + the current test case or the test program in the XML report. +* New feature (potentially breaking): --gtest_list_tests now prints + the type parameters and value parameters for each test. +* Improvement: char pointers and char arrays are now escaped properly + in failure messages. +* Improvement: failure summary in XML reports now includes file and + line information. +* Improvement: the XML element now has a timestamp attribute. +* Improvement: When --gtest_filter is specified, XML report now doesn't + contain information about tests that are filtered out. +* Fixed the bug where long --gtest_filter flag values are truncated in + death tests. +* Potentially breaking change: RUN_ALL_TESTS() is now implemented as a + function instead of a macro in order to work better with Clang. +* Compatibility fixes with C++ 11 and various platforms. +* Bug/warning fixes. + +Changes for 1.6.0: + +* New feature: ADD_FAILURE_AT() for reporting a test failure at the + given source location -- useful for writing testing utilities. +* New feature: the universal value printer is moved from Google Mock + to Google Test. +* New feature: type parameters and value parameters are reported in + the XML report now. +* A gtest_disable_pthreads CMake option. +* Colored output works in GNU Screen sessions now. +* Parameters of value-parameterized tests are now printed in the + textual output. +* Failures from ad hoc test assertions run before RUN_ALL_TESTS() are + now correctly reported. +* Arguments of ASSERT_XY and EXPECT_XY no longer need to support << to + ostream. +* More complete handling of exceptions. +* GTEST_ASSERT_XY can be used instead of ASSERT_XY in case the latter + name is already used by another library. +* --gtest_catch_exceptions is now true by default, allowing a test + program to continue after an exception is thrown. +* Value-parameterized test fixtures can now derive from Test and + WithParamInterface separately, easing conversion of legacy tests. +* Death test messages are clearly marked to make them more + distinguishable from other messages. +* Compatibility fixes for Android, Google Native Client, MinGW, HP UX, + PowerPC, Lucid autotools, libCStd, Sun C++, Borland C++ Builder (Code Gear), + IBM XL C++ (Visual Age C++), and C++0x. +* Bug fixes and implementation clean-ups. +* Potentially incompatible changes: disables the harmful 'make install' + command in autotools. + +Changes for 1.5.0: + + * New feature: assertions can be safely called in multiple threads + where the pthreads library is available. + * New feature: predicates used inside EXPECT_TRUE() and friends + can now generate custom failure messages. + * New feature: Google Test can now be compiled as a DLL. + * New feature: fused source files are included. + * New feature: prints help when encountering unrecognized Google Test flags. + * Experimental feature: CMake build script (requires CMake 2.6.4+). + * Experimental feature: the Pump script for meta programming. + * double values streamed to an assertion are printed with enough precision + to differentiate any two different values. + * Google Test now works on Solaris and AIX. + * Build and test script improvements. + * Bug fixes and implementation clean-ups. + + Potentially breaking changes: + + * Stopped supporting VC++ 7.1 with exceptions disabled. + * Dropped support for 'make install'. + +Changes for 1.4.0: + + * New feature: the event listener API + * New feature: test shuffling + * New feature: the XML report format is closer to junitreport and can + be parsed by Hudson now. + * New feature: when a test runs under Visual Studio, its failures are + integrated in the IDE. + * New feature: /MD(d) versions of VC++ projects. + * New feature: elapsed time for the tests is printed by default. + * New feature: comes with a TR1 tuple implementation such that Boost + is no longer needed for Combine(). + * New feature: EXPECT_DEATH_IF_SUPPORTED macro and friends. + * New feature: the Xcode project can now produce static gtest + libraries in addition to a framework. + * Compatibility fixes for Solaris, Cygwin, minGW, Windows Mobile, + Symbian, gcc, and C++Builder. + * Bug fixes and implementation clean-ups. + +Changes for 1.3.0: + + * New feature: death tests on Windows, Cygwin, and Mac. + * New feature: ability to use Google Test assertions in other testing + frameworks. + * New feature: ability to run disabled test via + --gtest_also_run_disabled_tests. + * New feature: the --help flag for printing the usage. + * New feature: access to Google Test flag values in user code. + * New feature: a script that packs Google Test into one .h and one + .cc file for easy deployment. + * New feature: support for distributing test functions to multiple + machines (requires support from the test runner). + * Bug fixes and implementation clean-ups. + +Changes for 1.2.1: + + * Compatibility fixes for Linux IA-64 and IBM z/OS. + * Added support for using Boost and other TR1 implementations. + * Changes to the build scripts to support upcoming release of Google C++ + Mocking Framework. + * Added Makefile to the distribution package. + * Improved build instructions in README. + +Changes for 1.2.0: + + * New feature: value-parameterized tests. + * New feature: the ASSERT/EXPECT_(NON)FATAL_FAILURE(_ON_ALL_THREADS) + macros. + * Changed the XML report format to match JUnit/Ant's. + * Added tests to the Xcode project. + * Added scons/SConscript for building with SCons. + * Added src/gtest-all.cc for building Google Test from a single file. + * Fixed compatibility with Solaris and z/OS. + * Enabled running Python tests on systems with python 2.3 installed, + e.g. Mac OS X 10.4. + * Bug fixes. + +Changes for 1.1.0: + + * New feature: type-parameterized tests. + * New feature: exception assertions. + * New feature: printing elapsed time of tests. + * Improved the robustness of death tests. + * Added an Xcode project and samples. + * Adjusted the output format on Windows to be understandable by Visual Studio. + * Minor bug fixes. + +Changes for 1.0.1: + + * Added project files for Visual Studio 7.1. + * Fixed issues with compiling on Mac OS X. + * Fixed issues with compiling on Cygwin. + +Changes for 1.0.0: + + * Initial Open Source release of Google Test diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/CMakeLists.txt b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/CMakeLists.txt new file mode 100644 index 00000000..57470c84 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/CMakeLists.txt @@ -0,0 +1,252 @@ +######################################################################## +# CMake build script for Google Test. +# +# To run the tests for Google Test itself on Linux, use 'make test' or +# ctest. You can select which tests to run using 'ctest -R regex'. +# For more options, run 'ctest --help'. + +# BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to +# make it prominent in the GUI. +option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF) + +# When other libraries are using a shared version of runtime libraries, +# Google Test also has to use one. +option( + gtest_force_shared_crt + "Use shared (DLL) run-time lib even when Google Test is built as static lib." + OFF) + +option(gtest_build_tests "Build all of gtest's own tests." OFF) + +option(gtest_build_samples "Build gtest's sample programs." OFF) + +option(gtest_disable_pthreads "Disable uses of pthreads in gtest." OFF) + +# Defines pre_project_set_up_hermetic_build() and set_up_hermetic_build(). +include(cmake/hermetic_build.cmake OPTIONAL) + +if (COMMAND pre_project_set_up_hermetic_build) + pre_project_set_up_hermetic_build() +endif() + +######################################################################## +# +# Project-wide settings + +# Name of the project. +# +# CMake files in this project can refer to the root source directory +# as ${gtest_SOURCE_DIR} and to the root binary directory as +# ${gtest_BINARY_DIR}. +# Language "C" is required for find_package(Threads). +project(gtest CXX C) +cmake_minimum_required(VERSION 2.6.2) + +if (COMMAND set_up_hermetic_build) + set_up_hermetic_build() +endif() + +# Define helper functions and macros used by Google Test. +include(cmake/internal_utils.cmake) + +config_compiler_and_linker() # Defined in internal_utils.cmake. + +# Where Google Test's .h files can be found. +include_directories( + ${gtest_SOURCE_DIR}/include + ${gtest_SOURCE_DIR}) + +# Where Google Test's libraries can be found. +link_directories(${gtest_BINARY_DIR}/src) + +######################################################################## +# +# Defines the gtest & gtest_main libraries. User tests should link +# with one of them. + +# Google Test libraries. We build them using more strict warnings than what +# are used for other targets, to ensure that gtest can be compiled by a user +# aggressive about warnings. +cxx_library(gtest "${cxx_strict}" src/gtest-all.cc) +cxx_library(gtest_main "${cxx_strict}" src/gtest_main.cc) +target_link_libraries(gtest_main gtest) + +######################################################################## +# +# Samples on how to link user tests with gtest or gtest_main. +# +# They are not built by default. To build them, set the +# gtest_build_samples option to ON. You can do it by running ccmake +# or specifying the -Dgtest_build_samples=ON flag when running cmake. + +if (gtest_build_samples) + cxx_executable(sample1_unittest samples gtest_main samples/sample1.cc) + cxx_executable(sample2_unittest samples gtest_main samples/sample2.cc) + cxx_executable(sample3_unittest samples gtest_main) + cxx_executable(sample4_unittest samples gtest_main samples/sample4.cc) + cxx_executable(sample5_unittest samples gtest_main samples/sample1.cc) + cxx_executable(sample6_unittest samples gtest_main) + cxx_executable(sample7_unittest samples gtest_main) + cxx_executable(sample8_unittest samples gtest_main) + cxx_executable(sample9_unittest samples gtest) + cxx_executable(sample10_unittest samples gtest) +endif() + +######################################################################## +# +# Google Test's own tests. +# +# You can skip this section if you aren't interested in testing +# Google Test itself. +# +# The tests are not built by default. To build them, set the +# gtest_build_tests option to ON. You can do it by running ccmake +# or specifying the -Dgtest_build_tests=ON flag when running cmake. + +if (gtest_build_tests) + # This must be set in the root directory for the tests to be run by + # 'make test' or ctest. + enable_testing() + + ############################################################ + # C++ tests built with standard compiler flags. + + cxx_test(gtest-death-test_test gtest_main) + cxx_test(gtest_environment_test gtest) + cxx_test(gtest-filepath_test gtest_main) + cxx_test(gtest-linked_ptr_test gtest_main) + cxx_test(gtest-listener_test gtest_main) + cxx_test(gtest_main_unittest gtest_main) + cxx_test(gtest-message_test gtest_main) + cxx_test(gtest_no_test_unittest gtest) + cxx_test(gtest-options_test gtest_main) + cxx_test(gtest-param-test_test gtest + test/gtest-param-test2_test.cc) + cxx_test(gtest-port_test gtest_main) + cxx_test(gtest_pred_impl_unittest gtest_main) + cxx_test(gtest_premature_exit_test gtest + test/gtest_premature_exit_test.cc) + cxx_test(gtest-printers_test gtest_main) + cxx_test(gtest_prod_test gtest_main + test/production.cc) + cxx_test(gtest_repeat_test gtest) + cxx_test(gtest_sole_header_test gtest_main) + cxx_test(gtest_stress_test gtest) + cxx_test(gtest-test-part_test gtest_main) + cxx_test(gtest_throw_on_failure_ex_test gtest) + cxx_test(gtest-typed-test_test gtest_main + test/gtest-typed-test2_test.cc) + cxx_test(gtest_unittest gtest_main) + cxx_test(gtest-unittest-api_test gtest) + + ############################################################ + # C++ tests built with non-standard compiler flags. + + # MSVC 7.1 does not support STL with exceptions disabled. + if (NOT MSVC OR MSVC_VERSION GREATER 1310) + cxx_library(gtest_no_exception "${cxx_no_exception}" + src/gtest-all.cc) + cxx_library(gtest_main_no_exception "${cxx_no_exception}" + src/gtest-all.cc src/gtest_main.cc) + endif() + cxx_library(gtest_main_no_rtti "${cxx_no_rtti}" + src/gtest-all.cc src/gtest_main.cc) + + cxx_test_with_flags(gtest-death-test_ex_nocatch_test + "${cxx_exception} -DGTEST_ENABLE_CATCH_EXCEPTIONS_=0" + gtest test/gtest-death-test_ex_test.cc) + cxx_test_with_flags(gtest-death-test_ex_catch_test + "${cxx_exception} -DGTEST_ENABLE_CATCH_EXCEPTIONS_=1" + gtest test/gtest-death-test_ex_test.cc) + + cxx_test_with_flags(gtest_no_rtti_unittest "${cxx_no_rtti}" + gtest_main_no_rtti test/gtest_unittest.cc) + + cxx_shared_library(gtest_dll "${cxx_default}" + src/gtest-all.cc src/gtest_main.cc) + + cxx_executable_with_flags(gtest_dll_test_ "${cxx_default}" + gtest_dll test/gtest_all_test.cc) + set_target_properties(gtest_dll_test_ + PROPERTIES + COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1") + + if (NOT MSVC OR NOT MSVC_VERSION EQUAL 1600) + # The C++ Standard specifies tuple_element. + # Yet MSVC 10's declares tuple_element. + # That declaration conflicts with our own standard-conforming + # tuple implementation. Therefore using our own tuple with + # MSVC 10 doesn't compile. + cxx_library(gtest_main_use_own_tuple "${cxx_use_own_tuple}" + src/gtest-all.cc src/gtest_main.cc) + + cxx_test_with_flags(gtest-tuple_test "${cxx_use_own_tuple}" + gtest_main_use_own_tuple test/gtest-tuple_test.cc) + + cxx_test_with_flags(gtest_use_own_tuple_test "${cxx_use_own_tuple}" + gtest_main_use_own_tuple + test/gtest-param-test_test.cc test/gtest-param-test2_test.cc) + endif() + + ############################################################ + # Python tests. + + cxx_executable(gtest_break_on_failure_unittest_ test gtest) + py_test(gtest_break_on_failure_unittest) + + # MSVC 7.1 does not support STL with exceptions disabled. + if (NOT MSVC OR MSVC_VERSION GREATER 1310) + cxx_executable_with_flags( + gtest_catch_exceptions_no_ex_test_ + "${cxx_no_exception}" + gtest_main_no_exception + test/gtest_catch_exceptions_test_.cc) + endif() + + cxx_executable_with_flags( + gtest_catch_exceptions_ex_test_ + "${cxx_exception}" + gtest_main + test/gtest_catch_exceptions_test_.cc) + py_test(gtest_catch_exceptions_test) + + cxx_executable(gtest_color_test_ test gtest) + py_test(gtest_color_test) + + cxx_executable(gtest_env_var_test_ test gtest) + py_test(gtest_env_var_test) + + cxx_executable(gtest_filter_unittest_ test gtest) + py_test(gtest_filter_unittest) + + cxx_executable(gtest_help_test_ test gtest_main) + py_test(gtest_help_test) + + cxx_executable(gtest_list_tests_unittest_ test gtest) + py_test(gtest_list_tests_unittest) + + cxx_executable(gtest_output_test_ test gtest) + py_test(gtest_output_test) + + cxx_executable(gtest_shuffle_test_ test gtest) + py_test(gtest_shuffle_test) + + # MSVC 7.1 does not support STL with exceptions disabled. + if (NOT MSVC OR MSVC_VERSION GREATER 1310) + cxx_executable(gtest_throw_on_failure_test_ test gtest_no_exception) + set_target_properties(gtest_throw_on_failure_test_ + PROPERTIES + COMPILE_FLAGS "${cxx_no_exception}") + py_test(gtest_throw_on_failure_test) + endif() + + cxx_executable(gtest_uninitialized_test_ test gtest) + py_test(gtest_uninitialized_test) + + cxx_executable(gtest_xml_outfile1_test_ test gtest_main) + cxx_executable(gtest_xml_outfile2_test_ test gtest_main) + py_test(gtest_xml_outfiles_test) + + cxx_executable(gtest_xml_output_unittest_ test gtest) + py_test(gtest_xml_output_unittest) +endif() diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/CONTRIBUTORS b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/CONTRIBUTORS new file mode 100644 index 00000000..feae2fc0 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/CONTRIBUTORS @@ -0,0 +1,37 @@ +# This file contains a list of people who've made non-trivial +# contribution to the Google C++ Testing Framework project. People +# who commit code to the project are encouraged to add their names +# here. Please keep the list sorted by first names. + +Ajay Joshi +Balázs Dán +Bharat Mediratta +Chandler Carruth +Chris Prince +Chris Taylor +Dan Egnor +Eric Roman +Hady Zalek +Jeffrey Yasskin +Jói Sigurðsson +Keir Mierle +Keith Ray +Kenton Varda +Manuel Klimek +Markus Heule +Mika Raento +Miklós Fazekas +Pasi Valminen +Patrick Hanna +Patrick Riley +Peter Kaminski +Preston Jackson +Rainer Klaffenboeck +Russ Cox +Russ Rufer +Sean Mcafee +Sigurður Ásgeirsson +Tracy Bialik +Vadim Berman +Vlad Losev +Zhanyong Wan diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/LICENSE b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/LICENSE new file mode 100644 index 00000000..1941a11f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/LICENSE @@ -0,0 +1,28 @@ +Copyright 2008, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/MINIFY.sh b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/MINIFY.sh new file mode 100755 index 00000000..957d8c97 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/MINIFY.sh @@ -0,0 +1,15 @@ +#!/bin/sh +set -x +rm -f aclocal.m4 +rm -rf build-aux +rm -rf msvc +rm -rf fused-src +rm -f configure configure.ac +rm -f Makefile.in Makefile.am +rm -rf xcode +rm -rf test +rm -rf m4 +rm -rf make +rm -rf codegear +rm -rf samples +rm -rf scripts diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/README b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/README new file mode 100644 index 00000000..26f35a84 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/README @@ -0,0 +1,435 @@ +Google C++ Testing Framework +============================ + +http://code.google.com/p/googletest/ + +Overview +-------- + +Google's framework for writing C++ tests on a variety of platforms +(Linux, Mac OS X, Windows, Windows CE, Symbian, etc). Based on the +xUnit architecture. Supports automatic test discovery, a rich set of +assertions, user-defined assertions, death tests, fatal and non-fatal +failures, various options for running the tests, and XML test report +generation. + +Please see the project page above for more information as well as the +mailing list for questions, discussions, and development. There is +also an IRC channel on OFTC (irc.oftc.net) #gtest available. Please +join us! + +Requirements for End Users +-------------------------- + +Google Test is designed to have fairly minimal requirements to build +and use with your projects, but there are some. Currently, we support +Linux, Windows, Mac OS X, and Cygwin. We will also make our best +effort to support other platforms (e.g. Solaris, AIX, and z/OS). +However, since core members of the Google Test project have no access +to these platforms, Google Test may have outstanding issues there. If +you notice any problems on your platform, please notify +googletestframework@googlegroups.com. Patches for fixing them are +even more welcome! + +### Linux Requirements ### + +These are the base requirements to build and use Google Test from a source +package (as described below): + * GNU-compatible Make or gmake + * POSIX-standard shell + * POSIX(-2) Regular Expressions (regex.h) + * A C++98-standard-compliant compiler + +### Windows Requirements ### + + * Microsoft Visual C++ 7.1 or newer + +### Cygwin Requirements ### + + * Cygwin 1.5.25-14 or newer + +### Mac OS X Requirements ### + + * Mac OS X 10.4 Tiger or newer + * Developer Tools Installed + +Also, you'll need CMake 2.6.4 or higher if you want to build the +samples using the provided CMake script, regardless of the platform. + +Requirements for Contributors +----------------------------- + +We welcome patches. If you plan to contribute a patch, you need to +build Google Test and its own tests from an SVN checkout (described +below), which has further requirements: + + * Python version 2.3 or newer (for running some of the tests and + re-generating certain source files from templates) + * CMake 2.6.4 or newer + +Getting the Source +------------------ + +There are two primary ways of getting Google Test's source code: you +can download a stable source release in your preferred archive format, +or directly check out the source from our Subversion (SVN) repositary. +The SVN checkout requires a few extra steps and some extra software +packages on your system, but lets you track the latest development and +make patches much more easily, so we highly encourage it. + +### Source Package ### + +Google Test is released in versioned source packages which can be +downloaded from the download page [1]. Several different archive +formats are provided, but the only difference is the tools used to +manipulate them, and the size of the resulting file. Download +whichever you are most comfortable with. + + [1] http://code.google.com/p/googletest/downloads/list + +Once the package is downloaded, expand it using whichever tools you +prefer for that type. This will result in a new directory with the +name "gtest-X.Y.Z" which contains all of the source code. Here are +some examples on Linux: + + tar -xvzf gtest-X.Y.Z.tar.gz + tar -xvjf gtest-X.Y.Z.tar.bz2 + unzip gtest-X.Y.Z.zip + +### SVN Checkout ### + +To check out the main branch (also known as the "trunk") of Google +Test, run the following Subversion command: + + svn checkout http://googletest.googlecode.com/svn/trunk/ gtest-svn + +Setting up the Build +-------------------- + +To build Google Test and your tests that use it, you need to tell your +build system where to find its headers and source files. The exact +way to do it depends on which build system you use, and is usually +straightforward. + +### Generic Build Instructions ### + +Suppose you put Google Test in directory ${GTEST_DIR}. To build it, +create a library build target (or a project as called by Visual Studio +and Xcode) to compile + + ${GTEST_DIR}/src/gtest-all.cc + +with ${GTEST_DIR}/include in the system header search path and ${GTEST_DIR} +in the normal header search path. Assuming a Linux-like system and gcc, +something like the following will do: + + g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \ + -pthread -c ${GTEST_DIR}/src/gtest-all.cc + ar -rv libgtest.a gtest-all.o + +(We need -pthread as Google Test uses threads.) + +Next, you should compile your test source file with +${GTEST_DIR}/include in the system header search path, and link it +with gtest and any other necessary libraries: + + g++ -isystem ${GTEST_DIR}/include -pthread path/to/your_test.cc libgtest.a \ + -o your_test + +As an example, the make/ directory contains a Makefile that you can +use to build Google Test on systems where GNU make is available +(e.g. Linux, Mac OS X, and Cygwin). It doesn't try to build Google +Test's own tests. Instead, it just builds the Google Test library and +a sample test. You can use it as a starting point for your own build +script. + +If the default settings are correct for your environment, the +following commands should succeed: + + cd ${GTEST_DIR}/make + make + ./sample1_unittest + +If you see errors, try to tweak the contents of make/Makefile to make +them go away. There are instructions in make/Makefile on how to do +it. + +### Using CMake ### + +Google Test comes with a CMake build script (CMakeLists.txt) that can +be used on a wide range of platforms ("C" stands for cross-platofrm.). +If you don't have CMake installed already, you can download it for +free from http://www.cmake.org/. + +CMake works by generating native makefiles or build projects that can +be used in the compiler environment of your choice. The typical +workflow starts with: + + mkdir mybuild # Create a directory to hold the build output. + cd mybuild + cmake ${GTEST_DIR} # Generate native build scripts. + +If you want to build Google Test's samples, you should replace the +last command with + + cmake -Dgtest_build_samples=ON ${GTEST_DIR} + +If you are on a *nix system, you should now see a Makefile in the +current directory. Just type 'make' to build gtest. + +If you use Windows and have Vistual Studio installed, a gtest.sln file +and several .vcproj files will be created. You can then build them +using Visual Studio. + +On Mac OS X with Xcode installed, a .xcodeproj file will be generated. + +### Legacy Build Scripts ### + +Before settling on CMake, we have been providing hand-maintained build +projects/scripts for Visual Studio, Xcode, and Autotools. While we +continue to provide them for convenience, they are not actively +maintained any more. We highly recommend that you follow the +instructions in the previous two sections to integrate Google Test +with your existing build system. + +If you still need to use the legacy build scripts, here's how: + +The msvc\ folder contains two solutions with Visual C++ projects. +Open the gtest.sln or gtest-md.sln file using Visual Studio, and you +are ready to build Google Test the same way you build any Visual +Studio project. Files that have names ending with -md use DLL +versions of Microsoft runtime libraries (the /MD or the /MDd compiler +option). Files without that suffix use static versions of the runtime +libraries (the /MT or the /MTd option). Please note that one must use +the same option to compile both gtest and the test code. If you use +Visual Studio 2005 or above, we recommend the -md version as /MD is +the default for new projects in these versions of Visual Studio. + +On Mac OS X, open the gtest.xcodeproj in the xcode/ folder using +Xcode. Build the "gtest" target. The universal binary framework will +end up in your selected build directory (selected in the Xcode +"Preferences..." -> "Building" pane and defaults to xcode/build). +Alternatively, at the command line, enter: + + xcodebuild + +This will build the "Release" configuration of gtest.framework in your +default build location. See the "xcodebuild" man page for more +information about building different configurations and building in +different locations. + +If you wish to use the Google Test Xcode project with Xcode 4.x and +above, you need to either: + * update the SDK configuration options in xcode/Config/General.xconfig. + Comment options SDKROOT, MACOS_DEPLOYMENT_TARGET, and GCC_VERSION. If + you choose this route you lose the ability to target earlier versions + of MacOS X. + * Install an SDK for an earlier version. This doesn't appear to be + supported by Apple, but has been reported to work + (http://stackoverflow.com/questions/5378518). + +Tweaking Google Test +-------------------- + +Google Test can be used in diverse environments. The default +configuration may not work (or may not work well) out of the box in +some environments. However, you can easily tweak Google Test by +defining control macros on the compiler command line. Generally, +these macros are named like GTEST_XYZ and you define them to either 1 +or 0 to enable or disable a certain feature. + +We list the most frequently used macros below. For a complete list, +see file include/gtest/internal/gtest-port.h. + +### Choosing a TR1 Tuple Library ### + +Some Google Test features require the C++ Technical Report 1 (TR1) +tuple library, which is not yet available with all compilers. The +good news is that Google Test implements a subset of TR1 tuple that's +enough for its own need, and will automatically use this when the +compiler doesn't provide TR1 tuple. + +Usually you don't need to care about which tuple library Google Test +uses. However, if your project already uses TR1 tuple, you need to +tell Google Test to use the same TR1 tuple library the rest of your +project uses, or the two tuple implementations will clash. To do +that, add + + -DGTEST_USE_OWN_TR1_TUPLE=0 + +to the compiler flags while compiling Google Test and your tests. If +you want to force Google Test to use its own tuple library, just add + + -DGTEST_USE_OWN_TR1_TUPLE=1 + +to the compiler flags instead. + +If you don't want Google Test to use tuple at all, add + + -DGTEST_HAS_TR1_TUPLE=0 + +and all features using tuple will be disabled. + +### Multi-threaded Tests ### + +Google Test is thread-safe where the pthread library is available. +After #include "gtest/gtest.h", you can check the GTEST_IS_THREADSAFE +macro to see whether this is the case (yes if the macro is #defined to +1, no if it's undefined.). + +If Google Test doesn't correctly detect whether pthread is available +in your environment, you can force it with + + -DGTEST_HAS_PTHREAD=1 + +or + + -DGTEST_HAS_PTHREAD=0 + +When Google Test uses pthread, you may need to add flags to your +compiler and/or linker to select the pthread library, or you'll get +link errors. If you use the CMake script or the deprecated Autotools +script, this is taken care of for you. If you use your own build +script, you'll need to read your compiler and linker's manual to +figure out what flags to add. + +### As a Shared Library (DLL) ### + +Google Test is compact, so most users can build and link it as a +static library for the simplicity. You can choose to use Google Test +as a shared library (known as a DLL on Windows) if you prefer. + +To compile *gtest* as a shared library, add + + -DGTEST_CREATE_SHARED_LIBRARY=1 + +to the compiler flags. You'll also need to tell the linker to produce +a shared library instead - consult your linker's manual for how to do +it. + +To compile your *tests* that use the gtest shared library, add + + -DGTEST_LINKED_AS_SHARED_LIBRARY=1 + +to the compiler flags. + +Note: while the above steps aren't technically necessary today when +using some compilers (e.g. GCC), they may become necessary in the +future, if we decide to improve the speed of loading the library (see +http://gcc.gnu.org/wiki/Visibility for details). Therefore you are +recommended to always add the above flags when using Google Test as a +shared library. Otherwise a future release of Google Test may break +your build script. + +### Avoiding Macro Name Clashes ### + +In C++, macros don't obey namespaces. Therefore two libraries that +both define a macro of the same name will clash if you #include both +definitions. In case a Google Test macro clashes with another +library, you can force Google Test to rename its macro to avoid the +conflict. + +Specifically, if both Google Test and some other code define macro +FOO, you can add + + -DGTEST_DONT_DEFINE_FOO=1 + +to the compiler flags to tell Google Test to change the macro's name +from FOO to GTEST_FOO. Currently FOO can be FAIL, SUCCEED, or TEST. +For example, with -DGTEST_DONT_DEFINE_TEST=1, you'll need to write + + GTEST_TEST(SomeTest, DoesThis) { ... } + +instead of + + TEST(SomeTest, DoesThis) { ... } + +in order to define a test. + +Upgrating from an Earlier Version +--------------------------------- + +We strive to keep Google Test releases backward compatible. +Sometimes, though, we have to make some breaking changes for the +users' long-term benefits. This section describes what you'll need to +do if you are upgrading from an earlier version of Google Test. + +### Upgrading from 1.3.0 or Earlier ### + +You may need to explicitly enable or disable Google Test's own TR1 +tuple library. See the instructions in section "Choosing a TR1 Tuple +Library". + +### Upgrading from 1.4.0 or Earlier ### + +The Autotools build script (configure + make) is no longer officially +supportted. You are encouraged to migrate to your own build system or +use CMake. If you still need to use Autotools, you can find +instructions in the README file from Google Test 1.4.0. + +On platforms where the pthread library is available, Google Test uses +it in order to be thread-safe. See the "Multi-threaded Tests" section +for what this means to your build script. + +If you use Microsoft Visual C++ 7.1 with exceptions disabled, Google +Test will no longer compile. This should affect very few people, as a +large portion of STL (including ) doesn't compile in this mode +anyway. We decided to stop supporting it in order to greatly simplify +Google Test's implementation. + +Developing Google Test +---------------------- + +This section discusses how to make your own changes to Google Test. + +### Testing Google Test Itself ### + +To make sure your changes work as intended and don't break existing +functionality, you'll want to compile and run Google Test's own tests. +For that you can use CMake: + + mkdir mybuild + cd mybuild + cmake -Dgtest_build_tests=ON ${GTEST_DIR} + +Make sure you have Python installed, as some of Google Test's tests +are written in Python. If the cmake command complains about not being +able to find Python ("Could NOT find PythonInterp (missing: +PYTHON_EXECUTABLE)"), try telling it explicitly where your Python +executable can be found: + + cmake -DPYTHON_EXECUTABLE=path/to/python -Dgtest_build_tests=ON ${GTEST_DIR} + +Next, you can build Google Test and all of its own tests. On *nix, +this is usually done by 'make'. To run the tests, do + + make test + +All tests should pass. + +### Regenerating Source Files ### + +Some of Google Test's source files are generated from templates (not +in the C++ sense) using a script. A template file is named FOO.pump, +where FOO is the name of the file it will generate. For example, the +file include/gtest/internal/gtest-type-util.h.pump is used to generate +gtest-type-util.h in the same directory. + +Normally you don't need to worry about regenerating the source files, +unless you need to modify them. In that case, you should modify the +corresponding .pump files instead and run the pump.py Python script to +regenerate them. You can find pump.py in the scripts/ directory. +Read the Pump manual [2] for how to use it. + + [2] http://code.google.com/p/googletest/wiki/PumpManual + +### Contributing a Patch ### + +We welcome patches. Please read the Google Test developer's guide [3] +for how you can contribute. In particular, make sure you have signed +the Contributor License Agreement, or we won't be able to accept the +patch. + + [3] http://code.google.com/p/googletest/wiki/GoogleTestDevGuide + +Happy testing! diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/cmake/internal_utils.cmake b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/cmake/internal_utils.cmake new file mode 100644 index 00000000..8cb21894 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/cmake/internal_utils.cmake @@ -0,0 +1,227 @@ +# Defines functions and macros useful for building Google Test and +# Google Mock. +# +# Note: +# +# - This file will be run twice when building Google Mock (once via +# Google Test's CMakeLists.txt, and once via Google Mock's). +# Therefore it shouldn't have any side effects other than defining +# the functions and macros. +# +# - The functions/macros defined in this file may depend on Google +# Test and Google Mock's option() definitions, and thus must be +# called *after* the options have been defined. + +# Tweaks CMake's default compiler/linker settings to suit Google Test's needs. +# +# This must be a macro(), as inside a function string() can only +# update variables in the function scope. +macro(fix_default_compiler_settings_) + if (MSVC) + # For MSVC, CMake sets certain flags to defaults we want to override. + # This replacement code is taken from sample in the CMake Wiki at + # http://www.cmake.org/Wiki/CMake_FAQ#Dynamic_Replace. + foreach (flag_var + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + if (NOT BUILD_SHARED_LIBS AND NOT gtest_force_shared_crt) + # When Google Test is built as a shared library, it should also use + # shared runtime libraries. Otherwise, it may end up with multiple + # copies of runtime library data in different modules, resulting in + # hard-to-find crashes. When it is built as a static library, it is + # preferable to use CRT as static libraries, as we don't have to rely + # on CRT DLLs being available. CMake always defaults to using shared + # CRT libraries, so we override that default here. + string(REPLACE "/MD" "-MT" ${flag_var} "${${flag_var}}") + endif() + + # We prefer more strict warning checking for building Google Test. + # Replaces /W3 with /W4 in defaults. + string(REPLACE "/W3" "-W4" ${flag_var} "${${flag_var}}") + endforeach() + endif() +endmacro() + +# Defines the compiler/linker flags used to build Google Test and +# Google Mock. You can tweak these definitions to suit your need. A +# variable's value is empty before it's explicitly assigned to. +macro(config_compiler_and_linker) + if (NOT gtest_disable_pthreads) + # Defines CMAKE_USE_PTHREADS_INIT and CMAKE_THREAD_LIBS_INIT. + find_package(Threads) + endif() + + fix_default_compiler_settings_() + if (MSVC) + # Newlines inside flags variables break CMake's NMake generator. + # TODO(vladl@google.com): Add -RTCs and -RTCu to debug builds. + set(cxx_base_flags "-GS -W4 -WX -wd4127 -wd4251 -wd4275 -nologo -J -Zi") + if (MSVC_VERSION LESS 1400) + # Suppress spurious warnings MSVC 7.1 sometimes issues. + # Forcing value to bool. + set(cxx_base_flags "${cxx_base_flags} -wd4800") + # Copy constructor and assignment operator could not be generated. + set(cxx_base_flags "${cxx_base_flags} -wd4511 -wd4512") + # Compatibility warnings not applicable to Google Test. + # Resolved overload was found by argument-dependent lookup. + set(cxx_base_flags "${cxx_base_flags} -wd4675") + endif() + set(cxx_base_flags "${cxx_base_flags} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32") + set(cxx_base_flags "${cxx_base_flags} -DSTRICT -DWIN32_LEAN_AND_MEAN") + set(cxx_exception_flags "-EHsc -D_HAS_EXCEPTIONS=1") + set(cxx_no_exception_flags "-D_HAS_EXCEPTIONS=0") + set(cxx_no_rtti_flags "-GR-") + elseif (CMAKE_COMPILER_IS_GNUCXX) + set(cxx_base_flags "-Wall -Wshadow") + set(cxx_exception_flags "-fexceptions") + set(cxx_no_exception_flags "-fno-exceptions") + # Until version 4.3.2, GCC doesn't define a macro to indicate + # whether RTTI is enabled. Therefore we define GTEST_HAS_RTTI + # explicitly. + set(cxx_no_rtti_flags "-fno-rtti -DGTEST_HAS_RTTI=0") + set(cxx_strict_flags + "-Wextra -Wno-unused-parameter -Wno-missing-field-initializers") + elseif (CMAKE_CXX_COMPILER_ID STREQUAL "SunPro") + set(cxx_exception_flags "-features=except") + # Sun Pro doesn't provide macros to indicate whether exceptions and + # RTTI are enabled, so we define GTEST_HAS_* explicitly. + set(cxx_no_exception_flags "-features=no%except -DGTEST_HAS_EXCEPTIONS=0") + set(cxx_no_rtti_flags "-features=no%rtti -DGTEST_HAS_RTTI=0") + elseif (CMAKE_CXX_COMPILER_ID STREQUAL "VisualAge" OR + CMAKE_CXX_COMPILER_ID STREQUAL "XL") + # CMake 2.8 changes Visual Age's compiler ID to "XL". + set(cxx_exception_flags "-qeh") + set(cxx_no_exception_flags "-qnoeh") + # Until version 9.0, Visual Age doesn't define a macro to indicate + # whether RTTI is enabled. Therefore we define GTEST_HAS_RTTI + # explicitly. + set(cxx_no_rtti_flags "-qnortti -DGTEST_HAS_RTTI=0") + elseif (CMAKE_CXX_COMPILER_ID STREQUAL "HP") + set(cxx_base_flags "-AA -mt") + set(cxx_exception_flags "-DGTEST_HAS_EXCEPTIONS=1") + set(cxx_no_exception_flags "+noeh -DGTEST_HAS_EXCEPTIONS=0") + # RTTI can not be disabled in HP aCC compiler. + set(cxx_no_rtti_flags "") + endif() + + if (CMAKE_USE_PTHREADS_INIT) # The pthreads library is available and allowed. + set(cxx_base_flags "${cxx_base_flags} -DGTEST_HAS_PTHREAD=1") + else() + set(cxx_base_flags "${cxx_base_flags} -DGTEST_HAS_PTHREAD=0") + endif() + + # For building gtest's own tests and samples. + set(cxx_exception "${CMAKE_CXX_FLAGS} ${cxx_base_flags} ${cxx_exception_flags}") + set(cxx_no_exception + "${CMAKE_CXX_FLAGS} ${cxx_base_flags} ${cxx_no_exception_flags}") + set(cxx_default "${cxx_exception}") + set(cxx_no_rtti "${cxx_default} ${cxx_no_rtti_flags}") + set(cxx_use_own_tuple "${cxx_default} -DGTEST_USE_OWN_TR1_TUPLE=1") + + # For building the gtest libraries. + set(cxx_strict "${cxx_default} ${cxx_strict_flags}") +endmacro() + +# Defines the gtest & gtest_main libraries. User tests should link +# with one of them. +function(cxx_library_with_type name type cxx_flags) + # type can be either STATIC or SHARED to denote a static or shared library. + # ARGN refers to additional arguments after 'cxx_flags'. + add_library(${name} ${type} ${ARGN}) + set_target_properties(${name} + PROPERTIES + COMPILE_FLAGS "${cxx_flags}") + if (BUILD_SHARED_LIBS OR type STREQUAL "SHARED") + set_target_properties(${name} + PROPERTIES + COMPILE_DEFINITIONS "GTEST_CREATE_SHARED_LIBRARY=1") + endif() + if (CMAKE_USE_PTHREADS_INIT) + target_link_libraries(${name} ${CMAKE_THREAD_LIBS_INIT}) + endif() +endfunction() + +######################################################################## +# +# Helper functions for creating build targets. + +function(cxx_shared_library name cxx_flags) + cxx_library_with_type(${name} SHARED "${cxx_flags}" ${ARGN}) +endfunction() + +function(cxx_library name cxx_flags) + cxx_library_with_type(${name} "" "${cxx_flags}" ${ARGN}) +endfunction() + +# cxx_executable_with_flags(name cxx_flags libs srcs...) +# +# creates a named C++ executable that depends on the given libraries and +# is built from the given source files with the given compiler flags. +function(cxx_executable_with_flags name cxx_flags libs) + add_executable(${name} ${ARGN}) + if (cxx_flags) + set_target_properties(${name} + PROPERTIES + COMPILE_FLAGS "${cxx_flags}") + endif() + if (BUILD_SHARED_LIBS) + set_target_properties(${name} + PROPERTIES + COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1") + endif() + # To support mixing linking in static and dynamic libraries, link each + # library in with an extra call to target_link_libraries. + foreach (lib "${libs}") + target_link_libraries(${name} ${lib}) + endforeach() +endfunction() + +# cxx_executable(name dir lib srcs...) +# +# creates a named target that depends on the given libs and is built +# from the given source files. dir/name.cc is implicitly included in +# the source file list. +function(cxx_executable name dir libs) + cxx_executable_with_flags( + ${name} "${cxx_default}" "${libs}" "${dir}/${name}.cc" ${ARGN}) +endfunction() + +# Sets PYTHONINTERP_FOUND and PYTHON_EXECUTABLE. +find_package(PythonInterp) + +# cxx_test_with_flags(name cxx_flags libs srcs...) +# +# creates a named C++ test that depends on the given libs and is built +# from the given source files with the given compiler flags. +function(cxx_test_with_flags name cxx_flags libs) + cxx_executable_with_flags(${name} "${cxx_flags}" "${libs}" ${ARGN}) + add_test(${name} ${name}) +endfunction() + +# cxx_test(name libs srcs...) +# +# creates a named test target that depends on the given libs and is +# built from the given source files. Unlike cxx_test_with_flags, +# test/name.cc is already implicitly included in the source file list. +function(cxx_test name libs) + cxx_test_with_flags("${name}" "${cxx_default}" "${libs}" + "test/${name}.cc" ${ARGN}) +endfunction() + +# py_test(name) +# +# creates a Python test with the given name whose main module is in +# test/name.py. It does nothing if Python is not installed. +function(py_test name) + # We are not supporting Python tests on Linux yet as they consider + # all Linux environments to be google3 and try to use google3 features. + if (PYTHONINTERP_FOUND) + # ${CMAKE_BINARY_DIR} is known at configuration time, so we can + # directly bind it from cmake. ${CTEST_CONFIGURATION_TYPE} is known + # only at ctest runtime (by calling ctest -c ), so + # we have to escape $ to delay variable substitution here. + add_test(${name} + ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py + --build_dir=${CMAKE_CURRENT_BINARY_DIR}/\${CTEST_CONFIGURATION_TYPE}) + endif() +endfunction() diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-death-test.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-death-test.h new file mode 100644 index 00000000..957a69c6 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-death-test.h @@ -0,0 +1,294 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines the public API for death tests. It is +// #included by gtest.h so a user doesn't need to include this +// directly. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ + +#include "gtest/internal/gtest-death-test-internal.h" + +namespace testing { + +// This flag controls the style of death tests. Valid values are "threadsafe", +// meaning that the death test child process will re-execute the test binary +// from the start, running only a single death test, or "fast", +// meaning that the child process will execute the test logic immediately +// after forking. +GTEST_DECLARE_string_(death_test_style); + +#if GTEST_HAS_DEATH_TEST + +namespace internal { + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +GTEST_API_ bool InDeathTestChild(); + +} // namespace internal + +// The following macros are useful for writing death tests. + +// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is +// executed: +// +// 1. It generates a warning if there is more than one active +// thread. This is because it's safe to fork() or clone() only +// when there is a single thread. +// +// 2. The parent process clone()s a sub-process and runs the death +// test in it; the sub-process exits with code 0 at the end of the +// death test, if it hasn't exited already. +// +// 3. The parent process waits for the sub-process to terminate. +// +// 4. The parent process checks the exit code and error message of +// the sub-process. +// +// Examples: +// +// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number"); +// for (int i = 0; i < 5; i++) { +// EXPECT_DEATH(server.ProcessRequest(i), +// "Invalid request .* in ProcessRequest()") +// << "Failed to die on request " << i; +// } +// +// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting"); +// +// bool KilledBySIGHUP(int exit_code) { +// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP; +// } +// +// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!"); +// +// On the regular expressions used in death tests: +// +// On POSIX-compliant systems (*nix), we use the library, +// which uses the POSIX extended regex syntax. +// +// On other platforms (e.g. Windows), we only support a simple regex +// syntax implemented as part of Google Test. This limited +// implementation should be enough most of the time when writing +// death tests; though it lacks many features you can find in PCRE +// or POSIX extended regex syntax. For example, we don't support +// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and +// repetition count ("x{5,7}"), among others. +// +// Below is the syntax that we do support. We chose it to be a +// subset of both PCRE and POSIX extended regex, so it's easy to +// learn wherever you come from. In the following: 'A' denotes a +// literal character, period (.), or a single \\ escape sequence; +// 'x' and 'y' denote regular expressions; 'm' and 'n' are for +// natural numbers. +// +// c matches any literal character c +// \\d matches any decimal digit +// \\D matches any character that's not a decimal digit +// \\f matches \f +// \\n matches \n +// \\r matches \r +// \\s matches any ASCII whitespace, including \n +// \\S matches any character that's not a whitespace +// \\t matches \t +// \\v matches \v +// \\w matches any letter, _, or decimal digit +// \\W matches any character that \\w doesn't match +// \\c matches any literal character c, which must be a punctuation +// . matches any single character except \n +// A? matches 0 or 1 occurrences of A +// A* matches 0 or many occurrences of A +// A+ matches 1 or many occurrences of A +// ^ matches the beginning of a string (not that of each line) +// $ matches the end of a string (not that of each line) +// xy matches x followed by y +// +// If you accidentally use PCRE or POSIX extended regex features +// not implemented by us, you will get a run-time failure. In that +// case, please try to rewrite your regular expression within the +// above syntax. +// +// This implementation is *not* meant to be as highly tuned or robust +// as a compiled regex library, but should perform well enough for a +// death test, which already incurs significant overhead by launching +// a child process. +// +// Known caveats: +// +// A "threadsafe" style death test obtains the path to the test +// program from argv[0] and re-executes it in the sub-process. For +// simplicity, the current implementation doesn't search the PATH +// when launching the sub-process. This means that the user must +// invoke the test program via a path that contains at least one +// path separator (e.g. path/to/foo_test and +// /absolute/path/to/bar_test are fine, but foo_test is not). This +// is rarely a problem as people usually don't put the test binary +// directory in PATH. +// +// TODO(wan@google.com): make thread-safe death tests search the PATH. + +// Asserts that a given statement causes the program to exit, with an +// integer exit status that satisfies predicate, and emitting error output +// that matches regex. +# define ASSERT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_) + +// Like ASSERT_EXIT, but continues on to successive tests in the +// test case, if any: +# define EXPECT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_NONFATAL_FAILURE_) + +// Asserts that a given statement causes the program to exit, either by +// explicitly exiting with a nonzero exit code or being killed by a +// signal, and emitting error output that matches regex. +# define ASSERT_DEATH(statement, regex) \ + ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) + +// Like ASSERT_DEATH, but continues on to successive tests in the +// test case, if any: +# define EXPECT_DEATH(statement, regex) \ + EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) + +// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*: + +// Tests that an exit code describes a normal exit with a given exit code. +class GTEST_API_ ExitedWithCode { + public: + explicit ExitedWithCode(int exit_code); + bool operator()(int exit_status) const; + private: + // No implementation - assignment is unsupported. + void operator=(const ExitedWithCode& other); + + const int exit_code_; +}; + +# if !GTEST_OS_WINDOWS +// Tests that an exit code describes an exit due to termination by a +// given signal. +class GTEST_API_ KilledBySignal { + public: + explicit KilledBySignal(int signum); + bool operator()(int exit_status) const; + private: + const int signum_; +}; +# endif // !GTEST_OS_WINDOWS + +// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode. +// The death testing framework causes this to have interesting semantics, +// since the sideeffects of the call are only visible in opt mode, and not +// in debug mode. +// +// In practice, this can be used to test functions that utilize the +// LOG(DFATAL) macro using the following style: +// +// int DieInDebugOr12(int* sideeffect) { +// if (sideeffect) { +// *sideeffect = 12; +// } +// LOG(DFATAL) << "death"; +// return 12; +// } +// +// TEST(TestCase, TestDieOr12WorksInDgbAndOpt) { +// int sideeffect = 0; +// // Only asserts in dbg. +// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death"); +// +// #ifdef NDEBUG +// // opt-mode has sideeffect visible. +// EXPECT_EQ(12, sideeffect); +// #else +// // dbg-mode no visible sideeffect. +// EXPECT_EQ(0, sideeffect); +// #endif +// } +// +// This will assert that DieInDebugReturn12InOpt() crashes in debug +// mode, usually due to a DCHECK or LOG(DFATAL), but returns the +// appropriate fallback value (12 in this case) in opt mode. If you +// need to test that a function has appropriate side-effects in opt +// mode, include assertions against the side-effects. A general +// pattern for this is: +// +// EXPECT_DEBUG_DEATH({ +// // Side-effects here will have an effect after this statement in +// // opt mode, but none in debug mode. +// EXPECT_EQ(12, DieInDebugOr12(&sideeffect)); +// }, "death"); +// +# ifdef NDEBUG + +# define EXPECT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +# define ASSERT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +# else + +# define EXPECT_DEBUG_DEATH(statement, regex) \ + EXPECT_DEATH(statement, regex) + +# define ASSERT_DEBUG_DEATH(statement, regex) \ + ASSERT_DEATH(statement, regex) + +# endif // NDEBUG for EXPECT_DEBUG_DEATH +#endif // GTEST_HAS_DEATH_TEST + +// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and +// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if +// death tests are supported; otherwise they just issue a warning. This is +// useful when you are combining death test assertions with normal test +// assertions in one test. +#if GTEST_HAS_DEATH_TEST +# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + EXPECT_DEATH(statement, regex) +# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + ASSERT_DEATH(statement, regex) +#else +# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, ) +# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, return) +#endif + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-message.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-message.h new file mode 100644 index 00000000..fe879bca --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-message.h @@ -0,0 +1,250 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines the Message class. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! + +#ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +#define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ + +#include + +#include "gtest/internal/gtest-port.h" + +// Ensures that there is at least one operator<< in the global namespace. +// See Message& operator<<(...) below for why. +void operator<<(const testing::internal::Secret&, int); + +namespace testing { + +// The Message class works like an ostream repeater. +// +// Typical usage: +// +// 1. You stream a bunch of values to a Message object. +// It will remember the text in a stringstream. +// 2. Then you stream the Message object to an ostream. +// This causes the text in the Message to be streamed +// to the ostream. +// +// For example; +// +// testing::Message foo; +// foo << 1 << " != " << 2; +// std::cout << foo; +// +// will print "1 != 2". +// +// Message is not intended to be inherited from. In particular, its +// destructor is not virtual. +// +// Note that stringstream behaves differently in gcc and in MSVC. You +// can stream a NULL char pointer to it in the former, but not in the +// latter (it causes an access violation if you do). The Message +// class hides this difference by treating a NULL char pointer as +// "(null)". +class GTEST_API_ Message { + private: + // The type of basic IO manipulators (endl, ends, and flush) for + // narrow streams. + typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&); + + public: + // Constructs an empty Message. + Message(); + + // Copy constructor. + Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT + *ss_ << msg.GetString(); + } + + // Constructs a Message from a C-string. + explicit Message(const char* str) : ss_(new ::std::stringstream) { + *ss_ << str; + } + +#if GTEST_OS_SYMBIAN + // Streams a value (either a pointer or not) to this object. + template + inline Message& operator <<(const T& value) { + StreamHelper(typename internal::is_pointer::type(), value); + return *this; + } +#else + // Streams a non-pointer value to this object. + template + inline Message& operator <<(const T& val) { + // Some libraries overload << for STL containers. These + // overloads are defined in the global namespace instead of ::std. + // + // C++'s symbol lookup rule (i.e. Koenig lookup) says that these + // overloads are visible in either the std namespace or the global + // namespace, but not other namespaces, including the testing + // namespace which Google Test's Message class is in. + // + // To allow STL containers (and other types that has a << operator + // defined in the global namespace) to be used in Google Test + // assertions, testing::Message must access the custom << operator + // from the global namespace. With this using declaration, + // overloads of << defined in the global namespace and those + // visible via Koenig lookup are both exposed in this function. + using ::operator <<; + *ss_ << val; + return *this; + } + + // Streams a pointer value to this object. + // + // This function is an overload of the previous one. When you + // stream a pointer to a Message, this definition will be used as it + // is more specialized. (The C++ Standard, section + // [temp.func.order].) If you stream a non-pointer, then the + // previous definition will be used. + // + // The reason for this overload is that streaming a NULL pointer to + // ostream is undefined behavior. Depending on the compiler, you + // may get "0", "(nil)", "(null)", or an access violation. To + // ensure consistent result across compilers, we always treat NULL + // as "(null)". + template + inline Message& operator <<(T* const& pointer) { // NOLINT + if (pointer == NULL) { + *ss_ << "(null)"; + } else { + *ss_ << pointer; + } + return *this; + } +#endif // GTEST_OS_SYMBIAN + + // Since the basic IO manipulators are overloaded for both narrow + // and wide streams, we have to provide this specialized definition + // of operator <<, even though its body is the same as the + // templatized version above. Without this definition, streaming + // endl or other basic IO manipulators to Message will confuse the + // compiler. + Message& operator <<(BasicNarrowIoManip val) { + *ss_ << val; + return *this; + } + + // Instead of 1/0, we want to see true/false for bool values. + Message& operator <<(bool b) { + return *this << (b ? "true" : "false"); + } + + // These two overloads allow streaming a wide C string to a Message + // using the UTF-8 encoding. + Message& operator <<(const wchar_t* wide_c_str); + Message& operator <<(wchar_t* wide_c_str); + +#if GTEST_HAS_STD_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator <<(const ::std::wstring& wstr); +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator <<(const ::wstring& wstr); +#endif // GTEST_HAS_GLOBAL_WSTRING + + // Gets the text streamed to this object so far as an std::string. + // Each '\0' character in the buffer is replaced with "\\0". + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + std::string GetString() const; + + private: + +#if GTEST_OS_SYMBIAN + // These are needed as the Nokia Symbian Compiler cannot decide between + // const T& and const T* in a function template. The Nokia compiler _can_ + // decide between class template specializations for T and T*, so a + // tr1::type_traits-like is_pointer works, and we can overload on that. + template + inline void StreamHelper(internal::true_type /*is_pointer*/, T* pointer) { + if (pointer == NULL) { + *ss_ << "(null)"; + } else { + *ss_ << pointer; + } + } + template + inline void StreamHelper(internal::false_type /*is_pointer*/, + const T& value) { + // See the comments in Message& operator <<(const T&) above for why + // we need this using statement. + using ::operator <<; + *ss_ << value; + } +#endif // GTEST_OS_SYMBIAN + + // We'll hold the text streamed to this object here. + const internal::scoped_ptr< ::std::stringstream> ss_; + + // We declare (but don't implement) this to prevent the compiler + // from implementing the assignment operator. + void operator=(const Message&); +}; + +// Streams a Message to an ostream. +inline std::ostream& operator <<(std::ostream& os, const Message& sb) { + return os << sb.GetString(); +} + +namespace internal { + +// Converts a streamable value to an std::string. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". +template +std::string StreamableToString(const T& streamable) { + return (Message() << streamable).GetString(); +} + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-param-test.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-param-test.h new file mode 100644 index 00000000..d6702c8f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-param-test.h @@ -0,0 +1,1421 @@ +// This file was GENERATED by command: +// pump.py gtest-param-test.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: vladl@google.com (Vlad Losev) +// +// Macros and functions for implementing parameterized tests +// in Google C++ Testing Framework (Google Test) +// +// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +#ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ + + +// Value-parameterized tests allow you to test your code with different +// parameters without writing multiple copies of the same test. +// +// Here is how you use value-parameterized tests: + +#if 0 + +// To write value-parameterized tests, first you should define a fixture +// class. It is usually derived from testing::TestWithParam (see below for +// another inheritance scheme that's sometimes useful in more complicated +// class hierarchies), where the type of your parameter values. +// TestWithParam is itself derived from testing::Test. T can be any +// copyable type. If it's a raw pointer, you are responsible for managing the +// lifespan of the pointed values. + +class FooTest : public ::testing::TestWithParam { + // You can implement all the usual class fixture members here. +}; + +// Then, use the TEST_P macro to define as many parameterized tests +// for this fixture as you want. The _P suffix is for "parameterized" +// or "pattern", whichever you prefer to think. + +TEST_P(FooTest, DoesBlah) { + // Inside a test, access the test parameter with the GetParam() method + // of the TestWithParam class: + EXPECT_TRUE(foo.Blah(GetParam())); + ... +} + +TEST_P(FooTest, HasBlahBlah) { + ... +} + +// Finally, you can use INSTANTIATE_TEST_CASE_P to instantiate the test +// case with any set of parameters you want. Google Test defines a number +// of functions for generating test parameters. They return what we call +// (surprise!) parameter generators. Here is a summary of them, which +// are all in the testing namespace: +// +// +// Range(begin, end [, step]) - Yields values {begin, begin+step, +// begin+step+step, ...}. The values do not +// include end. step defaults to 1. +// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}. +// ValuesIn(container) - Yields values from a C-style array, an STL +// ValuesIn(begin,end) container, or an iterator range [begin, end). +// Bool() - Yields sequence {false, true}. +// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product +// for the math savvy) of the values generated +// by the N generators. +// +// For more details, see comments at the definitions of these functions below +// in this file. +// +// The following statement will instantiate tests from the FooTest test case +// each with parameter values "meeny", "miny", and "moe". + +INSTANTIATE_TEST_CASE_P(InstantiationName, + FooTest, + Values("meeny", "miny", "moe")); + +// To distinguish different instances of the pattern, (yes, you +// can instantiate it more then once) the first argument to the +// INSTANTIATE_TEST_CASE_P macro is a prefix that will be added to the +// actual test case name. Remember to pick unique prefixes for different +// instantiations. The tests from the instantiation above will have +// these names: +// +// * InstantiationName/FooTest.DoesBlah/0 for "meeny" +// * InstantiationName/FooTest.DoesBlah/1 for "miny" +// * InstantiationName/FooTest.DoesBlah/2 for "moe" +// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny" +// * InstantiationName/FooTest.HasBlahBlah/1 for "miny" +// * InstantiationName/FooTest.HasBlahBlah/2 for "moe" +// +// You can use these names in --gtest_filter. +// +// This statement will instantiate all tests from FooTest again, each +// with parameter values "cat" and "dog": + +const char* pets[] = {"cat", "dog"}; +INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets)); + +// The tests from the instantiation above will have these names: +// +// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog" +// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog" +// +// Please note that INSTANTIATE_TEST_CASE_P will instantiate all tests +// in the given test case, whether their definitions come before or +// AFTER the INSTANTIATE_TEST_CASE_P statement. +// +// Please also note that generator expressions (including parameters to the +// generators) are evaluated in InitGoogleTest(), after main() has started. +// This allows the user on one hand, to adjust generator parameters in order +// to dynamically determine a set of tests to run and on the other hand, +// give the user a chance to inspect the generated tests with Google Test +// reflection API before RUN_ALL_TESTS() is executed. +// +// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc +// for more examples. +// +// In the future, we plan to publish the API for defining new parameter +// generators. But for now this interface remains part of the internal +// implementation and is subject to change. +// +// +// A parameterized test fixture must be derived from testing::Test and from +// testing::WithParamInterface, where T is the type of the parameter +// values. Inheriting from TestWithParam satisfies that requirement because +// TestWithParam inherits from both Test and WithParamInterface. In more +// complicated hierarchies, however, it is occasionally useful to inherit +// separately from Test and WithParamInterface. For example: + +class BaseTest : public ::testing::Test { + // You can inherit all the usual members for a non-parameterized test + // fixture here. +}; + +class DerivedTest : public BaseTest, public ::testing::WithParamInterface { + // The usual test fixture members go here too. +}; + +TEST_F(BaseTest, HasFoo) { + // This is an ordinary non-parameterized test. +} + +TEST_P(DerivedTest, DoesBlah) { + // GetParam works just the same here as if you inherit from TestWithParam. + EXPECT_TRUE(foo.Blah(GetParam())); +} + +#endif // 0 + +#include "gtest/internal/gtest-port.h" + +#if !GTEST_OS_SYMBIAN +# include +#endif + +// scripts/fuse_gtest.py depends on gtest's own header being #included +// *unconditionally*. Therefore these #includes cannot be moved +// inside #if GTEST_HAS_PARAM_TEST. +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-param-util.h" +#include "gtest/internal/gtest-param-util-generated.h" + +#if GTEST_HAS_PARAM_TEST + +namespace testing { + +// Functions producing parameter generators. +// +// Google Test uses these generators to produce parameters for value- +// parameterized tests. When a parameterized test case is instantiated +// with a particular generator, Google Test creates and runs tests +// for each element in the sequence produced by the generator. +// +// In the following sample, tests from test case FooTest are instantiated +// each three times with parameter values 3, 5, and 8: +// +// class FooTest : public TestWithParam { ... }; +// +// TEST_P(FooTest, TestThis) { +// } +// TEST_P(FooTest, TestThat) { +// } +// INSTANTIATE_TEST_CASE_P(TestSequence, FooTest, Values(3, 5, 8)); +// + +// Range() returns generators providing sequences of values in a range. +// +// Synopsis: +// Range(start, end) +// - returns a generator producing a sequence of values {start, start+1, +// start+2, ..., }. +// Range(start, end, step) +// - returns a generator producing a sequence of values {start, start+step, +// start+step+step, ..., }. +// Notes: +// * The generated sequences never include end. For example, Range(1, 5) +// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2) +// returns a generator producing {1, 3, 5, 7}. +// * start and end must have the same type. That type may be any integral or +// floating-point type or a user defined type satisfying these conditions: +// * It must be assignable (have operator=() defined). +// * It must have operator+() (operator+(int-compatible type) for +// two-operand version). +// * It must have operator<() defined. +// Elements in the resulting sequences will also have that type. +// * Condition start < end must be satisfied in order for resulting sequences +// to contain any elements. +// +template +internal::ParamGenerator Range(T start, T end, IncrementT step) { + return internal::ParamGenerator( + new internal::RangeGenerator(start, end, step)); +} + +template +internal::ParamGenerator Range(T start, T end) { + return Range(start, end, 1); +} + +// ValuesIn() function allows generation of tests with parameters coming from +// a container. +// +// Synopsis: +// ValuesIn(const T (&array)[N]) +// - returns a generator producing sequences with elements from +// a C-style array. +// ValuesIn(const Container& container) +// - returns a generator producing sequences with elements from +// an STL-style container. +// ValuesIn(Iterator begin, Iterator end) +// - returns a generator producing sequences with elements from +// a range [begin, end) defined by a pair of STL-style iterators. These +// iterators can also be plain C pointers. +// +// Please note that ValuesIn copies the values from the containers +// passed in and keeps them to generate tests in RUN_ALL_TESTS(). +// +// Examples: +// +// This instantiates tests from test case StringTest +// each with C-string values of "foo", "bar", and "baz": +// +// const char* strings[] = {"foo", "bar", "baz"}; +// INSTANTIATE_TEST_CASE_P(StringSequence, SrtingTest, ValuesIn(strings)); +// +// This instantiates tests from test case StlStringTest +// each with STL strings with values "a" and "b": +// +// ::std::vector< ::std::string> GetParameterStrings() { +// ::std::vector< ::std::string> v; +// v.push_back("a"); +// v.push_back("b"); +// return v; +// } +// +// INSTANTIATE_TEST_CASE_P(CharSequence, +// StlStringTest, +// ValuesIn(GetParameterStrings())); +// +// +// This will also instantiate tests from CharTest +// each with parameter values 'a' and 'b': +// +// ::std::list GetParameterChars() { +// ::std::list list; +// list.push_back('a'); +// list.push_back('b'); +// return list; +// } +// ::std::list l = GetParameterChars(); +// INSTANTIATE_TEST_CASE_P(CharSequence2, +// CharTest, +// ValuesIn(l.begin(), l.end())); +// +template +internal::ParamGenerator< + typename ::testing::internal::IteratorTraits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end) { + typedef typename ::testing::internal::IteratorTraits + ::value_type ParamType; + return internal::ParamGenerator( + new internal::ValuesInIteratorRangeGenerator(begin, end)); +} + +template +internal::ParamGenerator ValuesIn(const T (&array)[N]) { + return ValuesIn(array, array + N); +} + +template +internal::ParamGenerator ValuesIn( + const Container& container) { + return ValuesIn(container.begin(), container.end()); +} + +// Values() allows generating tests from explicitly specified list of +// parameters. +// +// Synopsis: +// Values(T v1, T v2, ..., T vN) +// - returns a generator producing sequences with elements v1, v2, ..., vN. +// +// For example, this instantiates tests from test case BarTest each +// with values "one", "two", and "three": +// +// INSTANTIATE_TEST_CASE_P(NumSequence, BarTest, Values("one", "two", "three")); +// +// This instantiates tests from test case BazTest each with values 1, 2, 3.5. +// The exact type of values will depend on the type of parameter in BazTest. +// +// INSTANTIATE_TEST_CASE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5)); +// +// Currently, Values() supports from 1 to 50 parameters. +// +template +internal::ValueArray1 Values(T1 v1) { + return internal::ValueArray1(v1); +} + +template +internal::ValueArray2 Values(T1 v1, T2 v2) { + return internal::ValueArray2(v1, v2); +} + +template +internal::ValueArray3 Values(T1 v1, T2 v2, T3 v3) { + return internal::ValueArray3(v1, v2, v3); +} + +template +internal::ValueArray4 Values(T1 v1, T2 v2, T3 v3, T4 v4) { + return internal::ValueArray4(v1, v2, v3, v4); +} + +template +internal::ValueArray5 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5) { + return internal::ValueArray5(v1, v2, v3, v4, v5); +} + +template +internal::ValueArray6 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6) { + return internal::ValueArray6(v1, v2, v3, v4, v5, v6); +} + +template +internal::ValueArray7 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6, T7 v7) { + return internal::ValueArray7(v1, v2, v3, v4, v5, + v6, v7); +} + +template +internal::ValueArray8 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8) { + return internal::ValueArray8(v1, v2, v3, v4, + v5, v6, v7, v8); +} + +template +internal::ValueArray9 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9) { + return internal::ValueArray9(v1, v2, v3, + v4, v5, v6, v7, v8, v9); +} + +template +internal::ValueArray10 Values(T1 v1, + T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10) { + return internal::ValueArray10(v1, + v2, v3, v4, v5, v6, v7, v8, v9, v10); +} + +template +internal::ValueArray11 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11) { + return internal::ValueArray11(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11); +} + +template +internal::ValueArray12 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12) { + return internal::ValueArray12(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12); +} + +template +internal::ValueArray13 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13) { + return internal::ValueArray13(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13); +} + +template +internal::ValueArray14 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) { + return internal::ValueArray14(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14); +} + +template +internal::ValueArray15 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) { + return internal::ValueArray15(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15); +} + +template +internal::ValueArray16 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16) { + return internal::ValueArray16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15, v16); +} + +template +internal::ValueArray17 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17) { + return internal::ValueArray17(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, + v11, v12, v13, v14, v15, v16, v17); +} + +template +internal::ValueArray18 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, + T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18) { + return internal::ValueArray18(v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18); +} + +template +internal::ValueArray19 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, + T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, + T15 v15, T16 v16, T17 v17, T18 v18, T19 v19) { + return internal::ValueArray19(v1, v2, v3, v4, v5, v6, v7, v8, + v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19); +} + +template +internal::ValueArray20 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20) { + return internal::ValueArray20(v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20); +} + +template +internal::ValueArray21 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21) { + return internal::ValueArray21(v1, v2, v3, v4, v5, v6, + v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21); +} + +template +internal::ValueArray22 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22) { + return internal::ValueArray22(v1, v2, v3, v4, + v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22); +} + +template +internal::ValueArray23 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23) { + return internal::ValueArray23(v1, v2, v3, + v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23); +} + +template +internal::ValueArray24 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24) { + return internal::ValueArray24(v1, v2, + v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, + v19, v20, v21, v22, v23, v24); +} + +template +internal::ValueArray25 Values(T1 v1, + T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, + T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, + T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25) { + return internal::ValueArray25(v1, + v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, + v18, v19, v20, v21, v22, v23, v24, v25); +} + +template +internal::ValueArray26 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26) { + return internal::ValueArray26(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26); +} + +template +internal::ValueArray27 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27) { + return internal::ValueArray27(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, + v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27); +} + +template +internal::ValueArray28 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28) { + return internal::ValueArray28(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, + v28); +} + +template +internal::ValueArray29 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29) { + return internal::ValueArray29(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, + v27, v28, v29); +} + +template +internal::ValueArray30 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, + T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, + T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) { + return internal::ValueArray30(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, + v26, v27, v28, v29, v30); +} + +template +internal::ValueArray31 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) { + return internal::ValueArray31(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, + v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, + v25, v26, v27, v28, v29, v30, v31); +} + +template +internal::ValueArray32 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32) { + return internal::ValueArray32(v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32); +} + +template +internal::ValueArray33 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, + T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33) { + return internal::ValueArray33(v1, v2, v3, v4, v5, v6, v7, v8, + v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32, v33); +} + +template +internal::ValueArray34 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, + T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, + T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, + T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, + T31 v31, T32 v32, T33 v33, T34 v34) { + return internal::ValueArray34(v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, + v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34); +} + +template +internal::ValueArray35 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, + T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, + T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35) { + return internal::ValueArray35(v1, v2, v3, v4, v5, v6, + v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, + v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35); +} + +template +internal::ValueArray36 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, + T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, + T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36) { + return internal::ValueArray36(v1, v2, v3, v4, + v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, + v34, v35, v36); +} + +template +internal::ValueArray37 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, + T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, + T37 v37) { + return internal::ValueArray37(v1, v2, v3, + v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, + v34, v35, v36, v37); +} + +template +internal::ValueArray38 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, + T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, + T37 v37, T38 v38) { + return internal::ValueArray38(v1, v2, + v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, + v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, + v33, v34, v35, v36, v37, v38); +} + +template +internal::ValueArray39 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, + T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, + T37 v37, T38 v38, T39 v39) { + return internal::ValueArray39(v1, + v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, + v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, + v32, v33, v34, v35, v36, v37, v38, v39); +} + +template +internal::ValueArray40 Values(T1 v1, + T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, + T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, + T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, + T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, + T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) { + return internal::ValueArray40(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, + v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40); +} + +template +internal::ValueArray41 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41) { + return internal::ValueArray41(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, + v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, + v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41); +} + +template +internal::ValueArray42 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42) { + return internal::ValueArray42(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, + v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, + v42); +} + +template +internal::ValueArray43 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43) { + return internal::ValueArray43(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, + v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, + v41, v42, v43); +} + +template +internal::ValueArray44 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44) { + return internal::ValueArray44(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, + v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, + v40, v41, v42, v43, v44); +} + +template +internal::ValueArray45 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, + T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, + T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, + T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, + T41 v41, T42 v42, T43 v43, T44 v44, T45 v45) { + return internal::ValueArray45(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, + v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, + v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, + v39, v40, v41, v42, v43, v44, v45); +} + +template +internal::ValueArray46 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, + T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) { + return internal::ValueArray46(v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, + v38, v39, v40, v41, v42, v43, v44, v45, v46); +} + +template +internal::ValueArray47 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, + T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) { + return internal::ValueArray47(v1, v2, v3, v4, v5, v6, v7, v8, + v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, + v38, v39, v40, v41, v42, v43, v44, v45, v46, v47); +} + +template +internal::ValueArray48 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, + T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, + T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, + T48 v48) { + return internal::ValueArray48(v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, + v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, + v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48); +} + +template +internal::ValueArray49 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, + T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, + T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, + T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, + T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, + T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, + T47 v47, T48 v48, T49 v49) { + return internal::ValueArray49(v1, v2, v3, v4, v5, v6, + v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, + v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, + v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49); +} + +template +internal::ValueArray50 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, + T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, + T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, + T38 v38, T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, + T46 v46, T47 v47, T48 v48, T49 v49, T50 v50) { + return internal::ValueArray50(v1, v2, v3, v4, + v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, + v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, + v48, v49, v50); +} + +// Bool() allows generating tests with parameters in a set of (false, true). +// +// Synopsis: +// Bool() +// - returns a generator producing sequences with elements {false, true}. +// +// It is useful when testing code that depends on Boolean flags. Combinations +// of multiple flags can be tested when several Bool()'s are combined using +// Combine() function. +// +// In the following example all tests in the test case FlagDependentTest +// will be instantiated twice with parameters false and true. +// +// class FlagDependentTest : public testing::TestWithParam { +// virtual void SetUp() { +// external_flag = GetParam(); +// } +// } +// INSTANTIATE_TEST_CASE_P(BoolSequence, FlagDependentTest, Bool()); +// +inline internal::ParamGenerator Bool() { + return Values(false, true); +} + +# if GTEST_HAS_COMBINE +// Combine() allows the user to combine two or more sequences to produce +// values of a Cartesian product of those sequences' elements. +// +// Synopsis: +// Combine(gen1, gen2, ..., genN) +// - returns a generator producing sequences with elements coming from +// the Cartesian product of elements from the sequences generated by +// gen1, gen2, ..., genN. The sequence elements will have a type of +// tuple where T1, T2, ..., TN are the types +// of elements from sequences produces by gen1, gen2, ..., genN. +// +// Combine can have up to 10 arguments. This number is currently limited +// by the maximum number of elements in the tuple implementation used by Google +// Test. +// +// Example: +// +// This will instantiate tests in test case AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// class AnimalTest +// : public testing::TestWithParam > {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_CASE_P(AnimalVariations, AnimalTest, +// Combine(Values("cat", "dog"), +// Values(BLACK, WHITE))); +// +// This will instantiate tests in FlagDependentTest with all variations of two +// Boolean flags: +// +// class FlagDependentTest +// : public testing::TestWithParam > { +// virtual void SetUp() { +// // Assigns external_flag_1 and external_flag_2 values from the tuple. +// tie(external_flag_1, external_flag_2) = GetParam(); +// } +// }; +// +// TEST_P(FlagDependentTest, TestFeature1) { +// // Test your code using external_flag_1 and external_flag_2 here. +// } +// INSTANTIATE_TEST_CASE_P(TwoBoolSequence, FlagDependentTest, +// Combine(Bool(), Bool())); +// +template +internal::CartesianProductHolder2 Combine( + const Generator1& g1, const Generator2& g2) { + return internal::CartesianProductHolder2( + g1, g2); +} + +template +internal::CartesianProductHolder3 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3) { + return internal::CartesianProductHolder3( + g1, g2, g3); +} + +template +internal::CartesianProductHolder4 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4) { + return internal::CartesianProductHolder4( + g1, g2, g3, g4); +} + +template +internal::CartesianProductHolder5 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5) { + return internal::CartesianProductHolder5( + g1, g2, g3, g4, g5); +} + +template +internal::CartesianProductHolder6 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6) { + return internal::CartesianProductHolder6( + g1, g2, g3, g4, g5, g6); +} + +template +internal::CartesianProductHolder7 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7) { + return internal::CartesianProductHolder7( + g1, g2, g3, g4, g5, g6, g7); +} + +template +internal::CartesianProductHolder8 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7, const Generator8& g8) { + return internal::CartesianProductHolder8( + g1, g2, g3, g4, g5, g6, g7, g8); +} + +template +internal::CartesianProductHolder9 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7, const Generator8& g8, const Generator9& g9) { + return internal::CartesianProductHolder9( + g1, g2, g3, g4, g5, g6, g7, g8, g9); +} + +template +internal::CartesianProductHolder10 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7, const Generator8& g8, const Generator9& g9, + const Generator10& g10) { + return internal::CartesianProductHolder10( + g1, g2, g3, g4, g5, g6, g7, g8, g9, g10); +} +# endif // GTEST_HAS_COMBINE + + + +# define TEST_P(test_case_name, test_name) \ + class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ + : public test_case_name { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {} \ + virtual void TestBody(); \ + private: \ + static int AddToRegistry() { \ + ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ + GetTestCasePatternHolder(\ + #test_case_name, __FILE__, __LINE__)->AddTestPattern(\ + #test_case_name, \ + #test_name, \ + new ::testing::internal::TestMetaFactory< \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>()); \ + return 0; \ + } \ + static int gtest_registering_dummy_; \ + GTEST_DISALLOW_COPY_AND_ASSIGN_(\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \ + }; \ + int GTEST_TEST_CLASS_NAME_(test_case_name, \ + test_name)::gtest_registering_dummy_ = \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \ + void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() + +# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator) \ + ::testing::internal::ParamGenerator \ + gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \ + int gtest_##prefix##test_case_name##_dummy_ = \ + ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ + GetTestCasePatternHolder(\ + #test_case_name, __FILE__, __LINE__)->AddTestCaseInstantiation(\ + #prefix, \ + >est_##prefix##test_case_name##_EvalGenerator_, \ + __FILE__, __LINE__) + +} // namespace testing + +#endif // GTEST_HAS_PARAM_TEST + +#endif // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-param-test.h.pump b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-param-test.h.pump new file mode 100644 index 00000000..2dc9303b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-param-test.h.pump @@ -0,0 +1,487 @@ +$$ -*- mode: c++; -*- +$var n = 50 $$ Maximum length of Values arguments we want to support. +$var maxtuple = 10 $$ Maximum number of Combine arguments we want to support. +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: vladl@google.com (Vlad Losev) +// +// Macros and functions for implementing parameterized tests +// in Google C++ Testing Framework (Google Test) +// +// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +#ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ + + +// Value-parameterized tests allow you to test your code with different +// parameters without writing multiple copies of the same test. +// +// Here is how you use value-parameterized tests: + +#if 0 + +// To write value-parameterized tests, first you should define a fixture +// class. It is usually derived from testing::TestWithParam (see below for +// another inheritance scheme that's sometimes useful in more complicated +// class hierarchies), where the type of your parameter values. +// TestWithParam is itself derived from testing::Test. T can be any +// copyable type. If it's a raw pointer, you are responsible for managing the +// lifespan of the pointed values. + +class FooTest : public ::testing::TestWithParam { + // You can implement all the usual class fixture members here. +}; + +// Then, use the TEST_P macro to define as many parameterized tests +// for this fixture as you want. The _P suffix is for "parameterized" +// or "pattern", whichever you prefer to think. + +TEST_P(FooTest, DoesBlah) { + // Inside a test, access the test parameter with the GetParam() method + // of the TestWithParam class: + EXPECT_TRUE(foo.Blah(GetParam())); + ... +} + +TEST_P(FooTest, HasBlahBlah) { + ... +} + +// Finally, you can use INSTANTIATE_TEST_CASE_P to instantiate the test +// case with any set of parameters you want. Google Test defines a number +// of functions for generating test parameters. They return what we call +// (surprise!) parameter generators. Here is a summary of them, which +// are all in the testing namespace: +// +// +// Range(begin, end [, step]) - Yields values {begin, begin+step, +// begin+step+step, ...}. The values do not +// include end. step defaults to 1. +// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}. +// ValuesIn(container) - Yields values from a C-style array, an STL +// ValuesIn(begin,end) container, or an iterator range [begin, end). +// Bool() - Yields sequence {false, true}. +// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product +// for the math savvy) of the values generated +// by the N generators. +// +// For more details, see comments at the definitions of these functions below +// in this file. +// +// The following statement will instantiate tests from the FooTest test case +// each with parameter values "meeny", "miny", and "moe". + +INSTANTIATE_TEST_CASE_P(InstantiationName, + FooTest, + Values("meeny", "miny", "moe")); + +// To distinguish different instances of the pattern, (yes, you +// can instantiate it more then once) the first argument to the +// INSTANTIATE_TEST_CASE_P macro is a prefix that will be added to the +// actual test case name. Remember to pick unique prefixes for different +// instantiations. The tests from the instantiation above will have +// these names: +// +// * InstantiationName/FooTest.DoesBlah/0 for "meeny" +// * InstantiationName/FooTest.DoesBlah/1 for "miny" +// * InstantiationName/FooTest.DoesBlah/2 for "moe" +// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny" +// * InstantiationName/FooTest.HasBlahBlah/1 for "miny" +// * InstantiationName/FooTest.HasBlahBlah/2 for "moe" +// +// You can use these names in --gtest_filter. +// +// This statement will instantiate all tests from FooTest again, each +// with parameter values "cat" and "dog": + +const char* pets[] = {"cat", "dog"}; +INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets)); + +// The tests from the instantiation above will have these names: +// +// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog" +// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog" +// +// Please note that INSTANTIATE_TEST_CASE_P will instantiate all tests +// in the given test case, whether their definitions come before or +// AFTER the INSTANTIATE_TEST_CASE_P statement. +// +// Please also note that generator expressions (including parameters to the +// generators) are evaluated in InitGoogleTest(), after main() has started. +// This allows the user on one hand, to adjust generator parameters in order +// to dynamically determine a set of tests to run and on the other hand, +// give the user a chance to inspect the generated tests with Google Test +// reflection API before RUN_ALL_TESTS() is executed. +// +// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc +// for more examples. +// +// In the future, we plan to publish the API for defining new parameter +// generators. But for now this interface remains part of the internal +// implementation and is subject to change. +// +// +// A parameterized test fixture must be derived from testing::Test and from +// testing::WithParamInterface, where T is the type of the parameter +// values. Inheriting from TestWithParam satisfies that requirement because +// TestWithParam inherits from both Test and WithParamInterface. In more +// complicated hierarchies, however, it is occasionally useful to inherit +// separately from Test and WithParamInterface. For example: + +class BaseTest : public ::testing::Test { + // You can inherit all the usual members for a non-parameterized test + // fixture here. +}; + +class DerivedTest : public BaseTest, public ::testing::WithParamInterface { + // The usual test fixture members go here too. +}; + +TEST_F(BaseTest, HasFoo) { + // This is an ordinary non-parameterized test. +} + +TEST_P(DerivedTest, DoesBlah) { + // GetParam works just the same here as if you inherit from TestWithParam. + EXPECT_TRUE(foo.Blah(GetParam())); +} + +#endif // 0 + +#include "gtest/internal/gtest-port.h" + +#if !GTEST_OS_SYMBIAN +# include +#endif + +// scripts/fuse_gtest.py depends on gtest's own header being #included +// *unconditionally*. Therefore these #includes cannot be moved +// inside #if GTEST_HAS_PARAM_TEST. +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-param-util.h" +#include "gtest/internal/gtest-param-util-generated.h" + +#if GTEST_HAS_PARAM_TEST + +namespace testing { + +// Functions producing parameter generators. +// +// Google Test uses these generators to produce parameters for value- +// parameterized tests. When a parameterized test case is instantiated +// with a particular generator, Google Test creates and runs tests +// for each element in the sequence produced by the generator. +// +// In the following sample, tests from test case FooTest are instantiated +// each three times with parameter values 3, 5, and 8: +// +// class FooTest : public TestWithParam { ... }; +// +// TEST_P(FooTest, TestThis) { +// } +// TEST_P(FooTest, TestThat) { +// } +// INSTANTIATE_TEST_CASE_P(TestSequence, FooTest, Values(3, 5, 8)); +// + +// Range() returns generators providing sequences of values in a range. +// +// Synopsis: +// Range(start, end) +// - returns a generator producing a sequence of values {start, start+1, +// start+2, ..., }. +// Range(start, end, step) +// - returns a generator producing a sequence of values {start, start+step, +// start+step+step, ..., }. +// Notes: +// * The generated sequences never include end. For example, Range(1, 5) +// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2) +// returns a generator producing {1, 3, 5, 7}. +// * start and end must have the same type. That type may be any integral or +// floating-point type or a user defined type satisfying these conditions: +// * It must be assignable (have operator=() defined). +// * It must have operator+() (operator+(int-compatible type) for +// two-operand version). +// * It must have operator<() defined. +// Elements in the resulting sequences will also have that type. +// * Condition start < end must be satisfied in order for resulting sequences +// to contain any elements. +// +template +internal::ParamGenerator Range(T start, T end, IncrementT step) { + return internal::ParamGenerator( + new internal::RangeGenerator(start, end, step)); +} + +template +internal::ParamGenerator Range(T start, T end) { + return Range(start, end, 1); +} + +// ValuesIn() function allows generation of tests with parameters coming from +// a container. +// +// Synopsis: +// ValuesIn(const T (&array)[N]) +// - returns a generator producing sequences with elements from +// a C-style array. +// ValuesIn(const Container& container) +// - returns a generator producing sequences with elements from +// an STL-style container. +// ValuesIn(Iterator begin, Iterator end) +// - returns a generator producing sequences with elements from +// a range [begin, end) defined by a pair of STL-style iterators. These +// iterators can also be plain C pointers. +// +// Please note that ValuesIn copies the values from the containers +// passed in and keeps them to generate tests in RUN_ALL_TESTS(). +// +// Examples: +// +// This instantiates tests from test case StringTest +// each with C-string values of "foo", "bar", and "baz": +// +// const char* strings[] = {"foo", "bar", "baz"}; +// INSTANTIATE_TEST_CASE_P(StringSequence, SrtingTest, ValuesIn(strings)); +// +// This instantiates tests from test case StlStringTest +// each with STL strings with values "a" and "b": +// +// ::std::vector< ::std::string> GetParameterStrings() { +// ::std::vector< ::std::string> v; +// v.push_back("a"); +// v.push_back("b"); +// return v; +// } +// +// INSTANTIATE_TEST_CASE_P(CharSequence, +// StlStringTest, +// ValuesIn(GetParameterStrings())); +// +// +// This will also instantiate tests from CharTest +// each with parameter values 'a' and 'b': +// +// ::std::list GetParameterChars() { +// ::std::list list; +// list.push_back('a'); +// list.push_back('b'); +// return list; +// } +// ::std::list l = GetParameterChars(); +// INSTANTIATE_TEST_CASE_P(CharSequence2, +// CharTest, +// ValuesIn(l.begin(), l.end())); +// +template +internal::ParamGenerator< + typename ::testing::internal::IteratorTraits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end) { + typedef typename ::testing::internal::IteratorTraits + ::value_type ParamType; + return internal::ParamGenerator( + new internal::ValuesInIteratorRangeGenerator(begin, end)); +} + +template +internal::ParamGenerator ValuesIn(const T (&array)[N]) { + return ValuesIn(array, array + N); +} + +template +internal::ParamGenerator ValuesIn( + const Container& container) { + return ValuesIn(container.begin(), container.end()); +} + +// Values() allows generating tests from explicitly specified list of +// parameters. +// +// Synopsis: +// Values(T v1, T v2, ..., T vN) +// - returns a generator producing sequences with elements v1, v2, ..., vN. +// +// For example, this instantiates tests from test case BarTest each +// with values "one", "two", and "three": +// +// INSTANTIATE_TEST_CASE_P(NumSequence, BarTest, Values("one", "two", "three")); +// +// This instantiates tests from test case BazTest each with values 1, 2, 3.5. +// The exact type of values will depend on the type of parameter in BazTest. +// +// INSTANTIATE_TEST_CASE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5)); +// +// Currently, Values() supports from 1 to $n parameters. +// +$range i 1..n +$for i [[ +$range j 1..i + +template <$for j, [[typename T$j]]> +internal::ValueArray$i<$for j, [[T$j]]> Values($for j, [[T$j v$j]]) { + return internal::ValueArray$i<$for j, [[T$j]]>($for j, [[v$j]]); +} + +]] + +// Bool() allows generating tests with parameters in a set of (false, true). +// +// Synopsis: +// Bool() +// - returns a generator producing sequences with elements {false, true}. +// +// It is useful when testing code that depends on Boolean flags. Combinations +// of multiple flags can be tested when several Bool()'s are combined using +// Combine() function. +// +// In the following example all tests in the test case FlagDependentTest +// will be instantiated twice with parameters false and true. +// +// class FlagDependentTest : public testing::TestWithParam { +// virtual void SetUp() { +// external_flag = GetParam(); +// } +// } +// INSTANTIATE_TEST_CASE_P(BoolSequence, FlagDependentTest, Bool()); +// +inline internal::ParamGenerator Bool() { + return Values(false, true); +} + +# if GTEST_HAS_COMBINE +// Combine() allows the user to combine two or more sequences to produce +// values of a Cartesian product of those sequences' elements. +// +// Synopsis: +// Combine(gen1, gen2, ..., genN) +// - returns a generator producing sequences with elements coming from +// the Cartesian product of elements from the sequences generated by +// gen1, gen2, ..., genN. The sequence elements will have a type of +// tuple where T1, T2, ..., TN are the types +// of elements from sequences produces by gen1, gen2, ..., genN. +// +// Combine can have up to $maxtuple arguments. This number is currently limited +// by the maximum number of elements in the tuple implementation used by Google +// Test. +// +// Example: +// +// This will instantiate tests in test case AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// class AnimalTest +// : public testing::TestWithParam > {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_CASE_P(AnimalVariations, AnimalTest, +// Combine(Values("cat", "dog"), +// Values(BLACK, WHITE))); +// +// This will instantiate tests in FlagDependentTest with all variations of two +// Boolean flags: +// +// class FlagDependentTest +// : public testing::TestWithParam > { +// virtual void SetUp() { +// // Assigns external_flag_1 and external_flag_2 values from the tuple. +// tie(external_flag_1, external_flag_2) = GetParam(); +// } +// }; +// +// TEST_P(FlagDependentTest, TestFeature1) { +// // Test your code using external_flag_1 and external_flag_2 here. +// } +// INSTANTIATE_TEST_CASE_P(TwoBoolSequence, FlagDependentTest, +// Combine(Bool(), Bool())); +// +$range i 2..maxtuple +$for i [[ +$range j 1..i + +template <$for j, [[typename Generator$j]]> +internal::CartesianProductHolder$i<$for j, [[Generator$j]]> Combine( + $for j, [[const Generator$j& g$j]]) { + return internal::CartesianProductHolder$i<$for j, [[Generator$j]]>( + $for j, [[g$j]]); +} + +]] +# endif // GTEST_HAS_COMBINE + + + +# define TEST_P(test_case_name, test_name) \ + class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ + : public test_case_name { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {} \ + virtual void TestBody(); \ + private: \ + static int AddToRegistry() { \ + ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ + GetTestCasePatternHolder(\ + #test_case_name, __FILE__, __LINE__)->AddTestPattern(\ + #test_case_name, \ + #test_name, \ + new ::testing::internal::TestMetaFactory< \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>()); \ + return 0; \ + } \ + static int gtest_registering_dummy_; \ + GTEST_DISALLOW_COPY_AND_ASSIGN_(\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \ + }; \ + int GTEST_TEST_CLASS_NAME_(test_case_name, \ + test_name)::gtest_registering_dummy_ = \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \ + void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() + +# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator) \ + ::testing::internal::ParamGenerator \ + gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \ + int gtest_##prefix##test_case_name##_dummy_ = \ + ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ + GetTestCasePatternHolder(\ + #test_case_name, __FILE__, __LINE__)->AddTestCaseInstantiation(\ + #prefix, \ + >est_##prefix##test_case_name##_EvalGenerator_, \ + __FILE__, __LINE__) + +} // namespace testing + +#endif // GTEST_HAS_PARAM_TEST + +#endif // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-printers.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-printers.h new file mode 100644 index 00000000..0639d9f5 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-printers.h @@ -0,0 +1,855 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Test - The Google C++ Testing Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter::Print(value, ostream_ptr); +// +// A user can teach this function how to print a class type T by +// defining either operator<<() or PrintTo() in the namespace that +// defines T. More specifically, the FIRST defined function in the +// following list will be used (assuming T is defined in namespace +// foo): +// +// 1. foo::PrintTo(const T&, ostream*) +// 2. operator<<(ostream&, const T&) defined in either foo or the +// global namespace. +// +// If none of the above is defined, it will print the debug string of +// the value if it is a protocol buffer, or print the raw bytes in the +// value otherwise. +// +// To aid debugging: when T is a reference type, the address of the +// value is also printed; when T is a (const) char pointer, both the +// pointer value and the NUL-terminated string it points to are +// printed. +// +// We also provide some convenient wrappers: +// +// // Prints a value to a string. For a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// std::string ::testing::PrintToString(const T& value); +// +// // Prints a value tersely: for a reference type, the referenced +// // value (but not the address) is printed; for a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// void ::testing::internal::UniversalTersePrint(const T& value, ostream*); +// +// // Prints value using the type inferred by the compiler. The difference +// // from UniversalTersePrint() is that this function prints both the +// // pointer and the NUL-terminated string for a (const or not) char pointer. +// void ::testing::internal::UniversalPrint(const T& value, ostream*); +// +// // Prints the fields of a tuple tersely to a string vector, one +// // element for each field. Tuple support must be enabled in +// // gtest-port.h. +// std::vector UniversalTersePrintTupleFieldsToStrings( +// const Tuple& value); +// +// Known limitation: +// +// The print primitives print the elements of an STL-style container +// using the compiler-inferred type of *iter where iter is a +// const_iterator of the container. When const_iterator is an input +// iterator but not a forward iterator, this inferred type may not +// match value_type, and the print output may be incorrect. In +// practice, this is rarely a problem as for most containers +// const_iterator is a forward iterator. We'll fix this if there's an +// actual need for it. Note that this fix cannot rely on value_type +// being defined as many user-defined container types don't have +// value_type. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +#include // NOLINT +#include +#include +#include +#include +#include "gtest/internal/gtest-port.h" +#include "gtest/internal/gtest-internal.h" + +namespace testing { + +// Definitions in the 'internal' and 'internal2' name spaces are +// subject to change without notice. DO NOT USE THEM IN USER CODE! +namespace internal2 { + +// Prints the given number of bytes in the given object to the given +// ostream. +GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes, + size_t count, + ::std::ostream* os); + +// For selecting which printer to use when a given type has neither << +// nor PrintTo(). +enum TypeKind { + kProtobuf, // a protobuf type + kConvertibleToInteger, // a type implicitly convertible to BiggestInt + // (e.g. a named or unnamed enum type) + kOtherType // anything else +}; + +// TypeWithoutFormatter::PrintValue(value, os) is called +// by the universal printer to print a value of type T when neither +// operator<< nor PrintTo() is defined for T, where kTypeKind is the +// "kind" of T as defined by enum TypeKind. +template +class TypeWithoutFormatter { + public: + // This default version is called when kTypeKind is kOtherType. + static void PrintValue(const T& value, ::std::ostream* os) { + PrintBytesInObjectTo(reinterpret_cast(&value), + sizeof(value), os); + } +}; + +// We print a protobuf using its ShortDebugString() when the string +// doesn't exceed this many characters; otherwise we print it using +// DebugString() for better readability. +const size_t kProtobufOneLinerMaxLength = 50; + +template +class TypeWithoutFormatter { + public: + static void PrintValue(const T& value, ::std::ostream* os) { + const ::testing::internal::string short_str = value.ShortDebugString(); + const ::testing::internal::string pretty_str = + short_str.length() <= kProtobufOneLinerMaxLength ? + short_str : ("\n" + value.DebugString()); + *os << ("<" + pretty_str + ">"); + } +}; + +template +class TypeWithoutFormatter { + public: + // Since T has no << operator or PrintTo() but can be implicitly + // converted to BiggestInt, we print it as a BiggestInt. + // + // Most likely T is an enum type (either named or unnamed), in which + // case printing it as an integer is the desired behavior. In case + // T is not an enum, printing it as an integer is the best we can do + // given that it has no user-defined printer. + static void PrintValue(const T& value, ::std::ostream* os) { + const internal::BiggestInt kBigInt = value; + *os << kBigInt; + } +}; + +// Prints the given value to the given ostream. If the value is a +// protocol message, its debug string is printed; if it's an enum or +// of a type implicitly convertible to BiggestInt, it's printed as an +// integer; otherwise the bytes in the value are printed. This is +// what UniversalPrinter::Print() does when it knows nothing about +// type T and T has neither << operator nor PrintTo(). +// +// A user can override this behavior for a class type Foo by defining +// a << operator in the namespace where Foo is defined. +// +// We put this operator in namespace 'internal2' instead of 'internal' +// to simplify the implementation, as much code in 'internal' needs to +// use << in STL, which would conflict with our own << were it defined +// in 'internal'. +// +// Note that this operator<< takes a generic std::basic_ostream type instead of the more restricted std::ostream. If +// we define it to take an std::ostream instead, we'll get an +// "ambiguous overloads" compiler error when trying to print a type +// Foo that supports streaming to std::basic_ostream, as the compiler cannot tell whether +// operator<<(std::ostream&, const T&) or +// operator<<(std::basic_stream, const Foo&) is more +// specific. +template +::std::basic_ostream& operator<<( + ::std::basic_ostream& os, const T& x) { + TypeWithoutFormatter::value ? kProtobuf : + internal::ImplicitlyConvertible::value ? + kConvertibleToInteger : kOtherType)>::PrintValue(x, &os); + return os; +} + +} // namespace internal2 +} // namespace testing + +// This namespace MUST NOT BE NESTED IN ::testing, or the name look-up +// magic needed for implementing UniversalPrinter won't work. +namespace testing_internal { + +// Used to print a value that is not an STL-style container when the +// user doesn't define PrintTo() for it. +template +void DefaultPrintNonContainerTo(const T& value, ::std::ostream* os) { + // With the following statement, during unqualified name lookup, + // testing::internal2::operator<< appears as if it was declared in + // the nearest enclosing namespace that contains both + // ::testing_internal and ::testing::internal2, i.e. the global + // namespace. For more details, refer to the C++ Standard section + // 7.3.4-1 [namespace.udir]. This allows us to fall back onto + // testing::internal2::operator<< in case T doesn't come with a << + // operator. + // + // We cannot write 'using ::testing::internal2::operator<<;', which + // gcc 3.3 fails to compile due to a compiler bug. + using namespace ::testing::internal2; // NOLINT + + // Assuming T is defined in namespace foo, in the next statement, + // the compiler will consider all of: + // + // 1. foo::operator<< (thanks to Koenig look-up), + // 2. ::operator<< (as the current namespace is enclosed in ::), + // 3. testing::internal2::operator<< (thanks to the using statement above). + // + // The operator<< whose type matches T best will be picked. + // + // We deliberately allow #2 to be a candidate, as sometimes it's + // impossible to define #1 (e.g. when foo is ::std, defining + // anything in it is undefined behavior unless you are a compiler + // vendor.). + *os << value; +} + +} // namespace testing_internal + +namespace testing { +namespace internal { + +// UniversalPrinter::Print(value, ostream_ptr) prints the given +// value to the given ostream. The caller must ensure that +// 'ostream_ptr' is not NULL, or the behavior is undefined. +// +// We define UniversalPrinter as a class template (as opposed to a +// function template), as we need to partially specialize it for +// reference types, which cannot be done with function templates. +template +class UniversalPrinter; + +template +void UniversalPrint(const T& value, ::std::ostream* os); + +// Used to print an STL-style container when the user doesn't define +// a PrintTo() for it. +template +void DefaultPrintTo(IsContainer /* dummy */, + false_type /* is not a pointer */, + const C& container, ::std::ostream* os) { + const size_t kMaxCount = 32; // The maximum number of elements to print. + *os << '{'; + size_t count = 0; + for (typename C::const_iterator it = container.begin(); + it != container.end(); ++it, ++count) { + if (count > 0) { + *os << ','; + if (count == kMaxCount) { // Enough has been printed. + *os << " ..."; + break; + } + } + *os << ' '; + // We cannot call PrintTo(*it, os) here as PrintTo() doesn't + // handle *it being a native array. + internal::UniversalPrint(*it, os); + } + + if (count > 0) { + *os << ' '; + } + *os << '}'; +} + +// Used to print a pointer that is neither a char pointer nor a member +// pointer, when the user doesn't define PrintTo() for it. (A member +// variable pointer or member function pointer doesn't really point to +// a location in the address space. Their representation is +// implementation-defined. Therefore they will be printed as raw +// bytes.) +template +void DefaultPrintTo(IsNotContainer /* dummy */, + true_type /* is a pointer */, + T* p, ::std::ostream* os) { + if (p == NULL) { + *os << "NULL"; + } else { + // C++ doesn't allow casting from a function pointer to any object + // pointer. + // + // IsTrue() silences warnings: "Condition is always true", + // "unreachable code". + if (IsTrue(ImplicitlyConvertible::value)) { + // T is not a function type. We just call << to print p, + // relying on ADL to pick up user-defined << for their pointer + // types, if any. + *os << p; + } else { + // T is a function type, so '*os << p' doesn't do what we want + // (it just prints p as bool). We want to print p as a const + // void*. However, we cannot cast it to const void* directly, + // even using reinterpret_cast, as earlier versions of gcc + // (e.g. 3.4.5) cannot compile the cast when p is a function + // pointer. Casting to UInt64 first solves the problem. + *os << reinterpret_cast( + reinterpret_cast(p)); + } + } +} + +// Used to print a non-container, non-pointer value when the user +// doesn't define PrintTo() for it. +template +void DefaultPrintTo(IsNotContainer /* dummy */, + false_type /* is not a pointer */, + const T& value, ::std::ostream* os) { + ::testing_internal::DefaultPrintNonContainerTo(value, os); +} + +// Prints the given value using the << operator if it has one; +// otherwise prints the bytes in it. This is what +// UniversalPrinter::Print() does when PrintTo() is not specialized +// or overloaded for type T. +// +// A user can override this behavior for a class type Foo by defining +// an overload of PrintTo() in the namespace where Foo is defined. We +// give the user this option as sometimes defining a << operator for +// Foo is not desirable (e.g. the coding style may prevent doing it, +// or there is already a << operator but it doesn't do what the user +// wants). +template +void PrintTo(const T& value, ::std::ostream* os) { + // DefaultPrintTo() is overloaded. The type of its first two + // arguments determine which version will be picked. If T is an + // STL-style container, the version for container will be called; if + // T is a pointer, the pointer version will be called; otherwise the + // generic version will be called. + // + // Note that we check for container types here, prior to we check + // for protocol message types in our operator<<. The rationale is: + // + // For protocol messages, we want to give people a chance to + // override Google Mock's format by defining a PrintTo() or + // operator<<. For STL containers, other formats can be + // incompatible with Google Mock's format for the container + // elements; therefore we check for container types here to ensure + // that our format is used. + // + // The second argument of DefaultPrintTo() is needed to bypass a bug + // in Symbian's C++ compiler that prevents it from picking the right + // overload between: + // + // PrintTo(const T& x, ...); + // PrintTo(T* x, ...); + DefaultPrintTo(IsContainerTest(0), is_pointer(), value, os); +} + +// The following list of PrintTo() overloads tells +// UniversalPrinter::Print() how to print standard types (built-in +// types, strings, plain arrays, and pointers). + +// Overloads for various char types. +GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os); +GTEST_API_ void PrintTo(signed char c, ::std::ostream* os); +inline void PrintTo(char c, ::std::ostream* os) { + // When printing a plain char, we always treat it as unsigned. This + // way, the output won't be affected by whether the compiler thinks + // char is signed or not. + PrintTo(static_cast(c), os); +} + +// Overloads for other simple built-in types. +inline void PrintTo(bool x, ::std::ostream* os) { + *os << (x ? "true" : "false"); +} + +// Overload for wchar_t type. +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its decimal code (except for L'\0'). +// The L'\0' char is printed as "L'\\0'". The decimal code is printed +// as signed integer when wchar_t is implemented by the compiler +// as a signed type and is printed as an unsigned integer when wchar_t +// is implemented as an unsigned type. +GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os); + +// Overloads for C strings. +GTEST_API_ void PrintTo(const char* s, ::std::ostream* os); +inline void PrintTo(char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} + +// signed/unsigned char is often used for representing binary data, so +// we print pointers to it as void* to be safe. +inline void PrintTo(const signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(const unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} + +// MSVC can be configured to define wchar_t as a typedef of unsigned +// short. It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native +// type. When wchar_t is a typedef, defining an overload for const +// wchar_t* would cause unsigned short* be printed as a wide string, +// possibly causing invalid memory accesses. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Overloads for wide C strings +GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os); +inline void PrintTo(wchar_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#endif + +// Overload for C arrays. Multi-dimensional arrays are printed +// properly. + +// Prints the given number of elements in an array, without printing +// the curly braces. +template +void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) { + UniversalPrint(a[0], os); + for (size_t i = 1; i != count; i++) { + *os << ", "; + UniversalPrint(a[i], os); + } +} + +// Overloads for ::string and ::std::string. +#if GTEST_HAS_GLOBAL_STRING +GTEST_API_ void PrintStringTo(const ::string&s, ::std::ostream* os); +inline void PrintTo(const ::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} +#endif // GTEST_HAS_GLOBAL_STRING + +GTEST_API_ void PrintStringTo(const ::std::string&s, ::std::ostream* os); +inline void PrintTo(const ::std::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} + +// Overloads for ::wstring and ::std::wstring. +#if GTEST_HAS_GLOBAL_WSTRING +GTEST_API_ void PrintWideStringTo(const ::wstring&s, ::std::ostream* os); +inline void PrintTo(const ::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +#if GTEST_HAS_STD_WSTRING +GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os); +inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_TR1_TUPLE +// Overload for ::std::tr1::tuple. Needed for printing function arguments, +// which are packed as tuples. + +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template +void PrintTupleTo(const T& t, ::std::ostream* os); + +// Overloaded PrintTo() for tuples of various arities. We support +// tuples of up-to 10 fields. The following implementation works +// regardless of whether tr1::tuple is implemented using the +// non-standard variadic template feature or not. + +inline void PrintTo(const ::std::tr1::tuple<>& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo( + const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} +#endif // GTEST_HAS_TR1_TUPLE + +// Overload for std::pair. +template +void PrintTo(const ::std::pair& value, ::std::ostream* os) { + *os << '('; + // We cannot use UniversalPrint(value.first, os) here, as T1 may be + // a reference type. The same for printing value.second. + UniversalPrinter::Print(value.first, os); + *os << ", "; + UniversalPrinter::Print(value.second, os); + *os << ')'; +} + +// Implements printing a non-reference type T by letting the compiler +// pick the right overload of PrintTo() for T. +template +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4180) // Temporarily disables warning 4180. +#endif // _MSC_VER + + // Note: we deliberately don't call this PrintTo(), as that name + // conflicts with ::testing::internal::PrintTo in the body of the + // function. + static void Print(const T& value, ::std::ostream* os) { + // By default, ::testing::internal::PrintTo() is used for printing + // the value. + // + // Thanks to Koenig look-up, if T is a class and has its own + // PrintTo() function defined in its namespace, that function will + // be visible here. Since it is more specific than the generic ones + // in ::testing::internal, it will be picked by the compiler in the + // following statement - exactly what we want. + PrintTo(value, os); + } + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif // _MSC_VER +}; + +// UniversalPrintArray(begin, len, os) prints an array of 'len' +// elements, starting at address 'begin'. +template +void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) { + if (len == 0) { + *os << "{}"; + } else { + *os << "{ "; + const size_t kThreshold = 18; + const size_t kChunkSize = 8; + // If the array has more than kThreshold elements, we'll have to + // omit some details by printing only the first and the last + // kChunkSize elements. + // TODO(wan@google.com): let the user control the threshold using a flag. + if (len <= kThreshold) { + PrintRawArrayTo(begin, len, os); + } else { + PrintRawArrayTo(begin, kChunkSize, os); + *os << ", ..., "; + PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os); + } + *os << " }"; + } +} +// This overload prints a (const) char array compactly. +GTEST_API_ void UniversalPrintArray( + const char* begin, size_t len, ::std::ostream* os); + +// This overload prints a (const) wchar_t array compactly. +GTEST_API_ void UniversalPrintArray( + const wchar_t* begin, size_t len, ::std::ostream* os); + +// Implements printing an array type T[N]. +template +class UniversalPrinter { + public: + // Prints the given array, omitting some elements when there are too + // many. + static void Print(const T (&a)[N], ::std::ostream* os) { + UniversalPrintArray(a, N, os); + } +}; + +// Implements printing a reference type T&. +template +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4180) // Temporarily disables warning 4180. +#endif // _MSC_VER + + static void Print(const T& value, ::std::ostream* os) { + // Prints the address of the value. We use reinterpret_cast here + // as static_cast doesn't compile when T is a function type. + *os << "@" << reinterpret_cast(&value) << " "; + + // Then prints the value itself. + UniversalPrint(value, os); + } + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif // _MSC_VER +}; + +// Prints a value tersely: for a reference type, the referenced value +// (but not the address) is printed; for a (const) char pointer, the +// NUL-terminated string (but not the pointer) is printed. + +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T (&value)[N], ::std::ostream* os) { + UniversalPrinter::Print(value, os); + } +}; +template <> +class UniversalTersePrinter { + public: + static void Print(const char* str, ::std::ostream* os) { + if (str == NULL) { + *os << "NULL"; + } else { + UniversalPrint(string(str), os); + } + } +}; +template <> +class UniversalTersePrinter { + public: + static void Print(char* str, ::std::ostream* os) { + UniversalTersePrinter::Print(str, os); + } +}; + +#if GTEST_HAS_STD_WSTRING +template <> +class UniversalTersePrinter { + public: + static void Print(const wchar_t* str, ::std::ostream* os) { + if (str == NULL) { + *os << "NULL"; + } else { + UniversalPrint(::std::wstring(str), os); + } + } +}; +#endif + +template <> +class UniversalTersePrinter { + public: + static void Print(wchar_t* str, ::std::ostream* os) { + UniversalTersePrinter::Print(str, os); + } +}; + +template +void UniversalTersePrint(const T& value, ::std::ostream* os) { + UniversalTersePrinter::Print(value, os); +} + +// Prints a value using the type inferred by the compiler. The +// difference between this and UniversalTersePrint() is that for a +// (const) char pointer, this prints both the pointer and the +// NUL-terminated string. +template +void UniversalPrint(const T& value, ::std::ostream* os) { + // A workarond for the bug in VC++ 7.1 that prevents us from instantiating + // UniversalPrinter with T directly. + typedef T T1; + UniversalPrinter::Print(value, os); +} + +#if GTEST_HAS_TR1_TUPLE +typedef ::std::vector Strings; + +// This helper template allows PrintTo() for tuples and +// UniversalTersePrintTupleFieldsToStrings() to be defined by +// induction on the number of tuple fields. The idea is that +// TuplePrefixPrinter::PrintPrefixTo(t, os) prints the first N +// fields in tuple t, and can be defined in terms of +// TuplePrefixPrinter. + +// The inductive case. +template +struct TuplePrefixPrinter { + // Prints the first N fields of a tuple. + template + static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) { + TuplePrefixPrinter::PrintPrefixTo(t, os); + *os << ", "; + UniversalPrinter::type> + ::Print(::std::tr1::get(t), os); + } + + // Tersely prints the first N fields of a tuple to a string vector, + // one element for each field. + template + static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { + TuplePrefixPrinter::TersePrintPrefixToStrings(t, strings); + ::std::stringstream ss; + UniversalTersePrint(::std::tr1::get(t), &ss); + strings->push_back(ss.str()); + } +}; + +// Base cases. +template <> +struct TuplePrefixPrinter<0> { + template + static void PrintPrefixTo(const Tuple&, ::std::ostream*) {} + + template + static void TersePrintPrefixToStrings(const Tuple&, Strings*) {} +}; +// We have to specialize the entire TuplePrefixPrinter<> class +// template here, even though the definition of +// TersePrintPrefixToStrings() is the same as the generic version, as +// Embarcadero (formerly CodeGear, formerly Borland) C++ doesn't +// support specializing a method template of a class template. +template <> +struct TuplePrefixPrinter<1> { + template + static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) { + UniversalPrinter::type>:: + Print(::std::tr1::get<0>(t), os); + } + + template + static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { + ::std::stringstream ss; + UniversalTersePrint(::std::tr1::get<0>(t), &ss); + strings->push_back(ss.str()); + } +}; + +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template +void PrintTupleTo(const T& t, ::std::ostream* os) { + *os << "("; + TuplePrefixPrinter< ::std::tr1::tuple_size::value>:: + PrintPrefixTo(t, os); + *os << ")"; +} + +// Prints the fields of a tuple tersely to a string vector, one +// element for each field. See the comment before +// UniversalTersePrint() for how we define "tersely". +template +Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) { + Strings result; + TuplePrefixPrinter< ::std::tr1::tuple_size::value>:: + TersePrintPrefixToStrings(value, &result); + return result; +} +#endif // GTEST_HAS_TR1_TUPLE + +} // namespace internal + +template +::std::string PrintToString(const T& value) { + ::std::stringstream ss; + internal::UniversalTersePrinter::Print(value, &ss); + return ss.str(); +} + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-spi.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-spi.h new file mode 100644 index 00000000..f63fa9a1 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-spi.h @@ -0,0 +1,232 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Utilities for testing Google Test itself and code that uses Google Test +// (e.g. frameworks built on top of Google Test). + +#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_ +#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_ + +#include "gtest/gtest.h" + +namespace testing { + +// This helper class can be used to mock out Google Test failure reporting +// so that we can test Google Test or code that builds on Google Test. +// +// An object of this class appends a TestPartResult object to the +// TestPartResultArray object given in the constructor whenever a Google Test +// failure is reported. It can either intercept only failures that are +// generated in the same thread that created this object or it can intercept +// all generated failures. The scope of this mock object can be controlled with +// the second argument to the two arguments constructor. +class GTEST_API_ ScopedFakeTestPartResultReporter + : public TestPartResultReporterInterface { + public: + // The two possible mocking modes of this object. + enum InterceptMode { + INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures. + INTERCEPT_ALL_THREADS // Intercepts all failures. + }; + + // The c'tor sets this object as the test part result reporter used + // by Google Test. The 'result' parameter specifies where to report the + // results. This reporter will only catch failures generated in the current + // thread. DEPRECATED + explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result); + + // Same as above, but you can choose the interception scope of this object. + ScopedFakeTestPartResultReporter(InterceptMode intercept_mode, + TestPartResultArray* result); + + // The d'tor restores the previous test part result reporter. + virtual ~ScopedFakeTestPartResultReporter(); + + // Appends the TestPartResult object to the TestPartResultArray + // received in the constructor. + // + // This method is from the TestPartResultReporterInterface + // interface. + virtual void ReportTestPartResult(const TestPartResult& result); + private: + void Init(); + + const InterceptMode intercept_mode_; + TestPartResultReporterInterface* old_reporter_; + TestPartResultArray* const result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter); +}; + +namespace internal { + +// A helper class for implementing EXPECT_FATAL_FAILURE() and +// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +class GTEST_API_ SingleFailureChecker { + public: + // The constructor remembers the arguments. + SingleFailureChecker(const TestPartResultArray* results, + TestPartResult::Type type, + const string& substr); + ~SingleFailureChecker(); + private: + const TestPartResultArray* const results_; + const TestPartResult::Type type_; + const string substr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker); +}; + +} // namespace internal + +} // namespace testing + +// A set of macros for testing Google Test assertions or code that's expected +// to generate Google Test fatal failures. It verifies that the given +// statement will cause exactly one fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_FATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - 'statement' cannot reference local non-static variables or +// non-static members of the current object. +// - 'statement' cannot return a value. +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. The AcceptsMacroThatExpandsToUnprotectedComma test in +// gtest_unittest.cc will fail to compile if we do that. +#define EXPECT_FATAL_FAILURE(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ALL_THREADS, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +// A macro for testing Google Test assertions or code that's expected to +// generate Google Test non-fatal failures. It asserts that the given +// statement will cause exactly one non-fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// 'statement' is allowed to reference local variables and members of +// the current object. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. If we do that, the code won't compile when the user gives +// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that +// expands to code containing an unprotected comma. The +// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc +// catches that. +// +// For the same reason, we have to write +// if (::testing::internal::AlwaysTrue()) { statement; } +// instead of +// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) +// to avoid an MSVC warning on unreachable code. +#define EXPECT_NONFATAL_FAILURE(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \ + >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#endif // GTEST_INCLUDE_GTEST_GTEST_SPI_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-test-part.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-test-part.h new file mode 100644 index 00000000..77eb8448 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-test-part.h @@ -0,0 +1,179 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: mheule@google.com (Markus Heule) +// + +#ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ +#define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ + +#include +#include +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-string.h" + +namespace testing { + +// A copyable object representing the result of a test part (i.e. an +// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()). +// +// Don't inherit from TestPartResult as its destructor is not virtual. +class GTEST_API_ TestPartResult { + public: + // The possible outcomes of a test part (i.e. an assertion or an + // explicit SUCCEED(), FAIL(), or ADD_FAILURE()). + enum Type { + kSuccess, // Succeeded. + kNonFatalFailure, // Failed but the test can continue. + kFatalFailure // Failed and the test should be terminated. + }; + + // C'tor. TestPartResult does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestPartResult object. + TestPartResult(Type a_type, + const char* a_file_name, + int a_line_number, + const char* a_message) + : type_(a_type), + file_name_(a_file_name == NULL ? "" : a_file_name), + line_number_(a_line_number), + summary_(ExtractSummary(a_message)), + message_(a_message) { + } + + // Gets the outcome of the test part. + Type type() const { return type_; } + + // Gets the name of the source file where the test part took place, or + // NULL if it's unknown. + const char* file_name() const { + return file_name_.empty() ? NULL : file_name_.c_str(); + } + + // Gets the line in the source file where the test part took place, + // or -1 if it's unknown. + int line_number() const { return line_number_; } + + // Gets the summary of the failure message. + const char* summary() const { return summary_.c_str(); } + + // Gets the message associated with the test part. + const char* message() const { return message_.c_str(); } + + // Returns true iff the test part passed. + bool passed() const { return type_ == kSuccess; } + + // Returns true iff the test part failed. + bool failed() const { return type_ != kSuccess; } + + // Returns true iff the test part non-fatally failed. + bool nonfatally_failed() const { return type_ == kNonFatalFailure; } + + // Returns true iff the test part fatally failed. + bool fatally_failed() const { return type_ == kFatalFailure; } + + private: + Type type_; + + // Gets the summary of the failure message by omitting the stack + // trace in it. + static std::string ExtractSummary(const char* message); + + // The name of the source file where the test part took place, or + // "" if the source file is unknown. + std::string file_name_; + // The line in the source file where the test part took place, or -1 + // if the line number is unknown. + int line_number_; + std::string summary_; // The test failure summary. + std::string message_; // The test failure message. +}; + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result); + +// An array of TestPartResult objects. +// +// Don't inherit from TestPartResultArray as its destructor is not +// virtual. +class GTEST_API_ TestPartResultArray { + public: + TestPartResultArray() {} + + // Appends the given TestPartResult to the array. + void Append(const TestPartResult& result); + + // Returns the TestPartResult at the given index (0-based). + const TestPartResult& GetTestPartResult(int index) const; + + // Returns the number of TestPartResult objects in the array. + int size() const; + + private: + std::vector array_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestPartResultArray); +}; + +// This interface knows how to report a test part result. +class TestPartResultReporterInterface { + public: + virtual ~TestPartResultReporterInterface() {} + + virtual void ReportTestPartResult(const TestPartResult& result) = 0; +}; + +namespace internal { + +// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a +// statement generates new fatal failures. To do so it registers itself as the +// current test part result reporter. Besides checking if fatal failures were +// reported, it only delegates the reporting to the former result reporter. +// The original result reporter is restored in the destructor. +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +class GTEST_API_ HasNewFatalFailureHelper + : public TestPartResultReporterInterface { + public: + HasNewFatalFailureHelper(); + virtual ~HasNewFatalFailureHelper(); + virtual void ReportTestPartResult(const TestPartResult& result); + bool has_new_fatal_failure() const { return has_new_fatal_failure_; } + private: + bool has_new_fatal_failure_; + TestPartResultReporterInterface* original_reporter_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(HasNewFatalFailureHelper); +}; + +} // namespace internal + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-typed-test.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-typed-test.h new file mode 100644 index 00000000..fe1e83b2 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest-typed-test.h @@ -0,0 +1,259 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ + +// This header implements typed tests and type-parameterized tests. + +// Typed (aka type-driven) tests repeat the same test for types in a +// list. You must know which types you want to test with when writing +// typed tests. Here's how you do it: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template +class FooTest : public testing::Test { + public: + ... + typedef std::list List; + static T shared_; + T value_; +}; + +// Next, associate a list of types with the test case, which will be +// repeated for each type in the list. The typedef is necessary for +// the macro to parse correctly. +typedef testing::Types MyTypes; +TYPED_TEST_CASE(FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// TYPED_TEST_CASE(FooTest, int); + +// Then, use TYPED_TEST() instead of TEST_F() to define as many typed +// tests for this test case as you want. +TYPED_TEST(FooTest, DoesBlah) { + // Inside a test, refer to TypeParam to get the type parameter. + // Since we are inside a derived class template, C++ requires use to + // visit the members of FooTest via 'this'. + TypeParam n = this->value_; + + // To visit static members of the fixture, add the TestFixture:: + // prefix. + n += TestFixture::shared_; + + // To refer to typedefs in the fixture, add the "typename + // TestFixture::" prefix. + typename TestFixture::List values; + values.push_back(n); + ... +} + +TYPED_TEST(FooTest, HasPropertyA) { ... } + +#endif // 0 + +// Type-parameterized tests are abstract test patterns parameterized +// by a type. Compared with typed tests, type-parameterized tests +// allow you to define the test pattern without knowing what the type +// parameters are. The defined pattern can be instantiated with +// different types any number of times, in any number of translation +// units. +// +// If you are designing an interface or concept, you can define a +// suite of type-parameterized tests to verify properties that any +// valid implementation of the interface/concept should have. Then, +// each implementation can easily instantiate the test suite to verify +// that it conforms to the requirements, without having to write +// similar tests repeatedly. Here's an example: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template +class FooTest : public testing::Test { + ... +}; + +// Next, declare that you will define a type-parameterized test case +// (the _P suffix is for "parameterized" or "pattern", whichever you +// prefer): +TYPED_TEST_CASE_P(FooTest); + +// Then, use TYPED_TEST_P() to define as many type-parameterized tests +// for this type-parameterized test case as you want. +TYPED_TEST_P(FooTest, DoesBlah) { + // Inside a test, refer to TypeParam to get the type parameter. + TypeParam n = 0; + ... +} + +TYPED_TEST_P(FooTest, HasPropertyA) { ... } + +// Now the tricky part: you need to register all test patterns before +// you can instantiate them. The first argument of the macro is the +// test case name; the rest are the names of the tests in this test +// case. +REGISTER_TYPED_TEST_CASE_P(FooTest, + DoesBlah, HasPropertyA); + +// Finally, you are free to instantiate the pattern with the types you +// want. If you put the above code in a header file, you can #include +// it in multiple C++ source files and instantiate it multiple times. +// +// To distinguish different instances of the pattern, the first +// argument to the INSTANTIATE_* macro is a prefix that will be added +// to the actual test case name. Remember to pick unique prefixes for +// different instances. +typedef testing::Types MyTypes; +INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int); + +#endif // 0 + +#include "gtest/internal/gtest-port.h" +#include "gtest/internal/gtest-type-util.h" + +// Implements typed tests. + +#if GTEST_HAS_TYPED_TEST + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the typedef for the type parameters of the +// given test case. +# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_ + +// The 'Types' template argument below must have spaces around it +// since some compilers may choke on '>>' when passing a template +// instance (e.g. Types) +# define TYPED_TEST_CASE(CaseName, Types) \ + typedef ::testing::internal::TypeList< Types >::type \ + GTEST_TYPE_PARAMS_(CaseName) + +# define TYPED_TEST(CaseName, TestName) \ + template \ + class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \ + : public CaseName { \ + private: \ + typedef CaseName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + virtual void TestBody(); \ + }; \ + bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTest< \ + CaseName, \ + ::testing::internal::TemplateSel< \ + GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \ + GTEST_TYPE_PARAMS_(CaseName)>::Register(\ + "", #CaseName, #TestName, 0); \ + template \ + void GTEST_TEST_CLASS_NAME_(CaseName, TestName)::TestBody() + +#endif // GTEST_HAS_TYPED_TEST + +// Implements type-parameterized tests. + +#if GTEST_HAS_TYPED_TEST_P + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the namespace name that the type-parameterized tests for +// the given type-parameterized test case are defined in. The exact +// name of the namespace is subject to change without notice. +# define GTEST_CASE_NAMESPACE_(TestCaseName) \ + gtest_case_##TestCaseName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the variable used to remember the names of +// the defined tests in the given test case. +# define GTEST_TYPED_TEST_CASE_P_STATE_(TestCaseName) \ + gtest_typed_test_case_p_state_##TestCaseName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY. +// +// Expands to the name of the variable used to remember the names of +// the registered tests in the given test case. +# define GTEST_REGISTERED_TEST_NAMES_(TestCaseName) \ + gtest_registered_test_names_##TestCaseName##_ + +// The variables defined in the type-parameterized test macros are +// static as typically these macros are used in a .h file that can be +// #included in multiple translation units linked together. +# define TYPED_TEST_CASE_P(CaseName) \ + static ::testing::internal::TypedTestCasePState \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName) + +# define TYPED_TEST_P(CaseName, TestName) \ + namespace GTEST_CASE_NAMESPACE_(CaseName) { \ + template \ + class TestName : public CaseName { \ + private: \ + typedef CaseName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + virtual void TestBody(); \ + }; \ + static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).AddTestName(\ + __FILE__, __LINE__, #CaseName, #TestName); \ + } \ + template \ + void GTEST_CASE_NAMESPACE_(CaseName)::TestName::TestBody() + +# define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \ + namespace GTEST_CASE_NAMESPACE_(CaseName) { \ + typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \ + } \ + static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) = \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames(\ + __FILE__, __LINE__, #__VA_ARGS__) + +// The 'Types' template argument below must have spaces around it +// since some compilers may choke on '>>' when passing a template +// instance (e.g. Types) +# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \ + bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTestCase::type>::Register(\ + #Prefix, #CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName)) + +#endif // GTEST_HAS_TYPED_TEST_P + +#endif // GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest.h new file mode 100644 index 00000000..6fa0a392 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest.h @@ -0,0 +1,2291 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines the public API for Google Test. It should be +// included by any test program that uses Google Test. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! +// +// Acknowledgment: Google Test borrowed the idea of automatic test +// registration from Barthelemy Dagenais' (barthelemy@prologique.com) +// easyUnit framework. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_H_ + +#include +#include +#include + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-string.h" +#include "gtest/gtest-death-test.h" +#include "gtest/gtest-message.h" +#include "gtest/gtest-param-test.h" +#include "gtest/gtest-printers.h" +#include "gtest/gtest_prod.h" +#include "gtest/gtest-test-part.h" +#include "gtest/gtest-typed-test.h" + +// Depending on the platform, different string classes are available. +// On Linux, in addition to ::std::string, Google also makes use of +// class ::string, which has the same interface as ::std::string, but +// has a different implementation. +// +// The user can define GTEST_HAS_GLOBAL_STRING to 1 to indicate that +// ::string is available AND is a distinct type to ::std::string, or +// define it to 0 to indicate otherwise. +// +// If the user's ::std::string and ::string are the same class due to +// aliasing, he should define GTEST_HAS_GLOBAL_STRING to 0. +// +// If the user doesn't define GTEST_HAS_GLOBAL_STRING, it is defined +// heuristically. + +namespace testing { + +// Declares the flags. + +// This flag temporary enables the disabled tests. +GTEST_DECLARE_bool_(also_run_disabled_tests); + +// This flag brings the debugger on an assertion failure. +GTEST_DECLARE_bool_(break_on_failure); + +// This flag controls whether Google Test catches all test-thrown exceptions +// and logs them as failures. +GTEST_DECLARE_bool_(catch_exceptions); + +// This flag enables using colors in terminal output. Available values are +// "yes" to enable colors, "no" (disable colors), or "auto" (the default) +// to let Google Test decide. +GTEST_DECLARE_string_(color); + +// This flag sets up the filter to select by name using a glob pattern +// the tests to run. If the filter is not given all tests are executed. +GTEST_DECLARE_string_(filter); + +// This flag causes the Google Test to list tests. None of the tests listed +// are actually run if the flag is provided. +GTEST_DECLARE_bool_(list_tests); + +// This flag controls whether Google Test emits a detailed XML report to a file +// in addition to its normal textual output. +GTEST_DECLARE_string_(output); + +// This flags control whether Google Test prints the elapsed time for each +// test. +GTEST_DECLARE_bool_(print_time); + +// This flag specifies the random number seed. +GTEST_DECLARE_int32_(random_seed); + +// This flag sets how many times the tests are repeated. The default value +// is 1. If the value is -1 the tests are repeating forever. +GTEST_DECLARE_int32_(repeat); + +// This flag controls whether Google Test includes Google Test internal +// stack frames in failure stack traces. +GTEST_DECLARE_bool_(show_internal_stack_frames); + +// When this flag is specified, tests' order is randomized on every iteration. +GTEST_DECLARE_bool_(shuffle); + +// This flag specifies the maximum number of stack frames to be +// printed in a failure message. +GTEST_DECLARE_int32_(stack_trace_depth); + +// When this flag is specified, a failed assertion will throw an +// exception if exceptions are enabled, or exit the program with a +// non-zero code otherwise. +GTEST_DECLARE_bool_(throw_on_failure); + +// When this flag is set with a "host:port" string, on supported +// platforms test results are streamed to the specified port on +// the specified host machine. +GTEST_DECLARE_string_(stream_result_to); + +// The upper limit for valid stack trace depths. +const int kMaxStackTraceDepth = 100; + +namespace internal { + +class AssertHelper; +class DefaultGlobalTestPartResultReporter; +class ExecDeathTest; +class NoExecDeathTest; +class FinalSuccessChecker; +class GTestFlagSaver; +class StreamingListenerTest; +class TestResultAccessor; +class TestEventListenersAccessor; +class TestEventRepeater; +class UnitTestRecordPropertyTestHelper; +class WindowsDeathTest; +class UnitTestImpl* GetUnitTestImpl(); +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message); + +} // namespace internal + +// The friend relationship of some of these classes is cyclic. +// If we don't forward declare them the compiler might confuse the classes +// in friendship clauses with same named classes on the scope. +class Test; +class TestCase; +class TestInfo; +class UnitTest; + +// A class for indicating whether an assertion was successful. When +// the assertion wasn't successful, the AssertionResult object +// remembers a non-empty message that describes how it failed. +// +// To create an instance of this class, use one of the factory functions +// (AssertionSuccess() and AssertionFailure()). +// +// This class is useful for two purposes: +// 1. Defining predicate functions to be used with Boolean test assertions +// EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts +// 2. Defining predicate-format functions to be +// used with predicate assertions (ASSERT_PRED_FORMAT*, etc). +// +// For example, if you define IsEven predicate: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5))) +// will print the message +// +// Value of: IsEven(Fib(5)) +// Actual: false (5 is odd) +// Expected: true +// +// instead of a more opaque +// +// Value of: IsEven(Fib(5)) +// Actual: false +// Expected: true +// +// in case IsEven is a simple Boolean predicate. +// +// If you expect your predicate to be reused and want to support informative +// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up +// about half as often as positive ones in our tests), supply messages for +// both success and failure cases: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess() << n << " is even"; +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print +// +// Value of: IsEven(Fib(6)) +// Actual: true (8 is even) +// Expected: false +// +// NB: Predicates that support negative Boolean assertions have reduced +// performance in positive ones so be careful not to use them in tests +// that have lots (tens of thousands) of positive Boolean assertions. +// +// To use this class with EXPECT_PRED_FORMAT assertions such as: +// +// // Verifies that Foo() returns an even number. +// EXPECT_PRED_FORMAT1(IsEven, Foo()); +// +// you need to define: +// +// testing::AssertionResult IsEven(const char* expr, int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() +// << "Expected: " << expr << " is even\n Actual: it's " << n; +// } +// +// If Foo() returns 5, you will see the following message: +// +// Expected: Foo() is even +// Actual: it's 5 +// +class GTEST_API_ AssertionResult { + public: + // Copy constructor. + // Used in EXPECT_TRUE/FALSE(assertion_result). + AssertionResult(const AssertionResult& other); + // Used in the EXPECT_TRUE/FALSE(bool_expression). + explicit AssertionResult(bool success) : success_(success) {} + + // Returns true iff the assertion succeeded. + operator bool() const { return success_; } // NOLINT + + // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. + AssertionResult operator!() const; + + // Returns the text streamed into this AssertionResult. Test assertions + // use it when they fail (i.e., the predicate's outcome doesn't match the + // assertion's expectation). When nothing has been streamed into the + // object, returns an empty string. + const char* message() const { + return message_.get() != NULL ? message_->c_str() : ""; + } + // TODO(vladl@google.com): Remove this after making sure no clients use it. + // Deprecated; please use message() instead. + const char* failure_message() const { return message(); } + + // Streams a custom failure message into this object. + template AssertionResult& operator<<(const T& value) { + AppendMessage(Message() << value); + return *this; + } + + // Allows streaming basic output manipulators such as endl or flush into + // this object. + AssertionResult& operator<<( + ::std::ostream& (*basic_manipulator)(::std::ostream& stream)) { + AppendMessage(Message() << basic_manipulator); + return *this; + } + + private: + // Appends the contents of message to message_. + void AppendMessage(const Message& a_message) { + if (message_.get() == NULL) + message_.reset(new ::std::string); + message_->append(a_message.GetString().c_str()); + } + + // Stores result of the assertion predicate. + bool success_; + // Stores the message describing the condition in case the expectation + // construct is not satisfied with the predicate's outcome. + // Referenced via a pointer to avoid taking too much stack frame space + // with test assertions. + internal::scoped_ptr< ::std::string> message_; + + GTEST_DISALLOW_ASSIGN_(AssertionResult); +}; + +// Makes a successful assertion result. +GTEST_API_ AssertionResult AssertionSuccess(); + +// Makes a failed assertion result. +GTEST_API_ AssertionResult AssertionFailure(); + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << msg. +GTEST_API_ AssertionResult AssertionFailure(const Message& msg); + +// The abstract class that all tests inherit from. +// +// In Google Test, a unit test program contains one or many TestCases, and +// each TestCase contains one or many Tests. +// +// When you define a test using the TEST macro, you don't need to +// explicitly derive from Test - the TEST macro automatically does +// this for you. +// +// The only time you derive from Test is when defining a test fixture +// to be used a TEST_F. For example: +// +// class FooTest : public testing::Test { +// protected: +// virtual void SetUp() { ... } +// virtual void TearDown() { ... } +// ... +// }; +// +// TEST_F(FooTest, Bar) { ... } +// TEST_F(FooTest, Baz) { ... } +// +// Test is not copyable. +class GTEST_API_ Test { + public: + friend class TestInfo; + + // Defines types for pointers to functions that set up and tear down + // a test case. + typedef internal::SetUpTestCaseFunc SetUpTestCaseFunc; + typedef internal::TearDownTestCaseFunc TearDownTestCaseFunc; + + // The d'tor is virtual as we intend to inherit from Test. + virtual ~Test(); + + // Sets up the stuff shared by all tests in this test case. + // + // Google Test will call Foo::SetUpTestCase() before running the first + // test in test case Foo. Hence a sub-class can define its own + // SetUpTestCase() method to shadow the one defined in the super + // class. + static void SetUpTestCase() {} + + // Tears down the stuff shared by all tests in this test case. + // + // Google Test will call Foo::TearDownTestCase() after running the last + // test in test case Foo. Hence a sub-class can define its own + // TearDownTestCase() method to shadow the one defined in the super + // class. + static void TearDownTestCase() {} + + // Returns true iff the current test has a fatal failure. + static bool HasFatalFailure(); + + // Returns true iff the current test has a non-fatal failure. + static bool HasNonfatalFailure(); + + // Returns true iff the current test has a (either fatal or + // non-fatal) failure. + static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); } + + // Logs a property for the current test, test case, or for the entire + // invocation of the test program when used outside of the context of a + // test case. Only the last value for a given key is remembered. These + // are public static so they can be called from utility functions that are + // not members of the test fixture. Calls to RecordProperty made during + // lifespan of the test (from the moment its constructor starts to the + // moment its destructor finishes) will be output in XML as attributes of + // the element. Properties recorded from fixture's + // SetUpTestCase or TearDownTestCase are logged as attributes of the + // corresponding element. Calls to RecordProperty made in the + // global context (before or after invocation of RUN_ALL_TESTS and from + // SetUp/TearDown method of Environment objects registered with Google + // Test) will be output as attributes of the element. + static void RecordProperty(const std::string& key, const std::string& value); + static void RecordProperty(const std::string& key, int value); + + protected: + // Creates a Test object. + Test(); + + // Sets up the test fixture. + virtual void SetUp(); + + // Tears down the test fixture. + virtual void TearDown(); + + private: + // Returns true iff the current test has the same fixture class as + // the first test in the current test case. + static bool HasSameFixtureClass(); + + // Runs the test after the test fixture has been set up. + // + // A sub-class must implement this to define the test logic. + // + // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM. + // Instead, use the TEST or TEST_F macro. + virtual void TestBody() = 0; + + // Sets up, executes, and tears down the test. + void Run(); + + // Deletes self. We deliberately pick an unusual name for this + // internal method to avoid clashing with names used in user TESTs. + void DeleteSelf_() { delete this; } + + // Uses a GTestFlagSaver to save and restore all Google Test flags. + const internal::GTestFlagSaver* const gtest_flag_saver_; + + // Often a user mis-spells SetUp() as Setup() and spends a long time + // wondering why it is never called by Google Test. The declaration of + // the following method is solely for catching such an error at + // compile time: + // + // - The return type is deliberately chosen to be not void, so it + // will be a conflict if a user declares void Setup() in his test + // fixture. + // + // - This method is private, so it will be another compiler error + // if a user calls it from his test fixture. + // + // DO NOT OVERRIDE THIS FUNCTION. + // + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } + + // We disallow copying Tests. + GTEST_DISALLOW_COPY_AND_ASSIGN_(Test); +}; + +typedef internal::TimeInMillis TimeInMillis; + +// A copyable object representing a user specified test property which can be +// output as a key/value string pair. +// +// Don't inherit from TestProperty as its destructor is not virtual. +class TestProperty { + public: + // C'tor. TestProperty does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestProperty object. + TestProperty(const std::string& a_key, const std::string& a_value) : + key_(a_key), value_(a_value) { + } + + // Gets the user supplied key. + const char* key() const { + return key_.c_str(); + } + + // Gets the user supplied value. + const char* value() const { + return value_.c_str(); + } + + // Sets a new value, overriding the one supplied in the constructor. + void SetValue(const std::string& new_value) { + value_ = new_value; + } + + private: + // The key supplied by the user. + std::string key_; + // The value supplied by the user. + std::string value_; +}; + +// The result of a single Test. This includes a list of +// TestPartResults, a list of TestProperties, a count of how many +// death tests there are in the Test, and how much time it took to run +// the Test. +// +// TestResult is not copyable. +class GTEST_API_ TestResult { + public: + // Creates an empty TestResult. + TestResult(); + + // D'tor. Do not inherit from TestResult. + ~TestResult(); + + // Gets the number of all test parts. This is the sum of the number + // of successful test parts and the number of failed test parts. + int total_part_count() const; + + // Returns the number of the test properties. + int test_property_count() const; + + // Returns true iff the test passed (i.e. no test part failed). + bool Passed() const { return !Failed(); } + + // Returns true iff the test failed. + bool Failed() const; + + // Returns true iff the test fatally failed. + bool HasFatalFailure() const; + + // Returns true iff the test has a non-fatal failure. + bool HasNonfatalFailure() const; + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns the i-th test part result among all the results. i can range + // from 0 to test_property_count() - 1. If i is not in that range, aborts + // the program. + const TestPartResult& GetTestPartResult(int i) const; + + // Returns the i-th test property. i can range from 0 to + // test_property_count() - 1. If i is not in that range, aborts the + // program. + const TestProperty& GetTestProperty(int i) const; + + private: + friend class TestInfo; + friend class TestCase; + friend class UnitTest; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::ExecDeathTest; + friend class internal::TestResultAccessor; + friend class internal::UnitTestImpl; + friend class internal::WindowsDeathTest; + + // Gets the vector of TestPartResults. + const std::vector& test_part_results() const { + return test_part_results_; + } + + // Gets the vector of TestProperties. + const std::vector& test_properties() const { + return test_properties_; + } + + // Sets the elapsed time. + void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; } + + // Adds a test property to the list. The property is validated and may add + // a non-fatal failure if invalid (e.g., if it conflicts with reserved + // key names). If a property is already recorded for the same key, the + // value will be updated, rather than storing multiple values for the same + // key. xml_element specifies the element for which the property is being + // recorded and is used for validation. + void RecordProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a failure if the key is a reserved attribute of Google Test + // testcase tags. Returns true if the property is valid. + // TODO(russr): Validate attribute names are legal and human readable. + static bool ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a test part result to the list. + void AddTestPartResult(const TestPartResult& test_part_result); + + // Returns the death test count. + int death_test_count() const { return death_test_count_; } + + // Increments the death test count, returning the new count. + int increment_death_test_count() { return ++death_test_count_; } + + // Clears the test part results. + void ClearTestPartResults(); + + // Clears the object. + void Clear(); + + // Protects mutable state of the property vector and of owned + // properties, whose values may be updated. + internal::Mutex test_properites_mutex_; + + // The vector of TestPartResults + std::vector test_part_results_; + // The vector of TestProperties + std::vector test_properties_; + // Running count of death tests. + int death_test_count_; + // The elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + + // We disallow copying TestResult. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestResult); +}; // class TestResult + +// A TestInfo object stores the following information about a test: +// +// Test case name +// Test name +// Whether the test should be run +// A function pointer that creates the test object when invoked +// Test result +// +// The constructor of TestInfo registers itself with the UnitTest +// singleton such that the RUN_ALL_TESTS() macro knows which tests to +// run. +class GTEST_API_ TestInfo { + public: + // Destructs a TestInfo object. This function is not virtual, so + // don't inherit from TestInfo. + ~TestInfo(); + + // Returns the test case name. + const char* test_case_name() const { return test_case_name_.c_str(); } + + // Returns the test name. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a typed + // or a type-parameterized test. + const char* type_param() const { + if (type_param_.get() != NULL) + return type_param_->c_str(); + return NULL; + } + + // Returns the text representation of the value parameter, or NULL if this + // is not a value-parameterized test. + const char* value_param() const { + if (value_param_.get() != NULL) + return value_param_->c_str(); + return NULL; + } + + // Returns true if this test should run, that is if the test is not + // disabled (or it is disabled but the also_run_disabled_tests flag has + // been specified) and its full name matches the user-specified filter. + // + // Google Test allows the user to filter the tests by their full names. + // The full name of a test Bar in test case Foo is defined as + // "Foo.Bar". Only the tests that match the filter will run. + // + // A filter is a colon-separated list of glob (not regex) patterns, + // optionally followed by a '-' and a colon-separated list of + // negative patterns (tests to exclude). A test is run if it + // matches one of the positive patterns and does not match any of + // the negative patterns. + // + // For example, *A*:Foo.* is a filter that matches any string that + // contains the character 'A' or starts with "Foo.". + bool should_run() const { return should_run_; } + + // Returns true iff this test will appear in the XML report. + bool is_reportable() const { + // For now, the XML report includes all tests matching the filter. + // In the future, we may trim tests that are excluded because of + // sharding. + return matches_filter_; + } + + // Returns the result of the test. + const TestResult* result() const { return &result_; } + + private: +#if GTEST_HAS_DEATH_TEST + friend class internal::DefaultDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + friend class Test; + friend class TestCase; + friend class internal::UnitTestImpl; + friend class internal::StreamingListenerTest; + friend TestInfo* internal::MakeAndRegisterTestInfo( + const char* test_case_name, + const char* name, + const char* type_param, + const char* value_param, + internal::TypeId fixture_class_id, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + internal::TestFactoryBase* factory); + + // Constructs a TestInfo object. The newly constructed instance assumes + // ownership of the factory object. + TestInfo(const std::string& test_case_name, + const std::string& name, + const char* a_type_param, // NULL if not a type-parameterized test + const char* a_value_param, // NULL if not a value-parameterized test + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory); + + // Increments the number of death tests encountered in this test so + // far. + int increment_death_test_count() { + return result_.increment_death_test_count(); + } + + // Creates the test object, runs it, records its result, and then + // deletes it. + void Run(); + + static void ClearTestResult(TestInfo* test_info) { + test_info->result_.Clear(); + } + + // These fields are immutable properties of the test. + const std::string test_case_name_; // Test case name + const std::string name_; // Test name + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const internal::scoped_ptr type_param_; + // Text representation of the value parameter, or NULL if this is not a + // value-parameterized test. + const internal::scoped_ptr value_param_; + const internal::TypeId fixture_class_id_; // ID of the test fixture class + bool should_run_; // True iff this test should run + bool is_disabled_; // True iff this test is disabled + bool matches_filter_; // True if this test matches the + // user-specified filter. + internal::TestFactoryBase* const factory_; // The factory that creates + // the test object + + // This field is mutable and needs to be reset before running the + // test for the second time. + TestResult result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfo); +}; + +// A test case, which consists of a vector of TestInfos. +// +// TestCase is not copyable. +class GTEST_API_ TestCase { + public: + // Creates a TestCase with the given name. + // + // TestCase does NOT have a default constructor. Always use this + // constructor to create a TestCase object. + // + // Arguments: + // + // name: name of the test case + // a_type_param: the name of the test's type parameter, or NULL if + // this is not a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + TestCase(const char* name, const char* a_type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc); + + // Destructor of TestCase. + virtual ~TestCase(); + + // Gets the name of the TestCase. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a + // type-parameterized test case. + const char* type_param() const { + if (type_param_.get() != NULL) + return type_param_->c_str(); + return NULL; + } + + // Returns true if any test in this test case should run. + bool should_run() const { return should_run_; } + + // Gets the number of successful tests in this test case. + int successful_test_count() const; + + // Gets the number of failed tests in this test case. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests in this test case. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Get the number of tests in this test case that should run. + int test_to_run_count() const; + + // Gets the number of all tests in this test case. + int total_test_count() const; + + // Returns true iff the test case passed. + bool Passed() const { return !Failed(); } + + // Returns true iff the test case failed. + bool Failed() const { return failed_test_count() > 0; } + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + const TestInfo* GetTestInfo(int i) const; + + // Returns the TestResult that holds test properties recorded during + // execution of SetUpTestCase and TearDownTestCase. + const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; } + + private: + friend class Test; + friend class internal::UnitTestImpl; + + // Gets the (mutable) vector of TestInfos in this TestCase. + std::vector& test_info_list() { return test_info_list_; } + + // Gets the (immutable) vector of TestInfos in this TestCase. + const std::vector& test_info_list() const { + return test_info_list_; + } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + TestInfo* GetMutableTestInfo(int i); + + // Sets the should_run member. + void set_should_run(bool should) { should_run_ = should; } + + // Adds a TestInfo to this test case. Will delete the TestInfo upon + // destruction of the TestCase object. + void AddTestInfo(TestInfo * test_info); + + // Clears the results of all tests in this test case. + void ClearResult(); + + // Clears the results of all tests in the given test case. + static void ClearTestCaseResult(TestCase* test_case) { + test_case->ClearResult(); + } + + // Runs every test in this TestCase. + void Run(); + + // Runs SetUpTestCase() for this TestCase. This wrapper is needed + // for catching exceptions thrown from SetUpTestCase(). + void RunSetUpTestCase() { (*set_up_tc_)(); } + + // Runs TearDownTestCase() for this TestCase. This wrapper is + // needed for catching exceptions thrown from TearDownTestCase(). + void RunTearDownTestCase() { (*tear_down_tc_)(); } + + // Returns true iff test passed. + static bool TestPassed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Passed(); + } + + // Returns true iff test failed. + static bool TestFailed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Failed(); + } + + // Returns true iff the test is disabled and will be reported in the XML + // report. + static bool TestReportableDisabled(const TestInfo* test_info) { + return test_info->is_reportable() && test_info->is_disabled_; + } + + // Returns true iff test is disabled. + static bool TestDisabled(const TestInfo* test_info) { + return test_info->is_disabled_; + } + + // Returns true iff this test will appear in the XML report. + static bool TestReportable(const TestInfo* test_info) { + return test_info->is_reportable(); + } + + // Returns true if the given test should run. + static bool ShouldRunTest(const TestInfo* test_info) { + return test_info->should_run(); + } + + // Shuffles the tests in this test case. + void ShuffleTests(internal::Random* random); + + // Restores the test order to before the first shuffle. + void UnshuffleTests(); + + // Name of the test case. + std::string name_; + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const internal::scoped_ptr type_param_; + // The vector of TestInfos in their original order. It owns the + // elements in the vector. + std::vector test_info_list_; + // Provides a level of indirection for the test list to allow easy + // shuffling and restoring the test order. The i-th element in this + // vector is the index of the i-th test in the shuffled test list. + std::vector test_indices_; + // Pointer to the function that sets up the test case. + Test::SetUpTestCaseFunc set_up_tc_; + // Pointer to the function that tears down the test case. + Test::TearDownTestCaseFunc tear_down_tc_; + // True iff any test in this test case should run. + bool should_run_; + // Elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + // Holds test properties recorded during execution of SetUpTestCase and + // TearDownTestCase. + TestResult ad_hoc_test_result_; + + // We disallow copying TestCases. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestCase); +}; + +// An Environment object is capable of setting up and tearing down an +// environment. The user should subclass this to define his own +// environment(s). +// +// An Environment object does the set-up and tear-down in virtual +// methods SetUp() and TearDown() instead of the constructor and the +// destructor, as: +// +// 1. You cannot safely throw from a destructor. This is a problem +// as in some cases Google Test is used where exceptions are enabled, and +// we may want to implement ASSERT_* using exceptions where they are +// available. +// 2. You cannot use ASSERT_* directly in a constructor or +// destructor. +class Environment { + public: + // The d'tor is virtual as we need to subclass Environment. + virtual ~Environment() {} + + // Override this to define how to set up the environment. + virtual void SetUp() {} + + // Override this to define how to tear down the environment. + virtual void TearDown() {} + private: + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } +}; + +// The interface for tracing execution of tests. The methods are organized in +// the order the corresponding events are fired. +class TestEventListener { + public: + virtual ~TestEventListener() {} + + // Fired before any test activity starts. + virtual void OnTestProgramStart(const UnitTest& unit_test) = 0; + + // Fired before each iteration of tests starts. There may be more than + // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration + // index, starting from 0. + virtual void OnTestIterationStart(const UnitTest& unit_test, + int iteration) = 0; + + // Fired before environment set-up for each iteration of tests starts. + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0; + + // Fired after environment set-up for each iteration of tests ends. + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0; + + // Fired before the test case starts. + virtual void OnTestCaseStart(const TestCase& test_case) = 0; + + // Fired before the test starts. + virtual void OnTestStart(const TestInfo& test_info) = 0; + + // Fired after a failed assertion or a SUCCEED() invocation. + virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0; + + // Fired after the test ends. + virtual void OnTestEnd(const TestInfo& test_info) = 0; + + // Fired after the test case ends. + virtual void OnTestCaseEnd(const TestCase& test_case) = 0; + + // Fired before environment tear-down for each iteration of tests starts. + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0; + + // Fired after environment tear-down for each iteration of tests ends. + virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0; + + // Fired after each iteration of tests finishes. + virtual void OnTestIterationEnd(const UnitTest& unit_test, + int iteration) = 0; + + // Fired after all test activities have ended. + virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0; +}; + +// The convenience class for users who need to override just one or two +// methods and are not concerned that a possible change to a signature of +// the methods they override will not be caught during the build. For +// comments about each method please see the definition of TestEventListener +// above. +class EmptyTestEventListener : public TestEventListener { + public: + virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationStart(const UnitTest& /*unit_test*/, + int /*iteration*/) {} + virtual void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) {} + virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestCaseStart(const TestCase& /*test_case*/) {} + virtual void OnTestStart(const TestInfo& /*test_info*/) {} + virtual void OnTestPartResult(const TestPartResult& /*test_part_result*/) {} + virtual void OnTestEnd(const TestInfo& /*test_info*/) {} + virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {} + virtual void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) {} + virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationEnd(const UnitTest& /*unit_test*/, + int /*iteration*/) {} + virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {} +}; + +// TestEventListeners lets users add listeners to track events in Google Test. +class GTEST_API_ TestEventListeners { + public: + TestEventListeners(); + ~TestEventListeners(); + + // Appends an event listener to the end of the list. Google Test assumes + // the ownership of the listener (i.e. it will delete the listener when + // the test program finishes). + void Append(TestEventListener* listener); + + // Removes the given event listener from the list and returns it. It then + // becomes the caller's responsibility to delete the listener. Returns + // NULL if the listener is not found in the list. + TestEventListener* Release(TestEventListener* listener); + + // Returns the standard listener responsible for the default console + // output. Can be removed from the listeners list to shut down default + // console output. Note that removing this object from the listener list + // with Release transfers its ownership to the caller and makes this + // function return NULL the next time. + TestEventListener* default_result_printer() const { + return default_result_printer_; + } + + // Returns the standard listener responsible for the default XML output + // controlled by the --gtest_output=xml flag. Can be removed from the + // listeners list by users who want to shut down the default XML output + // controlled by this flag and substitute it with custom one. Note that + // removing this object from the listener list with Release transfers its + // ownership to the caller and makes this function return NULL the next + // time. + TestEventListener* default_xml_generator() const { + return default_xml_generator_; + } + + private: + friend class TestCase; + friend class TestInfo; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::NoExecDeathTest; + friend class internal::TestEventListenersAccessor; + friend class internal::UnitTestImpl; + + // Returns repeater that broadcasts the TestEventListener events to all + // subscribers. + TestEventListener* repeater(); + + // Sets the default_result_printer attribute to the provided listener. + // The listener is also added to the listener list and previous + // default_result_printer is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultResultPrinter(TestEventListener* listener); + + // Sets the default_xml_generator attribute to the provided listener. The + // listener is also added to the listener list and previous + // default_xml_generator is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultXmlGenerator(TestEventListener* listener); + + // Controls whether events will be forwarded by the repeater to the + // listeners in the list. + bool EventForwardingEnabled() const; + void SuppressEventForwarding(); + + // The actual list of listeners. + internal::TestEventRepeater* repeater_; + // Listener responsible for the standard result output. + TestEventListener* default_result_printer_; + // Listener responsible for the creation of the XML output file. + TestEventListener* default_xml_generator_; + + // We disallow copying TestEventListeners. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventListeners); +}; + +// A UnitTest consists of a vector of TestCases. +// +// This is a singleton class. The only instance of UnitTest is +// created when UnitTest::GetInstance() is first called. This +// instance is never deleted. +// +// UnitTest is not copyable. +// +// This class is thread-safe as long as the methods are called +// according to their specification. +class GTEST_API_ UnitTest { + public: + // Gets the singleton UnitTest object. The first time this method + // is called, a UnitTest object is constructed and returned. + // Consecutive calls will return the same object. + static UnitTest* GetInstance(); + + // Runs all tests in this UnitTest object and prints the result. + // Returns 0 if successful, or 1 otherwise. + // + // This method can only be called from the main thread. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + int Run() GTEST_MUST_USE_RESULT_; + + // Returns the working directory when the first TEST() or TEST_F() + // was executed. The UnitTest object owns the string. + const char* original_working_dir() const; + + // Returns the TestCase object for the test that's currently running, + // or NULL if no test is running. + const TestCase* current_test_case() const + GTEST_LOCK_EXCLUDED_(mutex_); + + // Returns the TestInfo object for the test that's currently running, + // or NULL if no test is running. + const TestInfo* current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_); + + // Returns the random seed used at the start of the current test run. + int random_seed() const; + +#if GTEST_HAS_PARAM_TEST + // Returns the ParameterizedTestCaseRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + internal::ParameterizedTestCaseRegistry& parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_); +#endif // GTEST_HAS_PARAM_TEST + + // Gets the number of successful test cases. + int successful_test_case_count() const; + + // Gets the number of failed test cases. + int failed_test_case_count() const; + + // Gets the number of all test cases. + int total_test_case_count() const; + + // Gets the number of all test cases that contain at least one test + // that should run. + int test_case_to_run_count() const; + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const; + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const; + + // Returns true iff the unit test passed (i.e. all test cases passed). + bool Passed() const; + + // Returns true iff the unit test failed (i.e. some test case failed + // or something outside of all tests failed). + bool Failed() const; + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + const TestCase* GetTestCase(int i) const; + + // Returns the TestResult containing information on test failures and + // properties logged outside of individual test cases. + const TestResult& ad_hoc_test_result() const; + + // Returns the list of event listeners that can be used to track events + // inside Google Test. + TestEventListeners& listeners(); + + private: + // Registers and returns a global test environment. When a test + // program is run, all global test environments will be set-up in + // the order they were registered. After all tests in the program + // have finished, all global test environments will be torn-down in + // the *reverse* order they were registered. + // + // The UnitTest object takes ownership of the given environment. + // + // This method can only be called from the main thread. + Environment* AddEnvironment(Environment* env); + + // Adds a TestPartResult to the current TestResult object. All + // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) + // eventually call this to report their results. The user code + // should use the assertion macros instead of calling this directly. + void AddTestPartResult(TestPartResult::Type result_type, + const char* file_name, + int line_number, + const std::string& message, + const std::string& os_stack_trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Adds a TestProperty to the current TestResult object when invoked from + // inside a test, to current TestCase's ad_hoc_test_result_ when invoked + // from SetUpTestCase or TearDownTestCase, or to the global property set + // when invoked elsewhere. If the result already contains a property with + // the same key, the value will be updated. + void RecordProperty(const std::string& key, const std::string& value); + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + TestCase* GetMutableTestCase(int i); + + // Accessors for the implementation object. + internal::UnitTestImpl* impl() { return impl_; } + const internal::UnitTestImpl* impl() const { return impl_; } + + // These classes and funcions are friends as they need to access private + // members of UnitTest. + friend class Test; + friend class internal::AssertHelper; + friend class internal::ScopedTrace; + friend class internal::StreamingListenerTest; + friend class internal::UnitTestRecordPropertyTestHelper; + friend Environment* AddGlobalTestEnvironment(Environment* env); + friend internal::UnitTestImpl* internal::GetUnitTestImpl(); + friend void internal::ReportFailureInUnknownLocation( + TestPartResult::Type result_type, + const std::string& message); + + // Creates an empty UnitTest. + UnitTest(); + + // D'tor + virtual ~UnitTest(); + + // Pushes a trace defined by SCOPED_TRACE() on to the per-thread + // Google Test trace stack. + void PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Pops a trace from the per-thread Google Test trace stack. + void PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_); + + // Protects mutable state in *impl_. This is mutable as some const + // methods need to lock it too. + mutable internal::Mutex mutex_; + + // Opaque implementation object. This field is never changed once + // the object is constructed. We don't mark it as const here, as + // doing so will cause a warning in the constructor of UnitTest. + // Mutable state in *impl_ is protected by mutex_. + internal::UnitTestImpl* impl_; + + // We disallow copying UnitTest. + GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTest); +}; + +// A convenient wrapper for adding an environment for the test +// program. +// +// You should call this before RUN_ALL_TESTS() is called, probably in +// main(). If you use gtest_main, you need to call this before main() +// starts for it to take effect. For example, you can define a global +// variable like this: +// +// testing::Environment* const foo_env = +// testing::AddGlobalTestEnvironment(new FooEnvironment); +// +// However, we strongly recommend you to write your own main() and +// call AddGlobalTestEnvironment() there, as relying on initialization +// of global variables makes the code harder to read and may cause +// problems when you register multiple environments from different +// translation units and the environments have dependencies among them +// (remember that the compiler doesn't guarantee the order in which +// global variables from different translation units are initialized). +inline Environment* AddGlobalTestEnvironment(Environment* env) { + return UnitTest::GetInstance()->AddEnvironment(env); +} + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +GTEST_API_ void InitGoogleTest(int* argc, char** argv); + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv); + +namespace internal { + +// FormatForComparison::Format(value) formats a +// value of type ToPrint that is an operand of a comparison assertion +// (e.g. ASSERT_EQ). OtherOperand is the type of the other operand in +// the comparison, and is used to help determine the best way to +// format the value. In particular, when the value is a C string +// (char pointer) and the other operand is an STL string object, we +// want to format the C string as a string, since we know it is +// compared by value with the string object. If the value is a char +// pointer but the other operand is not an STL string object, we don't +// know whether the pointer is supposed to point to a NUL-terminated +// string, and thus want to print it as a pointer to be safe. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// The default case. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint& value) { + return ::testing::PrintToString(value); + } +}; + +// Array. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint* value) { + return FormatForComparison::Format(value); + } +}; + +// By default, print C string as pointers to be safe, as we don't know +// whether they actually point to a NUL-terminated string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType) \ + template \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(static_cast(value)); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t); + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_ + +// If a C string is compared with an STL string object, we know it's meant +// to point to a NUL-terminated string, and thus can print it as a string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \ + template <> \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(value); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string); + +#if GTEST_HAS_GLOBAL_STRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::string); +#endif + +#if GTEST_HAS_GLOBAL_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::wstring); +#endif + +#if GTEST_HAS_STD_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring); +#endif + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_ + +// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc) +// operand to be used in a failure message. The type (but not value) +// of the other operand may affect the format. This allows us to +// print a char* as a raw pointer when it is compared against another +// char* or void*, and print it as a C string when it is compared +// against an std::string object, for example. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +std::string FormatForComparisonFailureMessage( + const T1& value, const T2& /* other_operand */) { + return FormatForComparison::Format(value); +} + +// The helper function for {ASSERT|EXPECT}_EQ. +template +AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual) { +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4389) // Temporarily disables warning on + // signed/unsigned mismatch. +#endif + + if (expected == actual) { + return AssertionSuccess(); + } + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif + + return EqFailure(expected_expression, + actual_expression, + FormatForComparisonFailureMessage(expected, actual), + FormatForComparisonFailureMessage(actual, expected), + false); +} + +// With this overloaded version, we allow anonymous enums to be used +// in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous enums +// can be implicitly cast to BiggestInt. +GTEST_API_ AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual); + +// The helper class for {ASSERT|EXPECT}_EQ. The template argument +// lhs_is_null_literal is true iff the first argument to ASSERT_EQ() +// is a null pointer literal. The following default implementation is +// for lhs_is_null_literal being false. +template +class EqHelper { + public: + // This templatized version is for the general case. + template + static AssertionResult Compare(const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } + + // With this overloaded version, we allow anonymous enums to be used + // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous + // enums can be implicitly cast to BiggestInt. + // + // Even though its body looks the same as the above version, we + // cannot merge the two, as it will make anonymous enums unhappy. + static AssertionResult Compare(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } +}; + +// This specialization is used when the first argument to ASSERT_EQ() +// is a null pointer literal, like NULL, false, or 0. +template <> +class EqHelper { + public: + // We define two overloaded versions of Compare(). The first + // version will be picked when the second argument to ASSERT_EQ() is + // NOT a pointer, e.g. ASSERT_EQ(0, AnIntFunction()) or + // EXPECT_EQ(false, a_bool). + template + static AssertionResult Compare( + const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual, + // The following line prevents this overload from being considered if T2 + // is not a pointer type. We need this because ASSERT_EQ(NULL, my_ptr) + // expands to Compare("", "", NULL, my_ptr), which requires a conversion + // to match the Secret* in the other overload, which would otherwise make + // this template match better. + typename EnableIf::value>::type* = 0) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } + + // This version will be picked when the second argument to ASSERT_EQ() is a + // pointer, e.g. ASSERT_EQ(NULL, a_pointer). + template + static AssertionResult Compare( + const char* expected_expression, + const char* actual_expression, + // We used to have a second template parameter instead of Secret*. That + // template parameter would deduce to 'long', making this a better match + // than the first overload even without the first overload's EnableIf. + // Unfortunately, gcc with -Wconversion-null warns when "passing NULL to + // non-pointer argument" (even a deduced integral argument), so the old + // implementation caused warnings in user code. + Secret* /* expected (NULL) */, + T* actual) { + // We already know that 'expected' is a null pointer. + return CmpHelperEQ(expected_expression, actual_expression, + static_cast(NULL), actual); + } +}; + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste +// of similar code. +// +// For each templatized helper function, we also define an overloaded +// version for BiggestInt in order to reduce code bloat and allow +// anonymous enums to be used with {ASSERT|EXPECT}_?? when compiled +// with gcc 4. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ +template \ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + const T1& val1, const T2& val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + return AssertionFailure() \ + << "Expected: (" << expr1 << ") " #op " (" << expr2\ + << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ + << " vs " << FormatForComparisonFailureMessage(val2, val1);\ + }\ +}\ +GTEST_API_ AssertionResult CmpHelper##op_name(\ + const char* expr1, const char* expr2, BiggestInt val1, BiggestInt val2) + +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// Implements the helper function for {ASSERT|EXPECT}_NE +GTEST_IMPL_CMP_HELPER_(NE, !=); +// Implements the helper function for {ASSERT|EXPECT}_LE +GTEST_IMPL_CMP_HELPER_(LE, <=); +// Implements the helper function for {ASSERT|EXPECT}_LT +GTEST_IMPL_CMP_HELPER_(LT, <); +// Implements the helper function for {ASSERT|EXPECT}_GE +GTEST_IMPL_CMP_HELPER_(GE, >=); +// Implements the helper function for {ASSERT|EXPECT}_GT +GTEST_IMPL_CMP_HELPER_(GT, >); + +#undef GTEST_IMPL_CMP_HELPER_ + +// The helper function for {ASSERT|EXPECT}_STREQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual); + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual); + +// The helper function for {ASSERT|EXPECT}_STRNE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + + +// Helper function for *_STREQ on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const wchar_t* expected, + const wchar_t* actual); + +// Helper function for *_STRNE on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2); + +} // namespace internal + +// IsSubstring() and IsNotSubstring() are intended to be used as the +// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by +// themselves. They check whether needle is a substring of haystack +// (NULL is considered a substring of itself only), and return an +// appropriate error message when they fail. +// +// The {needle,haystack}_expr arguments are the stringified +// expressions that generated the two real arguments. +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); + +#if GTEST_HAS_STD_WSTRING +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +// Helper template function for comparing floating-points. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +AssertionResult CmpHelperFloatingPointEQ(const char* expected_expression, + const char* actual_expression, + RawType expected, + RawType actual) { + const FloatingPoint lhs(expected), rhs(actual); + + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + ::std::stringstream expected_ss; + expected_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << expected; + + ::std::stringstream actual_ss; + actual_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << actual; + + return EqFailure(expected_expression, + actual_expression, + StringStreamToString(&expected_ss), + StringStreamToString(&actual_ss), + false); +} + +// Helper function for implementing ASSERT_NEAR. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// A class that enables one to stream messages to assertion macros +class GTEST_API_ AssertHelper { + public: + // Constructor. + AssertHelper(TestPartResult::Type type, + const char* file, + int line, + const char* message); + ~AssertHelper(); + + // Message assignment is a semantic trick to enable assertion + // streaming; see the GTEST_MESSAGE_ macro below. + void operator=(const Message& message) const; + + private: + // We put our data in a struct so that the size of the AssertHelper class can + // be as small as possible. This is important because gcc is incapable of + // re-using stack space even for temporary variables, so every EXPECT_EQ + // reserves stack space for another AssertHelper. + struct AssertHelperData { + AssertHelperData(TestPartResult::Type t, + const char* srcfile, + int line_num, + const char* msg) + : type(t), file(srcfile), line(line_num), message(msg) { } + + TestPartResult::Type const type; + const char* const file; + int const line; + std::string const message; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelperData); + }; + + AssertHelperData* const data_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelper); +}; + +} // namespace internal + +#if GTEST_HAS_PARAM_TEST +// The pure interface class that all value-parameterized tests inherit from. +// A value-parameterized class must inherit from both ::testing::Test and +// ::testing::WithParamInterface. In most cases that just means inheriting +// from ::testing::TestWithParam, but more complicated test hierarchies +// may need to inherit from Test and WithParamInterface at different levels. +// +// This interface has support for accessing the test parameter value via +// the GetParam() method. +// +// Use it with one of the parameter generator defining functions, like Range(), +// Values(), ValuesIn(), Bool(), and Combine(). +// +// class FooTest : public ::testing::TestWithParam { +// protected: +// FooTest() { +// // Can use GetParam() here. +// } +// virtual ~FooTest() { +// // Can use GetParam() here. +// } +// virtual void SetUp() { +// // Can use GetParam() here. +// } +// virtual void TearDown { +// // Can use GetParam() here. +// } +// }; +// TEST_P(FooTest, DoesBar) { +// // Can use GetParam() method here. +// Foo foo; +// ASSERT_TRUE(foo.DoesBar(GetParam())); +// } +// INSTANTIATE_TEST_CASE_P(OneToTenRange, FooTest, ::testing::Range(1, 10)); + +template +class WithParamInterface { + public: + typedef T ParamType; + virtual ~WithParamInterface() {} + + // The current parameter value. Is also available in the test fixture's + // constructor. This member function is non-static, even though it only + // references static data, to reduce the opportunity for incorrect uses + // like writing 'WithParamInterface::GetParam()' for a test that + // uses a fixture whose parameter type is int. + const ParamType& GetParam() const { + GTEST_CHECK_(parameter_ != NULL) + << "GetParam() can only be called inside a value-parameterized test " + << "-- did you intend to write TEST_P instead of TEST_F?"; + return *parameter_; + } + + private: + // Sets parameter value. The caller is responsible for making sure the value + // remains alive and unchanged throughout the current test. + static void SetParam(const ParamType* parameter) { + parameter_ = parameter; + } + + // Static value used for accessing parameter during a test lifetime. + static const ParamType* parameter_; + + // TestClass must be a subclass of WithParamInterface and Test. + template friend class internal::ParameterizedTestFactory; +}; + +template +const T* WithParamInterface::parameter_ = NULL; + +// Most value-parameterized classes can ignore the existence of +// WithParamInterface, and can just inherit from ::testing::TestWithParam. + +template +class TestWithParam : public Test, public WithParamInterface { +}; + +#endif // GTEST_HAS_PARAM_TEST + +// Macros for indicating success/failure in test code. + +// ADD_FAILURE unconditionally adds a failure to the current test. +// SUCCEED generates a success - it doesn't automatically make the +// current test successful, as a test is only successful when it has +// no failure. +// +// EXPECT_* verifies that a certain condition is satisfied. If not, +// it behaves like ADD_FAILURE. In particular: +// +// EXPECT_TRUE verifies that a Boolean condition is true. +// EXPECT_FALSE verifies that a Boolean condition is false. +// +// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except +// that they will also abort the current function on failure. People +// usually want the fail-fast behavior of FAIL and ASSERT_*, but those +// writing data-driven tests often find themselves using ADD_FAILURE +// and EXPECT_* more. + +// Generates a nonfatal failure with a generic message. +#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed") + +// Generates a nonfatal failure at the given source file location with +// a generic message. +#define ADD_FAILURE_AT(file, line) \ + GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kNonFatalFailure) + +// Generates a fatal failure with a generic message. +#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed") + +// Define this macro to 1 to omit the definition of FAIL(), which is a +// generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_FAIL +# define FAIL() GTEST_FAIL() +#endif + +// Generates a success with a generic message. +#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded") + +// Define this macro to 1 to omit the definition of SUCCEED(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_SUCCEED +# define SUCCEED() GTEST_SUCCEED() +#endif + +// Macros for testing exceptions. +// +// * {ASSERT|EXPECT}_THROW(statement, expected_exception): +// Tests that the statement throws the expected exception. +// * {ASSERT|EXPECT}_NO_THROW(statement): +// Tests that the statement doesn't throw any exception. +// * {ASSERT|EXPECT}_ANY_THROW(statement): +// Tests that the statement throws an exception. + +#define EXPECT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_) +#define EXPECT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define EXPECT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define ASSERT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_) +#define ASSERT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_) +#define ASSERT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_) + +// Boolean assertions. Condition can be either a Boolean expression or an +// AssertionResult. For more information on how to use AssertionResult with +// these macros see comments on that class. +#define EXPECT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_NONFATAL_FAILURE_) +#define EXPECT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_NONFATAL_FAILURE_) +#define ASSERT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_FATAL_FAILURE_) +#define ASSERT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_FATAL_FAILURE_) + +// Includes the auto-generated header that implements a family of +// generic predicate assertion macros. +#include "gtest/gtest_pred_impl.h" + +// Macros for testing equalities and inequalities. +// +// * {ASSERT|EXPECT}_EQ(expected, actual): Tests that expected == actual +// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2 +// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2 +// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2 +// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2 +// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2 +// +// When they are not, Google Test prints both the tested expressions and +// their actual values. The values must be compatible built-in types, +// or you will get a compiler error. By "compatible" we mean that the +// values can be compared by the respective operator. +// +// Note: +// +// 1. It is possible to make a user-defined type work with +// {ASSERT|EXPECT}_??(), but that requires overloading the +// comparison operators and is thus discouraged by the Google C++ +// Usage Guide. Therefore, you are advised to use the +// {ASSERT|EXPECT}_TRUE() macro to assert that two objects are +// equal. +// +// 2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on +// pointers (in particular, C strings). Therefore, if you use it +// with two C strings, you are testing how their locations in memory +// are related, not how their content is related. To compare two C +// strings by content, use {ASSERT|EXPECT}_STR*(). +// +// 3. {ASSERT|EXPECT}_EQ(expected, actual) is preferred to +// {ASSERT|EXPECT}_TRUE(expected == actual), as the former tells you +// what the actual value is when it fails, and similarly for the +// other comparisons. +// +// 4. Do not depend on the order in which {ASSERT|EXPECT}_??() +// evaluate their arguments, which is undefined. +// +// 5. These macros evaluate their arguments exactly once. +// +// Examples: +// +// EXPECT_NE(5, Foo()); +// EXPECT_EQ(NULL, a_pointer); +// ASSERT_LT(i, array_size); +// ASSERT_GT(records.size(), 0) << "There is no record left."; + +#define EXPECT_EQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal:: \ + EqHelper::Compare, \ + expected, actual) +#define EXPECT_NE(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, expected, actual) +#define EXPECT_LE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define EXPECT_LT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define EXPECT_GE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define EXPECT_GT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +#define GTEST_ASSERT_EQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal:: \ + EqHelper::Compare, \ + expected, actual) +#define GTEST_ASSERT_NE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define GTEST_ASSERT_LE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define GTEST_ASSERT_LT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define GTEST_ASSERT_GE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define GTEST_ASSERT_GT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +// Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of +// ASSERT_XY(), which clashes with some users' own code. + +#if !GTEST_DONT_DEFINE_ASSERT_EQ +# define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_NE +# define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LE +# define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LT +# define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GE +# define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GT +# define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2) +#endif + +// C-string Comparisons. All tests treat NULL and any non-NULL string +// as different. Two NULLs are equal. +// +// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 +// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2 +// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case +// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case +// +// For wide or narrow string objects, you can use the +// {ASSERT|EXPECT}_??() macros. +// +// Don't depend on the order in which the arguments are evaluated, +// which is undefined. +// +// These macros evaluate their arguments exactly once. + +#define EXPECT_STREQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual) +#define EXPECT_STRNE(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define EXPECT_STRCASEEQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual) +#define EXPECT_STRCASENE(s1, s2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +#define ASSERT_STREQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual) +#define ASSERT_STRNE(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define ASSERT_STRCASEEQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual) +#define ASSERT_STRCASENE(s1, s2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +// Macros for comparing floating-point numbers. +// +// * {ASSERT|EXPECT}_FLOAT_EQ(expected, actual): +// Tests that two float values are almost equal. +// * {ASSERT|EXPECT}_DOUBLE_EQ(expected, actual): +// Tests that two double values are almost equal. +// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error): +// Tests that v1 and v2 are within the given distance to each other. +// +// Google Test uses ULP-based comparison to automatically pick a default +// error bound that is appropriate for the operands. See the +// FloatingPoint template class in gtest-internal.h if you are +// interested in the implementation details. + +#define EXPECT_FLOAT_EQ(expected, actual)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define EXPECT_DOUBLE_EQ(expected, actual)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define ASSERT_FLOAT_EQ(expected, actual)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define ASSERT_DOUBLE_EQ(expected, actual)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define EXPECT_NEAR(val1, val2, abs_error)\ + EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +#define ASSERT_NEAR(val1, val2, abs_error)\ + ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +// These predicate format functions work on floating-point values, and +// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g. +// +// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0); + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +GTEST_API_ AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2); +GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2); + + +#if GTEST_OS_WINDOWS + +// Macros that test for HRESULT failure and success, these are only useful +// on Windows, and rely on Windows SDK macros and APIs to compile. +// +// * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr) +// +// When expr unexpectedly fails or succeeds, Google Test prints the +// expected result and the actual result with both a human-readable +// string representation of the error, if available, as well as the +// hex result code. +# define EXPECT_HRESULT_SUCCEEDED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +# define ASSERT_HRESULT_SUCCEEDED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +# define EXPECT_HRESULT_FAILED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +# define ASSERT_HRESULT_FAILED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +#endif // GTEST_OS_WINDOWS + +// Macros that execute statement and check that it doesn't generate new fatal +// failures in the current thread. +// +// * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement); +// +// Examples: +// +// EXPECT_NO_FATAL_FAILURE(Process()); +// ASSERT_NO_FATAL_FAILURE(Process()) << "Process() failed"; +// +#define ASSERT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_FATAL_FAILURE_) +#define EXPECT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_) + +// Causes a trace (including the source file path, the current line +// number, and the given message) to be included in every test failure +// message generated by code in the current scope. The effect is +// undone when the control leaves the current scope. +// +// The message argument can be anything streamable to std::ostream. +// +// In the implementation, we include the current line number as part +// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s +// to appear in the same block - as long as they are on different +// lines. +#define SCOPED_TRACE(message) \ + ::testing::internal::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\ + __FILE__, __LINE__, ::testing::Message() << (message)) + +// Compile-time assertion for type equality. +// StaticAssertTypeEq() compiles iff type1 and type2 are +// the same type. The value it returns is not interesting. +// +// Instead of making StaticAssertTypeEq a class template, we make it a +// function template that invokes a helper class template. This +// prevents a user from misusing StaticAssertTypeEq by +// defining objects of that type. +// +// CAVEAT: +// +// When used inside a method of a class template, +// StaticAssertTypeEq() is effective ONLY IF the method is +// instantiated. For example, given: +// +// template class Foo { +// public: +// void Bar() { testing::StaticAssertTypeEq(); } +// }; +// +// the code: +// +// void Test1() { Foo foo; } +// +// will NOT generate a compiler error, as Foo::Bar() is never +// actually instantiated. Instead, you need: +// +// void Test2() { Foo foo; foo.Bar(); } +// +// to cause a compiler error. +template +bool StaticAssertTypeEq() { + (void)internal::StaticAssertTypeEqHelper(); + return true; +} + +// Defines a test. +// +// The first parameter is the name of the test case, and the second +// parameter is the name of the test within the test case. +// +// The convention is to end the test case name with "Test". For +// example, a test case for the Foo class can be named FooTest. +// +// The user should put his test code between braces after using this +// macro. Example: +// +// TEST(FooTest, InitializesCorrectly) { +// Foo foo; +// EXPECT_TRUE(foo.StatusIsOK()); +// } + +// Note that we call GetTestTypeId() instead of GetTypeId< +// ::testing::Test>() here to get the type ID of testing::Test. This +// is to work around a suspected linker bug when using Google Test as +// a framework on Mac OS X. The bug causes GetTypeId< +// ::testing::Test>() to return different values depending on whether +// the call is from the Google Test framework itself or from user test +// code. GetTestTypeId() is guaranteed to always return the same +// value, as it always calls GetTypeId<>() from the Google Test +// framework. +#define GTEST_TEST(test_case_name, test_name)\ + GTEST_TEST_(test_case_name, test_name, \ + ::testing::Test, ::testing::internal::GetTestTypeId()) + +// Define this macro to 1 to omit the definition of TEST(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_TEST +# define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name) +#endif + +// Defines a test that uses a test fixture. +// +// The first parameter is the name of the test fixture class, which +// also doubles as the test case name. The second parameter is the +// name of the test within the test case. +// +// A test fixture class must be declared earlier. The user should put +// his test code between braces after using this macro. Example: +// +// class FooTest : public testing::Test { +// protected: +// virtual void SetUp() { b_.AddElement(3); } +// +// Foo a_; +// Foo b_; +// }; +// +// TEST_F(FooTest, InitializesCorrectly) { +// EXPECT_TRUE(a_.StatusIsOK()); +// } +// +// TEST_F(FooTest, ReturnsElementCountCorrectly) { +// EXPECT_EQ(0, a_.size()); +// EXPECT_EQ(1, b_.size()); +// } + +#define TEST_F(test_fixture, test_name)\ + GTEST_TEST_(test_fixture, test_name, test_fixture, \ + ::testing::internal::GetTypeId()) + +} // namespace testing + +// Use this function in main() to run all tests. It returns 0 if all +// tests are successful, or 1 otherwise. +// +// RUN_ALL_TESTS() should be invoked after the command line has been +// parsed by InitGoogleTest(). +// +// This function was formerly a macro; thus, it is in the global +// namespace and has an all-caps name. +int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_; + +inline int RUN_ALL_TESTS() { + return ::testing::UnitTest::GetInstance()->Run(); +} + +#endif // GTEST_INCLUDE_GTEST_GTEST_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest_pred_impl.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest_pred_impl.h new file mode 100644 index 00000000..30ae712f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest_pred_impl.h @@ -0,0 +1,358 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file is AUTOMATICALLY GENERATED on 10/31/2011 by command +// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! +// +// Implements a family of generic predicate assertion macros. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +// Makes sure this header is not included before gtest.h. +#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ +# error Do not include gtest_pred_impl.h directly. Include gtest.h instead. +#endif // GTEST_INCLUDE_GTEST_GTEST_H_ + +// This header implements a family of generic predicate assertion +// macros: +// +// ASSERT_PRED_FORMAT1(pred_format, v1) +// ASSERT_PRED_FORMAT2(pred_format, v1, v2) +// ... +// +// where pred_format is a function or functor that takes n (in the +// case of ASSERT_PRED_FORMATn) values and their source expression +// text, and returns a testing::AssertionResult. See the definition +// of ASSERT_EQ in gtest.h for an example. +// +// If you don't care about formatting, you can use the more +// restrictive version: +// +// ASSERT_PRED1(pred, v1) +// ASSERT_PRED2(pred, v1, v2) +// ... +// +// where pred is an n-ary function or functor that returns bool, +// and the values v1, v2, ..., must support the << operator for +// streaming to std::ostream. +// +// We also define the EXPECT_* variations. +// +// For now we only support predicates whose arity is at most 5. +// Please email googletestframework@googlegroups.com if you need +// support for higher arities. + +// GTEST_ASSERT_ is the basic statement to which all of the assertions +// in this file reduce. Don't use this in your code. + +#define GTEST_ASSERT_(expression, on_failure) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar = (expression)) \ + ; \ + else \ + on_failure(gtest_ar.failure_message()) + + +// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +template +AssertionResult AssertPred1Helper(const char* pred_text, + const char* e1, + Pred pred, + const T1& v1) { + if (pred(v1)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. +// Don't use this in your code. +#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, v1), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +#define GTEST_PRED1_(pred, v1, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, \ + #v1, \ + pred, \ + v1), on_failure) + +// Unary predicate assertion macros. +#define EXPECT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED1(pred, v1) \ + GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED1(pred, v1) \ + GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +template +AssertionResult AssertPred2Helper(const char* pred_text, + const char* e1, + const char* e2, + Pred pred, + const T1& v1, + const T2& v2) { + if (pred(v1, v2)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. +// Don't use this in your code. +#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +#define GTEST_PRED2_(pred, v1, v2, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, \ + #v1, \ + #v2, \ + pred, \ + v1, \ + v2), on_failure) + +// Binary predicate assertion macros. +#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +template +AssertionResult AssertPred3Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3) { + if (pred(v1, v2, v3)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. +// Don't use this in your code. +#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +#define GTEST_PRED3_(pred, v1, v2, v3, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred3Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + pred, \ + v1, \ + v2, \ + v3), on_failure) + +// Ternary predicate assertion macros. +#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +template +AssertionResult AssertPred4Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4) { + if (pred(v1, v2, v3, v4)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ", " + << e4 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. +// Don't use this in your code. +#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4), on_failure) + +// 4-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +template +AssertionResult AssertPred5Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + const char* e5, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4, + const T5& v5) { + if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ", " + << e4 << ", " + << e5 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4 + << "\n" << e5 << " evaluates to " << v5; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. +// Don't use this in your code. +#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + #v5, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4, \ + v5), on_failure) + +// 5-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) + + + +#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest_prod.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest_prod.h new file mode 100644 index 00000000..da80ddc6 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/gtest_prod.h @@ -0,0 +1,58 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Google C++ Testing Framework definitions useful in production code. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_ + +// When you need to test the private or protected members of a class, +// use the FRIEND_TEST macro to declare your tests as friends of the +// class. For example: +// +// class MyClass { +// private: +// void MyMethod(); +// FRIEND_TEST(MyClassTest, MyMethod); +// }; +// +// class MyClassTest : public testing::Test { +// // ... +// }; +// +// TEST_F(MyClassTest, MyMethod) { +// // Can call MyClass::MyMethod() here. +// } + +#define FRIEND_TEST(test_case_name, test_name)\ +friend class test_case_name##_##test_name##_Test + +#endif // GTEST_INCLUDE_GTEST_GTEST_PROD_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-death-test-internal.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-death-test-internal.h new file mode 100644 index 00000000..2b3a78f5 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-death-test-internal.h @@ -0,0 +1,319 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines internal utilities needed for implementing +// death tests. They are subject to change without notice. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + +#include "gtest/internal/gtest-internal.h" + +#include + +namespace testing { +namespace internal { + +GTEST_DECLARE_string_(internal_run_death_test); + +// Names of the flags (needed for parsing Google Test flags). +const char kDeathTestStyleFlag[] = "death_test_style"; +const char kDeathTestUseFork[] = "death_test_use_fork"; +const char kInternalRunDeathTestFlag[] = "internal_run_death_test"; + +#if GTEST_HAS_DEATH_TEST + +// DeathTest is a class that hides much of the complexity of the +// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method +// returns a concrete class that depends on the prevailing death test +// style, as defined by the --gtest_death_test_style and/or +// --gtest_internal_run_death_test flags. + +// In describing the results of death tests, these terms are used with +// the corresponding definitions: +// +// exit status: The integer exit information in the format specified +// by wait(2) +// exit code: The integer code passed to exit(3), _exit(2), or +// returned from main() +class GTEST_API_ DeathTest { + public: + // Create returns false if there was an error determining the + // appropriate action to take for the current death test; for example, + // if the gtest_death_test_style flag is set to an invalid value. + // The LastMessage method will return a more detailed message in that + // case. Otherwise, the DeathTest pointer pointed to by the "test" + // argument is set. If the death test should be skipped, the pointer + // is set to NULL; otherwise, it is set to the address of a new concrete + // DeathTest object that controls the execution of the current test. + static bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test); + DeathTest(); + virtual ~DeathTest() { } + + // A helper class that aborts a death test when it's deleted. + class ReturnSentinel { + public: + explicit ReturnSentinel(DeathTest* test) : test_(test) { } + ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); } + private: + DeathTest* const test_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ReturnSentinel); + } GTEST_ATTRIBUTE_UNUSED_; + + // An enumeration of possible roles that may be taken when a death + // test is encountered. EXECUTE means that the death test logic should + // be executed immediately. OVERSEE means that the program should prepare + // the appropriate environment for a child process to execute the death + // test, then wait for it to complete. + enum TestRole { OVERSEE_TEST, EXECUTE_TEST }; + + // An enumeration of the three reasons that a test might be aborted. + enum AbortReason { + TEST_ENCOUNTERED_RETURN_STATEMENT, + TEST_THREW_EXCEPTION, + TEST_DID_NOT_DIE + }; + + // Assumes one of the above roles. + virtual TestRole AssumeRole() = 0; + + // Waits for the death test to finish and returns its status. + virtual int Wait() = 0; + + // Returns true if the death test passed; that is, the test process + // exited during the test, its exit status matches a user-supplied + // predicate, and its stderr output matches a user-supplied regular + // expression. + // The user-supplied predicate may be a macro expression rather + // than a function pointer or functor, or else Wait and Passed could + // be combined. + virtual bool Passed(bool exit_status_ok) = 0; + + // Signals that the death test did not die as expected. + virtual void Abort(AbortReason reason) = 0; + + // Returns a human-readable outcome message regarding the outcome of + // the last death test. + static const char* LastMessage(); + + static void set_last_death_test_message(const std::string& message); + + private: + // A string containing a description of the outcome of the last death test. + static std::string last_death_test_message_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest); +}; + +// Factory interface for death tests. May be mocked out for testing. +class DeathTestFactory { + public: + virtual ~DeathTestFactory() { } + virtual bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test) = 0; +}; + +// A concrete DeathTestFactory implementation for normal use. +class DefaultDeathTestFactory : public DeathTestFactory { + public: + virtual bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test); +}; + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +GTEST_API_ bool ExitedUnsuccessfully(int exit_status); + +// Traps C++ exceptions escaping statement and reports them as test +// failures. Note that trapping SEH exceptions is not implemented here. +# if GTEST_HAS_EXCEPTIONS +# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (const ::std::exception& gtest_exception) { \ + fprintf(\ + stderr, \ + "\n%s: Caught std::exception-derived exception escaping the " \ + "death test statement. Exception message: %s\n", \ + ::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \ + gtest_exception.what()); \ + fflush(stderr); \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } catch (...) { \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } + +# else +# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) + +# endif + +// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*, +// ASSERT_EXIT*, and EXPECT_EXIT*. +# define GTEST_DEATH_TEST_(statement, predicate, regex, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + const ::testing::internal::RE& gtest_regex = (regex); \ + ::testing::internal::DeathTest* gtest_dt; \ + if (!::testing::internal::DeathTest::Create(#statement, >est_regex, \ + __FILE__, __LINE__, >est_dt)) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + if (gtest_dt != NULL) { \ + ::testing::internal::scoped_ptr< ::testing::internal::DeathTest> \ + gtest_dt_ptr(gtest_dt); \ + switch (gtest_dt->AssumeRole()) { \ + case ::testing::internal::DeathTest::OVERSEE_TEST: \ + if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + break; \ + case ::testing::internal::DeathTest::EXECUTE_TEST: { \ + ::testing::internal::DeathTest::ReturnSentinel \ + gtest_sentinel(gtest_dt); \ + GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \ + gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \ + break; \ + } \ + default: \ + break; \ + } \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__): \ + fail(::testing::internal::DeathTest::LastMessage()) +// The symbol "fail" here expands to something into which a message +// can be streamed. + +// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in +// NDEBUG mode. In this case we need the statements to be executed, the regex is +// ignored, and the macro must accept a streamed message even though the message +// is never printed. +# define GTEST_EXECUTE_STATEMENT_(statement, regex) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } else \ + ::testing::Message() + +// A class representing the parsed contents of the +// --gtest_internal_run_death_test flag, as it existed when +// RUN_ALL_TESTS was called. +class InternalRunDeathTestFlag { + public: + InternalRunDeathTestFlag(const std::string& a_file, + int a_line, + int an_index, + int a_write_fd) + : file_(a_file), line_(a_line), index_(an_index), + write_fd_(a_write_fd) {} + + ~InternalRunDeathTestFlag() { + if (write_fd_ >= 0) + posix::Close(write_fd_); + } + + const std::string& file() const { return file_; } + int line() const { return line_; } + int index() const { return index_; } + int write_fd() const { return write_fd_; } + + private: + std::string file_; + int line_; + int index_; + int write_fd_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(InternalRunDeathTestFlag); +}; + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag(); + +#else // GTEST_HAS_DEATH_TEST + +// This macro is used for implementing macros such as +// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where +// death tests are not supported. Those macros must compile on such systems +// iff EXPECT_DEATH and ASSERT_DEATH compile with the same parameters on +// systems that support death tests. This allows one to write such a macro +// on a system that does not support death tests and be sure that it will +// compile on a death-test supporting system. +// +// Parameters: +// statement - A statement that a macro such as EXPECT_DEATH would test +// for program termination. This macro has to make sure this +// statement is compiled but not executed, to ensure that +// EXPECT_DEATH_IF_SUPPORTED compiles with a certain +// parameter iff EXPECT_DEATH compiles with it. +// regex - A regex that a macro such as EXPECT_DEATH would use to test +// the output of statement. This parameter has to be +// compiled but not evaluated by this macro, to ensure that +// this macro only accepts expressions that a macro such as +// EXPECT_DEATH would accept. +// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED +// and a return statement for ASSERT_DEATH_IF_SUPPORTED. +// This ensures that ASSERT_DEATH_IF_SUPPORTED will not +// compile inside functions where ASSERT_DEATH doesn't +// compile. +// +// The branch that has an always false condition is used to ensure that +// statement and regex are compiled (and thus syntactically correct) but +// never executed. The unreachable code macro protects the terminator +// statement from generating an 'unreachable code' warning in case +// statement unconditionally returns or throws. The Message constructor at +// the end allows the syntax of streaming additional messages into the +// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH. +# define GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, terminator) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_LOG_(WARNING) \ + << "Death tests are not supported on this platform.\n" \ + << "Statement '" #statement "' cannot be verified."; \ + } else if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::RE::PartialMatch(".*", (regex)); \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + terminator; \ + } else \ + ::testing::Message() + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-filepath.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-filepath.h new file mode 100644 index 00000000..7a13b4b0 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-filepath.h @@ -0,0 +1,206 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: keith.ray@gmail.com (Keith Ray) +// +// Google Test filepath utilities +// +// This header file declares classes and functions used internally by +// Google Test. They are subject to change without notice. +// +// This file is #included in . +// Do not include this header file separately! + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ + +#include "gtest/internal/gtest-string.h" + +namespace testing { +namespace internal { + +// FilePath - a class for file and directory pathname manipulation which +// handles platform-specific conventions (like the pathname separator). +// Used for helper functions for naming files in a directory for xml output. +// Except for Set methods, all methods are const or static, which provides an +// "immutable value object" -- useful for peace of mind. +// A FilePath with a value ending in a path separator ("like/this/") represents +// a directory, otherwise it is assumed to represent a file. In either case, +// it may or may not represent an actual file or directory in the file system. +// Names are NOT checked for syntax correctness -- no checking for illegal +// characters, malformed paths, etc. + +class GTEST_API_ FilePath { + public: + FilePath() : pathname_("") { } + FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { } + + explicit FilePath(const std::string& pathname) : pathname_(pathname) { + Normalize(); + } + + FilePath& operator=(const FilePath& rhs) { + Set(rhs); + return *this; + } + + void Set(const FilePath& rhs) { + pathname_ = rhs.pathname_; + } + + const std::string& string() const { return pathname_; } + const char* c_str() const { return pathname_.c_str(); } + + // Returns the current working directory, or "" if unsuccessful. + static FilePath GetCurrentDir(); + + // Given directory = "dir", base_name = "test", number = 0, + // extension = "xml", returns "dir/test.xml". If number is greater + // than zero (e.g., 12), returns "dir/test_12.xml". + // On Windows platform, uses \ as the separator rather than /. + static FilePath MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension); + + // Given directory = "dir", relative_path = "test.xml", + // returns "dir/test.xml". + // On Windows, uses \ as the separator rather than /. + static FilePath ConcatPaths(const FilePath& directory, + const FilePath& relative_path); + + // Returns a pathname for a file that does not currently exist. The pathname + // will be directory/base_name.extension or + // directory/base_name_.extension if directory/base_name.extension + // already exists. The number will be incremented until a pathname is found + // that does not already exist. + // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. + // There could be a race condition if two or more processes are calling this + // function at the same time -- they could both pick the same filename. + static FilePath GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension); + + // Returns true iff the path is "". + bool IsEmpty() const { return pathname_.empty(); } + + // If input name has a trailing separator character, removes it and returns + // the name, otherwise return the name string unmodified. + // On Windows platform, uses \ as the separator, other platforms use /. + FilePath RemoveTrailingPathSeparator() const; + + // Returns a copy of the FilePath with the directory part removed. + // Example: FilePath("path/to/file").RemoveDirectoryName() returns + // FilePath("file"). If there is no directory part ("just_a_file"), it returns + // the FilePath unmodified. If there is no file part ("just_a_dir/") it + // returns an empty FilePath (""). + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveDirectoryName() const; + + // RemoveFileName returns the directory path with the filename removed. + // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". + // If the FilePath is "a_file" or "/a_file", RemoveFileName returns + // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does + // not have a file, like "just/a/dir/", it returns the FilePath unmodified. + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveFileName() const; + + // Returns a copy of the FilePath with the case-insensitive extension removed. + // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns + // FilePath("dir/file"). If a case-insensitive extension is not + // found, returns a copy of the original FilePath. + FilePath RemoveExtension(const char* extension) const; + + // Creates directories so that path exists. Returns true if successful or if + // the directories already exist; returns false if unable to create + // directories for any reason. Will also return false if the FilePath does + // not represent a directory (that is, it doesn't end with a path separator). + bool CreateDirectoriesRecursively() const; + + // Create the directory so that path exists. Returns true if successful or + // if the directory already exists; returns false if unable to create the + // directory for any reason, including if the parent directory does not + // exist. Not named "CreateDirectory" because that's a macro on Windows. + bool CreateFolder() const; + + // Returns true if FilePath describes something in the file-system, + // either a file, directory, or whatever, and that something exists. + bool FileOrDirectoryExists() const; + + // Returns true if pathname describes a directory in the file-system + // that exists. + bool DirectoryExists() const; + + // Returns true if FilePath ends with a path separator, which indicates that + // it is intended to represent a directory. Returns false otherwise. + // This does NOT check that a directory (or file) actually exists. + bool IsDirectory() const; + + // Returns true if pathname describes a root directory. (Windows has one + // root directory per disk drive.) + bool IsRootDirectory() const; + + // Returns true if pathname describes an absolute path. + bool IsAbsolutePath() const; + + private: + // Replaces multiple consecutive separators with a single separator. + // For example, "bar///foo" becomes "bar/foo". Does not eliminate other + // redundancies that might be in a pathname involving "." or "..". + // + // A pathname with multiple consecutive separators may occur either through + // user error or as a result of some scripts or APIs that generate a pathname + // with a trailing separator. On other platforms the same API or script + // may NOT generate a pathname with a trailing "/". Then elsewhere that + // pathname may have another "/" and pathname components added to it, + // without checking for the separator already being there. + // The script language and operating system may allow paths like "foo//bar" + // but some of the functions in FilePath will not handle that correctly. In + // particular, RemoveTrailingPathSeparator() only removes one separator, and + // it is called in CreateDirectoriesRecursively() assuming that it will change + // a pathname from directory syntax (trailing separator) to filename syntax. + // + // On Windows this method also replaces the alternate path separator '/' with + // the primary path separator '\\', so that for example "bar\\/\\foo" becomes + // "bar\\foo". + + void Normalize(); + + // Returns a pointer to the last occurence of a valid path separator in + // the FilePath. On Windows, for example, both '/' and '\' are valid path + // separators. Returns NULL if no path separator was found. + const char* FindLastPathSeparator() const; + + std::string pathname_; +}; // class FilePath + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-internal.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-internal.h new file mode 100644 index 00000000..0dcc3a31 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-internal.h @@ -0,0 +1,1158 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file declares functions and macros used internally by +// Google Test. They are subject to change without notice. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ + +#include "gtest/internal/gtest-port.h" + +#if GTEST_OS_LINUX +# include +# include +# include +# include +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +# include +#endif + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest-message.h" +#include "gtest/internal/gtest-string.h" +#include "gtest/internal/gtest-filepath.h" +#include "gtest/internal/gtest-type-util.h" + +// Due to C++ preprocessor weirdness, we need double indirection to +// concatenate two tokens when one of them is __LINE__. Writing +// +// foo ## __LINE__ +// +// will result in the token foo__LINE__, instead of foo followed by +// the current line number. For more details, see +// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6 +#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar) +#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar + +class ProtocolMessage; +namespace proto2 { class Message; } + +namespace testing { + +// Forward declarations. + +class AssertionResult; // Result of an assertion. +class Message; // Represents a failure message. +class Test; // Represents a test. +class TestInfo; // Information about a test. +class TestPartResult; // Result of a test part. +class UnitTest; // A collection of test cases. + +template +::std::string PrintToString(const T& value); + +namespace internal { + +struct TraceInfo; // Information about a trace point. +class ScopedTrace; // Implements scoped trace. +class TestInfoImpl; // Opaque implementation of TestInfo +class UnitTestImpl; // Opaque implementation of UnitTest + +// How many times InitGoogleTest() has been called. +GTEST_API_ extern int g_init_gtest_count; + +// The text used in failure messages to indicate the start of the +// stack trace. +GTEST_API_ extern const char kStackTraceMarker[]; + +// Two overloaded helpers for checking at compile time whether an +// expression is a null pointer literal (i.e. NULL or any 0-valued +// compile-time integral constant). Their return values have +// different sizes, so we can use sizeof() to test which version is +// picked by the compiler. These helpers have no implementations, as +// we only need their signatures. +// +// Given IsNullLiteralHelper(x), the compiler will pick the first +// version if x can be implicitly converted to Secret*, and pick the +// second version otherwise. Since Secret is a secret and incomplete +// type, the only expression a user can write that has type Secret* is +// a null pointer literal. Therefore, we know that x is a null +// pointer literal if and only if the first version is picked by the +// compiler. +char IsNullLiteralHelper(Secret* p); +char (&IsNullLiteralHelper(...))[2]; // NOLINT + +// A compile-time bool constant that is true if and only if x is a +// null pointer literal (i.e. NULL or any 0-valued compile-time +// integral constant). +#ifdef GTEST_ELLIPSIS_NEEDS_POD_ +// We lose support for NULL detection where the compiler doesn't like +// passing non-POD classes through ellipsis (...). +# define GTEST_IS_NULL_LITERAL_(x) false +#else +# define GTEST_IS_NULL_LITERAL_(x) \ + (sizeof(::testing::internal::IsNullLiteralHelper(x)) == 1) +#endif // GTEST_ELLIPSIS_NEEDS_POD_ + +// Appends the user-supplied message to the Google-Test-generated message. +GTEST_API_ std::string AppendUserMessage( + const std::string& gtest_msg, const Message& user_msg); + +#if GTEST_HAS_EXCEPTIONS + +// This exception is thrown by (and only by) a failed Google Test +// assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions +// are enabled). We derive it from std::runtime_error, which is for +// errors presumably detectable only at run time. Since +// std::runtime_error inherits from std::exception, many testing +// frameworks know how to extract and print the message inside it. +class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error { + public: + explicit GoogleTestFailureException(const TestPartResult& failure); +}; + +#endif // GTEST_HAS_EXCEPTIONS + +// A helper class for creating scoped traces in user programs. +class GTEST_API_ ScopedTrace { + public: + // The c'tor pushes the given source file location and message onto + // a trace stack maintained by Google Test. + ScopedTrace(const char* file, int line, const Message& message); + + // The d'tor pops the info pushed by the c'tor. + // + // Note that the d'tor is not virtual in order to be efficient. + // Don't inherit from ScopedTrace! + ~ScopedTrace(); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace); +} GTEST_ATTRIBUTE_UNUSED_; // A ScopedTrace object does its job in its + // c'tor and d'tor. Therefore it doesn't + // need to be used otherwise. + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true iff the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +GTEST_API_ AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const std::string& expected_value, + const std::string& actual_value, + bool ignoring_case); + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +GTEST_API_ std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value); + +// This template class represents an IEEE floating-point number +// (either single-precision or double-precision, depending on the +// template parameters). +// +// The purpose of this class is to do more sophisticated number +// comparison. (Due to round-off error, etc, it's very unlikely that +// two floating-points will be equal exactly. Hence a naive +// comparison by the == operation often doesn't work.) +// +// Format of IEEE floating-point: +// +// The most-significant bit being the leftmost, an IEEE +// floating-point looks like +// +// sign_bit exponent_bits fraction_bits +// +// Here, sign_bit is a single bit that designates the sign of the +// number. +// +// For float, there are 8 exponent bits and 23 fraction bits. +// +// For double, there are 11 exponent bits and 52 fraction bits. +// +// More details can be found at +// http://en.wikipedia.org/wiki/IEEE_floating-point_standard. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +template +class FloatingPoint { + public: + // Defines the unsigned integer type that has the same size as the + // floating point number. + typedef typename TypeWithSize::UInt Bits; + + // Constants. + + // # of bits in a number. + static const size_t kBitCount = 8*sizeof(RawType); + + // # of fraction bits in a number. + static const size_t kFractionBitCount = + std::numeric_limits::digits - 1; + + // # of exponent bits in a number. + static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount; + + // The mask for the sign bit. + static const Bits kSignBitMask = static_cast(1) << (kBitCount - 1); + + // The mask for the fraction bits. + static const Bits kFractionBitMask = + ~static_cast(0) >> (kExponentBitCount + 1); + + // The mask for the exponent bits. + static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask); + + // How many ULP's (Units in the Last Place) we want to tolerate when + // comparing two numbers. The larger the value, the more error we + // allow. A 0 value means that two numbers must be exactly the same + // to be considered equal. + // + // The maximum error of a single floating-point operation is 0.5 + // units in the last place. On Intel CPU's, all floating-point + // calculations are done with 80-bit precision, while double has 64 + // bits. Therefore, 4 should be enough for ordinary use. + // + // See the following article for more details on ULP: + // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + static const size_t kMaxUlps = 4; + + // Constructs a FloatingPoint from a raw floating-point number. + // + // On an Intel CPU, passing a non-normalized NAN (Not a Number) + // around may change its bits, although the new value is guaranteed + // to be also a NAN. Therefore, don't expect this constructor to + // preserve the bits in x when x is a NAN. + explicit FloatingPoint(const RawType& x) { u_.value_ = x; } + + // Static methods + + // Reinterprets a bit pattern as a floating-point number. + // + // This function is needed to test the AlmostEquals() method. + static RawType ReinterpretBits(const Bits bits) { + FloatingPoint fp(0); + fp.u_.bits_ = bits; + return fp.u_.value_; + } + + // Returns the floating-point number that represent positive infinity. + static RawType Infinity() { + return ReinterpretBits(kExponentBitMask); + } + + // Returns the maximum representable finite floating-point number. + static RawType Max(); + + // Non-static methods + + // Returns the bits that represents this number. + const Bits &bits() const { return u_.bits_; } + + // Returns the exponent bits of this number. + Bits exponent_bits() const { return kExponentBitMask & u_.bits_; } + + // Returns the fraction bits of this number. + Bits fraction_bits() const { return kFractionBitMask & u_.bits_; } + + // Returns the sign bit of this number. + Bits sign_bit() const { return kSignBitMask & u_.bits_; } + + // Returns true iff this is NAN (not a number). + bool is_nan() const { + // It's a NAN if the exponent bits are all ones and the fraction + // bits are not entirely zeros. + return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0); + } + + // Returns true iff this number is at most kMaxUlps ULP's away from + // rhs. In particular, this function: + // + // - returns false if either number is (or both are) NAN. + // - treats really large numbers as almost equal to infinity. + // - thinks +0.0 and -0.0 are 0 DLP's apart. + bool AlmostEquals(const FloatingPoint& rhs) const { + // The IEEE standard says that any comparison operation involving + // a NAN must return false. + if (is_nan() || rhs.is_nan()) return false; + + return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_) + <= kMaxUlps; + } + + private: + // The data type used to store the actual floating-point number. + union FloatingPointUnion { + RawType value_; // The raw floating-point number. + Bits bits_; // The bits that represent the number. + }; + + // Converts an integer from the sign-and-magnitude representation to + // the biased representation. More precisely, let N be 2 to the + // power of (kBitCount - 1), an integer x is represented by the + // unsigned number x + N. + // + // For instance, + // + // -N + 1 (the most negative number representable using + // sign-and-magnitude) is represented by 1; + // 0 is represented by N; and + // N - 1 (the biggest number representable using + // sign-and-magnitude) is represented by 2N - 1. + // + // Read http://en.wikipedia.org/wiki/Signed_number_representations + // for more details on signed number representations. + static Bits SignAndMagnitudeToBiased(const Bits &sam) { + if (kSignBitMask & sam) { + // sam represents a negative number. + return ~sam + 1; + } else { + // sam represents a positive number. + return kSignBitMask | sam; + } + } + + // Given two numbers in the sign-and-magnitude representation, + // returns the distance between them as an unsigned number. + static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1, + const Bits &sam2) { + const Bits biased1 = SignAndMagnitudeToBiased(sam1); + const Bits biased2 = SignAndMagnitudeToBiased(sam2); + return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); + } + + FloatingPointUnion u_; +}; + +// We cannot use std::numeric_limits::max() as it clashes with the max() +// macro defined by . +template <> +inline float FloatingPoint::Max() { return FLT_MAX; } +template <> +inline double FloatingPoint::Max() { return DBL_MAX; } + +// Typedefs the instances of the FloatingPoint template class that we +// care to use. +typedef FloatingPoint Float; +typedef FloatingPoint Double; + +// In order to catch the mistake of putting tests that use different +// test fixture classes in the same test case, we need to assign +// unique IDs to fixture classes and compare them. The TypeId type is +// used to hold such IDs. The user should treat TypeId as an opaque +// type: the only operation allowed on TypeId values is to compare +// them for equality using the == operator. +typedef const void* TypeId; + +template +class TypeIdHelper { + public: + // dummy_ must not have a const type. Otherwise an overly eager + // compiler (e.g. MSVC 7.1 & 8.0) may try to merge + // TypeIdHelper::dummy_ for different Ts as an "optimization". + static bool dummy_; +}; + +template +bool TypeIdHelper::dummy_ = false; + +// GetTypeId() returns the ID of type T. Different values will be +// returned for different types. Calling the function twice with the +// same type argument is guaranteed to return the same ID. +template +TypeId GetTypeId() { + // The compiler is required to allocate a different + // TypeIdHelper::dummy_ variable for each T used to instantiate + // the template. Therefore, the address of dummy_ is guaranteed to + // be unique. + return &(TypeIdHelper::dummy_); +} + +// Returns the type ID of ::testing::Test. Always call this instead +// of GetTypeId< ::testing::Test>() to get the type ID of +// ::testing::Test, as the latter may give the wrong result due to a +// suspected linker bug when compiling Google Test as a Mac OS X +// framework. +GTEST_API_ TypeId GetTestTypeId(); + +// Defines the abstract factory interface that creates instances +// of a Test object. +class TestFactoryBase { + public: + virtual ~TestFactoryBase() {} + + // Creates a test instance to run. The instance is both created and destroyed + // within TestInfoImpl::Run() + virtual Test* CreateTest() = 0; + + protected: + TestFactoryBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestFactoryBase); +}; + +// This class provides implementation of TeastFactoryBase interface. +// It is used in TEST and TEST_F macros. +template +class TestFactoryImpl : public TestFactoryBase { + public: + virtual Test* CreateTest() { return new TestClass; } +}; + +#if GTEST_OS_WINDOWS + +// Predicate-formatters for implementing the HRESULT checking macros +// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED} +// We pass a long instead of HRESULT to avoid causing an +// include dependency for the HRESULT type. +GTEST_API_ AssertionResult IsHRESULTSuccess(const char* expr, + long hr); // NOLINT +GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr, + long hr); // NOLINT + +#endif // GTEST_OS_WINDOWS + +// Types of SetUpTestCase() and TearDownTestCase() functions. +typedef void (*SetUpTestCaseFunc)(); +typedef void (*TearDownTestCaseFunc)(); + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_case_name: name of the test case +// name: name of the test +// type_param the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param text representation of the test's value parameter, +// or NULL if this is not a type-parameterized test. +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +GTEST_API_ TestInfo* MakeAndRegisterTestInfo( + const char* test_case_name, + const char* name, + const char* type_param, + const char* value_param, + TypeId fixture_class_id, + SetUpTestCaseFunc set_up_tc, + TearDownTestCaseFunc tear_down_tc, + TestFactoryBase* factory); + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr); + +#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// State of the definition of a type-parameterized test case. +class GTEST_API_ TypedTestCasePState { + public: + TypedTestCasePState() : registered_(false) {} + + // Adds the given test name to defined_test_names_ and return true + // if the test case hasn't been registered; otherwise aborts the + // program. + bool AddTestName(const char* file, int line, const char* case_name, + const char* test_name) { + if (registered_) { + fprintf(stderr, "%s Test %s must be defined before " + "REGISTER_TYPED_TEST_CASE_P(%s, ...).\n", + FormatFileLocation(file, line).c_str(), test_name, case_name); + fflush(stderr); + posix::Abort(); + } + defined_test_names_.insert(test_name); + return true; + } + + // Verifies that registered_tests match the test names in + // defined_test_names_; returns registered_tests if successful, or + // aborts the program otherwise. + const char* VerifyRegisteredTestNames( + const char* file, int line, const char* registered_tests); + + private: + bool registered_; + ::std::set defined_test_names_; +}; + +// Skips to the first non-space char after the first comma in 'str'; +// returns NULL if no comma is found in 'str'. +inline const char* SkipComma(const char* str) { + const char* comma = strchr(str, ','); + if (comma == NULL) { + return NULL; + } + while (IsSpace(*(++comma))) {} + return comma; +} + +// Returns the prefix of 'str' before the first comma in it; returns +// the entire string if it contains no comma. +inline std::string GetPrefixUntilComma(const char* str) { + const char* comma = strchr(str, ','); + return comma == NULL ? str : std::string(str, comma); +} + +// TypeParameterizedTest::Register() +// registers a list of type-parameterized tests with Google Test. The +// return value is insignificant - we just need to return something +// such that we can call this function in a namespace scope. +// +// Implementation note: The GTEST_TEMPLATE_ macro declares a template +// template parameter. It's defined in gtest-type-util.h. +template +class TypeParameterizedTest { + public: + // 'index' is the index of the test in the type list 'Types' + // specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase, + // Types). Valid values for 'index' are [0, N - 1] where N is the + // length of Types. + static bool Register(const char* prefix, const char* case_name, + const char* test_names, int index) { + typedef typename Types::Head Type; + typedef Fixture FixtureClass; + typedef typename GTEST_BIND_(TestSel, Type) TestClass; + + // First, registers the first type-parameterized test in the type + // list. + MakeAndRegisterTestInfo( + (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + "/" + + StreamableToString(index)).c_str(), + GetPrefixUntilComma(test_names).c_str(), + GetTypeName().c_str(), + NULL, // No value parameter. + GetTypeId(), + TestClass::SetUpTestCase, + TestClass::TearDownTestCase, + new TestFactoryImpl); + + // Next, recurses (at compile time) with the tail of the type list. + return TypeParameterizedTest + ::Register(prefix, case_name, test_names, index + 1); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTest { + public: + static bool Register(const char* /*prefix*/, const char* /*case_name*/, + const char* /*test_names*/, int /*index*/) { + return true; + } +}; + +// TypeParameterizedTestCase::Register() +// registers *all combinations* of 'Tests' and 'Types' with Google +// Test. The return value is insignificant - we just need to return +// something such that we can call this function in a namespace scope. +template +class TypeParameterizedTestCase { + public: + static bool Register(const char* prefix, const char* case_name, + const char* test_names) { + typedef typename Tests::Head Head; + + // First, register the first test in 'Test' for each type in 'Types'. + TypeParameterizedTest::Register( + prefix, case_name, test_names, 0); + + // Next, recurses (at compile time) with the tail of the test list. + return TypeParameterizedTestCase + ::Register(prefix, case_name, SkipComma(test_names)); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTestCase { + public: + static bool Register(const char* /*prefix*/, const char* /*case_name*/, + const char* /*test_names*/) { + return true; + } +}; + +#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +GTEST_API_ std::string GetCurrentOsStackTraceExceptTop( + UnitTest* unit_test, int skip_count); + +// Helpers for suppressing warnings on unreachable code or constant +// condition. + +// Always returns true. +GTEST_API_ bool AlwaysTrue(); + +// Always returns false. +inline bool AlwaysFalse() { return !AlwaysTrue(); } + +// Helper for suppressing false warning from Clang on a const char* +// variable declared in a conditional expression always being NULL in +// the else branch. +struct GTEST_API_ ConstCharPtr { + ConstCharPtr(const char* str) : value(str) {} + operator bool() const { return true; } + const char* value; +}; + +// A simple Linear Congruential Generator for generating random +// numbers with a uniform distribution. Unlike rand() and srand(), it +// doesn't use global state (and therefore can't interfere with user +// code). Unlike rand_r(), it's portable. An LCG isn't very random, +// but it's good enough for our purposes. +class GTEST_API_ Random { + public: + static const UInt32 kMaxRange = 1u << 31; + + explicit Random(UInt32 seed) : state_(seed) {} + + void Reseed(UInt32 seed) { state_ = seed; } + + // Generates a random number from [0, range). Crashes if 'range' is + // 0 or greater than kMaxRange. + UInt32 Generate(UInt32 range); + + private: + UInt32 state_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(Random); +}; + +// Defining a variable of type CompileAssertTypesEqual will cause a +// compiler error iff T1 and T2 are different types. +template +struct CompileAssertTypesEqual; + +template +struct CompileAssertTypesEqual { +}; + +// Removes the reference from a type if it is a reference type, +// otherwise leaves it unchanged. This is the same as +// tr1::remove_reference, which is not widely available yet. +template +struct RemoveReference { typedef T type; }; // NOLINT +template +struct RemoveReference { typedef T type; }; // NOLINT + +// A handy wrapper around RemoveReference that works when the argument +// T depends on template parameters. +#define GTEST_REMOVE_REFERENCE_(T) \ + typename ::testing::internal::RemoveReference::type + +// Removes const from a type if it is a const type, otherwise leaves +// it unchanged. This is the same as tr1::remove_const, which is not +// widely available yet. +template +struct RemoveConst { typedef T type; }; // NOLINT +template +struct RemoveConst { typedef T type; }; // NOLINT + +// MSVC 8.0, Sun C++, and IBM XL C++ have a bug which causes the above +// definition to fail to remove the const in 'const int[3]' and 'const +// char[3][4]'. The following specialization works around the bug. +template +struct RemoveConst { + typedef typename RemoveConst::type type[N]; +}; + +#if defined(_MSC_VER) && _MSC_VER < 1400 +// This is the only specialization that allows VC++ 7.1 to remove const in +// 'const int[3] and 'const int[3][4]'. However, it causes trouble with GCC +// and thus needs to be conditionally compiled. +template +struct RemoveConst { + typedef typename RemoveConst::type type[N]; +}; +#endif + +// A handy wrapper around RemoveConst that works when the argument +// T depends on template parameters. +#define GTEST_REMOVE_CONST_(T) \ + typename ::testing::internal::RemoveConst::type + +// Turns const U&, U&, const U, and U all into U. +#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \ + GTEST_REMOVE_CONST_(GTEST_REMOVE_REFERENCE_(T)) + +// Adds reference to a type if it is not a reference type, +// otherwise leaves it unchanged. This is the same as +// tr1::add_reference, which is not widely available yet. +template +struct AddReference { typedef T& type; }; // NOLINT +template +struct AddReference { typedef T& type; }; // NOLINT + +// A handy wrapper around AddReference that works when the argument T +// depends on template parameters. +#define GTEST_ADD_REFERENCE_(T) \ + typename ::testing::internal::AddReference::type + +// Adds a reference to const on top of T as necessary. For example, +// it transforms +// +// char ==> const char& +// const char ==> const char& +// char& ==> const char& +// const char& ==> const char& +// +// The argument T must depend on some template parameters. +#define GTEST_REFERENCE_TO_CONST_(T) \ + GTEST_ADD_REFERENCE_(const GTEST_REMOVE_REFERENCE_(T)) + +// ImplicitlyConvertible::value is a compile-time bool +// constant that's true iff type From can be implicitly converted to +// type To. +template +class ImplicitlyConvertible { + private: + // We need the following helper functions only for their types. + // They have no implementations. + + // MakeFrom() is an expression whose type is From. We cannot simply + // use From(), as the type From may not have a public default + // constructor. + static From MakeFrom(); + + // These two functions are overloaded. Given an expression + // Helper(x), the compiler will pick the first version if x can be + // implicitly converted to type To; otherwise it will pick the + // second version. + // + // The first version returns a value of size 1, and the second + // version returns a value of size 2. Therefore, by checking the + // size of Helper(x), which can be done at compile time, we can tell + // which version of Helper() is used, and hence whether x can be + // implicitly converted to type To. + static char Helper(To); + static char (&Helper(...))[2]; // NOLINT + + // We have to put the 'public' section after the 'private' section, + // or MSVC refuses to compile the code. + public: + // MSVC warns about implicitly converting from double to int for + // possible loss of data, so we need to temporarily disable the + // warning. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4244) // Temporarily disables warning 4244. + + static const bool value = + sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1; +# pragma warning(pop) // Restores the warning state. +#elif defined(__BORLANDC__) + // C++Builder cannot use member overload resolution during template + // instantiation. The simplest workaround is to use its C++0x type traits + // functions (C++Builder 2009 and above only). + static const bool value = __is_convertible(From, To); +#else + static const bool value = + sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1; +#endif // _MSV_VER +}; +template +const bool ImplicitlyConvertible::value; + +// IsAProtocolMessage::value is a compile-time bool constant that's +// true iff T is type ProtocolMessage, proto2::Message, or a subclass +// of those. +template +struct IsAProtocolMessage + : public bool_constant< + ImplicitlyConvertible::value || + ImplicitlyConvertible::value> { +}; + +// When the compiler sees expression IsContainerTest(0), if C is an +// STL-style container class, the first overload of IsContainerTest +// will be viable (since both C::iterator* and C::const_iterator* are +// valid types and NULL can be implicitly converted to them). It will +// be picked over the second overload as 'int' is a perfect match for +// the type of argument 0. If C::iterator or C::const_iterator is not +// a valid type, the first overload is not viable, and the second +// overload will be picked. Therefore, we can determine whether C is +// a container class by checking the type of IsContainerTest(0). +// The value of the expression is insignificant. +// +// Note that we look for both C::iterator and C::const_iterator. The +// reason is that C++ injects the name of a class as a member of the +// class itself (e.g. you can refer to class iterator as either +// 'iterator' or 'iterator::iterator'). If we look for C::iterator +// only, for example, we would mistakenly think that a class named +// iterator is an STL container. +// +// Also note that the simpler approach of overloading +// IsContainerTest(typename C::const_iterator*) and +// IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++. +typedef int IsContainer; +template +IsContainer IsContainerTest(int /* dummy */, + typename C::iterator* /* it */ = NULL, + typename C::const_iterator* /* const_it */ = NULL) { + return 0; +} + +typedef char IsNotContainer; +template +IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; } + +// EnableIf::type is void when 'Cond' is true, and +// undefined when 'Cond' is false. To use SFINAE to make a function +// overload only apply when a particular expression is true, add +// "typename EnableIf::type* = 0" as the last parameter. +template struct EnableIf; +template<> struct EnableIf { typedef void type; }; // NOLINT + +// Utilities for native arrays. + +// ArrayEq() compares two k-dimensional native arrays using the +// elements' operator==, where k can be any integer >= 0. When k is +// 0, ArrayEq() degenerates into comparing a single pair of values. + +template +bool ArrayEq(const T* lhs, size_t size, const U* rhs); + +// This generic version is used when k is 0. +template +inline bool ArrayEq(const T& lhs, const U& rhs) { return lhs == rhs; } + +// This overload is used when k >= 1. +template +inline bool ArrayEq(const T(&lhs)[N], const U(&rhs)[N]) { + return internal::ArrayEq(lhs, N, rhs); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous ArrayEq() function, arrays with different sizes would +// lead to different copies of the template code. +template +bool ArrayEq(const T* lhs, size_t size, const U* rhs) { + for (size_t i = 0; i != size; i++) { + if (!internal::ArrayEq(lhs[i], rhs[i])) + return false; + } + return true; +} + +// Finds the first element in the iterator range [begin, end) that +// equals elem. Element may be a native array type itself. +template +Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) { + for (Iter it = begin; it != end; ++it) { + if (internal::ArrayEq(*it, elem)) + return it; + } + return end; +} + +// CopyArray() copies a k-dimensional native array using the elements' +// operator=, where k can be any integer >= 0. When k is 0, +// CopyArray() degenerates into copying a single value. + +template +void CopyArray(const T* from, size_t size, U* to); + +// This generic version is used when k is 0. +template +inline void CopyArray(const T& from, U* to) { *to = from; } + +// This overload is used when k >= 1. +template +inline void CopyArray(const T(&from)[N], U(*to)[N]) { + internal::CopyArray(from, N, *to); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous CopyArray() function, arrays with different sizes +// would lead to different copies of the template code. +template +void CopyArray(const T* from, size_t size, U* to) { + for (size_t i = 0; i != size; i++) { + internal::CopyArray(from[i], to + i); + } +} + +// The relation between an NativeArray object (see below) and the +// native array it represents. +enum RelationToSource { + kReference, // The NativeArray references the native array. + kCopy // The NativeArray makes a copy of the native array and + // owns the copy. +}; + +// Adapts a native array to a read-only STL-style container. Instead +// of the complete STL container concept, this adaptor only implements +// members useful for Google Mock's container matchers. New members +// should be added as needed. To simplify the implementation, we only +// support Element being a raw type (i.e. having no top-level const or +// reference modifier). It's the client's responsibility to satisfy +// this requirement. Element can be an array type itself (hence +// multi-dimensional arrays are supported). +template +class NativeArray { + public: + // STL-style container typedefs. + typedef Element value_type; + typedef Element* iterator; + typedef const Element* const_iterator; + + // Constructs from a native array. + NativeArray(const Element* array, size_t count, RelationToSource relation) { + Init(array, count, relation); + } + + // Copy constructor. + NativeArray(const NativeArray& rhs) { + Init(rhs.array_, rhs.size_, rhs.relation_to_source_); + } + + ~NativeArray() { + // Ensures that the user doesn't instantiate NativeArray with a + // const or reference type. + static_cast(StaticAssertTypeEqHelper()); + if (relation_to_source_ == kCopy) + delete[] array_; + } + + // STL-style container methods. + size_t size() const { return size_; } + const_iterator begin() const { return array_; } + const_iterator end() const { return array_ + size_; } + bool operator==(const NativeArray& rhs) const { + return size() == rhs.size() && + ArrayEq(begin(), size(), rhs.begin()); + } + + private: + // Initializes this object; makes a copy of the input array if + // 'relation' is kCopy. + void Init(const Element* array, size_t a_size, RelationToSource relation) { + if (relation == kReference) { + array_ = array; + } else { + Element* const copy = new Element[a_size]; + CopyArray(array, a_size, copy); + array_ = copy; + } + size_ = a_size; + relation_to_source_ = relation; + } + + const Element* array_; + size_t size_; + RelationToSource relation_to_source_; + + GTEST_DISALLOW_ASSIGN_(NativeArray); +}; + +} // namespace internal +} // namespace testing + +#define GTEST_MESSAGE_AT_(file, line, message, result_type) \ + ::testing::internal::AssertHelper(result_type, file, line, message) \ + = ::testing::Message() + +#define GTEST_MESSAGE_(message, result_type) \ + GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type) + +#define GTEST_FATAL_FAILURE_(message) \ + return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure) + +#define GTEST_NONFATAL_FAILURE_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure) + +#define GTEST_SUCCESS_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess) + +// Suppresses MSVC warnings 4072 (unreachable code) for the code following +// statement if it returns or throws (or doesn't return or throw in some +// situations). +#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \ + if (::testing::internal::AlwaysTrue()) { statement; } + +#define GTEST_TEST_THROW_(statement, expected_exception, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::ConstCharPtr gtest_msg = "") { \ + bool gtest_caught_expected = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (expected_exception const&) { \ + gtest_caught_expected = true; \ + } \ + catch (...) { \ + gtest_msg.value = \ + "Expected: " #statement " throws an exception of type " \ + #expected_exception ".\n Actual: it throws a different type."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + if (!gtest_caught_expected) { \ + gtest_msg.value = \ + "Expected: " #statement " throws an exception of type " \ + #expected_exception ".\n Actual: it throws nothing."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \ + fail(gtest_msg.value) + +#define GTEST_TEST_NO_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (...) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \ + fail("Expected: " #statement " doesn't throw an exception.\n" \ + " Actual: it throws.") + +#define GTEST_TEST_ANY_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + bool gtest_caught_any = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (...) { \ + gtest_caught_any = true; \ + } \ + if (!gtest_caught_any) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__): \ + fail("Expected: " #statement " throws an exception.\n" \ + " Actual: it doesn't.") + + +// Implements Boolean test assertions such as EXPECT_TRUE. expression can be +// either a boolean expression or an AssertionResult. text is a textual +// represenation of expression as it was passed into the EXPECT_TRUE. +#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar_ = \ + ::testing::AssertionResult(expression)) \ + ; \ + else \ + fail(::testing::internal::GetBoolAssertionFailureMessage(\ + gtest_ar_, text, #actual, #expected).c_str()) + +#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + ::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker; \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__): \ + fail("Expected: " #statement " doesn't generate new fatal " \ + "failures in the current thread.\n" \ + " Actual: it does.") + +// Expands to the name of the class that implements the given test. +#define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ + test_case_name##_##test_name##_Test + +// Helper macro for defining tests. +#define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)\ +class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\ + public:\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\ + private:\ + virtual void TestBody();\ + static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;\ + GTEST_DISALLOW_COPY_AND_ASSIGN_(\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\ +};\ +\ +::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\ + ::test_info_ =\ + ::testing::internal::MakeAndRegisterTestInfo(\ + #test_case_name, #test_name, NULL, NULL, \ + (parent_id), \ + parent_class::SetUpTestCase, \ + parent_class::TearDownTestCase, \ + new ::testing::internal::TestFactoryImpl<\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\ +void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-linked_ptr.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-linked_ptr.h new file mode 100644 index 00000000..b1362cd0 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-linked_ptr.h @@ -0,0 +1,233 @@ +// Copyright 2003 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Dan Egnor (egnor@google.com) +// +// A "smart" pointer type with reference tracking. Every pointer to a +// particular object is kept on a circular linked list. When the last pointer +// to an object is destroyed or reassigned, the object is deleted. +// +// Used properly, this deletes the object when the last reference goes away. +// There are several caveats: +// - Like all reference counting schemes, cycles lead to leaks. +// - Each smart pointer is actually two pointers (8 bytes instead of 4). +// - Every time a pointer is assigned, the entire list of pointers to that +// object is traversed. This class is therefore NOT SUITABLE when there +// will often be more than two or three pointers to a particular object. +// - References are only tracked as long as linked_ptr<> objects are copied. +// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS +// will happen (double deletion). +// +// A good use of this class is storing object references in STL containers. +// You can safely put linked_ptr<> in a vector<>. +// Other uses may not be as good. +// +// Note: If you use an incomplete type with linked_ptr<>, the class +// *containing* linked_ptr<> must have a constructor and destructor (even +// if they do nothing!). +// +// Bill Gibbons suggested we use something like this. +// +// Thread Safety: +// Unlike other linked_ptr implementations, in this implementation +// a linked_ptr object is thread-safe in the sense that: +// - it's safe to copy linked_ptr objects concurrently, +// - it's safe to copy *from* a linked_ptr and read its underlying +// raw pointer (e.g. via get()) concurrently, and +// - it's safe to write to two linked_ptrs that point to the same +// shared object concurrently. +// TODO(wan@google.com): rename this to safe_linked_ptr to avoid +// confusion with normal linked_ptr. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ + +#include +#include + +#include "gtest/internal/gtest-port.h" + +namespace testing { +namespace internal { + +// Protects copying of all linked_ptr objects. +GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_linked_ptr_mutex); + +// This is used internally by all instances of linked_ptr<>. It needs to be +// a non-template class because different types of linked_ptr<> can refer to +// the same object (linked_ptr(obj) vs linked_ptr(obj)). +// So, it needs to be possible for different types of linked_ptr to participate +// in the same circular linked list, so we need a single class type here. +// +// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr. +class linked_ptr_internal { + public: + // Create a new circle that includes only this instance. + void join_new() { + next_ = this; + } + + // Many linked_ptr operations may change p.link_ for some linked_ptr + // variable p in the same circle as this object. Therefore we need + // to prevent two such operations from occurring concurrently. + // + // Note that different types of linked_ptr objects can coexist in a + // circle (e.g. linked_ptr, linked_ptr, and + // linked_ptr). Therefore we must use a single mutex to + // protect all linked_ptr objects. This can create serious + // contention in production code, but is acceptable in a testing + // framework. + + // Join an existing circle. + void join(linked_ptr_internal const* ptr) + GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) { + MutexLock lock(&g_linked_ptr_mutex); + + linked_ptr_internal const* p = ptr; + while (p->next_ != ptr) p = p->next_; + p->next_ = this; + next_ = ptr; + } + + // Leave whatever circle we're part of. Returns true if we were the + // last member of the circle. Once this is done, you can join() another. + bool depart() + GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) { + MutexLock lock(&g_linked_ptr_mutex); + + if (next_ == this) return true; + linked_ptr_internal const* p = next_; + while (p->next_ != this) p = p->next_; + p->next_ = next_; + return false; + } + + private: + mutable linked_ptr_internal const* next_; +}; + +template +class linked_ptr { + public: + typedef T element_type; + + // Take over ownership of a raw pointer. This should happen as soon as + // possible after the object is created. + explicit linked_ptr(T* ptr = NULL) { capture(ptr); } + ~linked_ptr() { depart(); } + + // Copy an existing linked_ptr<>, adding ourselves to the list of references. + template linked_ptr(linked_ptr const& ptr) { copy(&ptr); } + linked_ptr(linked_ptr const& ptr) { // NOLINT + assert(&ptr != this); + copy(&ptr); + } + + // Assignment releases the old value and acquires the new. + template linked_ptr& operator=(linked_ptr const& ptr) { + depart(); + copy(&ptr); + return *this; + } + + linked_ptr& operator=(linked_ptr const& ptr) { + if (&ptr != this) { + depart(); + copy(&ptr); + } + return *this; + } + + // Smart pointer members. + void reset(T* ptr = NULL) { + depart(); + capture(ptr); + } + T* get() const { return value_; } + T* operator->() const { return value_; } + T& operator*() const { return *value_; } + + bool operator==(T* p) const { return value_ == p; } + bool operator!=(T* p) const { return value_ != p; } + template + bool operator==(linked_ptr const& ptr) const { + return value_ == ptr.get(); + } + template + bool operator!=(linked_ptr const& ptr) const { + return value_ != ptr.get(); + } + + private: + template + friend class linked_ptr; + + T* value_; + linked_ptr_internal link_; + + void depart() { + if (link_.depart()) delete value_; + } + + void capture(T* ptr) { + value_ = ptr; + link_.join_new(); + } + + template void copy(linked_ptr const* ptr) { + value_ = ptr->get(); + if (value_) + link_.join(&ptr->link_); + else + link_.join_new(); + } +}; + +template inline +bool operator==(T* ptr, const linked_ptr& x) { + return ptr == x.get(); +} + +template inline +bool operator!=(T* ptr, const linked_ptr& x) { + return ptr != x.get(); +} + +// A function to convert T* into linked_ptr +// Doing e.g. make_linked_ptr(new FooBarBaz(arg)) is a shorter notation +// for linked_ptr >(new FooBarBaz(arg)) +template +linked_ptr make_linked_ptr(T* ptr) { + return linked_ptr(ptr); +} + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-param-util-generated.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-param-util-generated.h new file mode 100644 index 00000000..e8054859 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-param-util-generated.h @@ -0,0 +1,5143 @@ +// This file was GENERATED by command: +// pump.py gtest-param-util-generated.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) + +// Type and function utilities for implementing parameterized tests. +// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +// Currently Google Test supports at most 50 arguments in Values, +// and at most 10 arguments in Combine. Please contact +// googletestframework@googlegroups.com if you need more. +// Please note that the number of arguments to Combine is limited +// by the maximum arity of the implementation of tr1::tuple which is +// currently set at 10. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ + +// scripts/fuse_gtest.py depends on gtest's own header being #included +// *unconditionally*. Therefore these #includes cannot be moved +// inside #if GTEST_HAS_PARAM_TEST. +#include "gtest/internal/gtest-param-util.h" +#include "gtest/internal/gtest-port.h" + +#if GTEST_HAS_PARAM_TEST + +namespace testing { + +// Forward declarations of ValuesIn(), which is implemented in +// include/gtest/gtest-param-test.h. +template +internal::ParamGenerator< + typename ::testing::internal::IteratorTraits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end); + +template +internal::ParamGenerator ValuesIn(const T (&array)[N]); + +template +internal::ParamGenerator ValuesIn( + const Container& container); + +namespace internal { + +// Used in the Values() function to provide polymorphic capabilities. +template +class ValueArray1 { + public: + explicit ValueArray1(T1 v1) : v1_(v1) {} + + template + operator ParamGenerator() const { return ValuesIn(&v1_, &v1_ + 1); } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray1& other); + + const T1 v1_; +}; + +template +class ValueArray2 { + public: + ValueArray2(T1 v1, T2 v2) : v1_(v1), v2_(v2) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray2& other); + + const T1 v1_; + const T2 v2_; +}; + +template +class ValueArray3 { + public: + ValueArray3(T1 v1, T2 v2, T3 v3) : v1_(v1), v2_(v2), v3_(v3) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray3& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; +}; + +template +class ValueArray4 { + public: + ValueArray4(T1 v1, T2 v2, T3 v3, T4 v4) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray4& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; +}; + +template +class ValueArray5 { + public: + ValueArray5(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray5& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; +}; + +template +class ValueArray6 { + public: + ValueArray6(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray6& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; +}; + +template +class ValueArray7 { + public: + ValueArray7(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray7& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; +}; + +template +class ValueArray8 { + public: + ValueArray8(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray8& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; +}; + +template +class ValueArray9 { + public: + ValueArray9(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray9& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; +}; + +template +class ValueArray10 { + public: + ValueArray10(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray10& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; +}; + +template +class ValueArray11 { + public: + ValueArray11(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray11& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; +}; + +template +class ValueArray12 { + public: + ValueArray12(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray12& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; +}; + +template +class ValueArray13 { + public: + ValueArray13(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray13& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; +}; + +template +class ValueArray14 { + public: + ValueArray14(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray14& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; +}; + +template +class ValueArray15 { + public: + ValueArray15(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray15& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; +}; + +template +class ValueArray16 { + public: + ValueArray16(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray16& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; +}; + +template +class ValueArray17 { + public: + ValueArray17(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, + T17 v17) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray17& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; +}; + +template +class ValueArray18 { + public: + ValueArray18(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray18& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; +}; + +template +class ValueArray19 { + public: + ValueArray19(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray19& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; +}; + +template +class ValueArray20 { + public: + ValueArray20(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray20& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; +}; + +template +class ValueArray21 { + public: + ValueArray21(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray21& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; +}; + +template +class ValueArray22 { + public: + ValueArray22(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray22& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; +}; + +template +class ValueArray23 { + public: + ValueArray23(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray23& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; +}; + +template +class ValueArray24 { + public: + ValueArray24(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray24& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; +}; + +template +class ValueArray25 { + public: + ValueArray25(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, + T25 v25) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray25& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; +}; + +template +class ValueArray26 { + public: + ValueArray26(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray26& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; +}; + +template +class ValueArray27 { + public: + ValueArray27(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), + v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), + v26_(v26), v27_(v27) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray27& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; +}; + +template +class ValueArray28 { + public: + ValueArray28(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), + v25_(v25), v26_(v26), v27_(v27), v28_(v28) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray28& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; +}; + +template +class ValueArray29 { + public: + ValueArray29(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), + v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray29& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; +}; + +template +class ValueArray30 { + public: + ValueArray30(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray30& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; +}; + +template +class ValueArray31 { + public: + ValueArray31(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray31& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; +}; + +template +class ValueArray32 { + public: + ValueArray32(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), + v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray32& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; +}; + +template +class ValueArray33 { + public: + ValueArray33(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, + T33 v33) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray33& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; +}; + +template +class ValueArray34 { + public: + ValueArray34(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray34& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; +}; + +template +class ValueArray35 { + public: + ValueArray35(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), + v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), + v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), + v32_(v32), v33_(v33), v34_(v34), v35_(v35) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray35& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; +}; + +template +class ValueArray36 { + public: + ValueArray36(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), + v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), + v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray36& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; +}; + +template +class ValueArray37 { + public: + ValueArray37(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), + v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), + v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), + v36_(v36), v37_(v37) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray37& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; +}; + +template +class ValueArray38 { + public: + ValueArray38(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray38& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; +}; + +template +class ValueArray39 { + public: + ValueArray39(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray39& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; +}; + +template +class ValueArray40 { + public: + ValueArray40(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), + v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), + v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), + v40_(v40) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray40& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; +}; + +template +class ValueArray41 { + public: + ValueArray41(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, + T41 v41) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray41& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; +}; + +template +class ValueArray42 { + public: + ValueArray42(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41), v42_(v42) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray42& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; +}; + +template +class ValueArray43 { + public: + ValueArray43(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), + v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), + v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), + v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), + v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray43& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; +}; + +template +class ValueArray44 { + public: + ValueArray44(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), + v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), + v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), + v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), + v43_(v43), v44_(v44) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray44& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; +}; + +template +class ValueArray45 { + public: + ValueArray45(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), + v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), + v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), + v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), + v42_(v42), v43_(v43), v44_(v44), v45_(v45) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray45& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; +}; + +template +class ValueArray46 { + public: + ValueArray46(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), + v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray46& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; +}; + +template +class ValueArray47 { + public: + ValueArray47(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), + v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46), + v47_(v47) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray47& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; +}; + +template +class ValueArray48 { + public: + ValueArray48(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), + v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), + v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), + v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), + v46_(v46), v47_(v47), v48_(v48) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray48& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; + const T48 v48_; +}; + +template +class ValueArray49 { + public: + ValueArray49(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, + T49 v49) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), + v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_), static_cast(v49_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray49& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; + const T48 v48_; + const T49 v49_; +}; + +template +class ValueArray50 { + public: + ValueArray50(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, T49 v49, + T50 v50) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), + v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49), v50_(v50) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_), static_cast(v49_), static_cast(v50_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray50& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; + const T48 v48_; + const T49 v49_; + const T50 v50_; +}; + +# if GTEST_HAS_COMBINE +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Generates values from the Cartesian product of values produced +// by the argument generators. +// +template +class CartesianProductGenerator2 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator2(const ParamGenerator& g1, + const ParamGenerator& g2) + : g1_(g1), g2_(g2) {} + virtual ~CartesianProductGenerator2() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current2_; + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + ParamType current_value_; + }; // class CartesianProductGenerator2::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator2& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; +}; // class CartesianProductGenerator2 + + +template +class CartesianProductGenerator3 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator3(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3) + : g1_(g1), g2_(g2), g3_(g3) {} + virtual ~CartesianProductGenerator3() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current3_; + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + ParamType current_value_; + }; // class CartesianProductGenerator3::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator3& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; +}; // class CartesianProductGenerator3 + + +template +class CartesianProductGenerator4 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator4(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {} + virtual ~CartesianProductGenerator4() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current4_; + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + ParamType current_value_; + }; // class CartesianProductGenerator4::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator4& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; +}; // class CartesianProductGenerator4 + + +template +class CartesianProductGenerator5 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator5(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {} + virtual ~CartesianProductGenerator5() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current5_; + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + ParamType current_value_; + }; // class CartesianProductGenerator5::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator5& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; +}; // class CartesianProductGenerator5 + + +template +class CartesianProductGenerator6 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator6(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {} + virtual ~CartesianProductGenerator6() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current6_; + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + ParamType current_value_; + }; // class CartesianProductGenerator6::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator6& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; +}; // class CartesianProductGenerator6 + + +template +class CartesianProductGenerator7 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator7(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {} + virtual ~CartesianProductGenerator7() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current7_; + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + ParamType current_value_; + }; // class CartesianProductGenerator7::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator7& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; +}; // class CartesianProductGenerator7 + + +template +class CartesianProductGenerator8 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator8(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7, + const ParamGenerator& g8) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), + g8_(g8) {} + virtual ~CartesianProductGenerator8() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin(), g8_, g8_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, + g8_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7, + const ParamGenerator& g8, + const typename ParamGenerator::iterator& current8) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7), + begin8_(g8.begin()), end8_(g8.end()), current8_(current8) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current8_; + if (current8_ == end8_) { + current8_ = begin8_; + ++current7_; + } + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_ && + current8_ == typed_other->current8_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_), + begin8_(other.begin8_), + end8_(other.end8_), + current8_(other.current8_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_ || + current8_ == end8_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + const typename ParamGenerator::iterator begin8_; + const typename ParamGenerator::iterator end8_; + typename ParamGenerator::iterator current8_; + ParamType current_value_; + }; // class CartesianProductGenerator8::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator8& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; + const ParamGenerator g8_; +}; // class CartesianProductGenerator8 + + +template +class CartesianProductGenerator9 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator9(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7, + const ParamGenerator& g8, const ParamGenerator& g9) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9) {} + virtual ~CartesianProductGenerator9() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, + g8_.end(), g9_, g9_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7, + const ParamGenerator& g8, + const typename ParamGenerator::iterator& current8, + const ParamGenerator& g9, + const typename ParamGenerator::iterator& current9) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7), + begin8_(g8.begin()), end8_(g8.end()), current8_(current8), + begin9_(g9.begin()), end9_(g9.end()), current9_(current9) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current9_; + if (current9_ == end9_) { + current9_ = begin9_; + ++current8_; + } + if (current8_ == end8_) { + current8_ = begin8_; + ++current7_; + } + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_ && + current8_ == typed_other->current8_ && + current9_ == typed_other->current9_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_), + begin8_(other.begin8_), + end8_(other.end8_), + current8_(other.current8_), + begin9_(other.begin9_), + end9_(other.end9_), + current9_(other.current9_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_, + *current9_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_ || + current8_ == end8_ || + current9_ == end9_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + const typename ParamGenerator::iterator begin8_; + const typename ParamGenerator::iterator end8_; + typename ParamGenerator::iterator current8_; + const typename ParamGenerator::iterator begin9_; + const typename ParamGenerator::iterator end9_; + typename ParamGenerator::iterator current9_; + ParamType current_value_; + }; // class CartesianProductGenerator9::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator9& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; + const ParamGenerator g8_; + const ParamGenerator g9_; +}; // class CartesianProductGenerator9 + + +template +class CartesianProductGenerator10 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator10(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7, + const ParamGenerator& g8, const ParamGenerator& g9, + const ParamGenerator& g10) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9), g10_(g10) {} + virtual ~CartesianProductGenerator10() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin(), g10_, g10_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, + g8_.end(), g9_, g9_.end(), g10_, g10_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7, + const ParamGenerator& g8, + const typename ParamGenerator::iterator& current8, + const ParamGenerator& g9, + const typename ParamGenerator::iterator& current9, + const ParamGenerator& g10, + const typename ParamGenerator::iterator& current10) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7), + begin8_(g8.begin()), end8_(g8.end()), current8_(current8), + begin9_(g9.begin()), end9_(g9.end()), current9_(current9), + begin10_(g10.begin()), end10_(g10.end()), current10_(current10) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current10_; + if (current10_ == end10_) { + current10_ = begin10_; + ++current9_; + } + if (current9_ == end9_) { + current9_ = begin9_; + ++current8_; + } + if (current8_ == end8_) { + current8_ = begin8_; + ++current7_; + } + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_ && + current8_ == typed_other->current8_ && + current9_ == typed_other->current9_ && + current10_ == typed_other->current10_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_), + begin8_(other.begin8_), + end8_(other.end8_), + current8_(other.current8_), + begin9_(other.begin9_), + end9_(other.end9_), + current9_(other.current9_), + begin10_(other.begin10_), + end10_(other.end10_), + current10_(other.current10_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_, + *current9_, *current10_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_ || + current8_ == end8_ || + current9_ == end9_ || + current10_ == end10_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + const typename ParamGenerator::iterator begin8_; + const typename ParamGenerator::iterator end8_; + typename ParamGenerator::iterator current8_; + const typename ParamGenerator::iterator begin9_; + const typename ParamGenerator::iterator end9_; + typename ParamGenerator::iterator current9_; + const typename ParamGenerator::iterator begin10_; + const typename ParamGenerator::iterator end10_; + typename ParamGenerator::iterator current10_; + ParamType current_value_; + }; // class CartesianProductGenerator10::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator10& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; + const ParamGenerator g8_; + const ParamGenerator g9_; + const ParamGenerator g10_; +}; // class CartesianProductGenerator10 + + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Helper classes providing Combine() with polymorphic features. They allow +// casting CartesianProductGeneratorN to ParamGenerator if T is +// convertible to U. +// +template +class CartesianProductHolder2 { + public: +CartesianProductHolder2(const Generator1& g1, const Generator2& g2) + : g1_(g1), g2_(g2) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator2( + static_cast >(g1_), + static_cast >(g2_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder2& other); + + const Generator1 g1_; + const Generator2 g2_; +}; // class CartesianProductHolder2 + +template +class CartesianProductHolder3 { + public: +CartesianProductHolder3(const Generator1& g1, const Generator2& g2, + const Generator3& g3) + : g1_(g1), g2_(g2), g3_(g3) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator3( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder3& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; +}; // class CartesianProductHolder3 + +template +class CartesianProductHolder4 { + public: +CartesianProductHolder4(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator4( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder4& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; +}; // class CartesianProductHolder4 + +template +class CartesianProductHolder5 { + public: +CartesianProductHolder5(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator5( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder5& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; +}; // class CartesianProductHolder5 + +template +class CartesianProductHolder6 { + public: +CartesianProductHolder6(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator6( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder6& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; +}; // class CartesianProductHolder6 + +template +class CartesianProductHolder7 { + public: +CartesianProductHolder7(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator7( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder7& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; +}; // class CartesianProductHolder7 + +template +class CartesianProductHolder8 { + public: +CartesianProductHolder8(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7, const Generator8& g8) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), + g8_(g8) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator8( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_), + static_cast >(g8_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder8& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; + const Generator8 g8_; +}; // class CartesianProductHolder8 + +template +class CartesianProductHolder9 { + public: +CartesianProductHolder9(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7, const Generator8& g8, + const Generator9& g9) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator9( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_), + static_cast >(g8_), + static_cast >(g9_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder9& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; + const Generator8 g8_; + const Generator9 g9_; +}; // class CartesianProductHolder9 + +template +class CartesianProductHolder10 { + public: +CartesianProductHolder10(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7, const Generator8& g8, + const Generator9& g9, const Generator10& g10) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9), g10_(g10) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator10( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_), + static_cast >(g8_), + static_cast >(g9_), + static_cast >(g10_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder10& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; + const Generator8 g8_; + const Generator9 g9_; + const Generator10 g10_; +}; // class CartesianProductHolder10 + +# endif // GTEST_HAS_COMBINE + +} // namespace internal +} // namespace testing + +#endif // GTEST_HAS_PARAM_TEST + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-param-util-generated.h.pump b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-param-util-generated.h.pump new file mode 100644 index 00000000..009206fd --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-param-util-generated.h.pump @@ -0,0 +1,301 @@ +$$ -*- mode: c++; -*- +$var n = 50 $$ Maximum length of Values arguments we want to support. +$var maxtuple = 10 $$ Maximum number of Combine arguments we want to support. +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) + +// Type and function utilities for implementing parameterized tests. +// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +// Currently Google Test supports at most $n arguments in Values, +// and at most $maxtuple arguments in Combine. Please contact +// googletestframework@googlegroups.com if you need more. +// Please note that the number of arguments to Combine is limited +// by the maximum arity of the implementation of tr1::tuple which is +// currently set at $maxtuple. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ + +// scripts/fuse_gtest.py depends on gtest's own header being #included +// *unconditionally*. Therefore these #includes cannot be moved +// inside #if GTEST_HAS_PARAM_TEST. +#include "gtest/internal/gtest-param-util.h" +#include "gtest/internal/gtest-port.h" + +#if GTEST_HAS_PARAM_TEST + +namespace testing { + +// Forward declarations of ValuesIn(), which is implemented in +// include/gtest/gtest-param-test.h. +template +internal::ParamGenerator< + typename ::testing::internal::IteratorTraits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end); + +template +internal::ParamGenerator ValuesIn(const T (&array)[N]); + +template +internal::ParamGenerator ValuesIn( + const Container& container); + +namespace internal { + +// Used in the Values() function to provide polymorphic capabilities. +template +class ValueArray1 { + public: + explicit ValueArray1(T1 v1) : v1_(v1) {} + + template + operator ParamGenerator() const { return ValuesIn(&v1_, &v1_ + 1); } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray1& other); + + const T1 v1_; +}; + +$range i 2..n +$for i [[ +$range j 1..i + +template <$for j, [[typename T$j]]> +class ValueArray$i { + public: + ValueArray$i($for j, [[T$j v$j]]) : $for j, [[v$(j)_(v$j)]] {} + + template + operator ParamGenerator() const { + const T array[] = {$for j, [[static_cast(v$(j)_)]]}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray$i& other); + +$for j [[ + + const T$j v$(j)_; +]] + +}; + +]] + +# if GTEST_HAS_COMBINE +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Generates values from the Cartesian product of values produced +// by the argument generators. +// +$range i 2..maxtuple +$for i [[ +$range j 1..i +$range k 2..i + +template <$for j, [[typename T$j]]> +class CartesianProductGenerator$i + : public ParamGeneratorInterface< ::std::tr1::tuple<$for j, [[T$j]]> > { + public: + typedef ::std::tr1::tuple<$for j, [[T$j]]> ParamType; + + CartesianProductGenerator$i($for j, [[const ParamGenerator& g$j]]) + : $for j, [[g$(j)_(g$j)]] {} + virtual ~CartesianProductGenerator$i() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, $for j, [[g$(j)_, g$(j)_.begin()]]); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, $for j, [[g$(j)_, g$(j)_.end()]]); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, $for j, [[ + + const ParamGenerator& g$j, + const typename ParamGenerator::iterator& current$(j)]]) + : base_(base), +$for j, [[ + + begin$(j)_(g$j.begin()), end$(j)_(g$j.end()), current$(j)_(current$j) +]] { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current$(i)_; + +$for k [[ + if (current$(i+2-k)_ == end$(i+2-k)_) { + current$(i+2-k)_ = begin$(i+2-k)_; + ++current$(i+2-k-1)_; + } + +]] + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ($for j && [[ + + current$(j)_ == typed_other->current$(j)_ +]]); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), $for j, [[ + + begin$(j)_(other.begin$(j)_), + end$(j)_(other.end$(j)_), + current$(j)_(other.current$(j)_) +]] { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType($for j, [[*current$(j)_]]); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return +$for j || [[ + + current$(j)_ == end$(j)_ +]]; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. +$for j [[ + + const typename ParamGenerator::iterator begin$(j)_; + const typename ParamGenerator::iterator end$(j)_; + typename ParamGenerator::iterator current$(j)_; +]] + + ParamType current_value_; + }; // class CartesianProductGenerator$i::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator$i& other); + + +$for j [[ + const ParamGenerator g$(j)_; + +]] +}; // class CartesianProductGenerator$i + + +]] + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Helper classes providing Combine() with polymorphic features. They allow +// casting CartesianProductGeneratorN to ParamGenerator if T is +// convertible to U. +// +$range i 2..maxtuple +$for i [[ +$range j 1..i + +template <$for j, [[class Generator$j]]> +class CartesianProductHolder$i { + public: +CartesianProductHolder$i($for j, [[const Generator$j& g$j]]) + : $for j, [[g$(j)_(g$j)]] {} + template <$for j, [[typename T$j]]> + operator ParamGenerator< ::std::tr1::tuple<$for j, [[T$j]]> >() const { + return ParamGenerator< ::std::tr1::tuple<$for j, [[T$j]]> >( + new CartesianProductGenerator$i<$for j, [[T$j]]>( +$for j,[[ + + static_cast >(g$(j)_) +]])); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder$i& other); + + +$for j [[ + const Generator$j g$(j)_; + +]] +}; // class CartesianProductHolder$i + +]] + +# endif // GTEST_HAS_COMBINE + +} // namespace internal +} // namespace testing + +#endif // GTEST_HAS_PARAM_TEST + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-param-util.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-param-util.h new file mode 100644 index 00000000..d5e1028b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-param-util.h @@ -0,0 +1,619 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) + +// Type and function utilities for implementing parameterized tests. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ + +#include +#include +#include + +// scripts/fuse_gtest.py depends on gtest's own header being #included +// *unconditionally*. Therefore these #includes cannot be moved +// inside #if GTEST_HAS_PARAM_TEST. +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-linked_ptr.h" +#include "gtest/internal/gtest-port.h" +#include "gtest/gtest-printers.h" + +#if GTEST_HAS_PARAM_TEST + +namespace testing { +namespace internal { + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Outputs a message explaining invalid registration of different +// fixture class for the same test case. This may happen when +// TEST_P macro is used to define two tests with the same name +// but in different namespaces. +GTEST_API_ void ReportInvalidTestCaseType(const char* test_case_name, + const char* file, int line); + +template class ParamGeneratorInterface; +template class ParamGenerator; + +// Interface for iterating over elements provided by an implementation +// of ParamGeneratorInterface. +template +class ParamIteratorInterface { + public: + virtual ~ParamIteratorInterface() {} + // A pointer to the base generator instance. + // Used only for the purposes of iterator comparison + // to make sure that two iterators belong to the same generator. + virtual const ParamGeneratorInterface* BaseGenerator() const = 0; + // Advances iterator to point to the next element + // provided by the generator. The caller is responsible + // for not calling Advance() on an iterator equal to + // BaseGenerator()->End(). + virtual void Advance() = 0; + // Clones the iterator object. Used for implementing copy semantics + // of ParamIterator. + virtual ParamIteratorInterface* Clone() const = 0; + // Dereferences the current iterator and provides (read-only) access + // to the pointed value. It is the caller's responsibility not to call + // Current() on an iterator equal to BaseGenerator()->End(). + // Used for implementing ParamGenerator::operator*(). + virtual const T* Current() const = 0; + // Determines whether the given iterator and other point to the same + // element in the sequence generated by the generator. + // Used for implementing ParamGenerator::operator==(). + virtual bool Equals(const ParamIteratorInterface& other) const = 0; +}; + +// Class iterating over elements provided by an implementation of +// ParamGeneratorInterface. It wraps ParamIteratorInterface +// and implements the const forward iterator concept. +template +class ParamIterator { + public: + typedef T value_type; + typedef const T& reference; + typedef ptrdiff_t difference_type; + + // ParamIterator assumes ownership of the impl_ pointer. + ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {} + ParamIterator& operator=(const ParamIterator& other) { + if (this != &other) + impl_.reset(other.impl_->Clone()); + return *this; + } + + const T& operator*() const { return *impl_->Current(); } + const T* operator->() const { return impl_->Current(); } + // Prefix version of operator++. + ParamIterator& operator++() { + impl_->Advance(); + return *this; + } + // Postfix version of operator++. + ParamIterator operator++(int /*unused*/) { + ParamIteratorInterface* clone = impl_->Clone(); + impl_->Advance(); + return ParamIterator(clone); + } + bool operator==(const ParamIterator& other) const { + return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_); + } + bool operator!=(const ParamIterator& other) const { + return !(*this == other); + } + + private: + friend class ParamGenerator; + explicit ParamIterator(ParamIteratorInterface* impl) : impl_(impl) {} + scoped_ptr > impl_; +}; + +// ParamGeneratorInterface is the binary interface to access generators +// defined in other translation units. +template +class ParamGeneratorInterface { + public: + typedef T ParamType; + + virtual ~ParamGeneratorInterface() {} + + // Generator interface definition + virtual ParamIteratorInterface* Begin() const = 0; + virtual ParamIteratorInterface* End() const = 0; +}; + +// Wraps ParamGeneratorInterface and provides general generator syntax +// compatible with the STL Container concept. +// This class implements copy initialization semantics and the contained +// ParamGeneratorInterface instance is shared among all copies +// of the original object. This is possible because that instance is immutable. +template +class ParamGenerator { + public: + typedef ParamIterator iterator; + + explicit ParamGenerator(ParamGeneratorInterface* impl) : impl_(impl) {} + ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {} + + ParamGenerator& operator=(const ParamGenerator& other) { + impl_ = other.impl_; + return *this; + } + + iterator begin() const { return iterator(impl_->Begin()); } + iterator end() const { return iterator(impl_->End()); } + + private: + linked_ptr > impl_; +}; + +// Generates values from a range of two comparable values. Can be used to +// generate sequences of user-defined types that implement operator+() and +// operator<(). +// This class is used in the Range() function. +template +class RangeGenerator : public ParamGeneratorInterface { + public: + RangeGenerator(T begin, T end, IncrementT step) + : begin_(begin), end_(end), + step_(step), end_index_(CalculateEndIndex(begin, end, step)) {} + virtual ~RangeGenerator() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, begin_, 0, step_); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, end_, end_index_, step_); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, T value, int index, + IncrementT step) + : base_(base), value_(value), index_(index), step_(step) {} + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + virtual void Advance() { + value_ = value_ + step_; + index_++; + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const T* Current() const { return &value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const int other_index = + CheckedDowncastToActualType(&other)->index_; + return index_ == other_index; + } + + private: + Iterator(const Iterator& other) + : ParamIteratorInterface(), + base_(other.base_), value_(other.value_), index_(other.index_), + step_(other.step_) {} + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + T value_; + int index_; + const IncrementT step_; + }; // class RangeGenerator::Iterator + + static int CalculateEndIndex(const T& begin, + const T& end, + const IncrementT& step) { + int end_index = 0; + for (T i = begin; i < end; i = i + step) + end_index++; + return end_index; + } + + // No implementation - assignment is unsupported. + void operator=(const RangeGenerator& other); + + const T begin_; + const T end_; + const IncrementT step_; + // The index for the end() iterator. All the elements in the generated + // sequence are indexed (0-based) to aid iterator comparison. + const int end_index_; +}; // class RangeGenerator + + +// Generates values from a pair of STL-style iterators. Used in the +// ValuesIn() function. The elements are copied from the source range +// since the source can be located on the stack, and the generator +// is likely to persist beyond that stack frame. +template +class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface { + public: + template + ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end) + : container_(begin, end) {} + virtual ~ValuesInIteratorRangeGenerator() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, container_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, container_.end()); + } + + private: + typedef typename ::std::vector ContainerType; + + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + typename ContainerType::const_iterator iterator) + : base_(base), iterator_(iterator) {} + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + virtual void Advance() { + ++iterator_; + value_.reset(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + // We need to use cached value referenced by iterator_ because *iterator_ + // can return a temporary object (and of type other then T), so just + // having "return &*iterator_;" doesn't work. + // value_ is updated here and not in Advance() because Advance() + // can advance iterator_ beyond the end of the range, and we cannot + // detect that fact. The client code, on the other hand, is + // responsible for not calling Current() on an out-of-range iterator. + virtual const T* Current() const { + if (value_.get() == NULL) + value_.reset(new T(*iterator_)); + return value_.get(); + } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + return iterator_ == + CheckedDowncastToActualType(&other)->iterator_; + } + + private: + Iterator(const Iterator& other) + // The explicit constructor call suppresses a false warning + // emitted by gcc when supplied with the -Wextra option. + : ParamIteratorInterface(), + base_(other.base_), + iterator_(other.iterator_) {} + + const ParamGeneratorInterface* const base_; + typename ContainerType::const_iterator iterator_; + // A cached value of *iterator_. We keep it here to allow access by + // pointer in the wrapping iterator's operator->(). + // value_ needs to be mutable to be accessed in Current(). + // Use of scoped_ptr helps manage cached value's lifetime, + // which is bound by the lifespan of the iterator itself. + mutable scoped_ptr value_; + }; // class ValuesInIteratorRangeGenerator::Iterator + + // No implementation - assignment is unsupported. + void operator=(const ValuesInIteratorRangeGenerator& other); + + const ContainerType container_; +}; // class ValuesInIteratorRangeGenerator + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Stores a parameter value and later creates tests parameterized with that +// value. +template +class ParameterizedTestFactory : public TestFactoryBase { + public: + typedef typename TestClass::ParamType ParamType; + explicit ParameterizedTestFactory(ParamType parameter) : + parameter_(parameter) {} + virtual Test* CreateTest() { + TestClass::SetParam(¶meter_); + return new TestClass(); + } + + private: + const ParamType parameter_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactoryBase is a base class for meta-factories that create +// test factories for passing into MakeAndRegisterTestInfo function. +template +class TestMetaFactoryBase { + public: + virtual ~TestMetaFactoryBase() {} + + virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactory creates test factories for passing into +// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives +// ownership of test factory pointer, same factory object cannot be passed +// into that method twice. But ParameterizedTestCaseInfo is going to call +// it for each Test/Parameter value combination. Thus it needs meta factory +// creator class. +template +class TestMetaFactory + : public TestMetaFactoryBase { + public: + typedef typename TestCase::ParamType ParamType; + + TestMetaFactory() {} + + virtual TestFactoryBase* CreateTestFactory(ParamType parameter) { + return new ParameterizedTestFactory(parameter); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestMetaFactory); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestCaseInfoBase is a generic interface +// to ParameterizedTestCaseInfo classes. ParameterizedTestCaseInfoBase +// accumulates test information provided by TEST_P macro invocations +// and generators provided by INSTANTIATE_TEST_CASE_P macro invocations +// and uses that information to register all resulting test instances +// in RegisterTests method. The ParameterizeTestCaseRegistry class holds +// a collection of pointers to the ParameterizedTestCaseInfo objects +// and calls RegisterTests() on each of them when asked. +class ParameterizedTestCaseInfoBase { + public: + virtual ~ParameterizedTestCaseInfoBase() {} + + // Base part of test case name for display purposes. + virtual const string& GetTestCaseName() const = 0; + // Test case id to verify identity. + virtual TypeId GetTestCaseTypeId() const = 0; + // UnitTest class invokes this method to register tests in this + // test case right before running them in RUN_ALL_TESTS macro. + // This method should not be called more then once on any single + // instance of a ParameterizedTestCaseInfoBase derived class. + virtual void RegisterTests() = 0; + + protected: + ParameterizedTestCaseInfoBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfoBase); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestCaseInfo accumulates tests obtained from TEST_P +// macro invocations for a particular test case and generators +// obtained from INSTANTIATE_TEST_CASE_P macro invocations for that +// test case. It registers tests with all values generated by all +// generators when asked. +template +class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { + public: + // ParamType and GeneratorCreationFunc are private types but are required + // for declarations of public methods AddTestPattern() and + // AddTestCaseInstantiation(). + typedef typename TestCase::ParamType ParamType; + // A function that returns an instance of appropriate generator type. + typedef ParamGenerator(GeneratorCreationFunc)(); + + explicit ParameterizedTestCaseInfo(const char* name) + : test_case_name_(name) {} + + // Test case base name for display purposes. + virtual const string& GetTestCaseName() const { return test_case_name_; } + // Test case id to verify identity. + virtual TypeId GetTestCaseTypeId() const { return GetTypeId(); } + // TEST_P macro uses AddTestPattern() to record information + // about a single test in a LocalTestInfo structure. + // test_case_name is the base name of the test case (without invocation + // prefix). test_base_name is the name of an individual test without + // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is + // test case base name and DoBar is test base name. + void AddTestPattern(const char* test_case_name, + const char* test_base_name, + TestMetaFactoryBase* meta_factory) { + tests_.push_back(linked_ptr(new TestInfo(test_case_name, + test_base_name, + meta_factory))); + } + // INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information + // about a generator. + int AddTestCaseInstantiation(const string& instantiation_name, + GeneratorCreationFunc* func, + const char* /* file */, + int /* line */) { + instantiations_.push_back(::std::make_pair(instantiation_name, func)); + return 0; // Return value used only to run this method in namespace scope. + } + // UnitTest class invokes this method to register tests in this test case + // test cases right before running tests in RUN_ALL_TESTS macro. + // This method should not be called more then once on any single + // instance of a ParameterizedTestCaseInfoBase derived class. + // UnitTest has a guard to prevent from calling this method more then once. + virtual void RegisterTests() { + for (typename TestInfoContainer::iterator test_it = tests_.begin(); + test_it != tests_.end(); ++test_it) { + linked_ptr test_info = *test_it; + for (typename InstantiationContainer::iterator gen_it = + instantiations_.begin(); gen_it != instantiations_.end(); + ++gen_it) { + const string& instantiation_name = gen_it->first; + ParamGenerator generator((*gen_it->second)()); + + string test_case_name; + if ( !instantiation_name.empty() ) + test_case_name = instantiation_name + "/"; + test_case_name += test_info->test_case_base_name; + + int i = 0; + for (typename ParamGenerator::iterator param_it = + generator.begin(); + param_it != generator.end(); ++param_it, ++i) { + Message test_name_stream; + test_name_stream << test_info->test_base_name << "/" << i; + MakeAndRegisterTestInfo( + test_case_name.c_str(), + test_name_stream.GetString().c_str(), + NULL, // No type parameter. + PrintToString(*param_it).c_str(), + GetTestCaseTypeId(), + TestCase::SetUpTestCase, + TestCase::TearDownTestCase, + test_info->test_meta_factory->CreateTestFactory(*param_it)); + } // for param_it + } // for gen_it + } // for test_it + } // RegisterTests + + private: + // LocalTestInfo structure keeps information about a single test registered + // with TEST_P macro. + struct TestInfo { + TestInfo(const char* a_test_case_base_name, + const char* a_test_base_name, + TestMetaFactoryBase* a_test_meta_factory) : + test_case_base_name(a_test_case_base_name), + test_base_name(a_test_base_name), + test_meta_factory(a_test_meta_factory) {} + + const string test_case_base_name; + const string test_base_name; + const scoped_ptr > test_meta_factory; + }; + typedef ::std::vector > TestInfoContainer; + // Keeps pairs of + // received from INSTANTIATE_TEST_CASE_P macros. + typedef ::std::vector > + InstantiationContainer; + + const string test_case_name_; + TestInfoContainer tests_; + InstantiationContainer instantiations_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfo); +}; // class ParameterizedTestCaseInfo + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestCaseRegistry contains a map of ParameterizedTestCaseInfoBase +// classes accessed by test case names. TEST_P and INSTANTIATE_TEST_CASE_P +// macros use it to locate their corresponding ParameterizedTestCaseInfo +// descriptors. +class ParameterizedTestCaseRegistry { + public: + ParameterizedTestCaseRegistry() {} + ~ParameterizedTestCaseRegistry() { + for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); + it != test_case_infos_.end(); ++it) { + delete *it; + } + } + + // Looks up or creates and returns a structure containing information about + // tests and instantiations of a particular test case. + template + ParameterizedTestCaseInfo* GetTestCasePatternHolder( + const char* test_case_name, + const char* file, + int line) { + ParameterizedTestCaseInfo* typed_test_info = NULL; + for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); + it != test_case_infos_.end(); ++it) { + if ((*it)->GetTestCaseName() == test_case_name) { + if ((*it)->GetTestCaseTypeId() != GetTypeId()) { + // Complain about incorrect usage of Google Test facilities + // and terminate the program since we cannot guaranty correct + // test case setup and tear-down in this case. + ReportInvalidTestCaseType(test_case_name, file, line); + posix::Abort(); + } else { + // At this point we are sure that the object we found is of the same + // type we are looking for, so we downcast it to that type + // without further checks. + typed_test_info = CheckedDowncastToActualType< + ParameterizedTestCaseInfo >(*it); + } + break; + } + } + if (typed_test_info == NULL) { + typed_test_info = new ParameterizedTestCaseInfo(test_case_name); + test_case_infos_.push_back(typed_test_info); + } + return typed_test_info; + } + void RegisterTests() { + for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); + it != test_case_infos_.end(); ++it) { + (*it)->RegisterTests(); + } + } + + private: + typedef ::std::vector TestCaseInfoContainer; + + TestCaseInfoContainer test_case_infos_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseRegistry); +}; + +} // namespace internal +} // namespace testing + +#endif // GTEST_HAS_PARAM_TEST + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-port.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-port.h new file mode 100644 index 00000000..dc4fe0cb --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-port.h @@ -0,0 +1,1947 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan) +// +// Low-level types and utilities for porting Google Test to various +// platforms. They are subject to change without notice. DO NOT USE +// THEM IN USER CODE. +// +// This file is fundamental to Google Test. All other Google Test source +// files are expected to #include this. Therefore, it cannot #include +// any other Google Test header. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +// The user can define the following macros in the build script to +// control Google Test's behavior. If the user doesn't define a macro +// in this list, Google Test will define it. +// +// GTEST_HAS_CLONE - Define it to 1/0 to indicate that clone(2) +// is/isn't available. +// GTEST_HAS_EXCEPTIONS - Define it to 1/0 to indicate that exceptions +// are enabled. +// GTEST_HAS_GLOBAL_STRING - Define it to 1/0 to indicate that ::string +// is/isn't available (some systems define +// ::string, which is different to std::string). +// GTEST_HAS_GLOBAL_WSTRING - Define it to 1/0 to indicate that ::string +// is/isn't available (some systems define +// ::wstring, which is different to std::wstring). +// GTEST_HAS_POSIX_RE - Define it to 1/0 to indicate that POSIX regular +// expressions are/aren't available. +// GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that +// is/isn't available. +// GTEST_HAS_RTTI - Define it to 1/0 to indicate that RTTI is/isn't +// enabled. +// GTEST_HAS_STD_WSTRING - Define it to 1/0 to indicate that +// std::wstring does/doesn't work (Google Test can +// be used where std::wstring is unavailable). +// GTEST_HAS_TR1_TUPLE - Define it to 1/0 to indicate tr1::tuple +// is/isn't available. +// GTEST_HAS_SEH - Define it to 1/0 to indicate whether the +// compiler supports Microsoft's "Structured +// Exception Handling". +// GTEST_HAS_STREAM_REDIRECTION +// - Define it to 1/0 to indicate whether the +// platform supports I/O stream redirection using +// dup() and dup2(). +// GTEST_USE_OWN_TR1_TUPLE - Define it to 1/0 to indicate whether Google +// Test's own tr1 tuple implementation should be +// used. Unused when the user sets +// GTEST_HAS_TR1_TUPLE to 0. +// GTEST_LANG_CXX11 - Define it to 1/0 to indicate that Google Test +// is building in C++11/C++98 mode. +// GTEST_LINKED_AS_SHARED_LIBRARY +// - Define to 1 when compiling tests that use +// Google Test as a shared library (known as +// DLL on Windows). +// GTEST_CREATE_SHARED_LIBRARY +// - Define to 1 when compiling Google Test itself +// as a shared library. + +// This header defines the following utilities: +// +// Macros indicating the current platform (defined to 1 if compiled on +// the given platform; otherwise undefined): +// GTEST_OS_AIX - IBM AIX +// GTEST_OS_CYGWIN - Cygwin +// GTEST_OS_HPUX - HP-UX +// GTEST_OS_LINUX - Linux +// GTEST_OS_LINUX_ANDROID - Google Android +// GTEST_OS_MAC - Mac OS X +// GTEST_OS_IOS - iOS +// GTEST_OS_IOS_SIMULATOR - iOS simulator +// GTEST_OS_NACL - Google Native Client (NaCl) +// GTEST_OS_OPENBSD - OpenBSD +// GTEST_OS_QNX - QNX +// GTEST_OS_SOLARIS - Sun Solaris +// GTEST_OS_SYMBIAN - Symbian +// GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile) +// GTEST_OS_WINDOWS_DESKTOP - Windows Desktop +// GTEST_OS_WINDOWS_MINGW - MinGW +// GTEST_OS_WINDOWS_MOBILE - Windows Mobile +// GTEST_OS_ZOS - z/OS +// +// Among the platforms, Cygwin, Linux, Max OS X, and Windows have the +// most stable support. Since core members of the Google Test project +// don't have access to other platforms, support for them may be less +// stable. If you notice any problems on your platform, please notify +// googletestframework@googlegroups.com (patches for fixing them are +// even more welcome!). +// +// Note that it is possible that none of the GTEST_OS_* macros are defined. +// +// Macros indicating available Google Test features (defined to 1 if +// the corresponding feature is supported; otherwise undefined): +// GTEST_HAS_COMBINE - the Combine() function (for value-parameterized +// tests) +// GTEST_HAS_DEATH_TEST - death tests +// GTEST_HAS_PARAM_TEST - value-parameterized tests +// GTEST_HAS_TYPED_TEST - typed tests +// GTEST_HAS_TYPED_TEST_P - type-parameterized tests +// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with +// GTEST_HAS_POSIX_RE (see above) which users can +// define themselves. +// GTEST_USES_SIMPLE_RE - our own simple regex is used; +// the above two are mutually exclusive. +// GTEST_CAN_COMPARE_NULL - accepts untyped NULL in EXPECT_EQ(). +// +// Macros for basic C++ coding: +// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning. +// GTEST_ATTRIBUTE_UNUSED_ - declares that a class' instances or a +// variable don't have to be used. +// GTEST_DISALLOW_ASSIGN_ - disables operator=. +// GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=. +// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used. +// +// Synchronization: +// Mutex, MutexLock, ThreadLocal, GetThreadCount() +// - synchronization primitives. +// GTEST_IS_THREADSAFE - defined to 1 to indicate that the above +// synchronization primitives have real implementations +// and Google Test is thread-safe; or 0 otherwise. +// +// Template meta programming: +// is_pointer - as in TR1; needed on Symbian and IBM XL C/C++ only. +// IteratorTraits - partial implementation of std::iterator_traits, which +// is not available in libCstd when compiled with Sun C++. +// +// Smart pointers: +// scoped_ptr - as in TR2. +// +// Regular expressions: +// RE - a simple regular expression class using the POSIX +// Extended Regular Expression syntax on UNIX-like +// platforms, or a reduced regular exception syntax on +// other platforms, including Windows. +// +// Logging: +// GTEST_LOG_() - logs messages at the specified severity level. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. +// +// Stdout and stderr capturing: +// CaptureStdout() - starts capturing stdout. +// GetCapturedStdout() - stops capturing stdout and returns the captured +// string. +// CaptureStderr() - starts capturing stderr. +// GetCapturedStderr() - stops capturing stderr and returns the captured +// string. +// +// Integer types: +// TypeWithSize - maps an integer to a int type. +// Int32, UInt32, Int64, UInt64, TimeInMillis +// - integers of known sizes. +// BiggestInt - the biggest signed integer type. +// +// Command-line utilities: +// GTEST_FLAG() - references a flag. +// GTEST_DECLARE_*() - declares a flag. +// GTEST_DEFINE_*() - defines a flag. +// GetInjectableArgvs() - returns the command line as a vector of strings. +// +// Environment variable utilities: +// GetEnv() - gets the value of an environment variable. +// BoolFromGTestEnv() - parses a bool environment variable. +// Int32FromGTestEnv() - parses an Int32 environment variable. +// StringFromGTestEnv() - parses a string environment variable. + +#include // for isspace, etc +#include // for ptrdiff_t +#include +#include +#include +#ifndef _WIN32_WCE +# include +# include +#endif // !_WIN32_WCE + +#if defined __APPLE__ +# include +# include +#endif + +#include // NOLINT +#include // NOLINT +#include // NOLINT + +#define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com" +#define GTEST_FLAG_PREFIX_ "gtest_" +#define GTEST_FLAG_PREFIX_DASH_ "gtest-" +#define GTEST_FLAG_PREFIX_UPPER_ "GTEST_" +#define GTEST_NAME_ "Google Test" +#define GTEST_PROJECT_URL_ "http://code.google.com/p/googletest/" + +// Determines the version of gcc that is used to compile this. +#ifdef __GNUC__ +// 40302 means version 4.3.2. +# define GTEST_GCC_VER_ \ + (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__) +#endif // __GNUC__ + +// Determines the platform on which Google Test is compiled. +#ifdef __CYGWIN__ +# define GTEST_OS_CYGWIN 1 +#elif defined __SYMBIAN32__ +# define GTEST_OS_SYMBIAN 1 +#elif defined _WIN32 +# define GTEST_OS_WINDOWS 1 +# ifdef _WIN32_WCE +# define GTEST_OS_WINDOWS_MOBILE 1 +# elif defined(__MINGW__) || defined(__MINGW32__) +# define GTEST_OS_WINDOWS_MINGW 1 +# else +# define GTEST_OS_WINDOWS_DESKTOP 1 +# endif // _WIN32_WCE +#elif defined __APPLE__ +# define GTEST_OS_MAC 1 +# if TARGET_OS_IPHONE +# define GTEST_OS_IOS 1 +# if TARGET_IPHONE_SIMULATOR +# define GTEST_OS_IOS_SIMULATOR 1 +# endif +# endif +#elif defined __linux__ +# define GTEST_OS_LINUX 1 +# if defined __ANDROID__ +# define GTEST_OS_LINUX_ANDROID 1 +# endif +#elif defined __MVS__ +# define GTEST_OS_ZOS 1 +#elif defined(__sun) && defined(__SVR4) +# define GTEST_OS_SOLARIS 1 +#elif defined(_AIX) +# define GTEST_OS_AIX 1 +#elif defined(__hpux) +# define GTEST_OS_HPUX 1 +#elif defined __native_client__ +# define GTEST_OS_NACL 1 +#elif defined __OpenBSD__ +# define GTEST_OS_OPENBSD 1 +#elif defined __QNX__ +# define GTEST_OS_QNX 1 +#endif // __CYGWIN__ + +#ifndef GTEST_LANG_CXX11 +// gcc and clang define __GXX_EXPERIMENTAL_CXX0X__ when +// -std={c,gnu}++{0x,11} is passed. The C++11 standard specifies a +// value for __cplusplus, and recent versions of clang, gcc, and +// probably other compilers set that too in C++11 mode. +# if __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L +// Compiling in at least C++11 mode. +# define GTEST_LANG_CXX11 1 +# else +# define GTEST_LANG_CXX11 0 +# endif +#endif + +// Brings in definitions for functions used in the testing::internal::posix +// namespace (read, write, close, chdir, isatty, stat). We do not currently +// use them on Windows Mobile. +#if !GTEST_OS_WINDOWS +// This assumes that non-Windows OSes provide unistd.h. For OSes where this +// is not the case, we need to include headers that provide the functions +// mentioned above. +# include +# include +#elif !GTEST_OS_WINDOWS_MOBILE +# include +# include +#endif + +#if GTEST_OS_LINUX_ANDROID +// Used to define __ANDROID_API__ matching the target NDK API level. +# include // NOLINT +#endif + +// Defines this to true iff Google Test can use POSIX regular expressions. +#ifndef GTEST_HAS_POSIX_RE +# if GTEST_OS_LINUX_ANDROID +// On Android, is only available starting with Gingerbread. +# define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9) +# else +# define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS) +# endif +#endif + +#if GTEST_HAS_POSIX_RE + +// On some platforms, needs someone to define size_t, and +// won't compile otherwise. We can #include it here as we already +// included , which is guaranteed to define size_t through +// . +# include // NOLINT + +# define GTEST_USES_POSIX_RE 1 + +#elif GTEST_OS_WINDOWS + +// is not available on Windows. Use our own simple regex +// implementation instead. +# define GTEST_USES_SIMPLE_RE 1 + +#else + +// may not be available on this platform. Use our own +// simple regex implementation instead. +# define GTEST_USES_SIMPLE_RE 1 + +#endif // GTEST_HAS_POSIX_RE + +#ifndef GTEST_HAS_EXCEPTIONS +// The user didn't tell us whether exceptions are enabled, so we need +// to figure it out. +# if defined(_MSC_VER) || defined(__BORLANDC__) +// MSVC's and C++Builder's implementations of the STL use the _HAS_EXCEPTIONS +// macro to enable exceptions, so we'll do the same. +// Assumes that exceptions are enabled by default. +# ifndef _HAS_EXCEPTIONS +# define _HAS_EXCEPTIONS 1 +# endif // _HAS_EXCEPTIONS +# define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS +# elif defined(__GNUC__) && __EXCEPTIONS +// gcc defines __EXCEPTIONS to 1 iff exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__SUNPRO_CC) +// Sun Pro CC supports exceptions. However, there is no compile-time way of +// detecting whether they are enabled or not. Therefore, we assume that +// they are enabled unless the user tells us otherwise. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__IBMCPP__) && __EXCEPTIONS +// xlC defines __EXCEPTIONS to 1 iff exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__HP_aCC) +// Exception handling is in effect by default in HP aCC compiler. It has to +// be turned of by +noeh compiler option if desired. +# define GTEST_HAS_EXCEPTIONS 1 +# else +// For other compilers, we assume exceptions are disabled to be +// conservative. +# define GTEST_HAS_EXCEPTIONS 0 +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +#endif // GTEST_HAS_EXCEPTIONS + +#if !defined(GTEST_HAS_STD_STRING) +// Even though we don't use this macro any longer, we keep it in case +// some clients still depend on it. +# define GTEST_HAS_STD_STRING 1 +#elif !GTEST_HAS_STD_STRING +// The user told us that ::std::string isn't available. +# error "Google Test cannot be used where ::std::string isn't available." +#endif // !defined(GTEST_HAS_STD_STRING) + +#ifndef GTEST_HAS_GLOBAL_STRING +// The user didn't tell us whether ::string is available, so we need +// to figure it out. + +# define GTEST_HAS_GLOBAL_STRING 0 + +#endif // GTEST_HAS_GLOBAL_STRING + +#ifndef GTEST_HAS_STD_WSTRING +// The user didn't tell us whether ::std::wstring is available, so we need +// to figure it out. +// TODO(wan@google.com): uses autoconf to detect whether ::std::wstring +// is available. + +// Cygwin 1.7 and below doesn't support ::std::wstring. +// Solaris' libc++ doesn't support it either. Android has +// no support for it at least as recent as Froyo (2.2). +# define GTEST_HAS_STD_WSTRING \ + (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS)) + +#endif // GTEST_HAS_STD_WSTRING + +#ifndef GTEST_HAS_GLOBAL_WSTRING +// The user didn't tell us whether ::wstring is available, so we need +// to figure it out. +# define GTEST_HAS_GLOBAL_WSTRING \ + (GTEST_HAS_STD_WSTRING && GTEST_HAS_GLOBAL_STRING) +#endif // GTEST_HAS_GLOBAL_WSTRING + +// Determines whether RTTI is available. +#ifndef GTEST_HAS_RTTI +// The user didn't tell us whether RTTI is enabled, so we need to +// figure it out. + +# ifdef _MSC_VER + +# ifdef _CPPRTTI // MSVC defines this macro iff RTTI is enabled. +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif + +// Starting with version 4.3.2, gcc defines __GXX_RTTI iff RTTI is enabled. +# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40302) + +# ifdef __GXX_RTTI +// When building against STLport with the Android NDK and with +// -frtti -fno-exceptions, the build fails at link time with undefined +// references to __cxa_bad_typeid. Note sure if STL or toolchain bug, +// so disable RTTI when detected. +# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) && \ + !defined(__EXCEPTIONS) +# define GTEST_HAS_RTTI 0 +# else +# define GTEST_HAS_RTTI 1 +# endif // GTEST_OS_LINUX_ANDROID && __STLPORT_MAJOR && !__EXCEPTIONS +# else +# define GTEST_HAS_RTTI 0 +# endif // __GXX_RTTI + +// Clang defines __GXX_RTTI starting with version 3.0, but its manual recommends +// using has_feature instead. has_feature(cxx_rtti) is supported since 2.7, the +// first version with C++ support. +# elif defined(__clang__) + +# define GTEST_HAS_RTTI __has_feature(cxx_rtti) + +// Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if +// both the typeid and dynamic_cast features are present. +# elif defined(__IBMCPP__) && (__IBMCPP__ >= 900) + +# ifdef __RTTI_ALL__ +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif + +# else + +// For all other compilers, we assume RTTI is enabled. +# define GTEST_HAS_RTTI 1 + +# endif // _MSC_VER + +#endif // GTEST_HAS_RTTI + +// It's this header's responsibility to #include when RTTI +// is enabled. +#if GTEST_HAS_RTTI +# include +#endif + +// Determines whether Google Test can use the pthreads library. +#ifndef GTEST_HAS_PTHREAD +// The user didn't tell us explicitly, so we assume pthreads support is +// available on Linux and Mac. +// +// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0 +// to your compiler flags. +# define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX \ + || GTEST_OS_QNX) +#endif // GTEST_HAS_PTHREAD + +#if GTEST_HAS_PTHREAD +// gtest-port.h guarantees to #include when GTEST_HAS_PTHREAD is +// true. +# include // NOLINT + +// For timespec and nanosleep, used below. +# include // NOLINT +#endif + +// Determines whether Google Test can use tr1/tuple. You can define +// this macro to 0 to prevent Google Test from using tuple (any +// feature depending on tuple with be disabled in this mode). +#ifndef GTEST_HAS_TR1_TUPLE +# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) +// STLport, provided with the Android NDK, has neither or . +# define GTEST_HAS_TR1_TUPLE 0 +# else +// The user didn't tell us not to do it, so we assume it's OK. +# define GTEST_HAS_TR1_TUPLE 1 +# endif +#endif // GTEST_HAS_TR1_TUPLE + +// Determines whether Google Test's own tr1 tuple implementation +// should be used. +#ifndef GTEST_USE_OWN_TR1_TUPLE +// The user didn't tell us, so we need to figure it out. + +// We use our own TR1 tuple if we aren't sure the user has an +// implementation of it already. At this time, libstdc++ 4.0.0+ and +// MSVC 2010 are the only mainstream standard libraries that come +// with a TR1 tuple implementation. NVIDIA's CUDA NVCC compiler +// pretends to be GCC by defining __GNUC__ and friends, but cannot +// compile GCC's tuple implementation. MSVC 2008 (9.0) provides TR1 +// tuple in a 323 MB Feature Pack download, which we cannot assume the +// user has. QNX's QCC compiler is a modified GCC but it doesn't +// support TR1 tuple. libc++ only provides std::tuple, in C++11 mode, +// and it can be used with some compilers that define __GNUC__. +# if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000) \ + && !GTEST_OS_QNX && !defined(_LIBCPP_VERSION)) || _MSC_VER >= 1600 +# define GTEST_ENV_HAS_TR1_TUPLE_ 1 +# endif + +// C++11 specifies that provides std::tuple. Use that if gtest is used +// in C++11 mode and libstdc++ isn't very old (binaries targeting OS X 10.6 +// can build with clang but need to use gcc4.2's libstdc++). +# if GTEST_LANG_CXX11 && (!defined(__GLIBCXX__) || __GLIBCXX__ > 20110325) +# define GTEST_ENV_HAS_STD_TUPLE_ 1 +# endif + +# if GTEST_ENV_HAS_TR1_TUPLE_ || GTEST_ENV_HAS_STD_TUPLE_ +# define GTEST_USE_OWN_TR1_TUPLE 0 +# else +# define GTEST_USE_OWN_TR1_TUPLE 1 +# endif + +#endif // GTEST_USE_OWN_TR1_TUPLE + +// To avoid conditional compilation everywhere, we make it +// gtest-port.h's responsibility to #include the header implementing +// tr1/tuple. +#if GTEST_HAS_TR1_TUPLE + +# if GTEST_USE_OWN_TR1_TUPLE +# include "gtest/internal/gtest-tuple.h" +# elif GTEST_ENV_HAS_STD_TUPLE_ +# include +// C++11 puts its tuple into the ::std namespace rather than +// ::std::tr1. gtest expects tuple to live in ::std::tr1, so put it there. +// This causes undefined behavior, but supported compilers react in +// the way we intend. +namespace std { +namespace tr1 { +using ::std::get; +using ::std::make_tuple; +using ::std::tuple; +using ::std::tuple_element; +using ::std::tuple_size; +} +} + +# elif GTEST_OS_SYMBIAN + +// On Symbian, BOOST_HAS_TR1_TUPLE causes Boost's TR1 tuple library to +// use STLport's tuple implementation, which unfortunately doesn't +// work as the copy of STLport distributed with Symbian is incomplete. +// By making sure BOOST_HAS_TR1_TUPLE is undefined, we force Boost to +// use its own tuple implementation. +# ifdef BOOST_HAS_TR1_TUPLE +# undef BOOST_HAS_TR1_TUPLE +# endif // BOOST_HAS_TR1_TUPLE + +// This prevents , which defines +// BOOST_HAS_TR1_TUPLE, from being #included by Boost's . +# define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED +# include + +# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40000) +// GCC 4.0+ implements tr1/tuple in the header. This does +// not conform to the TR1 spec, which requires the header to be . + +# if !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 +// Until version 4.3.2, gcc has a bug that causes , +// which is #included by , to not compile when RTTI is +// disabled. _TR1_FUNCTIONAL is the header guard for +// . Hence the following #define is a hack to prevent +// from being included. +# define _TR1_FUNCTIONAL 1 +# include +# undef _TR1_FUNCTIONAL // Allows the user to #include + // if he chooses to. +# else +# include // NOLINT +# endif // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 + +# else +// If the compiler is not GCC 4.0+, we assume the user is using a +// spec-conforming TR1 implementation. +# include // NOLINT +# endif // GTEST_USE_OWN_TR1_TUPLE + +#endif // GTEST_HAS_TR1_TUPLE + +// Determines whether clone(2) is supported. +// Usually it will only be available on Linux, excluding +// Linux on the Itanium architecture. +// Also see http://linux.die.net/man/2/clone. +#ifndef GTEST_HAS_CLONE +// The user didn't tell us, so we need to figure it out. + +# if GTEST_OS_LINUX && !defined(__ia64__) +# if GTEST_OS_LINUX_ANDROID +// On Android, clone() is only available on ARM starting with Gingerbread. +# if defined(__arm__) && __ANDROID_API__ >= 9 +# define GTEST_HAS_CLONE 1 +# else +# define GTEST_HAS_CLONE 0 +# endif +# else +# define GTEST_HAS_CLONE 1 +# endif +# else +# define GTEST_HAS_CLONE 0 +# endif // GTEST_OS_LINUX && !defined(__ia64__) + +#endif // GTEST_HAS_CLONE + +// Determines whether to support stream redirection. This is used to test +// output correctness and to implement death tests. +#ifndef GTEST_HAS_STREAM_REDIRECTION +// By default, we assume that stream redirection is supported on all +// platforms except known mobile ones. +# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN +# define GTEST_HAS_STREAM_REDIRECTION 0 +# else +# define GTEST_HAS_STREAM_REDIRECTION 1 +# endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_SYMBIAN +#endif // GTEST_HAS_STREAM_REDIRECTION + +// Determines whether to support death tests. +// Google Test does not support death tests for VC 7.1 and earlier as +// abort() in a VC 7.1 application compiled as GUI in debug config +// pops up a dialog window that cannot be suppressed programmatically. +#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ + (GTEST_OS_MAC && !GTEST_OS_IOS) || GTEST_OS_IOS_SIMULATOR || \ + (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \ + GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX || \ + GTEST_OS_OPENBSD || GTEST_OS_QNX) +# define GTEST_HAS_DEATH_TEST 1 +# include // NOLINT +#endif + +// We don't support MSVC 7.1 with exceptions disabled now. Therefore +// all the compilers we care about are adequate for supporting +// value-parameterized tests. +#define GTEST_HAS_PARAM_TEST 1 + +// Determines whether to support type-driven tests. + +// Typed tests need and variadic macros, which GCC, VC++ 8.0, +// Sun Pro CC, IBM Visual Age, and HP aCC support. +#if defined(__GNUC__) || (_MSC_VER >= 1400) || defined(__SUNPRO_CC) || \ + defined(__IBMCPP__) || defined(__HP_aCC) +# define GTEST_HAS_TYPED_TEST 1 +# define GTEST_HAS_TYPED_TEST_P 1 +#endif + +// Determines whether to support Combine(). This only makes sense when +// value-parameterized tests are enabled. The implementation doesn't +// work on Sun Studio since it doesn't understand templated conversion +// operators. +#if GTEST_HAS_PARAM_TEST && GTEST_HAS_TR1_TUPLE && !defined(__SUNPRO_CC) +# define GTEST_HAS_COMBINE 1 +#endif + +// Determines whether the system compiler uses UTF-16 for encoding wide strings. +#define GTEST_WIDE_STRING_USES_UTF16_ \ + (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_SYMBIAN || GTEST_OS_AIX) + +// Determines whether test results can be streamed to a socket. +#if GTEST_OS_LINUX +# define GTEST_CAN_STREAM_RESULTS_ 1 +#endif + +// Defines some utility macros. + +// The GNU compiler emits a warning if nested "if" statements are followed by +// an "else" statement and braces are not used to explicitly disambiguate the +// "else" binding. This leads to problems with code like: +// +// if (gate) +// ASSERT_*(condition) << "Some message"; +// +// The "switch (0) case 0:" idiom is used to suppress this. +#ifdef __INTEL_COMPILER +# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ +#else +# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ switch (0) case 0: default: // NOLINT +#endif + +// Use this annotation at the end of a struct/class definition to +// prevent the compiler from optimizing away instances that are never +// used. This is useful when all interesting logic happens inside the +// c'tor and / or d'tor. Example: +// +// struct Foo { +// Foo() { ... } +// } GTEST_ATTRIBUTE_UNUSED_; +// +// Also use it after a variable or parameter declaration to tell the +// compiler the variable/parameter does not have to be used. +#if defined(__GNUC__) && !defined(COMPILER_ICC) +# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused)) +#else +# define GTEST_ATTRIBUTE_UNUSED_ +#endif + +// A macro to disallow operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_ASSIGN_(type)\ + void operator=(type const &) + +// A macro to disallow copy constructor and operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type)\ + type(type const &);\ + GTEST_DISALLOW_ASSIGN_(type) + +// Tell the compiler to warn about unused return values for functions declared +// with this macro. The macro should be used on function declarations +// following the argument list: +// +// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_; +#if defined(__GNUC__) && (GTEST_GCC_VER_ >= 30400) && !defined(COMPILER_ICC) +# define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result)) +#else +# define GTEST_MUST_USE_RESULT_ +#endif // __GNUC__ && (GTEST_GCC_VER_ >= 30400) && !COMPILER_ICC + +// Determine whether the compiler supports Microsoft's Structured Exception +// Handling. This is supported by several Windows compilers but generally +// does not exist on any other system. +#ifndef GTEST_HAS_SEH +// The user didn't tell us, so we need to figure it out. + +# if defined(_MSC_VER) || defined(__BORLANDC__) +// These two compilers are known to support SEH. +# define GTEST_HAS_SEH 1 +# else +// Assume no SEH. +# define GTEST_HAS_SEH 0 +# endif + +#endif // GTEST_HAS_SEH + +#ifdef _MSC_VER + +# if GTEST_LINKED_AS_SHARED_LIBRARY +# define GTEST_API_ __declspec(dllimport) +# elif GTEST_CREATE_SHARED_LIBRARY +# define GTEST_API_ __declspec(dllexport) +# endif + +#endif // _MSC_VER + +#ifndef GTEST_API_ +# define GTEST_API_ +#endif + +#ifdef __GNUC__ +// Ask the compiler to never inline a given function. +# define GTEST_NO_INLINE_ __attribute__((noinline)) +#else +# define GTEST_NO_INLINE_ +#endif + +// _LIBCPP_VERSION is defined by the libc++ library from the LLVM project. +#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) +# define GTEST_HAS_CXXABI_H_ 1 +#else +# define GTEST_HAS_CXXABI_H_ 0 +#endif + +namespace testing { + +class Message; + +namespace internal { + +// A secret type that Google Test users don't know about. It has no +// definition on purpose. Therefore it's impossible to create a +// Secret object, which is what we want. +class Secret; + +// The GTEST_COMPILE_ASSERT_ macro can be used to verify that a compile time +// expression is true. For example, you could use it to verify the +// size of a static array: +// +// GTEST_COMPILE_ASSERT_(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES, +// content_type_names_incorrect_size); +// +// or to make sure a struct is smaller than a certain size: +// +// GTEST_COMPILE_ASSERT_(sizeof(foo) < 128, foo_too_large); +// +// The second argument to the macro is the name of the variable. If +// the expression is false, most compilers will issue a warning/error +// containing the name of the variable. + +template +struct CompileAssert { +}; + +#define GTEST_COMPILE_ASSERT_(expr, msg) \ + typedef ::testing::internal::CompileAssert<(static_cast(expr))> \ + msg[static_cast(expr) ? 1 : -1] GTEST_ATTRIBUTE_UNUSED_ + +// Implementation details of GTEST_COMPILE_ASSERT_: +// +// - GTEST_COMPILE_ASSERT_ works by defining an array type that has -1 +// elements (and thus is invalid) when the expression is false. +// +// - The simpler definition +// +// #define GTEST_COMPILE_ASSERT_(expr, msg) typedef char msg[(expr) ? 1 : -1] +// +// does not work, as gcc supports variable-length arrays whose sizes +// are determined at run-time (this is gcc's extension and not part +// of the C++ standard). As a result, gcc fails to reject the +// following code with the simple definition: +// +// int foo; +// GTEST_COMPILE_ASSERT_(foo, msg); // not supposed to compile as foo is +// // not a compile-time constant. +// +// - By using the type CompileAssert<(bool(expr))>, we ensures that +// expr is a compile-time constant. (Template arguments must be +// determined at compile-time.) +// +// - The outter parentheses in CompileAssert<(bool(expr))> are necessary +// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written +// +// CompileAssert +// +// instead, these compilers will refuse to compile +// +// GTEST_COMPILE_ASSERT_(5 > 0, some_message); +// +// (They seem to think the ">" in "5 > 0" marks the end of the +// template argument list.) +// +// - The array size is (bool(expr) ? 1 : -1), instead of simply +// +// ((expr) ? 1 : -1). +// +// This is to avoid running into a bug in MS VC 7.1, which +// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. + +// StaticAssertTypeEqHelper is used by StaticAssertTypeEq defined in gtest.h. +// +// This template is declared, but intentionally undefined. +template +struct StaticAssertTypeEqHelper; + +template +struct StaticAssertTypeEqHelper {}; + +#if GTEST_HAS_GLOBAL_STRING +typedef ::string string; +#else +typedef ::std::string string; +#endif // GTEST_HAS_GLOBAL_STRING + +#if GTEST_HAS_GLOBAL_WSTRING +typedef ::wstring wstring; +#elif GTEST_HAS_STD_WSTRING +typedef ::std::wstring wstring; +#endif // GTEST_HAS_GLOBAL_WSTRING + +// A helper for suppressing warnings on constant condition. It just +// returns 'condition'. +GTEST_API_ bool IsTrue(bool condition); + +// Defines scoped_ptr. + +// This implementation of scoped_ptr is PARTIAL - it only contains +// enough stuff to satisfy Google Test's need. +template +class scoped_ptr { + public: + typedef T element_type; + + explicit scoped_ptr(T* p = NULL) : ptr_(p) {} + ~scoped_ptr() { reset(); } + + T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + T* get() const { return ptr_; } + + T* release() { + T* const ptr = ptr_; + ptr_ = NULL; + return ptr; + } + + void reset(T* p = NULL) { + if (p != ptr_) { + if (IsTrue(sizeof(T) > 0)) { // Makes sure T is a complete type. + delete ptr_; + } + ptr_ = p; + } + } + + private: + T* ptr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(scoped_ptr); +}; + +// Defines RE. + +// A simple C++ wrapper for . It uses the POSIX Extended +// Regular Expression syntax. +class GTEST_API_ RE { + public: + // A copy constructor is required by the Standard to initialize object + // references from r-values. + RE(const RE& other) { Init(other.pattern()); } + + // Constructs an RE from a string. + RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT + +#if GTEST_HAS_GLOBAL_STRING + + RE(const ::string& regex) { Init(regex.c_str()); } // NOLINT + +#endif // GTEST_HAS_GLOBAL_STRING + + RE(const char* regex) { Init(regex); } // NOLINT + ~RE(); + + // Returns the string representation of the regex. + const char* pattern() const { return pattern_; } + + // FullMatch(str, re) returns true iff regular expression re matches + // the entire str. + // PartialMatch(str, re) returns true iff regular expression re + // matches a substring of str (including str itself). + // + // TODO(wan@google.com): make FullMatch() and PartialMatch() work + // when str contains NUL characters. + static bool FullMatch(const ::std::string& str, const RE& re) { + return FullMatch(str.c_str(), re); + } + static bool PartialMatch(const ::std::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } + +#if GTEST_HAS_GLOBAL_STRING + + static bool FullMatch(const ::string& str, const RE& re) { + return FullMatch(str.c_str(), re); + } + static bool PartialMatch(const ::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } + +#endif // GTEST_HAS_GLOBAL_STRING + + static bool FullMatch(const char* str, const RE& re); + static bool PartialMatch(const char* str, const RE& re); + + private: + void Init(const char* regex); + + // We use a const char* instead of an std::string, as Google Test used to be + // used where std::string is not available. TODO(wan@google.com): change to + // std::string. + const char* pattern_; + bool is_valid_; + +#if GTEST_USES_POSIX_RE + + regex_t full_regex_; // For FullMatch(). + regex_t partial_regex_; // For PartialMatch(). + +#else // GTEST_USES_SIMPLE_RE + + const char* full_pattern_; // For FullMatch(); + +#endif + + GTEST_DISALLOW_ASSIGN_(RE); +}; + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line); + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(const char* file, + int line); + +// Defines logging utilities: +// GTEST_LOG_(severity) - logs messages at the specified severity level. The +// message itself is streamed into the macro. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. + +enum GTestLogSeverity { + GTEST_INFO, + GTEST_WARNING, + GTEST_ERROR, + GTEST_FATAL +}; + +// Formats log entry severity, provides a stream object for streaming the +// log message, and terminates the message with a newline when going out of +// scope. +class GTEST_API_ GTestLog { + public: + GTestLog(GTestLogSeverity severity, const char* file, int line); + + // Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. + ~GTestLog(); + + ::std::ostream& GetStream() { return ::std::cerr; } + + private: + const GTestLogSeverity severity_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestLog); +}; + +#define GTEST_LOG_(severity) \ + ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \ + __FILE__, __LINE__).GetStream() + +inline void LogToStderr() {} +inline void FlushInfoLog() { fflush(NULL); } + +// INTERNAL IMPLEMENTATION - DO NOT USE. +// +// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition +// is not satisfied. +// Synopsys: +// GTEST_CHECK_(boolean_condition); +// or +// GTEST_CHECK_(boolean_condition) << "Additional message"; +// +// This checks the condition and if the condition is not satisfied +// it prints message about the condition violation, including the +// condition itself, plus additional message streamed into it, if any, +// and then it aborts the program. It aborts the program irrespective of +// whether it is built in the debug mode or not. +#define GTEST_CHECK_(condition) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::IsTrue(condition)) \ + ; \ + else \ + GTEST_LOG_(FATAL) << "Condition " #condition " failed. " + +// An all-mode assert to verify that the given POSIX-style function +// call returns 0 (indicating success). Known limitation: this +// doesn't expand to a balanced 'if' statement, so enclose the macro +// in {} if you need to use it as the only statement in an 'if' +// branch. +#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \ + if (const int gtest_error = (posix_call)) \ + GTEST_LOG_(FATAL) << #posix_call << "failed with error " \ + << gtest_error + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Use ImplicitCast_ as a safe version of static_cast for upcasting in +// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a +// const Foo*). When you use ImplicitCast_, the compiler checks that +// the cast is safe. Such explicit ImplicitCast_s are necessary in +// surprisingly many situations where C++ demands an exact type match +// instead of an argument type convertable to a target type. +// +// The syntax for using ImplicitCast_ is the same as for static_cast: +// +// ImplicitCast_(expr) +// +// ImplicitCast_ would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., implicit_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template +inline To ImplicitCast_(To x) { return x; } + +// When you upcast (that is, cast a pointer from type Foo to type +// SuperclassOfFoo), it's fine to use ImplicitCast_<>, since upcasts +// always succeed. When you downcast (that is, cast a pointer from +// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because +// how do you know the pointer is really of type SubclassOfFoo? It +// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus, +// when you downcast, you should use this macro. In debug mode, we +// use dynamic_cast<> to double-check the downcast is legal (we die +// if it's not). In normal mode, we do the efficient static_cast<> +// instead. Thus, it's important to test in debug mode to make sure +// the cast is legal! +// This is the only place in the code we should use dynamic_cast<>. +// In particular, you SHOULDN'T be using dynamic_cast<> in order to +// do RTTI (eg code like this: +// if (dynamic_cast(foo)) HandleASubclass1Object(foo); +// if (dynamic_cast(foo)) HandleASubclass2Object(foo); +// You should design the code some other way not to need this. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., down_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template // use like this: DownCast_(foo); +inline To DownCast_(From* f) { // so we only accept pointers + // Ensures that To is a sub-type of From *. This test is here only + // for compile-time type checking, and has no overhead in an + // optimized build at run-time, as it will be optimized away + // completely. + if (false) { + const To to = NULL; + ::testing::internal::ImplicitCast_(to); + } + +#if GTEST_HAS_RTTI + // RTTI: debug mode only! + GTEST_CHECK_(f == NULL || dynamic_cast(f) != NULL); +#endif + return static_cast(f); +} + +// Downcasts the pointer of type Base to Derived. +// Derived must be a subclass of Base. The parameter MUST +// point to a class of type Derived, not any subclass of it. +// When RTTI is available, the function performs a runtime +// check to enforce this. +template +Derived* CheckedDowncastToActualType(Base* base) { +#if GTEST_HAS_RTTI + GTEST_CHECK_(typeid(*base) == typeid(Derived)); + return dynamic_cast(base); // NOLINT +#else + return static_cast(base); // Poor man's downcast. +#endif +} + +#if GTEST_HAS_STREAM_REDIRECTION + +// Defines the stderr capturer: +// CaptureStdout - starts capturing stdout. +// GetCapturedStdout - stops capturing stdout and returns the captured string. +// CaptureStderr - starts capturing stderr. +// GetCapturedStderr - stops capturing stderr and returns the captured string. +// +GTEST_API_ void CaptureStdout(); +GTEST_API_ std::string GetCapturedStdout(); +GTEST_API_ void CaptureStderr(); +GTEST_API_ std::string GetCapturedStderr(); + +#endif // GTEST_HAS_STREAM_REDIRECTION + + +#if GTEST_HAS_DEATH_TEST + +const ::std::vector& GetInjectableArgvs(); +void SetInjectableArgvs(const ::std::vector* + new_argvs); + +// A copy of all command line arguments. Set by InitGoogleTest(). +extern ::std::vector g_argvs; + +#endif // GTEST_HAS_DEATH_TEST + +// Defines synchronization primitives. + +#if GTEST_HAS_PTHREAD + +// Sleeps for (roughly) n milli-seconds. This function is only for +// testing Google Test's own constructs. Don't use it in user tests, +// either directly or indirectly. +inline void SleepMilliseconds(int n) { + const timespec time = { + 0, // 0 seconds. + n * 1000L * 1000L, // And n ms. + }; + nanosleep(&time, NULL); +} + +// Allows a controller thread to pause execution of newly created +// threads until notified. Instances of this class must be created +// and destroyed in the controller thread. +// +// This class is only for testing Google Test's own constructs. Do not +// use it in user tests, either directly or indirectly. +class Notification { + public: + Notification() : notified_(false) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL)); + } + ~Notification() { + pthread_mutex_destroy(&mutex_); + } + + // Notifies all threads created with this notification to start. Must + // be called from the controller thread. + void Notify() { + pthread_mutex_lock(&mutex_); + notified_ = true; + pthread_mutex_unlock(&mutex_); + } + + // Blocks until the controller thread notifies. Must be called from a test + // thread. + void WaitForNotification() { + for (;;) { + pthread_mutex_lock(&mutex_); + const bool notified = notified_; + pthread_mutex_unlock(&mutex_); + if (notified) + break; + SleepMilliseconds(10); + } + } + + private: + pthread_mutex_t mutex_; + bool notified_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); +}; + +// As a C-function, ThreadFuncWithCLinkage cannot be templated itself. +// Consequently, it cannot select a correct instantiation of ThreadWithParam +// in order to call its Run(). Introducing ThreadWithParamBase as a +// non-templated base class for ThreadWithParam allows us to bypass this +// problem. +class ThreadWithParamBase { + public: + virtual ~ThreadWithParamBase() {} + virtual void Run() = 0; +}; + +// pthread_create() accepts a pointer to a function type with the C linkage. +// According to the Standard (7.5/1), function types with different linkages +// are different even if they are otherwise identical. Some compilers (for +// example, SunStudio) treat them as different types. Since class methods +// cannot be defined with C-linkage we need to define a free C-function to +// pass into pthread_create(). +extern "C" inline void* ThreadFuncWithCLinkage(void* thread) { + static_cast(thread)->Run(); + return NULL; +} + +// Helper class for testing Google Test's multi-threading constructs. +// To use it, write: +// +// void ThreadFunc(int param) { /* Do things with param */ } +// Notification thread_can_start; +// ... +// // The thread_can_start parameter is optional; you can supply NULL. +// ThreadWithParam thread(&ThreadFunc, 5, &thread_can_start); +// thread_can_start.Notify(); +// +// These classes are only for testing Google Test's own constructs. Do +// not use them in user tests, either directly or indirectly. +template +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void (*UserThreadFunc)(T); + + ThreadWithParam( + UserThreadFunc func, T param, Notification* thread_can_start) + : func_(func), + param_(param), + thread_can_start_(thread_can_start), + finished_(false) { + ThreadWithParamBase* const base = this; + // The thread can be created only after all fields except thread_ + // have been initialized. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_create(&thread_, 0, &ThreadFuncWithCLinkage, base)); + } + ~ThreadWithParam() { Join(); } + + void Join() { + if (!finished_) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, 0)); + finished_ = true; + } + } + + virtual void Run() { + if (thread_can_start_ != NULL) + thread_can_start_->WaitForNotification(); + func_(param_); + } + + private: + const UserThreadFunc func_; // User-supplied thread function. + const T param_; // User-supplied parameter to the thread function. + // When non-NULL, used to block execution until the controller thread + // notifies. + Notification* const thread_can_start_; + bool finished_; // true iff we know that the thread function has finished. + pthread_t thread_; // The native thread object. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam); +}; + +// MutexBase and Mutex implement mutex on pthreads-based platforms. They +// are used in conjunction with class MutexLock: +// +// Mutex mutex; +// ... +// MutexLock lock(&mutex); // Acquires the mutex and releases it at the end +// // of the current scope. +// +// MutexBase implements behavior for both statically and dynamically +// allocated mutexes. Do not use MutexBase directly. Instead, write +// the following to define a static mutex: +// +// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex); +// +// You can forward declare a static mutex like this: +// +// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex); +// +// To create a dynamic mutex, just define an object of type Mutex. +class MutexBase { + public: + // Acquires this mutex. + void Lock() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_)); + owner_ = pthread_self(); + has_owner_ = true; + } + + // Releases this mutex. + void Unlock() { + // Since the lock is being released the owner_ field should no longer be + // considered valid. We don't protect writing to has_owner_ here, as it's + // the caller's responsibility to ensure that the current thread holds the + // mutex when this is called. + has_owner_ = false; + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_)); + } + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld() const { + GTEST_CHECK_(has_owner_ && pthread_equal(owner_, pthread_self())) + << "The current thread is not holding the mutex @" << this; + } + + // A static mutex may be used before main() is entered. It may even + // be used before the dynamic initialization stage. Therefore we + // must be able to initialize a static mutex object at link time. + // This means MutexBase has to be a POD and its member variables + // have to be public. + public: + pthread_mutex_t mutex_; // The underlying pthread mutex. + // has_owner_ indicates whether the owner_ field below contains a valid thread + // ID and is therefore safe to inspect (e.g., to use in pthread_equal()). All + // accesses to the owner_ field should be protected by a check of this field. + // An alternative might be to memset() owner_ to all zeros, but there's no + // guarantee that a zero'd pthread_t is necessarily invalid or even different + // from pthread_self(). + bool has_owner_; + pthread_t owner_; // The thread holding the mutex. +}; + +// Forward-declares a static mutex. +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::MutexBase mutex + +// Defines and statically (i.e. at link time) initializes a static mutex. +// The initialization list here does not explicitly initialize each field, +// instead relying on default initialization for the unspecified fields. In +// particular, the owner_ field (a pthread_t) is not explicitly initialized. +// This allows initialization to work whether pthread_t is a scalar or struct. +// The flag -Wmissing-field-initializers must not be specified for this to work. +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, false } + +// The Mutex class can only be used for mutexes created at runtime. It +// shares its API with MutexBase otherwise. +class Mutex : public MutexBase { + public: + Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL)); + has_owner_ = false; + } + ~Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_)); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex); +}; + +// We cannot name this class MutexLock as the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(MutexBase* mutex) + : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + MutexBase* const mutex_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock); +}; + +typedef GTestMutexLock MutexLock; + +// Helpers for ThreadLocal. + +// pthread_key_create() requires DeleteThreadLocalValue() to have +// C-linkage. Therefore it cannot be templatized to access +// ThreadLocal. Hence the need for class +// ThreadLocalValueHolderBase. +class ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() {} +}; + +// Called by pthread to delete thread-local data stored by +// pthread_setspecific(). +extern "C" inline void DeleteThreadLocalValue(void* value_holder) { + delete static_cast(value_holder); +} + +// Implements thread-local storage on pthreads-based systems. +// +// // Thread 1 +// ThreadLocal tl(100); // 100 is the default value for each thread. +// +// // Thread 2 +// tl.set(150); // Changes the value for thread 2 only. +// EXPECT_EQ(150, tl.get()); +// +// // Thread 1 +// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value. +// tl.set(200); +// EXPECT_EQ(200, tl.get()); +// +// The template type argument T must have a public copy constructor. +// In addition, the default ThreadLocal constructor requires T to have +// a public default constructor. +// +// An object managed for a thread by a ThreadLocal instance is deleted +// when the thread exits. Or, if the ThreadLocal instance dies in +// that thread, when the ThreadLocal dies. It's the user's +// responsibility to ensure that all other threads using a ThreadLocal +// have exited when it dies, or the per-thread objects for those +// threads will not be deleted. +// +// Google Test only uses global ThreadLocal objects. That means they +// will die after main() has returned. Therefore, no per-thread +// object managed by Google Test will be leaked as long as all threads +// using Google Test have exited when main() returns. +template +class ThreadLocal { + public: + ThreadLocal() : key_(CreateKey()), + default_() {} + explicit ThreadLocal(const T& value) : key_(CreateKey()), + default_(value) {} + + ~ThreadLocal() { + // Destroys the managed object for the current thread, if any. + DeleteThreadLocalValue(pthread_getspecific(key_)); + + // Releases resources associated with the key. This will *not* + // delete managed objects for other threads. + GTEST_CHECK_POSIX_SUCCESS_(pthread_key_delete(key_)); + } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of type T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder); + }; + + static pthread_key_t CreateKey() { + pthread_key_t key; + // When a thread exits, DeleteThreadLocalValue() will be called on + // the object managed for that thread. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_key_create(&key, &DeleteThreadLocalValue)); + return key; + } + + T* GetOrCreateValue() const { + ThreadLocalValueHolderBase* const holder = + static_cast(pthread_getspecific(key_)); + if (holder != NULL) { + return CheckedDowncastToActualType(holder)->pointer(); + } + + ValueHolder* const new_holder = new ValueHolder(default_); + ThreadLocalValueHolderBase* const holder_base = new_holder; + GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base)); + return new_holder->pointer(); + } + + // A key pthreads uses for looking up per-thread values. + const pthread_key_t key_; + const T default_; // The default value for each thread. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); +}; + +# define GTEST_IS_THREADSAFE 1 + +#else // GTEST_HAS_PTHREAD + +// A dummy implementation of synchronization primitives (mutex, lock, +// and thread-local variable). Necessary for compiling Google Test where +// mutex is not supported - using Google Test in multiple threads is not +// supported on such platforms. + +class Mutex { + public: + Mutex() {} + void Lock() {} + void Unlock() {} + void AssertHeld() const {} +}; + +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex + +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex*) {} // NOLINT +}; + +typedef GTestMutexLock MutexLock; + +template +class ThreadLocal { + public: + ThreadLocal() : value_() {} + explicit ThreadLocal(const T& value) : value_(value) {} + T* pointer() { return &value_; } + const T* pointer() const { return &value_; } + const T& get() const { return value_; } + void set(const T& value) { value_ = value; } + private: + T value_; +}; + +// The above synchronization primitives have dummy implementations. +// Therefore Google Test is not thread-safe. +# define GTEST_IS_THREADSAFE 0 + +#endif // GTEST_HAS_PTHREAD + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +GTEST_API_ size_t GetThreadCount(); + +// Passing non-POD classes through ellipsis (...) crashes the ARM +// compiler and generates a warning in Sun Studio. The Nokia Symbian +// and the IBM XL C/C++ compiler try to instantiate a copy constructor +// for objects passed through ellipsis (...), failing for uncopyable +// objects. We define this to ensure that only POD is passed through +// ellipsis on these systems. +#if defined(__SYMBIAN32__) || defined(__IBMCPP__) || defined(__SUNPRO_CC) +// We lose support for NULL detection where the compiler doesn't like +// passing non-POD classes through ellipsis (...). +# define GTEST_ELLIPSIS_NEEDS_POD_ 1 +#else +# define GTEST_CAN_COMPARE_NULL 1 +#endif + +// The Nokia Symbian and IBM XL C/C++ compilers cannot decide between +// const T& and const T* in a function template. These compilers +// _can_ decide between class template specializations for T and T*, +// so a tr1::type_traits-like is_pointer works. +#if defined(__SYMBIAN32__) || defined(__IBMCPP__) +# define GTEST_NEEDS_IS_POINTER_ 1 +#endif + +template +struct bool_constant { + typedef bool_constant type; + static const bool value = bool_value; +}; +template const bool bool_constant::value; + +typedef bool_constant false_type; +typedef bool_constant true_type; + +template +struct is_pointer : public false_type {}; + +template +struct is_pointer : public true_type {}; + +template +struct IteratorTraits { + typedef typename Iterator::value_type value_type; +}; + +template +struct IteratorTraits { + typedef T value_type; +}; + +template +struct IteratorTraits { + typedef T value_type; +}; + +#if GTEST_OS_WINDOWS +# define GTEST_PATH_SEP_ "\\" +# define GTEST_HAS_ALT_PATH_SEP_ 1 +// The biggest signed integer type the compiler supports. +typedef __int64 BiggestInt; +#else +# define GTEST_PATH_SEP_ "/" +# define GTEST_HAS_ALT_PATH_SEP_ 0 +typedef long long BiggestInt; // NOLINT +#endif // GTEST_OS_WINDOWS + +// Utilities for char. + +// isspace(int ch) and friends accept an unsigned char or EOF. char +// may be signed, depending on the compiler (or compiler flags). +// Therefore we need to cast a char to unsigned char before calling +// isspace(), etc. + +inline bool IsAlpha(char ch) { + return isalpha(static_cast(ch)) != 0; +} +inline bool IsAlNum(char ch) { + return isalnum(static_cast(ch)) != 0; +} +inline bool IsDigit(char ch) { + return isdigit(static_cast(ch)) != 0; +} +inline bool IsLower(char ch) { + return islower(static_cast(ch)) != 0; +} +inline bool IsSpace(char ch) { + return isspace(static_cast(ch)) != 0; +} +inline bool IsUpper(char ch) { + return isupper(static_cast(ch)) != 0; +} +inline bool IsXDigit(char ch) { + return isxdigit(static_cast(ch)) != 0; +} +inline bool IsXDigit(wchar_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} + +inline char ToLower(char ch) { + return static_cast(tolower(static_cast(ch))); +} +inline char ToUpper(char ch) { + return static_cast(toupper(static_cast(ch))); +} + +// The testing::internal::posix namespace holds wrappers for common +// POSIX functions. These wrappers hide the differences between +// Windows/MSVC and POSIX systems. Since some compilers define these +// standard functions as macros, the wrapper cannot have the same name +// as the wrapped function. + +namespace posix { + +// Functions with a different name on Windows. + +#if GTEST_OS_WINDOWS + +typedef struct _stat StatStruct; + +# ifdef __BORLANDC__ +inline int IsATTY(int fd) { return isatty(fd); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return stricmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +# else // !__BORLANDC__ +# if GTEST_OS_WINDOWS_MOBILE +inline int IsATTY(int /* fd */) { return 0; } +# else +inline int IsATTY(int fd) { return _isatty(fd); } +# endif // GTEST_OS_WINDOWS_MOBILE +inline int StrCaseCmp(const char* s1, const char* s2) { + return _stricmp(s1, s2); +} +inline char* StrDup(const char* src) { return _strdup(src); } +# endif // __BORLANDC__ + +# if GTEST_OS_WINDOWS_MOBILE +inline int FileNo(FILE* file) { return reinterpret_cast(_fileno(file)); } +// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this +// time and thus not defined there. +# else +inline int FileNo(FILE* file) { return _fileno(file); } +inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); } +inline int RmDir(const char* dir) { return _rmdir(dir); } +inline bool IsDir(const StatStruct& st) { + return (_S_IFDIR & st.st_mode) != 0; +} +# endif // GTEST_OS_WINDOWS_MOBILE + +#else + +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +inline int IsATTY(int fd) { return isatty(fd); } +inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return strcasecmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +inline int RmDir(const char* dir) { return rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } + +#endif // GTEST_OS_WINDOWS + +// Functions deprecated by MSVC 8.0. + +#ifdef _MSC_VER +// Temporarily disable warning 4996 (deprecated function). +# pragma warning(push) +# pragma warning(disable:4996) +#endif + +inline const char* StrNCpy(char* dest, const char* src, size_t n) { + return strncpy(dest, src, n); +} + +// ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and +// StrError() aren't needed on Windows CE at this time and thus not +// defined there. + +#if !GTEST_OS_WINDOWS_MOBILE +inline int ChDir(const char* dir) { return chdir(dir); } +#endif +inline FILE* FOpen(const char* path, const char* mode) { + return fopen(path, mode); +} +#if !GTEST_OS_WINDOWS_MOBILE +inline FILE *FReopen(const char* path, const char* mode, FILE* stream) { + return freopen(path, mode, stream); +} +inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); } +#endif +inline int FClose(FILE* fp) { return fclose(fp); } +#if !GTEST_OS_WINDOWS_MOBILE +inline int Read(int fd, void* buf, unsigned int count) { + return static_cast(read(fd, buf, count)); +} +inline int Write(int fd, const void* buf, unsigned int count) { + return static_cast(write(fd, buf, count)); +} +inline int Close(int fd) { return close(fd); } +inline const char* StrError(int errnum) { return strerror(errnum); } +#endif +inline const char* GetEnv(const char* name) { +#if GTEST_OS_WINDOWS_MOBILE + // We are on Windows CE, which has no environment variables. + return NULL; +#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9) + // Environment variables which we programmatically clear will be set to the + // empty string rather than unset (NULL). Handle that case. + const char* const env = getenv(name); + return (env != NULL && env[0] != '\0') ? env : NULL; +#else + return getenv(name); +#endif +} + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif + +#if GTEST_OS_WINDOWS_MOBILE +// Windows CE has no C library. The abort() function is used in +// several places in Google Test. This implementation provides a reasonable +// imitation of standard behaviour. +void Abort(); +#else +inline void Abort() { abort(); } +#endif // GTEST_OS_WINDOWS_MOBILE + +} // namespace posix + +// MSVC "deprecates" snprintf and issues warnings wherever it is used. In +// order to avoid these warnings, we need to use _snprintf or _snprintf_s on +// MSVC-based platforms. We map the GTEST_SNPRINTF_ macro to the appropriate +// function in order to achieve that. We use macro definition here because +// snprintf is a variadic function. +#if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE +// MSVC 2005 and above support variadic macros. +# define GTEST_SNPRINTF_(buffer, size, format, ...) \ + _snprintf_s(buffer, size, size, format, __VA_ARGS__) +#elif defined(_MSC_VER) +// Windows CE does not define _snprintf_s and MSVC prior to 2005 doesn't +// complain about _snprintf. +# define GTEST_SNPRINTF_ _snprintf +#else +# define GTEST_SNPRINTF_ snprintf +#endif + +// The maximum number a BiggestInt can represent. This definition +// works no matter BiggestInt is represented in one's complement or +// two's complement. +// +// We cannot rely on numeric_limits in STL, as __int64 and long long +// are not part of standard C++ and numeric_limits doesn't need to be +// defined for them. +const BiggestInt kMaxBiggestInt = + ~(static_cast(1) << (8*sizeof(BiggestInt) - 1)); + +// This template class serves as a compile-time function from size to +// type. It maps a size in bytes to a primitive type with that +// size. e.g. +// +// TypeWithSize<4>::UInt +// +// is typedef-ed to be unsigned int (unsigned integer made up of 4 +// bytes). +// +// Such functionality should belong to STL, but I cannot find it +// there. +// +// Google Test uses this class in the implementation of floating-point +// comparison. +// +// For now it only handles UInt (unsigned int) as that's all Google Test +// needs. Other types can be easily added in the future if need +// arises. +template +class TypeWithSize { + public: + // This prevents the user from using TypeWithSize with incorrect + // values of N. + typedef void UInt; +}; + +// The specialization for size 4. +template <> +class TypeWithSize<4> { + public: + // unsigned int has size 4 in both gcc and MSVC. + // + // As base/basictypes.h doesn't compile on Windows, we cannot use + // uint32, uint64, and etc here. + typedef int Int; + typedef unsigned int UInt; +}; + +// The specialization for size 8. +template <> +class TypeWithSize<8> { + public: +#if GTEST_OS_WINDOWS + typedef __int64 Int; + typedef unsigned __int64 UInt; +#else + typedef long long Int; // NOLINT + typedef unsigned long long UInt; // NOLINT +#endif // GTEST_OS_WINDOWS +}; + +// Integer types of known sizes. +typedef TypeWithSize<4>::Int Int32; +typedef TypeWithSize<4>::UInt UInt32; +typedef TypeWithSize<8>::Int Int64; +typedef TypeWithSize<8>::UInt UInt64; +typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds. + +// Utilities for command line flags and environment variables. + +// Macro for referencing flags. +#define GTEST_FLAG(name) FLAGS_gtest_##name + +// Macros for declaring flags. +#define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name) +#define GTEST_DECLARE_int32_(name) \ + GTEST_API_ extern ::testing::internal::Int32 GTEST_FLAG(name) +#define GTEST_DECLARE_string_(name) \ + GTEST_API_ extern ::std::string GTEST_FLAG(name) + +// Macros for defining flags. +#define GTEST_DEFINE_bool_(name, default_val, doc) \ + GTEST_API_ bool GTEST_FLAG(name) = (default_val) +#define GTEST_DEFINE_int32_(name, default_val, doc) \ + GTEST_API_ ::testing::internal::Int32 GTEST_FLAG(name) = (default_val) +#define GTEST_DEFINE_string_(name, default_val, doc) \ + GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val) + +// Thread annotations +#define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks) +#define GTEST_LOCK_EXCLUDED_(locks) + +// Parses 'str' for a 32-bit signed integer. If successful, writes the result +// to *value and returns true; otherwise leaves *value unchanged and returns +// false. +// TODO(chandlerc): Find a better way to refactor flag and environment parsing +// out of both gtest-port.cc and gtest.cc to avoid exporting this utility +// function. +bool ParseInt32(const Message& src_text, const char* str, Int32* value); + +// Parses a bool/Int32/string from the environment variable +// corresponding to the given Google Test flag. +bool BoolFromGTestEnv(const char* flag, bool default_val); +GTEST_API_ Int32 Int32FromGTestEnv(const char* flag, Int32 default_val); +const char* StringFromGTestEnv(const char* flag, const char* default_val); + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-string.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-string.h new file mode 100644 index 00000000..97f1a7fd --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-string.h @@ -0,0 +1,167 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file declares the String class and functions used internally by +// Google Test. They are subject to change without notice. They should not used +// by code external to Google Test. +// +// This header file is #included by . +// It should not be #included by other files. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ + +#ifdef __BORLANDC__ +// string.h is not guaranteed to provide strcpy on C++ Builder. +# include +#endif + +#include +#include + +#include "gtest/internal/gtest-port.h" + +namespace testing { +namespace internal { + +// String - an abstract class holding static string utilities. +class GTEST_API_ String { + public: + // Static utility methods + + // Clones a 0-terminated C string, allocating memory using new. The + // caller is responsible for deleting the return value using + // delete[]. Returns the cloned string, or NULL if the input is + // NULL. + // + // This is different from strdup() in string.h, which allocates + // memory using malloc(). + static const char* CloneCString(const char* c_str); + +#if GTEST_OS_WINDOWS_MOBILE + // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be + // able to pass strings to Win32 APIs on CE we need to convert them + // to 'Unicode', UTF-16. + + // Creates a UTF-16 wide string from the given ANSI string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the wide string, or NULL if the + // input is NULL. + // + // The wide string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static LPCWSTR AnsiToUtf16(const char* c_str); + + // Creates an ANSI string from the given wide string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the ANSI string, or NULL if the + // input is NULL. + // + // The returned string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static const char* Utf16ToAnsi(LPCWSTR utf16_str); +#endif + + // Compares two C strings. Returns true iff they have the same content. + // + // Unlike strcmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CStringEquals(const char* lhs, const char* rhs); + + // Converts a wide C string to a String using the UTF-8 encoding. + // NULL will be converted to "(null)". If an error occurred during + // the conversion, "(failed to convert from wide string)" is + // returned. + static std::string ShowWideCString(const wchar_t* wide_c_str); + + // Compares two wide C strings. Returns true iff they have the same + // content. + // + // Unlike wcscmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); + + // Compares two C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike strcasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CaseInsensitiveCStringEquals(const char* lhs, + const char* rhs); + + // Compares two wide C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. + static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs); + + // Returns true iff the given string ends with the given suffix, ignoring + // case. Any string is considered to end with an empty suffix. + static bool EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix); + + // Formats an int value as "%02d". + static std::string FormatIntWidth2(int value); // "%02d" for width == 2 + + // Formats an int value as "%X". + static std::string FormatHexInt(int value); + + // Formats a byte as "%02X". + static std::string FormatByte(unsigned char value); + + private: + String(); // Not meant to be instantiated. +}; // class String + +// Gets the content of the stringstream's buffer as an std::string. Each '\0' +// character in the buffer is replaced with "\\0". +GTEST_API_ std::string StringStreamToString(::std::stringstream* stream); + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-tuple.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-tuple.h new file mode 100644 index 00000000..7b3dfc31 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-tuple.h @@ -0,0 +1,1012 @@ +// This file was GENERATED by command: +// pump.py gtest-tuple.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2009 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Implements a subset of TR1 tuple needed by Google Test and Google Mock. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ + +#include // For ::std::pair. + +// The compiler used in Symbian has a bug that prevents us from declaring the +// tuple template as a friend (it complains that tuple is redefined). This +// hack bypasses the bug by declaring the members that should otherwise be +// private as public. +// Sun Studio versions < 12 also have the above bug. +#if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590) +# define GTEST_DECLARE_TUPLE_AS_FRIEND_ public: +#else +# define GTEST_DECLARE_TUPLE_AS_FRIEND_ \ + template friend class tuple; \ + private: +#endif + +// GTEST_n_TUPLE_(T) is the type of an n-tuple. +#define GTEST_0_TUPLE_(T) tuple<> +#define GTEST_1_TUPLE_(T) tuple +#define GTEST_2_TUPLE_(T) tuple +#define GTEST_3_TUPLE_(T) tuple +#define GTEST_4_TUPLE_(T) tuple +#define GTEST_5_TUPLE_(T) tuple +#define GTEST_6_TUPLE_(T) tuple +#define GTEST_7_TUPLE_(T) tuple +#define GTEST_8_TUPLE_(T) tuple +#define GTEST_9_TUPLE_(T) tuple +#define GTEST_10_TUPLE_(T) tuple + +// GTEST_n_TYPENAMES_(T) declares a list of n typenames. +#define GTEST_0_TYPENAMES_(T) +#define GTEST_1_TYPENAMES_(T) typename T##0 +#define GTEST_2_TYPENAMES_(T) typename T##0, typename T##1 +#define GTEST_3_TYPENAMES_(T) typename T##0, typename T##1, typename T##2 +#define GTEST_4_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3 +#define GTEST_5_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4 +#define GTEST_6_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5 +#define GTEST_7_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6 +#define GTEST_8_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6, typename T##7 +#define GTEST_9_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6, \ + typename T##7, typename T##8 +#define GTEST_10_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6, \ + typename T##7, typename T##8, typename T##9 + +// In theory, defining stuff in the ::std namespace is undefined +// behavior. We can do this as we are playing the role of a standard +// library vendor. +namespace std { +namespace tr1 { + +template +class tuple; + +// Anything in namespace gtest_internal is Google Test's INTERNAL +// IMPLEMENTATION DETAIL and MUST NOT BE USED DIRECTLY in user code. +namespace gtest_internal { + +// ByRef::type is T if T is a reference; otherwise it's const T&. +template +struct ByRef { typedef const T& type; }; // NOLINT +template +struct ByRef { typedef T& type; }; // NOLINT + +// A handy wrapper for ByRef. +#define GTEST_BY_REF_(T) typename ::std::tr1::gtest_internal::ByRef::type + +// AddRef::type is T if T is a reference; otherwise it's T&. This +// is the same as tr1::add_reference::type. +template +struct AddRef { typedef T& type; }; // NOLINT +template +struct AddRef { typedef T& type; }; // NOLINT + +// A handy wrapper for AddRef. +#define GTEST_ADD_REF_(T) typename ::std::tr1::gtest_internal::AddRef::type + +// A helper for implementing get(). +template class Get; + +// A helper for implementing tuple_element. kIndexValid is true +// iff k < the number of fields in tuple type T. +template +struct TupleElement; + +template +struct TupleElement { + typedef T0 type; +}; + +template +struct TupleElement { + typedef T1 type; +}; + +template +struct TupleElement { + typedef T2 type; +}; + +template +struct TupleElement { + typedef T3 type; +}; + +template +struct TupleElement { + typedef T4 type; +}; + +template +struct TupleElement { + typedef T5 type; +}; + +template +struct TupleElement { + typedef T6 type; +}; + +template +struct TupleElement { + typedef T7 type; +}; + +template +struct TupleElement { + typedef T8 type; +}; + +template +struct TupleElement { + typedef T9 type; +}; + +} // namespace gtest_internal + +template <> +class tuple<> { + public: + tuple() {} + tuple(const tuple& /* t */) {} + tuple& operator=(const tuple& /* t */) { return *this; } +}; + +template +class GTEST_1_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0) : f0_(f0) {} + + tuple(const tuple& t) : f0_(t.f0_) {} + + template + tuple(const GTEST_1_TUPLE_(U)& t) : f0_(t.f0_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_1_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_1_TUPLE_(U)& t) { + f0_ = t.f0_; + return *this; + } + + T0 f0_; +}; + +template +class GTEST_2_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1) : f0_(f0), + f1_(f1) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_) {} + + template + tuple(const GTEST_2_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_) {} + template + tuple(const ::std::pair& p) : f0_(p.first), f1_(p.second) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_2_TUPLE_(U)& t) { + return CopyFrom(t); + } + template + tuple& operator=(const ::std::pair& p) { + f0_ = p.first; + f1_ = p.second; + return *this; + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_2_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + return *this; + } + + T0 f0_; + T1 f1_; +}; + +template +class GTEST_3_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2) : f0_(f0), f1_(f1), f2_(f2) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {} + + template + tuple(const GTEST_3_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_3_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_3_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; +}; + +template +class GTEST_4_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3) : f0_(f0), f1_(f1), f2_(f2), + f3_(f3) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_) {} + + template + tuple(const GTEST_4_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_4_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_4_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; +}; + +template +class GTEST_5_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, + GTEST_BY_REF_(T4) f4) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_) {} + + template + tuple(const GTEST_5_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_5_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_5_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; +}; + +template +class GTEST_6_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), + f5_(f5) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_) {} + + template + tuple(const GTEST_6_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_6_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_6_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; +}; + +template +class GTEST_7_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6) : f0_(f0), f1_(f1), f2_(f2), + f3_(f3), f4_(f4), f5_(f5), f6_(f6) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {} + + template + tuple(const GTEST_7_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_7_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_7_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; +}; + +template +class GTEST_8_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, + GTEST_BY_REF_(T7) f7) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), + f5_(f5), f6_(f6), f7_(f7) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {} + + template + tuple(const GTEST_8_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_8_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_8_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + f7_ = t.f7_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; + T7 f7_; +}; + +template +class GTEST_9_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7, + GTEST_BY_REF_(T8) f8) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), + f5_(f5), f6_(f6), f7_(f7), f8_(f8) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {} + + template + tuple(const GTEST_9_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_9_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_9_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + f7_ = t.f7_; + f8_ = t.f8_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; + T7 f7_; + T8 f8_; +}; + +template +class tuple { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_(), + f9_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7, + GTEST_BY_REF_(T8) f8, GTEST_BY_REF_(T9) f9) : f0_(f0), f1_(f1), f2_(f2), + f3_(f3), f4_(f4), f5_(f5), f6_(f6), f7_(f7), f8_(f8), f9_(f9) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), f9_(t.f9_) {} + + template + tuple(const GTEST_10_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), + f9_(t.f9_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_10_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_10_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + f7_ = t.f7_; + f8_ = t.f8_; + f9_ = t.f9_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; + T7 f7_; + T8 f8_; + T9 f9_; +}; + +// 6.1.3.2 Tuple creation functions. + +// Known limitations: we don't support passing an +// std::tr1::reference_wrapper to make_tuple(). And we don't +// implement tie(). + +inline tuple<> make_tuple() { return tuple<>(); } + +template +inline GTEST_1_TUPLE_(T) make_tuple(const T0& f0) { + return GTEST_1_TUPLE_(T)(f0); +} + +template +inline GTEST_2_TUPLE_(T) make_tuple(const T0& f0, const T1& f1) { + return GTEST_2_TUPLE_(T)(f0, f1); +} + +template +inline GTEST_3_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2) { + return GTEST_3_TUPLE_(T)(f0, f1, f2); +} + +template +inline GTEST_4_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3) { + return GTEST_4_TUPLE_(T)(f0, f1, f2, f3); +} + +template +inline GTEST_5_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4) { + return GTEST_5_TUPLE_(T)(f0, f1, f2, f3, f4); +} + +template +inline GTEST_6_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5) { + return GTEST_6_TUPLE_(T)(f0, f1, f2, f3, f4, f5); +} + +template +inline GTEST_7_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6) { + return GTEST_7_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6); +} + +template +inline GTEST_8_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7) { + return GTEST_8_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7); +} + +template +inline GTEST_9_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7, + const T8& f8) { + return GTEST_9_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8); +} + +template +inline GTEST_10_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7, + const T8& f8, const T9& f9) { + return GTEST_10_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9); +} + +// 6.1.3.3 Tuple helper classes. + +template struct tuple_size; + +template +struct tuple_size { + static const int value = 0; +}; + +template +struct tuple_size { + static const int value = 1; +}; + +template +struct tuple_size { + static const int value = 2; +}; + +template +struct tuple_size { + static const int value = 3; +}; + +template +struct tuple_size { + static const int value = 4; +}; + +template +struct tuple_size { + static const int value = 5; +}; + +template +struct tuple_size { + static const int value = 6; +}; + +template +struct tuple_size { + static const int value = 7; +}; + +template +struct tuple_size { + static const int value = 8; +}; + +template +struct tuple_size { + static const int value = 9; +}; + +template +struct tuple_size { + static const int value = 10; +}; + +template +struct tuple_element { + typedef typename gtest_internal::TupleElement< + k < (tuple_size::value), k, Tuple>::type type; +}; + +#define GTEST_TUPLE_ELEMENT_(k, Tuple) typename tuple_element::type + +// 6.1.3.4 Element access. + +namespace gtest_internal { + +template <> +class Get<0> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple)) + Field(Tuple& t) { return t.f0_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple)) + ConstField(const Tuple& t) { return t.f0_; } +}; + +template <> +class Get<1> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple)) + Field(Tuple& t) { return t.f1_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple)) + ConstField(const Tuple& t) { return t.f1_; } +}; + +template <> +class Get<2> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple)) + Field(Tuple& t) { return t.f2_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple)) + ConstField(const Tuple& t) { return t.f2_; } +}; + +template <> +class Get<3> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple)) + Field(Tuple& t) { return t.f3_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple)) + ConstField(const Tuple& t) { return t.f3_; } +}; + +template <> +class Get<4> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple)) + Field(Tuple& t) { return t.f4_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple)) + ConstField(const Tuple& t) { return t.f4_; } +}; + +template <> +class Get<5> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple)) + Field(Tuple& t) { return t.f5_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple)) + ConstField(const Tuple& t) { return t.f5_; } +}; + +template <> +class Get<6> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple)) + Field(Tuple& t) { return t.f6_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple)) + ConstField(const Tuple& t) { return t.f6_; } +}; + +template <> +class Get<7> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple)) + Field(Tuple& t) { return t.f7_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple)) + ConstField(const Tuple& t) { return t.f7_; } +}; + +template <> +class Get<8> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple)) + Field(Tuple& t) { return t.f8_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple)) + ConstField(const Tuple& t) { return t.f8_; } +}; + +template <> +class Get<9> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple)) + Field(Tuple& t) { return t.f9_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple)) + ConstField(const Tuple& t) { return t.f9_; } +}; + +} // namespace gtest_internal + +template +GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T))) +get(GTEST_10_TUPLE_(T)& t) { + return gtest_internal::Get::Field(t); +} + +template +GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T))) +get(const GTEST_10_TUPLE_(T)& t) { + return gtest_internal::Get::ConstField(t); +} + +// 6.1.3.5 Relational operators + +// We only implement == and !=, as we don't have a need for the rest yet. + +namespace gtest_internal { + +// SameSizeTuplePrefixComparator::Eq(t1, t2) returns true if the +// first k fields of t1 equals the first k fields of t2. +// SameSizeTuplePrefixComparator(k1, k2) would be a compiler error if +// k1 != k2. +template +struct SameSizeTuplePrefixComparator; + +template <> +struct SameSizeTuplePrefixComparator<0, 0> { + template + static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) { + return true; + } +}; + +template +struct SameSizeTuplePrefixComparator { + template + static bool Eq(const Tuple1& t1, const Tuple2& t2) { + return SameSizeTuplePrefixComparator::Eq(t1, t2) && + ::std::tr1::get(t1) == ::std::tr1::get(t2); + } +}; + +} // namespace gtest_internal + +template +inline bool operator==(const GTEST_10_TUPLE_(T)& t, + const GTEST_10_TUPLE_(U)& u) { + return gtest_internal::SameSizeTuplePrefixComparator< + tuple_size::value, + tuple_size::value>::Eq(t, u); +} + +template +inline bool operator!=(const GTEST_10_TUPLE_(T)& t, + const GTEST_10_TUPLE_(U)& u) { return !(t == u); } + +// 6.1.4 Pairs. +// Unimplemented. + +} // namespace tr1 +} // namespace std + +#undef GTEST_0_TUPLE_ +#undef GTEST_1_TUPLE_ +#undef GTEST_2_TUPLE_ +#undef GTEST_3_TUPLE_ +#undef GTEST_4_TUPLE_ +#undef GTEST_5_TUPLE_ +#undef GTEST_6_TUPLE_ +#undef GTEST_7_TUPLE_ +#undef GTEST_8_TUPLE_ +#undef GTEST_9_TUPLE_ +#undef GTEST_10_TUPLE_ + +#undef GTEST_0_TYPENAMES_ +#undef GTEST_1_TYPENAMES_ +#undef GTEST_2_TYPENAMES_ +#undef GTEST_3_TYPENAMES_ +#undef GTEST_4_TYPENAMES_ +#undef GTEST_5_TYPENAMES_ +#undef GTEST_6_TYPENAMES_ +#undef GTEST_7_TYPENAMES_ +#undef GTEST_8_TYPENAMES_ +#undef GTEST_9_TYPENAMES_ +#undef GTEST_10_TYPENAMES_ + +#undef GTEST_DECLARE_TUPLE_AS_FRIEND_ +#undef GTEST_BY_REF_ +#undef GTEST_ADD_REF_ +#undef GTEST_TUPLE_ELEMENT_ + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-tuple.h.pump b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-tuple.h.pump new file mode 100644 index 00000000..c7d9e039 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-tuple.h.pump @@ -0,0 +1,339 @@ +$$ -*- mode: c++; -*- +$var n = 10 $$ Maximum number of tuple fields we want to support. +$$ This meta comment fixes auto-indentation in Emacs. }} +// Copyright 2009 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Implements a subset of TR1 tuple needed by Google Test and Google Mock. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ + +#include // For ::std::pair. + +// The compiler used in Symbian has a bug that prevents us from declaring the +// tuple template as a friend (it complains that tuple is redefined). This +// hack bypasses the bug by declaring the members that should otherwise be +// private as public. +// Sun Studio versions < 12 also have the above bug. +#if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590) +# define GTEST_DECLARE_TUPLE_AS_FRIEND_ public: +#else +# define GTEST_DECLARE_TUPLE_AS_FRIEND_ \ + template friend class tuple; \ + private: +#endif + + +$range i 0..n-1 +$range j 0..n +$range k 1..n +// GTEST_n_TUPLE_(T) is the type of an n-tuple. +#define GTEST_0_TUPLE_(T) tuple<> + +$for k [[ +$range m 0..k-1 +$range m2 k..n-1 +#define GTEST_$(k)_TUPLE_(T) tuple<$for m, [[T##$m]]$for m2 [[, void]]> + +]] + +// GTEST_n_TYPENAMES_(T) declares a list of n typenames. + +$for j [[ +$range m 0..j-1 +#define GTEST_$(j)_TYPENAMES_(T) $for m, [[typename T##$m]] + + +]] + +// In theory, defining stuff in the ::std namespace is undefined +// behavior. We can do this as we are playing the role of a standard +// library vendor. +namespace std { +namespace tr1 { + +template <$for i, [[typename T$i = void]]> +class tuple; + +// Anything in namespace gtest_internal is Google Test's INTERNAL +// IMPLEMENTATION DETAIL and MUST NOT BE USED DIRECTLY in user code. +namespace gtest_internal { + +// ByRef::type is T if T is a reference; otherwise it's const T&. +template +struct ByRef { typedef const T& type; }; // NOLINT +template +struct ByRef { typedef T& type; }; // NOLINT + +// A handy wrapper for ByRef. +#define GTEST_BY_REF_(T) typename ::std::tr1::gtest_internal::ByRef::type + +// AddRef::type is T if T is a reference; otherwise it's T&. This +// is the same as tr1::add_reference::type. +template +struct AddRef { typedef T& type; }; // NOLINT +template +struct AddRef { typedef T& type; }; // NOLINT + +// A handy wrapper for AddRef. +#define GTEST_ADD_REF_(T) typename ::std::tr1::gtest_internal::AddRef::type + +// A helper for implementing get(). +template class Get; + +// A helper for implementing tuple_element. kIndexValid is true +// iff k < the number of fields in tuple type T. +template +struct TupleElement; + + +$for i [[ +template +struct TupleElement { + typedef T$i type; +}; + + +]] +} // namespace gtest_internal + +template <> +class tuple<> { + public: + tuple() {} + tuple(const tuple& /* t */) {} + tuple& operator=(const tuple& /* t */) { return *this; } +}; + + +$for k [[ +$range m 0..k-1 +template +class $if k < n [[GTEST_$(k)_TUPLE_(T)]] $else [[tuple]] { + public: + template friend class gtest_internal::Get; + + tuple() : $for m, [[f$(m)_()]] {} + + explicit tuple($for m, [[GTEST_BY_REF_(T$m) f$m]]) : [[]] +$for m, [[f$(m)_(f$m)]] {} + + tuple(const tuple& t) : $for m, [[f$(m)_(t.f$(m)_)]] {} + + template + tuple(const GTEST_$(k)_TUPLE_(U)& t) : $for m, [[f$(m)_(t.f$(m)_)]] {} + +$if k == 2 [[ + template + tuple(const ::std::pair& p) : f0_(p.first), f1_(p.second) {} + +]] + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_$(k)_TUPLE_(U)& t) { + return CopyFrom(t); + } + +$if k == 2 [[ + template + tuple& operator=(const ::std::pair& p) { + f0_ = p.first; + f1_ = p.second; + return *this; + } + +]] + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_$(k)_TUPLE_(U)& t) { + +$for m [[ + f$(m)_ = t.f$(m)_; + +]] + return *this; + } + + +$for m [[ + T$m f$(m)_; + +]] +}; + + +]] +// 6.1.3.2 Tuple creation functions. + +// Known limitations: we don't support passing an +// std::tr1::reference_wrapper to make_tuple(). And we don't +// implement tie(). + +inline tuple<> make_tuple() { return tuple<>(); } + +$for k [[ +$range m 0..k-1 + +template +inline GTEST_$(k)_TUPLE_(T) make_tuple($for m, [[const T$m& f$m]]) { + return GTEST_$(k)_TUPLE_(T)($for m, [[f$m]]); +} + +]] + +// 6.1.3.3 Tuple helper classes. + +template struct tuple_size; + + +$for j [[ +template +struct tuple_size { + static const int value = $j; +}; + + +]] +template +struct tuple_element { + typedef typename gtest_internal::TupleElement< + k < (tuple_size::value), k, Tuple>::type type; +}; + +#define GTEST_TUPLE_ELEMENT_(k, Tuple) typename tuple_element::type + +// 6.1.3.4 Element access. + +namespace gtest_internal { + + +$for i [[ +template <> +class Get<$i> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_($i, Tuple)) + Field(Tuple& t) { return t.f$(i)_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_($i, Tuple)) + ConstField(const Tuple& t) { return t.f$(i)_; } +}; + + +]] +} // namespace gtest_internal + +template +GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_$(n)_TUPLE_(T))) +get(GTEST_$(n)_TUPLE_(T)& t) { + return gtest_internal::Get::Field(t); +} + +template +GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_$(n)_TUPLE_(T))) +get(const GTEST_$(n)_TUPLE_(T)& t) { + return gtest_internal::Get::ConstField(t); +} + +// 6.1.3.5 Relational operators + +// We only implement == and !=, as we don't have a need for the rest yet. + +namespace gtest_internal { + +// SameSizeTuplePrefixComparator::Eq(t1, t2) returns true if the +// first k fields of t1 equals the first k fields of t2. +// SameSizeTuplePrefixComparator(k1, k2) would be a compiler error if +// k1 != k2. +template +struct SameSizeTuplePrefixComparator; + +template <> +struct SameSizeTuplePrefixComparator<0, 0> { + template + static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) { + return true; + } +}; + +template +struct SameSizeTuplePrefixComparator { + template + static bool Eq(const Tuple1& t1, const Tuple2& t2) { + return SameSizeTuplePrefixComparator::Eq(t1, t2) && + ::std::tr1::get(t1) == ::std::tr1::get(t2); + } +}; + +} // namespace gtest_internal + +template +inline bool operator==(const GTEST_$(n)_TUPLE_(T)& t, + const GTEST_$(n)_TUPLE_(U)& u) { + return gtest_internal::SameSizeTuplePrefixComparator< + tuple_size::value, + tuple_size::value>::Eq(t, u); +} + +template +inline bool operator!=(const GTEST_$(n)_TUPLE_(T)& t, + const GTEST_$(n)_TUPLE_(U)& u) { return !(t == u); } + +// 6.1.4 Pairs. +// Unimplemented. + +} // namespace tr1 +} // namespace std + + +$for j [[ +#undef GTEST_$(j)_TUPLE_ + +]] + + +$for j [[ +#undef GTEST_$(j)_TYPENAMES_ + +]] + +#undef GTEST_DECLARE_TUPLE_AS_FRIEND_ +#undef GTEST_BY_REF_ +#undef GTEST_ADD_REF_ +#undef GTEST_TUPLE_ELEMENT_ + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-type-util.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-type-util.h new file mode 100644 index 00000000..e46f7cfc --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-type-util.h @@ -0,0 +1,3331 @@ +// This file was GENERATED by command: +// pump.py gtest-type-util.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Type utilities needed for implementing typed and type-parameterized +// tests. This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +// Currently we support at most 50 types in a list, and at most 50 +// type-parameterized tests in one type-parameterized test case. +// Please contact googletestframework@googlegroups.com if you need +// more. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ + +#include "gtest/internal/gtest-port.h" + +// #ifdef __GNUC__ is too general here. It is possible to use gcc without using +// libstdc++ (which is where cxxabi.h comes from). +# if GTEST_HAS_CXXABI_H_ +# include +# elif defined(__HP_aCC) +# include +# endif // GTEST_HASH_CXXABI_H_ + +namespace testing { +namespace internal { + +// GetTypeName() returns a human-readable name of type T. +// NB: This function is also used in Google Mock, so don't move it inside of +// the typed-test-only section below. +template +std::string GetTypeName() { +# if GTEST_HAS_RTTI + + const char* const name = typeid(T).name(); +# if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC) + int status = 0; + // gcc's implementation of typeid(T).name() mangles the type name, + // so we have to demangle it. +# if GTEST_HAS_CXXABI_H_ + using abi::__cxa_demangle; +# endif // GTEST_HAS_CXXABI_H_ + char* const readable_name = __cxa_demangle(name, 0, 0, &status); + const std::string name_str(status == 0 ? readable_name : name); + free(readable_name); + return name_str; +# else + return name; +# endif // GTEST_HAS_CXXABI_H_ || __HP_aCC + +# else + + return ""; + +# endif // GTEST_HAS_RTTI +} + +#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// AssertyTypeEq::type is defined iff T1 and T2 are the same +// type. This can be used as a compile-time assertion to ensure that +// two types are equal. + +template +struct AssertTypeEq; + +template +struct AssertTypeEq { + typedef bool type; +}; + +// A unique type used as the default value for the arguments of class +// template Types. This allows us to simulate variadic templates +// (e.g. Types, Type, and etc), which C++ doesn't +// support directly. +struct None {}; + +// The following family of struct and struct templates are used to +// represent type lists. In particular, TypesN +// represents a type list with N types (T1, T2, ..., and TN) in it. +// Except for Types0, every struct in the family has two member types: +// Head for the first type in the list, and Tail for the rest of the +// list. + +// The empty type list. +struct Types0 {}; + +// Type lists of length 1, 2, 3, and so on. + +template +struct Types1 { + typedef T1 Head; + typedef Types0 Tail; +}; +template +struct Types2 { + typedef T1 Head; + typedef Types1 Tail; +}; + +template +struct Types3 { + typedef T1 Head; + typedef Types2 Tail; +}; + +template +struct Types4 { + typedef T1 Head; + typedef Types3 Tail; +}; + +template +struct Types5 { + typedef T1 Head; + typedef Types4 Tail; +}; + +template +struct Types6 { + typedef T1 Head; + typedef Types5 Tail; +}; + +template +struct Types7 { + typedef T1 Head; + typedef Types6 Tail; +}; + +template +struct Types8 { + typedef T1 Head; + typedef Types7 Tail; +}; + +template +struct Types9 { + typedef T1 Head; + typedef Types8 Tail; +}; + +template +struct Types10 { + typedef T1 Head; + typedef Types9 Tail; +}; + +template +struct Types11 { + typedef T1 Head; + typedef Types10 Tail; +}; + +template +struct Types12 { + typedef T1 Head; + typedef Types11 Tail; +}; + +template +struct Types13 { + typedef T1 Head; + typedef Types12 Tail; +}; + +template +struct Types14 { + typedef T1 Head; + typedef Types13 Tail; +}; + +template +struct Types15 { + typedef T1 Head; + typedef Types14 Tail; +}; + +template +struct Types16 { + typedef T1 Head; + typedef Types15 Tail; +}; + +template +struct Types17 { + typedef T1 Head; + typedef Types16 Tail; +}; + +template +struct Types18 { + typedef T1 Head; + typedef Types17 Tail; +}; + +template +struct Types19 { + typedef T1 Head; + typedef Types18 Tail; +}; + +template +struct Types20 { + typedef T1 Head; + typedef Types19 Tail; +}; + +template +struct Types21 { + typedef T1 Head; + typedef Types20 Tail; +}; + +template +struct Types22 { + typedef T1 Head; + typedef Types21 Tail; +}; + +template +struct Types23 { + typedef T1 Head; + typedef Types22 Tail; +}; + +template +struct Types24 { + typedef T1 Head; + typedef Types23 Tail; +}; + +template +struct Types25 { + typedef T1 Head; + typedef Types24 Tail; +}; + +template +struct Types26 { + typedef T1 Head; + typedef Types25 Tail; +}; + +template +struct Types27 { + typedef T1 Head; + typedef Types26 Tail; +}; + +template +struct Types28 { + typedef T1 Head; + typedef Types27 Tail; +}; + +template +struct Types29 { + typedef T1 Head; + typedef Types28 Tail; +}; + +template +struct Types30 { + typedef T1 Head; + typedef Types29 Tail; +}; + +template +struct Types31 { + typedef T1 Head; + typedef Types30 Tail; +}; + +template +struct Types32 { + typedef T1 Head; + typedef Types31 Tail; +}; + +template +struct Types33 { + typedef T1 Head; + typedef Types32 Tail; +}; + +template +struct Types34 { + typedef T1 Head; + typedef Types33 Tail; +}; + +template +struct Types35 { + typedef T1 Head; + typedef Types34 Tail; +}; + +template +struct Types36 { + typedef T1 Head; + typedef Types35 Tail; +}; + +template +struct Types37 { + typedef T1 Head; + typedef Types36 Tail; +}; + +template +struct Types38 { + typedef T1 Head; + typedef Types37 Tail; +}; + +template +struct Types39 { + typedef T1 Head; + typedef Types38 Tail; +}; + +template +struct Types40 { + typedef T1 Head; + typedef Types39 Tail; +}; + +template +struct Types41 { + typedef T1 Head; + typedef Types40 Tail; +}; + +template +struct Types42 { + typedef T1 Head; + typedef Types41 Tail; +}; + +template +struct Types43 { + typedef T1 Head; + typedef Types42 Tail; +}; + +template +struct Types44 { + typedef T1 Head; + typedef Types43 Tail; +}; + +template +struct Types45 { + typedef T1 Head; + typedef Types44 Tail; +}; + +template +struct Types46 { + typedef T1 Head; + typedef Types45 Tail; +}; + +template +struct Types47 { + typedef T1 Head; + typedef Types46 Tail; +}; + +template +struct Types48 { + typedef T1 Head; + typedef Types47 Tail; +}; + +template +struct Types49 { + typedef T1 Head; + typedef Types48 Tail; +}; + +template +struct Types50 { + typedef T1 Head; + typedef Types49 Tail; +}; + + +} // namespace internal + +// We don't want to require the users to write TypesN<...> directly, +// as that would require them to count the length. Types<...> is much +// easier to write, but generates horrible messages when there is a +// compiler error, as gcc insists on printing out each template +// argument, even if it has the default value (this means Types +// will appear as Types in the compiler +// errors). +// +// Our solution is to combine the best part of the two approaches: a +// user would write Types, and Google Test will translate +// that to TypesN internally to make error messages +// readable. The translation is done by the 'type' member of the +// Types template. +template +struct Types { + typedef internal::Types50 type; +}; + +template <> +struct Types { + typedef internal::Types0 type; +}; +template +struct Types { + typedef internal::Types1 type; +}; +template +struct Types { + typedef internal::Types2 type; +}; +template +struct Types { + typedef internal::Types3 type; +}; +template +struct Types { + typedef internal::Types4 type; +}; +template +struct Types { + typedef internal::Types5 type; +}; +template +struct Types { + typedef internal::Types6 type; +}; +template +struct Types { + typedef internal::Types7 type; +}; +template +struct Types { + typedef internal::Types8 type; +}; +template +struct Types { + typedef internal::Types9 type; +}; +template +struct Types { + typedef internal::Types10 type; +}; +template +struct Types { + typedef internal::Types11 type; +}; +template +struct Types { + typedef internal::Types12 type; +}; +template +struct Types { + typedef internal::Types13 type; +}; +template +struct Types { + typedef internal::Types14 type; +}; +template +struct Types { + typedef internal::Types15 type; +}; +template +struct Types { + typedef internal::Types16 type; +}; +template +struct Types { + typedef internal::Types17 type; +}; +template +struct Types { + typedef internal::Types18 type; +}; +template +struct Types { + typedef internal::Types19 type; +}; +template +struct Types { + typedef internal::Types20 type; +}; +template +struct Types { + typedef internal::Types21 type; +}; +template +struct Types { + typedef internal::Types22 type; +}; +template +struct Types { + typedef internal::Types23 type; +}; +template +struct Types { + typedef internal::Types24 type; +}; +template +struct Types { + typedef internal::Types25 type; +}; +template +struct Types { + typedef internal::Types26 type; +}; +template +struct Types { + typedef internal::Types27 type; +}; +template +struct Types { + typedef internal::Types28 type; +}; +template +struct Types { + typedef internal::Types29 type; +}; +template +struct Types { + typedef internal::Types30 type; +}; +template +struct Types { + typedef internal::Types31 type; +}; +template +struct Types { + typedef internal::Types32 type; +}; +template +struct Types { + typedef internal::Types33 type; +}; +template +struct Types { + typedef internal::Types34 type; +}; +template +struct Types { + typedef internal::Types35 type; +}; +template +struct Types { + typedef internal::Types36 type; +}; +template +struct Types { + typedef internal::Types37 type; +}; +template +struct Types { + typedef internal::Types38 type; +}; +template +struct Types { + typedef internal::Types39 type; +}; +template +struct Types { + typedef internal::Types40 type; +}; +template +struct Types { + typedef internal::Types41 type; +}; +template +struct Types { + typedef internal::Types42 type; +}; +template +struct Types { + typedef internal::Types43 type; +}; +template +struct Types { + typedef internal::Types44 type; +}; +template +struct Types { + typedef internal::Types45 type; +}; +template +struct Types { + typedef internal::Types46 type; +}; +template +struct Types { + typedef internal::Types47 type; +}; +template +struct Types { + typedef internal::Types48 type; +}; +template +struct Types { + typedef internal::Types49 type; +}; + +namespace internal { + +# define GTEST_TEMPLATE_ template class + +// The template "selector" struct TemplateSel is used to +// represent Tmpl, which must be a class template with one type +// parameter, as a type. TemplateSel::Bind::type is defined +// as the type Tmpl. This allows us to actually instantiate the +// template "selected" by TemplateSel. +// +// This trick is necessary for simulating typedef for class templates, +// which C++ doesn't support directly. +template +struct TemplateSel { + template + struct Bind { + typedef Tmpl type; + }; +}; + +# define GTEST_BIND_(TmplSel, T) \ + TmplSel::template Bind::type + +// A unique struct template used as the default value for the +// arguments of class template Templates. This allows us to simulate +// variadic templates (e.g. Templates, Templates, +// and etc), which C++ doesn't support directly. +template +struct NoneT {}; + +// The following family of struct and struct templates are used to +// represent template lists. In particular, TemplatesN represents a list of N templates (T1, T2, ..., and TN). Except +// for Templates0, every struct in the family has two member types: +// Head for the selector of the first template in the list, and Tail +// for the rest of the list. + +// The empty template list. +struct Templates0 {}; + +// Template lists of length 1, 2, 3, and so on. + +template +struct Templates1 { + typedef TemplateSel Head; + typedef Templates0 Tail; +}; +template +struct Templates2 { + typedef TemplateSel Head; + typedef Templates1 Tail; +}; + +template +struct Templates3 { + typedef TemplateSel Head; + typedef Templates2 Tail; +}; + +template +struct Templates4 { + typedef TemplateSel Head; + typedef Templates3 Tail; +}; + +template +struct Templates5 { + typedef TemplateSel Head; + typedef Templates4 Tail; +}; + +template +struct Templates6 { + typedef TemplateSel Head; + typedef Templates5 Tail; +}; + +template +struct Templates7 { + typedef TemplateSel Head; + typedef Templates6 Tail; +}; + +template +struct Templates8 { + typedef TemplateSel Head; + typedef Templates7 Tail; +}; + +template +struct Templates9 { + typedef TemplateSel Head; + typedef Templates8 Tail; +}; + +template +struct Templates10 { + typedef TemplateSel Head; + typedef Templates9 Tail; +}; + +template +struct Templates11 { + typedef TemplateSel Head; + typedef Templates10 Tail; +}; + +template +struct Templates12 { + typedef TemplateSel Head; + typedef Templates11 Tail; +}; + +template +struct Templates13 { + typedef TemplateSel Head; + typedef Templates12 Tail; +}; + +template +struct Templates14 { + typedef TemplateSel Head; + typedef Templates13 Tail; +}; + +template +struct Templates15 { + typedef TemplateSel Head; + typedef Templates14 Tail; +}; + +template +struct Templates16 { + typedef TemplateSel Head; + typedef Templates15 Tail; +}; + +template +struct Templates17 { + typedef TemplateSel Head; + typedef Templates16 Tail; +}; + +template +struct Templates18 { + typedef TemplateSel Head; + typedef Templates17 Tail; +}; + +template +struct Templates19 { + typedef TemplateSel Head; + typedef Templates18 Tail; +}; + +template +struct Templates20 { + typedef TemplateSel Head; + typedef Templates19 Tail; +}; + +template +struct Templates21 { + typedef TemplateSel Head; + typedef Templates20 Tail; +}; + +template +struct Templates22 { + typedef TemplateSel Head; + typedef Templates21 Tail; +}; + +template +struct Templates23 { + typedef TemplateSel Head; + typedef Templates22 Tail; +}; + +template +struct Templates24 { + typedef TemplateSel Head; + typedef Templates23 Tail; +}; + +template +struct Templates25 { + typedef TemplateSel Head; + typedef Templates24 Tail; +}; + +template +struct Templates26 { + typedef TemplateSel Head; + typedef Templates25 Tail; +}; + +template +struct Templates27 { + typedef TemplateSel Head; + typedef Templates26 Tail; +}; + +template +struct Templates28 { + typedef TemplateSel Head; + typedef Templates27 Tail; +}; + +template +struct Templates29 { + typedef TemplateSel Head; + typedef Templates28 Tail; +}; + +template +struct Templates30 { + typedef TemplateSel Head; + typedef Templates29 Tail; +}; + +template +struct Templates31 { + typedef TemplateSel Head; + typedef Templates30 Tail; +}; + +template +struct Templates32 { + typedef TemplateSel Head; + typedef Templates31 Tail; +}; + +template +struct Templates33 { + typedef TemplateSel Head; + typedef Templates32 Tail; +}; + +template +struct Templates34 { + typedef TemplateSel Head; + typedef Templates33 Tail; +}; + +template +struct Templates35 { + typedef TemplateSel Head; + typedef Templates34 Tail; +}; + +template +struct Templates36 { + typedef TemplateSel Head; + typedef Templates35 Tail; +}; + +template +struct Templates37 { + typedef TemplateSel Head; + typedef Templates36 Tail; +}; + +template +struct Templates38 { + typedef TemplateSel Head; + typedef Templates37 Tail; +}; + +template +struct Templates39 { + typedef TemplateSel Head; + typedef Templates38 Tail; +}; + +template +struct Templates40 { + typedef TemplateSel Head; + typedef Templates39 Tail; +}; + +template +struct Templates41 { + typedef TemplateSel Head; + typedef Templates40 Tail; +}; + +template +struct Templates42 { + typedef TemplateSel Head; + typedef Templates41 Tail; +}; + +template +struct Templates43 { + typedef TemplateSel Head; + typedef Templates42 Tail; +}; + +template +struct Templates44 { + typedef TemplateSel Head; + typedef Templates43 Tail; +}; + +template +struct Templates45 { + typedef TemplateSel Head; + typedef Templates44 Tail; +}; + +template +struct Templates46 { + typedef TemplateSel Head; + typedef Templates45 Tail; +}; + +template +struct Templates47 { + typedef TemplateSel Head; + typedef Templates46 Tail; +}; + +template +struct Templates48 { + typedef TemplateSel Head; + typedef Templates47 Tail; +}; + +template +struct Templates49 { + typedef TemplateSel Head; + typedef Templates48 Tail; +}; + +template +struct Templates50 { + typedef TemplateSel Head; + typedef Templates49 Tail; +}; + + +// We don't want to require the users to write TemplatesN<...> directly, +// as that would require them to count the length. Templates<...> is much +// easier to write, but generates horrible messages when there is a +// compiler error, as gcc insists on printing out each template +// argument, even if it has the default value (this means Templates +// will appear as Templates in the compiler +// errors). +// +// Our solution is to combine the best part of the two approaches: a +// user would write Templates, and Google Test will translate +// that to TemplatesN internally to make error messages +// readable. The translation is done by the 'type' member of the +// Templates template. +template +struct Templates { + typedef Templates50 type; +}; + +template <> +struct Templates { + typedef Templates0 type; +}; +template +struct Templates { + typedef Templates1 type; +}; +template +struct Templates { + typedef Templates2 type; +}; +template +struct Templates { + typedef Templates3 type; +}; +template +struct Templates { + typedef Templates4 type; +}; +template +struct Templates { + typedef Templates5 type; +}; +template +struct Templates { + typedef Templates6 type; +}; +template +struct Templates { + typedef Templates7 type; +}; +template +struct Templates { + typedef Templates8 type; +}; +template +struct Templates { + typedef Templates9 type; +}; +template +struct Templates { + typedef Templates10 type; +}; +template +struct Templates { + typedef Templates11 type; +}; +template +struct Templates { + typedef Templates12 type; +}; +template +struct Templates { + typedef Templates13 type; +}; +template +struct Templates { + typedef Templates14 type; +}; +template +struct Templates { + typedef Templates15 type; +}; +template +struct Templates { + typedef Templates16 type; +}; +template +struct Templates { + typedef Templates17 type; +}; +template +struct Templates { + typedef Templates18 type; +}; +template +struct Templates { + typedef Templates19 type; +}; +template +struct Templates { + typedef Templates20 type; +}; +template +struct Templates { + typedef Templates21 type; +}; +template +struct Templates { + typedef Templates22 type; +}; +template +struct Templates { + typedef Templates23 type; +}; +template +struct Templates { + typedef Templates24 type; +}; +template +struct Templates { + typedef Templates25 type; +}; +template +struct Templates { + typedef Templates26 type; +}; +template +struct Templates { + typedef Templates27 type; +}; +template +struct Templates { + typedef Templates28 type; +}; +template +struct Templates { + typedef Templates29 type; +}; +template +struct Templates { + typedef Templates30 type; +}; +template +struct Templates { + typedef Templates31 type; +}; +template +struct Templates { + typedef Templates32 type; +}; +template +struct Templates { + typedef Templates33 type; +}; +template +struct Templates { + typedef Templates34 type; +}; +template +struct Templates { + typedef Templates35 type; +}; +template +struct Templates { + typedef Templates36 type; +}; +template +struct Templates { + typedef Templates37 type; +}; +template +struct Templates { + typedef Templates38 type; +}; +template +struct Templates { + typedef Templates39 type; +}; +template +struct Templates { + typedef Templates40 type; +}; +template +struct Templates { + typedef Templates41 type; +}; +template +struct Templates { + typedef Templates42 type; +}; +template +struct Templates { + typedef Templates43 type; +}; +template +struct Templates { + typedef Templates44 type; +}; +template +struct Templates { + typedef Templates45 type; +}; +template +struct Templates { + typedef Templates46 type; +}; +template +struct Templates { + typedef Templates47 type; +}; +template +struct Templates { + typedef Templates48 type; +}; +template +struct Templates { + typedef Templates49 type; +}; + +// The TypeList template makes it possible to use either a single type +// or a Types<...> list in TYPED_TEST_CASE() and +// INSTANTIATE_TYPED_TEST_CASE_P(). + +template +struct TypeList { + typedef Types1 type; +}; + +template +struct TypeList > { + typedef typename Types::type type; +}; + +#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-type-util.h.pump b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-type-util.h.pump new file mode 100644 index 00000000..251fdf02 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/include/gtest/internal/gtest-type-util.h.pump @@ -0,0 +1,297 @@ +$$ -*- mode: c++; -*- +$var n = 50 $$ Maximum length of type lists we want to support. +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Type utilities needed for implementing typed and type-parameterized +// tests. This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +// Currently we support at most $n types in a list, and at most $n +// type-parameterized tests in one type-parameterized test case. +// Please contact googletestframework@googlegroups.com if you need +// more. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ + +#include "gtest/internal/gtest-port.h" + +// #ifdef __GNUC__ is too general here. It is possible to use gcc without using +// libstdc++ (which is where cxxabi.h comes from). +# if GTEST_HAS_CXXABI_H_ +# include +# elif defined(__HP_aCC) +# include +# endif // GTEST_HASH_CXXABI_H_ + +namespace testing { +namespace internal { + +// GetTypeName() returns a human-readable name of type T. +// NB: This function is also used in Google Mock, so don't move it inside of +// the typed-test-only section below. +template +std::string GetTypeName() { +# if GTEST_HAS_RTTI + + const char* const name = typeid(T).name(); +# if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC) + int status = 0; + // gcc's implementation of typeid(T).name() mangles the type name, + // so we have to demangle it. +# if GTEST_HAS_CXXABI_H_ + using abi::__cxa_demangle; +# endif // GTEST_HAS_CXXABI_H_ + char* const readable_name = __cxa_demangle(name, 0, 0, &status); + const std::string name_str(status == 0 ? readable_name : name); + free(readable_name); + return name_str; +# else + return name; +# endif // GTEST_HAS_CXXABI_H_ || __HP_aCC + +# else + + return ""; + +# endif // GTEST_HAS_RTTI +} + +#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// AssertyTypeEq::type is defined iff T1 and T2 are the same +// type. This can be used as a compile-time assertion to ensure that +// two types are equal. + +template +struct AssertTypeEq; + +template +struct AssertTypeEq { + typedef bool type; +}; + +// A unique type used as the default value for the arguments of class +// template Types. This allows us to simulate variadic templates +// (e.g. Types, Type, and etc), which C++ doesn't +// support directly. +struct None {}; + +// The following family of struct and struct templates are used to +// represent type lists. In particular, TypesN +// represents a type list with N types (T1, T2, ..., and TN) in it. +// Except for Types0, every struct in the family has two member types: +// Head for the first type in the list, and Tail for the rest of the +// list. + +// The empty type list. +struct Types0 {}; + +// Type lists of length 1, 2, 3, and so on. + +template +struct Types1 { + typedef T1 Head; + typedef Types0 Tail; +}; + +$range i 2..n + +$for i [[ +$range j 1..i +$range k 2..i +template <$for j, [[typename T$j]]> +struct Types$i { + typedef T1 Head; + typedef Types$(i-1)<$for k, [[T$k]]> Tail; +}; + + +]] + +} // namespace internal + +// We don't want to require the users to write TypesN<...> directly, +// as that would require them to count the length. Types<...> is much +// easier to write, but generates horrible messages when there is a +// compiler error, as gcc insists on printing out each template +// argument, even if it has the default value (this means Types +// will appear as Types in the compiler +// errors). +// +// Our solution is to combine the best part of the two approaches: a +// user would write Types, and Google Test will translate +// that to TypesN internally to make error messages +// readable. The translation is done by the 'type' member of the +// Types template. + +$range i 1..n +template <$for i, [[typename T$i = internal::None]]> +struct Types { + typedef internal::Types$n<$for i, [[T$i]]> type; +}; + +template <> +struct Types<$for i, [[internal::None]]> { + typedef internal::Types0 type; +}; + +$range i 1..n-1 +$for i [[ +$range j 1..i +$range k i+1..n +template <$for j, [[typename T$j]]> +struct Types<$for j, [[T$j]]$for k[[, internal::None]]> { + typedef internal::Types$i<$for j, [[T$j]]> type; +}; + +]] + +namespace internal { + +# define GTEST_TEMPLATE_ template class + +// The template "selector" struct TemplateSel is used to +// represent Tmpl, which must be a class template with one type +// parameter, as a type. TemplateSel::Bind::type is defined +// as the type Tmpl. This allows us to actually instantiate the +// template "selected" by TemplateSel. +// +// This trick is necessary for simulating typedef for class templates, +// which C++ doesn't support directly. +template +struct TemplateSel { + template + struct Bind { + typedef Tmpl type; + }; +}; + +# define GTEST_BIND_(TmplSel, T) \ + TmplSel::template Bind::type + +// A unique struct template used as the default value for the +// arguments of class template Templates. This allows us to simulate +// variadic templates (e.g. Templates, Templates, +// and etc), which C++ doesn't support directly. +template +struct NoneT {}; + +// The following family of struct and struct templates are used to +// represent template lists. In particular, TemplatesN represents a list of N templates (T1, T2, ..., and TN). Except +// for Templates0, every struct in the family has two member types: +// Head for the selector of the first template in the list, and Tail +// for the rest of the list. + +// The empty template list. +struct Templates0 {}; + +// Template lists of length 1, 2, 3, and so on. + +template +struct Templates1 { + typedef TemplateSel Head; + typedef Templates0 Tail; +}; + +$range i 2..n + +$for i [[ +$range j 1..i +$range k 2..i +template <$for j, [[GTEST_TEMPLATE_ T$j]]> +struct Templates$i { + typedef TemplateSel Head; + typedef Templates$(i-1)<$for k, [[T$k]]> Tail; +}; + + +]] + +// We don't want to require the users to write TemplatesN<...> directly, +// as that would require them to count the length. Templates<...> is much +// easier to write, but generates horrible messages when there is a +// compiler error, as gcc insists on printing out each template +// argument, even if it has the default value (this means Templates +// will appear as Templates in the compiler +// errors). +// +// Our solution is to combine the best part of the two approaches: a +// user would write Templates, and Google Test will translate +// that to TemplatesN internally to make error messages +// readable. The translation is done by the 'type' member of the +// Templates template. + +$range i 1..n +template <$for i, [[GTEST_TEMPLATE_ T$i = NoneT]]> +struct Templates { + typedef Templates$n<$for i, [[T$i]]> type; +}; + +template <> +struct Templates<$for i, [[NoneT]]> { + typedef Templates0 type; +}; + +$range i 1..n-1 +$for i [[ +$range j 1..i +$range k i+1..n +template <$for j, [[GTEST_TEMPLATE_ T$j]]> +struct Templates<$for j, [[T$j]]$for k[[, NoneT]]> { + typedef Templates$i<$for j, [[T$j]]> type; +}; + +]] + +// The TypeList template makes it possible to use either a single type +// or a Types<...> list in TYPED_TEST_CASE() and +// INSTANTIATE_TYPED_TEST_CASE_P(). + +template +struct TypeList { + typedef Types1 type; +}; + + +$range i 1..n +template <$for i, [[typename T$i]]> +struct TypeList > { + typedef typename Types<$for i, [[T$i]]>::type type; +}; + +#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-all.cc b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-all.cc new file mode 100644 index 00000000..0a9cee52 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-all.cc @@ -0,0 +1,48 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: mheule@google.com (Markus Heule) +// +// Google C++ Testing Framework (Google Test) +// +// Sometimes it's desirable to build Google Test by compiling a single file. +// This file serves this purpose. + +// This line ensures that gtest.h can be compiled on its own, even +// when it's fused. +#include "gtest/gtest.h" + +// The following lines pull in the real gtest *.cc files. +#include "src/gtest.cc" +#include "src/gtest-death-test.cc" +#include "src/gtest-filepath.cc" +#include "src/gtest-port.cc" +#include "src/gtest-printers.cc" +#include "src/gtest-test-part.cc" +#include "src/gtest-typed-test.cc" diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-death-test.cc b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-death-test.cc new file mode 100644 index 00000000..a6023fce --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-death-test.cc @@ -0,0 +1,1344 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan), vladl@google.com (Vlad Losev) +// +// This file implements death tests. + +#include "gtest/gtest-death-test.h" +#include "gtest/internal/gtest-port.h" + +#if GTEST_HAS_DEATH_TEST + +# if GTEST_OS_MAC +# include +# endif // GTEST_OS_MAC + +# include +# include +# include + +# if GTEST_OS_LINUX +# include +# endif // GTEST_OS_LINUX + +# include + +# if GTEST_OS_WINDOWS +# include +# else +# include +# include +# endif // GTEST_OS_WINDOWS + +# if GTEST_OS_QNX +# include +# endif // GTEST_OS_QNX + +#endif // GTEST_HAS_DEATH_TEST + +#include "gtest/gtest-message.h" +#include "gtest/internal/gtest-string.h" + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#include "src/gtest-internal-inl.h" +#undef GTEST_IMPLEMENTATION_ + +namespace testing { + +// Constants. + +// The default death test style. +static const char kDefaultDeathTestStyle[] = "fast"; + +GTEST_DEFINE_string_( + death_test_style, + internal::StringFromGTestEnv("death_test_style", kDefaultDeathTestStyle), + "Indicates how to run a death test in a forked child process: " + "\"threadsafe\" (child process re-executes the test binary " + "from the beginning, running only the specific death test) or " + "\"fast\" (child process runs the death test immediately " + "after forking)."); + +GTEST_DEFINE_bool_( + death_test_use_fork, + internal::BoolFromGTestEnv("death_test_use_fork", false), + "Instructs to use fork()/_exit() instead of clone() in death tests. " + "Ignored and always uses fork() on POSIX systems where clone() is not " + "implemented. Useful when running under valgrind or similar tools if " + "those do not support clone(). Valgrind 3.3.1 will just fail if " + "it sees an unsupported combination of clone() flags. " + "It is not recommended to use this flag w/o valgrind though it will " + "work in 99% of the cases. Once valgrind is fixed, this flag will " + "most likely be removed."); + +namespace internal { +GTEST_DEFINE_string_( + internal_run_death_test, "", + "Indicates the file, line number, temporal index of " + "the single death test to run, and a file descriptor to " + "which a success code may be sent, all separated by " + "the '|' characters. This flag is specified if and only if the current " + "process is a sub-process launched for running a thread-safe " + "death test. FOR INTERNAL USE ONLY."); +} // namespace internal + +#if GTEST_HAS_DEATH_TEST + +namespace internal { + +// Valid only for fast death tests. Indicates the code is running in the +// child process of a fast style death test. +static bool g_in_fast_death_test_child = false; + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +bool InDeathTestChild() { +# if GTEST_OS_WINDOWS + + // On Windows, death tests are thread-safe regardless of the value of the + // death_test_style flag. + return !GTEST_FLAG(internal_run_death_test).empty(); + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") + return !GTEST_FLAG(internal_run_death_test).empty(); + else + return g_in_fast_death_test_child; +#endif +} + +} // namespace internal + +// ExitedWithCode constructor. +ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) { +} + +// ExitedWithCode function-call operator. +bool ExitedWithCode::operator()(int exit_status) const { +# if GTEST_OS_WINDOWS + + return exit_status == exit_code_; + +# else + + return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_; + +# endif // GTEST_OS_WINDOWS +} + +# if !GTEST_OS_WINDOWS +// KilledBySignal constructor. +KilledBySignal::KilledBySignal(int signum) : signum_(signum) { +} + +// KilledBySignal function-call operator. +bool KilledBySignal::operator()(int exit_status) const { + return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_; +} +# endif // !GTEST_OS_WINDOWS + +namespace internal { + +// Utilities needed for death tests. + +// Generates a textual description of a given exit code, in the format +// specified by wait(2). +static std::string ExitSummary(int exit_code) { + Message m; + +# if GTEST_OS_WINDOWS + + m << "Exited with exit status " << exit_code; + +# else + + if (WIFEXITED(exit_code)) { + m << "Exited with exit status " << WEXITSTATUS(exit_code); + } else if (WIFSIGNALED(exit_code)) { + m << "Terminated by signal " << WTERMSIG(exit_code); + } +# ifdef WCOREDUMP + if (WCOREDUMP(exit_code)) { + m << " (core dumped)"; + } +# endif +# endif // GTEST_OS_WINDOWS + + return m.GetString(); +} + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +bool ExitedUnsuccessfully(int exit_status) { + return !ExitedWithCode(0)(exit_status); +} + +# if !GTEST_OS_WINDOWS +// Generates a textual failure message when a death test finds more than +// one thread running, or cannot determine the number of threads, prior +// to executing the given statement. It is the responsibility of the +// caller not to pass a thread_count of 1. +static std::string DeathTestThreadWarning(size_t thread_count) { + Message msg; + msg << "Death tests use fork(), which is unsafe particularly" + << " in a threaded context. For this test, " << GTEST_NAME_ << " "; + if (thread_count == 0) + msg << "couldn't detect the number of threads."; + else + msg << "detected " << thread_count << " threads."; + return msg.GetString(); +} +# endif // !GTEST_OS_WINDOWS + +// Flag characters for reporting a death test that did not die. +static const char kDeathTestLived = 'L'; +static const char kDeathTestReturned = 'R'; +static const char kDeathTestThrew = 'T'; +static const char kDeathTestInternalError = 'I'; + +// An enumeration describing all of the possible ways that a death test can +// conclude. DIED means that the process died while executing the test +// code; LIVED means that process lived beyond the end of the test code; +// RETURNED means that the test statement attempted to execute a return +// statement, which is not allowed; THREW means that the test statement +// returned control by throwing an exception. IN_PROGRESS means the test +// has not yet concluded. +// TODO(vladl@google.com): Unify names and possibly values for +// AbortReason, DeathTestOutcome, and flag characters above. +enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW }; + +// Routine for aborting the program which is safe to call from an +// exec-style death test child process, in which case the error +// message is propagated back to the parent process. Otherwise, the +// message is simply printed to stderr. In either case, the program +// then exits with status 1. +void DeathTestAbort(const std::string& message) { + // On a POSIX system, this function may be called from a threadsafe-style + // death test child process, which operates on a very small stack. Use + // the heap for any additional non-minuscule memory requirements. + const InternalRunDeathTestFlag* const flag = + GetUnitTestImpl()->internal_run_death_test_flag(); + if (flag != NULL) { + FILE* parent = posix::FDOpen(flag->write_fd(), "w"); + fputc(kDeathTestInternalError, parent); + fprintf(parent, "%s", message.c_str()); + fflush(parent); + _exit(1); + } else { + fprintf(stderr, "%s", message.c_str()); + fflush(stderr); + posix::Abort(); + } +} + +// A replacement for CHECK that calls DeathTestAbort if the assertion +// fails. +# define GTEST_DEATH_TEST_CHECK_(expression) \ + do { \ + if (!::testing::internal::IsTrue(expression)) { \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// This macro is similar to GTEST_DEATH_TEST_CHECK_, but it is meant for +// evaluating any system call that fulfills two conditions: it must return +// -1 on failure, and set errno to EINTR when it is interrupted and +// should be tried again. The macro expands to a loop that repeatedly +// evaluates the expression as long as it evaluates to -1 and sets +// errno to EINTR. If the expression evaluates to -1 but errno is +// something other than EINTR, DeathTestAbort is called. +# define GTEST_DEATH_TEST_CHECK_SYSCALL_(expression) \ + do { \ + int gtest_retval; \ + do { \ + gtest_retval = (expression); \ + } while (gtest_retval == -1 && errno == EINTR); \ + if (gtest_retval == -1) { \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression + " != -1"); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// Returns the message describing the last system error in errno. +std::string GetLastErrnoDescription() { + return errno == 0 ? "" : posix::StrError(errno); +} + +// This is called from a death test parent process to read a failure +// message from the death test child process and log it with the FATAL +// severity. On Windows, the message is read from a pipe handle. On other +// platforms, it is read from a file descriptor. +static void FailFromInternalError(int fd) { + Message error; + char buffer[256]; + int num_read; + + do { + while ((num_read = posix::Read(fd, buffer, 255)) > 0) { + buffer[num_read] = '\0'; + error << buffer; + } + } while (num_read == -1 && errno == EINTR); + + if (num_read == 0) { + GTEST_LOG_(FATAL) << error.GetString(); + } else { + const int last_error = errno; + GTEST_LOG_(FATAL) << "Error while reading death test internal: " + << GetLastErrnoDescription() << " [" << last_error << "]"; + } +} + +// Death test constructor. Increments the running death test count +// for the current test. +DeathTest::DeathTest() { + TestInfo* const info = GetUnitTestImpl()->current_test_info(); + if (info == NULL) { + DeathTestAbort("Cannot run a death test outside of a TEST or " + "TEST_F construct"); + } +} + +// Creates and returns a death test by dispatching to the current +// death test factory. +bool DeathTest::Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test) { + return GetUnitTestImpl()->death_test_factory()->Create( + statement, regex, file, line, test); +} + +const char* DeathTest::LastMessage() { + return last_death_test_message_.c_str(); +} + +void DeathTest::set_last_death_test_message(const std::string& message) { + last_death_test_message_ = message; +} + +std::string DeathTest::last_death_test_message_; + +// Provides cross platform implementation for some death functionality. +class DeathTestImpl : public DeathTest { + protected: + DeathTestImpl(const char* a_statement, const RE* a_regex) + : statement_(a_statement), + regex_(a_regex), + spawned_(false), + status_(-1), + outcome_(IN_PROGRESS), + read_fd_(-1), + write_fd_(-1) {} + + // read_fd_ is expected to be closed and cleared by a derived class. + ~DeathTestImpl() { GTEST_DEATH_TEST_CHECK_(read_fd_ == -1); } + + void Abort(AbortReason reason); + virtual bool Passed(bool status_ok); + + const char* statement() const { return statement_; } + const RE* regex() const { return regex_; } + bool spawned() const { return spawned_; } + void set_spawned(bool is_spawned) { spawned_ = is_spawned; } + int status() const { return status_; } + void set_status(int a_status) { status_ = a_status; } + DeathTestOutcome outcome() const { return outcome_; } + void set_outcome(DeathTestOutcome an_outcome) { outcome_ = an_outcome; } + int read_fd() const { return read_fd_; } + void set_read_fd(int fd) { read_fd_ = fd; } + int write_fd() const { return write_fd_; } + void set_write_fd(int fd) { write_fd_ = fd; } + + // Called in the parent process only. Reads the result code of the death + // test child process via a pipe, interprets it to set the outcome_ + // member, and closes read_fd_. Outputs diagnostics and terminates in + // case of unexpected codes. + void ReadAndInterpretStatusByte(); + + private: + // The textual content of the code this object is testing. This class + // doesn't own this string and should not attempt to delete it. + const char* const statement_; + // The regular expression which test output must match. DeathTestImpl + // doesn't own this object and should not attempt to delete it. + const RE* const regex_; + // True if the death test child process has been successfully spawned. + bool spawned_; + // The exit status of the child process. + int status_; + // How the death test concluded. + DeathTestOutcome outcome_; + // Descriptor to the read end of the pipe to the child process. It is + // always -1 in the child process. The child keeps its write end of the + // pipe in write_fd_. + int read_fd_; + // Descriptor to the child's write end of the pipe to the parent process. + // It is always -1 in the parent process. The parent keeps its end of the + // pipe in read_fd_. + int write_fd_; +}; + +// Called in the parent process only. Reads the result code of the death +// test child process via a pipe, interprets it to set the outcome_ +// member, and closes read_fd_. Outputs diagnostics and terminates in +// case of unexpected codes. +void DeathTestImpl::ReadAndInterpretStatusByte() { + char flag; + int bytes_read; + + // The read() here blocks until data is available (signifying the + // failure of the death test) or until the pipe is closed (signifying + // its success), so it's okay to call this in the parent before + // the child process has exited. + do { + bytes_read = posix::Read(read_fd(), &flag, 1); + } while (bytes_read == -1 && errno == EINTR); + + if (bytes_read == 0) { + set_outcome(DIED); + } else if (bytes_read == 1) { + switch (flag) { + case kDeathTestReturned: + set_outcome(RETURNED); + break; + case kDeathTestThrew: + set_outcome(THREW); + break; + case kDeathTestLived: + set_outcome(LIVED); + break; + case kDeathTestInternalError: + FailFromInternalError(read_fd()); // Does not return. + break; + default: + GTEST_LOG_(FATAL) << "Death test child process reported " + << "unexpected status byte (" + << static_cast(flag) << ")"; + } + } else { + GTEST_LOG_(FATAL) << "Read from death test child process failed: " + << GetLastErrnoDescription(); + } + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Close(read_fd())); + set_read_fd(-1); +} + +// Signals that the death test code which should have exited, didn't. +// Should be called only in a death test child process. +// Writes a status byte to the child's status file descriptor, then +// calls _exit(1). +void DeathTestImpl::Abort(AbortReason reason) { + // The parent process considers the death test to be a failure if + // it finds any data in our pipe. So, here we write a single flag byte + // to the pipe, then exit. + const char status_ch = + reason == TEST_DID_NOT_DIE ? kDeathTestLived : + reason == TEST_THREW_EXCEPTION ? kDeathTestThrew : kDeathTestReturned; + + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Write(write_fd(), &status_ch, 1)); + // We are leaking the descriptor here because on some platforms (i.e., + // when built as Windows DLL), destructors of global objects will still + // run after calling _exit(). On such systems, write_fd_ will be + // indirectly closed from the destructor of UnitTestImpl, causing double + // close if it is also closed here. On debug configurations, double close + // may assert. As there are no in-process buffers to flush here, we are + // relying on the OS to close the descriptor after the process terminates + // when the destructors are not run. + _exit(1); // Exits w/o any normal exit hooks (we were supposed to crash) +} + +// Returns an indented copy of stderr output for a death test. +// This makes distinguishing death test output lines from regular log lines +// much easier. +static ::std::string FormatDeathTestOutput(const ::std::string& output) { + ::std::string ret; + for (size_t at = 0; ; ) { + const size_t line_end = output.find('\n', at); + ret += "[ DEATH ] "; + if (line_end == ::std::string::npos) { + ret += output.substr(at); + break; + } + ret += output.substr(at, line_end + 1 - at); + at = line_end + 1; + } + return ret; +} + +// Assesses the success or failure of a death test, using both private +// members which have previously been set, and one argument: +// +// Private data members: +// outcome: An enumeration describing how the death test +// concluded: DIED, LIVED, THREW, or RETURNED. The death test +// fails in the latter three cases. +// status: The exit status of the child process. On *nix, it is in the +// in the format specified by wait(2). On Windows, this is the +// value supplied to the ExitProcess() API or a numeric code +// of the exception that terminated the program. +// regex: A regular expression object to be applied to +// the test's captured standard error output; the death test +// fails if it does not match. +// +// Argument: +// status_ok: true if exit_status is acceptable in the context of +// this particular death test, which fails if it is false +// +// Returns true iff all of the above conditions are met. Otherwise, the +// first failing condition, in the order given above, is the one that is +// reported. Also sets the last death test message string. +bool DeathTestImpl::Passed(bool status_ok) { + if (!spawned()) + return false; + + const std::string error_message = GetCapturedStderr(); + + bool success = false; + Message buffer; + + buffer << "Death test: " << statement() << "\n"; + switch (outcome()) { + case LIVED: + buffer << " Result: failed to die.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case THREW: + buffer << " Result: threw an exception.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case RETURNED: + buffer << " Result: illegal return in test statement.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case DIED: + if (status_ok) { + const bool matched = RE::PartialMatch(error_message.c_str(), *regex()); + if (matched) { + success = true; + } else { + buffer << " Result: died but not with expected error.\n" + << " Expected: " << regex()->pattern() << "\n" + << "Actual msg:\n" << FormatDeathTestOutput(error_message); + } + } else { + buffer << " Result: died but not with expected exit code:\n" + << " " << ExitSummary(status()) << "\n" + << "Actual msg:\n" << FormatDeathTestOutput(error_message); + } + break; + case IN_PROGRESS: + default: + GTEST_LOG_(FATAL) + << "DeathTest::Passed somehow called before conclusion of test"; + } + + DeathTest::set_last_death_test_message(buffer.GetString()); + return success; +} + +# if GTEST_OS_WINDOWS +// WindowsDeathTest implements death tests on Windows. Due to the +// specifics of starting new processes on Windows, death tests there are +// always threadsafe, and Google Test considers the +// --gtest_death_test_style=fast setting to be equivalent to +// --gtest_death_test_style=threadsafe there. +// +// A few implementation notes: Like the Linux version, the Windows +// implementation uses pipes for child-to-parent communication. But due to +// the specifics of pipes on Windows, some extra steps are required: +// +// 1. The parent creates a communication pipe and stores handles to both +// ends of it. +// 2. The parent starts the child and provides it with the information +// necessary to acquire the handle to the write end of the pipe. +// 3. The child acquires the write end of the pipe and signals the parent +// using a Windows event. +// 4. Now the parent can release the write end of the pipe on its side. If +// this is done before step 3, the object's reference count goes down to +// 0 and it is destroyed, preventing the child from acquiring it. The +// parent now has to release it, or read operations on the read end of +// the pipe will not return when the child terminates. +// 5. The parent reads child's output through the pipe (outcome code and +// any possible error messages) from the pipe, and its stderr and then +// determines whether to fail the test. +// +// Note: to distinguish Win32 API calls from the local method and function +// calls, the former are explicitly resolved in the global namespace. +// +class WindowsDeathTest : public DeathTestImpl { + public: + WindowsDeathTest(const char* a_statement, + const RE* a_regex, + const char* file, + int line) + : DeathTestImpl(a_statement, a_regex), file_(file), line_(line) {} + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + virtual TestRole AssumeRole(); + + private: + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; + // Handle to the write end of the pipe to the child process. + AutoHandle write_handle_; + // Child process handle. + AutoHandle child_handle_; + // Event the child process uses to signal the parent that it has + // acquired the handle to the write end of the pipe. After seeing this + // event the parent can release its own handles to make sure its + // ReadFile() calls return when the child terminates. + AutoHandle event_handle_; +}; + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int WindowsDeathTest::Wait() { + if (!spawned()) + return 0; + + // Wait until the child either signals that it has acquired the write end + // of the pipe or it dies. + const HANDLE wait_handles[2] = { child_handle_.Get(), event_handle_.Get() }; + switch (::WaitForMultipleObjects(2, + wait_handles, + FALSE, // Waits for any of the handles. + INFINITE)) { + case WAIT_OBJECT_0: + case WAIT_OBJECT_0 + 1: + break; + default: + GTEST_DEATH_TEST_CHECK_(false); // Should not get here. + } + + // The child has acquired the write end of the pipe or exited. + // We release the handle on our side and continue. + write_handle_.Reset(); + event_handle_.Reset(); + + ReadAndInterpretStatusByte(); + + // Waits for the child process to exit if it haven't already. This + // returns immediately if the child has already exited, regardless of + // whether previous calls to WaitForMultipleObjects synchronized on this + // handle or not. + GTEST_DEATH_TEST_CHECK_( + WAIT_OBJECT_0 == ::WaitForSingleObject(child_handle_.Get(), + INFINITE)); + DWORD status_code; + GTEST_DEATH_TEST_CHECK_( + ::GetExitCodeProcess(child_handle_.Get(), &status_code) != FALSE); + child_handle_.Reset(); + set_status(static_cast(status_code)); + return status(); +} + +// The AssumeRole process for a Windows death test. It creates a child +// process with the same executable as the current process to run the +// death test. The child process is given the --gtest_filter and +// --gtest_internal_run_death_test flags such that it knows to run the +// current death test only. +DeathTest::TestRole WindowsDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != NULL) { + // ParseInternalRunDeathTestFlag() has performed all the necessary + // processing. + set_write_fd(flag->write_fd()); + return EXECUTE_TEST; + } + + // WindowsDeathTest uses an anonymous pipe to communicate results of + // a death test. + SECURITY_ATTRIBUTES handles_are_inheritable = { + sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; + HANDLE read_handle, write_handle; + GTEST_DEATH_TEST_CHECK_( + ::CreatePipe(&read_handle, &write_handle, &handles_are_inheritable, + 0) // Default buffer size. + != FALSE); + set_read_fd(::_open_osfhandle(reinterpret_cast(read_handle), + O_RDONLY)); + write_handle_.Reset(write_handle); + event_handle_.Reset(::CreateEvent( + &handles_are_inheritable, + TRUE, // The event will automatically reset to non-signaled state. + FALSE, // The initial state is non-signalled. + NULL)); // The even is unnamed. + GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != NULL); + const std::string filter_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" + + info->test_case_name() + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + + "=" + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(static_cast(::GetCurrentProcessId())) + + // size_t has the same width as pointers on both 32-bit and 64-bit + // Windows platforms. + // See http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx. + "|" + StreamableToString(reinterpret_cast(write_handle)) + + "|" + StreamableToString(reinterpret_cast(event_handle_.Get())); + + char executable_path[_MAX_PATH + 1]; // NOLINT + GTEST_DEATH_TEST_CHECK_( + _MAX_PATH + 1 != ::GetModuleFileNameA(NULL, + executable_path, + _MAX_PATH)); + + std::string command_line = + std::string(::GetCommandLineA()) + " " + filter_flag + " \"" + + internal_flag + "\""; + + DeathTest::set_last_death_test_message(""); + + CaptureStderr(); + // Flush the log buffers since the log streams are shared with the child. + FlushInfoLog(); + + // The child process will share the standard handles with the parent. + STARTUPINFOA startup_info; + memset(&startup_info, 0, sizeof(STARTUPINFO)); + startup_info.dwFlags = STARTF_USESTDHANDLES; + startup_info.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE); + startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); + startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); + + PROCESS_INFORMATION process_info; + GTEST_DEATH_TEST_CHECK_(::CreateProcessA( + executable_path, + const_cast(command_line.c_str()), + NULL, // Retuned process handle is not inheritable. + NULL, // Retuned thread handle is not inheritable. + TRUE, // Child inherits all inheritable handles (for write_handle_). + 0x0, // Default creation flags. + NULL, // Inherit the parent's environment. + UnitTest::GetInstance()->original_working_dir(), + &startup_info, + &process_info) != FALSE); + child_handle_.Reset(process_info.hProcess); + ::CloseHandle(process_info.hThread); + set_spawned(true); + return OVERSEE_TEST; +} +# else // We are not on Windows. + +// ForkingDeathTest provides implementations for most of the abstract +// methods of the DeathTest interface. Only the AssumeRole method is +// left undefined. +class ForkingDeathTest : public DeathTestImpl { + public: + ForkingDeathTest(const char* statement, const RE* regex); + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + + protected: + void set_child_pid(pid_t child_pid) { child_pid_ = child_pid; } + + private: + // PID of child process during death test; 0 in the child process itself. + pid_t child_pid_; +}; + +// Constructs a ForkingDeathTest. +ForkingDeathTest::ForkingDeathTest(const char* a_statement, const RE* a_regex) + : DeathTestImpl(a_statement, a_regex), + child_pid_(-1) {} + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int ForkingDeathTest::Wait() { + if (!spawned()) + return 0; + + ReadAndInterpretStatusByte(); + + int status_value; + GTEST_DEATH_TEST_CHECK_SYSCALL_(waitpid(child_pid_, &status_value, 0)); + set_status(status_value); + return status_value; +} + +// A concrete death test class that forks, then immediately runs the test +// in the child process. +class NoExecDeathTest : public ForkingDeathTest { + public: + NoExecDeathTest(const char* a_statement, const RE* a_regex) : + ForkingDeathTest(a_statement, a_regex) { } + virtual TestRole AssumeRole(); +}; + +// The AssumeRole process for a fork-and-run death test. It implements a +// straightforward fork, with a simple pipe to transmit the status byte. +DeathTest::TestRole NoExecDeathTest::AssumeRole() { + const size_t thread_count = GetThreadCount(); + if (thread_count != 1) { + GTEST_LOG_(WARNING) << DeathTestThreadWarning(thread_count); + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); + + DeathTest::set_last_death_test_message(""); + CaptureStderr(); + // When we fork the process below, the log file buffers are copied, but the + // file descriptors are shared. We flush all log files here so that closing + // the file descriptors in the child process doesn't throw off the + // synchronization between descriptors and buffers in the parent process. + // This is as close to the fork as possible to avoid a race condition in case + // there are multiple threads running before the death test, and another + // thread writes to the log file. + FlushInfoLog(); + + const pid_t child_pid = fork(); + GTEST_DEATH_TEST_CHECK_(child_pid != -1); + set_child_pid(child_pid); + if (child_pid == 0) { + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[0])); + set_write_fd(pipe_fd[1]); + // Redirects all logging to stderr in the child process to prevent + // concurrent writes to the log files. We capture stderr in the parent + // process and append the child process' output to a log. + LogToStderr(); + // Event forwarding to the listeners of event listener API mush be shut + // down in death test subprocesses. + GetUnitTestImpl()->listeners()->SuppressEventForwarding(); + g_in_fast_death_test_child = true; + return EXECUTE_TEST; + } else { + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); + set_read_fd(pipe_fd[0]); + set_spawned(true); + return OVERSEE_TEST; + } +} + +// A concrete death test class that forks and re-executes the main +// program from the beginning, with command-line flags set that cause +// only this specific death test to be run. +class ExecDeathTest : public ForkingDeathTest { + public: + ExecDeathTest(const char* a_statement, const RE* a_regex, + const char* file, int line) : + ForkingDeathTest(a_statement, a_regex), file_(file), line_(line) { } + virtual TestRole AssumeRole(); + private: + static ::std::vector + GetArgvsForDeathTestChildProcess() { + ::std::vector args = GetInjectableArgvs(); + return args; + } + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; +}; + +// Utility class for accumulating command-line arguments. +class Arguments { + public: + Arguments() { + args_.push_back(NULL); + } + + ~Arguments() { + for (std::vector::iterator i = args_.begin(); i != args_.end(); + ++i) { + free(*i); + } + } + void AddArgument(const char* argument) { + args_.insert(args_.end() - 1, posix::StrDup(argument)); + } + + template + void AddArguments(const ::std::vector& arguments) { + for (typename ::std::vector::const_iterator i = arguments.begin(); + i != arguments.end(); + ++i) { + args_.insert(args_.end() - 1, posix::StrDup(i->c_str())); + } + } + char* const* Argv() { + return &args_[0]; + } + + private: + std::vector args_; +}; + +// A struct that encompasses the arguments to the child process of a +// threadsafe-style death test process. +struct ExecDeathTestArgs { + char* const* argv; // Command-line arguments for the child's call to exec + int close_fd; // File descriptor to close; the read end of a pipe +}; + +# if GTEST_OS_MAC +inline char** GetEnviron() { + // When Google Test is built as a framework on MacOS X, the environ variable + // is unavailable. Apple's documentation (man environ) recommends using + // _NSGetEnviron() instead. + return *_NSGetEnviron(); +} +# else +// Some POSIX platforms expect you to declare environ. extern "C" makes +// it reside in the global namespace. +extern "C" char** environ; +inline char** GetEnviron() { return environ; } +# endif // GTEST_OS_MAC + +# if !GTEST_OS_QNX +// The main function for a threadsafe-style death test child process. +// This function is called in a clone()-ed process and thus must avoid +// any potentially unsafe operations like malloc or libc functions. +static int ExecDeathTestChildMain(void* child_arg) { + ExecDeathTestArgs* const args = static_cast(child_arg); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(args->close_fd)); + + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; + } + + // We can safely call execve() as it's a direct system call. We + // cannot use execvp() as it's a libc function and thus potentially + // unsafe. Since execve() doesn't search the PATH, the user must + // invoke the test program via a valid path that contains at least + // one path separator. + execve(args->argv[0], args->argv, GetEnviron()); + DeathTestAbort(std::string("execve(") + args->argv[0] + ", ...) in " + + original_dir + " failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; +} +# endif // !GTEST_OS_QNX + +// Two utility routines that together determine the direction the stack +// grows. +// This could be accomplished more elegantly by a single recursive +// function, but we want to guard against the unlikely possibility of +// a smart compiler optimizing the recursion away. +// +// GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining +// StackLowerThanAddress into StackGrowsDown, which then doesn't give +// correct answer. +void StackLowerThanAddress(const void* ptr, bool* result) GTEST_NO_INLINE_; +void StackLowerThanAddress(const void* ptr, bool* result) { + int dummy; + *result = (&dummy < ptr); +} + +bool StackGrowsDown() { + int dummy; + bool result; + StackLowerThanAddress(&dummy, &result); + return result; +} + +// Spawns a child process with the same executable as the current process in +// a thread-safe manner and instructs it to run the death test. The +// implementation uses fork(2) + exec. On systems where clone(2) is +// available, it is used instead, being slightly more thread-safe. On QNX, +// fork supports only single-threaded environments, so this function uses +// spawn(2) there instead. The function dies with an error message if +// anything goes wrong. +static pid_t ExecDeathTestSpawnChild(char* const* argv, int close_fd) { + ExecDeathTestArgs args = { argv, close_fd }; + pid_t child_pid = -1; + +# if GTEST_OS_QNX + // Obtains the current directory and sets it to be closed in the child + // process. + const int cwd_fd = open(".", O_RDONLY); + GTEST_DEATH_TEST_CHECK_(cwd_fd != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(cwd_fd, F_SETFD, FD_CLOEXEC)); + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; + } + + int fd_flags; + // Set close_fd to be closed after spawn. + GTEST_DEATH_TEST_CHECK_SYSCALL_(fd_flags = fcntl(close_fd, F_GETFD)); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(close_fd, F_SETFD, + fd_flags | FD_CLOEXEC)); + struct inheritance inherit = {0}; + // spawn is a system call. + child_pid = spawn(args.argv[0], 0, NULL, &inherit, args.argv, GetEnviron()); + // Restores the current working directory. + GTEST_DEATH_TEST_CHECK_(fchdir(cwd_fd) != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(cwd_fd)); + +# else // GTEST_OS_QNX +# if GTEST_OS_LINUX + // When a SIGPROF signal is received while fork() or clone() are executing, + // the process may hang. To avoid this, we ignore SIGPROF here and re-enable + // it after the call to fork()/clone() is complete. + struct sigaction saved_sigprof_action; + struct sigaction ignore_sigprof_action; + memset(&ignore_sigprof_action, 0, sizeof(ignore_sigprof_action)); + sigemptyset(&ignore_sigprof_action.sa_mask); + ignore_sigprof_action.sa_handler = SIG_IGN; + GTEST_DEATH_TEST_CHECK_SYSCALL_(sigaction( + SIGPROF, &ignore_sigprof_action, &saved_sigprof_action)); +# endif // GTEST_OS_LINUX + +# if GTEST_HAS_CLONE + const bool use_fork = GTEST_FLAG(death_test_use_fork); + + if (!use_fork) { + static const bool stack_grows_down = StackGrowsDown(); + const size_t stack_size = getpagesize(); + // MMAP_ANONYMOUS is not defined on Mac, so we use MAP_ANON instead. + void* const stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + GTEST_DEATH_TEST_CHECK_(stack != MAP_FAILED); + + // Maximum stack alignment in bytes: For a downward-growing stack, this + // amount is subtracted from size of the stack space to get an address + // that is within the stack space and is aligned on all systems we care + // about. As far as I know there is no ABI with stack alignment greater + // than 64. We assume stack and stack_size already have alignment of + // kMaxStackAlignment. + const size_t kMaxStackAlignment = 64; + void* const stack_top = + static_cast(stack) + + (stack_grows_down ? stack_size - kMaxStackAlignment : 0); + GTEST_DEATH_TEST_CHECK_(stack_size > kMaxStackAlignment && + reinterpret_cast(stack_top) % kMaxStackAlignment == 0); + + child_pid = clone(&ExecDeathTestChildMain, stack_top, SIGCHLD, &args); + + GTEST_DEATH_TEST_CHECK_(munmap(stack, stack_size) != -1); + } +# else + const bool use_fork = true; +# endif // GTEST_HAS_CLONE + + if (use_fork && (child_pid = fork()) == 0) { + ExecDeathTestChildMain(&args); + _exit(0); + } +# endif // GTEST_OS_QNX +# if GTEST_OS_LINUX + GTEST_DEATH_TEST_CHECK_SYSCALL_( + sigaction(SIGPROF, &saved_sigprof_action, NULL)); +# endif // GTEST_OS_LINUX + + GTEST_DEATH_TEST_CHECK_(child_pid != -1); + return child_pid; +} + +// The AssumeRole process for a fork-and-exec death test. It re-executes the +// main program from the beginning, setting the --gtest_filter +// and --gtest_internal_run_death_test flags to cause only the current +// death test to be re-run. +DeathTest::TestRole ExecDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != NULL) { + set_write_fd(flag->write_fd()); + return EXECUTE_TEST; + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); + // Clear the close-on-exec flag on the write end of the pipe, lest + // it be closed when the child process does an exec: + GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1); + + const std::string filter_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" + + info->test_case_name() + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "=" + + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(pipe_fd[1]); + Arguments args; + args.AddArguments(GetArgvsForDeathTestChildProcess()); + args.AddArgument(filter_flag.c_str()); + args.AddArgument(internal_flag.c_str()); + + DeathTest::set_last_death_test_message(""); + + CaptureStderr(); + // See the comment in NoExecDeathTest::AssumeRole for why the next line + // is necessary. + FlushInfoLog(); + + const pid_t child_pid = ExecDeathTestSpawnChild(args.Argv(), pipe_fd[0]); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); + set_child_pid(child_pid); + set_read_fd(pipe_fd[0]); + set_spawned(true); + return OVERSEE_TEST; +} + +# endif // !GTEST_OS_WINDOWS + +// Creates a concrete DeathTest-derived class that depends on the +// --gtest_death_test_style flag, and sets the pointer pointed to +// by the "test" argument to its address. If the test should be +// skipped, sets that pointer to NULL. Returns true, unless the +// flag is set to an invalid value. +bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, + const char* file, int line, + DeathTest** test) { + UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const int death_test_index = impl->current_test_info() + ->increment_death_test_count(); + + if (flag != NULL) { + if (death_test_index > flag->index()) { + DeathTest::set_last_death_test_message( + "Death test count (" + StreamableToString(death_test_index) + + ") somehow exceeded expected maximum (" + + StreamableToString(flag->index()) + ")"); + return false; + } + + if (!(flag->file() == file && flag->line() == line && + flag->index() == death_test_index)) { + *test = NULL; + return true; + } + } + +# if GTEST_OS_WINDOWS + + if (GTEST_FLAG(death_test_style) == "threadsafe" || + GTEST_FLAG(death_test_style) == "fast") { + *test = new WindowsDeathTest(statement, regex, file, line); + } + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") { + *test = new ExecDeathTest(statement, regex, file, line); + } else if (GTEST_FLAG(death_test_style) == "fast") { + *test = new NoExecDeathTest(statement, regex); + } + +# endif // GTEST_OS_WINDOWS + + else { // NOLINT - this is more readable than unbalanced brackets inside #if. + DeathTest::set_last_death_test_message( + "Unknown death test style \"" + GTEST_FLAG(death_test_style) + + "\" encountered"); + return false; + } + + return true; +} + +// Splits a given string on a given delimiter, populating a given +// vector with the fields. GTEST_HAS_DEATH_TEST implies that we have +// ::std::string, so we can use it here. +static void SplitString(const ::std::string& str, char delimiter, + ::std::vector< ::std::string>* dest) { + ::std::vector< ::std::string> parsed; + ::std::string::size_type pos = 0; + while (::testing::internal::AlwaysTrue()) { + const ::std::string::size_type colon = str.find(delimiter, pos); + if (colon == ::std::string::npos) { + parsed.push_back(str.substr(pos)); + break; + } else { + parsed.push_back(str.substr(pos, colon - pos)); + pos = colon + 1; + } + } + dest->swap(parsed); +} + +# if GTEST_OS_WINDOWS +// Recreates the pipe and event handles from the provided parameters, +// signals the event, and returns a file descriptor wrapped around the pipe +// handle. This function is called in the child process only. +int GetStatusFileDescriptor(unsigned int parent_process_id, + size_t write_handle_as_size_t, + size_t event_handle_as_size_t) { + AutoHandle parent_process_handle(::OpenProcess(PROCESS_DUP_HANDLE, + FALSE, // Non-inheritable. + parent_process_id)); + if (parent_process_handle.Get() == INVALID_HANDLE_VALUE) { + DeathTestAbort("Unable to open parent process " + + StreamableToString(parent_process_id)); + } + + // TODO(vladl@google.com): Replace the following check with a + // compile-time assertion when available. + GTEST_CHECK_(sizeof(HANDLE) <= sizeof(size_t)); + + const HANDLE write_handle = + reinterpret_cast(write_handle_as_size_t); + HANDLE dup_write_handle; + + // The newly initialized handle is accessible only in in the parent + // process. To obtain one accessible within the child, we need to use + // DuplicateHandle. + if (!::DuplicateHandle(parent_process_handle.Get(), write_handle, + ::GetCurrentProcess(), &dup_write_handle, + 0x0, // Requested privileges ignored since + // DUPLICATE_SAME_ACCESS is used. + FALSE, // Request non-inheritable handler. + DUPLICATE_SAME_ACCESS)) { + DeathTestAbort("Unable to duplicate the pipe handle " + + StreamableToString(write_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); + } + + const HANDLE event_handle = reinterpret_cast(event_handle_as_size_t); + HANDLE dup_event_handle; + + if (!::DuplicateHandle(parent_process_handle.Get(), event_handle, + ::GetCurrentProcess(), &dup_event_handle, + 0x0, + FALSE, + DUPLICATE_SAME_ACCESS)) { + DeathTestAbort("Unable to duplicate the event handle " + + StreamableToString(event_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); + } + + const int write_fd = + ::_open_osfhandle(reinterpret_cast(dup_write_handle), O_APPEND); + if (write_fd == -1) { + DeathTestAbort("Unable to convert pipe handle " + + StreamableToString(write_handle_as_size_t) + + " to a file descriptor"); + } + + // Signals the parent that the write end of the pipe has been acquired + // so the parent can release its own write end. + ::SetEvent(dup_event_handle); + + return write_fd; +} +# endif // GTEST_OS_WINDOWS + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { + if (GTEST_FLAG(internal_run_death_test) == "") return NULL; + + // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we + // can use it here. + int line = -1; + int index = -1; + ::std::vector< ::std::string> fields; + SplitString(GTEST_FLAG(internal_run_death_test).c_str(), '|', &fields); + int write_fd = -1; + +# if GTEST_OS_WINDOWS + + unsigned int parent_process_id = 0; + size_t write_handle_as_size_t = 0; + size_t event_handle_as_size_t = 0; + + if (fields.size() != 6 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index) + || !ParseNaturalNumber(fields[3], &parent_process_id) + || !ParseNaturalNumber(fields[4], &write_handle_as_size_t) + || !ParseNaturalNumber(fields[5], &event_handle_as_size_t)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + write_fd = GetStatusFileDescriptor(parent_process_id, + write_handle_as_size_t, + event_handle_as_size_t); +# else + + if (fields.size() != 4 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index) + || !ParseNaturalNumber(fields[3], &write_fd)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + +# endif // GTEST_OS_WINDOWS + + return new InternalRunDeathTestFlag(fields[0], line, index, write_fd); +} + +} // namespace internal + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace testing diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-filepath.cc b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-filepath.cc new file mode 100644 index 00000000..6be58b6f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-filepath.cc @@ -0,0 +1,382 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: keith.ray@gmail.com (Keith Ray) + +#include "gtest/gtest-message.h" +#include "gtest/internal/gtest-filepath.h" +#include "gtest/internal/gtest-port.h" + +#include + +#if GTEST_OS_WINDOWS_MOBILE +# include +#elif GTEST_OS_WINDOWS +# include +# include +#elif GTEST_OS_SYMBIAN +// Symbian OpenC has PATH_MAX in sys/syslimits.h +# include +#else +# include +# include // Some Linux distributions define PATH_MAX here. +#endif // GTEST_OS_WINDOWS_MOBILE + +#if GTEST_OS_WINDOWS +# define GTEST_PATH_MAX_ _MAX_PATH +#elif defined(PATH_MAX) +# define GTEST_PATH_MAX_ PATH_MAX +#elif defined(_XOPEN_PATH_MAX) +# define GTEST_PATH_MAX_ _XOPEN_PATH_MAX +#else +# define GTEST_PATH_MAX_ _POSIX_PATH_MAX +#endif // GTEST_OS_WINDOWS + +#include "gtest/internal/gtest-string.h" + +namespace testing { +namespace internal { + +#if GTEST_OS_WINDOWS +// On Windows, '\\' is the standard path separator, but many tools and the +// Windows API also accept '/' as an alternate path separator. Unless otherwise +// noted, a file path can contain either kind of path separators, or a mixture +// of them. +const char kPathSeparator = '\\'; +const char kAlternatePathSeparator = '/'; +const char kPathSeparatorString[] = "\\"; +const char kAlternatePathSeparatorString[] = "/"; +# if GTEST_OS_WINDOWS_MOBILE +// Windows CE doesn't have a current directory. You should not use +// the current directory in tests on Windows CE, but this at least +// provides a reasonable fallback. +const char kCurrentDirectoryString[] = "\\"; +// Windows CE doesn't define INVALID_FILE_ATTRIBUTES +const DWORD kInvalidFileAttributes = 0xffffffff; +# else +const char kCurrentDirectoryString[] = ".\\"; +# endif // GTEST_OS_WINDOWS_MOBILE +#else +const char kPathSeparator = '/'; +const char kPathSeparatorString[] = "/"; +const char kCurrentDirectoryString[] = "./"; +#endif // GTEST_OS_WINDOWS + +// Returns whether the given character is a valid path separator. +static bool IsPathSeparator(char c) { +#if GTEST_HAS_ALT_PATH_SEP_ + return (c == kPathSeparator) || (c == kAlternatePathSeparator); +#else + return c == kPathSeparator; +#endif +} + +// Returns the current working directory, or "" if unsuccessful. +FilePath FilePath::GetCurrentDir() { +#if GTEST_OS_WINDOWS_MOBILE + // Windows CE doesn't have a current directory, so we just return + // something reasonable. + return FilePath(kCurrentDirectoryString); +#elif GTEST_OS_WINDOWS + char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); +#else + char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Returns a copy of the FilePath with the case-insensitive extension removed. +// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns +// FilePath("dir/file"). If a case-insensitive extension is not +// found, returns a copy of the original FilePath. +FilePath FilePath::RemoveExtension(const char* extension) const { + const std::string dot_extension = std::string(".") + extension; + if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) { + return FilePath(pathname_.substr( + 0, pathname_.length() - dot_extension.length())); + } + return *this; +} + +// Returns a pointer to the last occurence of a valid path separator in +// the FilePath. On Windows, for example, both '/' and '\' are valid path +// separators. Returns NULL if no path separator was found. +const char* FilePath::FindLastPathSeparator() const { + const char* const last_sep = strrchr(c_str(), kPathSeparator); +#if GTEST_HAS_ALT_PATH_SEP_ + const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator); + // Comparing two pointers of which only one is NULL is undefined. + if (last_alt_sep != NULL && + (last_sep == NULL || last_alt_sep > last_sep)) { + return last_alt_sep; + } +#endif + return last_sep; +} + +// Returns a copy of the FilePath with the directory part removed. +// Example: FilePath("path/to/file").RemoveDirectoryName() returns +// FilePath("file"). If there is no directory part ("just_a_file"), it returns +// the FilePath unmodified. If there is no file part ("just_a_dir/") it +// returns an empty FilePath (""). +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveDirectoryName() const { + const char* const last_sep = FindLastPathSeparator(); + return last_sep ? FilePath(last_sep + 1) : *this; +} + +// RemoveFileName returns the directory path with the filename removed. +// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". +// If the FilePath is "a_file" or "/a_file", RemoveFileName returns +// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does +// not have a file, like "just/a/dir/", it returns the FilePath unmodified. +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveFileName() const { + const char* const last_sep = FindLastPathSeparator(); + std::string dir; + if (last_sep) { + dir = std::string(c_str(), last_sep + 1 - c_str()); + } else { + dir = kCurrentDirectoryString; + } + return FilePath(dir); +} + +// Helper functions for naming files in a directory for xml output. + +// Given directory = "dir", base_name = "test", number = 0, +// extension = "xml", returns "dir/test.xml". If number is greater +// than zero (e.g., 12), returns "dir/test_12.xml". +// On Windows platform, uses \ as the separator rather than /. +FilePath FilePath::MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension) { + std::string file; + if (number == 0) { + file = base_name.string() + "." + extension; + } else { + file = base_name.string() + "_" + StreamableToString(number) + + "." + extension; + } + return ConcatPaths(directory, FilePath(file)); +} + +// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml". +// On Windows, uses \ as the separator rather than /. +FilePath FilePath::ConcatPaths(const FilePath& directory, + const FilePath& relative_path) { + if (directory.IsEmpty()) + return relative_path; + const FilePath dir(directory.RemoveTrailingPathSeparator()); + return FilePath(dir.string() + kPathSeparator + relative_path.string()); +} + +// Returns true if pathname describes something findable in the file-system, +// either a file, directory, or whatever. +bool FilePath::FileOrDirectoryExists() const { +#if GTEST_OS_WINDOWS_MOBILE + LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str()); + const DWORD attributes = GetFileAttributes(unicode); + delete [] unicode; + return attributes != kInvalidFileAttributes; +#else + posix::StatStruct file_stat; + return posix::Stat(pathname_.c_str(), &file_stat) == 0; +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Returns true if pathname describes a directory in the file-system +// that exists. +bool FilePath::DirectoryExists() const { + bool result = false; +#if GTEST_OS_WINDOWS + // Don't strip off trailing separator if path is a root directory on + // Windows (like "C:\\"). + const FilePath& path(IsRootDirectory() ? *this : + RemoveTrailingPathSeparator()); +#else + const FilePath& path(*this); +#endif + +#if GTEST_OS_WINDOWS_MOBILE + LPCWSTR unicode = String::AnsiToUtf16(path.c_str()); + const DWORD attributes = GetFileAttributes(unicode); + delete [] unicode; + if ((attributes != kInvalidFileAttributes) && + (attributes & FILE_ATTRIBUTE_DIRECTORY)) { + result = true; + } +#else + posix::StatStruct file_stat; + result = posix::Stat(path.c_str(), &file_stat) == 0 && + posix::IsDir(file_stat); +#endif // GTEST_OS_WINDOWS_MOBILE + + return result; +} + +// Returns true if pathname describes a root directory. (Windows has one +// root directory per disk drive.) +bool FilePath::IsRootDirectory() const { +#if GTEST_OS_WINDOWS + // TODO(wan@google.com): on Windows a network share like + // \\server\share can be a root directory, although it cannot be the + // current directory. Handle this properly. + return pathname_.length() == 3 && IsAbsolutePath(); +#else + return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]); +#endif +} + +// Returns true if pathname describes an absolute path. +bool FilePath::IsAbsolutePath() const { + const char* const name = pathname_.c_str(); +#if GTEST_OS_WINDOWS + return pathname_.length() >= 3 && + ((name[0] >= 'a' && name[0] <= 'z') || + (name[0] >= 'A' && name[0] <= 'Z')) && + name[1] == ':' && + IsPathSeparator(name[2]); +#else + return IsPathSeparator(name[0]); +#endif +} + +// Returns a pathname for a file that does not currently exist. The pathname +// will be directory/base_name.extension or +// directory/base_name_.extension if directory/base_name.extension +// already exists. The number will be incremented until a pathname is found +// that does not already exist. +// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. +// There could be a race condition if two or more processes are calling this +// function at the same time -- they could both pick the same filename. +FilePath FilePath::GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension) { + FilePath full_pathname; + int number = 0; + do { + full_pathname.Set(MakeFileName(directory, base_name, number++, extension)); + } while (full_pathname.FileOrDirectoryExists()); + return full_pathname; +} + +// Returns true if FilePath ends with a path separator, which indicates that +// it is intended to represent a directory. Returns false otherwise. +// This does NOT check that a directory (or file) actually exists. +bool FilePath::IsDirectory() const { + return !pathname_.empty() && + IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]); +} + +// Create directories so that path exists. Returns true if successful or if +// the directories already exist; returns false if unable to create directories +// for any reason. +bool FilePath::CreateDirectoriesRecursively() const { + if (!this->IsDirectory()) { + return false; + } + + if (pathname_.length() == 0 || this->DirectoryExists()) { + return true; + } + + const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName()); + return parent.CreateDirectoriesRecursively() && this->CreateFolder(); +} + +// Create the directory so that path exists. Returns true if successful or +// if the directory already exists; returns false if unable to create the +// directory for any reason, including if the parent directory does not +// exist. Not named "CreateDirectory" because that's a macro on Windows. +bool FilePath::CreateFolder() const { +#if GTEST_OS_WINDOWS_MOBILE + FilePath removed_sep(this->RemoveTrailingPathSeparator()); + LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str()); + int result = CreateDirectory(unicode, NULL) ? 0 : -1; + delete [] unicode; +#elif GTEST_OS_WINDOWS + int result = _mkdir(pathname_.c_str()); +#else + int result = mkdir(pathname_.c_str(), 0777); +#endif // GTEST_OS_WINDOWS_MOBILE + + if (result == -1) { + return this->DirectoryExists(); // An error is OK if the directory exists. + } + return true; // No error. +} + +// If input name has a trailing separator character, remove it and return the +// name, otherwise return the name string unmodified. +// On Windows platform, uses \ as the separator, other platforms use /. +FilePath FilePath::RemoveTrailingPathSeparator() const { + return IsDirectory() + ? FilePath(pathname_.substr(0, pathname_.length() - 1)) + : *this; +} + +// Removes any redundant separators that might be in the pathname. +// For example, "bar///foo" becomes "bar/foo". Does not eliminate other +// redundancies that might be in a pathname involving "." or "..". +// TODO(wan@google.com): handle Windows network shares (e.g. \\server\share). +void FilePath::Normalize() { + if (pathname_.c_str() == NULL) { + pathname_ = ""; + return; + } + const char* src = pathname_.c_str(); + char* const dest = new char[pathname_.length() + 1]; + char* dest_ptr = dest; + memset(dest_ptr, 0, pathname_.length() + 1); + + while (*src != '\0') { + *dest_ptr = *src; + if (!IsPathSeparator(*src)) { + src++; + } else { +#if GTEST_HAS_ALT_PATH_SEP_ + if (*dest_ptr == kAlternatePathSeparator) { + *dest_ptr = kPathSeparator; + } +#endif + while (IsPathSeparator(*src)) + src++; + } + dest_ptr++; + } + *dest_ptr = '\0'; + pathname_ = dest; + delete[] dest; +} + +} // namespace internal +} // namespace testing diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-internal-inl.h b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-internal-inl.h new file mode 100644 index 00000000..35df303c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-internal-inl.h @@ -0,0 +1,1218 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Utility functions and classes used by the Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) +// +// This file contains purely Google Test's internal implementation. Please +// DO NOT #INCLUDE IT IN A USER PROGRAM. + +#ifndef GTEST_SRC_GTEST_INTERNAL_INL_H_ +#define GTEST_SRC_GTEST_INTERNAL_INL_H_ + +// GTEST_IMPLEMENTATION_ is defined to 1 iff the current translation unit is +// part of Google Test's implementation; otherwise it's undefined. +#if !GTEST_IMPLEMENTATION_ +// A user is trying to include this from his code - just say no. +# error "gtest-internal-inl.h is part of Google Test's internal implementation." +# error "It must not be included except by Google Test itself." +#endif // GTEST_IMPLEMENTATION_ + +#ifndef _WIN32_WCE +# include +#endif // !_WIN32_WCE +#include +#include // For strtoll/_strtoul64/malloc/free. +#include // For memmove. + +#include +#include +#include + +#include "gtest/internal/gtest-port.h" + +#if GTEST_CAN_STREAM_RESULTS_ +# include // NOLINT +# include // NOLINT +#endif + +#if GTEST_OS_WINDOWS +# include // NOLINT +#endif // GTEST_OS_WINDOWS + +#include "gtest/gtest.h" // NOLINT +#include "gtest/gtest-spi.h" + +namespace testing { + +// Declares the flags. +// +// We don't want the users to modify this flag in the code, but want +// Google Test's own unit tests to be able to access it. Therefore we +// declare it here as opposed to in gtest.h. +GTEST_DECLARE_bool_(death_test_use_fork); + +namespace internal { + +// The value of GetTestTypeId() as seen from within the Google Test +// library. This is solely for testing GetTestTypeId(). +GTEST_API_ extern const TypeId kTestTypeIdInGoogleTest; + +// Names of the flags (needed for parsing Google Test flags). +const char kAlsoRunDisabledTestsFlag[] = "also_run_disabled_tests"; +const char kBreakOnFailureFlag[] = "break_on_failure"; +const char kCatchExceptionsFlag[] = "catch_exceptions"; +const char kColorFlag[] = "color"; +const char kFilterFlag[] = "filter"; +const char kListTestsFlag[] = "list_tests"; +const char kOutputFlag[] = "output"; +const char kPrintTimeFlag[] = "print_time"; +const char kRandomSeedFlag[] = "random_seed"; +const char kRepeatFlag[] = "repeat"; +const char kShuffleFlag[] = "shuffle"; +const char kStackTraceDepthFlag[] = "stack_trace_depth"; +const char kStreamResultToFlag[] = "stream_result_to"; +const char kThrowOnFailureFlag[] = "throw_on_failure"; + +// A valid random seed must be in [1, kMaxRandomSeed]. +const int kMaxRandomSeed = 99999; + +// g_help_flag is true iff the --help flag or an equivalent form is +// specified on the command line. +GTEST_API_ extern bool g_help_flag; + +// Returns the current time in milliseconds. +GTEST_API_ TimeInMillis GetTimeInMillis(); + +// Returns true iff Google Test should use colors in the output. +GTEST_API_ bool ShouldUseColor(bool stdout_is_tty); + +// Formats the given time in milliseconds as seconds. +GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms); + +// Converts the given time in milliseconds to a date string in the ISO 8601 +// format, without the timezone information. N.B.: due to the use the +// non-reentrant localtime() function, this function is not thread safe. Do +// not use it in any code that can be called from multiple threads. +GTEST_API_ std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms); + +// Parses a string for an Int32 flag, in the form of "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +GTEST_API_ bool ParseInt32Flag( + const char* str, const char* flag, Int32* value); + +// Returns a random seed in range [1, kMaxRandomSeed] based on the +// given --gtest_random_seed flag value. +inline int GetRandomSeedFromFlag(Int32 random_seed_flag) { + const unsigned int raw_seed = (random_seed_flag == 0) ? + static_cast(GetTimeInMillis()) : + static_cast(random_seed_flag); + + // Normalizes the actual seed to range [1, kMaxRandomSeed] such that + // it's easy to type. + const int normalized_seed = + static_cast((raw_seed - 1U) % + static_cast(kMaxRandomSeed)) + 1; + return normalized_seed; +} + +// Returns the first valid random seed after 'seed'. The behavior is +// undefined if 'seed' is invalid. The seed after kMaxRandomSeed is +// considered to be 1. +inline int GetNextRandomSeed(int seed) { + GTEST_CHECK_(1 <= seed && seed <= kMaxRandomSeed) + << "Invalid random seed " << seed << " - must be in [1, " + << kMaxRandomSeed << "]."; + const int next_seed = seed + 1; + return (next_seed > kMaxRandomSeed) ? 1 : next_seed; +} + +// This class saves the values of all Google Test flags in its c'tor, and +// restores them in its d'tor. +class GTestFlagSaver { + public: + // The c'tor. + GTestFlagSaver() { + also_run_disabled_tests_ = GTEST_FLAG(also_run_disabled_tests); + break_on_failure_ = GTEST_FLAG(break_on_failure); + catch_exceptions_ = GTEST_FLAG(catch_exceptions); + color_ = GTEST_FLAG(color); + death_test_style_ = GTEST_FLAG(death_test_style); + death_test_use_fork_ = GTEST_FLAG(death_test_use_fork); + filter_ = GTEST_FLAG(filter); + internal_run_death_test_ = GTEST_FLAG(internal_run_death_test); + list_tests_ = GTEST_FLAG(list_tests); + output_ = GTEST_FLAG(output); + print_time_ = GTEST_FLAG(print_time); + random_seed_ = GTEST_FLAG(random_seed); + repeat_ = GTEST_FLAG(repeat); + shuffle_ = GTEST_FLAG(shuffle); + stack_trace_depth_ = GTEST_FLAG(stack_trace_depth); + stream_result_to_ = GTEST_FLAG(stream_result_to); + throw_on_failure_ = GTEST_FLAG(throw_on_failure); + } + + // The d'tor is not virtual. DO NOT INHERIT FROM THIS CLASS. + ~GTestFlagSaver() { + GTEST_FLAG(also_run_disabled_tests) = also_run_disabled_tests_; + GTEST_FLAG(break_on_failure) = break_on_failure_; + GTEST_FLAG(catch_exceptions) = catch_exceptions_; + GTEST_FLAG(color) = color_; + GTEST_FLAG(death_test_style) = death_test_style_; + GTEST_FLAG(death_test_use_fork) = death_test_use_fork_; + GTEST_FLAG(filter) = filter_; + GTEST_FLAG(internal_run_death_test) = internal_run_death_test_; + GTEST_FLAG(list_tests) = list_tests_; + GTEST_FLAG(output) = output_; + GTEST_FLAG(print_time) = print_time_; + GTEST_FLAG(random_seed) = random_seed_; + GTEST_FLAG(repeat) = repeat_; + GTEST_FLAG(shuffle) = shuffle_; + GTEST_FLAG(stack_trace_depth) = stack_trace_depth_; + GTEST_FLAG(stream_result_to) = stream_result_to_; + GTEST_FLAG(throw_on_failure) = throw_on_failure_; + } + + private: + // Fields for saving the original values of flags. + bool also_run_disabled_tests_; + bool break_on_failure_; + bool catch_exceptions_; + std::string color_; + std::string death_test_style_; + bool death_test_use_fork_; + std::string filter_; + std::string internal_run_death_test_; + bool list_tests_; + std::string output_; + bool print_time_; + internal::Int32 random_seed_; + internal::Int32 repeat_; + bool shuffle_; + internal::Int32 stack_trace_depth_; + std::string stream_result_to_; + bool throw_on_failure_; +} GTEST_ATTRIBUTE_UNUSED_; + +// Converts a Unicode code point to a narrow string in UTF-8 encoding. +// code_point parameter is of type UInt32 because wchar_t may not be +// wide enough to contain a code point. +// If the code_point is not a valid Unicode code point +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +GTEST_API_ std::string CodePointToUtf8(UInt32 code_point); + +// Converts a wide string to a narrow string in UTF-8 encoding. +// The wide string is assumed to have the following encoding: +// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS) +// UTF-32 if sizeof(wchar_t) == 4 (on Linux) +// Parameter str points to a null-terminated wide string. +// Parameter num_chars may additionally limit the number +// of wchar_t characters processed. -1 is used when the entire string +// should be processed. +// If the string contains code points that are not valid Unicode code points +// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding +// and contains invalid UTF-16 surrogate pairs, values in those pairs +// will be encoded as individual Unicode characters from Basic Normal Plane. +GTEST_API_ std::string WideStringToUtf8(const wchar_t* str, int num_chars); + +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded(); + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (e.g., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +GTEST_API_ bool ShouldShard(const char* total_shards_str, + const char* shard_index_str, + bool in_subprocess_for_death_test); + +// Parses the environment variable var as an Int32. If it is unset, +// returns default_val. If it is not an Int32, prints an error and +// and aborts. +GTEST_API_ Int32 Int32FromEnvOrDie(const char* env_var, Int32 default_val); + +// Given the total number of shards, the shard index, and the test id, +// returns true iff the test should be run on this shard. The test id is +// some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +GTEST_API_ bool ShouldRunTestOnShard( + int total_shards, int shard_index, int test_id); + +// STL container utilities. + +// Returns the number of elements in the given container that satisfy +// the given predicate. +template +inline int CountIf(const Container& c, Predicate predicate) { + // Implemented as an explicit loop since std::count_if() in libCstd on + // Solaris has a non-standard signature. + int count = 0; + for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it) { + if (predicate(*it)) + ++count; + } + return count; +} + +// Applies a function/functor to each element in the container. +template +void ForEach(const Container& c, Functor functor) { + std::for_each(c.begin(), c.end(), functor); +} + +// Returns the i-th element of the vector, or default_value if i is not +// in range [0, v.size()). +template +inline E GetElementOr(const std::vector& v, int i, E default_value) { + return (i < 0 || i >= static_cast(v.size())) ? default_value : v[i]; +} + +// Performs an in-place shuffle of a range of the vector's elements. +// 'begin' and 'end' are element indices as an STL-style range; +// i.e. [begin, end) are shuffled, where 'end' == size() means to +// shuffle to the end of the vector. +template +void ShuffleRange(internal::Random* random, int begin, int end, + std::vector* v) { + const int size = static_cast(v->size()); + GTEST_CHECK_(0 <= begin && begin <= size) + << "Invalid shuffle range start " << begin << ": must be in range [0, " + << size << "]."; + GTEST_CHECK_(begin <= end && end <= size) + << "Invalid shuffle range finish " << end << ": must be in range [" + << begin << ", " << size << "]."; + + // Fisher-Yates shuffle, from + // http://en.wikipedia.org/wiki/Fisher-Yates_shuffle + for (int range_width = end - begin; range_width >= 2; range_width--) { + const int last_in_range = begin + range_width - 1; + const int selected = begin + random->Generate(range_width); + std::swap((*v)[selected], (*v)[last_in_range]); + } +} + +// Performs an in-place shuffle of the vector's elements. +template +inline void Shuffle(internal::Random* random, std::vector* v) { + ShuffleRange(random, 0, static_cast(v->size()), v); +} + +// A function for deleting an object. Handy for being used as a +// functor. +template +static void Delete(T* x) { + delete x; +} + +// A predicate that checks the key of a TestProperty against a known key. +// +// TestPropertyKeyIs is copyable. +class TestPropertyKeyIs { + public: + // Constructor. + // + // TestPropertyKeyIs has NO default constructor. + explicit TestPropertyKeyIs(const std::string& key) : key_(key) {} + + // Returns true iff the test name of test property matches on key_. + bool operator()(const TestProperty& test_property) const { + return test_property.key() == key_; + } + + private: + std::string key_; +}; + +// Class UnitTestOptions. +// +// This class contains functions for processing options the user +// specifies when running the tests. It has only static members. +// +// In most cases, the user can specify an option using either an +// environment variable or a command line flag. E.g. you can set the +// test filter using either GTEST_FILTER or --gtest_filter. If both +// the variable and the flag are present, the latter overrides the +// former. +class GTEST_API_ UnitTestOptions { + public: + // Functions for processing the gtest_output flag. + + // Returns the output format, or "" for normal printed output. + static std::string GetOutputFormat(); + + // Returns the absolute path of the requested output file, or the + // default (test_detail.xml in the original working directory) if + // none was explicitly specified. + static std::string GetAbsolutePathToOutputFile(); + + // Functions for processing the gtest_filter flag. + + // Returns true iff the wildcard pattern matches the string. The + // first ':' or '\0' character in pattern marks the end of it. + // + // This recursive algorithm isn't very efficient, but is clear and + // works well enough for matching test names, which are short. + static bool PatternMatchesString(const char *pattern, const char *str); + + // Returns true iff the user-specified filter matches the test case + // name and the test name. + static bool FilterMatchesTest(const std::string &test_case_name, + const std::string &test_name); + +#if GTEST_OS_WINDOWS + // Function for supporting the gtest_catch_exception flag. + + // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the + // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. + // This function is useful as an __except condition. + static int GTestShouldProcessSEH(DWORD exception_code); +#endif // GTEST_OS_WINDOWS + + // Returns true if "name" matches the ':' separated list of glob-style + // filters in "filter". + static bool MatchesFilter(const std::string& name, const char* filter); +}; + +// Returns the current application's name, removing directory path if that +// is present. Used by UnitTestOptions::GetOutputFile. +GTEST_API_ FilePath GetCurrentExecutableName(); + +// The role interface for getting the OS stack trace as a string. +class OsStackTraceGetterInterface { + public: + OsStackTraceGetterInterface() {} + virtual ~OsStackTraceGetterInterface() {} + + // Returns the current OS stack trace as an std::string. Parameters: + // + // max_depth - the maximum number of stack frames to be included + // in the trace. + // skip_count - the number of top frames to be skipped; doesn't count + // against max_depth. + virtual string CurrentStackTrace(int max_depth, int skip_count) = 0; + + // UponLeavingGTest() should be called immediately before Google Test calls + // user code. It saves some information about the current stack that + // CurrentStackTrace() will use to find and hide Google Test stack frames. + virtual void UponLeavingGTest() = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetterInterface); +}; + +// A working implementation of the OsStackTraceGetterInterface interface. +class OsStackTraceGetter : public OsStackTraceGetterInterface { + public: + OsStackTraceGetter() : caller_frame_(NULL) {} + + virtual string CurrentStackTrace(int max_depth, int skip_count) + GTEST_LOCK_EXCLUDED_(mutex_); + + virtual void UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_); + + // This string is inserted in place of stack frames that are part of + // Google Test's implementation. + static const char* const kElidedFramesMarker; + + private: + Mutex mutex_; // protects all internal state + + // We save the stack frame below the frame that calls user code. + // We do this because the address of the frame immediately below + // the user code changes between the call to UponLeavingGTest() + // and any calls to CurrentStackTrace() from within the user code. + void* caller_frame_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter); +}; + +// Information about a Google Test trace point. +struct TraceInfo { + const char* file; + int line; + std::string message; +}; + +// This is the default global test part result reporter used in UnitTestImpl. +// This class should only be used by UnitTestImpl. +class DefaultGlobalTestPartResultReporter + : public TestPartResultReporterInterface { + public: + explicit DefaultGlobalTestPartResultReporter(UnitTestImpl* unit_test); + // Implements the TestPartResultReporterInterface. Reports the test part + // result in the current test. + virtual void ReportTestPartResult(const TestPartResult& result); + + private: + UnitTestImpl* const unit_test_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultGlobalTestPartResultReporter); +}; + +// This is the default per thread test part result reporter used in +// UnitTestImpl. This class should only be used by UnitTestImpl. +class DefaultPerThreadTestPartResultReporter + : public TestPartResultReporterInterface { + public: + explicit DefaultPerThreadTestPartResultReporter(UnitTestImpl* unit_test); + // Implements the TestPartResultReporterInterface. The implementation just + // delegates to the current global test part result reporter of *unit_test_. + virtual void ReportTestPartResult(const TestPartResult& result); + + private: + UnitTestImpl* const unit_test_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultPerThreadTestPartResultReporter); +}; + +// The private implementation of the UnitTest class. We don't protect +// the methods under a mutex, as this class is not accessible by a +// user and the UnitTest class that delegates work to this class does +// proper locking. +class GTEST_API_ UnitTestImpl { + public: + explicit UnitTestImpl(UnitTest* parent); + virtual ~UnitTestImpl(); + + // There are two different ways to register your own TestPartResultReporter. + // You can register your own repoter to listen either only for test results + // from the current thread or for results from all threads. + // By default, each per-thread test result repoter just passes a new + // TestPartResult to the global test result reporter, which registers the + // test part result for the currently running test. + + // Returns the global test part result reporter. + TestPartResultReporterInterface* GetGlobalTestPartResultReporter(); + + // Sets the global test part result reporter. + void SetGlobalTestPartResultReporter( + TestPartResultReporterInterface* reporter); + + // Returns the test part result reporter for the current thread. + TestPartResultReporterInterface* GetTestPartResultReporterForCurrentThread(); + + // Sets the test part result reporter for the current thread. + void SetTestPartResultReporterForCurrentThread( + TestPartResultReporterInterface* reporter); + + // Gets the number of successful test cases. + int successful_test_case_count() const; + + // Gets the number of failed test cases. + int failed_test_case_count() const; + + // Gets the number of all test cases. + int total_test_case_count() const; + + // Gets the number of all test cases that contain at least one test + // that should run. + int test_case_to_run_count() const; + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns true iff the unit test passed (i.e. all test cases passed). + bool Passed() const { return !Failed(); } + + // Returns true iff the unit test failed (i.e. some test case failed + // or something outside of all tests failed). + bool Failed() const { + return failed_test_case_count() > 0 || ad_hoc_test_result()->Failed(); + } + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + const TestCase* GetTestCase(int i) const { + const int index = GetElementOr(test_case_indices_, i, -1); + return index < 0 ? NULL : test_cases_[i]; + } + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + TestCase* GetMutableTestCase(int i) { + const int index = GetElementOr(test_case_indices_, i, -1); + return index < 0 ? NULL : test_cases_[index]; + } + + // Provides access to the event listener list. + TestEventListeners* listeners() { return &listeners_; } + + // Returns the TestResult for the test that's currently running, or + // the TestResult for the ad hoc test if no test is running. + TestResult* current_test_result(); + + // Returns the TestResult for the ad hoc test. + const TestResult* ad_hoc_test_result() const { return &ad_hoc_test_result_; } + + // Sets the OS stack trace getter. + // + // Does nothing if the input and the current OS stack trace getter + // are the same; otherwise, deletes the old getter and makes the + // input the current getter. + void set_os_stack_trace_getter(OsStackTraceGetterInterface* getter); + + // Returns the current OS stack trace getter if it is not NULL; + // otherwise, creates an OsStackTraceGetter, makes it the current + // getter, and returns it. + OsStackTraceGetterInterface* os_stack_trace_getter(); + + // Returns the current OS stack trace as an std::string. + // + // The maximum number of stack frames to be included is specified by + // the gtest_stack_trace_depth flag. The skip_count parameter + // specifies the number of top frames to be skipped, which doesn't + // count against the number of frames to be included. + // + // For example, if Foo() calls Bar(), which in turn calls + // CurrentOsStackTraceExceptTop(1), Foo() will be included in the + // trace but Bar() and CurrentOsStackTraceExceptTop() won't. + std::string CurrentOsStackTraceExceptTop(int skip_count) GTEST_NO_INLINE_; + + // Finds and returns a TestCase with the given name. If one doesn't + // exist, creates one and returns it. + // + // Arguments: + // + // test_case_name: name of the test case + // type_param: the name of the test's type parameter, or NULL if + // this is not a typed or a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + TestCase* GetTestCase(const char* test_case_name, + const char* type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc); + + // Adds a TestInfo to the unit test. + // + // Arguments: + // + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + // test_info: the TestInfo object + void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + TestInfo* test_info) { + // In order to support thread-safe death tests, we need to + // remember the original working directory when the test program + // was first invoked. We cannot do this in RUN_ALL_TESTS(), as + // the user may have changed the current directory before calling + // RUN_ALL_TESTS(). Therefore we capture the current directory in + // AddTestInfo(), which is called to register a TEST or TEST_F + // before main() is reached. + if (original_working_dir_.IsEmpty()) { + original_working_dir_.Set(FilePath::GetCurrentDir()); + GTEST_CHECK_(!original_working_dir_.IsEmpty()) + << "Failed to get the current working directory."; + } + + GetTestCase(test_info->test_case_name(), + test_info->type_param(), + set_up_tc, + tear_down_tc)->AddTestInfo(test_info); + } + +#if GTEST_HAS_PARAM_TEST + // Returns ParameterizedTestCaseRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + internal::ParameterizedTestCaseRegistry& parameterized_test_registry() { + return parameterized_test_registry_; + } +#endif // GTEST_HAS_PARAM_TEST + + // Sets the TestCase object for the test that's currently running. + void set_current_test_case(TestCase* a_current_test_case) { + current_test_case_ = a_current_test_case; + } + + // Sets the TestInfo object for the test that's currently running. If + // current_test_info is NULL, the assertion results will be stored in + // ad_hoc_test_result_. + void set_current_test_info(TestInfo* a_current_test_info) { + current_test_info_ = a_current_test_info; + } + + // Registers all parameterized tests defined using TEST_P and + // INSTANTIATE_TEST_CASE_P, creating regular tests for each test/parameter + // combination. This method can be called more then once; it has guards + // protecting from registering the tests more then once. If + // value-parameterized tests are disabled, RegisterParameterizedTests is + // present but does nothing. + void RegisterParameterizedTests(); + + // Runs all tests in this UnitTest object, prints the result, and + // returns true if all tests are successful. If any exception is + // thrown during a test, this test is considered to be failed, but + // the rest of the tests will still be run. + bool RunAllTests(); + + // Clears the results of all tests, except the ad hoc tests. + void ClearNonAdHocTestResult() { + ForEach(test_cases_, TestCase::ClearTestCaseResult); + } + + // Clears the results of ad-hoc test assertions. + void ClearAdHocTestResult() { + ad_hoc_test_result_.Clear(); + } + + // Adds a TestProperty to the current TestResult object when invoked in a + // context of a test or a test case, or to the global property set. If the + // result already contains a property with the same key, the value will be + // updated. + void RecordProperty(const TestProperty& test_property); + + enum ReactionToSharding { + HONOR_SHARDING_PROTOCOL, + IGNORE_SHARDING_PROTOCOL + }; + + // Matches the full name of each test against the user-specified + // filter to decide whether the test should run, then records the + // result in each TestCase and TestInfo object. + // If shard_tests == HONOR_SHARDING_PROTOCOL, further filters tests + // based on sharding variables in the environment. + // Returns the number of tests that should run. + int FilterTests(ReactionToSharding shard_tests); + + // Prints the names of the tests matching the user-specified filter flag. + void ListTestsMatchingFilter(); + + const TestCase* current_test_case() const { return current_test_case_; } + TestInfo* current_test_info() { return current_test_info_; } + const TestInfo* current_test_info() const { return current_test_info_; } + + // Returns the vector of environments that need to be set-up/torn-down + // before/after the tests are run. + std::vector& environments() { return environments_; } + + // Getters for the per-thread Google Test trace stack. + std::vector& gtest_trace_stack() { + return *(gtest_trace_stack_.pointer()); + } + const std::vector& gtest_trace_stack() const { + return gtest_trace_stack_.get(); + } + +#if GTEST_HAS_DEATH_TEST + void InitDeathTestSubprocessControlInfo() { + internal_run_death_test_flag_.reset(ParseInternalRunDeathTestFlag()); + } + // Returns a pointer to the parsed --gtest_internal_run_death_test + // flag, or NULL if that flag was not specified. + // This information is useful only in a death test child process. + // Must not be called before a call to InitGoogleTest. + const InternalRunDeathTestFlag* internal_run_death_test_flag() const { + return internal_run_death_test_flag_.get(); + } + + // Returns a pointer to the current death test factory. + internal::DeathTestFactory* death_test_factory() { + return death_test_factory_.get(); + } + + void SuppressTestEventsIfInSubprocess(); + + friend class ReplaceDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + + // Initializes the event listener performing XML output as specified by + // UnitTestOptions. Must not be called before InitGoogleTest. + void ConfigureXmlOutput(); + +#if GTEST_CAN_STREAM_RESULTS_ + // Initializes the event listener for streaming test results to a socket. + // Must not be called before InitGoogleTest. + void ConfigureStreamingOutput(); +#endif + + // Performs initialization dependent upon flag values obtained in + // ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to + // ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest + // this function is also called from RunAllTests. Since this function can be + // called more than once, it has to be idempotent. + void PostFlagParsingInit(); + + // Gets the random seed used at the start of the current test iteration. + int random_seed() const { return random_seed_; } + + // Gets the random number generator. + internal::Random* random() { return &random_; } + + // Shuffles all test cases, and the tests within each test case, + // making sure that death tests are still run first. + void ShuffleTests(); + + // Restores the test cases and tests to their order before the first shuffle. + void UnshuffleTests(); + + // Returns the value of GTEST_FLAG(catch_exceptions) at the moment + // UnitTest::Run() starts. + bool catch_exceptions() const { return catch_exceptions_; } + + private: + friend class ::testing::UnitTest; + + // Used by UnitTest::Run() to capture the state of + // GTEST_FLAG(catch_exceptions) at the moment it starts. + void set_catch_exceptions(bool value) { catch_exceptions_ = value; } + + // The UnitTest object that owns this implementation object. + UnitTest* const parent_; + + // The working directory when the first TEST() or TEST_F() was + // executed. + internal::FilePath original_working_dir_; + + // The default test part result reporters. + DefaultGlobalTestPartResultReporter default_global_test_part_result_reporter_; + DefaultPerThreadTestPartResultReporter + default_per_thread_test_part_result_reporter_; + + // Points to (but doesn't own) the global test part result reporter. + TestPartResultReporterInterface* global_test_part_result_repoter_; + + // Protects read and write access to global_test_part_result_reporter_. + internal::Mutex global_test_part_result_reporter_mutex_; + + // Points to (but doesn't own) the per-thread test part result reporter. + internal::ThreadLocal + per_thread_test_part_result_reporter_; + + // The vector of environments that need to be set-up/torn-down + // before/after the tests are run. + std::vector environments_; + + // The vector of TestCases in their original order. It owns the + // elements in the vector. + std::vector test_cases_; + + // Provides a level of indirection for the test case list to allow + // easy shuffling and restoring the test case order. The i-th + // element of this vector is the index of the i-th test case in the + // shuffled order. + std::vector test_case_indices_; + +#if GTEST_HAS_PARAM_TEST + // ParameterizedTestRegistry object used to register value-parameterized + // tests. + internal::ParameterizedTestCaseRegistry parameterized_test_registry_; + + // Indicates whether RegisterParameterizedTests() has been called already. + bool parameterized_tests_registered_; +#endif // GTEST_HAS_PARAM_TEST + + // Index of the last death test case registered. Initially -1. + int last_death_test_case_; + + // This points to the TestCase for the currently running test. It + // changes as Google Test goes through one test case after another. + // When no test is running, this is set to NULL and Google Test + // stores assertion results in ad_hoc_test_result_. Initially NULL. + TestCase* current_test_case_; + + // This points to the TestInfo for the currently running test. It + // changes as Google Test goes through one test after another. When + // no test is running, this is set to NULL and Google Test stores + // assertion results in ad_hoc_test_result_. Initially NULL. + TestInfo* current_test_info_; + + // Normally, a user only writes assertions inside a TEST or TEST_F, + // or inside a function called by a TEST or TEST_F. Since Google + // Test keeps track of which test is current running, it can + // associate such an assertion with the test it belongs to. + // + // If an assertion is encountered when no TEST or TEST_F is running, + // Google Test attributes the assertion result to an imaginary "ad hoc" + // test, and records the result in ad_hoc_test_result_. + TestResult ad_hoc_test_result_; + + // The list of event listeners that can be used to track events inside + // Google Test. + TestEventListeners listeners_; + + // The OS stack trace getter. Will be deleted when the UnitTest + // object is destructed. By default, an OsStackTraceGetter is used, + // but the user can set this field to use a custom getter if that is + // desired. + OsStackTraceGetterInterface* os_stack_trace_getter_; + + // True iff PostFlagParsingInit() has been called. + bool post_flag_parse_init_performed_; + + // The random number seed used at the beginning of the test run. + int random_seed_; + + // Our random number generator. + internal::Random random_; + + // The time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp_; + + // How long the test took to run, in milliseconds. + TimeInMillis elapsed_time_; + +#if GTEST_HAS_DEATH_TEST + // The decomposed components of the gtest_internal_run_death_test flag, + // parsed when RUN_ALL_TESTS is called. + internal::scoped_ptr internal_run_death_test_flag_; + internal::scoped_ptr death_test_factory_; +#endif // GTEST_HAS_DEATH_TEST + + // A per-thread stack of traces created by the SCOPED_TRACE() macro. + internal::ThreadLocal > gtest_trace_stack_; + + // The value of GTEST_FLAG(catch_exceptions) at the moment RunAllTests() + // starts. + bool catch_exceptions_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTestImpl); +}; // class UnitTestImpl + +// Convenience function for accessing the global UnitTest +// implementation object. +inline UnitTestImpl* GetUnitTestImpl() { + return UnitTest::GetInstance()->impl(); +} + +#if GTEST_USES_SIMPLE_RE + +// Internal helper functions for implementing the simple regular +// expression matcher. +GTEST_API_ bool IsInSet(char ch, const char* str); +GTEST_API_ bool IsAsciiDigit(char ch); +GTEST_API_ bool IsAsciiPunct(char ch); +GTEST_API_ bool IsRepeat(char ch); +GTEST_API_ bool IsAsciiWhiteSpace(char ch); +GTEST_API_ bool IsAsciiWordChar(char ch); +GTEST_API_ bool IsValidEscape(char ch); +GTEST_API_ bool AtomMatchesChar(bool escaped, char pattern, char ch); +GTEST_API_ bool ValidateRegex(const char* regex); +GTEST_API_ bool MatchRegexAtHead(const char* regex, const char* str); +GTEST_API_ bool MatchRepetitionAndRegexAtHead( + bool escaped, char ch, char repeat, const char* regex, const char* str); +GTEST_API_ bool MatchRegexAnywhere(const char* regex, const char* str); + +#endif // GTEST_USES_SIMPLE_RE + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. +GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, char** argv); +GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv); + +#if GTEST_HAS_DEATH_TEST + +// Returns the message describing the last system error, regardless of the +// platform. +GTEST_API_ std::string GetLastErrnoDescription(); + +# if GTEST_OS_WINDOWS +// Provides leak-safe Windows kernel handle ownership. +class AutoHandle { + public: + AutoHandle() : handle_(INVALID_HANDLE_VALUE) {} + explicit AutoHandle(HANDLE handle) : handle_(handle) {} + + ~AutoHandle() { Reset(); } + + HANDLE Get() const { return handle_; } + void Reset() { Reset(INVALID_HANDLE_VALUE); } + void Reset(HANDLE handle) { + if (handle != handle_) { + if (handle_ != INVALID_HANDLE_VALUE) + ::CloseHandle(handle_); + handle_ = handle; + } + } + + private: + HANDLE handle_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle); +}; +# endif // GTEST_OS_WINDOWS + +// Attempts to parse a string into a positive integer pointed to by the +// number parameter. Returns true if that is possible. +// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use +// it here. +template +bool ParseNaturalNumber(const ::std::string& str, Integer* number) { + // Fail fast if the given string does not begin with a digit; + // this bypasses strtoXXX's "optional leading whitespace and plus + // or minus sign" semantics, which are undesirable here. + if (str.empty() || !IsDigit(str[0])) { + return false; + } + errno = 0; + + char* end; + // BiggestConvertible is the largest integer type that system-provided + // string-to-number conversion routines can return. + +# if GTEST_OS_WINDOWS && !defined(__GNUC__) + + // MSVC and C++ Builder define __int64 instead of the standard long long. + typedef unsigned __int64 BiggestConvertible; + const BiggestConvertible parsed = _strtoui64(str.c_str(), &end, 10); + +# else + + typedef unsigned long long BiggestConvertible; // NOLINT + const BiggestConvertible parsed = strtoull(str.c_str(), &end, 10); + +# endif // GTEST_OS_WINDOWS && !defined(__GNUC__) + + const bool parse_success = *end == '\0' && errno == 0; + + // TODO(vladl@google.com): Convert this to compile time assertion when it is + // available. + GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed)); + + const Integer result = static_cast(parsed); + if (parse_success && static_cast(result) == parsed) { + *number = result; + return true; + } + return false; +} +#endif // GTEST_HAS_DEATH_TEST + +// TestResult contains some private methods that should be hidden from +// Google Test user but are required for testing. This class allow our tests +// to access them. +// +// This class is supplied only for the purpose of testing Google Test's own +// constructs. Do not use it in user tests, either directly or indirectly. +class TestResultAccessor { + public: + static void RecordProperty(TestResult* test_result, + const std::string& xml_element, + const TestProperty& property) { + test_result->RecordProperty(xml_element, property); + } + + static void ClearTestPartResults(TestResult* test_result) { + test_result->ClearTestPartResults(); + } + + static const std::vector& test_part_results( + const TestResult& test_result) { + return test_result.test_part_results(); + } +}; + +#if GTEST_CAN_STREAM_RESULTS_ + +// Streams test results to the given port on the given host machine. +class StreamingListener : public EmptyTestEventListener { + public: + // Abstract base class for writing strings to a socket. + class AbstractSocketWriter { + public: + virtual ~AbstractSocketWriter() {} + + // Sends a string to the socket. + virtual void Send(const string& message) = 0; + + // Closes the socket. + virtual void CloseConnection() {} + + // Sends a string and a newline to the socket. + void SendLn(const string& message) { + Send(message + "\n"); + } + }; + + // Concrete class for actually writing strings to a socket. + class SocketWriter : public AbstractSocketWriter { + public: + SocketWriter(const string& host, const string& port) + : sockfd_(-1), host_name_(host), port_num_(port) { + MakeConnection(); + } + + virtual ~SocketWriter() { + if (sockfd_ != -1) + CloseConnection(); + } + + // Sends a string to the socket. + virtual void Send(const string& message) { + GTEST_CHECK_(sockfd_ != -1) + << "Send() can be called only when there is a connection."; + + const int len = static_cast(message.length()); + if (write(sockfd_, message.c_str(), len) != len) { + GTEST_LOG_(WARNING) + << "stream_result_to: failed to stream to " + << host_name_ << ":" << port_num_; + } + } + + private: + // Creates a client socket and connects to the server. + void MakeConnection(); + + // Closes the socket. + void CloseConnection() { + GTEST_CHECK_(sockfd_ != -1) + << "CloseConnection() can be called only when there is a connection."; + + close(sockfd_); + sockfd_ = -1; + } + + int sockfd_; // socket file descriptor + const string host_name_; + const string port_num_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SocketWriter); + }; // class SocketWriter + + // Escapes '=', '&', '%', and '\n' characters in str as "%xx". + static string UrlEncode(const char* str); + + StreamingListener(const string& host, const string& port) + : socket_writer_(new SocketWriter(host, port)) { Start(); } + + explicit StreamingListener(AbstractSocketWriter* socket_writer) + : socket_writer_(socket_writer) { Start(); } + + void OnTestProgramStart(const UnitTest& /* unit_test */) { + SendLn("event=TestProgramStart"); + } + + void OnTestProgramEnd(const UnitTest& unit_test) { + // Note that Google Test current only report elapsed time for each + // test iteration, not for the entire test program. + SendLn("event=TestProgramEnd&passed=" + FormatBool(unit_test.Passed())); + + // Notify the streaming server to stop. + socket_writer_->CloseConnection(); + } + + void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) { + SendLn("event=TestIterationStart&iteration=" + + StreamableToString(iteration)); + } + + void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) { + SendLn("event=TestIterationEnd&passed=" + + FormatBool(unit_test.Passed()) + "&elapsed_time=" + + StreamableToString(unit_test.elapsed_time()) + "ms"); + } + + void OnTestCaseStart(const TestCase& test_case) { + SendLn(std::string("event=TestCaseStart&name=") + test_case.name()); + } + + void OnTestCaseEnd(const TestCase& test_case) { + SendLn("event=TestCaseEnd&passed=" + FormatBool(test_case.Passed()) + + "&elapsed_time=" + StreamableToString(test_case.elapsed_time()) + + "ms"); + } + + void OnTestStart(const TestInfo& test_info) { + SendLn(std::string("event=TestStart&name=") + test_info.name()); + } + + void OnTestEnd(const TestInfo& test_info) { + SendLn("event=TestEnd&passed=" + + FormatBool((test_info.result())->Passed()) + + "&elapsed_time=" + + StreamableToString((test_info.result())->elapsed_time()) + "ms"); + } + + void OnTestPartResult(const TestPartResult& test_part_result) { + const char* file_name = test_part_result.file_name(); + if (file_name == NULL) + file_name = ""; + SendLn("event=TestPartResult&file=" + UrlEncode(file_name) + + "&line=" + StreamableToString(test_part_result.line_number()) + + "&message=" + UrlEncode(test_part_result.message())); + } + + private: + // Sends the given message and a newline to the socket. + void SendLn(const string& message) { socket_writer_->SendLn(message); } + + // Called at the start of streaming to notify the receiver what + // protocol we are using. + void Start() { SendLn("gtest_streaming_protocol_version=1.0"); } + + string FormatBool(bool value) { return value ? "1" : "0"; } + + const scoped_ptr socket_writer_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener); +}; // class StreamingListener + +#endif // GTEST_CAN_STREAM_RESULTS_ + +} // namespace internal +} // namespace testing + +#endif // GTEST_SRC_GTEST_INTERNAL_INL_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-port.cc b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-port.cc new file mode 100644 index 00000000..0c4df5f2 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-port.cc @@ -0,0 +1,805 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#include "gtest/internal/gtest-port.h" + +#include +#include +#include +#include + +#if GTEST_OS_WINDOWS_MOBILE +# include // For TerminateProcess() +#elif GTEST_OS_WINDOWS +# include +# include +#else +# include +#endif // GTEST_OS_WINDOWS_MOBILE + +#if GTEST_OS_MAC +# include +# include +# include +#endif // GTEST_OS_MAC + +#if GTEST_OS_QNX +# include +# include +#endif // GTEST_OS_QNX + +#include "gtest/gtest-spi.h" +#include "gtest/gtest-message.h" +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-string.h" + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#include "src/gtest-internal-inl.h" +#undef GTEST_IMPLEMENTATION_ + +namespace testing { +namespace internal { + +#if defined(_MSC_VER) || defined(__BORLANDC__) +// MSVC and C++Builder do not provide a definition of STDERR_FILENO. +const int kStdOutFileno = 1; +const int kStdErrFileno = 2; +#else +const int kStdOutFileno = STDOUT_FILENO; +const int kStdErrFileno = STDERR_FILENO; +#endif // _MSC_VER + +#if GTEST_OS_MAC + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + const task_t task = mach_task_self(); + mach_msg_type_number_t thread_count; + thread_act_array_t thread_list; + const kern_return_t status = task_threads(task, &thread_list, &thread_count); + if (status == KERN_SUCCESS) { + // task_threads allocates resources in thread_list and we need to free them + // to avoid leaks. + vm_deallocate(task, + reinterpret_cast(thread_list), + sizeof(thread_t) * thread_count); + return static_cast(thread_count); + } else { + return 0; + } +} + +#elif GTEST_OS_QNX + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + const int fd = open("/proc/self/as", O_RDONLY); + if (fd < 0) { + return 0; + } + procfs_info process_info; + const int status = + devctl(fd, DCMD_PROC_INFO, &process_info, sizeof(process_info), NULL); + close(fd); + if (status == EOK) { + return static_cast(process_info.num_threads); + } else { + return 0; + } +} + +#else + +size_t GetThreadCount() { + // There's no portable way to detect the number of threads, so we just + // return 0 to indicate that we cannot detect it. + return 0; +} + +#endif // GTEST_OS_MAC + +#if GTEST_USES_POSIX_RE + +// Implements RE. Currently only needed for death tests. + +RE::~RE() { + if (is_valid_) { + // regfree'ing an invalid regex might crash because the content + // of the regex is undefined. Since the regex's are essentially + // the same, one cannot be valid (or invalid) without the other + // being so too. + regfree(&partial_regex_); + regfree(&full_regex_); + } + free(const_cast(pattern_)); +} + +// Returns true iff regular expression re matches the entire str. +bool RE::FullMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.full_regex_, str, 1, &match, 0) == 0; +} + +// Returns true iff regular expression re matches a substring of str +// (including str itself). +bool RE::PartialMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.partial_regex_, str, 1, &match, 0) == 0; +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = posix::StrDup(regex); + + // Reserves enough bytes to hold the regular expression used for a + // full match. + const size_t full_regex_len = strlen(regex) + 10; + char* const full_pattern = new char[full_regex_len]; + + snprintf(full_pattern, full_regex_len, "^(%s)$", regex); + is_valid_ = regcomp(&full_regex_, full_pattern, REG_EXTENDED) == 0; + // We want to call regcomp(&partial_regex_, ...) even if the + // previous expression returns false. Otherwise partial_regex_ may + // not be properly initialized can may cause trouble when it's + // freed. + // + // Some implementation of POSIX regex (e.g. on at least some + // versions of Cygwin) doesn't accept the empty string as a valid + // regex. We change it to an equivalent form "()" to be safe. + if (is_valid_) { + const char* const partial_regex = (*regex == '\0') ? "()" : regex; + is_valid_ = regcomp(&partial_regex_, partial_regex, REG_EXTENDED) == 0; + } + EXPECT_TRUE(is_valid_) + << "Regular expression \"" << regex + << "\" is not a valid POSIX Extended regular expression."; + + delete[] full_pattern; +} + +#elif GTEST_USES_SIMPLE_RE + +// Returns true iff ch appears anywhere in str (excluding the +// terminating '\0' character). +bool IsInSet(char ch, const char* str) { + return ch != '\0' && strchr(str, ch) != NULL; +} + +// Returns true iff ch belongs to the given classification. Unlike +// similar functions in , these aren't affected by the +// current locale. +bool IsAsciiDigit(char ch) { return '0' <= ch && ch <= '9'; } +bool IsAsciiPunct(char ch) { + return IsInSet(ch, "^-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~"); +} +bool IsRepeat(char ch) { return IsInSet(ch, "?*+"); } +bool IsAsciiWhiteSpace(char ch) { return IsInSet(ch, " \f\n\r\t\v"); } +bool IsAsciiWordChar(char ch) { + return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || + ('0' <= ch && ch <= '9') || ch == '_'; +} + +// Returns true iff "\\c" is a supported escape sequence. +bool IsValidEscape(char c) { + return (IsAsciiPunct(c) || IsInSet(c, "dDfnrsStvwW")); +} + +// Returns true iff the given atom (specified by escaped and pattern) +// matches ch. The result is undefined if the atom is invalid. +bool AtomMatchesChar(bool escaped, char pattern_char, char ch) { + if (escaped) { // "\\p" where p is pattern_char. + switch (pattern_char) { + case 'd': return IsAsciiDigit(ch); + case 'D': return !IsAsciiDigit(ch); + case 'f': return ch == '\f'; + case 'n': return ch == '\n'; + case 'r': return ch == '\r'; + case 's': return IsAsciiWhiteSpace(ch); + case 'S': return !IsAsciiWhiteSpace(ch); + case 't': return ch == '\t'; + case 'v': return ch == '\v'; + case 'w': return IsAsciiWordChar(ch); + case 'W': return !IsAsciiWordChar(ch); + } + return IsAsciiPunct(pattern_char) && pattern_char == ch; + } + + return (pattern_char == '.' && ch != '\n') || pattern_char == ch; +} + +// Helper function used by ValidateRegex() to format error messages. +std::string FormatRegexSyntaxError(const char* regex, int index) { + return (Message() << "Syntax error at index " << index + << " in simple regular expression \"" << regex << "\": ").GetString(); +} + +// Generates non-fatal failures and returns false if regex is invalid; +// otherwise returns true. +bool ValidateRegex(const char* regex) { + if (regex == NULL) { + // TODO(wan@google.com): fix the source file location in the + // assertion failures to match where the regex is used in user + // code. + ADD_FAILURE() << "NULL is not a valid simple regular expression."; + return false; + } + + bool is_valid = true; + + // True iff ?, *, or + can follow the previous atom. + bool prev_repeatable = false; + for (int i = 0; regex[i]; i++) { + if (regex[i] == '\\') { // An escape sequence + i++; + if (regex[i] == '\0') { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) + << "'\\' cannot appear at the end."; + return false; + } + + if (!IsValidEscape(regex[i])) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) + << "invalid escape sequence \"\\" << regex[i] << "\"."; + is_valid = false; + } + prev_repeatable = true; + } else { // Not an escape sequence. + const char ch = regex[i]; + + if (ch == '^' && i > 0) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'^' can only appear at the beginning."; + is_valid = false; + } else if (ch == '$' && regex[i + 1] != '\0') { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'$' can only appear at the end."; + is_valid = false; + } else if (IsInSet(ch, "()[]{}|")) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'" << ch << "' is unsupported."; + is_valid = false; + } else if (IsRepeat(ch) && !prev_repeatable) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'" << ch << "' can only follow a repeatable token."; + is_valid = false; + } + + prev_repeatable = !IsInSet(ch, "^$?*+"); + } + } + + return is_valid; +} + +// Matches a repeated regex atom followed by a valid simple regular +// expression. The regex atom is defined as c if escaped is false, +// or \c otherwise. repeat is the repetition meta character (?, *, +// or +). The behavior is undefined if str contains too many +// characters to be indexable by size_t, in which case the test will +// probably time out anyway. We are fine with this limitation as +// std::string has it too. +bool MatchRepetitionAndRegexAtHead( + bool escaped, char c, char repeat, const char* regex, + const char* str) { + const size_t min_count = (repeat == '+') ? 1 : 0; + const size_t max_count = (repeat == '?') ? 1 : + static_cast(-1) - 1; + // We cannot call numeric_limits::max() as it conflicts with the + // max() macro on Windows. + + for (size_t i = 0; i <= max_count; ++i) { + // We know that the atom matches each of the first i characters in str. + if (i >= min_count && MatchRegexAtHead(regex, str + i)) { + // We have enough matches at the head, and the tail matches too. + // Since we only care about *whether* the pattern matches str + // (as opposed to *how* it matches), there is no need to find a + // greedy match. + return true; + } + if (str[i] == '\0' || !AtomMatchesChar(escaped, c, str[i])) + return false; + } + return false; +} + +// Returns true iff regex matches a prefix of str. regex must be a +// valid simple regular expression and not start with "^", or the +// result is undefined. +bool MatchRegexAtHead(const char* regex, const char* str) { + if (*regex == '\0') // An empty regex matches a prefix of anything. + return true; + + // "$" only matches the end of a string. Note that regex being + // valid guarantees that there's nothing after "$" in it. + if (*regex == '$') + return *str == '\0'; + + // Is the first thing in regex an escape sequence? + const bool escaped = *regex == '\\'; + if (escaped) + ++regex; + if (IsRepeat(regex[1])) { + // MatchRepetitionAndRegexAtHead() calls MatchRegexAtHead(), so + // here's an indirect recursion. It terminates as the regex gets + // shorter in each recursion. + return MatchRepetitionAndRegexAtHead( + escaped, regex[0], regex[1], regex + 2, str); + } else { + // regex isn't empty, isn't "$", and doesn't start with a + // repetition. We match the first atom of regex with the first + // character of str and recurse. + return (*str != '\0') && AtomMatchesChar(escaped, *regex, *str) && + MatchRegexAtHead(regex + 1, str + 1); + } +} + +// Returns true iff regex matches any substring of str. regex must be +// a valid simple regular expression, or the result is undefined. +// +// The algorithm is recursive, but the recursion depth doesn't exceed +// the regex length, so we won't need to worry about running out of +// stack space normally. In rare cases the time complexity can be +// exponential with respect to the regex length + the string length, +// but usually it's must faster (often close to linear). +bool MatchRegexAnywhere(const char* regex, const char* str) { + if (regex == NULL || str == NULL) + return false; + + if (*regex == '^') + return MatchRegexAtHead(regex + 1, str); + + // A successful match can be anywhere in str. + do { + if (MatchRegexAtHead(regex, str)) + return true; + } while (*str++ != '\0'); + return false; +} + +// Implements the RE class. + +RE::~RE() { + free(const_cast(pattern_)); + free(const_cast(full_pattern_)); +} + +// Returns true iff regular expression re matches the entire str. +bool RE::FullMatch(const char* str, const RE& re) { + return re.is_valid_ && MatchRegexAnywhere(re.full_pattern_, str); +} + +// Returns true iff regular expression re matches a substring of str +// (including str itself). +bool RE::PartialMatch(const char* str, const RE& re) { + return re.is_valid_ && MatchRegexAnywhere(re.pattern_, str); +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = full_pattern_ = NULL; + if (regex != NULL) { + pattern_ = posix::StrDup(regex); + } + + is_valid_ = ValidateRegex(regex); + if (!is_valid_) { + // No need to calculate the full pattern when the regex is invalid. + return; + } + + const size_t len = strlen(regex); + // Reserves enough bytes to hold the regular expression used for a + // full match: we need space to prepend a '^', append a '$', and + // terminate the string with '\0'. + char* buffer = static_cast(malloc(len + 3)); + full_pattern_ = buffer; + + if (*regex != '^') + *buffer++ = '^'; // Makes sure full_pattern_ starts with '^'. + + // We don't use snprintf or strncpy, as they trigger a warning when + // compiled with VC++ 8.0. + memcpy(buffer, regex, len); + buffer += len; + + if (len == 0 || regex[len - 1] != '$') + *buffer++ = '$'; // Makes sure full_pattern_ ends with '$'. + + *buffer = '\0'; +} + +#endif // GTEST_USES_POSIX_RE + +const char kUnknownFile[] = "unknown file"; + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) { + const std::string file_name(file == NULL ? kUnknownFile : file); + + if (line < 0) { + return file_name + ":"; + } +#ifdef _MSC_VER + return file_name + "(" + StreamableToString(line) + "):"; +#else + return file_name + ":" + StreamableToString(line) + ":"; +#endif // _MSC_VER +} + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +// Note that FormatCompilerIndependentFileLocation() does NOT append colon +// to the file location it produces, unlike FormatFileLocation(). +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation( + const char* file, int line) { + const std::string file_name(file == NULL ? kUnknownFile : file); + + if (line < 0) + return file_name; + else + return file_name + ":" + StreamableToString(line); +} + + +GTestLog::GTestLog(GTestLogSeverity severity, const char* file, int line) + : severity_(severity) { + const char* const marker = + severity == GTEST_INFO ? "[ INFO ]" : + severity == GTEST_WARNING ? "[WARNING]" : + severity == GTEST_ERROR ? "[ ERROR ]" : "[ FATAL ]"; + GetStream() << ::std::endl << marker << " " + << FormatFileLocation(file, line).c_str() << ": "; +} + +// Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. +GTestLog::~GTestLog() { + GetStream() << ::std::endl; + if (severity_ == GTEST_FATAL) { + fflush(stderr); + posix::Abort(); + } +} +// Disable Microsoft deprecation warnings for POSIX functions called from +// this class (creat, dup, dup2, and close) +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4996) +#endif // _MSC_VER + +#if GTEST_HAS_STREAM_REDIRECTION + +// Object that captures an output stream (stdout/stderr). +class CapturedStream { + public: + // The ctor redirects the stream to a temporary file. + explicit CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) { +# if GTEST_OS_WINDOWS + char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT + char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT + + ::GetTempPathA(sizeof(temp_dir_path), temp_dir_path); + const UINT success = ::GetTempFileNameA(temp_dir_path, + "gtest_redir", + 0, // Generate unique file name. + temp_file_path); + GTEST_CHECK_(success != 0) + << "Unable to create a temporary file in " << temp_dir_path; + const int captured_fd = creat(temp_file_path, _S_IREAD | _S_IWRITE); + GTEST_CHECK_(captured_fd != -1) << "Unable to open temporary file " + << temp_file_path; + filename_ = temp_file_path; +# else + // There's no guarantee that a test has write access to the current + // directory, so we create the temporary file in the /tmp directory + // instead. We use /tmp on most systems, and /sdcard on Android. + // That's because Android doesn't have /tmp. +# if GTEST_OS_LINUX_ANDROID + // Note: Android applications are expected to call the framework's + // Context.getExternalStorageDirectory() method through JNI to get + // the location of the world-writable SD Card directory. However, + // this requires a Context handle, which cannot be retrieved + // globally from native code. Doing so also precludes running the + // code as part of a regular standalone executable, which doesn't + // run in a Dalvik process (e.g. when running it through 'adb shell'). + // + // The location /sdcard is directly accessible from native code + // and is the only location (unofficially) supported by the Android + // team. It's generally a symlink to the real SD Card mount point + // which can be /mnt/sdcard, /mnt/sdcard0, /system/media/sdcard, or + // other OEM-customized locations. Never rely on these, and always + // use /sdcard. + char name_template[] = "/sdcard/gtest_captured_stream.XXXXXX"; +# else + char name_template[] = "/tmp/captured_stream.XXXXXX"; +# endif // GTEST_OS_LINUX_ANDROID + const int captured_fd = mkstemp(name_template); + filename_ = name_template; +# endif // GTEST_OS_WINDOWS + fflush(NULL); + dup2(captured_fd, fd_); + close(captured_fd); + } + + ~CapturedStream() { + remove(filename_.c_str()); + } + + std::string GetCapturedString() { + if (uncaptured_fd_ != -1) { + // Restores the original stream. + fflush(NULL); + dup2(uncaptured_fd_, fd_); + close(uncaptured_fd_); + uncaptured_fd_ = -1; + } + + FILE* const file = posix::FOpen(filename_.c_str(), "r"); + const std::string content = ReadEntireFile(file); + posix::FClose(file); + return content; + } + + private: + // Reads the entire content of a file as an std::string. + static std::string ReadEntireFile(FILE* file); + + // Returns the size (in bytes) of a file. + static size_t GetFileSize(FILE* file); + + const int fd_; // A stream to capture. + int uncaptured_fd_; + // Name of the temporary file holding the stderr output. + ::std::string filename_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream); +}; + +// Returns the size (in bytes) of a file. +size_t CapturedStream::GetFileSize(FILE* file) { + fseek(file, 0, SEEK_END); + return static_cast(ftell(file)); +} + +// Reads the entire content of a file as a string. +std::string CapturedStream::ReadEntireFile(FILE* file) { + const size_t file_size = GetFileSize(file); + char* const buffer = new char[file_size]; + + size_t bytes_last_read = 0; // # of bytes read in the last fread() + size_t bytes_read = 0; // # of bytes read so far + + fseek(file, 0, SEEK_SET); + + // Keeps reading the file until we cannot read further or the + // pre-determined file size is reached. + do { + bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file); + bytes_read += bytes_last_read; + } while (bytes_last_read > 0 && bytes_read < file_size); + + const std::string content(buffer, bytes_read); + delete[] buffer; + + return content; +} + +# ifdef _MSC_VER +# pragma warning(pop) +# endif // _MSC_VER + +static CapturedStream* g_captured_stderr = NULL; +static CapturedStream* g_captured_stdout = NULL; + +// Starts capturing an output stream (stdout/stderr). +void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) { + if (*stream != NULL) { + GTEST_LOG_(FATAL) << "Only one " << stream_name + << " capturer can exist at a time."; + } + *stream = new CapturedStream(fd); +} + +// Stops capturing the output stream and returns the captured string. +std::string GetCapturedStream(CapturedStream** captured_stream) { + const std::string content = (*captured_stream)->GetCapturedString(); + + delete *captured_stream; + *captured_stream = NULL; + + return content; +} + +// Starts capturing stdout. +void CaptureStdout() { + CaptureStream(kStdOutFileno, "stdout", &g_captured_stdout); +} + +// Starts capturing stderr. +void CaptureStderr() { + CaptureStream(kStdErrFileno, "stderr", &g_captured_stderr); +} + +// Stops capturing stdout and returns the captured string. +std::string GetCapturedStdout() { + return GetCapturedStream(&g_captured_stdout); +} + +// Stops capturing stderr and returns the captured string. +std::string GetCapturedStderr() { + return GetCapturedStream(&g_captured_stderr); +} + +#endif // GTEST_HAS_STREAM_REDIRECTION + +#if GTEST_HAS_DEATH_TEST + +// A copy of all command line arguments. Set by InitGoogleTest(). +::std::vector g_argvs; + +static const ::std::vector* g_injected_test_argvs = + NULL; // Owned. + +void SetInjectableArgvs(const ::std::vector* argvs) { + if (g_injected_test_argvs != argvs) + delete g_injected_test_argvs; + g_injected_test_argvs = argvs; +} + +const ::std::vector& GetInjectableArgvs() { + if (g_injected_test_argvs != NULL) { + return *g_injected_test_argvs; + } + return g_argvs; +} +#endif // GTEST_HAS_DEATH_TEST + +#if GTEST_OS_WINDOWS_MOBILE +namespace posix { +void Abort() { + DebugBreak(); + TerminateProcess(GetCurrentProcess(), 1); +} +} // namespace posix +#endif // GTEST_OS_WINDOWS_MOBILE + +// Returns the name of the environment variable corresponding to the +// given flag. For example, FlagToEnvVar("foo") will return +// "GTEST_FOO" in the open-source version. +static std::string FlagToEnvVar(const char* flag) { + const std::string full_flag = + (Message() << GTEST_FLAG_PREFIX_ << flag).GetString(); + + Message env_var; + for (size_t i = 0; i != full_flag.length(); i++) { + env_var << ToUpper(full_flag.c_str()[i]); + } + + return env_var.GetString(); +} + +// Parses 'str' for a 32-bit signed integer. If successful, writes +// the result to *value and returns true; otherwise leaves *value +// unchanged and returns false. +bool ParseInt32(const Message& src_text, const char* str, Int32* value) { + // Parses the environment variable as a decimal integer. + char* end = NULL; + const long long_value = strtol(str, &end, 10); // NOLINT + + // Has strtol() consumed all characters in the string? + if (*end != '\0') { + // No - an invalid character was encountered. + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value \"" << str << "\".\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + // Is the parsed value in the range of an Int32? + const Int32 result = static_cast(long_value); + if (long_value == LONG_MAX || long_value == LONG_MIN || + // The parsed value overflows as a long. (strtol() returns + // LONG_MAX or LONG_MIN when the input overflows.) + result != long_value + // The parsed value overflows as an Int32. + ) { + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value " << str << ", which overflows.\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + *value = result; + return true; +} + +// Reads and returns the Boolean environment variable corresponding to +// the given flag; if it's not set, returns default_value. +// +// The value is considered true iff it's not "0". +bool BoolFromGTestEnv(const char* flag, bool default_value) { + const std::string env_var = FlagToEnvVar(flag); + const char* const string_value = posix::GetEnv(env_var.c_str()); + return string_value == NULL ? + default_value : strcmp(string_value, "0") != 0; +} + +// Reads and returns a 32-bit integer stored in the environment +// variable corresponding to the given flag; if it isn't set or +// doesn't represent a valid 32-bit integer, returns default_value. +Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) { + const std::string env_var = FlagToEnvVar(flag); + const char* const string_value = posix::GetEnv(env_var.c_str()); + if (string_value == NULL) { + // The environment variable is not set. + return default_value; + } + + Int32 result = default_value; + if (!ParseInt32(Message() << "Environment variable " << env_var, + string_value, &result)) { + printf("The default value %s is used.\n", + (Message() << default_value).GetString().c_str()); + fflush(stdout); + return default_value; + } + + return result; +} + +// Reads and returns the string environment variable corresponding to +// the given flag; if it's not set, returns default_value. +const char* StringFromGTestEnv(const char* flag, const char* default_value) { + const std::string env_var = FlagToEnvVar(flag); + const char* const value = posix::GetEnv(env_var.c_str()); + return value == NULL ? default_value : value; +} + +} // namespace internal +} // namespace testing diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-printers.cc b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-printers.cc new file mode 100644 index 00000000..75fa4081 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-printers.cc @@ -0,0 +1,363 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Test - The Google C++ Testing Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter::Print(value, ostream_ptr); +// +// It uses the << operator when possible, and prints the bytes in the +// object otherwise. A user can override its behavior for a class +// type Foo by defining either operator<<(::std::ostream&, const Foo&) +// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that +// defines Foo. + +#include "gtest/gtest-printers.h" +#include +#include +#include // NOLINT +#include +#include "gtest/internal/gtest-port.h" + +namespace testing { + +namespace { + +using ::std::ostream; + +// Prints a segment of bytes in the given object. +void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start, + size_t count, ostream* os) { + char text[5] = ""; + for (size_t i = 0; i != count; i++) { + const size_t j = start + i; + if (i != 0) { + // Organizes the bytes into groups of 2 for easy parsing by + // human. + if ((j % 2) == 0) + *os << ' '; + else + *os << '-'; + } + GTEST_SNPRINTF_(text, sizeof(text), "%02X", obj_bytes[j]); + *os << text; + } +} + +// Prints the bytes in the given value to the given ostream. +void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count, + ostream* os) { + // Tells the user how big the object is. + *os << count << "-byte object <"; + + const size_t kThreshold = 132; + const size_t kChunkSize = 64; + // If the object size is bigger than kThreshold, we'll have to omit + // some details by printing only the first and the last kChunkSize + // bytes. + // TODO(wan): let the user control the threshold using a flag. + if (count < kThreshold) { + PrintByteSegmentInObjectTo(obj_bytes, 0, count, os); + } else { + PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os); + *os << " ... "; + // Rounds up to 2-byte boundary. + const size_t resume_pos = (count - kChunkSize + 1)/2*2; + PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os); + } + *os << ">"; +} + +} // namespace + +namespace internal2 { + +// Delegates to PrintBytesInObjectToImpl() to print the bytes in the +// given object. The delegation simplifies the implementation, which +// uses the << operator and thus is easier done outside of the +// ::testing::internal namespace, which contains a << operator that +// sometimes conflicts with the one in STL. +void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count, + ostream* os) { + PrintBytesInObjectToImpl(obj_bytes, count, os); +} + +} // namespace internal2 + +namespace internal { + +// Depending on the value of a char (or wchar_t), we print it in one +// of three formats: +// - as is if it's a printable ASCII (e.g. 'a', '2', ' '), +// - as a hexidecimal escape sequence (e.g. '\x7F'), or +// - as a special escape sequence (e.g. '\r', '\n'). +enum CharFormat { + kAsIs, + kHexEscape, + kSpecialEscape +}; + +// Returns true if c is a printable ASCII character. We test the +// value of c directly instead of calling isprint(), which is buggy on +// Windows Mobile. +inline bool IsPrintableAscii(wchar_t c) { + return 0x20 <= c && c <= 0x7E; +} + +// Prints a wide or narrow char c as a character literal without the +// quotes, escaping it when necessary; returns how c was formatted. +// The template argument UnsignedChar is the unsigned version of Char, +// which is the type of c. +template +static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) { + switch (static_cast(c)) { + case L'\0': + *os << "\\0"; + break; + case L'\'': + *os << "\\'"; + break; + case L'\\': + *os << "\\\\"; + break; + case L'\a': + *os << "\\a"; + break; + case L'\b': + *os << "\\b"; + break; + case L'\f': + *os << "\\f"; + break; + case L'\n': + *os << "\\n"; + break; + case L'\r': + *os << "\\r"; + break; + case L'\t': + *os << "\\t"; + break; + case L'\v': + *os << "\\v"; + break; + default: + if (IsPrintableAscii(c)) { + *os << static_cast(c); + return kAsIs; + } else { + *os << "\\x" + String::FormatHexInt(static_cast(c)); + return kHexEscape; + } + } + return kSpecialEscape; +} + +// Prints a wchar_t c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsStringLiteralTo(wchar_t c, ostream* os) { + switch (c) { + case L'\'': + *os << "'"; + return kAsIs; + case L'"': + *os << "\\\""; + return kSpecialEscape; + default: + return PrintAsCharLiteralTo(c, os); + } +} + +// Prints a char c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsStringLiteralTo(char c, ostream* os) { + return PrintAsStringLiteralTo( + static_cast(static_cast(c)), os); +} + +// Prints a wide or narrow character c and its code. '\0' is printed +// as "'\\0'", other unprintable characters are also properly escaped +// using the standard C++ escape sequence. The template argument +// UnsignedChar is the unsigned version of Char, which is the type of c. +template +void PrintCharAndCodeTo(Char c, ostream* os) { + // First, print c as a literal in the most readable form we can find. + *os << ((sizeof(c) > 1) ? "L'" : "'"); + const CharFormat format = PrintAsCharLiteralTo(c, os); + *os << "'"; + + // To aid user debugging, we also print c's code in decimal, unless + // it's 0 (in which case c was printed as '\\0', making the code + // obvious). + if (c == 0) + return; + *os << " (" << static_cast(c); + + // For more convenience, we print c's code again in hexidecimal, + // unless c was already printed in the form '\x##' or the code is in + // [1, 9]. + if (format == kHexEscape || (1 <= c && c <= 9)) { + // Do nothing. + } else { + *os << ", 0x" << String::FormatHexInt(static_cast(c)); + } + *os << ")"; +} + +void PrintTo(unsigned char c, ::std::ostream* os) { + PrintCharAndCodeTo(c, os); +} +void PrintTo(signed char c, ::std::ostream* os) { + PrintCharAndCodeTo(c, os); +} + +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its code. L'\0' is printed as "L'\\0'". +void PrintTo(wchar_t wc, ostream* os) { + PrintCharAndCodeTo(wc, os); +} + +// Prints the given array of characters to the ostream. CharType must be either +// char or wchar_t. +// The array starts at begin, the length is len, it may include '\0' characters +// and may not be NUL-terminated. +template +static void PrintCharsAsStringTo( + const CharType* begin, size_t len, ostream* os) { + const char* const kQuoteBegin = sizeof(CharType) == 1 ? "\"" : "L\""; + *os << kQuoteBegin; + bool is_previous_hex = false; + for (size_t index = 0; index < len; ++index) { + const CharType cur = begin[index]; + if (is_previous_hex && IsXDigit(cur)) { + // Previous character is of '\x..' form and this character can be + // interpreted as another hexadecimal digit in its number. Break string to + // disambiguate. + *os << "\" " << kQuoteBegin; + } + is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape; + } + *os << "\""; +} + +// Prints a (const) char/wchar_t array of 'len' elements, starting at address +// 'begin'. CharType must be either char or wchar_t. +template +static void UniversalPrintCharArray( + const CharType* begin, size_t len, ostream* os) { + // The code + // const char kFoo[] = "foo"; + // generates an array of 4, not 3, elements, with the last one being '\0'. + // + // Therefore when printing a char array, we don't print the last element if + // it's '\0', such that the output matches the string literal as it's + // written in the source code. + if (len > 0 && begin[len - 1] == '\0') { + PrintCharsAsStringTo(begin, len - 1, os); + return; + } + + // If, however, the last element in the array is not '\0', e.g. + // const char kFoo[] = { 'f', 'o', 'o' }; + // we must print the entire array. We also print a message to indicate + // that the array is not NUL-terminated. + PrintCharsAsStringTo(begin, len, os); + *os << " (no terminating NUL)"; +} + +// Prints a (const) char array of 'len' elements, starting at address 'begin'. +void UniversalPrintArray(const char* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +// Prints a (const) wchar_t array of 'len' elements, starting at address +// 'begin'. +void UniversalPrintArray(const wchar_t* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +// Prints the given C string to the ostream. +void PrintTo(const char* s, ostream* os) { + if (s == NULL) { + *os << "NULL"; + } else { + *os << ImplicitCast_(s) << " pointing to "; + PrintCharsAsStringTo(s, strlen(s), os); + } +} + +// MSVC compiler can be configured to define whar_t as a typedef +// of unsigned short. Defining an overload for const wchar_t* in that case +// would cause pointers to unsigned shorts be printed as wide strings, +// possibly accessing more memory than intended and causing invalid +// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when +// wchar_t is implemented as a native type. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Prints the given wide C string to the ostream. +void PrintTo(const wchar_t* s, ostream* os) { + if (s == NULL) { + *os << "NULL"; + } else { + *os << ImplicitCast_(s) << " pointing to "; + PrintCharsAsStringTo(s, wcslen(s), os); + } +} +#endif // wchar_t is native + +// Prints a ::string object. +#if GTEST_HAS_GLOBAL_STRING +void PrintStringTo(const ::string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_GLOBAL_STRING + +void PrintStringTo(const ::std::string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} + +// Prints a ::wstring object. +#if GTEST_HAS_GLOBAL_WSTRING +void PrintWideStringTo(const ::wstring& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +#if GTEST_HAS_STD_WSTRING +void PrintWideStringTo(const ::std::wstring& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_STD_WSTRING + +} // namespace internal + +} // namespace testing diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-test-part.cc b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-test-part.cc new file mode 100644 index 00000000..c60eef3a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-test-part.cc @@ -0,0 +1,110 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: mheule@google.com (Markus Heule) +// +// The Google C++ Testing Framework (Google Test) + +#include "gtest/gtest-test-part.h" + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#include "src/gtest-internal-inl.h" +#undef GTEST_IMPLEMENTATION_ + +namespace testing { + +using internal::GetUnitTestImpl; + +// Gets the summary of the failure message by omitting the stack trace +// in it. +std::string TestPartResult::ExtractSummary(const char* message) { + const char* const stack_trace = strstr(message, internal::kStackTraceMarker); + return stack_trace == NULL ? message : + std::string(message, stack_trace); +} + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result) { + return os + << result.file_name() << ":" << result.line_number() << ": " + << (result.type() == TestPartResult::kSuccess ? "Success" : + result.type() == TestPartResult::kFatalFailure ? "Fatal failure" : + "Non-fatal failure") << ":\n" + << result.message() << std::endl; +} + +// Appends a TestPartResult to the array. +void TestPartResultArray::Append(const TestPartResult& result) { + array_.push_back(result); +} + +// Returns the TestPartResult at the given index (0-based). +const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const { + if (index < 0 || index >= size()) { + printf("\nInvalid index (%d) into TestPartResultArray.\n", index); + internal::posix::Abort(); + } + + return array_[index]; +} + +// Returns the number of TestPartResult objects in the array. +int TestPartResultArray::size() const { + return static_cast(array_.size()); +} + +namespace internal { + +HasNewFatalFailureHelper::HasNewFatalFailureHelper() + : has_new_fatal_failure_(false), + original_reporter_(GetUnitTestImpl()-> + GetTestPartResultReporterForCurrentThread()) { + GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(this); +} + +HasNewFatalFailureHelper::~HasNewFatalFailureHelper() { + GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread( + original_reporter_); +} + +void HasNewFatalFailureHelper::ReportTestPartResult( + const TestPartResult& result) { + if (result.fatally_failed()) + has_new_fatal_failure_ = true; + original_reporter_->ReportTestPartResult(result); +} + +} // namespace internal + +} // namespace testing diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-typed-test.cc b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-typed-test.cc new file mode 100644 index 00000000..f0079f40 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest-typed-test.cc @@ -0,0 +1,110 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#include "gtest/gtest-typed-test.h" +#include "gtest/gtest.h" + +namespace testing { +namespace internal { + +#if GTEST_HAS_TYPED_TEST_P + +// Skips to the first non-space char in str. Returns an empty string if str +// contains only whitespace characters. +static const char* SkipSpaces(const char* str) { + while (IsSpace(*str)) + str++; + return str; +} + +// Verifies that registered_tests match the test names in +// defined_test_names_; returns registered_tests if successful, or +// aborts the program otherwise. +const char* TypedTestCasePState::VerifyRegisteredTestNames( + const char* file, int line, const char* registered_tests) { + typedef ::std::set::const_iterator DefinedTestIter; + registered_ = true; + + // Skip initial whitespace in registered_tests since some + // preprocessors prefix stringizied literals with whitespace. + registered_tests = SkipSpaces(registered_tests); + + Message errors; + ::std::set tests; + for (const char* names = registered_tests; names != NULL; + names = SkipComma(names)) { + const std::string name = GetPrefixUntilComma(names); + if (tests.count(name) != 0) { + errors << "Test " << name << " is listed more than once.\n"; + continue; + } + + bool found = false; + for (DefinedTestIter it = defined_test_names_.begin(); + it != defined_test_names_.end(); + ++it) { + if (name == *it) { + found = true; + break; + } + } + + if (found) { + tests.insert(name); + } else { + errors << "No test named " << name + << " can be found in this test case.\n"; + } + } + + for (DefinedTestIter it = defined_test_names_.begin(); + it != defined_test_names_.end(); + ++it) { + if (tests.count(*it) == 0) { + errors << "You forgot to list test " << *it << ".\n"; + } + } + + const std::string& errors_str = errors.GetString(); + if (errors_str != "") { + fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), + errors_str.c_str()); + fflush(stderr); + posix::Abort(); + } + + return registered_tests; +} + +#endif // GTEST_HAS_TYPED_TEST_P + +} // namespace internal +} // namespace testing diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest.cc b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest.cc new file mode 100644 index 00000000..6de53dd0 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest.cc @@ -0,0 +1,5015 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) + +#include "gtest/gtest.h" +#include "gtest/gtest-spi.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include // NOLINT +#include +#include + +#if GTEST_OS_LINUX + +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +# define GTEST_HAS_GETTIMEOFDAY_ 1 + +# include // NOLINT +# include // NOLINT +# include // NOLINT +// Declares vsnprintf(). This header is not available on Windows. +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include + +#elif GTEST_OS_SYMBIAN +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include // NOLINT + +#elif GTEST_OS_ZOS +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include // NOLINT + +// On z/OS we additionally need strings.h for strcasecmp. +# include // NOLINT + +#elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE. + +# include // NOLINT + +#elif GTEST_OS_WINDOWS // We are on Windows proper. + +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include // NOLINT + +# if GTEST_OS_WINDOWS_MINGW +// MinGW has gettimeofday() but not _ftime64(). +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +// TODO(kenton@google.com): There are other ways to get the time on +// Windows, like GetTickCount() or GetSystemTimeAsFileTime(). MinGW +// supports these. consider using them instead. +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include // NOLINT +# endif // GTEST_OS_WINDOWS_MINGW + +// cpplint thinks that the header is already included, so we want to +// silence it. +# include // NOLINT + +#else + +// Assume other platforms have gettimeofday(). +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +# define GTEST_HAS_GETTIMEOFDAY_ 1 + +// cpplint thinks that the header is already included, so we want to +// silence it. +# include // NOLINT +# include // NOLINT + +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +# include +#endif + +#if GTEST_CAN_STREAM_RESULTS_ +# include // NOLINT +# include // NOLINT +#endif + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#include "src/gtest-internal-inl.h" +#undef GTEST_IMPLEMENTATION_ + +#if GTEST_OS_WINDOWS +# define vsnprintf _vsnprintf +#endif // GTEST_OS_WINDOWS + +namespace testing { + +using internal::CountIf; +using internal::ForEach; +using internal::GetElementOr; +using internal::Shuffle; + +// Constants. + +// A test whose test case name or test name matches this filter is +// disabled and not run. +static const char kDisableTestFilter[] = "DISABLED_*:*/DISABLED_*"; + +// A test case whose name matches this filter is considered a death +// test case and will be run before test cases whose name doesn't +// match this filter. +static const char kDeathTestCaseFilter[] = "*DeathTest:*DeathTest/*"; + +// A test filter that matches everything. +static const char kUniversalFilter[] = "*"; + +// The default output file for XML output. +static const char kDefaultOutputFile[] = "test_detail.xml"; + +// The environment variable name for the test shard index. +static const char kTestShardIndex[] = "GTEST_SHARD_INDEX"; +// The environment variable name for the total number of test shards. +static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS"; +// The environment variable name for the test shard status file. +static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE"; + +namespace internal { + +// The text used in failure messages to indicate the start of the +// stack trace. +const char kStackTraceMarker[] = "\nStack trace:\n"; + +// g_help_flag is true iff the --help flag or an equivalent form is +// specified on the command line. +bool g_help_flag = false; + +} // namespace internal + +static const char* GetDefaultFilter() { + return kUniversalFilter; +} + +GTEST_DEFINE_bool_( + also_run_disabled_tests, + internal::BoolFromGTestEnv("also_run_disabled_tests", false), + "Run disabled tests too, in addition to the tests normally being run."); + +GTEST_DEFINE_bool_( + break_on_failure, + internal::BoolFromGTestEnv("break_on_failure", false), + "True iff a failed assertion should be a debugger break-point."); + +GTEST_DEFINE_bool_( + catch_exceptions, + internal::BoolFromGTestEnv("catch_exceptions", true), + "True iff " GTEST_NAME_ + " should catch exceptions and treat them as test failures."); + +GTEST_DEFINE_string_( + color, + internal::StringFromGTestEnv("color", "auto"), + "Whether to use colors in the output. Valid values: yes, no, " + "and auto. 'auto' means to use colors if the output is " + "being sent to a terminal and the TERM environment variable " + "is set to a terminal type that supports colors."); + +GTEST_DEFINE_string_( + filter, + internal::StringFromGTestEnv("filter", GetDefaultFilter()), + "A colon-separated list of glob (not regex) patterns " + "for filtering the tests to run, optionally followed by a " + "'-' and a : separated list of negative patterns (tests to " + "exclude). A test is run if it matches one of the positive " + "patterns and does not match any of the negative patterns."); + +GTEST_DEFINE_bool_(list_tests, false, + "List all tests without running them."); + +GTEST_DEFINE_string_( + output, + internal::StringFromGTestEnv("output", ""), + "A format (currently must be \"xml\"), optionally followed " + "by a colon and an output file name or directory. A directory " + "is indicated by a trailing pathname separator. " + "Examples: \"xml:filename.xml\", \"xml::directoryname/\". " + "If a directory is specified, output files will be created " + "within that directory, with file-names based on the test " + "executable's name and, if necessary, made unique by adding " + "digits."); + +GTEST_DEFINE_bool_( + print_time, + internal::BoolFromGTestEnv("print_time", true), + "True iff " GTEST_NAME_ + " should display elapsed time in text output."); + +GTEST_DEFINE_int32_( + random_seed, + internal::Int32FromGTestEnv("random_seed", 0), + "Random number seed to use when shuffling test orders. Must be in range " + "[1, 99999], or 0 to use a seed based on the current time."); + +GTEST_DEFINE_int32_( + repeat, + internal::Int32FromGTestEnv("repeat", 1), + "How many times to repeat each test. Specify a negative number " + "for repeating forever. Useful for shaking out flaky tests."); + +GTEST_DEFINE_bool_( + show_internal_stack_frames, false, + "True iff " GTEST_NAME_ " should include internal stack frames when " + "printing test failure stack traces."); + +GTEST_DEFINE_bool_( + shuffle, + internal::BoolFromGTestEnv("shuffle", false), + "True iff " GTEST_NAME_ + " should randomize tests' order on every run."); + +GTEST_DEFINE_int32_( + stack_trace_depth, + internal::Int32FromGTestEnv("stack_trace_depth", kMaxStackTraceDepth), + "The maximum number of stack frames to print when an " + "assertion fails. The valid range is 0 through 100, inclusive."); + +GTEST_DEFINE_string_( + stream_result_to, + internal::StringFromGTestEnv("stream_result_to", ""), + "This flag specifies the host name and the port number on which to stream " + "test results. Example: \"localhost:555\". The flag is effective only on " + "Linux."); + +GTEST_DEFINE_bool_( + throw_on_failure, + internal::BoolFromGTestEnv("throw_on_failure", false), + "When this flag is specified, a failed assertion will throw an exception " + "if exceptions are enabled or exit the program with a non-zero code " + "otherwise."); + +namespace internal { + +// Generates a random number from [0, range), using a Linear +// Congruential Generator (LCG). Crashes if 'range' is 0 or greater +// than kMaxRange. +UInt32 Random::Generate(UInt32 range) { + // These constants are the same as are used in glibc's rand(3). + state_ = (1103515245U*state_ + 12345U) % kMaxRange; + + GTEST_CHECK_(range > 0) + << "Cannot generate a number in the range [0, 0)."; + GTEST_CHECK_(range <= kMaxRange) + << "Generation of a number in [0, " << range << ") was requested, " + << "but this can only generate numbers in [0, " << kMaxRange << ")."; + + // Converting via modulus introduces a bit of downward bias, but + // it's simple, and a linear congruential generator isn't too good + // to begin with. + return state_ % range; +} + +// GTestIsInitialized() returns true iff the user has initialized +// Google Test. Useful for catching the user mistake of not initializing +// Google Test before calling RUN_ALL_TESTS(). +// +// A user must call testing::InitGoogleTest() to initialize Google +// Test. g_init_gtest_count is set to the number of times +// InitGoogleTest() has been called. We don't protect this variable +// under a mutex as it is only accessed in the main thread. +GTEST_API_ int g_init_gtest_count = 0; +static bool GTestIsInitialized() { return g_init_gtest_count != 0; } + +// Iterates over a vector of TestCases, keeping a running sum of the +// results of calling a given int-returning method on each. +// Returns the sum. +static int SumOverTestCaseList(const std::vector& case_list, + int (TestCase::*method)() const) { + int sum = 0; + for (size_t i = 0; i < case_list.size(); i++) { + sum += (case_list[i]->*method)(); + } + return sum; +} + +// Returns true iff the test case passed. +static bool TestCasePassed(const TestCase* test_case) { + return test_case->should_run() && test_case->Passed(); +} + +// Returns true iff the test case failed. +static bool TestCaseFailed(const TestCase* test_case) { + return test_case->should_run() && test_case->Failed(); +} + +// Returns true iff test_case contains at least one test that should +// run. +static bool ShouldRunTestCase(const TestCase* test_case) { + return test_case->should_run(); +} + +// AssertHelper constructor. +AssertHelper::AssertHelper(TestPartResult::Type type, + const char* file, + int line, + const char* message) + : data_(new AssertHelperData(type, file, line, message)) { +} + +AssertHelper::~AssertHelper() { + delete data_; +} + +// Message assignment, for assertion streaming support. +void AssertHelper::operator=(const Message& message) const { + UnitTest::GetInstance()-> + AddTestPartResult(data_->type, data_->file, data_->line, + AppendUserMessage(data_->message, message), + UnitTest::GetInstance()->impl() + ->CurrentOsStackTraceExceptTop(1) + // Skips the stack frame for this function itself. + ); // NOLINT +} + +// Mutex for linked pointers. +GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex); + +// Application pathname gotten in InitGoogleTest. +std::string g_executable_path; + +// Returns the current application's name, removing directory path if that +// is present. +FilePath GetCurrentExecutableName() { + FilePath result; + +#if GTEST_OS_WINDOWS + result.Set(FilePath(g_executable_path).RemoveExtension("exe")); +#else + result.Set(FilePath(g_executable_path)); +#endif // GTEST_OS_WINDOWS + + return result.RemoveDirectoryName(); +} + +// Functions for processing the gtest_output flag. + +// Returns the output format, or "" for normal printed output. +std::string UnitTestOptions::GetOutputFormat() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + if (gtest_output_flag == NULL) return std::string(""); + + const char* const colon = strchr(gtest_output_flag, ':'); + return (colon == NULL) ? + std::string(gtest_output_flag) : + std::string(gtest_output_flag, colon - gtest_output_flag); +} + +// Returns the name of the requested output file, or the default if none +// was explicitly specified. +std::string UnitTestOptions::GetAbsolutePathToOutputFile() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + if (gtest_output_flag == NULL) + return ""; + + const char* const colon = strchr(gtest_output_flag, ':'); + if (colon == NULL) + return internal::FilePath::ConcatPaths( + internal::FilePath( + UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(kDefaultOutputFile)).string(); + + internal::FilePath output_name(colon + 1); + if (!output_name.IsAbsolutePath()) + // TODO(wan@google.com): on Windows \some\path is not an absolute + // path (as its meaning depends on the current drive), yet the + // following logic for turning it into an absolute path is wrong. + // Fix it. + output_name = internal::FilePath::ConcatPaths( + internal::FilePath(UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(colon + 1)); + + if (!output_name.IsDirectory()) + return output_name.string(); + + internal::FilePath result(internal::FilePath::GenerateUniqueFileName( + output_name, internal::GetCurrentExecutableName(), + GetOutputFormat().c_str())); + return result.string(); +} + +// Returns true iff the wildcard pattern matches the string. The +// first ':' or '\0' character in pattern marks the end of it. +// +// This recursive algorithm isn't very efficient, but is clear and +// works well enough for matching test names, which are short. +bool UnitTestOptions::PatternMatchesString(const char *pattern, + const char *str) { + switch (*pattern) { + case '\0': + case ':': // Either ':' or '\0' marks the end of the pattern. + return *str == '\0'; + case '?': // Matches any single character. + return *str != '\0' && PatternMatchesString(pattern + 1, str + 1); + case '*': // Matches any string (possibly empty) of characters. + return (*str != '\0' && PatternMatchesString(pattern, str + 1)) || + PatternMatchesString(pattern + 1, str); + default: // Non-special character. Matches itself. + return *pattern == *str && + PatternMatchesString(pattern + 1, str + 1); + } +} + +bool UnitTestOptions::MatchesFilter( + const std::string& name, const char* filter) { + const char *cur_pattern = filter; + for (;;) { + if (PatternMatchesString(cur_pattern, name.c_str())) { + return true; + } + + // Finds the next pattern in the filter. + cur_pattern = strchr(cur_pattern, ':'); + + // Returns if no more pattern can be found. + if (cur_pattern == NULL) { + return false; + } + + // Skips the pattern separater (the ':' character). + cur_pattern++; + } +} + +// Returns true iff the user-specified filter matches the test case +// name and the test name. +bool UnitTestOptions::FilterMatchesTest(const std::string &test_case_name, + const std::string &test_name) { + const std::string& full_name = test_case_name + "." + test_name.c_str(); + + // Split --gtest_filter at '-', if there is one, to separate into + // positive filter and negative filter portions + const char* const p = GTEST_FLAG(filter).c_str(); + const char* const dash = strchr(p, '-'); + std::string positive; + std::string negative; + if (dash == NULL) { + positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter + negative = ""; + } else { + positive = std::string(p, dash); // Everything up to the dash + negative = std::string(dash + 1); // Everything after the dash + if (positive.empty()) { + // Treat '-test1' as the same as '*-test1' + positive = kUniversalFilter; + } + } + + // A filter is a colon-separated list of patterns. It matches a + // test if any pattern in it matches the test. + return (MatchesFilter(full_name, positive.c_str()) && + !MatchesFilter(full_name, negative.c_str())); +} + +#if GTEST_HAS_SEH +// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the +// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. +// This function is useful as an __except condition. +int UnitTestOptions::GTestShouldProcessSEH(DWORD exception_code) { + // Google Test should handle a SEH exception if: + // 1. the user wants it to, AND + // 2. this is not a breakpoint exception, AND + // 3. this is not a C++ exception (VC++ implements them via SEH, + // apparently). + // + // SEH exception code for C++ exceptions. + // (see http://support.microsoft.com/kb/185294 for more information). + const DWORD kCxxExceptionCode = 0xe06d7363; + + bool should_handle = true; + + if (!GTEST_FLAG(catch_exceptions)) + should_handle = false; + else if (exception_code == EXCEPTION_BREAKPOINT) + should_handle = false; + else if (exception_code == kCxxExceptionCode) + should_handle = false; + + return should_handle ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; +} +#endif // GTEST_HAS_SEH + +} // namespace internal + +// The c'tor sets this object as the test part result reporter used by +// Google Test. The 'result' parameter specifies where to report the +// results. Intercepts only failures from the current thread. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + TestPartResultArray* result) + : intercept_mode_(INTERCEPT_ONLY_CURRENT_THREAD), + result_(result) { + Init(); +} + +// The c'tor sets this object as the test part result reporter used by +// Google Test. The 'result' parameter specifies where to report the +// results. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + InterceptMode intercept_mode, TestPartResultArray* result) + : intercept_mode_(intercept_mode), + result_(result) { + Init(); +} + +void ScopedFakeTestPartResultReporter::Init() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + if (intercept_mode_ == INTERCEPT_ALL_THREADS) { + old_reporter_ = impl->GetGlobalTestPartResultReporter(); + impl->SetGlobalTestPartResultReporter(this); + } else { + old_reporter_ = impl->GetTestPartResultReporterForCurrentThread(); + impl->SetTestPartResultReporterForCurrentThread(this); + } +} + +// The d'tor restores the test part result reporter used by Google Test +// before. +ScopedFakeTestPartResultReporter::~ScopedFakeTestPartResultReporter() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + if (intercept_mode_ == INTERCEPT_ALL_THREADS) { + impl->SetGlobalTestPartResultReporter(old_reporter_); + } else { + impl->SetTestPartResultReporterForCurrentThread(old_reporter_); + } +} + +// Increments the test part result count and remembers the result. +// This method is from the TestPartResultReporterInterface interface. +void ScopedFakeTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + result_->Append(result); +} + +namespace internal { + +// Returns the type ID of ::testing::Test. We should always call this +// instead of GetTypeId< ::testing::Test>() to get the type ID of +// testing::Test. This is to work around a suspected linker bug when +// using Google Test as a framework on Mac OS X. The bug causes +// GetTypeId< ::testing::Test>() to return different values depending +// on whether the call is from the Google Test framework itself or +// from user test code. GetTestTypeId() is guaranteed to always +// return the same value, as it always calls GetTypeId<>() from the +// gtest.cc, which is within the Google Test framework. +TypeId GetTestTypeId() { + return GetTypeId(); +} + +// The value of GetTestTypeId() as seen from within the Google Test +// library. This is solely for testing GetTestTypeId(). +extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId(); + +// This predicate-formatter checks that 'results' contains a test part +// failure of the given type and that the failure message contains the +// given substring. +AssertionResult HasOneFailure(const char* /* results_expr */, + const char* /* type_expr */, + const char* /* substr_expr */, + const TestPartResultArray& results, + TestPartResult::Type type, + const string& substr) { + const std::string expected(type == TestPartResult::kFatalFailure ? + "1 fatal failure" : + "1 non-fatal failure"); + Message msg; + if (results.size() != 1) { + msg << "Expected: " << expected << "\n" + << " Actual: " << results.size() << " failures"; + for (int i = 0; i < results.size(); i++) { + msg << "\n" << results.GetTestPartResult(i); + } + return AssertionFailure() << msg; + } + + const TestPartResult& r = results.GetTestPartResult(0); + if (r.type() != type) { + return AssertionFailure() << "Expected: " << expected << "\n" + << " Actual:\n" + << r; + } + + if (strstr(r.message(), substr.c_str()) == NULL) { + return AssertionFailure() << "Expected: " << expected << " containing \"" + << substr << "\"\n" + << " Actual:\n" + << r; + } + + return AssertionSuccess(); +} + +// The constructor of SingleFailureChecker remembers where to look up +// test part results, what type of failure we expect, and what +// substring the failure message should contain. +SingleFailureChecker:: SingleFailureChecker( + const TestPartResultArray* results, + TestPartResult::Type type, + const string& substr) + : results_(results), + type_(type), + substr_(substr) {} + +// The destructor of SingleFailureChecker verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +SingleFailureChecker::~SingleFailureChecker() { + EXPECT_PRED_FORMAT3(HasOneFailure, *results_, type_, substr_); +} + +DefaultGlobalTestPartResultReporter::DefaultGlobalTestPartResultReporter( + UnitTestImpl* unit_test) : unit_test_(unit_test) {} + +void DefaultGlobalTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + unit_test_->current_test_result()->AddTestPartResult(result); + unit_test_->listeners()->repeater()->OnTestPartResult(result); +} + +DefaultPerThreadTestPartResultReporter::DefaultPerThreadTestPartResultReporter( + UnitTestImpl* unit_test) : unit_test_(unit_test) {} + +void DefaultPerThreadTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + unit_test_->GetGlobalTestPartResultReporter()->ReportTestPartResult(result); +} + +// Returns the global test part result reporter. +TestPartResultReporterInterface* +UnitTestImpl::GetGlobalTestPartResultReporter() { + internal::MutexLock lock(&global_test_part_result_reporter_mutex_); + return global_test_part_result_repoter_; +} + +// Sets the global test part result reporter. +void UnitTestImpl::SetGlobalTestPartResultReporter( + TestPartResultReporterInterface* reporter) { + internal::MutexLock lock(&global_test_part_result_reporter_mutex_); + global_test_part_result_repoter_ = reporter; +} + +// Returns the test part result reporter for the current thread. +TestPartResultReporterInterface* +UnitTestImpl::GetTestPartResultReporterForCurrentThread() { + return per_thread_test_part_result_reporter_.get(); +} + +// Sets the test part result reporter for the current thread. +void UnitTestImpl::SetTestPartResultReporterForCurrentThread( + TestPartResultReporterInterface* reporter) { + per_thread_test_part_result_reporter_.set(reporter); +} + +// Gets the number of successful test cases. +int UnitTestImpl::successful_test_case_count() const { + return CountIf(test_cases_, TestCasePassed); +} + +// Gets the number of failed test cases. +int UnitTestImpl::failed_test_case_count() const { + return CountIf(test_cases_, TestCaseFailed); +} + +// Gets the number of all test cases. +int UnitTestImpl::total_test_case_count() const { + return static_cast(test_cases_.size()); +} + +// Gets the number of all test cases that contain at least one test +// that should run. +int UnitTestImpl::test_case_to_run_count() const { + return CountIf(test_cases_, ShouldRunTestCase); +} + +// Gets the number of successful tests. +int UnitTestImpl::successful_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::successful_test_count); +} + +// Gets the number of failed tests. +int UnitTestImpl::failed_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count); +} + +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTestImpl::reportable_disabled_test_count() const { + return SumOverTestCaseList(test_cases_, + &TestCase::reportable_disabled_test_count); +} + +// Gets the number of disabled tests. +int UnitTestImpl::disabled_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count); +} + +// Gets the number of tests to be printed in the XML report. +int UnitTestImpl::reportable_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::reportable_test_count); +} + +// Gets the number of all tests. +int UnitTestImpl::total_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::total_test_count); +} + +// Gets the number of tests that should run. +int UnitTestImpl::test_to_run_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count); +} + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// CurrentOsStackTraceExceptTop(1), Foo() will be included in the +// trace but Bar() and CurrentOsStackTraceExceptTop() won't. +std::string UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { + (void)skip_count; + return ""; +} + +// Returns the current time in milliseconds. +TimeInMillis GetTimeInMillis() { +#if GTEST_OS_WINDOWS_MOBILE || defined(__BORLANDC__) + // Difference between 1970-01-01 and 1601-01-01 in milliseconds. + // http://analogous.blogspot.com/2005/04/epoch.html + const TimeInMillis kJavaEpochToWinFileTimeDelta = + static_cast(116444736UL) * 100000UL; + const DWORD kTenthMicrosInMilliSecond = 10000; + + SYSTEMTIME now_systime; + FILETIME now_filetime; + ULARGE_INTEGER now_int64; + // TODO(kenton@google.com): Shouldn't this just use + // GetSystemTimeAsFileTime()? + GetSystemTime(&now_systime); + if (SystemTimeToFileTime(&now_systime, &now_filetime)) { + now_int64.LowPart = now_filetime.dwLowDateTime; + now_int64.HighPart = now_filetime.dwHighDateTime; + now_int64.QuadPart = (now_int64.QuadPart / kTenthMicrosInMilliSecond) - + kJavaEpochToWinFileTimeDelta; + return now_int64.QuadPart; + } + return 0; +#elif GTEST_OS_WINDOWS && !GTEST_HAS_GETTIMEOFDAY_ + __timeb64 now; + +# ifdef _MSC_VER + + // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996 + // (deprecated function) there. + // TODO(kenton@google.com): Use GetTickCount()? Or use + // SystemTimeToFileTime() +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4996) // Temporarily disables warning 4996. + _ftime64(&now); +# pragma warning(pop) // Restores the warning state. +# else + + _ftime64(&now); + +# endif // _MSC_VER + + return static_cast(now.time) * 1000 + now.millitm; +#elif GTEST_HAS_GETTIMEOFDAY_ + struct timeval now; + gettimeofday(&now, NULL); + return static_cast(now.tv_sec) * 1000 + now.tv_usec / 1000; +#else +# error "Don't know how to get the current time on your system." +#endif +} + +// Utilities + +// class String. + +#if GTEST_OS_WINDOWS_MOBILE +// Creates a UTF-16 wide string from the given ANSI string, allocating +// memory using new. The caller is responsible for deleting the return +// value using delete[]. Returns the wide string, or NULL if the +// input is NULL. +LPCWSTR String::AnsiToUtf16(const char* ansi) { + if (!ansi) return NULL; + const int length = strlen(ansi); + const int unicode_length = + MultiByteToWideChar(CP_ACP, 0, ansi, length, + NULL, 0); + WCHAR* unicode = new WCHAR[unicode_length + 1]; + MultiByteToWideChar(CP_ACP, 0, ansi, length, + unicode, unicode_length); + unicode[unicode_length] = 0; + return unicode; +} + +// Creates an ANSI string from the given wide string, allocating +// memory using new. The caller is responsible for deleting the return +// value using delete[]. Returns the ANSI string, or NULL if the +// input is NULL. +const char* String::Utf16ToAnsi(LPCWSTR utf16_str) { + if (!utf16_str) return NULL; + const int ansi_length = + WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, + NULL, 0, NULL, NULL); + char* ansi = new char[ansi_length + 1]; + WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, + ansi, ansi_length, NULL, NULL); + ansi[ansi_length] = 0; + return ansi; +} + +#endif // GTEST_OS_WINDOWS_MOBILE + +// Compares two C strings. Returns true iff they have the same content. +// +// Unlike strcmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CStringEquals(const char * lhs, const char * rhs) { + if ( lhs == NULL ) return rhs == NULL; + + if ( rhs == NULL ) return false; + + return strcmp(lhs, rhs) == 0; +} + +#if GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING + +// Converts an array of wide chars to a narrow string using the UTF-8 +// encoding, and streams the result to the given Message object. +static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length, + Message* msg) { + for (size_t i = 0; i != length; ) { // NOLINT + if (wstr[i] != L'\0') { + *msg << WideStringToUtf8(wstr + i, static_cast(length - i)); + while (i != length && wstr[i] != L'\0') + i++; + } else { + *msg << '\0'; + i++; + } + } +} + +#endif // GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING + +} // namespace internal + +// Constructs an empty Message. +// We allocate the stringstream separately because otherwise each use of +// ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's +// stack frame leading to huge stack frames in some cases; gcc does not reuse +// the stack space. +Message::Message() : ss_(new ::std::stringstream) { + // By default, we want there to be enough precision when printing + // a double to a Message. + *ss_ << std::setprecision(std::numeric_limits::digits10 + 2); +} + +// These two overloads allow streaming a wide C string to a Message +// using the UTF-8 encoding. +Message& Message::operator <<(const wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} +Message& Message::operator <<(wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} + +#if GTEST_HAS_STD_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::std::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +// Gets the text streamed to this object so far as an std::string. +// Each '\0' character in the buffer is replaced with "\\0". +std::string Message::GetString() const { + return internal::StringStreamToString(ss_.get()); +} + +// AssertionResult constructors. +// Used in EXPECT_TRUE/FALSE(assertion_result). +AssertionResult::AssertionResult(const AssertionResult& other) + : success_(other.success_), + message_(other.message_.get() != NULL ? + new ::std::string(*other.message_) : + static_cast< ::std::string*>(NULL)) { +} + +// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. +AssertionResult AssertionResult::operator!() const { + AssertionResult negation(!success_); + if (message_.get() != NULL) + negation << *message_; + return negation; +} + +// Makes a successful assertion result. +AssertionResult AssertionSuccess() { + return AssertionResult(true); +} + +// Makes a failed assertion result. +AssertionResult AssertionFailure() { + return AssertionResult(false); +} + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << message. +AssertionResult AssertionFailure(const Message& message) { + return AssertionFailure() << message; +} + +namespace internal { + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true iff the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const std::string& expected_value, + const std::string& actual_value, + bool ignoring_case) { + Message msg; + msg << "Value of: " << actual_expression; + if (actual_value != actual_expression) { + msg << "\n Actual: " << actual_value; + } + + msg << "\nExpected: " << expected_expression; + if (ignoring_case) { + msg << " (ignoring case)"; + } + if (expected_value != expected_expression) { + msg << "\nWhich is: " << expected_value; + } + + return AssertionFailure() << msg; +} + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value) { + const char* actual_message = assertion_result.message(); + Message msg; + msg << "Value of: " << expression_text + << "\n Actual: " << actual_predicate_value; + if (actual_message[0] != '\0') + msg << " (" << actual_message << ")"; + msg << "\nExpected: " << expected_predicate_value; + return msg.GetString(); +} + +// Helper function for implementing ASSERT_NEAR. +AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error) { + const double diff = fabs(val1 - val2); + if (diff <= abs_error) return AssertionSuccess(); + + // TODO(wan): do not print the value of an expression if it's + // already a literal. + return AssertionFailure() + << "The difference between " << expr1 << " and " << expr2 + << " is " << diff << ", which exceeds " << abs_error_expr << ", where\n" + << expr1 << " evaluates to " << val1 << ",\n" + << expr2 << " evaluates to " << val2 << ", and\n" + << abs_error_expr << " evaluates to " << abs_error << "."; +} + + +// Helper template for implementing FloatLE() and DoubleLE(). +template +AssertionResult FloatingPointLE(const char* expr1, + const char* expr2, + RawType val1, + RawType val2) { + // Returns success if val1 is less than val2, + if (val1 < val2) { + return AssertionSuccess(); + } + + // or if val1 is almost equal to val2. + const FloatingPoint lhs(val1), rhs(val2); + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + // Note that the above two checks will both fail if either val1 or + // val2 is NaN, as the IEEE floating-point standard requires that + // any predicate involving a NaN must return false. + + ::std::stringstream val1_ss; + val1_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << val1; + + ::std::stringstream val2_ss; + val2_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << val2; + + return AssertionFailure() + << "Expected: (" << expr1 << ") <= (" << expr2 << ")\n" + << " Actual: " << StringStreamToString(&val1_ss) << " vs " + << StringStreamToString(&val2_ss); +} + +} // namespace internal + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2) { + return internal::FloatingPointLE(expr1, expr2, val1, val2); +} + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2) { + return internal::FloatingPointLE(expr1, expr2, val1, val2); +} + +namespace internal { + +// The helper function for {ASSERT|EXPECT}_EQ with int or enum +// arguments. +AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual) { + if (expected == actual) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + FormatForComparisonFailureMessage(expected, actual), + FormatForComparisonFailureMessage(actual, expected), + false); +} + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_?? with integer or enum arguments. It is here +// just to avoid copy-and-paste of similar code. +#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + BiggestInt val1, BiggestInt val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + return AssertionFailure() \ + << "Expected: (" << expr1 << ") " #op " (" << expr2\ + << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ + << " vs " << FormatForComparisonFailureMessage(val2, val1);\ + }\ +} + +// Implements the helper function for {ASSERT|EXPECT}_NE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(NE, !=) +// Implements the helper function for {ASSERT|EXPECT}_LE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(LE, <=) +// Implements the helper function for {ASSERT|EXPECT}_LT with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(LT, < ) +// Implements the helper function for {ASSERT|EXPECT}_GE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(GE, >=) +// Implements the helper function for {ASSERT|EXPECT}_GT with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(GT, > ) + +#undef GTEST_IMPL_CMP_HELPER_ + +// The helper function for {ASSERT|EXPECT}_STREQ. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual) { + if (String::CStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + PrintToString(expected), + PrintToString(actual), + false); +} + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual) { + if (String::CaseInsensitiveCStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + PrintToString(expected), + PrintToString(actual), + true); +} + +// The helper function for {ASSERT|EXPECT}_STRNE. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + } +} + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CaseInsensitiveCStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + return AssertionFailure() + << "Expected: (" << s1_expression << ") != (" + << s2_expression << ") (ignoring case), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + } +} + +} // namespace internal + +namespace { + +// Helper functions for implementing IsSubString() and IsNotSubstring(). + +// This group of overloaded functions return true iff needle is a +// substring of haystack. NULL is considered a substring of itself +// only. + +bool IsSubstringPred(const char* needle, const char* haystack) { + if (needle == NULL || haystack == NULL) + return needle == haystack; + + return strstr(haystack, needle) != NULL; +} + +bool IsSubstringPred(const wchar_t* needle, const wchar_t* haystack) { + if (needle == NULL || haystack == NULL) + return needle == haystack; + + return wcsstr(haystack, needle) != NULL; +} + +// StringType here can be either ::std::string or ::std::wstring. +template +bool IsSubstringPred(const StringType& needle, + const StringType& haystack) { + return haystack.find(needle) != StringType::npos; +} + +// This function implements either IsSubstring() or IsNotSubstring(), +// depending on the value of the expected_to_be_substring parameter. +// StringType here can be const char*, const wchar_t*, ::std::string, +// or ::std::wstring. +template +AssertionResult IsSubstringImpl( + bool expected_to_be_substring, + const char* needle_expr, const char* haystack_expr, + const StringType& needle, const StringType& haystack) { + if (IsSubstringPred(needle, haystack) == expected_to_be_substring) + return AssertionSuccess(); + + const bool is_wide_string = sizeof(needle[0]) > 1; + const char* const begin_string_quote = is_wide_string ? "L\"" : "\""; + return AssertionFailure() + << "Value of: " << needle_expr << "\n" + << " Actual: " << begin_string_quote << needle << "\"\n" + << "Expected: " << (expected_to_be_substring ? "" : "not ") + << "a substring of " << haystack_expr << "\n" + << "Which is: " << begin_string_quote << haystack << "\""; +} + +} // namespace + +// IsSubstring() and IsNotSubstring() check whether needle is a +// substring of haystack (NULL is considered a substring of itself +// only), and return an appropriate error message when they fail. + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +#if GTEST_HAS_STD_WSTRING +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +#if GTEST_OS_WINDOWS + +namespace { + +// Helper function for IsHRESULT{SuccessFailure} predicates +AssertionResult HRESULTFailureHelper(const char* expr, + const char* expected, + long hr) { // NOLINT +# if GTEST_OS_WINDOWS_MOBILE + + // Windows CE doesn't support FormatMessage. + const char error_text[] = ""; + +# else + + // Looks up the human-readable system message for the HRESULT code + // and since we're not passing any params to FormatMessage, we don't + // want inserts expanded. + const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS; + const DWORD kBufSize = 4096; + // Gets the system's human readable message string for this HRESULT. + char error_text[kBufSize] = { '\0' }; + DWORD message_length = ::FormatMessageA(kFlags, + 0, // no source, we're asking system + hr, // the error + 0, // no line width restrictions + error_text, // output buffer + kBufSize, // buf size + NULL); // no arguments for inserts + // Trims tailing white space (FormatMessage leaves a trailing CR-LF) + for (; message_length && IsSpace(error_text[message_length - 1]); + --message_length) { + error_text[message_length - 1] = '\0'; + } + +# endif // GTEST_OS_WINDOWS_MOBILE + + const std::string error_hex("0x" + String::FormatHexInt(hr)); + return ::testing::AssertionFailure() + << "Expected: " << expr << " " << expected << ".\n" + << " Actual: " << error_hex << " " << error_text << "\n"; +} + +} // namespace + +AssertionResult IsHRESULTSuccess(const char* expr, long hr) { // NOLINT + if (SUCCEEDED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "succeeds", hr); +} + +AssertionResult IsHRESULTFailure(const char* expr, long hr) { // NOLINT + if (FAILED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "fails", hr); +} + +#endif // GTEST_OS_WINDOWS + +// Utility functions for encoding Unicode text (wide strings) in +// UTF-8. + +// A Unicode code-point can have upto 21 bits, and is encoded in UTF-8 +// like this: +// +// Code-point length Encoding +// 0 - 7 bits 0xxxxxxx +// 8 - 11 bits 110xxxxx 10xxxxxx +// 12 - 16 bits 1110xxxx 10xxxxxx 10xxxxxx +// 17 - 21 bits 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + +// The maximum code-point a one-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint1 = (static_cast(1) << 7) - 1; + +// The maximum code-point a two-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint2 = (static_cast(1) << (5 + 6)) - 1; + +// The maximum code-point a three-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint3 = (static_cast(1) << (4 + 2*6)) - 1; + +// The maximum code-point a four-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint4 = (static_cast(1) << (3 + 3*6)) - 1; + +// Chops off the n lowest bits from a bit pattern. Returns the n +// lowest bits. As a side effect, the original bit pattern will be +// shifted to the right by n bits. +inline UInt32 ChopLowBits(UInt32* bits, int n) { + const UInt32 low_bits = *bits & ((static_cast(1) << n) - 1); + *bits >>= n; + return low_bits; +} + +// Converts a Unicode code point to a narrow string in UTF-8 encoding. +// code_point parameter is of type UInt32 because wchar_t may not be +// wide enough to contain a code point. +// If the code_point is not a valid Unicode code point +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +std::string CodePointToUtf8(UInt32 code_point) { + if (code_point > kMaxCodePoint4) { + return "(Invalid Unicode 0x" + String::FormatHexInt(code_point) + ")"; + } + + char str[5]; // Big enough for the largest valid code point. + if (code_point <= kMaxCodePoint1) { + str[1] = '\0'; + str[0] = static_cast(code_point); // 0xxxxxxx + } else if (code_point <= kMaxCodePoint2) { + str[2] = '\0'; + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xC0 | code_point); // 110xxxxx + } else if (code_point <= kMaxCodePoint3) { + str[3] = '\0'; + str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xE0 | code_point); // 1110xxxx + } else { // code_point <= kMaxCodePoint4 + str[4] = '\0'; + str[3] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xF0 | code_point); // 11110xxx + } + return str; +} + +// The following two functions only make sense if the the system +// uses UTF-16 for wide string encoding. All supported systems +// with 16 bit wchar_t (Windows, Cygwin, Symbian OS) do use UTF-16. + +// Determines if the arguments constitute UTF-16 surrogate pair +// and thus should be combined into a single Unicode code point +// using CreateCodePointFromUtf16SurrogatePair. +inline bool IsUtf16SurrogatePair(wchar_t first, wchar_t second) { + return sizeof(wchar_t) == 2 && + (first & 0xFC00) == 0xD800 && (second & 0xFC00) == 0xDC00; +} + +// Creates a Unicode code point from UTF16 surrogate pair. +inline UInt32 CreateCodePointFromUtf16SurrogatePair(wchar_t first, + wchar_t second) { + const UInt32 mask = (1 << 10) - 1; + return (sizeof(wchar_t) == 2) ? + (((first & mask) << 10) | (second & mask)) + 0x10000 : + // This function should not be called when the condition is + // false, but we provide a sensible default in case it is. + static_cast(first); +} + +// Converts a wide string to a narrow string in UTF-8 encoding. +// The wide string is assumed to have the following encoding: +// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS) +// UTF-32 if sizeof(wchar_t) == 4 (on Linux) +// Parameter str points to a null-terminated wide string. +// Parameter num_chars may additionally limit the number +// of wchar_t characters processed. -1 is used when the entire string +// should be processed. +// If the string contains code points that are not valid Unicode code points +// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding +// and contains invalid UTF-16 surrogate pairs, values in those pairs +// will be encoded as individual Unicode characters from Basic Normal Plane. +std::string WideStringToUtf8(const wchar_t* str, int num_chars) { + if (num_chars == -1) + num_chars = static_cast(wcslen(str)); + + ::std::stringstream stream; + for (int i = 0; i < num_chars; ++i) { + UInt32 unicode_code_point; + + if (str[i] == L'\0') { + break; + } else if (i + 1 < num_chars && IsUtf16SurrogatePair(str[i], str[i + 1])) { + unicode_code_point = CreateCodePointFromUtf16SurrogatePair(str[i], + str[i + 1]); + i++; + } else { + unicode_code_point = static_cast(str[i]); + } + + stream << CodePointToUtf8(unicode_code_point); + } + return StringStreamToString(&stream); +} + +// Converts a wide C string to an std::string using the UTF-8 encoding. +// NULL will be converted to "(null)". +std::string String::ShowWideCString(const wchar_t * wide_c_str) { + if (wide_c_str == NULL) return "(null)"; + + return internal::WideStringToUtf8(wide_c_str, -1); +} + +// Compares two wide C strings. Returns true iff they have the same +// content. +// +// Unlike wcscmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::WideCStringEquals(const wchar_t * lhs, const wchar_t * rhs) { + if (lhs == NULL) return rhs == NULL; + + if (rhs == NULL) return false; + + return wcscmp(lhs, rhs) == 0; +} + +// Helper function for *_STREQ on wide strings. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const wchar_t* expected, + const wchar_t* actual) { + if (String::WideCStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + PrintToString(expected), + PrintToString(actual), + false); +} + +// Helper function for *_STRNE on wide strings. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2) { + if (!String::WideCStringEquals(s1, s2)) { + return AssertionSuccess(); + } + + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: " + << PrintToString(s1) + << " vs " << PrintToString(s2); +} + +// Compares two C strings, ignoring case. Returns true iff they have +// the same content. +// +// Unlike strcasecmp(), this function can handle NULL argument(s). A +// NULL C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CaseInsensitiveCStringEquals(const char * lhs, const char * rhs) { + if (lhs == NULL) + return rhs == NULL; + if (rhs == NULL) + return false; + return posix::StrCaseCmp(lhs, rhs) == 0; +} + + // Compares two wide C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. +bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs) { + if (lhs == NULL) return rhs == NULL; + + if (rhs == NULL) return false; + +#if GTEST_OS_WINDOWS + return _wcsicmp(lhs, rhs) == 0; +#elif GTEST_OS_LINUX && !GTEST_OS_LINUX_ANDROID + return wcscasecmp(lhs, rhs) == 0; +#else + // Android, Mac OS X and Cygwin don't define wcscasecmp. + // Other unknown OSes may not define it either. + wint_t left, right; + do { + left = towlower(*lhs++); + right = towlower(*rhs++); + } while (left && left == right); + return left == right; +#endif // OS selector +} + +// Returns true iff str ends with the given suffix, ignoring case. +// Any string is considered to end with an empty suffix. +bool String::EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix) { + const size_t str_len = str.length(); + const size_t suffix_len = suffix.length(); + return (str_len >= suffix_len) && + CaseInsensitiveCStringEquals(str.c_str() + str_len - suffix_len, + suffix.c_str()); +} + +// Formats an int value as "%02d". +std::string String::FormatIntWidth2(int value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << value; + return ss.str(); +} + +// Formats an int value as "%X". +std::string String::FormatHexInt(int value) { + std::stringstream ss; + ss << std::hex << std::uppercase << value; + return ss.str(); +} + +// Formats a byte as "%02X". +std::string String::FormatByte(unsigned char value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << std::hex << std::uppercase + << static_cast(value); + return ss.str(); +} + +// Converts the buffer in a stringstream to an std::string, converting NUL +// bytes to "\\0" along the way. +std::string StringStreamToString(::std::stringstream* ss) { + const ::std::string& str = ss->str(); + const char* const start = str.c_str(); + const char* const end = start + str.length(); + + std::string result; + result.reserve(2 * (end - start)); + for (const char* ch = start; ch != end; ++ch) { + if (*ch == '\0') { + result += "\\0"; // Replaces NUL with "\\0"; + } else { + result += *ch; + } + } + + return result; +} + +// Appends the user-supplied message to the Google-Test-generated message. +std::string AppendUserMessage(const std::string& gtest_msg, + const Message& user_msg) { + // Appends the user message if it's non-empty. + const std::string user_msg_string = user_msg.GetString(); + if (user_msg_string.empty()) { + return gtest_msg; + } + + return gtest_msg + "\n" + user_msg_string; +} + +} // namespace internal + +// class TestResult + +// Creates an empty TestResult. +TestResult::TestResult() + : death_test_count_(0), + elapsed_time_(0) { +} + +// D'tor. +TestResult::~TestResult() { +} + +// Returns the i-th test part result among all the results. i can +// range from 0 to total_part_count() - 1. If i is not in that range, +// aborts the program. +const TestPartResult& TestResult::GetTestPartResult(int i) const { + if (i < 0 || i >= total_part_count()) + internal::posix::Abort(); + return test_part_results_.at(i); +} + +// Returns the i-th test property. i can range from 0 to +// test_property_count() - 1. If i is not in that range, aborts the +// program. +const TestProperty& TestResult::GetTestProperty(int i) const { + if (i < 0 || i >= test_property_count()) + internal::posix::Abort(); + return test_properties_.at(i); +} + +// Clears the test part results. +void TestResult::ClearTestPartResults() { + test_part_results_.clear(); +} + +// Adds a test part result to the list. +void TestResult::AddTestPartResult(const TestPartResult& test_part_result) { + test_part_results_.push_back(test_part_result); +} + +// Adds a test property to the list. If a property with the same key as the +// supplied property is already represented, the value of this test_property +// replaces the old value for that key. +void TestResult::RecordProperty(const std::string& xml_element, + const TestProperty& test_property) { + if (!ValidateTestProperty(xml_element, test_property)) { + return; + } + internal::MutexLock lock(&test_properites_mutex_); + const std::vector::iterator property_with_matching_key = + std::find_if(test_properties_.begin(), test_properties_.end(), + internal::TestPropertyKeyIs(test_property.key())); + if (property_with_matching_key == test_properties_.end()) { + test_properties_.push_back(test_property); + return; + } + property_with_matching_key->SetValue(test_property.value()); +} + +// The list of reserved attributes used in the element of XML +// output. +static const char* const kReservedTestSuitesAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "random_seed", + "tests", + "time", + "timestamp" +}; + +// The list of reserved attributes used in the element of XML +// output. +static const char* const kReservedTestSuiteAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "tests", + "time" +}; + +// The list of reserved attributes used in the element of XML output. +static const char* const kReservedTestCaseAttributes[] = { + "classname", + "name", + "status", + "time", + "type_param", + "value_param" +}; + +template +std::vector ArrayAsVector(const char* const (&array)[kSize]) { + return std::vector(array, array + kSize); +} + +static std::vector GetReservedAttributesForElement( + const std::string& xml_element) { + if (xml_element == "testsuites") { + return ArrayAsVector(kReservedTestSuitesAttributes); + } else if (xml_element == "testsuite") { + return ArrayAsVector(kReservedTestSuiteAttributes); + } else if (xml_element == "testcase") { + return ArrayAsVector(kReservedTestCaseAttributes); + } else { + GTEST_CHECK_(false) << "Unrecognized xml_element provided: " << xml_element; + } + // This code is unreachable but some compilers may not realizes that. + return std::vector(); +} + +static std::string FormatWordList(const std::vector& words) { + Message word_list; + for (size_t i = 0; i < words.size(); ++i) { + if (i > 0 && words.size() > 2) { + word_list << ", "; + } + if (i == words.size() - 1) { + word_list << "and "; + } + word_list << "'" << words[i] << "'"; + } + return word_list.GetString(); +} + +bool ValidateTestPropertyName(const std::string& property_name, + const std::vector& reserved_names) { + if (std::find(reserved_names.begin(), reserved_names.end(), property_name) != + reserved_names.end()) { + ADD_FAILURE() << "Reserved key used in RecordProperty(): " << property_name + << " (" << FormatWordList(reserved_names) + << " are reserved by " << GTEST_NAME_ << ")"; + return false; + } + return true; +} + +// Adds a failure if the key is a reserved attribute of the element named +// xml_element. Returns true if the property is valid. +bool TestResult::ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property) { + return ValidateTestPropertyName(test_property.key(), + GetReservedAttributesForElement(xml_element)); +} + +// Clears the object. +void TestResult::Clear() { + test_part_results_.clear(); + test_properties_.clear(); + death_test_count_ = 0; + elapsed_time_ = 0; +} + +// Returns true iff the test failed. +bool TestResult::Failed() const { + for (int i = 0; i < total_part_count(); ++i) { + if (GetTestPartResult(i).failed()) + return true; + } + return false; +} + +// Returns true iff the test part fatally failed. +static bool TestPartFatallyFailed(const TestPartResult& result) { + return result.fatally_failed(); +} + +// Returns true iff the test fatally failed. +bool TestResult::HasFatalFailure() const { + return CountIf(test_part_results_, TestPartFatallyFailed) > 0; +} + +// Returns true iff the test part non-fatally failed. +static bool TestPartNonfatallyFailed(const TestPartResult& result) { + return result.nonfatally_failed(); +} + +// Returns true iff the test has a non-fatal failure. +bool TestResult::HasNonfatalFailure() const { + return CountIf(test_part_results_, TestPartNonfatallyFailed) > 0; +} + +// Gets the number of all test parts. This is the sum of the number +// of successful test parts and the number of failed test parts. +int TestResult::total_part_count() const { + return static_cast(test_part_results_.size()); +} + +// Returns the number of the test properties. +int TestResult::test_property_count() const { + return static_cast(test_properties_.size()); +} + +// class Test + +// Creates a Test object. + +// The c'tor saves the values of all Google Test flags. +Test::Test() + : gtest_flag_saver_(new internal::GTestFlagSaver) { +} + +// The d'tor restores the values of all Google Test flags. +Test::~Test() { + delete gtest_flag_saver_; +} + +// Sets up the test fixture. +// +// A sub-class may override this. +void Test::SetUp() { +} + +// Tears down the test fixture. +// +// A sub-class may override this. +void Test::TearDown() { +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const std::string& key, const std::string& value) { + UnitTest::GetInstance()->RecordProperty(key, value); +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const std::string& key, int value) { + Message value_message; + value_message << value; + RecordProperty(key, value_message.GetString().c_str()); +} + +namespace internal { + +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message) { + // This function is a friend of UnitTest and as such has access to + // AddTestPartResult. + UnitTest::GetInstance()->AddTestPartResult( + result_type, + NULL, // No info about the source file where the exception occurred. + -1, // We have no info on which line caused the exception. + message, + ""); // No stack trace, either. +} + +} // namespace internal + +// Google Test requires all tests in the same test case to use the same test +// fixture class. This function checks if the current test has the +// same fixture class as the first test in the current test case. If +// yes, it returns true; otherwise it generates a Google Test failure and +// returns false. +bool Test::HasSameFixtureClass() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + const TestCase* const test_case = impl->current_test_case(); + + // Info about the first test in the current test case. + const TestInfo* const first_test_info = test_case->test_info_list()[0]; + const internal::TypeId first_fixture_id = first_test_info->fixture_class_id_; + const char* const first_test_name = first_test_info->name(); + + // Info about the current test. + const TestInfo* const this_test_info = impl->current_test_info(); + const internal::TypeId this_fixture_id = this_test_info->fixture_class_id_; + const char* const this_test_name = this_test_info->name(); + + if (this_fixture_id != first_fixture_id) { + // Is the first test defined using TEST? + const bool first_is_TEST = first_fixture_id == internal::GetTestTypeId(); + // Is this test defined using TEST? + const bool this_is_TEST = this_fixture_id == internal::GetTestTypeId(); + + if (first_is_TEST || this_is_TEST) { + // The user mixed TEST and TEST_F in this test case - we'll tell + // him/her how to fix it. + + // Gets the name of the TEST and the name of the TEST_F. Note + // that first_is_TEST and this_is_TEST cannot both be true, as + // the fixture IDs are different for the two tests. + const char* const TEST_name = + first_is_TEST ? first_test_name : this_test_name; + const char* const TEST_F_name = + first_is_TEST ? this_test_name : first_test_name; + + ADD_FAILURE() + << "All tests in the same test case must use the same test fixture\n" + << "class, so mixing TEST_F and TEST in the same test case is\n" + << "illegal. In test case " << this_test_info->test_case_name() + << ",\n" + << "test " << TEST_F_name << " is defined using TEST_F but\n" + << "test " << TEST_name << " is defined using TEST. You probably\n" + << "want to change the TEST to TEST_F or move it to another test\n" + << "case."; + } else { + // The user defined two fixture classes with the same name in + // two namespaces - we'll tell him/her how to fix it. + ADD_FAILURE() + << "All tests in the same test case must use the same test fixture\n" + << "class. However, in test case " + << this_test_info->test_case_name() << ",\n" + << "you defined test " << first_test_name + << " and test " << this_test_name << "\n" + << "using two different test fixture classes. This can happen if\n" + << "the two classes are from different namespaces or translation\n" + << "units and have the same name. You should probably rename one\n" + << "of the classes to put the tests into different test cases."; + } + return false; + } + + return true; +} + +#if GTEST_HAS_SEH + +// Adds an "exception thrown" fatal failure to the current test. This +// function returns its result via an output parameter pointer because VC++ +// prohibits creation of objects with destructors on stack in functions +// using __try (see error C2712). +static std::string* FormatSehExceptionMessage(DWORD exception_code, + const char* location) { + Message message; + message << "SEH exception with code 0x" << std::setbase(16) << + exception_code << std::setbase(10) << " thrown in " << location << "."; + + return new std::string(message.GetString()); +} + +#endif // GTEST_HAS_SEH + +namespace internal { + +#if GTEST_HAS_EXCEPTIONS + +// Adds an "exception thrown" fatal failure to the current test. +static std::string FormatCxxExceptionMessage(const char* description, + const char* location) { + Message message; + if (description != NULL) { + message << "C++ exception with description \"" << description << "\""; + } else { + message << "Unknown C++ exception"; + } + message << " thrown in " << location << "."; + + return message.GetString(); +} + +static std::string PrintTestPartResultToString( + const TestPartResult& test_part_result); + +GoogleTestFailureException::GoogleTestFailureException( + const TestPartResult& failure) + : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} + +#endif // GTEST_HAS_EXCEPTIONS + +// We put these helper functions in the internal namespace as IBM's xlC +// compiler rejects the code if they were declared static. + +// Runs the given method and handles SEH exceptions it throws, when +// SEH is supported; returns the 0-value for type Result in case of an +// SEH exception. (Microsoft compilers cannot handle SEH and C++ +// exceptions in the same function. Therefore, we provide a separate +// wrapper function for handling SEH exceptions.) +template +Result HandleSehExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { +#if GTEST_HAS_SEH + __try { + return (object->*method)(); + } __except (internal::UnitTestOptions::GTestShouldProcessSEH( // NOLINT + GetExceptionCode())) { + // We create the exception message on the heap because VC++ prohibits + // creation of objects with destructors on stack in functions using __try + // (see error C2712). + std::string* exception_message = FormatSehExceptionMessage( + GetExceptionCode(), location); + internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure, + *exception_message); + delete exception_message; + return static_cast(0); + } +#else + (void)location; + return (object->*method)(); +#endif // GTEST_HAS_SEH +} + +// Runs the given method and catches and reports C++ and/or SEH-style +// exceptions, if they are supported; returns the 0-value for type +// Result in case of an SEH exception. +template +Result HandleExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { + // NOTE: The user code can affect the way in which Google Test handles + // exceptions by setting GTEST_FLAG(catch_exceptions), but only before + // RUN_ALL_TESTS() starts. It is technically possible to check the flag + // after the exception is caught and either report or re-throw the + // exception based on the flag's value: + // + // try { + // // Perform the test method. + // } catch (...) { + // if (GTEST_FLAG(catch_exceptions)) + // // Report the exception as failure. + // else + // throw; // Re-throws the original exception. + // } + // + // However, the purpose of this flag is to allow the program to drop into + // the debugger when the exception is thrown. On most platforms, once the + // control enters the catch block, the exception origin information is + // lost and the debugger will stop the program at the point of the + // re-throw in this function -- instead of at the point of the original + // throw statement in the code under test. For this reason, we perform + // the check early, sacrificing the ability to affect Google Test's + // exception handling in the method where the exception is thrown. + if (internal::GetUnitTestImpl()->catch_exceptions()) { +#if GTEST_HAS_EXCEPTIONS + try { + return HandleSehExceptionsInMethodIfSupported(object, method, location); + } catch (const internal::GoogleTestFailureException&) { // NOLINT + // This exception type can only be thrown by a failed Google + // Test assertion with the intention of letting another testing + // framework catch it. Therefore we just re-throw it. + throw; + } catch (const std::exception& e) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(e.what(), location)); + } catch (...) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(NULL, location)); + } + return static_cast(0); +#else + return HandleSehExceptionsInMethodIfSupported(object, method, location); +#endif // GTEST_HAS_EXCEPTIONS + } else { + return (object->*method)(); + } +} + +} // namespace internal + +// Runs the test and updates the test result. +void Test::Run() { + if (!HasSameFixtureClass()) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()"); + // We will run the test only if SetUp() was successful. + if (!HasFatalFailure()) { + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TestBody, "the test body"); + } + + // However, we want to clean up as much as possible. Hence we will + // always call TearDown(), even if SetUp() or the test body has + // failed. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TearDown, "TearDown()"); +} + +// Returns true iff the current test has a fatal failure. +bool Test::HasFatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()->HasFatalFailure(); +} + +// Returns true iff the current test has a non-fatal failure. +bool Test::HasNonfatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()-> + HasNonfatalFailure(); +} + +// class TestInfo + +// Constructs a TestInfo object. It assumes ownership of the test factory +// object. +TestInfo::TestInfo(const std::string& a_test_case_name, + const std::string& a_name, + const char* a_type_param, + const char* a_value_param, + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory) + : test_case_name_(a_test_case_name), + name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : NULL), + value_param_(a_value_param ? new std::string(a_value_param) : NULL), + fixture_class_id_(fixture_class_id), + should_run_(false), + is_disabled_(false), + matches_filter_(false), + factory_(factory), + result_() {} + +// Destructs a TestInfo object. +TestInfo::~TestInfo() { delete factory_; } + +namespace internal { + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_case_name: name of the test case +// name: name of the test +// type_param: the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param: text representation of the test's value parameter, +// or NULL if this is not a value-parameterized test. +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +TestInfo* MakeAndRegisterTestInfo( + const char* test_case_name, + const char* name, + const char* type_param, + const char* value_param, + TypeId fixture_class_id, + SetUpTestCaseFunc set_up_tc, + TearDownTestCaseFunc tear_down_tc, + TestFactoryBase* factory) { + TestInfo* const test_info = + new TestInfo(test_case_name, name, type_param, value_param, + fixture_class_id, factory); + GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info); + return test_info; +} + +#if GTEST_HAS_PARAM_TEST +void ReportInvalidTestCaseType(const char* test_case_name, + const char* file, int line) { + Message errors; + errors + << "Attempted redefinition of test case " << test_case_name << ".\n" + << "All tests in the same test case must use the same test fixture\n" + << "class. However, in test case " << test_case_name << ", you tried\n" + << "to define a test using a fixture class different from the one\n" + << "used earlier. This can happen if the two fixture classes are\n" + << "from different namespaces and have the same name. You should\n" + << "probably rename one of the classes to put the tests into different\n" + << "test cases."; + + fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), + errors.GetString().c_str()); +} +#endif // GTEST_HAS_PARAM_TEST + +} // namespace internal + +namespace { + +// A predicate that checks the test name of a TestInfo against a known +// value. +// +// This is used for implementation of the TestCase class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestNameIs is copyable. +class TestNameIs { + public: + // Constructor. + // + // TestNameIs has NO default constructor. + explicit TestNameIs(const char* name) + : name_(name) {} + + // Returns true iff the test name of test_info matches name_. + bool operator()(const TestInfo * test_info) const { + return test_info && test_info->name() == name_; + } + + private: + std::string name_; +}; + +} // namespace + +namespace internal { + +// This method expands all parameterized tests registered with macros TEST_P +// and INSTANTIATE_TEST_CASE_P into regular tests and registers those. +// This will be done just once during the program runtime. +void UnitTestImpl::RegisterParameterizedTests() { +#if GTEST_HAS_PARAM_TEST + if (!parameterized_tests_registered_) { + parameterized_test_registry_.RegisterTests(); + parameterized_tests_registered_ = true; + } +#endif +} + +} // namespace internal + +// Creates the test object, runs it, records its result, and then +// deletes it. +void TestInfo::Run() { + if (!should_run_) return; + + // Tells UnitTest where to store test result. + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_info(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + // Notifies the unit test event listeners that a test is about to start. + repeater->OnTestStart(*this); + + const TimeInMillis start = internal::GetTimeInMillis(); + + impl->os_stack_trace_getter()->UponLeavingGTest(); + + // Creates the test object. + Test* const test = internal::HandleExceptionsInMethodIfSupported( + factory_, &internal::TestFactoryBase::CreateTest, + "the test fixture's constructor"); + + // Runs the test only if the test object was created and its + // constructor didn't generate a fatal failure. + if ((test != NULL) && !Test::HasFatalFailure()) { + // This doesn't throw as all user code that can throw are wrapped into + // exception handling code. + test->Run(); + } + + // Deletes the test object. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + test, &Test::DeleteSelf_, "the test fixture's destructor"); + + result_.set_elapsed_time(internal::GetTimeInMillis() - start); + + // Notifies the unit test event listener that a test has just finished. + repeater->OnTestEnd(*this); + + // Tells UnitTest to stop associating assertion results to this + // test. + impl->set_current_test_info(NULL); +} + +// class TestCase + +// Gets the number of successful tests in this test case. +int TestCase::successful_test_count() const { + return CountIf(test_info_list_, TestPassed); +} + +// Gets the number of failed tests in this test case. +int TestCase::failed_test_count() const { + return CountIf(test_info_list_, TestFailed); +} + +// Gets the number of disabled tests that will be reported in the XML report. +int TestCase::reportable_disabled_test_count() const { + return CountIf(test_info_list_, TestReportableDisabled); +} + +// Gets the number of disabled tests in this test case. +int TestCase::disabled_test_count() const { + return CountIf(test_info_list_, TestDisabled); +} + +// Gets the number of tests to be printed in the XML report. +int TestCase::reportable_test_count() const { + return CountIf(test_info_list_, TestReportable); +} + +// Get the number of tests in this test case that should run. +int TestCase::test_to_run_count() const { + return CountIf(test_info_list_, ShouldRunTest); +} + +// Gets the number of all tests. +int TestCase::total_test_count() const { + return static_cast(test_info_list_.size()); +} + +// Creates a TestCase with the given name. +// +// Arguments: +// +// name: name of the test case +// a_type_param: the name of the test case's type parameter, or NULL if +// this is not a typed or a type-parameterized test case. +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +TestCase::TestCase(const char* a_name, const char* a_type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc) + : name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : NULL), + set_up_tc_(set_up_tc), + tear_down_tc_(tear_down_tc), + should_run_(false), + elapsed_time_(0) { +} + +// Destructor of TestCase. +TestCase::~TestCase() { + // Deletes every Test in the collection. + ForEach(test_info_list_, internal::Delete); +} + +// Returns the i-th test among all the tests. i can range from 0 to +// total_test_count() - 1. If i is not in that range, returns NULL. +const TestInfo* TestCase::GetTestInfo(int i) const { + const int index = GetElementOr(test_indices_, i, -1); + return index < 0 ? NULL : test_info_list_[index]; +} + +// Returns the i-th test among all the tests. i can range from 0 to +// total_test_count() - 1. If i is not in that range, returns NULL. +TestInfo* TestCase::GetMutableTestInfo(int i) { + const int index = GetElementOr(test_indices_, i, -1); + return index < 0 ? NULL : test_info_list_[index]; +} + +// Adds a test to this test case. Will delete the test upon +// destruction of the TestCase object. +void TestCase::AddTestInfo(TestInfo * test_info) { + test_info_list_.push_back(test_info); + test_indices_.push_back(static_cast(test_indices_.size())); +} + +// Runs every test in this TestCase. +void TestCase::Run() { + if (!should_run_) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_case(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + repeater->OnTestCaseStart(*this); + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestCase::RunSetUpTestCase, "SetUpTestCase()"); + + const internal::TimeInMillis start = internal::GetTimeInMillis(); + for (int i = 0; i < total_test_count(); i++) { + GetMutableTestInfo(i)->Run(); + } + elapsed_time_ = internal::GetTimeInMillis() - start; + + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestCase::RunTearDownTestCase, "TearDownTestCase()"); + + repeater->OnTestCaseEnd(*this); + impl->set_current_test_case(NULL); +} + +// Clears the results of all tests in this test case. +void TestCase::ClearResult() { + ad_hoc_test_result_.Clear(); + ForEach(test_info_list_, TestInfo::ClearTestResult); +} + +// Shuffles the tests in this test case. +void TestCase::ShuffleTests(internal::Random* random) { + Shuffle(random, &test_indices_); +} + +// Restores the test order to before the first shuffle. +void TestCase::UnshuffleTests() { + for (size_t i = 0; i < test_indices_.size(); i++) { + test_indices_[i] = static_cast(i); + } +} + +// Formats a countable noun. Depending on its quantity, either the +// singular form or the plural form is used. e.g. +// +// FormatCountableNoun(1, "formula", "formuli") returns "1 formula". +// FormatCountableNoun(5, "book", "books") returns "5 books". +static std::string FormatCountableNoun(int count, + const char * singular_form, + const char * plural_form) { + return internal::StreamableToString(count) + " " + + (count == 1 ? singular_form : plural_form); +} + +// Formats the count of tests. +static std::string FormatTestCount(int test_count) { + return FormatCountableNoun(test_count, "test", "tests"); +} + +// Formats the count of test cases. +static std::string FormatTestCaseCount(int test_case_count) { + return FormatCountableNoun(test_case_count, "test case", "test cases"); +} + +// Converts a TestPartResult::Type enum to human-friendly string +// representation. Both kNonFatalFailure and kFatalFailure are translated +// to "Failure", as the user usually doesn't care about the difference +// between the two when viewing the test result. +static const char * TestPartResultTypeToString(TestPartResult::Type type) { + switch (type) { + case TestPartResult::kSuccess: + return "Success"; + + case TestPartResult::kNonFatalFailure: + case TestPartResult::kFatalFailure: +#ifdef _MSC_VER + return "error: "; +#else + return "Failure\n"; +#endif + default: + return "Unknown result type"; + } +} + +namespace internal { + +// Prints a TestPartResult to an std::string. +static std::string PrintTestPartResultToString( + const TestPartResult& test_part_result) { + return (Message() + << internal::FormatFileLocation(test_part_result.file_name(), + test_part_result.line_number()) + << " " << TestPartResultTypeToString(test_part_result.type()) + << test_part_result.message()).GetString(); +} + +// Prints a TestPartResult. +static void PrintTestPartResult(const TestPartResult& test_part_result) { + const std::string& result = + PrintTestPartResultToString(test_part_result); + printf("%s\n", result.c_str()); + fflush(stdout); + // If the test program runs in Visual Studio or a debugger, the + // following statements add the test part result message to the Output + // window such that the user can double-click on it to jump to the + // corresponding source code location; otherwise they do nothing. +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + // We don't call OutputDebugString*() on Windows Mobile, as printing + // to stdout is done by OutputDebugString() there already - we don't + // want the same message printed twice. + ::OutputDebugStringA(result.c_str()); + ::OutputDebugStringA("\n"); +#endif +} + +// class PrettyUnitTestResultPrinter + +enum GTestColor { + COLOR_DEFAULT, + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW +}; + +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + +// Returns the character attribute for the given color. +WORD GetColorAttribute(GTestColor color) { + switch (color) { + case COLOR_RED: return FOREGROUND_RED; + case COLOR_GREEN: return FOREGROUND_GREEN; + case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN; + default: return 0; + } +} + +#else + +// Returns the ANSI color code for the given color. COLOR_DEFAULT is +// an invalid input. +const char* GetAnsiColorCode(GTestColor color) { + switch (color) { + case COLOR_RED: return "1"; + case COLOR_GREEN: return "2"; + case COLOR_YELLOW: return "3"; + default: return NULL; + }; +} + +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + +// Returns true iff Google Test should use colors in the output. +bool ShouldUseColor(bool stdout_is_tty) { + const char* const gtest_color = GTEST_FLAG(color).c_str(); + + if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) { +#if GTEST_OS_WINDOWS + // On Windows the TERM variable is usually not set, but the + // console there does support colors. + return stdout_is_tty; +#else + // On non-Windows platforms, we rely on the TERM variable. + const char* const term = posix::GetEnv("TERM"); + const bool term_supports_color = + String::CStringEquals(term, "xterm") || + String::CStringEquals(term, "xterm-color") || + String::CStringEquals(term, "xterm-256color") || + String::CStringEquals(term, "screen") || + String::CStringEquals(term, "screen-256color") || + String::CStringEquals(term, "linux") || + String::CStringEquals(term, "cygwin"); + return stdout_is_tty && term_supports_color; +#endif // GTEST_OS_WINDOWS + } + + return String::CaseInsensitiveCStringEquals(gtest_color, "yes") || + String::CaseInsensitiveCStringEquals(gtest_color, "true") || + String::CaseInsensitiveCStringEquals(gtest_color, "t") || + String::CStringEquals(gtest_color, "1"); + // We take "yes", "true", "t", and "1" as meaning "yes". If the + // value is neither one of these nor "auto", we treat it as "no" to + // be conservative. +} + +// Helpers for printing colored strings to stdout. Note that on Windows, we +// cannot simply emit special characters and have the terminal change colors. +// This routine must actually emit the characters rather than return a string +// that would be colored when printed, as can be done on Linux. +void ColoredPrintf(GTestColor color, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS || GTEST_OS_IOS + const bool use_color = false; +#else + static const bool in_color_mode = + ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0); + const bool use_color = in_color_mode && (color != COLOR_DEFAULT); +#endif // GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS + // The '!= 0' comparison is necessary to satisfy MSVC 7.1. + + if (!use_color) { + vprintf(fmt, args); + va_end(args); + return; + } + +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + + // Gets the current text color. + CONSOLE_SCREEN_BUFFER_INFO buffer_info; + GetConsoleScreenBufferInfo(stdout_handle, &buffer_info); + const WORD old_color_attrs = buffer_info.wAttributes; + + // We need to flush the stream buffers into the console before each + // SetConsoleTextAttribute call lest it affect the text that is already + // printed but has not yet reached the console. + fflush(stdout); + SetConsoleTextAttribute(stdout_handle, + GetColorAttribute(color) | FOREGROUND_INTENSITY); + vprintf(fmt, args); + + fflush(stdout); + // Restores the text color. + SetConsoleTextAttribute(stdout_handle, old_color_attrs); +#else + printf("\033[0;3%sm", GetAnsiColorCode(color)); + vprintf(fmt, args); + printf("\033[m"); // Resets the terminal to default. +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + va_end(args); +} + +// Text printed in Google Test's text output and --gunit_list_tests +// output to label the type parameter and value parameter for a test. +static const char kTypeParamLabel[] = "TypeParam"; +static const char kValueParamLabel[] = "GetParam()"; + +void PrintFullTestCommentIfPresent(const TestInfo& test_info) { + const char* const type_param = test_info.type_param(); + const char* const value_param = test_info.value_param(); + + if (type_param != NULL || value_param != NULL) { + printf(", where "); + if (type_param != NULL) { + printf("%s = %s", kTypeParamLabel, type_param); + if (value_param != NULL) + printf(" and "); + } + if (value_param != NULL) { + printf("%s = %s", kValueParamLabel, value_param); + } + } +} + +// This class implements the TestEventListener interface. +// +// Class PrettyUnitTestResultPrinter is copyable. +class PrettyUnitTestResultPrinter : public TestEventListener { + public: + PrettyUnitTestResultPrinter() {} + static void PrintTestName(const char * test_case, const char * test) { + printf("%s.%s", test_case, test); + } + + // The following methods override what's in the TestEventListener class. + virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration); + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); + virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestCaseStart(const TestCase& test_case); + virtual void OnTestStart(const TestInfo& test_info); + virtual void OnTestPartResult(const TestPartResult& result); + virtual void OnTestEnd(const TestInfo& test_info); + virtual void OnTestCaseEnd(const TestCase& test_case); + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); + virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {} + + private: + static void PrintFailedTests(const UnitTest& unit_test); +}; + + // Fired before each iteration of tests starts. +void PrettyUnitTestResultPrinter::OnTestIterationStart( + const UnitTest& unit_test, int iteration) { + if (GTEST_FLAG(repeat) != 1) + printf("\nRepeating all tests (iteration %d) . . .\n\n", iteration + 1); + + const char* const filter = GTEST_FLAG(filter).c_str(); + + // Prints the filter if it's not *. This reminds the user that some + // tests may be skipped. + if (!String::CStringEquals(filter, kUniversalFilter)) { + ColoredPrintf(COLOR_YELLOW, + "Note: %s filter = %s\n", GTEST_NAME_, filter); + } + + if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) { + const Int32 shard_index = Int32FromEnvOrDie(kTestShardIndex, -1); + ColoredPrintf(COLOR_YELLOW, + "Note: This is test shard %d of %s.\n", + static_cast(shard_index) + 1, + internal::posix::GetEnv(kTestTotalShards)); + } + + if (GTEST_FLAG(shuffle)) { + ColoredPrintf(COLOR_YELLOW, + "Note: Randomizing tests' orders with a seed of %d .\n", + unit_test.random_seed()); + } + + ColoredPrintf(COLOR_GREEN, "[==========] "); + printf("Running %s from %s.\n", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart( + const UnitTest& /*unit_test*/) { + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("Global test environment set-up.\n"); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) { + const std::string counts = + FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("%s from %s", counts.c_str(), test_case.name()); + if (test_case.type_param() == NULL) { + printf("\n"); + } else { + printf(", where %s = %s\n", kTypeParamLabel, test_case.type_param()); + } + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { + ColoredPrintf(COLOR_GREEN, "[ RUN ] "); + PrintTestName(test_info.test_case_name(), test_info.name()); + printf("\n"); + fflush(stdout); +} + +// Called after an assertion failure. +void PrettyUnitTestResultPrinter::OnTestPartResult( + const TestPartResult& result) { + // If the test part succeeded, we don't need to do anything. + if (result.type() == TestPartResult::kSuccess) + return; + + // Print failure message from the assertion (e.g. expected this and got that). + PrintTestPartResult(result); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { + if (test_info.result()->Passed()) { + ColoredPrintf(COLOR_GREEN, "[ OK ] "); + } else { + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + } + PrintTestName(test_info.test_case_name(), test_info.name()); + if (test_info.result()->Failed()) + PrintFullTestCommentIfPresent(test_info); + + if (GTEST_FLAG(print_time)) { + printf(" (%s ms)\n", internal::StreamableToString( + test_info.result()->elapsed_time()).c_str()); + } else { + printf("\n"); + } + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) { + if (!GTEST_FLAG(print_time)) return; + + const std::string counts = + FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("%s from %s (%s ms total)\n\n", + counts.c_str(), test_case.name(), + internal::StreamableToString(test_case.elapsed_time()).c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnEnvironmentsTearDownStart( + const UnitTest& /*unit_test*/) { + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("Global test environment tear-down\n"); + fflush(stdout); +} + +// Internal helper for printing the list of failed tests. +void PrettyUnitTestResultPrinter::PrintFailedTests(const UnitTest& unit_test) { + const int failed_test_count = unit_test.failed_test_count(); + if (failed_test_count == 0) { + return; + } + + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + const TestCase& test_case = *unit_test.GetTestCase(i); + if (!test_case.should_run() || (test_case.failed_test_count() == 0)) { + continue; + } + for (int j = 0; j < test_case.total_test_count(); ++j) { + const TestInfo& test_info = *test_case.GetTestInfo(j); + if (!test_info.should_run() || test_info.result()->Passed()) { + continue; + } + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + printf("%s.%s", test_case.name(), test_info.name()); + PrintFullTestCommentIfPresent(test_info); + printf("\n"); + } + } +} + +void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + ColoredPrintf(COLOR_GREEN, "[==========] "); + printf("%s from %s ran.", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str()); + if (GTEST_FLAG(print_time)) { + printf(" (%s ms total)", + internal::StreamableToString(unit_test.elapsed_time()).c_str()); + } + printf("\n"); + ColoredPrintf(COLOR_GREEN, "[ PASSED ] "); + printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str()); + + int num_failures = unit_test.failed_test_count(); + if (!unit_test.Passed()) { + const int failed_test_count = unit_test.failed_test_count(); + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + printf("%s, listed below:\n", FormatTestCount(failed_test_count).c_str()); + PrintFailedTests(unit_test); + printf("\n%2d FAILED %s\n", num_failures, + num_failures == 1 ? "TEST" : "TESTS"); + } + + int num_disabled = unit_test.reportable_disabled_test_count(); + if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) { + if (!num_failures) { + printf("\n"); // Add a spacer if no FAILURE banner is displayed. + } + ColoredPrintf(COLOR_YELLOW, + " YOU HAVE %d DISABLED %s\n\n", + num_disabled, + num_disabled == 1 ? "TEST" : "TESTS"); + } + // Ensure that Google Test output is printed before, e.g., heapchecker output. + fflush(stdout); +} + +// End PrettyUnitTestResultPrinter + +// class TestEventRepeater +// +// This class forwards events to other event listeners. +class TestEventRepeater : public TestEventListener { + public: + TestEventRepeater() : forwarding_enabled_(true) {} + virtual ~TestEventRepeater(); + void Append(TestEventListener *listener); + TestEventListener* Release(TestEventListener* listener); + + // Controls whether events will be forwarded to listeners_. Set to false + // in death test child processes. + bool forwarding_enabled() const { return forwarding_enabled_; } + void set_forwarding_enabled(bool enable) { forwarding_enabled_ = enable; } + + virtual void OnTestProgramStart(const UnitTest& unit_test); + virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration); + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test); + virtual void OnTestCaseStart(const TestCase& test_case); + virtual void OnTestStart(const TestInfo& test_info); + virtual void OnTestPartResult(const TestPartResult& result); + virtual void OnTestEnd(const TestInfo& test_info); + virtual void OnTestCaseEnd(const TestCase& test_case); + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); + virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test); + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + virtual void OnTestProgramEnd(const UnitTest& unit_test); + + private: + // Controls whether events will be forwarded to listeners_. Set to false + // in death test child processes. + bool forwarding_enabled_; + // The list of listeners that receive events. + std::vector listeners_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventRepeater); +}; + +TestEventRepeater::~TestEventRepeater() { + ForEach(listeners_, Delete); +} + +void TestEventRepeater::Append(TestEventListener *listener) { + listeners_.push_back(listener); +} + +// TODO(vladl@google.com): Factor the search functionality into Vector::Find. +TestEventListener* TestEventRepeater::Release(TestEventListener *listener) { + for (size_t i = 0; i < listeners_.size(); ++i) { + if (listeners_[i] == listener) { + listeners_.erase(listeners_.begin() + i); + return listener; + } + } + + return NULL; +} + +// Since most methods are very similar, use macros to reduce boilerplate. +// This defines a member that forwards the call to all listeners. +#define GTEST_REPEATER_METHOD_(Name, Type) \ +void TestEventRepeater::Name(const Type& parameter) { \ + if (forwarding_enabled_) { \ + for (size_t i = 0; i < listeners_.size(); i++) { \ + listeners_[i]->Name(parameter); \ + } \ + } \ +} +// This defines a member that forwards the call to all listeners in reverse +// order. +#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \ +void TestEventRepeater::Name(const Type& parameter) { \ + if (forwarding_enabled_) { \ + for (int i = static_cast(listeners_.size()) - 1; i >= 0; i--) { \ + listeners_[i]->Name(parameter); \ + } \ + } \ +} + +GTEST_REPEATER_METHOD_(OnTestProgramStart, UnitTest) +GTEST_REPEATER_METHOD_(OnEnvironmentsSetUpStart, UnitTest) +GTEST_REPEATER_METHOD_(OnTestCaseStart, TestCase) +GTEST_REPEATER_METHOD_(OnTestStart, TestInfo) +GTEST_REPEATER_METHOD_(OnTestPartResult, TestPartResult) +GTEST_REPEATER_METHOD_(OnEnvironmentsTearDownStart, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsSetUpEnd, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsTearDownEnd, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnTestEnd, TestInfo) +GTEST_REVERSE_REPEATER_METHOD_(OnTestCaseEnd, TestCase) +GTEST_REVERSE_REPEATER_METHOD_(OnTestProgramEnd, UnitTest) + +#undef GTEST_REPEATER_METHOD_ +#undef GTEST_REVERSE_REPEATER_METHOD_ + +void TestEventRepeater::OnTestIterationStart(const UnitTest& unit_test, + int iteration) { + if (forwarding_enabled_) { + for (size_t i = 0; i < listeners_.size(); i++) { + listeners_[i]->OnTestIterationStart(unit_test, iteration); + } + } +} + +void TestEventRepeater::OnTestIterationEnd(const UnitTest& unit_test, + int iteration) { + if (forwarding_enabled_) { + for (int i = static_cast(listeners_.size()) - 1; i >= 0; i--) { + listeners_[i]->OnTestIterationEnd(unit_test, iteration); + } + } +} + +// End TestEventRepeater + +// This class generates an XML output file. +class XmlUnitTestResultPrinter : public EmptyTestEventListener { + public: + explicit XmlUnitTestResultPrinter(const char* output_file); + + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + + private: + // Is c a whitespace character that is normalized to a space character + // when it appears in an XML attribute value? + static bool IsNormalizableWhitespace(char c) { + return c == 0x9 || c == 0xA || c == 0xD; + } + + // May c appear in a well-formed XML document? + static bool IsValidXmlCharacter(char c) { + return IsNormalizableWhitespace(c) || c >= 0x20; + } + + // Returns an XML-escaped copy of the input string str. If + // is_attribute is true, the text is meant to appear as an attribute + // value, and normalizable whitespace is preserved by replacing it + // with character references. + static std::string EscapeXml(const std::string& str, bool is_attribute); + + // Returns the given string with all characters invalid in XML removed. + static std::string RemoveInvalidXmlCharacters(const std::string& str); + + // Convenience wrapper around EscapeXml when str is an attribute value. + static std::string EscapeXmlAttribute(const std::string& str) { + return EscapeXml(str, true); + } + + // Convenience wrapper around EscapeXml when str is not an attribute value. + static std::string EscapeXmlText(const char* str) { + return EscapeXml(str, false); + } + + // Verifies that the given attribute belongs to the given element and + // streams the attribute as XML. + static void OutputXmlAttribute(std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value); + + // Streams an XML CDATA section, escaping invalid CDATA sequences as needed. + static void OutputXmlCDataSection(::std::ostream* stream, const char* data); + + // Streams an XML representation of a TestInfo object. + static void OutputXmlTestInfo(::std::ostream* stream, + const char* test_case_name, + const TestInfo& test_info); + + // Prints an XML representation of a TestCase object + static void PrintXmlTestCase(::std::ostream* stream, + const TestCase& test_case); + + // Prints an XML summary of unit_test to output stream out. + static void PrintXmlUnitTest(::std::ostream* stream, + const UnitTest& unit_test); + + // Produces a string representing the test properties in a result as space + // delimited XML attributes based on the property key="value" pairs. + // When the std::string is not empty, it includes a space at the beginning, + // to delimit this attribute from prior attributes. + static std::string TestPropertiesAsXmlAttributes(const TestResult& result); + + // The output file. + const std::string output_file_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(XmlUnitTestResultPrinter); +}; + +// Creates a new XmlUnitTestResultPrinter. +XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file) + : output_file_(output_file) { + if (output_file_.c_str() == NULL || output_file_.empty()) { + fprintf(stderr, "XML output file may not be null\n"); + fflush(stderr); + exit(EXIT_FAILURE); + } +} + +// Called after the unit test ends. +void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + FILE* xmlout = NULL; + FilePath output_file(output_file_); + FilePath output_dir(output_file.RemoveFileName()); + + if (output_dir.CreateDirectoriesRecursively()) { + xmlout = posix::FOpen(output_file_.c_str(), "w"); + } + if (xmlout == NULL) { + // TODO(wan): report the reason of the failure. + // + // We don't do it for now as: + // + // 1. There is no urgent need for it. + // 2. It's a bit involved to make the errno variable thread-safe on + // all three operating systems (Linux, Windows, and Mac OS). + // 3. To interpret the meaning of errno in a thread-safe way, + // we need the strerror_r() function, which is not available on + // Windows. + fprintf(stderr, + "Unable to open file \"%s\"\n", + output_file_.c_str()); + fflush(stderr); + exit(EXIT_FAILURE); + } + std::stringstream stream; + PrintXmlUnitTest(&stream, unit_test); + fprintf(xmlout, "%s", StringStreamToString(&stream).c_str()); + fclose(xmlout); +} + +// Returns an XML-escaped copy of the input string str. If is_attribute +// is true, the text is meant to appear as an attribute value, and +// normalizable whitespace is preserved by replacing it with character +// references. +// +// Invalid XML characters in str, if any, are stripped from the output. +// It is expected that most, if not all, of the text processed by this +// module will consist of ordinary English text. +// If this module is ever modified to produce version 1.1 XML output, +// most invalid characters can be retained using character references. +// TODO(wan): It might be nice to have a minimally invasive, human-readable +// escaping scheme for invalid characters, rather than dropping them. +std::string XmlUnitTestResultPrinter::EscapeXml( + const std::string& str, bool is_attribute) { + Message m; + + for (size_t i = 0; i < str.size(); ++i) { + const char ch = str[i]; + switch (ch) { + case '<': + m << "<"; + break; + case '>': + m << ">"; + break; + case '&': + m << "&"; + break; + case '\'': + if (is_attribute) + m << "'"; + else + m << '\''; + break; + case '"': + if (is_attribute) + m << """; + else + m << '"'; + break; + default: + if (IsValidXmlCharacter(ch)) { + if (is_attribute && IsNormalizableWhitespace(ch)) + m << "&#x" << String::FormatByte(static_cast(ch)) + << ";"; + else + m << ch; + } + break; + } + } + + return m.GetString(); +} + +// Returns the given string with all characters invalid in XML removed. +// Currently invalid characters are dropped from the string. An +// alternative is to replace them with certain characters such as . or ?. +std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters( + const std::string& str) { + std::string output; + output.reserve(str.size()); + for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) + if (IsValidXmlCharacter(*it)) + output.push_back(*it); + + return output; +} + +// The following routines generate an XML representation of a UnitTest +// object. +// +// This is how Google Test concepts map to the DTD: +// +// <-- corresponds to a UnitTest object +// <-- corresponds to a TestCase object +// <-- corresponds to a TestInfo object +// ... +// ... +// ... +// <-- individual assertion failures +// +// +// + +// Formats the given time in milliseconds as seconds. +std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) { + ::std::stringstream ss; + ss << ms/1000.0; + return ss.str(); +} + +// Converts the given epoch time in milliseconds to a date string in the ISO +// 8601 format, without the timezone information. +std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) { + // Using non-reentrant version as localtime_r is not portable. + time_t seconds = static_cast(ms / 1000); +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4996) // Temporarily disables warning 4996 + // (function or variable may be unsafe). + const struct tm* const time_struct = localtime(&seconds); // NOLINT +# pragma warning(pop) // Restores the warning state again. +#else + const struct tm* const time_struct = localtime(&seconds); // NOLINT +#endif + if (time_struct == NULL) + return ""; // Invalid ms value + + // YYYY-MM-DDThh:mm:ss + return StreamableToString(time_struct->tm_year + 1900) + "-" + + String::FormatIntWidth2(time_struct->tm_mon + 1) + "-" + + String::FormatIntWidth2(time_struct->tm_mday) + "T" + + String::FormatIntWidth2(time_struct->tm_hour) + ":" + + String::FormatIntWidth2(time_struct->tm_min) + ":" + + String::FormatIntWidth2(time_struct->tm_sec); +} + +// Streams an XML CDATA section, escaping invalid CDATA sequences as needed. +void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream, + const char* data) { + const char* segment = data; + *stream << ""); + if (next_segment != NULL) { + stream->write( + segment, static_cast(next_segment - segment)); + *stream << "]]>]]>"); + } else { + *stream << segment; + break; + } + } + *stream << "]]>"; +} + +void XmlUnitTestResultPrinter::OutputXmlAttribute( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value) { + const std::vector& allowed_names = + GetReservedAttributesForElement(element_name); + + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Attribute " << name << " is not allowed for element <" << element_name + << ">."; + + *stream << " " << name << "=\"" << EscapeXmlAttribute(value) << "\""; +} + +// Prints an XML representation of a TestInfo object. +// TODO(wan): There is also value in printing properties with the plain printer. +void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, + const char* test_case_name, + const TestInfo& test_info) { + const TestResult& result = *test_info.result(); + const std::string kTestcase = "testcase"; + + *stream << " \n"; + } + const string location = internal::FormatCompilerIndependentFileLocation( + part.file_name(), part.line_number()); + const string summary = location + "\n" + part.summary(); + *stream << " "; + const string detail = location + "\n" + part.message(); + OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str()); + *stream << "\n"; + } + } + + if (failures == 0) + *stream << " />\n"; + else + *stream << " \n"; +} + +// Prints an XML representation of a TestCase object +void XmlUnitTestResultPrinter::PrintXmlTestCase(std::ostream* stream, + const TestCase& test_case) { + const std::string kTestsuite = "testsuite"; + *stream << " <" << kTestsuite; + OutputXmlAttribute(stream, kTestsuite, "name", test_case.name()); + OutputXmlAttribute(stream, kTestsuite, "tests", + StreamableToString(test_case.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuite, "failures", + StreamableToString(test_case.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuite, "disabled", + StreamableToString(test_case.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuite, "errors", "0"); + OutputXmlAttribute(stream, kTestsuite, "time", + FormatTimeInMillisAsSeconds(test_case.elapsed_time())); + *stream << TestPropertiesAsXmlAttributes(test_case.ad_hoc_test_result()) + << ">\n"; + + for (int i = 0; i < test_case.total_test_count(); ++i) { + if (test_case.GetTestInfo(i)->is_reportable()) + OutputXmlTestInfo(stream, test_case.name(), *test_case.GetTestInfo(i)); + } + *stream << " \n"; +} + +// Prints an XML summary of unit_test to output stream out. +void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream, + const UnitTest& unit_test) { + const std::string kTestsuites = "testsuites"; + + *stream << "\n"; + *stream << "<" << kTestsuites; + + OutputXmlAttribute(stream, kTestsuites, "tests", + StreamableToString(unit_test.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuites, "failures", + StreamableToString(unit_test.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuites, "disabled", + StreamableToString(unit_test.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuites, "errors", "0"); + OutputXmlAttribute( + stream, kTestsuites, "timestamp", + FormatEpochTimeInMillisAsIso8601(unit_test.start_timestamp())); + OutputXmlAttribute(stream, kTestsuites, "time", + FormatTimeInMillisAsSeconds(unit_test.elapsed_time())); + + if (GTEST_FLAG(shuffle)) { + OutputXmlAttribute(stream, kTestsuites, "random_seed", + StreamableToString(unit_test.random_seed())); + } + + *stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result()); + + OutputXmlAttribute(stream, kTestsuites, "name", "AllTests"); + *stream << ">\n"; + + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + if (unit_test.GetTestCase(i)->reportable_test_count() > 0) + PrintXmlTestCase(stream, *unit_test.GetTestCase(i)); + } + *stream << "\n"; +} + +// Produces a string representing the test properties in a result as space +// delimited XML attributes based on the property key="value" pairs. +std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( + const TestResult& result) { + Message attributes; + for (int i = 0; i < result.test_property_count(); ++i) { + const TestProperty& property = result.GetTestProperty(i); + attributes << " " << property.key() << "=" + << "\"" << EscapeXmlAttribute(property.value()) << "\""; + } + return attributes.GetString(); +} + +// End XmlUnitTestResultPrinter + +#if GTEST_CAN_STREAM_RESULTS_ + +// Checks if str contains '=', '&', '%' or '\n' characters. If yes, +// replaces them by "%xx" where xx is their hexadecimal value. For +// example, replaces "=" with "%3D". This algorithm is O(strlen(str)) +// in both time and space -- important as the input str may contain an +// arbitrarily long test failure message and stack trace. +string StreamingListener::UrlEncode(const char* str) { + string result; + result.reserve(strlen(str) + 1); + for (char ch = *str; ch != '\0'; ch = *++str) { + switch (ch) { + case '%': + case '=': + case '&': + case '\n': + result.append("%" + String::FormatByte(static_cast(ch))); + break; + default: + result.push_back(ch); + break; + } + } + return result; +} + +void StreamingListener::SocketWriter::MakeConnection() { + GTEST_CHECK_(sockfd_ == -1) + << "MakeConnection() can't be called when there is already a connection."; + + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; // To allow both IPv4 and IPv6 addresses. + hints.ai_socktype = SOCK_STREAM; + addrinfo* servinfo = NULL; + + // Use the getaddrinfo() to get a linked list of IP addresses for + // the given host name. + const int error_num = getaddrinfo( + host_name_.c_str(), port_num_.c_str(), &hints, &servinfo); + if (error_num != 0) { + GTEST_LOG_(WARNING) << "stream_result_to: getaddrinfo() failed: " + << gai_strerror(error_num); + } + + // Loop through all the results and connect to the first we can. + for (addrinfo* cur_addr = servinfo; sockfd_ == -1 && cur_addr != NULL; + cur_addr = cur_addr->ai_next) { + sockfd_ = socket( + cur_addr->ai_family, cur_addr->ai_socktype, cur_addr->ai_protocol); + if (sockfd_ != -1) { + // Connect the client socket to the server socket. + if (connect(sockfd_, cur_addr->ai_addr, cur_addr->ai_addrlen) == -1) { + close(sockfd_); + sockfd_ = -1; + } + } + } + + freeaddrinfo(servinfo); // all done with this structure + + if (sockfd_ == -1) { + GTEST_LOG_(WARNING) << "stream_result_to: failed to connect to " + << host_name_ << ":" << port_num_; + } +} + +// End of class Streaming Listener +#endif // GTEST_CAN_STREAM_RESULTS__ + +// Class ScopedTrace + +// Pushes the given source file location and message onto a per-thread +// trace stack maintained by Google Test. +ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { + TraceInfo trace; + trace.file = file; + trace.line = line; + trace.message = message.GetString(); + + UnitTest::GetInstance()->PushGTestTrace(trace); +} + +// Pops the info pushed by the c'tor. +ScopedTrace::~ScopedTrace() + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { + UnitTest::GetInstance()->PopGTestTrace(); +} + + +// class OsStackTraceGetter + +// Returns the current OS stack trace as an std::string. Parameters: +// +// max_depth - the maximum number of stack frames to be included +// in the trace. +// skip_count - the number of top frames to be skipped; doesn't count +// against max_depth. +// +string OsStackTraceGetter::CurrentStackTrace(int /* max_depth */, + int /* skip_count */) + GTEST_LOCK_EXCLUDED_(mutex_) { + return ""; +} + +void OsStackTraceGetter::UponLeavingGTest() + GTEST_LOCK_EXCLUDED_(mutex_) { +} + +const char* const +OsStackTraceGetter::kElidedFramesMarker = + "... " GTEST_NAME_ " internal frames ..."; + +// A helper class that creates the premature-exit file in its +// constructor and deletes the file in its destructor. +class ScopedPrematureExitFile { + public: + explicit ScopedPrematureExitFile(const char* premature_exit_filepath) + : premature_exit_filepath_(premature_exit_filepath) { + // If a path to the premature-exit file is specified... + if (premature_exit_filepath != NULL && *premature_exit_filepath != '\0') { + // create the file with a single "0" character in it. I/O + // errors are ignored as there's nothing better we can do and we + // don't want to fail the test because of this. + FILE* pfile = posix::FOpen(premature_exit_filepath, "w"); + fwrite("0", 1, 1, pfile); + fclose(pfile); + } + } + + ~ScopedPrematureExitFile() { + if (premature_exit_filepath_ != NULL && *premature_exit_filepath_ != '\0') { + remove(premature_exit_filepath_); + } + } + + private: + const char* const premature_exit_filepath_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedPrematureExitFile); +}; + +} // namespace internal + +// class TestEventListeners + +TestEventListeners::TestEventListeners() + : repeater_(new internal::TestEventRepeater()), + default_result_printer_(NULL), + default_xml_generator_(NULL) { +} + +TestEventListeners::~TestEventListeners() { delete repeater_; } + +// Returns the standard listener responsible for the default console +// output. Can be removed from the listeners list to shut down default +// console output. Note that removing this object from the listener list +// with Release transfers its ownership to the user. +void TestEventListeners::Append(TestEventListener* listener) { + repeater_->Append(listener); +} + +// Removes the given event listener from the list and returns it. It then +// becomes the caller's responsibility to delete the listener. Returns +// NULL if the listener is not found in the list. +TestEventListener* TestEventListeners::Release(TestEventListener* listener) { + if (listener == default_result_printer_) + default_result_printer_ = NULL; + else if (listener == default_xml_generator_) + default_xml_generator_ = NULL; + return repeater_->Release(listener); +} + +// Returns repeater that broadcasts the TestEventListener events to all +// subscribers. +TestEventListener* TestEventListeners::repeater() { return repeater_; } + +// Sets the default_result_printer attribute to the provided listener. +// The listener is also added to the listener list and previous +// default_result_printer is removed from it and deleted. The listener can +// also be NULL in which case it will not be added to the list. Does +// nothing if the previous and the current listener objects are the same. +void TestEventListeners::SetDefaultResultPrinter(TestEventListener* listener) { + if (default_result_printer_ != listener) { + // It is an error to pass this method a listener that is already in the + // list. + delete Release(default_result_printer_); + default_result_printer_ = listener; + if (listener != NULL) + Append(listener); + } +} + +// Sets the default_xml_generator attribute to the provided listener. The +// listener is also added to the listener list and previous +// default_xml_generator is removed from it and deleted. The listener can +// also be NULL in which case it will not be added to the list. Does +// nothing if the previous and the current listener objects are the same. +void TestEventListeners::SetDefaultXmlGenerator(TestEventListener* listener) { + if (default_xml_generator_ != listener) { + // It is an error to pass this method a listener that is already in the + // list. + delete Release(default_xml_generator_); + default_xml_generator_ = listener; + if (listener != NULL) + Append(listener); + } +} + +// Controls whether events will be forwarded by the repeater to the +// listeners in the list. +bool TestEventListeners::EventForwardingEnabled() const { + return repeater_->forwarding_enabled(); +} + +void TestEventListeners::SuppressEventForwarding() { + repeater_->set_forwarding_enabled(false); +} + +// class UnitTest + +// Gets the singleton UnitTest object. The first time this method is +// called, a UnitTest object is constructed and returned. Consecutive +// calls will return the same object. +// +// We don't protect this under mutex_ as a user is not supposed to +// call this before main() starts, from which point on the return +// value will never change. +UnitTest* UnitTest::GetInstance() { + // When compiled with MSVC 7.1 in optimized mode, destroying the + // UnitTest object upon exiting the program messes up the exit code, + // causing successful tests to appear failed. We have to use a + // different implementation in this case to bypass the compiler bug. + // This implementation makes the compiler happy, at the cost of + // leaking the UnitTest object. + + // CodeGear C++Builder insists on a public destructor for the + // default implementation. Use this implementation to keep good OO + // design with private destructor. + +#if (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) + static UnitTest* const instance = new UnitTest; + return instance; +#else + static UnitTest instance; + return &instance; +#endif // (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) +} + +// Gets the number of successful test cases. +int UnitTest::successful_test_case_count() const { + return impl()->successful_test_case_count(); +} + +// Gets the number of failed test cases. +int UnitTest::failed_test_case_count() const { + return impl()->failed_test_case_count(); +} + +// Gets the number of all test cases. +int UnitTest::total_test_case_count() const { + return impl()->total_test_case_count(); +} + +// Gets the number of all test cases that contain at least one test +// that should run. +int UnitTest::test_case_to_run_count() const { + return impl()->test_case_to_run_count(); +} + +// Gets the number of successful tests. +int UnitTest::successful_test_count() const { + return impl()->successful_test_count(); +} + +// Gets the number of failed tests. +int UnitTest::failed_test_count() const { return impl()->failed_test_count(); } + +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTest::reportable_disabled_test_count() const { + return impl()->reportable_disabled_test_count(); +} + +// Gets the number of disabled tests. +int UnitTest::disabled_test_count() const { + return impl()->disabled_test_count(); +} + +// Gets the number of tests to be printed in the XML report. +int UnitTest::reportable_test_count() const { + return impl()->reportable_test_count(); +} + +// Gets the number of all tests. +int UnitTest::total_test_count() const { return impl()->total_test_count(); } + +// Gets the number of tests that should run. +int UnitTest::test_to_run_count() const { return impl()->test_to_run_count(); } + +// Gets the time of the test program start, in ms from the start of the +// UNIX epoch. +internal::TimeInMillis UnitTest::start_timestamp() const { + return impl()->start_timestamp(); +} + +// Gets the elapsed time, in milliseconds. +internal::TimeInMillis UnitTest::elapsed_time() const { + return impl()->elapsed_time(); +} + +// Returns true iff the unit test passed (i.e. all test cases passed). +bool UnitTest::Passed() const { return impl()->Passed(); } + +// Returns true iff the unit test failed (i.e. some test case failed +// or something outside of all tests failed). +bool UnitTest::Failed() const { return impl()->Failed(); } + +// Gets the i-th test case among all the test cases. i can range from 0 to +// total_test_case_count() - 1. If i is not in that range, returns NULL. +const TestCase* UnitTest::GetTestCase(int i) const { + return impl()->GetTestCase(i); +} + +// Returns the TestResult containing information on test failures and +// properties logged outside of individual test cases. +const TestResult& UnitTest::ad_hoc_test_result() const { + return *impl()->ad_hoc_test_result(); +} + +// Gets the i-th test case among all the test cases. i can range from 0 to +// total_test_case_count() - 1. If i is not in that range, returns NULL. +TestCase* UnitTest::GetMutableTestCase(int i) { + return impl()->GetMutableTestCase(i); +} + +// Returns the list of event listeners that can be used to track events +// inside Google Test. +TestEventListeners& UnitTest::listeners() { + return *impl()->listeners(); +} + +// Registers and returns a global test environment. When a test +// program is run, all global test environments will be set-up in the +// order they were registered. After all tests in the program have +// finished, all global test environments will be torn-down in the +// *reverse* order they were registered. +// +// The UnitTest object takes ownership of the given environment. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +Environment* UnitTest::AddEnvironment(Environment* env) { + if (env == NULL) { + return NULL; + } + + impl_->environments().push_back(env); + return env; +} + +// Adds a TestPartResult to the current TestResult object. All Google Test +// assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call +// this to report their results. The user code should use the +// assertion macros instead of calling this directly. +void UnitTest::AddTestPartResult( + TestPartResult::Type result_type, + const char* file_name, + int line_number, + const std::string& message, + const std::string& os_stack_trace) GTEST_LOCK_EXCLUDED_(mutex_) { + Message msg; + msg << message; + + internal::MutexLock lock(&mutex_); + if (impl_->gtest_trace_stack().size() > 0) { + msg << "\n" << GTEST_NAME_ << " trace:"; + + for (int i = static_cast(impl_->gtest_trace_stack().size()); + i > 0; --i) { + const internal::TraceInfo& trace = impl_->gtest_trace_stack()[i - 1]; + msg << "\n" << internal::FormatFileLocation(trace.file, trace.line) + << " " << trace.message; + } + } + + if (os_stack_trace.c_str() != NULL && !os_stack_trace.empty()) { + msg << internal::kStackTraceMarker << os_stack_trace; + } + + const TestPartResult result = + TestPartResult(result_type, file_name, line_number, + msg.GetString().c_str()); + impl_->GetTestPartResultReporterForCurrentThread()-> + ReportTestPartResult(result); + + if (result_type != TestPartResult::kSuccess) { + // gtest_break_on_failure takes precedence over + // gtest_throw_on_failure. This allows a user to set the latter + // in the code (perhaps in order to use Google Test assertions + // with another testing framework) and specify the former on the + // command line for debugging. + if (GTEST_FLAG(break_on_failure)) { +#if GTEST_OS_WINDOWS + // Using DebugBreak on Windows allows gtest to still break into a debugger + // when a failure happens and both the --gtest_break_on_failure and + // the --gtest_catch_exceptions flags are specified. + DebugBreak(); +#else + // Dereference NULL through a volatile pointer to prevent the compiler + // from removing. We use this rather than abort() or __builtin_trap() for + // portability: Symbian doesn't implement abort() well, and some debuggers + // don't correctly trap abort(). + *static_cast(NULL) = 1; +#endif // GTEST_OS_WINDOWS + } else if (GTEST_FLAG(throw_on_failure)) { +#if GTEST_HAS_EXCEPTIONS + throw internal::GoogleTestFailureException(result); +#else + // We cannot call abort() as it generates a pop-up in debug mode + // that cannot be suppressed in VC 7.1 or below. + exit(1); +#endif + } + } +} + +// Adds a TestProperty to the current TestResult object when invoked from +// inside a test, to current TestCase's ad_hoc_test_result_ when invoked +// from SetUpTestCase or TearDownTestCase, or to the global property set +// when invoked elsewhere. If the result already contains a property with +// the same key, the value will be updated. +void UnitTest::RecordProperty(const std::string& key, + const std::string& value) { + impl_->RecordProperty(TestProperty(key, value)); +} + +// Runs all tests in this UnitTest object and prints the result. +// Returns 0 if successful, or 1 otherwise. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +int UnitTest::Run() { + const bool in_death_test_child_process = + internal::GTEST_FLAG(internal_run_death_test).length() > 0; + + // Google Test implements this protocol for catching that a test + // program exits before returning control to Google Test: + // + // 1. Upon start, Google Test creates a file whose absolute path + // is specified by the environment variable + // TEST_PREMATURE_EXIT_FILE. + // 2. When Google Test has finished its work, it deletes the file. + // + // This allows a test runner to set TEST_PREMATURE_EXIT_FILE before + // running a Google-Test-based test program and check the existence + // of the file at the end of the test execution to see if it has + // exited prematurely. + + // If we are in the child process of a death test, don't + // create/delete the premature exit file, as doing so is unnecessary + // and will confuse the parent process. Otherwise, create/delete + // the file upon entering/leaving this function. If the program + // somehow exits before this function has a chance to return, the + // premature-exit file will be left undeleted, causing a test runner + // that understands the premature-exit-file protocol to report the + // test as having failed. + const internal::ScopedPrematureExitFile premature_exit_file( + in_death_test_child_process ? + NULL : internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE")); + + // Captures the value of GTEST_FLAG(catch_exceptions). This value will be + // used for the duration of the program. + impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions)); + +#if GTEST_HAS_SEH + // Either the user wants Google Test to catch exceptions thrown by the + // tests or this is executing in the context of death test child + // process. In either case the user does not want to see pop-up dialogs + // about crashes - they are expected. + if (impl()->catch_exceptions() || in_death_test_child_process) { +# if !GTEST_OS_WINDOWS_MOBILE + // SetErrorMode doesn't exist on CE. + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | + SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); +# endif // !GTEST_OS_WINDOWS_MOBILE + +# if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE + // Death test children can be terminated with _abort(). On Windows, + // _abort() can show a dialog with a warning message. This forces the + // abort message to go to stderr instead. + _set_error_mode(_OUT_TO_STDERR); +# endif + +# if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE + // In the debug version, Visual Studio pops up a separate dialog + // offering a choice to debug the aborted program. We need to suppress + // this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement + // executed. Google Test will notify the user of any unexpected + // failure via stderr. + // + // VC++ doesn't define _set_abort_behavior() prior to the version 8.0. + // Users of prior VC versions shall suffer the agony and pain of + // clicking through the countless debug dialogs. + // TODO(vladl@google.com): find a way to suppress the abort dialog() in the + // debug mode when compiled with VC 7.1 or lower. + if (!GTEST_FLAG(break_on_failure)) + _set_abort_behavior( + 0x0, // Clear the following flags: + _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump. +# endif + } +#endif // GTEST_HAS_SEH + + return internal::HandleExceptionsInMethodIfSupported( + impl(), + &internal::UnitTestImpl::RunAllTests, + "auxiliary test code (environments or event listeners)") ? 0 : 1; +} + +// Returns the working directory when the first TEST() or TEST_F() was +// executed. +const char* UnitTest::original_working_dir() const { + return impl_->original_working_dir_.c_str(); +} + +// Returns the TestCase object for the test that's currently running, +// or NULL if no test is running. +const TestCase* UnitTest::current_test_case() const + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + return impl_->current_test_case(); +} + +// Returns the TestInfo object for the test that's currently running, +// or NULL if no test is running. +const TestInfo* UnitTest::current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + return impl_->current_test_info(); +} + +// Returns the random seed used at the start of the current test run. +int UnitTest::random_seed() const { return impl_->random_seed(); } + +#if GTEST_HAS_PARAM_TEST +// Returns ParameterizedTestCaseRegistry object used to keep track of +// value-parameterized tests and instantiate and register them. +internal::ParameterizedTestCaseRegistry& + UnitTest::parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_) { + return impl_->parameterized_test_registry(); +} +#endif // GTEST_HAS_PARAM_TEST + +// Creates an empty UnitTest. +UnitTest::UnitTest() { + impl_ = new internal::UnitTestImpl(this); +} + +// Destructor of UnitTest. +UnitTest::~UnitTest() { + delete impl_; +} + +// Pushes a trace defined by SCOPED_TRACE() on to the per-thread +// Google Test trace stack. +void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack().push_back(trace); +} + +// Pops a trace from the per-thread Google Test trace stack. +void UnitTest::PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack().pop_back(); +} + +namespace internal { + +UnitTestImpl::UnitTestImpl(UnitTest* parent) + : parent_(parent), +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4355) // Temporarily disables warning 4355 + // (using this in initializer). + default_global_test_part_result_reporter_(this), + default_per_thread_test_part_result_reporter_(this), +# pragma warning(pop) // Restores the warning state again. +#else + default_global_test_part_result_reporter_(this), + default_per_thread_test_part_result_reporter_(this), +#endif // _MSC_VER + global_test_part_result_repoter_( + &default_global_test_part_result_reporter_), + per_thread_test_part_result_reporter_( + &default_per_thread_test_part_result_reporter_), +#if GTEST_HAS_PARAM_TEST + parameterized_test_registry_(), + parameterized_tests_registered_(false), +#endif // GTEST_HAS_PARAM_TEST + last_death_test_case_(-1), + current_test_case_(NULL), + current_test_info_(NULL), + ad_hoc_test_result_(), + os_stack_trace_getter_(NULL), + post_flag_parse_init_performed_(false), + random_seed_(0), // Will be overridden by the flag before first use. + random_(0), // Will be reseeded before first use. + start_timestamp_(0), + elapsed_time_(0), +#if GTEST_HAS_DEATH_TEST + death_test_factory_(new DefaultDeathTestFactory), +#endif + // Will be overridden by the flag before first use. + catch_exceptions_(false) { + listeners()->SetDefaultResultPrinter(new PrettyUnitTestResultPrinter); +} + +UnitTestImpl::~UnitTestImpl() { + // Deletes every TestCase. + ForEach(test_cases_, internal::Delete); + + // Deletes every Environment. + ForEach(environments_, internal::Delete); + + delete os_stack_trace_getter_; +} + +// Adds a TestProperty to the current TestResult object when invoked in a +// context of a test, to current test case's ad_hoc_test_result when invoke +// from SetUpTestCase/TearDownTestCase, or to the global property set +// otherwise. If the result already contains a property with the same key, +// the value will be updated. +void UnitTestImpl::RecordProperty(const TestProperty& test_property) { + std::string xml_element; + TestResult* test_result; // TestResult appropriate for property recording. + + if (current_test_info_ != NULL) { + xml_element = "testcase"; + test_result = &(current_test_info_->result_); + } else if (current_test_case_ != NULL) { + xml_element = "testsuite"; + test_result = &(current_test_case_->ad_hoc_test_result_); + } else { + xml_element = "testsuites"; + test_result = &ad_hoc_test_result_; + } + test_result->RecordProperty(xml_element, test_property); +} + +#if GTEST_HAS_DEATH_TEST +// Disables event forwarding if the control is currently in a death test +// subprocess. Must not be called before InitGoogleTest. +void UnitTestImpl::SuppressTestEventsIfInSubprocess() { + if (internal_run_death_test_flag_.get() != NULL) + listeners()->SuppressEventForwarding(); +} +#endif // GTEST_HAS_DEATH_TEST + +// Initializes event listeners performing XML output as specified by +// UnitTestOptions. Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureXmlOutput() { + const std::string& output_format = UnitTestOptions::GetOutputFormat(); + if (output_format == "xml") { + listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); + } else if (output_format != "") { + printf("WARNING: unrecognized output format \"%s\" ignored.\n", + output_format.c_str()); + fflush(stdout); + } +} + +#if GTEST_CAN_STREAM_RESULTS_ +// Initializes event listeners for streaming test results in string form. +// Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureStreamingOutput() { + const std::string& target = GTEST_FLAG(stream_result_to); + if (!target.empty()) { + const size_t pos = target.find(':'); + if (pos != std::string::npos) { + listeners()->Append(new StreamingListener(target.substr(0, pos), + target.substr(pos+1))); + } else { + printf("WARNING: unrecognized streaming target \"%s\" ignored.\n", + target.c_str()); + fflush(stdout); + } + } +} +#endif // GTEST_CAN_STREAM_RESULTS_ + +// Performs initialization dependent upon flag values obtained in +// ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to +// ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest +// this function is also called from RunAllTests. Since this function can be +// called more than once, it has to be idempotent. +void UnitTestImpl::PostFlagParsingInit() { + // Ensures that this function does not execute more than once. + if (!post_flag_parse_init_performed_) { + post_flag_parse_init_performed_ = true; + +#if GTEST_HAS_DEATH_TEST + InitDeathTestSubprocessControlInfo(); + SuppressTestEventsIfInSubprocess(); +#endif // GTEST_HAS_DEATH_TEST + + // Registers parameterized tests. This makes parameterized tests + // available to the UnitTest reflection API without running + // RUN_ALL_TESTS. + RegisterParameterizedTests(); + + // Configures listeners for XML output. This makes it possible for users + // to shut down the default XML output before invoking RUN_ALL_TESTS. + ConfigureXmlOutput(); + +#if GTEST_CAN_STREAM_RESULTS_ + // Configures listeners for streaming test results to the specified server. + ConfigureStreamingOutput(); +#endif // GTEST_CAN_STREAM_RESULTS_ + } +} + +// A predicate that checks the name of a TestCase against a known +// value. +// +// This is used for implementation of the UnitTest class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestCaseNameIs is copyable. +class TestCaseNameIs { + public: + // Constructor. + explicit TestCaseNameIs(const std::string& name) + : name_(name) {} + + // Returns true iff the name of test_case matches name_. + bool operator()(const TestCase* test_case) const { + return test_case != NULL && strcmp(test_case->name(), name_.c_str()) == 0; + } + + private: + std::string name_; +}; + +// Finds and returns a TestCase with the given name. If one doesn't +// exist, creates one and returns it. It's the CALLER'S +// RESPONSIBILITY to ensure that this function is only called WHEN THE +// TESTS ARE NOT SHUFFLED. +// +// Arguments: +// +// test_case_name: name of the test case +// type_param: the name of the test case's type parameter, or NULL if +// this is not a typed or a type-parameterized test case. +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +TestCase* UnitTestImpl::GetTestCase(const char* test_case_name, + const char* type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc) { + // Can we find a TestCase with the given name? + const std::vector::const_iterator test_case = + std::find_if(test_cases_.begin(), test_cases_.end(), + TestCaseNameIs(test_case_name)); + + if (test_case != test_cases_.end()) + return *test_case; + + // No. Let's create one. + TestCase* const new_test_case = + new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc); + + // Is this a death test case? + if (internal::UnitTestOptions::MatchesFilter(test_case_name, + kDeathTestCaseFilter)) { + // Yes. Inserts the test case after the last death test case + // defined so far. This only works when the test cases haven't + // been shuffled. Otherwise we may end up running a death test + // after a non-death test. + ++last_death_test_case_; + test_cases_.insert(test_cases_.begin() + last_death_test_case_, + new_test_case); + } else { + // No. Appends to the end of the list. + test_cases_.push_back(new_test_case); + } + + test_case_indices_.push_back(static_cast(test_case_indices_.size())); + return new_test_case; +} + +// Helpers for setting up / tearing down the given environment. They +// are for use in the ForEach() function. +static void SetUpEnvironment(Environment* env) { env->SetUp(); } +static void TearDownEnvironment(Environment* env) { env->TearDown(); } + +// Runs all tests in this UnitTest object, prints the result, and +// returns true if all tests are successful. If any exception is +// thrown during a test, the test is considered to be failed, but the +// rest of the tests will still be run. +// +// When parameterized tests are enabled, it expands and registers +// parameterized tests first in RegisterParameterizedTests(). +// All other functions called from RunAllTests() may safely assume that +// parameterized tests are ready to be counted and run. +bool UnitTestImpl::RunAllTests() { + // Makes sure InitGoogleTest() was called. + if (!GTestIsInitialized()) { + printf("%s", + "\nThis test program did NOT call ::testing::InitGoogleTest " + "before calling RUN_ALL_TESTS(). Please fix it.\n"); + return false; + } + + // Do not run any test if the --help flag was specified. + if (g_help_flag) + return true; + + // Repeats the call to the post-flag parsing initialization in case the + // user didn't call InitGoogleTest. + PostFlagParsingInit(); + + // Even if sharding is not on, test runners may want to use the + // GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding + // protocol. + internal::WriteToShardStatusFileIfNeeded(); + + // True iff we are in a subprocess for running a thread-safe-style + // death test. + bool in_subprocess_for_death_test = false; + +#if GTEST_HAS_DEATH_TEST + in_subprocess_for_death_test = (internal_run_death_test_flag_.get() != NULL); +#endif // GTEST_HAS_DEATH_TEST + + const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex, + in_subprocess_for_death_test); + + // Compares the full test names with the filter to decide which + // tests to run. + const bool has_tests_to_run = FilterTests(should_shard + ? HONOR_SHARDING_PROTOCOL + : IGNORE_SHARDING_PROTOCOL) > 0; + + // Lists the tests and exits if the --gtest_list_tests flag was specified. + if (GTEST_FLAG(list_tests)) { + // This must be called *after* FilterTests() has been called. + ListTestsMatchingFilter(); + return true; + } + + random_seed_ = GTEST_FLAG(shuffle) ? + GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0; + + // True iff at least one test has failed. + bool failed = false; + + TestEventListener* repeater = listeners()->repeater(); + + start_timestamp_ = GetTimeInMillis(); + repeater->OnTestProgramStart(*parent_); + + // How many times to repeat the tests? We don't want to repeat them + // when we are inside the subprocess of a death test. + const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat); + // Repeats forever if the repeat count is negative. + const bool forever = repeat < 0; + for (int i = 0; forever || i != repeat; i++) { + // We want to preserve failures generated by ad-hoc test + // assertions executed before RUN_ALL_TESTS(). + ClearNonAdHocTestResult(); + + const TimeInMillis start = GetTimeInMillis(); + + // Shuffles test cases and tests if requested. + if (has_tests_to_run && GTEST_FLAG(shuffle)) { + random()->Reseed(random_seed_); + // This should be done before calling OnTestIterationStart(), + // such that a test event listener can see the actual test order + // in the event. + ShuffleTests(); + } + + // Tells the unit test event listeners that the tests are about to start. + repeater->OnTestIterationStart(*parent_, i); + + // Runs each test case if there is at least one test to run. + if (has_tests_to_run) { + // Sets up all environments beforehand. + repeater->OnEnvironmentsSetUpStart(*parent_); + ForEach(environments_, SetUpEnvironment); + repeater->OnEnvironmentsSetUpEnd(*parent_); + + // Runs the tests only if there was no fatal failure during global + // set-up. + if (!Test::HasFatalFailure()) { + for (int test_index = 0; test_index < total_test_case_count(); + test_index++) { + GetMutableTestCase(test_index)->Run(); + } + } + + // Tears down all environments in reverse order afterwards. + repeater->OnEnvironmentsTearDownStart(*parent_); + std::for_each(environments_.rbegin(), environments_.rend(), + TearDownEnvironment); + repeater->OnEnvironmentsTearDownEnd(*parent_); + } + + elapsed_time_ = GetTimeInMillis() - start; + + // Tells the unit test event listener that the tests have just finished. + repeater->OnTestIterationEnd(*parent_, i); + + // Gets the result and clears it. + if (!Passed()) { + failed = true; + } + + // Restores the original test order after the iteration. This + // allows the user to quickly repro a failure that happens in the + // N-th iteration without repeating the first (N - 1) iterations. + // This is not enclosed in "if (GTEST_FLAG(shuffle)) { ... }", in + // case the user somehow changes the value of the flag somewhere + // (it's always safe to unshuffle the tests). + UnshuffleTests(); + + if (GTEST_FLAG(shuffle)) { + // Picks a new random seed for each iteration. + random_seed_ = GetNextRandomSeed(random_seed_); + } + } + + repeater->OnTestProgramEnd(*parent_); + + return !failed; +} + +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded() { + const char* const test_shard_file = posix::GetEnv(kTestShardStatusFile); + if (test_shard_file != NULL) { + FILE* const file = posix::FOpen(test_shard_file, "w"); + if (file == NULL) { + ColoredPrintf(COLOR_RED, + "Could not write to the test shard status file \"%s\" " + "specified by the %s environment variable.\n", + test_shard_file, kTestShardStatusFile); + fflush(stdout); + exit(EXIT_FAILURE); + } + fclose(file); + } +} + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (i.e., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +bool ShouldShard(const char* total_shards_env, + const char* shard_index_env, + bool in_subprocess_for_death_test) { + if (in_subprocess_for_death_test) { + return false; + } + + const Int32 total_shards = Int32FromEnvOrDie(total_shards_env, -1); + const Int32 shard_index = Int32FromEnvOrDie(shard_index_env, -1); + + if (total_shards == -1 && shard_index == -1) { + return false; + } else if (total_shards == -1 && shard_index != -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestShardIndex << " = " << shard_index + << ", but have left " << kTestTotalShards << " unset.\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (total_shards != -1 && shard_index == -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestTotalShards << " = " << total_shards + << ", but have left " << kTestShardIndex << " unset.\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (shard_index < 0 || shard_index >= total_shards) { + const Message msg = Message() + << "Invalid environment variables: we require 0 <= " + << kTestShardIndex << " < " << kTestTotalShards + << ", but you have " << kTestShardIndex << "=" << shard_index + << ", " << kTestTotalShards << "=" << total_shards << ".\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } + + return total_shards > 1; +} + +// Parses the environment variable var as an Int32. If it is unset, +// returns default_val. If it is not an Int32, prints an error +// and aborts. +Int32 Int32FromEnvOrDie(const char* var, Int32 default_val) { + const char* str_val = posix::GetEnv(var); + if (str_val == NULL) { + return default_val; + } + + Int32 result; + if (!ParseInt32(Message() << "The value of environment variable " << var, + str_val, &result)) { + exit(EXIT_FAILURE); + } + return result; +} + +// Given the total number of shards, the shard index, and the test id, +// returns true iff the test should be run on this shard. The test id is +// some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) { + return (test_id % total_shards) == shard_index; +} + +// Compares the name of each test with the user-specified filter to +// decide whether the test should be run, then records the result in +// each TestCase and TestInfo object. +// If shard_tests == true, further filters tests based on sharding +// variables in the environment - see +// http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide. +// Returns the number of tests that should run. +int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { + const Int32 total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestTotalShards, -1) : -1; + const Int32 shard_index = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestShardIndex, -1) : -1; + + // num_runnable_tests are the number of tests that will + // run across all shards (i.e., match filter and are not disabled). + // num_selected_tests are the number of tests to be run on + // this shard. + int num_runnable_tests = 0; + int num_selected_tests = 0; + for (size_t i = 0; i < test_cases_.size(); i++) { + TestCase* const test_case = test_cases_[i]; + const std::string &test_case_name = test_case->name(); + test_case->set_should_run(false); + + for (size_t j = 0; j < test_case->test_info_list().size(); j++) { + TestInfo* const test_info = test_case->test_info_list()[j]; + const std::string test_name(test_info->name()); + // A test is disabled if test case name or test name matches + // kDisableTestFilter. + const bool is_disabled = + internal::UnitTestOptions::MatchesFilter(test_case_name, + kDisableTestFilter) || + internal::UnitTestOptions::MatchesFilter(test_name, + kDisableTestFilter); + test_info->is_disabled_ = is_disabled; + + const bool matches_filter = + internal::UnitTestOptions::FilterMatchesTest(test_case_name, + test_name); + test_info->matches_filter_ = matches_filter; + + const bool is_runnable = + (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) && + matches_filter; + + const bool is_selected = is_runnable && + (shard_tests == IGNORE_SHARDING_PROTOCOL || + ShouldRunTestOnShard(total_shards, shard_index, + num_runnable_tests)); + + num_runnable_tests += is_runnable; + num_selected_tests += is_selected; + + test_info->should_run_ = is_selected; + test_case->set_should_run(test_case->should_run() || is_selected); + } + } + return num_selected_tests; +} + +// Prints the given C-string on a single line by replacing all '\n' +// characters with string "\\n". If the output takes more than +// max_length characters, only prints the first max_length characters +// and "...". +static void PrintOnOneLine(const char* str, int max_length) { + if (str != NULL) { + for (int i = 0; *str != '\0'; ++str) { + if (i >= max_length) { + printf("..."); + break; + } + if (*str == '\n') { + printf("\\n"); + i += 2; + } else { + printf("%c", *str); + ++i; + } + } + } +} + +// Prints the names of the tests matching the user-specified filter flag. +void UnitTestImpl::ListTestsMatchingFilter() { + // Print at most this many characters for each type/value parameter. + const int kMaxParamLength = 250; + + for (size_t i = 0; i < test_cases_.size(); i++) { + const TestCase* const test_case = test_cases_[i]; + bool printed_test_case_name = false; + + for (size_t j = 0; j < test_case->test_info_list().size(); j++) { + const TestInfo* const test_info = + test_case->test_info_list()[j]; + if (test_info->matches_filter_) { + if (!printed_test_case_name) { + printed_test_case_name = true; + printf("%s.", test_case->name()); + if (test_case->type_param() != NULL) { + printf(" # %s = ", kTypeParamLabel); + // We print the type parameter on a single line to make + // the output easy to parse by a program. + PrintOnOneLine(test_case->type_param(), kMaxParamLength); + } + printf("\n"); + } + printf(" %s", test_info->name()); + if (test_info->value_param() != NULL) { + printf(" # %s = ", kValueParamLabel); + // We print the value parameter on a single line to make the + // output easy to parse by a program. + PrintOnOneLine(test_info->value_param(), kMaxParamLength); + } + printf("\n"); + } + } + } + fflush(stdout); +} + +// Sets the OS stack trace getter. +// +// Does nothing if the input and the current OS stack trace getter are +// the same; otherwise, deletes the old getter and makes the input the +// current getter. +void UnitTestImpl::set_os_stack_trace_getter( + OsStackTraceGetterInterface* getter) { + if (os_stack_trace_getter_ != getter) { + delete os_stack_trace_getter_; + os_stack_trace_getter_ = getter; + } +} + +// Returns the current OS stack trace getter if it is not NULL; +// otherwise, creates an OsStackTraceGetter, makes it the current +// getter, and returns it. +OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() { + if (os_stack_trace_getter_ == NULL) { + os_stack_trace_getter_ = new OsStackTraceGetter; + } + + return os_stack_trace_getter_; +} + +// Returns the TestResult for the test that's currently running, or +// the TestResult for the ad hoc test if no test is running. +TestResult* UnitTestImpl::current_test_result() { + return current_test_info_ ? + &(current_test_info_->result_) : &ad_hoc_test_result_; +} + +// Shuffles all test cases, and the tests within each test case, +// making sure that death tests are still run first. +void UnitTestImpl::ShuffleTests() { + // Shuffles the death test cases. + ShuffleRange(random(), 0, last_death_test_case_ + 1, &test_case_indices_); + + // Shuffles the non-death test cases. + ShuffleRange(random(), last_death_test_case_ + 1, + static_cast(test_cases_.size()), &test_case_indices_); + + // Shuffles the tests inside each test case. + for (size_t i = 0; i < test_cases_.size(); i++) { + test_cases_[i]->ShuffleTests(random()); + } +} + +// Restores the test cases and tests to their order before the first shuffle. +void UnitTestImpl::UnshuffleTests() { + for (size_t i = 0; i < test_cases_.size(); i++) { + // Unshuffles the tests in each test case. + test_cases_[i]->UnshuffleTests(); + // Resets the index of each test case. + test_case_indices_[i] = static_cast(i); + } +} + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +std::string GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, + int skip_count) { + // We pass skip_count + 1 to skip this wrapper function in addition + // to what the user really wants to skip. + return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1); +} + +// Used by the GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_ macro to +// suppress unreachable code warnings. +namespace { +class ClassUniqueToAlwaysTrue {}; +} + +bool IsTrue(bool condition) { return condition; } + +bool AlwaysTrue() { +#if GTEST_HAS_EXCEPTIONS + // This condition is always false so AlwaysTrue() never actually throws, + // but it makes the compiler think that it may throw. + if (IsTrue(false)) + throw ClassUniqueToAlwaysTrue(); +#endif // GTEST_HAS_EXCEPTIONS + return true; +} + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +bool SkipPrefix(const char* prefix, const char** pstr) { + const size_t prefix_len = strlen(prefix); + if (strncmp(*pstr, prefix, prefix_len) == 0) { + *pstr += prefix_len; + return true; + } + return false; +} + +// Parses a string as a command line flag. The string should have +// the format "--flag=value". When def_optional is true, the "=value" +// part can be omitted. +// +// Returns the value of the flag, or NULL if the parsing failed. +const char* ParseFlagValue(const char* str, + const char* flag, + bool def_optional) { + // str and flag must not be NULL. + if (str == NULL || flag == NULL) return NULL; + + // The flag must start with "--" followed by GTEST_FLAG_PREFIX_. + const std::string flag_str = std::string("--") + GTEST_FLAG_PREFIX_ + flag; + const size_t flag_len = flag_str.length(); + if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL; + + // Skips the flag name. + const char* flag_end = str + flag_len; + + // When def_optional is true, it's OK to not have a "=value" part. + if (def_optional && (flag_end[0] == '\0')) { + return flag_end; + } + + // If def_optional is true and there are more characters after the + // flag name, or if def_optional is false, there must be a '=' after + // the flag name. + if (flag_end[0] != '=') return NULL; + + // Returns the string after "=". + return flag_end + 1; +} + +// Parses a string for a bool flag, in the form of either +// "--flag=value" or "--flag". +// +// In the former case, the value is taken as true as long as it does +// not start with '0', 'f', or 'F'. +// +// In the latter case, the value is taken as true. +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseBoolFlag(const char* str, const char* flag, bool* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, true); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Converts the string value to a bool. + *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); + return true; +} + +// Parses a string for an Int32 flag, in the form of +// "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseInt32Flag(const char* str, const char* flag, Int32* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + return ParseInt32(Message() << "The value of flag --" << flag, + value_str, value); +} + +// Parses a string for a string flag, in the form of +// "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseStringFlag(const char* str, const char* flag, std::string* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + *value = value_str; + return true; +} + +// Determines whether a string has a prefix that Google Test uses for its +// flags, i.e., starts with GTEST_FLAG_PREFIX_ or GTEST_FLAG_PREFIX_DASH_. +// If Google Test detects that a command line flag has its prefix but is not +// recognized, it will print its help message. Flags starting with +// GTEST_INTERNAL_PREFIX_ followed by "internal_" are considered Google Test +// internal flags and do not trigger the help message. +static bool HasGoogleTestFlagPrefix(const char* str) { + return (SkipPrefix("--", &str) || + SkipPrefix("-", &str) || + SkipPrefix("/", &str)) && + !SkipPrefix(GTEST_FLAG_PREFIX_ "internal_", &str) && + (SkipPrefix(GTEST_FLAG_PREFIX_, &str) || + SkipPrefix(GTEST_FLAG_PREFIX_DASH_, &str)); +} + +// Prints a string containing code-encoded text. The following escape +// sequences can be used in the string to control the text color: +// +// @@ prints a single '@' character. +// @R changes the color to red. +// @G changes the color to green. +// @Y changes the color to yellow. +// @D changes to the default terminal text color. +// +// TODO(wan@google.com): Write tests for this once we add stdout +// capturing to Google Test. +static void PrintColorEncoded(const char* str) { + GTestColor color = COLOR_DEFAULT; // The current color. + + // Conceptually, we split the string into segments divided by escape + // sequences. Then we print one segment at a time. At the end of + // each iteration, the str pointer advances to the beginning of the + // next segment. + for (;;) { + const char* p = strchr(str, '@'); + if (p == NULL) { + ColoredPrintf(color, "%s", str); + return; + } + + ColoredPrintf(color, "%s", std::string(str, p).c_str()); + + const char ch = p[1]; + str = p + 2; + if (ch == '@') { + ColoredPrintf(color, "@"); + } else if (ch == 'D') { + color = COLOR_DEFAULT; + } else if (ch == 'R') { + color = COLOR_RED; + } else if (ch == 'G') { + color = COLOR_GREEN; + } else if (ch == 'Y') { + color = COLOR_YELLOW; + } else { + --str; + } + } +} + +static const char kColorEncodedHelpMessage[] = +"This program contains tests written using " GTEST_NAME_ ". You can use the\n" +"following command line flags to control its behavior:\n" +"\n" +"Test Selection:\n" +" @G--" GTEST_FLAG_PREFIX_ "list_tests@D\n" +" List the names of all tests instead of running them. The name of\n" +" TEST(Foo, Bar) is \"Foo.Bar\".\n" +" @G--" GTEST_FLAG_PREFIX_ "filter=@YPOSTIVE_PATTERNS" + "[@G-@YNEGATIVE_PATTERNS]@D\n" +" Run only the tests whose name matches one of the positive patterns but\n" +" none of the negative patterns. '?' matches any single character; '*'\n" +" matches any substring; ':' separates two patterns.\n" +" @G--" GTEST_FLAG_PREFIX_ "also_run_disabled_tests@D\n" +" Run all disabled tests too.\n" +"\n" +"Test Execution:\n" +" @G--" GTEST_FLAG_PREFIX_ "repeat=@Y[COUNT]@D\n" +" Run the tests repeatedly; use a negative count to repeat forever.\n" +" @G--" GTEST_FLAG_PREFIX_ "shuffle@D\n" +" Randomize tests' orders on every iteration.\n" +" @G--" GTEST_FLAG_PREFIX_ "random_seed=@Y[NUMBER]@D\n" +" Random number seed to use for shuffling test orders (between 1 and\n" +" 99999, or 0 to use a seed based on the current time).\n" +"\n" +"Test Output:\n" +" @G--" GTEST_FLAG_PREFIX_ "color=@Y(@Gyes@Y|@Gno@Y|@Gauto@Y)@D\n" +" Enable/disable colored output. The default is @Gauto@D.\n" +" -@G-" GTEST_FLAG_PREFIX_ "print_time=0@D\n" +" Don't print the elapsed time of each test.\n" +" @G--" GTEST_FLAG_PREFIX_ "output=xml@Y[@G:@YDIRECTORY_PATH@G" + GTEST_PATH_SEP_ "@Y|@G:@YFILE_PATH]@D\n" +" Generate an XML report in the given directory or with the given file\n" +" name. @YFILE_PATH@D defaults to @Gtest_details.xml@D.\n" +#if GTEST_CAN_STREAM_RESULTS_ +" @G--" GTEST_FLAG_PREFIX_ "stream_result_to=@YHOST@G:@YPORT@D\n" +" Stream test results to the given server.\n" +#endif // GTEST_CAN_STREAM_RESULTS_ +"\n" +"Assertion Behavior:\n" +#if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS +" @G--" GTEST_FLAG_PREFIX_ "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n" +" Set the default death test style.\n" +#endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS +" @G--" GTEST_FLAG_PREFIX_ "break_on_failure@D\n" +" Turn assertion failures into debugger break-points.\n" +" @G--" GTEST_FLAG_PREFIX_ "throw_on_failure@D\n" +" Turn assertion failures into C++ exceptions.\n" +" @G--" GTEST_FLAG_PREFIX_ "catch_exceptions=0@D\n" +" Do not report exceptions as test failures. Instead, allow them\n" +" to crash the program or throw a pop-up (on Windows).\n" +"\n" +"Except for @G--" GTEST_FLAG_PREFIX_ "list_tests@D, you can alternatively set " + "the corresponding\n" +"environment variable of a flag (all letters in upper-case). For example, to\n" +"disable colored text output, you can either specify @G--" GTEST_FLAG_PREFIX_ + "color=no@D or set\n" +"the @G" GTEST_FLAG_PREFIX_UPPER_ "COLOR@D environment variable to @Gno@D.\n" +"\n" +"For more information, please read the " GTEST_NAME_ " documentation at\n" +"@G" GTEST_PROJECT_URL_ "@D. If you find a bug in " GTEST_NAME_ "\n" +"(not one in your own code or tests), please report it to\n" +"@G<" GTEST_DEV_EMAIL_ ">@D.\n"; + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. The type parameter CharType can be +// instantiated to either char or wchar_t. +template +void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) { + for (int i = 1; i < *argc; i++) { + const std::string arg_string = StreamableToString(argv[i]); + const char* const arg = arg_string.c_str(); + + using internal::ParseBoolFlag; + using internal::ParseInt32Flag; + using internal::ParseStringFlag; + + // Do we see a Google Test flag? + if (ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag, + >EST_FLAG(also_run_disabled_tests)) || + ParseBoolFlag(arg, kBreakOnFailureFlag, + >EST_FLAG(break_on_failure)) || + ParseBoolFlag(arg, kCatchExceptionsFlag, + >EST_FLAG(catch_exceptions)) || + ParseStringFlag(arg, kColorFlag, >EST_FLAG(color)) || + ParseStringFlag(arg, kDeathTestStyleFlag, + >EST_FLAG(death_test_style)) || + ParseBoolFlag(arg, kDeathTestUseFork, + >EST_FLAG(death_test_use_fork)) || + ParseStringFlag(arg, kFilterFlag, >EST_FLAG(filter)) || + ParseStringFlag(arg, kInternalRunDeathTestFlag, + >EST_FLAG(internal_run_death_test)) || + ParseBoolFlag(arg, kListTestsFlag, >EST_FLAG(list_tests)) || + ParseStringFlag(arg, kOutputFlag, >EST_FLAG(output)) || + ParseBoolFlag(arg, kPrintTimeFlag, >EST_FLAG(print_time)) || + ParseInt32Flag(arg, kRandomSeedFlag, >EST_FLAG(random_seed)) || + ParseInt32Flag(arg, kRepeatFlag, >EST_FLAG(repeat)) || + ParseBoolFlag(arg, kShuffleFlag, >EST_FLAG(shuffle)) || + ParseInt32Flag(arg, kStackTraceDepthFlag, + >EST_FLAG(stack_trace_depth)) || + ParseStringFlag(arg, kStreamResultToFlag, + >EST_FLAG(stream_result_to)) || + ParseBoolFlag(arg, kThrowOnFailureFlag, + >EST_FLAG(throw_on_failure)) + ) { + // Yes. Shift the remainder of the argv list left by one. Note + // that argv has (*argc + 1) elements, the last one always being + // NULL. The following loop moves the trailing NULL element as + // well. + for (int j = i; j != *argc; j++) { + argv[j] = argv[j + 1]; + } + + // Decrements the argument count. + (*argc)--; + + // We also need to decrement the iterator as we just removed + // an element. + i--; + } else if (arg_string == "--help" || arg_string == "-h" || + arg_string == "-?" || arg_string == "/?" || + HasGoogleTestFlagPrefix(arg)) { + // Both help flag and unrecognized Google Test flags (excluding + // internal ones) trigger help display. + g_help_flag = true; + } + } + + if (g_help_flag) { + // We print the help here instead of in RUN_ALL_TESTS(), as the + // latter may not be called at all if the user is using Google + // Test with another testing framework. + PrintColorEncoded(kColorEncodedHelpMessage); + } +} + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. +void ParseGoogleTestFlagsOnly(int* argc, char** argv) { + ParseGoogleTestFlagsOnlyImpl(argc, argv); +} +void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv) { + ParseGoogleTestFlagsOnlyImpl(argc, argv); +} + +// The internal implementation of InitGoogleTest(). +// +// The type parameter CharType can be instantiated to either char or +// wchar_t. +template +void InitGoogleTestImpl(int* argc, CharType** argv) { + g_init_gtest_count++; + + // We don't want to run the initialization code twice. + if (g_init_gtest_count != 1) return; + + if (*argc <= 0) return; + + internal::g_executable_path = internal::StreamableToString(argv[0]); + +#if GTEST_HAS_DEATH_TEST + + g_argvs.clear(); + for (int i = 0; i != *argc; i++) { + g_argvs.push_back(StreamableToString(argv[i])); + } + +#endif // GTEST_HAS_DEATH_TEST + + ParseGoogleTestFlagsOnly(argc, argv); + GetUnitTestImpl()->PostFlagParsingInit(); +} + +} // namespace internal + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +void InitGoogleTest(int* argc, char** argv) { + internal::InitGoogleTestImpl(argc, argv); +} + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +void InitGoogleTest(int* argc, wchar_t** argv) { + internal::InitGoogleTestImpl(argc, argv); +} + +} // namespace testing diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest_main.cc b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest_main.cc new file mode 100644 index 00000000..f3028225 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/gtest-1.7.0/src/gtest_main.cc @@ -0,0 +1,38 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "gtest/gtest.h" + +GTEST_API_ int main(int argc, char **argv) { + printf("Running main() from gtest_main.cc\n"); + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/http_parser/LICENSE-MIT b/couchbase-sys/libcouchbase-2.7.0/contrib/http_parser/LICENSE-MIT new file mode 100644 index 00000000..5baf7c04 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/http_parser/LICENSE-MIT @@ -0,0 +1,23 @@ +http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright +Igor Sysoev. + +Additional changes are licensed under the same terms as NGINX and +copyright Joyent, Inc. and other Node contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/http_parser/README.md b/couchbase-sys/libcouchbase-2.7.0/contrib/http_parser/README.md new file mode 100644 index 00000000..3815e361 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/http_parser/README.md @@ -0,0 +1,178 @@ +HTTP Parser +=========== + +This is a parser for HTTP messages written in C. It parses both requests and +responses. The parser is designed to be used in performance HTTP +applications. It does not make any syscalls nor allocations, it does not +buffer data, it can be interrupted at anytime. Depending on your +architecture, it only requires about 40 bytes of data per message +stream (in a web server that is per connection). + +Features: + + * No dependencies + * Handles persistent streams (keep-alive). + * Decodes chunked encoding. + * Upgrade support + * Defends against buffer overflow attacks. + +The parser extracts the following information from HTTP messages: + + * Header fields and values + * Content-Length + * Request method + * Response status code + * Transfer-Encoding + * HTTP version + * Request URL + * Message body + + +Usage +----- + +One `http_parser` object is used per TCP connection. Initialize the struct +using `http_parser_init()` and set the callbacks. That might look something +like this for a request parser: + + http_parser_settings settings; + settings.on_path = my_path_callback; + settings.on_header_field = my_header_field_callback; + /* ... */ + + http_parser *parser = malloc(sizeof(http_parser)); + http_parser_init(parser, HTTP_REQUEST); + parser->data = my_socket; + +When data is received on the socket execute the parser and check for errors. + + size_t len = 80*1024, nparsed; + char buf[len]; + ssize_t recved; + + recved = recv(fd, buf, len, 0); + + if (recved < 0) { + /* Handle error. */ + } + + /* Start up / continue the parser. + * Note we pass recved==0 to signal that EOF has been recieved. + */ + nparsed = http_parser_execute(parser, &settings, buf, recved); + + if (parser->upgrade) { + /* handle new protocol */ + } else if (nparsed != recved) { + /* Handle error. Usually just close the connection. */ + } + +HTTP needs to know where the end of the stream is. For example, sometimes +servers send responses without Content-Length and expect the client to +consume input (for the body) until EOF. To tell http_parser about EOF, give +`0` as the forth parameter to `http_parser_execute()`. Callbacks and errors +can still be encountered during an EOF, so one must still be prepared +to receive them. + +Scalar valued message information such as `status_code`, `method`, and the +HTTP version are stored in the parser structure. This data is only +temporally stored in `http_parser` and gets reset on each new message. If +this information is needed later, copy it out of the structure during the +`headers_complete` callback. + +The parser decodes the transfer-encoding for both requests and responses +transparently. That is, a chunked encoding is decoded before being sent to +the on_body callback. + + +The Special Problem of Upgrade +------------------------------ + +HTTP supports upgrading the connection to a different protocol. An +increasingly common example of this is the Web Socket protocol which sends +a request like + + GET /demo HTTP/1.1 + Upgrade: WebSocket + Connection: Upgrade + Host: example.com + Origin: http://example.com + WebSocket-Protocol: sample + +followed by non-HTTP data. + +(See http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 for more +information the Web Socket protocol.) + +To support this, the parser will treat this as a normal HTTP message without a +body. Issuing both on_headers_complete and on_message_complete callbacks. However +http_parser_execute() will stop parsing at the end of the headers and return. + +The user is expected to check if `parser->upgrade` has been set to 1 after +`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied +offset by the return value of `http_parser_execute()`. + + +Callbacks +--------- + +During the `http_parser_execute()` call, the callbacks set in +`http_parser_settings` will be executed. The parser maintains state and +never looks behind, so buffering the data is not necessary. If you need to +save certain data for later usage, you can do that from the callbacks. + +There are two types of callbacks: + +* notification `typedef int (*http_cb) (http_parser*);` + Callbacks: on_message_begin, on_headers_complete, on_message_complete. +* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);` + Callbacks: (requests only) on_uri, + (common) on_header_field, on_header_value, on_body; + +Callbacks must return 0 on success. Returning a non-zero value indicates +error to the parser, making it exit immediately. + +In case you parse HTTP message in chunks (i.e. `read()` request line +from socket, parse, read half headers, parse, etc) your data callbacks +may be called more than once. Http-parser guarantees that data pointer is only +valid for the lifetime of callback. You can also `read()` into a heap allocated +buffer to avoid copying memory around if this fits your application. + +Reading headers may be a tricky task if you read/parse headers partially. +Basically, you need to remember whether last header callback was field or value +and apply following logic: + + (on_header_field and on_header_value shortened to on_h_*) + ------------------------ ------------ -------------------------------------------- + | State (prev. callback) | Callback | Description/action | + ------------------------ ------------ -------------------------------------------- + | nothing (first call) | on_h_field | Allocate new buffer and copy callback data | + | | | into it | + ------------------------ ------------ -------------------------------------------- + | value | on_h_field | New header started. | + | | | Copy current name,value buffers to headers | + | | | list and allocate new buffer for new name | + ------------------------ ------------ -------------------------------------------- + | field | on_h_field | Previous name continues. Reallocate name | + | | | buffer and append callback data to it | + ------------------------ ------------ -------------------------------------------- + | field | on_h_value | Value for current header started. Allocate | + | | | new buffer and copy callback data to it | + ------------------------ ------------ -------------------------------------------- + | value | on_h_value | Value continues. Reallocate value buffer | + | | | and append callback data to it | + ------------------------ ------------ -------------------------------------------- + + +Parsing URLs +------------ + +A simplistic zero-copy URL parser is provided as `http_parser_parse_url()`. +Users of this library may wish to use it to parse URLs constructed from +consecutive `on_url` callbacks. + +See examples of reading in headers: + +* [partial example](http://gist.github.com/155877) in C +* [from http-parser tests](http://github.com/joyent/http-parser/blob/37a0ff8/test.c#L403) in C +* [from Node library](http://github.com/joyent/node/blob/842eaf4/src/http.js#L284) in Javascript diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/http_parser/http_parser.c b/couchbase-sys/libcouchbase-2.7.0/contrib/http_parser/http_parser.c new file mode 100644 index 00000000..6722f1bc --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/http_parser/http_parser.c @@ -0,0 +1,2060 @@ +/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev + * + * Additional changes are licensed under the same terms as NGINX and + * copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "http_parser.h" +#include +#include +#include +#include +#include +#include + +#if defined(ULLONG_MAX) && __STDC_VERSION__ >= 199901L +# define HTTP_PARSER_ULLONG_MAX ULLONG_MAX +#else +# define HTTP_PARSER_ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ +#endif + +#ifndef MIN +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + + +#if HTTP_PARSER_DEBUG +#define SET_ERRNO(e) \ +do { \ + parser->http_errno = (e); \ + parser->error_lineno = __LINE__; \ +} while (0) +#else +#define SET_ERRNO(e) \ +do { \ + parser->http_errno = (e); \ +} while(0) +#endif + + +/* Run the notify callback FOR, returning ER if it fails */ +#define CALLBACK_NOTIFY_(FOR, ER) \ +do { \ + assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ + \ + if (settings->on_##FOR) { \ + if (0 != settings->on_##FOR(parser)) { \ + SET_ERRNO(HPE_CB_##FOR); \ + } \ + \ + /* We either errored above or got paused; get out */ \ + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \ + return (ER); \ + } \ + } \ +} while (0) + +/* Run the notify callback FOR and consume the current byte */ +#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1) + +/* Run the notify callback FOR and don't consume the current byte */ +#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data) + +/* Run data callback FOR with LEN bytes, returning ER if it fails */ +#define CALLBACK_DATA_(FOR, LEN, ER) \ +do { \ + assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ + \ + if (FOR##_mark) { \ + if (settings->on_##FOR) { \ + if (0 != settings->on_##FOR(parser, FOR##_mark, (LEN))) { \ + SET_ERRNO(HPE_CB_##FOR); \ + } \ + \ + /* We either errored above or got paused; get out */ \ + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \ + return (ER); \ + } \ + } \ + FOR##_mark = NULL; \ + } \ +} while (0) + +/* Run the data callback FOR and consume the current byte */ +#define CALLBACK_DATA(FOR) \ + CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1) + +/* Run the data callback FOR and don't consume the current byte */ +#define CALLBACK_DATA_NOADVANCE(FOR) \ + CALLBACK_DATA_(FOR, p - FOR##_mark, p - data) + +/* Set the mark FOR; non-destructive if mark is already set */ +#define MARK(FOR) \ +do { \ + if (!FOR##_mark) { \ + FOR##_mark = p; \ + } \ +} while (0) + + +#define PROXY_CONNECTION "proxy-connection" +#define CONNECTION "connection" +#define CONTENT_LENGTH "content-length" +#define TRANSFER_ENCODING "transfer-encoding" +#define UPGRADE "upgrade" +#define CHUNKED "chunked" +#define KEEP_ALIVE "keep-alive" +#define CLOSE "close" + + +static const char *method_strings[] = + { "DELETE" + , "GET" + , "HEAD" + , "POST" + , "PUT" + , "CONNECT" + , "OPTIONS" + , "TRACE" + , "COPY" + , "LOCK" + , "MKCOL" + , "MOVE" + , "PROPFIND" + , "PROPPATCH" + , "UNLOCK" + , "REPORT" + , "MKACTIVITY" + , "CHECKOUT" + , "MERGE" + , "M-SEARCH" + , "NOTIFY" + , "SUBSCRIBE" + , "UNSUBSCRIBE" + , "PATCH" + , "PURGE" + }; + + +/* Tokens as defined by rfc 2616. Also lowercases them. + * token = 1* + * separators = "(" | ")" | "<" | ">" | "@" + * | "," | ";" | ":" | "\" | <"> + * | "/" | "[" | "]" | "?" | "=" + * | "{" | "}" | SP | HT + */ +static const char tokens[256] = { +/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + 0, '!', 0, '#', '$', '%', '&', '\'', +/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 0, 0, '*', '+', 0, '-', '.', 0, +/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + '0', '1', '2', '3', '4', '5', '6', '7', +/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + '8', '9', 0, 0, 0, 0, 0, 0, +/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', +/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', +/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 'x', 'y', 'z', 0, 0, 0, '^', '_', +/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', +/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', +/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 'x', 'y', 'z', 0, '|', 0, '~', 0 }; + + +static const int8_t unhex[256] = + {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 + ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + }; + + +static const uint8_t normal_url_char[256] = { +/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + 0, 1, 1, 0, 1, 1, 1, 1, +/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 1, 1, 1, 1, 1, 1, 1, 1, +/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + 1, 1, 1, 1, 1, 1, 1, 1, +/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + 1, 1, 1, 1, 1, 1, 1, 0, +/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 1, 1, 1, 1, 1, 1, 1, 1, +/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 1, 1, 1, 1, 1, 1, 1, 1, +/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 1, 1, 1, 1, 1, 1, 1, 1, +/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 1, 1, 1, 1, 1, 1, 1, 1, +/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + 1, 1, 1, 1, 1, 1, 1, 1, +/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 1, 1, 1, 1, 1, 1, 1, 1, +/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 1, 1, 1, 1, 1, 1, 1, 1, +/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 1, 1, 1, 1, 1, 1, 1, 0, }; + + +enum state + { s_dead = 1 /* important that this is > 0 */ + + , s_start_req_or_res + , s_res_or_resp_H + , s_start_res + , s_res_H + , s_res_HT + , s_res_HTT + , s_res_HTTP + , s_res_first_http_major + , s_res_http_major + , s_res_first_http_minor + , s_res_http_minor + , s_res_first_status_code + , s_res_status_code + , s_res_status + , s_res_line_almost_done + + , s_start_req + + , s_req_method + , s_req_spaces_before_url + , s_req_schema + , s_req_schema_slash + , s_req_schema_slash_slash + , s_req_host_start + , s_req_host_v6_start + , s_req_host_v6 + , s_req_host_v6_end + , s_req_host + , s_req_port_start + , s_req_port + , s_req_path + , s_req_query_string_start + , s_req_query_string + , s_req_fragment_start + , s_req_fragment + , s_req_http_start + , s_req_http_H + , s_req_http_HT + , s_req_http_HTT + , s_req_http_HTTP + , s_req_first_http_major + , s_req_http_major + , s_req_first_http_minor + , s_req_http_minor + , s_req_line_almost_done + + , s_header_field_start + , s_header_field + , s_header_value_start + , s_header_value + , s_header_value_lws + + , s_header_almost_done + + , s_chunk_size_start + , s_chunk_size + , s_chunk_parameters + , s_chunk_size_almost_done + + , s_headers_almost_done + , s_headers_done + + /* Important: 's_headers_done' must be the last 'header' state. All + * states beyond this must be 'body' states. It is used for overflow + * checking. See the PARSING_HEADER() macro. + */ + + , s_chunk_data + , s_chunk_data_almost_done + , s_chunk_data_done + + , s_body_identity + , s_body_identity_eof + + , s_message_done + }; + + +#define PARSING_HEADER(state) (state <= s_headers_done) + + +enum header_states + { h_general = 0 + , h_C + , h_CO + , h_CON + + , h_matching_connection + , h_matching_proxy_connection + , h_matching_content_length + , h_matching_transfer_encoding + , h_matching_upgrade + + , h_connection + , h_content_length + , h_transfer_encoding + , h_upgrade + + , h_matching_transfer_encoding_chunked + , h_matching_connection_keep_alive + , h_matching_connection_close + + , h_transfer_encoding_chunked + , h_connection_keep_alive + , h_connection_close + }; + + +/* Macros for character classes; depends on strict-mode */ +#define CR '\r' +#define LF '\n' +#define LOWER(c) (unsigned char)(c | 0x20) +#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') +#define IS_NUM(c) ((c) >= '0' && (c) <= '9') +#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) +#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) + +#if HTTP_PARSER_STRICT +#define TOKEN(c) (tokens[(unsigned char)c]) +#define IS_URL_CHAR(c) (normal_url_char[(unsigned char) (c)]) +#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') +#else +#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) +#define IS_URL_CHAR(c) \ + (normal_url_char[(unsigned char) (c)] || ((c) & 0x80)) +#define IS_HOST_CHAR(c) \ + (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') +#endif + + +#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) + + +#if HTTP_PARSER_STRICT +# define STRICT_CHECK(cond) \ +do { \ + if (cond) { \ + SET_ERRNO(HPE_STRICT); \ + goto error; \ + } \ +} while (0) +# define NEW_MESSAGE() (_lcb_http_should_keep_alive(parser) ? start_state : s_dead) +#else +# define STRICT_CHECK(cond) +# define NEW_MESSAGE() start_state +#endif + + +/* Map errno values to strings for human-readable output */ +#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s }, +static struct { + const char *name; + const char *description; +} http_strerror_tab[] = { + HTTP_ERRNO_MAP(HTTP_STRERROR_GEN) +}; +#undef HTTP_STRERROR_GEN + +int http_message_needs_eof(http_parser *parser); + +/* Our URL parser. + * + * This is designed to be shared by http_parser_execute() for URL validation, + * hence it has a state transition + byte-for-byte interface. In addition, it + * is meant to be embedded in http_parser_parse_url(), which does the dirty + * work of turning state transitions URL components for its API. + * + * This function should only be invoked with non-space characters. It is + * assumed that the caller cares about (and can detect) the transition between + * URL and non-URL states by looking for these. + */ +static enum state +parse_url_char(enum state s, const char ch) +{ + assert(!isspace(ch)); + + switch (s) { + case s_req_spaces_before_url: + /* Proxied requests are followed by scheme of an absolute URI (alpha). + * All methods except CONNECT are followed by '/' or '*'. + */ + + if (ch == '/' || ch == '*') { + return s_req_path; + } + + if (IS_ALPHA(ch)) { + return s_req_schema; + } + + break; + + case s_req_schema: + if (IS_ALPHA(ch)) { + return s; + } + + if (ch == ':') { + return s_req_schema_slash; + } + + break; + + case s_req_schema_slash: + if (ch == '/') { + return s_req_schema_slash_slash; + } + + break; + + case s_req_schema_slash_slash: + if (ch == '/') { + return s_req_host_start; + } + + break; + + case s_req_host_start: + if (ch == '[') { + return s_req_host_v6_start; + } + + if (IS_HOST_CHAR(ch)) { + return s_req_host; + } + + break; + + case s_req_host: + if (IS_HOST_CHAR(ch)) { + return s_req_host; + } + + /* FALLTHROUGH */ + case s_req_host_v6_end: + switch (ch) { + case ':': + return s_req_port_start; + + case '/': + return s_req_path; + + case '?': + return s_req_query_string_start; + } + + break; + + case s_req_host_v6: + if (ch == ']') { + return s_req_host_v6_end; + } + + /* FALLTHROUGH */ + case s_req_host_v6_start: + if (IS_HEX(ch) || ch == ':') { + return s_req_host_v6; + } + break; + + case s_req_port: + switch (ch) { + case '/': + return s_req_path; + + case '?': + return s_req_query_string_start; + } + + /* FALLTHROUGH */ + case s_req_port_start: + if (IS_NUM(ch)) { + return s_req_port; + } + + break; + + case s_req_path: + if (IS_URL_CHAR(ch)) { + return s; + } + + switch (ch) { + case '?': + return s_req_query_string_start; + + case '#': + return s_req_fragment_start; + } + + break; + + case s_req_query_string_start: + case s_req_query_string: + if (IS_URL_CHAR(ch)) { + return s_req_query_string; + } + + switch (ch) { + case '?': + /* allow extra '?' in query string */ + return s_req_query_string; + + case '#': + return s_req_fragment_start; + } + + break; + + case s_req_fragment_start: + if (IS_URL_CHAR(ch)) { + return s_req_fragment; + } + + switch (ch) { + case '?': + return s_req_fragment; + + case '#': + return s; + } + + break; + + case s_req_fragment: + if (IS_URL_CHAR(ch)) { + return s; + } + + switch (ch) { + case '?': + case '#': + return s; + } + + break; + + default: + break; + } + + /* We should never fall out of the switch above unless there's an error */ + return s_dead; +} + +size_t _lcb_http_parser_execute (http_parser *parser, + const http_parser_settings *settings, + const char *data, + size_t len) +{ + char c, ch; + int8_t unhex_val; + const char *p = data; + const char *header_field_mark = 0; + const char *header_value_mark = 0; + const char *url_mark = 0; + const char *body_mark = 0; + + /* We're in an error state. Don't bother doing anything. */ + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { + return 0; + } + + if (len == 0) { + switch (parser->state) { + case s_body_identity_eof: + /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if + * we got paused. + */ + CALLBACK_NOTIFY_NOADVANCE(message_complete); + return 0; + + case s_dead: + case s_start_req_or_res: + case s_start_res: + case s_start_req: + return 0; + + default: + SET_ERRNO(HPE_INVALID_EOF_STATE); + return 1; + } + } + + + if (parser->state == s_header_field) + header_field_mark = data; + if (parser->state == s_header_value) + header_value_mark = data; + switch (parser->state) { + case s_req_path: + case s_req_schema: + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_host_start: + case s_req_host_v6_start: + case s_req_host_v6: + case s_req_host_v6_end: + case s_req_host: + case s_req_port_start: + case s_req_port: + case s_req_query_string_start: + case s_req_query_string: + case s_req_fragment_start: + case s_req_fragment: + url_mark = data; + break; + } + + for (p=data; p != data + len; p++) { + ch = *p; + + if (PARSING_HEADER(parser->state)) { + ++parser->nread; + /* Buffer overflow attack */ + if (parser->nread > HTTP_MAX_HEADER_SIZE) { + SET_ERRNO(HPE_HEADER_OVERFLOW); + goto error; + } + } + + reexecute_byte: + switch (parser->state) { + + case s_dead: + /* this state is used after a 'Connection: close' message + * the parser will error out if it reads another message + */ + if (ch == CR || ch == LF) + break; + + SET_ERRNO(HPE_CLOSED_CONNECTION); + goto error; + + case s_start_req_or_res: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = HTTP_PARSER_ULLONG_MAX; + + if (ch == 'H') { + parser->state = s_res_or_resp_H; + + CALLBACK_NOTIFY(message_begin); + } else { + parser->type = HTTP_REQUEST; + parser->state = s_start_req; + goto reexecute_byte; + } + + break; + } + + case s_res_or_resp_H: + if (ch == 'T') { + parser->type = HTTP_RESPONSE; + parser->state = s_res_HT; + } else { + if (ch != 'E') { + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + + parser->type = HTTP_REQUEST; + parser->method = HTTP_HEAD; + parser->index = 2; + parser->state = s_req_method; + } + break; + + case s_start_res: + { + parser->flags = 0; + parser->content_length = HTTP_PARSER_ULLONG_MAX; + + switch (ch) { + case 'H': + parser->state = s_res_H; + break; + + case CR: + case LF: + break; + + default: + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + + CALLBACK_NOTIFY(message_begin); + break; + } + + case s_res_H: + STRICT_CHECK(ch != 'T'); + parser->state = s_res_HT; + break; + + case s_res_HT: + STRICT_CHECK(ch != 'T'); + parser->state = s_res_HTT; + break; + + case s_res_HTT: + STRICT_CHECK(ch != 'P'); + parser->state = s_res_HTTP; + break; + + case s_res_HTTP: + STRICT_CHECK(ch != '/'); + parser->state = s_res_first_http_major; + break; + + case s_res_first_http_major: + if (ch < '0' || ch > '9') { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major = ch - '0'; + parser->state = s_res_http_major; + break; + + /* major HTTP version or dot */ + case s_res_http_major: + { + if (ch == '.') { + parser->state = s_res_first_http_minor; + break; + } + + if (!IS_NUM(ch)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major *= 10; + parser->http_major += ch - '0'; + + if (parser->http_major > 999) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + break; + } + + /* first digit of minor HTTP version */ + case s_res_first_http_minor: + if (!IS_NUM(ch)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor = ch - '0'; + parser->state = s_res_http_minor; + break; + + /* minor HTTP version or end of request line */ + case s_res_http_minor: + { + if (ch == ' ') { + parser->state = s_res_first_status_code; + break; + } + + if (!IS_NUM(ch)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor *= 10; + parser->http_minor += ch - '0'; + + if (parser->http_minor > 999) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + break; + } + + case s_res_first_status_code: + { + if (!IS_NUM(ch)) { + if (ch == ' ') { + break; + } + + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + parser->status_code = ch - '0'; + parser->state = s_res_status_code; + break; + } + + case s_res_status_code: + { + if (!IS_NUM(ch)) { + switch (ch) { + case ' ': + parser->state = s_res_status; + break; + case CR: + parser->state = s_res_line_almost_done; + break; + case LF: + parser->state = s_header_field_start; + break; + default: + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + break; + } + + parser->status_code *= 10; + parser->status_code += ch - '0'; + + if (parser->status_code > 999) { + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + + break; + } + + case s_res_status: + /* the human readable status. e.g. "NOT FOUND" + * we are not humans so just ignore this */ + if (ch == CR) { + parser->state = s_res_line_almost_done; + break; + } + + if (ch == LF) { + parser->state = s_header_field_start; + break; + } + break; + + case s_res_line_almost_done: + STRICT_CHECK(ch != LF); + parser->state = s_header_field_start; + break; + + case s_start_req: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = HTTP_PARSER_ULLONG_MAX; + + if (!IS_ALPHA(ch)) { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + parser->method = (enum http_method) 0; + parser->index = 1; + switch (ch) { + case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break; + case 'D': parser->method = HTTP_DELETE; break; + case 'G': parser->method = HTTP_GET; break; + case 'H': parser->method = HTTP_HEAD; break; + case 'L': parser->method = HTTP_LOCK; break; + case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break; + case 'N': parser->method = HTTP_NOTIFY; break; + case 'O': parser->method = HTTP_OPTIONS; break; + case 'P': parser->method = HTTP_POST; + /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ + break; + case 'R': parser->method = HTTP_REPORT; break; + case 'S': parser->method = HTTP_SUBSCRIBE; break; + case 'T': parser->method = HTTP_TRACE; break; + case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break; + default: + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + parser->state = s_req_method; + + CALLBACK_NOTIFY(message_begin); + + break; + } + + case s_req_method: + { + const char *matcher; + if (ch == '\0') { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + matcher = method_strings[parser->method]; + if (ch == ' ' && matcher[parser->index] == '\0') { + parser->state = s_req_spaces_before_url; + } else if (ch == matcher[parser->index]) { + ; /* nada */ + } else if (parser->method == HTTP_CONNECT) { + if (parser->index == 1 && ch == 'H') { + parser->method = HTTP_CHECKOUT; + } else if (parser->index == 2 && ch == 'P') { + parser->method = HTTP_COPY; + } else { + goto error; + } + } else if (parser->method == HTTP_MKCOL) { + if (parser->index == 1 && ch == 'O') { + parser->method = HTTP_MOVE; + } else if (parser->index == 1 && ch == 'E') { + parser->method = HTTP_MERGE; + } else if (parser->index == 1 && ch == '-') { + parser->method = HTTP_MSEARCH; + } else if (parser->index == 2 && ch == 'A') { + parser->method = HTTP_MKACTIVITY; + } else { + goto error; + } + } else if (parser->index == 1 && parser->method == HTTP_POST) { + if (ch == 'R') { + parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */ + } else if (ch == 'U') { + parser->method = HTTP_PUT; /* or HTTP_PURGE */ + } else if (ch == 'A') { + parser->method = HTTP_PATCH; + } else { + goto error; + } + } else if (parser->index == 2) { + if (parser->method == HTTP_PUT) { + if (ch == 'R') parser->method = HTTP_PURGE; + } else if (parser->method == HTTP_UNLOCK) { + if (ch == 'S') parser->method = HTTP_UNSUBSCRIBE; + } + } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') { + parser->method = HTTP_PROPPATCH; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + ++parser->index; + break; + } + + case s_req_spaces_before_url: + { + if (ch == ' ') break; + + MARK(url); + if (parser->method == HTTP_CONNECT) { + parser->state = s_req_host_start; + } + + parser->state = parse_url_char((enum state)parser->state, ch); + if (parser->state == s_dead) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + + break; + } + + case s_req_schema: + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_host_start: + case s_req_host_v6_start: + case s_req_host_v6: + case s_req_port_start: + { + switch (ch) { + /* No whitespace allowed here */ + case ' ': + case CR: + case LF: + SET_ERRNO(HPE_INVALID_URL); + goto error; + default: + parser->state = parse_url_char((enum state)parser->state, ch); + if (parser->state == s_dead) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + } + + break; + } + + case s_req_host: + case s_req_host_v6_end: + case s_req_port: + case s_req_path: + case s_req_query_string_start: + case s_req_query_string: + case s_req_fragment_start: + case s_req_fragment: + { + switch (ch) { + case ' ': + parser->state = s_req_http_start; + CALLBACK_DATA(url); + break; + case CR: + case LF: + parser->http_major = 0; + parser->http_minor = 9; + parser->state = (ch == CR) ? + s_req_line_almost_done : + s_header_field_start; + CALLBACK_DATA(url); + break; + default: + parser->state = parse_url_char((enum state)parser->state, ch); + if (parser->state == s_dead) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + } + break; + } + + case s_req_http_start: + switch (ch) { + case 'H': + parser->state = s_req_http_H; + break; + case ' ': + break; + default: + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + break; + + case s_req_http_H: + STRICT_CHECK(ch != 'T'); + parser->state = s_req_http_HT; + break; + + case s_req_http_HT: + STRICT_CHECK(ch != 'T'); + parser->state = s_req_http_HTT; + break; + + case s_req_http_HTT: + STRICT_CHECK(ch != 'P'); + parser->state = s_req_http_HTTP; + break; + + case s_req_http_HTTP: + STRICT_CHECK(ch != '/'); + parser->state = s_req_first_http_major; + break; + + /* first digit of major HTTP version */ + case s_req_first_http_major: + if (ch < '1' || ch > '9') { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major = ch - '0'; + parser->state = s_req_http_major; + break; + + /* major HTTP version or dot */ + case s_req_http_major: + { + if (ch == '.') { + parser->state = s_req_first_http_minor; + break; + } + + if (!IS_NUM(ch)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major *= 10; + parser->http_major += ch - '0'; + + if (parser->http_major > 999) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + break; + } + + /* first digit of minor HTTP version */ + case s_req_first_http_minor: + if (!IS_NUM(ch)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor = ch - '0'; + parser->state = s_req_http_minor; + break; + + /* minor HTTP version or end of request line */ + case s_req_http_minor: + { + if (ch == CR) { + parser->state = s_req_line_almost_done; + break; + } + + if (ch == LF) { + parser->state = s_header_field_start; + break; + } + + /* XXX allow spaces after digit? */ + + if (!IS_NUM(ch)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor *= 10; + parser->http_minor += ch - '0'; + + if (parser->http_minor > 999) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + break; + } + + /* end of request line */ + case s_req_line_almost_done: + { + if (ch != LF) { + SET_ERRNO(HPE_LF_EXPECTED); + goto error; + } + + parser->state = s_header_field_start; + break; + } + + case s_header_field_start: + { + if (ch == CR) { + parser->state = s_headers_almost_done; + break; + } + + if (ch == LF) { + /* they might be just sending \n instead of \r\n so this would be + * the second \n to denote the end of headers*/ + parser->state = s_headers_almost_done; + goto reexecute_byte; + } + + c = TOKEN(ch); + + if (!c) { + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + + MARK(header_field); + + parser->index = 0; + parser->state = s_header_field; + + switch (c) { + case 'c': + parser->header_state = h_C; + break; + + case 'p': + parser->header_state = h_matching_proxy_connection; + break; + + case 't': + parser->header_state = h_matching_transfer_encoding; + break; + + case 'u': + parser->header_state = h_matching_upgrade; + break; + + default: + parser->header_state = h_general; + break; + } + break; + } + + case s_header_field: + { + c = TOKEN(ch); + + if (c) { + switch (parser->header_state) { + case h_general: + break; + + case h_C: + parser->index++; + parser->header_state = (c == 'o' ? h_CO : h_general); + break; + + case h_CO: + parser->index++; + parser->header_state = (c == 'n' ? h_CON : h_general); + break; + + case h_CON: + parser->index++; + switch (c) { + case 'n': + parser->header_state = h_matching_connection; + break; + case 't': + parser->header_state = h_matching_content_length; + break; + default: + parser->header_state = h_general; + break; + } + break; + + /* connection */ + + case h_matching_connection: + parser->index++; + if (parser->index > sizeof(CONNECTION)-1 + || c != CONNECTION[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(CONNECTION)-2) { + parser->header_state = h_connection; + } + break; + + /* proxy-connection */ + + case h_matching_proxy_connection: + parser->index++; + if (parser->index > sizeof(PROXY_CONNECTION)-1 + || c != PROXY_CONNECTION[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(PROXY_CONNECTION)-2) { + parser->header_state = h_connection; + } + break; + + /* content-length */ + + case h_matching_content_length: + parser->index++; + if (parser->index > sizeof(CONTENT_LENGTH)-1 + || c != CONTENT_LENGTH[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(CONTENT_LENGTH)-2) { + parser->header_state = h_content_length; + } + break; + + /* transfer-encoding */ + + case h_matching_transfer_encoding: + parser->index++; + if (parser->index > sizeof(TRANSFER_ENCODING)-1 + || c != TRANSFER_ENCODING[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { + parser->header_state = h_transfer_encoding; + } + break; + + /* upgrade */ + + case h_matching_upgrade: + parser->index++; + if (parser->index > sizeof(UPGRADE)-1 + || c != UPGRADE[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(UPGRADE)-2) { + parser->header_state = h_upgrade; + } + break; + + case h_connection: + case h_content_length: + case h_transfer_encoding: + case h_upgrade: + if (ch != ' ') parser->header_state = h_general; + break; + + default: + assert(0 && "Unknown header_state"); + break; + } + break; + } + + if (ch == ':') { + parser->state = s_header_value_start; + CALLBACK_DATA(header_field); + break; + } + + if (ch == CR) { + parser->state = s_header_almost_done; + CALLBACK_DATA(header_field); + break; + } + + if (ch == LF) { + parser->state = s_header_field_start; + CALLBACK_DATA(header_field); + break; + } + + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + + case s_header_value_start: + { + if (ch == ' ' || ch == '\t') break; + + MARK(header_value); + + parser->state = s_header_value; + parser->index = 0; + + if (ch == CR) { + parser->header_state = h_general; + parser->state = s_header_almost_done; + CALLBACK_DATA(header_value); + break; + } + + if (ch == LF) { + parser->state = s_header_field_start; + CALLBACK_DATA(header_value); + break; + } + + c = LOWER(ch); + + switch (parser->header_state) { + case h_upgrade: + parser->flags |= F_UPGRADE; + parser->header_state = h_general; + break; + + case h_transfer_encoding: + /* looking for 'Transfer-Encoding: chunked' */ + if ('c' == c) { + parser->header_state = h_matching_transfer_encoding_chunked; + } else { + parser->header_state = h_general; + } + break; + + case h_content_length: + if (!IS_NUM(ch)) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } + + parser->content_length = ch - '0'; + break; + + case h_connection: + /* looking for 'Connection: keep-alive' */ + if (c == 'k') { + parser->header_state = h_matching_connection_keep_alive; + /* looking for 'Connection: close' */ + } else if (c == 'c') { + parser->header_state = h_matching_connection_close; + } else { + parser->header_state = h_general; + } + break; + + default: + parser->header_state = h_general; + break; + } + break; + } + + case s_header_value: + { + + if (ch == CR) { + parser->state = s_header_almost_done; + CALLBACK_DATA(header_value); + break; + } + + if (ch == LF) { + parser->state = s_header_almost_done; + CALLBACK_DATA_NOADVANCE(header_value); + goto reexecute_byte; + } + + c = LOWER(ch); + + switch (parser->header_state) { + case h_general: + break; + + case h_connection: + case h_transfer_encoding: + assert(0 && "Shouldn't get here."); + break; + + case h_content_length: + { + uint64_t t; + + if (ch == ' ') break; + + if (!IS_NUM(ch)) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } + + t = parser->content_length; + t *= 10; + t += ch - '0'; + + /* Overflow? */ + if (t < parser->content_length || t == HTTP_PARSER_ULLONG_MAX) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } + + parser->content_length = t; + break; + } + + /* Transfer-Encoding: chunked */ + case h_matching_transfer_encoding_chunked: + parser->index++; + if (parser->index > sizeof(CHUNKED)-1 + || c != CHUNKED[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(CHUNKED)-2) { + parser->header_state = h_transfer_encoding_chunked; + } + break; + + /* looking for 'Connection: keep-alive' */ + case h_matching_connection_keep_alive: + parser->index++; + if (parser->index > sizeof(KEEP_ALIVE)-1 + || c != KEEP_ALIVE[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(KEEP_ALIVE)-2) { + parser->header_state = h_connection_keep_alive; + } + break; + + /* looking for 'Connection: close' */ + case h_matching_connection_close: + parser->index++; + if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(CLOSE)-2) { + parser->header_state = h_connection_close; + } + break; + + case h_transfer_encoding_chunked: + case h_connection_keep_alive: + case h_connection_close: + if (ch != ' ') parser->header_state = h_general; + break; + + default: + parser->state = s_header_value; + parser->header_state = h_general; + break; + } + break; + } + + case s_header_almost_done: + { + STRICT_CHECK(ch != LF); + + parser->state = s_header_value_lws; + + switch (parser->header_state) { + case h_connection_keep_alive: + parser->flags |= F_CONNECTION_KEEP_ALIVE; + break; + case h_connection_close: + parser->flags |= F_CONNECTION_CLOSE; + break; + case h_transfer_encoding_chunked: + parser->flags |= F_CHUNKED; + break; + default: + break; + } + + break; + } + + case s_header_value_lws: + { + if (ch == ' ' || ch == '\t') + parser->state = s_header_value_start; + else + { + parser->state = s_header_field_start; + goto reexecute_byte; + } + break; + } + + case s_headers_almost_done: + { + STRICT_CHECK(ch != LF); + + if (parser->flags & F_TRAILING) { + /* End of a chunked request */ + parser->state = NEW_MESSAGE(); + CALLBACK_NOTIFY(message_complete); + break; + } + + parser->state = s_headers_done; + + /* Set this here so that on_headers_complete() callbacks can see it */ + parser->upgrade = + (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT); + + /* Here we call the headers_complete callback. This is somewhat + * different than other callbacks because if the user returns 1, we + * will interpret that as saying that this message has no body. This + * is needed for the annoying case of recieving a response to a HEAD + * request. + * + * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so + * we have to simulate it by handling a change in errno below. + */ + if (settings->on_headers_complete) { + switch (settings->on_headers_complete(parser)) { + case 0: + break; + + case 1: + parser->flags |= F_SKIPBODY; + break; + + default: + SET_ERRNO(HPE_CB_headers_complete); + return p - data; /* Error */ + } + } + + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { + return p - data; + } + + goto reexecute_byte; + } + + case s_headers_done: + { + STRICT_CHECK(ch != LF); + + parser->nread = 0; + + /* Exit, the rest of the connect is in a different protocol. */ + if (parser->upgrade) { + parser->state = NEW_MESSAGE(); + CALLBACK_NOTIFY(message_complete); + return (p - data) + 1; + } + + if (parser->flags & F_SKIPBODY) { + parser->state = NEW_MESSAGE(); + CALLBACK_NOTIFY(message_complete); + } else if (parser->flags & F_CHUNKED) { + /* chunked encoding - ignore Content-Length header */ + parser->state = s_chunk_size_start; + } else { + if (parser->content_length == 0) { + /* Content-Length header given but zero: Content-Length: 0\r\n */ + parser->state = NEW_MESSAGE(); + CALLBACK_NOTIFY(message_complete); + } else if (parser->content_length != HTTP_PARSER_ULLONG_MAX) { + /* Content-Length header given and non-zero */ + parser->state = s_body_identity; + } else { + if (parser->type == HTTP_REQUEST || + !http_message_needs_eof(parser)) { + /* Assume content-length 0 - read the next */ + parser->state = NEW_MESSAGE(); + CALLBACK_NOTIFY(message_complete); + } else { + /* Read body until EOF */ + parser->state = s_body_identity_eof; + } + } + } + + break; + } + + case s_body_identity: + { + uint64_t to_read = MIN(parser->content_length, + (uint64_t) ((data + len) - p)); + + assert(parser->content_length != 0 + && parser->content_length != HTTP_PARSER_ULLONG_MAX); + + /* The difference between advancing content_length and p is because + * the latter will automaticaly advance on the next loop iteration. + * Further, if content_length ends up at 0, we want to see the last + * byte again for our message complete callback. + */ + MARK(body); + parser->content_length -= to_read; + p += to_read - 1; + + if (parser->content_length == 0) { + parser->state = s_message_done; + + /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. + * + * The alternative to doing this is to wait for the next byte to + * trigger the data callback, just as in every other case. The + * problem with this is that this makes it difficult for the test + * harness to distinguish between complete-on-EOF and + * complete-on-length. It's not clear that this distinction is + * important for applications, but let's keep it for now. + */ + CALLBACK_DATA_(body, p - body_mark + 1, p - data); + goto reexecute_byte; + } + + break; + } + + /* read until EOF */ + case s_body_identity_eof: + MARK(body); + p = data + len - 1; + + break; + + case s_message_done: + parser->state = NEW_MESSAGE(); + CALLBACK_NOTIFY(message_complete); + break; + + case s_chunk_size_start: + { + assert(parser->nread == 1); + assert(parser->flags & F_CHUNKED); + + unhex_val = unhex[(unsigned char)ch]; + if (unhex_val == -1) { + SET_ERRNO(HPE_INVALID_CHUNK_SIZE); + goto error; + } + + parser->content_length = unhex_val; + parser->state = s_chunk_size; + break; + } + + case s_chunk_size: + { + uint64_t t; + + assert(parser->flags & F_CHUNKED); + + if (ch == CR) { + parser->state = s_chunk_size_almost_done; + break; + } + + unhex_val = unhex[(unsigned char)ch]; + + if (unhex_val == -1) { + if (ch == ';' || ch == ' ') { + parser->state = s_chunk_parameters; + break; + } + + SET_ERRNO(HPE_INVALID_CHUNK_SIZE); + goto error; + } + + t = parser->content_length; + t *= 16; + t += unhex_val; + + /* Overflow? */ + if (t < parser->content_length || t == HTTP_PARSER_ULLONG_MAX) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } + + parser->content_length = t; + break; + } + + case s_chunk_parameters: + { + assert(parser->flags & F_CHUNKED); + /* just ignore this shit. TODO check for overflow */ + if (ch == CR) { + parser->state = s_chunk_size_almost_done; + break; + } + break; + } + + case s_chunk_size_almost_done: + { + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != LF); + + parser->nread = 0; + + if (parser->content_length == 0) { + parser->flags |= F_TRAILING; + parser->state = s_header_field_start; + } else { + parser->state = s_chunk_data; + } + break; + } + + case s_chunk_data: + { + uint64_t to_read = MIN(parser->content_length, + (uint64_t) ((data + len) - p)); + + assert(parser->flags & F_CHUNKED); + assert(parser->content_length != 0 + && parser->content_length != HTTP_PARSER_ULLONG_MAX); + + /* See the explanation in s_body_identity for why the content + * length and data pointers are managed this way. + */ + MARK(body); + parser->content_length -= to_read; + p += to_read - 1; + + if (parser->content_length == 0) { + parser->state = s_chunk_data_almost_done; + } + + break; + } + + case s_chunk_data_almost_done: + assert(parser->flags & F_CHUNKED); + assert(parser->content_length == 0); + STRICT_CHECK(ch != CR); + parser->state = s_chunk_data_done; + CALLBACK_DATA(body); + break; + + case s_chunk_data_done: + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != LF); + parser->nread = 0; + parser->state = s_chunk_size_start; + break; + + default: + assert(0 && "unhandled state"); + SET_ERRNO(HPE_INVALID_INTERNAL_STATE); + goto error; + } + } + + /* Run callbacks for any marks that we have leftover after we ran our of + * bytes. There should be at most one of these set, so it's OK to invoke + * them in series (unset marks will not result in callbacks). + * + * We use the NOADVANCE() variety of callbacks here because 'p' has already + * overflowed 'data' and this allows us to correct for the off-by-one that + * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p' + * value that's in-bounds). + */ + + assert(((header_field_mark ? 1 : 0) + + (header_value_mark ? 1 : 0) + + (url_mark ? 1 : 0) + + (body_mark ? 1 : 0)) <= 1); + + CALLBACK_DATA_NOADVANCE(header_field); + CALLBACK_DATA_NOADVANCE(header_value); + CALLBACK_DATA_NOADVANCE(url); + CALLBACK_DATA_NOADVANCE(body); + + return len; + +error: + if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { + SET_ERRNO(HPE_UNKNOWN); + } + + return (p - data); +} + + +/* Does the parser need to see an EOF to find the end of the message? */ +int +http_message_needs_eof (http_parser *parser) +{ + if (parser->type == HTTP_REQUEST) { + return 0; + } + + /* See RFC 2616 section 4.4 */ + if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ + parser->status_code == 204 || /* No Content */ + parser->status_code == 304 || /* Not Modified */ + parser->flags & F_SKIPBODY) { /* response to a HEAD request */ + return 0; + } + + if ((parser->flags & F_CHUNKED) || parser->content_length != HTTP_PARSER_ULLONG_MAX) { + return 0; + } + + return 1; +} + + +int +_lcb_http_should_keep_alive (http_parser *parser) +{ + if (parser->http_major > 0 && parser->http_minor > 0) { + /* HTTP/1.1 */ + if (parser->flags & F_CONNECTION_CLOSE) { + return 0; + } + } else { + /* HTTP/1.0 or earlier */ + if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { + return 0; + } + } + + return !http_message_needs_eof(parser); +} + + +const char * _lcb_http_method_str (enum http_method m) +{ + return method_strings[m]; +} + + +void +_lcb_http_parser_init (http_parser *parser, enum http_parser_type t) +{ + void *data = parser->data; /* preserve application data */ + memset(parser, 0, sizeof(*parser)); + parser->data = data; + parser->type = t; + parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); + parser->http_errno = HPE_OK; +} + +const char * +_lcb_http_errno_name(enum http_errno err) { + assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0]))); + return http_strerror_tab[err].name; +} + +const char * +_lcb_http_errno_description(enum http_errno err) { + assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0]))); + return http_strerror_tab[err].description; +} + +int +_lcb_http_parser_parse_url(const char *buf, size_t buflen, int is_connect, + struct http_parser_url *u) +{ + enum state s; + const char *p; + enum http_parser_url_fields uf, old_uf; + + u->port = u->field_set = 0; + s = is_connect ? s_req_host_start : s_req_spaces_before_url; + uf = old_uf = UF_MAX; + + for (p = buf; p < buf + buflen; p++) { + s = parse_url_char(s, *p); + + /* Figure out the next field that we're operating on */ + switch (s) { + case s_dead: + return 1; + + /* Skip delimeters */ + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_host_start: + case s_req_host_v6_start: + case s_req_host_v6_end: + case s_req_port_start: + case s_req_query_string_start: + case s_req_fragment_start: + continue; + + case s_req_schema: + uf = UF_SCHEMA; + break; + + case s_req_host: + case s_req_host_v6: + uf = UF_HOST; + break; + + case s_req_port: + uf = UF_PORT; + break; + + case s_req_path: + uf = UF_PATH; + break; + + case s_req_query_string: + uf = UF_QUERY; + break; + + case s_req_fragment: + uf = UF_FRAGMENT; + break; + + default: + assert(!"Unexpected state"); + return 1; + } + + /* Nothing's changed; soldier on */ + if (uf == old_uf) { + u->field_data[uf].len++; + continue; + } + + u->field_data[uf].off = (uint16_t)(p - buf); + u->field_data[uf].len = 1; + + u->field_set |= (1 << uf); + old_uf = uf; + } + + /* CONNECT requests can only contain "hostname:port" */ + if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) { + return 1; + } + + /* Make sure we don't end somewhere unexpected */ + switch (s) { + case s_req_host_v6_start: + case s_req_host_v6: + case s_req_host_v6_end: + case s_req_host: + case s_req_port_start: + return 1; + default: + break; + } + + if (u->field_set & (1 << UF_PORT)) { + /* Don't bother with endp; we've already validated the string */ + unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10); + + /* Ports have a max value of 2^16 */ + if (v > 0xffff) { + return 1; + } + + u->port = (uint16_t) v; + } + + return 0; +} + +void +_lcb_http_parser_pause(http_parser *parser, int paused) { + /* Users should only be pausing/unpausing a parser that is not in an error + * state. In non-debug builds, there's not much that we can do about this + * other than ignore it. + */ + if (HTTP_PARSER_ERRNO(parser) == HPE_OK || + HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) { + SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); + } else { + assert(0 && "Attempting to pause parser in error state"); + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/http_parser/http_parser.h b/couchbase-sys/libcouchbase-2.7.0/contrib/http_parser/http_parser.h new file mode 100644 index 00000000..e9f3ee8b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/http_parser/http_parser.h @@ -0,0 +1,321 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef http_parser_h +#define http_parser_h +#ifdef __cplusplus +extern "C" { +#endif + +#define HTTP_PARSER_VERSION_MAJOR 1 +#define HTTP_PARSER_VERSION_MINOR 0 + +#include +#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#include +#else +#include +#endif + +/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run + * faster + */ +#ifndef HTTP_PARSER_STRICT +# define HTTP_PARSER_STRICT 1 +#endif + +/* Compile with -DHTTP_PARSER_DEBUG=1 to add extra debugging information to + * the error reporting facility. + */ +#ifndef HTTP_PARSER_DEBUG +# define HTTP_PARSER_DEBUG 0 +#endif + + +/* Maximium header size allowed */ +#define HTTP_MAX_HEADER_SIZE (80*1024) + + +typedef struct http_parser http_parser; +typedef struct http_parser_settings http_parser_settings; + + +/* Callbacks should return non-zero to indicate an error. The parser will + * then halt execution. + * + * The one exception is on_headers_complete. In a HTTP_RESPONSE parser + * returning '1' from on_headers_complete will tell the parser that it + * should not expect a body. This is used when receiving a response to a + * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: + * chunked' headers that indicate the presence of a body. + * + * http_data_cb does not return data chunks. It will be call arbitrarally + * many times for each string. E.G. you might get 10 callbacks for "on_path" + * each providing just a few characters more data. + */ +typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); +typedef int (*http_cb) (http_parser*); + + +/* Request Methods */ +#define HTTP_METHOD_MAP(XX) \ + XX(0, DELETE) \ + XX(1, GET) \ + XX(2, HEAD) \ + XX(3, POST) \ + XX(4, PUT) \ + /* pathological */ \ + XX(5, CONNECT) \ + XX(6, OPTIONS) \ + XX(7, TRACE) \ + /* webdav */ \ + XX(8, COPY) \ + XX(9, LOCK) \ + XX(10, MKCOL) \ + XX(11, MOVE) \ + XX(12, PROPFIND) \ + XX(13, PROPPATCH) \ + XX(14, UNLOCK) \ + /* subversion */ \ + XX(15, REPORT) \ + XX(16, MKACTIVITY) \ + XX(17, CHECKOUT) \ + XX(18, MERGE) \ + /* upnp */ \ + XX(19, MSEARCH) \ + XX(20, NOTIFY) \ + XX(21, SUBSCRIBE) \ + XX(22, UNSUBSCRIBE) \ + /* RFC-5789 */ \ + XX(23, PATCH) \ + XX(24, PURGE) \ + +enum http_method + { +#define XX(num, name) HTTP_##name = num, + HTTP_METHOD_MAP(XX) +#undef XX + HTTP_MAX + }; + + +enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; + + +/* Flag values for http_parser.flags field */ +enum flags + { F_CHUNKED = 1 << 0 + , F_CONNECTION_KEEP_ALIVE = 1 << 1 + , F_CONNECTION_CLOSE = 1 << 2 + , F_TRAILING = 1 << 3 + , F_UPGRADE = 1 << 4 + , F_SKIPBODY = 1 << 5 + }; + + +/* Map for errno-related constants + * + * The provided argument should be a macro that takes 2 arguments. + */ +#define HTTP_ERRNO_MAP(XX) \ + /* No error */ \ + XX(OK, "success") \ + \ + /* Callback-related errors */ \ + XX(CB_message_begin, "the on_message_begin callback failed") \ + XX(CB_url, "the on_url callback failed") \ + XX(CB_header_field, "the on_header_field callback failed") \ + XX(CB_header_value, "the on_header_value callback failed") \ + XX(CB_headers_complete, "the on_headers_complete callback failed") \ + XX(CB_body, "the on_body callback failed") \ + XX(CB_message_complete, "the on_message_complete callback failed") \ + \ + /* Parsing-related errors */ \ + XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ + XX(HEADER_OVERFLOW, \ + "too many header bytes seen; overflow detected") \ + XX(CLOSED_CONNECTION, \ + "data received after completed connection: close message") \ + XX(INVALID_VERSION, "invalid HTTP version") \ + XX(INVALID_STATUS, "invalid HTTP status code") \ + XX(INVALID_METHOD, "invalid HTTP method") \ + XX(INVALID_URL, "invalid URL") \ + XX(INVALID_HOST, "invalid host") \ + XX(INVALID_PORT, "invalid port") \ + XX(INVALID_PATH, "invalid path") \ + XX(INVALID_QUERY_STRING, "invalid query string") \ + XX(INVALID_FRAGMENT, "invalid fragment") \ + XX(LF_EXPECTED, "LF character expected") \ + XX(INVALID_HEADER_TOKEN, "invalid character in header") \ + XX(INVALID_CONTENT_LENGTH, \ + "invalid character in content-length header") \ + XX(INVALID_CHUNK_SIZE, \ + "invalid character in chunk size header") \ + XX(INVALID_CONSTANT, "invalid constant string") \ + XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ + XX(STRICT, "strict mode assertion failed") \ + XX(PAUSED, "parser is paused") \ + XX(UNKNOWN, "an unknown error occurred") + + +/* Define HPE_* values for each errno value above */ +#define HTTP_ERRNO_GEN(n, s) HPE_##n, +enum http_errno { + HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) + HPE_MAX +}; +#undef HTTP_ERRNO_GEN + + +/* Get an http_errno value from an http_parser */ +#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) + +/* Get the line number that generated the current error */ +#if HTTP_PARSER_DEBUG +#define HTTP_PARSER_ERRNO_LINE(p) ((p)->error_lineno) +#else +#define HTTP_PARSER_ERRNO_LINE(p) 0 +#endif + + +struct http_parser { + /** PRIVATE **/ + unsigned int type : 2, /* enum http_parser_type */ + flags : 6, /* F_* values from 'flags' enum, semi-public */ + + /** READ-ONLY **/ + method, /* requests only */ + http_errno : 7, + + /* 1 = Upgrade header was present and the parser has exited because of that. + * 0 = No upgrade header present. + * Should be checked when http_parser_execute() returns in addition to + * error checking. + */ + upgrade : 1; + + /** PRIVATE **/ + unsigned char state; /* enum state from http_parser.c */ + unsigned char header_state; /* enum header_state from http_parser.c */ + unsigned char index; /* index into current matcher */ + + uint32_t nread; /* # bytes read in various scenarios */ + uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ + + /** READ-ONLY **/ + unsigned short http_major; + unsigned short http_minor; + unsigned short status_code; /* responses only */ + +#if HTTP_PARSER_DEBUG + uint32_t error_lineno; +#endif + + /** PUBLIC **/ + void *data; /* A pointer to get hook to the "connection" or "socket" object */ +}; + + +struct http_parser_settings { + http_cb on_message_begin; + http_data_cb on_url; + http_data_cb on_header_field; + http_data_cb on_header_value; + http_cb on_headers_complete; + http_data_cb on_body; + http_cb on_message_complete; +}; + + +enum http_parser_url_fields + { UF_SCHEMA = 0 + , UF_HOST = 1 + , UF_PORT = 2 + , UF_PATH = 3 + , UF_QUERY = 4 + , UF_FRAGMENT = 5 + , UF_MAX = 6 + }; + + +/* Result structure for http_parser_parse_url(). + * + * Callers should index into field_data[] with UF_* values iff field_set + * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and + * because we probably have padding left over), we convert any port to + * a uint16_t. + */ +struct http_parser_url { + uint16_t field_set; /* Bitmask of (1 << UF_*) values */ + uint16_t port; /* Converted UF_PORT string */ + + struct { + uint16_t off; /* Offset into buffer in which field starts */ + uint16_t len; /* Length of run in buffer */ + } field_data[UF_MAX]; +}; + + +void _lcb_http_parser_init(http_parser *parser, enum http_parser_type type); + + +size_t _lcb_http_parser_execute(http_parser *parser, + const http_parser_settings *settings, + const char *data, + size_t len); + + +/* If http_should_keep_alive() in the on_headers_complete or + * on_message_complete callback returns true, then this will be should be + * the last message on the connection. + * If you are the server, respond with the "Connection: close" header. + * If you are the client, close the connection. + */ +int _lcb_http_should_keep_alive(http_parser *parser); + +/* Returns a string version of the HTTP method. */ +const char *_lcb_http_method_str(enum http_method m); + +/* Return a string name of the given error */ +const char *_lcb_http_errno_name(enum http_errno err); + +/* Return a string description of the given error */ +const char *_lcb_http_errno_description(enum http_errno err); + +/* Parse a URL; return nonzero on failure */ +int _lcb_http_parser_parse_url(const char *buf, size_t buflen, + int is_connect, + struct http_parser_url *u); + +/* Pause or un-pause the parser; a nonzero value pauses */ +void _lcb_http_parser_pause(http_parser *parser, int paused); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/jsonsl/LICENSE b/couchbase-sys/libcouchbase-2.7.0/contrib/jsonsl/LICENSE new file mode 100644 index 00000000..f5162e5f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/jsonsl/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2012 M. Nunberg, mnunberg@haskalah.org + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/jsonsl/jsonsl.c b/couchbase-sys/libcouchbase-2.7.0/contrib/jsonsl/jsonsl.c new file mode 100644 index 00000000..cbdcf407 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/jsonsl/jsonsl.c @@ -0,0 +1,1452 @@ +/* Copyright (C) 2012-2015 Mark Nunberg. + * + * See included LICENSE file for license details. + */ + +#include "jsonsl.h" +#include +#include +#include + +#ifdef JSONSL_USE_METRICS +#define XMETRICS \ + X(STRINGY_INSIGNIFICANT) \ + X(STRINGY_SLOWPATH) \ + X(ALLOWED_WHITESPACE) \ + X(QUOTE_FASTPATH) \ + X(SPECIAL_FASTPATH) \ + X(SPECIAL_WSPOP) \ + X(SPECIAL_SLOWPATH) \ + X(GENERIC) \ + X(STRUCTURAL_TOKEN) \ + X(SPECIAL_SWITCHFIRST) \ + X(STRINGY_CATCH) \ + X(NUMBER_FASTPATH) \ + X(ESCAPES) \ + X(TOTAL) \ + +struct jsonsl_metrics_st { +#define X(m) \ + unsigned long metric_##m; + XMETRICS +#undef X +}; + +static struct jsonsl_metrics_st GlobalMetrics = { 0 }; +static unsigned long GenericCounter[0x100] = { 0 }; +static unsigned long StringyCatchCounter[0x100] = { 0 }; + +#define INCR_METRIC(m) \ + GlobalMetrics.metric_##m++; + +#define INCR_GENERIC(c) \ + INCR_METRIC(GENERIC); \ + GenericCounter[c]++; \ + +#define INCR_STRINGY_CATCH(c) \ + INCR_METRIC(STRINGY_CATCH); \ + StringyCatchCounter[c]++; + +JSONSL_API +void jsonsl_dump_global_metrics(void) +{ + int ii; + printf("JSONSL Metrics:\n"); +#define X(m) \ + printf("\t%-30s %20lu (%0.2f%%)\n", #m, GlobalMetrics.metric_##m, \ + (float)((float)(GlobalMetrics.metric_##m/(float)GlobalMetrics.metric_TOTAL)) * 100); + XMETRICS +#undef X + printf("Generic Characters:\n"); + for (ii = 0; ii < 0xff; ii++) { + if (GenericCounter[ii]) { + printf("\t[ %c ] %lu\n", ii, GenericCounter[ii]); + } + } + printf("Weird string loop\n"); + for (ii = 0; ii < 0xff; ii++) { + if (StringyCatchCounter[ii]) { + printf("\t[ %c ] %lu\n", ii, StringyCatchCounter[ii]); + } + } +} + +#else +#define INCR_METRIC(m) +#define INCR_GENERIC(c) +#define INCR_STRINGY_CATCH(c) +JSONSL_API +void jsonsl_dump_global_metrics(void) { } +#endif /* JSONSL_USE_METRICS */ + +#define CASE_DIGITS \ +case '1': \ +case '2': \ +case '3': \ +case '4': \ +case '5': \ +case '6': \ +case '7': \ +case '8': \ +case '9': \ +case '0': + +static unsigned extract_special(unsigned); +static int is_special_end(unsigned); +static int is_allowed_whitespace(unsigned); +static int is_allowed_escape(unsigned); +static char get_escape_equiv(unsigned); + +JSONSL_API +jsonsl_t jsonsl_new(int nlevels) +{ + struct jsonsl_st *jsn = (struct jsonsl_st *) + calloc(1, sizeof (*jsn) + + ( (nlevels-1) * sizeof (struct jsonsl_state_st) ) + ); + + jsn->levels_max = nlevels; + jsn->max_callback_level = -1; + jsonsl_reset(jsn); + return jsn; +} + +JSONSL_API +void jsonsl_reset(jsonsl_t jsn) +{ + unsigned int ii; + jsn->tok_last = 0; + jsn->can_insert = 1; + jsn->pos = 0; + jsn->level = 0; + jsn->stopfl = 0; + jsn->in_escape = 0; + jsn->expecting = 0; + + memset(jsn->stack, 0, (jsn->levels_max * sizeof (struct jsonsl_state_st))); + + for (ii = 0; ii < jsn->levels_max; ii++) { + jsn->stack[ii].level = ii; + } +} + +JSONSL_API +void jsonsl_destroy(jsonsl_t jsn) +{ + if (jsn) { + free(jsn); + } +} + + +#define FASTPARSE_EXHAUSTED 1 +#define FASTPARSE_BREAK 0 +static const int chrt_string_nopass[0x100] = { JSONSL_CHARTABLE_string_nopass }; + +/* + * This function is meant to accelerate string parsing, reducing the main loop's + * check if we are indeed a string. + * + * @param jsn the parser + * @param[in,out] bytes_p A pointer to the current buffer (i.e. current position) + * @param[in,out] nbytes_p A pointer to the current size of the buffer + * @return true if all bytes have been exhausted (and thus the main loop can + * return), false if a special character was examined which requires greater + * examination. + */ +static int +jsonsl__str_fastparse(jsonsl_t jsn, + const jsonsl_uchar_t **bytes_p, size_t *nbytes_p) +{ + int exhausted = 1; + size_t nbytes = *nbytes_p; + const jsonsl_uchar_t *bytes = *bytes_p; + + for (; nbytes; nbytes--, bytes++) { + if ( +#ifdef JSONSL_USE_WCHAR + *bytes >= 0x100 || +#endif /* JSONSL_USE_WCHAR */ + (!chrt_string_nopass[*bytes])) { + INCR_METRIC(TOTAL); + INCR_METRIC(STRINGY_INSIGNIFICANT); + } else { + exhausted = 0; + break; + } + } + + /* Once we're done here, re-calculate the position variables */ + jsn->pos += (*nbytes_p - nbytes); + if (exhausted) { + return FASTPARSE_EXHAUSTED; + } + + *nbytes_p = nbytes; + *bytes_p = bytes; + return FASTPARSE_BREAK; +} + +/* Functions exactly like str_fastparse, except it also accepts a 'state' + * argument, since the number's value is updated in the state. */ +static int +jsonsl__num_fastparse(jsonsl_t jsn, + const jsonsl_uchar_t **bytes_p, size_t *nbytes_p, + struct jsonsl_state_st *state) +{ + int exhausted = 1; + size_t nbytes = *nbytes_p; + const jsonsl_uchar_t *bytes = *bytes_p; + + for (; nbytes; nbytes--, bytes++) { + jsonsl_uchar_t c = *bytes; + if (isdigit(c)) { + INCR_METRIC(TOTAL); + INCR_METRIC(NUMBER_FASTPATH); + state->nelem = (state->nelem * 10) + (c - 0x30); + } else { + exhausted = 0; + break; + } + } + jsn->pos += (*nbytes_p - nbytes); + if (exhausted) { + return FASTPARSE_EXHAUSTED; + } + *nbytes_p = nbytes; + *bytes_p = bytes; + return FASTPARSE_BREAK; +} + +JSONSL_API +void +jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes) +{ + +#define INVOKE_ERROR(eb) \ + if (jsn->error_callback(jsn, JSONSL_ERROR_##eb, state, (char*)c)) { \ + goto GT_AGAIN; \ + } \ + return; + +#define STACK_PUSH \ + if (jsn->level >= (levels_max-1)) { \ + jsn->error_callback(jsn, JSONSL_ERROR_LEVELS_EXCEEDED, state, (char*)c); \ + return; \ + } \ + state = jsn->stack + (++jsn->level); \ + state->ignore_callback = jsn->stack[jsn->level-1].ignore_callback; \ + state->pos_begin = jsn->pos; + +#define STACK_POP_NOPOS \ + state->pos_cur = jsn->pos; \ + state = jsn->stack + (--jsn->level); + + +#define STACK_POP \ + STACK_POP_NOPOS; \ + state->pos_cur = jsn->pos; + +#define CALLBACK_AND_POP_NOPOS(T) \ + state->pos_cur = jsn->pos; \ + DO_CALLBACK(T, POP); \ + state->nescapes = 0; \ + state = jsn->stack + (--jsn->level); + +#define CALLBACK_AND_POP(T) \ + CALLBACK_AND_POP_NOPOS(T); \ + state->pos_cur = jsn->pos; + +#define SPECIAL_POP \ + CALLBACK_AND_POP(SPECIAL); \ + jsn->expecting = 0; \ + jsn->tok_last = 0; \ + +#define CUR_CHAR (*(jsonsl_uchar_t*)c) + +#define DO_CALLBACK(T, action) \ + if (jsn->call_##T && \ + jsn->max_callback_level > state->level && \ + state->ignore_callback == 0) { \ + \ + if (jsn->action_callback_##action) { \ + jsn->action_callback_##action(jsn, JSONSL_ACTION_##action, state, (jsonsl_char_t*)c); \ + } else if (jsn->action_callback) { \ + jsn->action_callback(jsn, JSONSL_ACTION_##action, state, (jsonsl_char_t*)c); \ + } \ + if (jsn->stopfl) { return; } \ + } + + /** + * Verifies that we are able to insert the (non-string) item into a hash. + */ +#define ENSURE_HVAL \ + if (state->nelem % 2 == 0 && state->type == JSONSL_T_OBJECT) { \ + INVOKE_ERROR(HKEY_EXPECTED); \ + } + +#define VERIFY_SPECIAL(lit) \ + if (CUR_CHAR != (lit)[jsn->pos - state->pos_begin]) { \ + INVOKE_ERROR(SPECIAL_EXPECTED); \ + } + +#define STATE_SPECIAL_LENGTH \ + (state)->nescapes + +#define IS_NORMAL_NUMBER \ + ((state)->special_flags == JSONSL_SPECIALf_UNSIGNED || \ + (state)->special_flags == JSONSL_SPECIALf_SIGNED) + +#define STATE_NUM_LAST jsn->tok_last + +#define CONTINUE_NEXT_CHAR() continue + + const jsonsl_uchar_t *c = (jsonsl_uchar_t*)bytes; + size_t levels_max = jsn->levels_max; + struct jsonsl_state_st *state = jsn->stack + jsn->level; + jsn->base = bytes; + + for (; nbytes; nbytes--, jsn->pos++, c++) { + unsigned state_type; + INCR_METRIC(TOTAL); + + GT_AGAIN: + state_type = state->type; + /* Most common type is typically a string: */ + if (state_type & JSONSL_Tf_STRINGY) { + /* Special escape handling for some stuff */ + if (jsn->in_escape) { + jsn->in_escape = 0; + if (!is_allowed_escape(CUR_CHAR)) { + INVOKE_ERROR(ESCAPE_INVALID); + } else if (CUR_CHAR == 'u') { + DO_CALLBACK(UESCAPE, UESCAPE); + if (jsn->return_UESCAPE) { + return; + } + } + CONTINUE_NEXT_CHAR(); + } + + if (jsonsl__str_fastparse(jsn, &c, &nbytes) == + FASTPARSE_EXHAUSTED) { + /* No need to readjust variables as we've exhausted the iterator */ + return; + } else { + if (CUR_CHAR == '"') { + goto GT_QUOTE; + } else if (CUR_CHAR == '\\') { + goto GT_ESCAPE; + } else { + INVOKE_ERROR(WEIRD_WHITESPACE); + } + } + INCR_METRIC(STRINGY_SLOWPATH); + + } else if (state_type == JSONSL_T_SPECIAL) { + /* Fast track for signed/unsigned */ + if (IS_NORMAL_NUMBER) { + if (jsonsl__num_fastparse(jsn, &c, &nbytes, state) == + FASTPARSE_EXHAUSTED) { + return; + } else { + goto GT_SPECIAL_NUMERIC; + } + } else if (state->special_flags == JSONSL_SPECIALf_DASH) { + if (!isdigit(CUR_CHAR)) { + INVOKE_ERROR(INVALID_NUMBER); + } + + if (CUR_CHAR == '0') { + state->special_flags = JSONSL_SPECIALf_ZERO|JSONSL_SPECIALf_SIGNED; + } else if (isdigit(CUR_CHAR)) { + state->special_flags = JSONSL_SPECIALf_SIGNED; + state->nelem = CUR_CHAR - 0x30; + } else { + INVOKE_ERROR(INVALID_NUMBER); + } + CONTINUE_NEXT_CHAR(); + + } else if (state->special_flags == JSONSL_SPECIALf_ZERO) { + if (isdigit(CUR_CHAR)) { + /* Following a zero! */ + INVOKE_ERROR(INVALID_NUMBER); + } + /* Unset the 'zero' flag: */ + if (state->special_flags & JSONSL_SPECIALf_SIGNED) { + state->special_flags = JSONSL_SPECIALf_SIGNED; + } else { + state->special_flags = JSONSL_SPECIALf_UNSIGNED; + } + goto GT_SPECIAL_NUMERIC; + } + + if (state->special_flags & JSONSL_SPECIALf_NUMERIC) { + GT_SPECIAL_NUMERIC: + switch (CUR_CHAR) { + CASE_DIGITS + STATE_NUM_LAST = '1'; + CONTINUE_NEXT_CHAR(); + + case '.': + if (state->special_flags & JSONSL_SPECIALf_FLOAT) { + INVOKE_ERROR(INVALID_NUMBER); + } + state->special_flags |= JSONSL_SPECIALf_FLOAT; + STATE_NUM_LAST = '.'; + CONTINUE_NEXT_CHAR(); + + case 'e': + case 'E': + if (state->special_flags & JSONSL_SPECIALf_EXPONENT) { + INVOKE_ERROR(INVALID_NUMBER); + } + state->special_flags |= JSONSL_SPECIALf_EXPONENT; + STATE_NUM_LAST = 'e'; + CONTINUE_NEXT_CHAR(); + + case '-': + case '+': + if (STATE_NUM_LAST != 'e') { + INVOKE_ERROR(INVALID_NUMBER); + } + STATE_NUM_LAST = '-'; + CONTINUE_NEXT_CHAR(); + + default: + if (is_special_end(CUR_CHAR)) { + goto GT_SPECIAL_POP; + } + INVOKE_ERROR(INVALID_NUMBER); + break; + } + } + /* else if (!NUMERIC) */ + if (!is_special_end(CUR_CHAR)) { + STATE_SPECIAL_LENGTH++; + + /* Verify TRUE, FALSE, NULL */ + if (state->special_flags == JSONSL_SPECIALf_TRUE) { + VERIFY_SPECIAL("true"); + } else if (state->special_flags == JSONSL_SPECIALf_FALSE) { + VERIFY_SPECIAL("false"); + } else if (state->special_flags == JSONSL_SPECIALf_NULL) { + VERIFY_SPECIAL("null"); + } + INCR_METRIC(SPECIAL_FASTPATH); + CONTINUE_NEXT_CHAR(); + } + + GT_SPECIAL_POP: + if (IS_NORMAL_NUMBER) { + /* Nothing */ + } else if (state->special_flags == JSONSL_SPECIALf_ZERO || + state->special_flags == (JSONSL_SPECIALf_ZERO|JSONSL_SPECIALf_SIGNED)) { + /* 0 is unsigned! */ + state->special_flags = JSONSL_SPECIALf_UNSIGNED; + } else if (state->special_flags == JSONSL_SPECIALf_DASH) { + /* Still in dash! */ + INVOKE_ERROR(INVALID_NUMBER); + } else if (state->special_flags & JSONSL_SPECIALf_NUMERIC) { + /* Check that we're not at the end of a token */ + if (STATE_NUM_LAST != '1') { + INVOKE_ERROR(INVALID_NUMBER); + } + } else if (state->special_flags == JSONSL_SPECIALf_TRUE) { + if (STATE_SPECIAL_LENGTH != 4) { + INVOKE_ERROR(SPECIAL_INCOMPLETE); + } + state->nelem = 1; + } else if (state->special_flags == JSONSL_SPECIALf_FALSE) { + if (STATE_SPECIAL_LENGTH != 5) { + INVOKE_ERROR(SPECIAL_INCOMPLETE); + } + } else if (state->special_flags == JSONSL_SPECIALf_NULL) { + if (STATE_SPECIAL_LENGTH != 4) { + INVOKE_ERROR(SPECIAL_INCOMPLETE); + } + } + SPECIAL_POP; + jsn->expecting = ','; + if (is_allowed_whitespace(CUR_CHAR)) { + CONTINUE_NEXT_CHAR(); + } + /** + * This works because we have a non-whitespace token + * which is not a special token. If this is a structural + * character then it will be gracefully handled by the + * switch statement. Otherwise it will default to the 'special' + * state again, + */ + goto GT_STRUCTURAL_TOKEN; + } else if (is_allowed_whitespace(CUR_CHAR)) { + INCR_METRIC(ALLOWED_WHITESPACE); + /* So we're not special. Harmless insignificant whitespace + * passthrough + */ + CONTINUE_NEXT_CHAR(); + } else if (extract_special(CUR_CHAR)) { + /* not a string, whitespace, or structural token. must be special */ + goto GT_SPECIAL_BEGIN; + } + + INCR_GENERIC(CUR_CHAR); + + if (CUR_CHAR == '"') { + GT_QUOTE: + jsn->can_insert = 0; + switch (state_type) { + + /* the end of a string or hash key */ + case JSONSL_T_STRING: + CALLBACK_AND_POP(STRING); + CONTINUE_NEXT_CHAR(); + case JSONSL_T_HKEY: + CALLBACK_AND_POP(HKEY); + CONTINUE_NEXT_CHAR(); + + case JSONSL_T_OBJECT: + state->nelem++; + if ( (state->nelem-1) % 2 ) { + /* Odd, this must be a hash value */ + if (jsn->tok_last != ':') { + INVOKE_ERROR(MISSING_TOKEN); + } + jsn->expecting = ','; /* Can't figure out what to expect next */ + jsn->tok_last = 0; + + STACK_PUSH; + state->type = JSONSL_T_STRING; + DO_CALLBACK(STRING, PUSH); + + } else { + /* hash key */ + if (jsn->expecting != '"') { + INVOKE_ERROR(STRAY_TOKEN); + } + jsn->tok_last = 0; + jsn->expecting = ':'; + + STACK_PUSH; + state->type = JSONSL_T_HKEY; + DO_CALLBACK(HKEY, PUSH); + } + CONTINUE_NEXT_CHAR(); + + case JSONSL_T_LIST: + state->nelem++; + STACK_PUSH; + state->type = JSONSL_T_STRING; + jsn->expecting = ','; + jsn->tok_last = 0; + DO_CALLBACK(STRING, PUSH); + CONTINUE_NEXT_CHAR(); + + case JSONSL_T_SPECIAL: + INVOKE_ERROR(STRAY_TOKEN); + break; + + default: + INVOKE_ERROR(STRING_OUTSIDE_CONTAINER); + break; + } /* switch(state->type) */ + } else if (CUR_CHAR == '\\') { + GT_ESCAPE: + INCR_METRIC(ESCAPES); + /* Escape */ + if ( (state->type & JSONSL_Tf_STRINGY) == 0 ) { + INVOKE_ERROR(ESCAPE_OUTSIDE_STRING); + } + state->nescapes++; + jsn->in_escape = 1; + CONTINUE_NEXT_CHAR(); + } /* " or \ */ + + GT_STRUCTURAL_TOKEN: + switch (CUR_CHAR) { + case ':': + INCR_METRIC(STRUCTURAL_TOKEN); + if (jsn->expecting != CUR_CHAR) { + INVOKE_ERROR(STRAY_TOKEN); + } + jsn->tok_last = ':'; + jsn->can_insert = 1; + jsn->expecting = '"'; + CONTINUE_NEXT_CHAR(); + + case ',': + INCR_METRIC(STRUCTURAL_TOKEN); + /** + * The comma is one of the more generic tokens. + * In the context of an OBJECT, the can_insert flag + * should never be set, and no other action is + * necessary. + */ + if (jsn->expecting != CUR_CHAR) { + /* make this branch execute only when we haven't manually + * just placed the ',' in the expecting register. + */ + INVOKE_ERROR(STRAY_TOKEN); + } + + if (state->type == JSONSL_T_OBJECT) { + /* end of hash value, expect a string as a hash key */ + jsn->expecting = '"'; + } else { + jsn->can_insert = 1; + } + + jsn->tok_last = ','; + jsn->expecting = '"'; + CONTINUE_NEXT_CHAR(); + + /* new list or object */ + /* hashes are more common */ + case '{': + case '[': + INCR_METRIC(STRUCTURAL_TOKEN); + if (!jsn->can_insert) { + INVOKE_ERROR(CANT_INSERT); + } + + ENSURE_HVAL; + state->nelem++; + + STACK_PUSH; + /* because the constants match the opening delimiters, we can do this: */ + state->type = CUR_CHAR; + state->nelem = 0; + jsn->can_insert = 1; + if (CUR_CHAR == '{') { + /* If we're a hash, we expect a key first, which is quouted */ + jsn->expecting = '"'; + } + if (CUR_CHAR == JSONSL_T_OBJECT) { + DO_CALLBACK(OBJECT, PUSH); + } else { + DO_CALLBACK(LIST, PUSH); + } + jsn->tok_last = 0; + CONTINUE_NEXT_CHAR(); + + /* closing of list or object */ + case '}': + case ']': + INCR_METRIC(STRUCTURAL_TOKEN); + if (jsn->tok_last == ',' && jsn->options.allow_trailing_comma == 0) { + INVOKE_ERROR(TRAILING_COMMA); + } + + jsn->can_insert = 0; + jsn->level--; + jsn->expecting = ','; + jsn->tok_last = 0; + if (CUR_CHAR == ']') { + if (state->type != '[') { + INVOKE_ERROR(BRACKET_MISMATCH); + } + DO_CALLBACK(LIST, POP); + } else { + if (state->type != '{') { + INVOKE_ERROR(BRACKET_MISMATCH); + } else if (state->nelem && state->nelem % 2 != 0) { + INVOKE_ERROR(VALUE_EXPECTED); + } + DO_CALLBACK(OBJECT, POP); + } + state = jsn->stack + jsn->level; + state->pos_cur = jsn->pos; + CONTINUE_NEXT_CHAR(); + + default: + GT_SPECIAL_BEGIN: + /** + * Not a string, not a structural token, and not benign whitespace. + * Technically we should iterate over the character always, but since + * we are not doing full numerical/value decoding anyway (but only hinting), + * we only check upon entry. + */ + if (state->type != JSONSL_T_SPECIAL) { + int special_flags = extract_special(CUR_CHAR); + if (!special_flags) { + /** + * Try to do some heuristics here anyway to figure out what kind of + * error this is. The 'special' case is a fallback scenario anyway. + */ + if (CUR_CHAR == '\0') { + INVOKE_ERROR(FOUND_NULL_BYTE); + } else if (CUR_CHAR < 0x20) { + INVOKE_ERROR(WEIRD_WHITESPACE); + } else { + INVOKE_ERROR(SPECIAL_EXPECTED); + } + } + ENSURE_HVAL; + state->nelem++; + if (!jsn->can_insert) { + INVOKE_ERROR(CANT_INSERT); + } + STACK_PUSH; + state->type = JSONSL_T_SPECIAL; + state->special_flags = special_flags; + STATE_SPECIAL_LENGTH = 1; + + if (special_flags == JSONSL_SPECIALf_UNSIGNED) { + state->nelem = CUR_CHAR - 0x30; + STATE_NUM_LAST = '1'; + } else { + STATE_NUM_LAST = '-'; + state->nelem = 0; + } + DO_CALLBACK(SPECIAL, PUSH); + } + CONTINUE_NEXT_CHAR(); + } + } +} + +JSONSL_API +const char* jsonsl_strerror(jsonsl_error_t err) +{ + if (err == JSONSL_ERROR_SUCCESS) { + return "SUCCESS"; + } +#define X(t) \ + if (err == JSONSL_ERROR_##t) \ + return #t; + JSONSL_XERR; +#undef X + return ""; +} + +JSONSL_API +const char *jsonsl_strtype(jsonsl_type_t type) +{ +#define X(o,c) \ + if (type == JSONSL_T_##o) \ + return #o; + JSONSL_XTYPE +#undef X + return "UNKNOWN TYPE"; + +} + +/* + * + * JPR/JSONPointer functions + * + * + */ +#ifndef JSONSL_NO_JPR +static +jsonsl_jpr_type_t +populate_component(char *in, + struct jsonsl_jpr_component_st *component, + char **next, + jsonsl_error_t *errp) +{ + unsigned long pctval; + char *c = NULL, *outp = NULL, *end = NULL; + size_t input_len; + jsonsl_jpr_type_t ret = JSONSL_PATH_NONE; + + if (*next == NULL || *(*next) == '\0') { + return JSONSL_PATH_NONE; + } + + /* Replace the next / with a NULL */ + *next = strstr(in, "/"); + if (*next != NULL) { + *(*next) = '\0'; /* drop the forward slash */ + input_len = *next - in; + end = *next; + *next += 1; /* next character after the '/' */ + } else { + input_len = strlen(in); + end = in + input_len + 1; + } + + component->pstr = in; + + /* Check for special components of interest */ + if (*in == JSONSL_PATH_WILDCARD_CHAR && input_len == 1) { + /* Lone wildcard */ + ret = JSONSL_PATH_WILDCARD; + goto GT_RET; + } else if (isdigit(*in)) { + /* ASCII Numeric */ + char *endptr; + component->idx = strtoul(in, &endptr, 10); + if (endptr && *endptr == '\0') { + ret = JSONSL_PATH_NUMERIC; + goto GT_RET; + } + } + + /* Default, it's a string */ + ret = JSONSL_PATH_STRING; + for (c = outp = in; c < end; c++, outp++) { + char origc; + if (*c != '%') { + goto GT_ASSIGN; + } + /* + * c = { [+0] = '%', [+1] = 'b', [+2] = 'e', [+3] = '\0' } + */ + + /* Need %XX */ + if (c+2 >= end) { + *errp = JSONSL_ERROR_PERCENT_BADHEX; + return JSONSL_PATH_INVALID; + } + if (! (isxdigit(*(c+1)) && isxdigit(*(c+2))) ) { + *errp = JSONSL_ERROR_PERCENT_BADHEX; + return JSONSL_PATH_INVALID; + } + + /* Temporarily null-terminate the characters */ + origc = *(c+3); + *(c+3) = '\0'; + pctval = strtoul(c+1, NULL, 16); + *(c+3) = origc; + + *outp = (char) pctval; + c += 2; + continue; + + GT_ASSIGN: + *outp = *c; + } + /* Null-terminate the string */ + for (; outp < c; outp++) { + *outp = '\0'; + } + + GT_RET: + component->ptype = ret; + if (ret != JSONSL_PATH_WILDCARD) { + component->len = strlen(component->pstr); + } + return ret; +} + +JSONSL_API +jsonsl_jpr_t +jsonsl_jpr_new(const char *path, jsonsl_error_t *errp) +{ + char *my_copy = NULL; + int count, curidx; + struct jsonsl_jpr_st *ret = NULL; + struct jsonsl_jpr_component_st *components = NULL; + size_t origlen; + jsonsl_error_t errstacked; + +#define JPR_BAIL(err) *errp = err; goto GT_ERROR; + + if (errp == NULL) { + errp = &errstacked; + } + + if (path == NULL || *path != '/') { + JPR_BAIL(JSONSL_ERROR_JPR_NOROOT); + return NULL; + } + + count = 1; + path++; + { + const char *c = path; + for (; *c; c++) { + if (*c == '/') { + count++; + if (*(c+1) == '/') { + JPR_BAIL(JSONSL_ERROR_JPR_DUPSLASH); + } + } + } + } + if(*path) { + count++; + } + + components = (struct jsonsl_jpr_component_st *) + malloc(sizeof(*components) * count); + if (!components) { + JPR_BAIL(JSONSL_ERROR_ENOMEM); + } + + my_copy = (char *)malloc(strlen(path) + 1); + if (!my_copy) { + JPR_BAIL(JSONSL_ERROR_ENOMEM); + } + + strcpy(my_copy, path); + + components[0].ptype = JSONSL_PATH_ROOT; + + if (*my_copy) { + char *cur = my_copy; + int pathret = JSONSL_PATH_STRING; + curidx = 1; + while (pathret > 0 && curidx < count) { + pathret = populate_component(cur, components + curidx, &cur, errp); + if (pathret > 0) { + curidx++; + } else { + break; + } + } + + if (pathret == JSONSL_PATH_INVALID) { + JPR_BAIL(JSONSL_ERROR_JPR_BADPATH); + } + } else { + curidx = 1; + } + + path--; /*revert path to leading '/' */ + origlen = strlen(path) + 1; + ret = (struct jsonsl_jpr_st *)malloc(sizeof(*ret)); + if (!ret) { + JPR_BAIL(JSONSL_ERROR_ENOMEM); + } + ret->orig = (char *)malloc(origlen); + if (!ret->orig) { + JPR_BAIL(JSONSL_ERROR_ENOMEM); + } + ret->components = components; + ret->ncomponents = curidx; + ret->basestr = my_copy; + ret->norig = origlen-1; + strcpy(ret->orig, path); + + return ret; + + GT_ERROR: + free(my_copy); + free(components); + if (ret) { + free(ret->orig); + } + free(ret); + return NULL; +#undef JPR_BAIL +} + +void jsonsl_jpr_destroy(jsonsl_jpr_t jpr) +{ + free(jpr->components); + free(jpr->basestr); + free(jpr->orig); + free(jpr); +} + +JSONSL_API +jsonsl_jpr_match_t +jsonsl_jpr_match(jsonsl_jpr_t jpr, + unsigned int parent_type, + unsigned int parent_level, + const char *key, + size_t nkey) +{ + /* find our current component. This is the child level */ + int cmpret; + struct jsonsl_jpr_component_st *p_component; + p_component = jpr->components + parent_level; + + if (parent_level >= jpr->ncomponents) { + return JSONSL_MATCH_NOMATCH; + } + + /* Lone query for 'root' element. Always matches */ + if (parent_level == 0) { + if (jpr->ncomponents == 1) { + return JSONSL_MATCH_COMPLETE; + } else { + return JSONSL_MATCH_POSSIBLE; + } + } + + /* Wildcard, always matches */ + if (p_component->ptype == JSONSL_PATH_WILDCARD) { + if (parent_level == jpr->ncomponents-1) { + return JSONSL_MATCH_COMPLETE; + } else { + return JSONSL_MATCH_POSSIBLE; + } + } + + /* Check numeric array index. This gets its special block so we can avoid + * string comparisons */ + if (p_component->ptype == JSONSL_PATH_NUMERIC) { + if (parent_type == JSONSL_T_LIST) { + if (p_component->idx != nkey) { + /* Wrong index */ + return JSONSL_MATCH_NOMATCH; + } else { + if (parent_level == jpr->ncomponents-1) { + /* This is the last element of the path */ + return JSONSL_MATCH_COMPLETE; + } else { + /* Intermediate element */ + return JSONSL_MATCH_POSSIBLE; + } + } + } else if (p_component->is_arridx) { + /* Numeric and an array index (set explicitly by user). But not + * a list for a parent */ + return JSONSL_MATCH_TYPE_MISMATCH; + } + } else if (parent_type == JSONSL_T_LIST) { + return JSONSL_MATCH_TYPE_MISMATCH; + } + + /* Check lengths */ + if (p_component->len != nkey) { + return JSONSL_MATCH_NOMATCH; + } + + /* Check string comparison */ + cmpret = strncmp(p_component->pstr, key, nkey); + if (cmpret == 0) { + if (parent_level == jpr->ncomponents-1) { + return JSONSL_MATCH_COMPLETE; + } else { + return JSONSL_MATCH_POSSIBLE; + } + } + + return JSONSL_MATCH_NOMATCH; +} + +JSONSL_API +void jsonsl_jpr_match_state_init(jsonsl_t jsn, + jsonsl_jpr_t *jprs, + size_t njprs) +{ + size_t ii, *firstjmp; + if (njprs == 0) { + return; + } + jsn->jprs = (jsonsl_jpr_t *)malloc(sizeof(jsonsl_jpr_t) * njprs); + jsn->jpr_count = njprs; + jsn->jpr_root = (size_t*)calloc(1, sizeof(size_t) * njprs * jsn->levels_max); + memcpy(jsn->jprs, jprs, sizeof(jsonsl_jpr_t) * njprs); + /* Set the initial jump table values */ + + firstjmp = jsn->jpr_root; + for (ii = 0; ii < njprs; ii++) { + firstjmp[ii] = ii+1; + } +} + +JSONSL_API +void jsonsl_jpr_match_state_cleanup(jsonsl_t jsn) +{ + if (jsn->jpr_count == 0) { + return; + } + + free(jsn->jpr_root); + free(jsn->jprs); + jsn->jprs = NULL; + jsn->jpr_root = NULL; + jsn->jpr_count = 0; +} + +/** + * This function should be called exactly once on each element... + * This should also be called in recursive order, since we rely + * on the parent having been initalized for a match. + * + * Since the parent is checked for a match as well, we maintain a 'serial' counter. + * Whenever we traverse an element, we expect the serial to be the same as a global + * integer. If they do not match, we re-initialize the context, and set the serial. + * + * This ensures a type of consistency without having a proactive reset by the + * main lexer itself. + * + */ +JSONSL_API +jsonsl_jpr_t jsonsl_jpr_match_state(jsonsl_t jsn, + struct jsonsl_state_st *state, + const char *key, + size_t nkey, + jsonsl_jpr_match_t *out) +{ + struct jsonsl_state_st *parent_state; + jsonsl_jpr_t ret = NULL; + + /* Jump and JPR tables for our own state and the parent state */ + size_t *jmptable, *pjmptable; + size_t jmp_cur, ii, ourjmpidx; + + if (!jsn->jpr_root) { + *out = JSONSL_MATCH_NOMATCH; + return NULL; + } + + pjmptable = jsn->jpr_root + (jsn->jpr_count * (state->level-1)); + jmptable = pjmptable + jsn->jpr_count; + + /* If the parent cannot match, then invalidate it */ + if (*pjmptable == 0) { + *jmptable = 0; + *out = JSONSL_MATCH_NOMATCH; + return NULL; + } + + parent_state = jsn->stack + state->level - 1; + + if (parent_state->type == JSONSL_T_LIST) { + nkey = (size_t) parent_state->nelem; + } + + *jmptable = 0; + ourjmpidx = 0; + memset(jmptable, 0, sizeof(int) * jsn->jpr_count); + + for (ii = 0; ii < jsn->jpr_count; ii++) { + jmp_cur = pjmptable[ii]; + if (jmp_cur) { + jsonsl_jpr_t jpr = jsn->jprs[jmp_cur-1]; + *out = jsonsl_jpr_match(jpr, + parent_state->type, + parent_state->level, + key, nkey); + if (*out == JSONSL_MATCH_COMPLETE) { + ret = jpr; + *jmptable = 0; + return ret; + } else if (*out == JSONSL_MATCH_POSSIBLE) { + jmptable[ourjmpidx] = ii+1; + ourjmpidx++; + } + } else { + break; + } + } + if (!*jmptable) { + *out = JSONSL_MATCH_NOMATCH; + } + return NULL; +} + +JSONSL_API +const char *jsonsl_strmatchtype(jsonsl_jpr_match_t match) +{ +#define X(T,v) \ + if ( match == JSONSL_MATCH_##T ) \ + return #T; + JSONSL_XMATCH +#undef X + return ""; +} + +#endif /* JSONSL_WITH_JPR */ + +/** + * Utility function to convert escape sequences + */ +JSONSL_API +size_t jsonsl_util_unescape_ex(const char *in, + char *out, + size_t len, + const int toEscape[128], + unsigned *oflags, + jsonsl_error_t *err, + const char **errat) +{ + const unsigned char *c = (const unsigned char*)in; + int in_escape = 0; + size_t origlen = len; + /* difference between the length of the input buffer and the output buffer */ + size_t ndiff = 0; + if (oflags) { + *oflags = 0; + } +#define UNESCAPE_BAIL(e,offset) \ + *err = JSONSL_ERROR_##e; \ + if (errat) { \ + *errat = (const char*)(c+ (ptrdiff_t)(offset)); \ + } \ + return 0; + + for (; len; len--, c++, out++) { + unsigned int uesc_val[2]; + if (in_escape) { + /* inside a previously ignored escape. Ignore */ + in_escape = 0; + goto GT_ASSIGN; + } + + if (*c != '\\') { + /* Not an escape, so we don't care about this */ + goto GT_ASSIGN; + } + + if (len < 2) { + UNESCAPE_BAIL(ESCAPE_INVALID, 0); + } + if (!is_allowed_escape(c[1])) { + UNESCAPE_BAIL(ESCAPE_INVALID, 1) + } + if ((toEscape[(unsigned char)c[1] & 0x7f] == 0 && + c[1] != '\\' && c[1] != '"')) { + /* if we don't want to unescape this string, just continue with + * the escape flag set + */ + in_escape = 1; + goto GT_ASSIGN; + } + + if (c[1] != 'u') { + /* simple skip-and-replace using pre-defined maps. + * TODO: should the maps actually reflect the desired + * replacement character in toEscape? + */ + char esctmp = get_escape_equiv(c[1]); + if (esctmp) { + /* Check if there is a corresponding replacement */ + *out = esctmp; + } else { + /* Just gobble up the 'reverse-solidus' */ + *out = c[1]; + } + len--; + ndiff++; + c++; + /* do not assign, just continue */ + continue; + } + + /* next == 'u' */ + if (len < 6) { + /* Need at least six characters: + * { [0] = '\\', [1] = 'u', [2] = 'f', [3] = 'f', [4] = 'f', [5] = 'f' } + */ + UNESCAPE_BAIL(UESCAPE_TOOSHORT, -1); + } + + if (sscanf((const char*)(c+2), "%02x%02x", uesc_val, uesc_val+1) != 2) { + /* We treat the sequence as two octets */ + UNESCAPE_BAIL(UESCAPE_TOOSHORT, -1); + } + + /* By now, we gobble up all the six bytes (current implied + 5 next + * characters), and have at least four missing bytes from the output + * buffer. + */ + len -= 5; + c += 5; + + ndiff += 4; + if (uesc_val[0] == 0) { + /* only one byte is extracted from the two + * possible octets. Increment the diff counter by one. + */ + *out = uesc_val[1]; + if (oflags && *(unsigned char*)out > 0x7f) { + *oflags |= JSONSL_SPECIALf_NONASCII; + } + ndiff++; + } else { + *(out++) = uesc_val[0]; + *out = uesc_val[1]; + if (oflags && (uesc_val[0] > 0x7f || uesc_val[1] > 0x7f)) { + *oflags |= JSONSL_SPECIALf_NONASCII; + } + } + continue; + + /* Only reached by previous branches */ + GT_ASSIGN: + *out = *c; + } + *err = JSONSL_ERROR_SUCCESS; + return origlen - ndiff; +} + +/** + * Character Table definitions. + * These were all generated via srcutil/genchartables.pl + */ + +/** + * This table contains the beginnings of non-string + * allowable (bareword) values. + */ +static unsigned short Special_Table[0x100] = { + /* 0x00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */ + /* 0x20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x2c */ + /* 0x2d */ JSONSL_SPECIALf_DASH /* <-> */, /* 0x2d */ + /* 0x2e */ 0,0, /* 0x2f */ + /* 0x30 */ JSONSL_SPECIALf_ZERO /* <0> */, /* 0x30 */ + /* 0x31 */ JSONSL_SPECIALf_UNSIGNED /* <1> */, /* 0x31 */ + /* 0x32 */ JSONSL_SPECIALf_UNSIGNED /* <2> */, /* 0x32 */ + /* 0x33 */ JSONSL_SPECIALf_UNSIGNED /* <3> */, /* 0x33 */ + /* 0x34 */ JSONSL_SPECIALf_UNSIGNED /* <4> */, /* 0x34 */ + /* 0x35 */ JSONSL_SPECIALf_UNSIGNED /* <5> */, /* 0x35 */ + /* 0x36 */ JSONSL_SPECIALf_UNSIGNED /* <6> */, /* 0x36 */ + /* 0x37 */ JSONSL_SPECIALf_UNSIGNED /* <7> */, /* 0x37 */ + /* 0x38 */ JSONSL_SPECIALf_UNSIGNED /* <8> */, /* 0x38 */ + /* 0x39 */ JSONSL_SPECIALf_UNSIGNED /* <9> */, /* 0x39 */ + /* 0x3a */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x59 */ + /* 0x5a */ 0,0,0,0,0,0,0,0,0,0,0,0, /* 0x65 */ + /* 0x66 */ JSONSL_SPECIALf_FALSE /* */, /* 0x66 */ + /* 0x67 */ 0,0,0,0,0,0,0, /* 0x6d */ + /* 0x6e */ JSONSL_SPECIALf_NULL /* */, /* 0x6e */ + /* 0x6f */ 0,0,0,0,0, /* 0x73 */ + /* 0x74 */ JSONSL_SPECIALf_TRUE /* */, /* 0x74 */ + /* 0x75 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x94 */ + /* 0x95 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xb4 */ + /* 0xb5 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xd4 */ + /* 0xd5 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xf4 */ + /* 0xf5 */ 0,0,0,0,0,0,0,0,0,0, /* 0xfe */ +}; + +/** + * Contains characters which signal the termination of any of the 'special' bareword + * values. + */ +static int Special_Endings[0x100] = { + /* 0x00 */ 0,0,0,0,0,0,0,0,0, /* 0x08 */ + /* 0x09 */ 1 /* */, /* 0x09 */ + /* 0x0a */ 1 /* */, /* 0x0a */ + /* 0x0b */ 0,0, /* 0x0c */ + /* 0x0d */ 1 /* */, /* 0x0d */ + /* 0x0e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */ + /* 0x20 */ 1 /* */, /* 0x20 */ + /* 0x21 */ 0, /* 0x21 */ + /* 0x22 */ 1 /* " */, /* 0x22 */ + /* 0x23 */ 0,0,0,0,0,0,0,0,0, /* 0x2b */ + /* 0x2c */ 1 /* , */, /* 0x2c */ + /* 0x2d */ 0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x39 */ + /* 0x3a */ 1 /* : */, /* 0x3a */ + /* 0x3b */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5a */ + /* 0x5b */ 1 /* [ */, /* 0x5b */ + /* 0x5c */ 1 /* \ */, /* 0x5c */ + /* 0x5d */ 1 /* ] */, /* 0x5d */ + /* 0x5e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x7a */ + /* 0x7b */ 1 /* { */, /* 0x7b */ + /* 0x7c */ 0, /* 0x7c */ + /* 0x7d */ 1 /* } */, /* 0x7d */ + /* 0x7e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x9d */ + /* 0x9e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xbd */ + /* 0xbe */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xdd */ + /* 0xde */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xfd */ + /* 0xfe */ 0 /* 0xfe */ +}; + +/** + * This table contains entries for the allowed whitespace as per RFC 4627 + */ +static int Allowed_Whitespace[0x100] = { + /* 0x00 */ 0,0,0,0,0,0,0,0,0, /* 0x08 */ + /* 0x09 */ 1 /* */, /* 0x09 */ + /* 0x0a */ 1 /* */, /* 0x0a */ + /* 0x0b */ 0,0, /* 0x0c */ + /* 0x0d */ 1 /* */, /* 0x0d */ + /* 0x0e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */ + /* 0x20 */ 1 /* */, /* 0x20 */ + /* 0x21 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x40 */ + /* 0x41 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x60 */ + /* 0x61 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x80 */ + /* 0x81 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xa0 */ + /* 0xa1 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xc0 */ + /* 0xc1 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xe0 */ + /* 0xe1 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* 0xfe */ +}; + +/** + * Allowable two-character 'common' escapes: + */ +static int Allowed_Escapes[0x100] = { + /* 0x00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */ + /* 0x20 */ 0,0, /* 0x21 */ + /* 0x22 */ 1 /* <"> */, /* 0x22 */ + /* 0x23 */ 0,0,0,0,0,0,0,0,0,0,0,0, /* 0x2e */ + /* 0x2f */ 1 /* */, /* 0x2f */ + /* 0x30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x4f */ + /* 0x50 */ 0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5b */ + /* 0x5c */ 1 /* <\> */, /* 0x5c */ + /* 0x5d */ 0,0,0,0,0, /* 0x61 */ + /* 0x62 */ 1 /* */, /* 0x62 */ + /* 0x63 */ 0,0,0, /* 0x65 */ + /* 0x66 */ 1 /* */, /* 0x66 */ + /* 0x67 */ 0,0,0,0,0,0,0, /* 0x6d */ + /* 0x6e */ 1 /* */, /* 0x6e */ + /* 0x6f */ 0,0,0, /* 0x71 */ + /* 0x72 */ 1 /* */, /* 0x72 */ + /* 0x73 */ 0, /* 0x73 */ + /* 0x74 */ 1 /* */, /* 0x74 */ + /* 0x75 */ 1 /* */, /* 0x75 */ + /* 0x76 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x95 */ + /* 0x96 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xb5 */ + /* 0xb6 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xd5 */ + /* 0xd6 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xf5 */ + /* 0xf6 */ 0,0,0,0,0,0,0,0,0, /* 0xfe */ +}; + +/** + * This table contains the _values_ for a given (single) escaped character. + */ +static unsigned char Escape_Equivs[0x100] = { + /* 0x00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */ + /* 0x20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x3f */ + /* 0x40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5f */ + /* 0x60 */ 0,0, /* 0x61 */ + /* 0x62 */ 8 /* */, /* 0x62 */ + /* 0x63 */ 0,0,0, /* 0x65 */ + /* 0x66 */ 12 /* */, /* 0x66 */ + /* 0x67 */ 0,0,0,0,0,0,0, /* 0x6d */ + /* 0x6e */ 10 /* */, /* 0x6e */ + /* 0x6f */ 0,0,0, /* 0x71 */ + /* 0x72 */ 13 /* */, /* 0x72 */ + /* 0x73 */ 0, /* 0x73 */ + /* 0x74 */ 9 /* */, /* 0x74 */ + /* 0x75 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x94 */ + /* 0x95 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xb4 */ + /* 0xb5 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xd4 */ + /* 0xd5 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xf4 */ + /* 0xf5 */ 0,0,0,0,0,0,0,0,0,0 /* 0xfe */ +}; + +/* Definitions of above-declared static functions */ +static char get_escape_equiv(unsigned c) { + return Escape_Equivs[c & 0xff]; +} +static unsigned extract_special(unsigned c) { + return Special_Table[c & 0xff]; +} +static int is_special_end(unsigned c) { + return Special_Endings[c & 0xff]; +} +static int is_allowed_whitespace(unsigned c) { + return c == ' ' || Allowed_Whitespace[c & 0xff]; +} +static int is_allowed_escape(unsigned c) { + return Allowed_Escapes[c & 0xff]; +} + +/* Clean up all our macros! */ +#undef INCR_METRIC +#undef INCR_GENERIC +#undef INCR_STRINGY_CATCH +#undef CASE_DIGITS +#undef INVOKE_ERROR +#undef STACK_PUSH +#undef STACK_POP_NOPOS +#undef STACK_POP +#undef CALLBACK_AND_POP_NOPOS +#undef CALLBACK_AND_POP +#undef SPECIAL_POP +#undef CUR_CHAR +#undef DO_CALLBACK +#undef ENSURE_HVAL +#undef VERIFY_SPECIAL +#undef STATE_SPECIAL_LENGTH +#undef IS_NORMAL_NUMBER +#undef STATE_NUM_LAST +#undef FASTPARSE_EXHAUSTED +#undef FASTPARSE_BREAK diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/jsonsl/jsonsl.h b/couchbase-sys/libcouchbase-2.7.0/contrib/jsonsl/jsonsl.h new file mode 100644 index 00000000..93e262a0 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/jsonsl/jsonsl.h @@ -0,0 +1,971 @@ +/** + * JSON Simple/Stacked/Stateful Lexer. + * - Does not buffer data + * - Maintains state + * - Callback oriented + * - Lightweight and fast. One source file and one header file + * + * Copyright (C) 2012-2015 Mark Nunberg + * See included LICENSE file for license details. + */ + +#ifndef JSONSL_H_ +#define JSONSL_H_ + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef JSONSL_USE_WCHAR +typedef jsonsl_char_t wchar_t; +typedef jsonsl_uchar_t unsigned wchar_t; +#else +typedef char jsonsl_char_t; +typedef unsigned char jsonsl_uchar_t; +#endif /* JSONSL_USE_WCHAR */ + +/* Stolen from http-parser.h, and possibly others */ +#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#if !defined(_MSC_VER) || _MSC_VER<1400 +typedef unsigned int size_t; +typedef int ssize_t; +#endif +#else +#include +#endif + + +#if (!defined(JSONSL_STATE_GENERIC)) && (!defined(JSONSL_STATE_USER_FIELDS)) +#define JSONSL_STATE_GENERIC +#endif /* !defined JSONSL_STATE_GENERIC */ + +#ifdef JSONSL_STATE_GENERIC +#define JSONSL_STATE_USER_FIELDS +#endif /* JSONSL_STATE_GENERIC */ + +/* Additional fields for component object */ +#ifndef JSONSL_JPR_COMPONENT_USER_FIELDS +#define JSONSL_JPR_COMPONENT_USER_FIELDS +#endif + +#ifndef JSONSL_API +/** + * We require a /DJSONSL_DLL so that users already using this as a static + * or embedded library don't get confused + */ +#if defined(_WIN32) && defined(JSONSL_DLL) +#define JSONSL_API __declspec(dllexport) +#else +#define JSONSL_API +#endif /* _WIN32 */ + +#endif /* !JSONSL_API */ + +#ifndef JSONSL_INLINE +#if defined(_MSC_VER) + #define JSONSL_INLINE __inline + #elif defined(__GNUC__) + #define JSONSL_INLINE __inline__ + #else + #define JSONSL_INLINE inline + #endif /* _MSC_VER or __GNUC__ */ +#endif /* JSONSL_INLINE */ + +#define JSONSL_MAX_LEVELS 512 + +struct jsonsl_st; +typedef struct jsonsl_st *jsonsl_t; + +typedef struct jsonsl_jpr_st* jsonsl_jpr_t; + +/** + * This flag is true when AND'd against a type whose value + * must be in "quoutes" i.e. T_HKEY and T_STRING + */ +#define JSONSL_Tf_STRINGY 0xffff00 + +/** + * Constant representing the special JSON types. + * The values are special and aid in speed (the OBJECT and LIST + * values are the char literals of their openings). + * + * Their actual value is a character which attempts to resemble + * some mnemonic reference to the actual type. + * + * If new types are added, they must fit into the ASCII printable + * range (so they should be AND'd with 0x7f and yield something + * meaningful) + */ +#define JSONSL_XTYPE \ + X(STRING, '"'|JSONSL_Tf_STRINGY) \ + X(HKEY, '#'|JSONSL_Tf_STRINGY) \ + X(OBJECT, '{') \ + X(LIST, '[') \ + X(SPECIAL, '^') \ + X(UESCAPE, 'u') +typedef enum { +#define X(o, c) \ + JSONSL_T_##o = c, + JSONSL_XTYPE + JSONSL_T_UNKNOWN = '?', + /* Abstract 'root' object */ + JSONSL_T_ROOT = 0 +#undef X +} jsonsl_type_t; + +/** + * Subtypes for T_SPECIAL. We define them as flags + * because more than one type can be applied to a + * given object. + */ + +#define JSONSL_XSPECIAL \ + X(NONE, 0) \ + X(SIGNED, 1<<0) \ + X(UNSIGNED, 1<<1) \ + X(TRUE, 1<<2) \ + X(FALSE, 1<<3) \ + X(NULL, 1<<4) \ + X(FLOAT, 1<<5) \ + X(EXPONENT, 1<<6) \ + X(NONASCII, 1<<7) +typedef enum { +#define X(o,b) \ + JSONSL_SPECIALf_##o = b, + JSONSL_XSPECIAL +#undef X + /* Handy flags for checking */ + + JSONSL_SPECIALf_UNKNOWN = 1 << 8, + + /** @private Private */ + JSONSL_SPECIALf_ZERO = 1 << 9 | JSONSL_SPECIALf_UNSIGNED, + /** @private */ + JSONSL_SPECIALf_DASH = 1 << 10, + + /** Type is numeric */ + JSONSL_SPECIALf_NUMERIC = (JSONSL_SPECIALf_SIGNED| JSONSL_SPECIALf_UNSIGNED), + + /** Type is a boolean */ + JSONSL_SPECIALf_BOOLEAN = (JSONSL_SPECIALf_TRUE|JSONSL_SPECIALf_FALSE), + + /** Type is an "extended", not integral type (but numeric) */ + JSONSL_SPECIALf_NUMNOINT = (JSONSL_SPECIALf_FLOAT|JSONSL_SPECIALf_EXPONENT) +} jsonsl_special_t; + + +/** + * These are the various types of stack (or other) events + * which will trigger a callback. + * Like the type constants, this are also mnemonic + */ +#define JSONSL_XACTION \ + X(PUSH, '+') \ + X(POP, '-') \ + X(UESCAPE, 'U') \ + X(ERROR, '!') +typedef enum { +#define X(a,c) \ + JSONSL_ACTION_##a = c, + JSONSL_XACTION + JSONSL_ACTION_UNKNOWN = '?' +#undef X +} jsonsl_action_t; + + +/** + * Various errors which may be thrown while parsing JSON + */ +#define JSONSL_XERR \ +/* Trailing garbage characters */ \ + X(GARBAGE_TRAILING) \ +/* We were expecting a 'special' (numeric, true, false, null) */ \ + X(SPECIAL_EXPECTED) \ +/* The 'special' value was incomplete */ \ + X(SPECIAL_INCOMPLETE) \ +/* Found a stray token */ \ + X(STRAY_TOKEN) \ +/* We were expecting a token before this one */ \ + X(MISSING_TOKEN) \ +/* Cannot insert because the container is not ready */ \ + X(CANT_INSERT) \ +/* Found a '\' outside a string */ \ + X(ESCAPE_OUTSIDE_STRING) \ +/* Found a ':' outside of a hash */ \ + X(KEY_OUTSIDE_OBJECT) \ +/* found a string outside of a container */ \ + X(STRING_OUTSIDE_CONTAINER) \ +/* Found a null byte in middle of string */ \ + X(FOUND_NULL_BYTE) \ +/* Current level exceeds limit specified in constructor */ \ + X(LEVELS_EXCEEDED) \ +/* Got a } as a result of an opening [ or vice versa */ \ + X(BRACKET_MISMATCH) \ +/* We expected a key, but got something else instead */ \ + X(HKEY_EXPECTED) \ +/* We got an illegal control character (bad whitespace or something) */ \ + X(WEIRD_WHITESPACE) \ +/* Found a \u-escape, but there were less than 4 following hex digits */ \ + X(UESCAPE_TOOSHORT) \ +/* Invalid two-character escape */ \ + X(ESCAPE_INVALID) \ +/* Trailing comma */ \ + X(TRAILING_COMMA) \ +/* An invalid number was passed in a numeric field */ \ + X(INVALID_NUMBER) \ +/* Value is missing for object */ \ + X(VALUE_EXPECTED) \ +/* The following are for JPR Stuff */ \ + \ +/* Found a literal '%' but it was only followed by a single valid hex digit */ \ + X(PERCENT_BADHEX) \ +/* jsonpointer URI is malformed '/' */ \ + X(JPR_BADPATH) \ +/* Duplicate slash */ \ + X(JPR_DUPSLASH) \ +/* No leading root */ \ + X(JPR_NOROOT) \ +/* Allocation failure */ \ + X(ENOMEM) + +typedef enum { + JSONSL_ERROR_SUCCESS = 0, +#define X(e) \ + JSONSL_ERROR_##e, + JSONSL_XERR +#undef X + JSONSL_ERROR_GENERIC +} jsonsl_error_t; + + +/** + * A state is a single level of the stack. + * Non-private data (i.e. the 'data' field, see the STATE_GENERIC section) + * will remain in tact until the item is popped. + * + * As a result, it means a parent state object may be accessed from a child + * object, (the parents fields will all be valid). This allows a user to create + * an ad-hoc hierarchy on top of the JSON one. + * + */ +struct jsonsl_state_st { + /** + * The JSON object type + */ + unsigned type; + + /** If this element is special, then its extended type is here */ + unsigned special_flags; + + /** + * The position (in terms of number of bytes since the first call to + * jsonsl_feed()) at which the state was first pushed. This includes + * opening tokens, if applicable. + * + * @note For strings (i.e. type & JSONSL_Tf_STRINGY is nonzero) this will + * be the position of the first quote. + * + * @see jsonsl_st::pos which contains the _current_ position and can be + * used during a POP callback to get the length of the element. + */ + size_t pos_begin; + + /**FIXME: This is redundant as the same information can be derived from + * jsonsl_st::pos at pop-time */ + size_t pos_cur; + + /** + * Level of recursion into nesting. This is mainly a convenience + * variable, as this can technically be deduced from the lexer's + * level parameter (though the logic is not that simple) + */ + unsigned int level; + + + /** + * how many elements in the object/list. + * For objects (hashes), an element is either + * a key or a value. Thus for one complete pair, + * nelem will be 2. + * + * For special types, this will hold the sum of the digits. + * This only holds true for values which are simple signed/unsigned + * numbers. Otherwise a special flag is set, and extra handling is not + * performed. + */ + uint64_t nelem; + + + + /*TODO: merge this and special_flags into a union */ + + + /** + * Useful for an opening nest, this will prevent a callback from being + * invoked on this item or any of its children + */ + int ignore_callback; + + /** + * Counter which is incremented each time an escape ('\') is encountered. + * This is used internally for non-string types and should only be + * inspected by the user if the state actually represents a string + * type. + */ + unsigned int nescapes; + + /** + * Put anything you want here. if JSONSL_STATE_USER_FIELDS is here, then + * the macro expansion happens here. + * + * You can use these fields to store hierarchical or 'tagging' information + * for specific objects. + * + * See the documentation above for the lifetime of the state object (i.e. + * if the private data points to allocated memory, it should be freed + * when the object is popped, as the state object will be re-used) + */ +#ifndef JSONSL_STATE_GENERIC + JSONSL_STATE_USER_FIELDS +#else + + /** + * Otherwise, this is a simple void * pointer for anything you want + */ + void *data; +#endif /* JSONSL_STATE_USER_FIELDS */ +}; + +/**Gets the number of elements in the list. + * @param st The state. Must be of type JSONSL_T_LIST + * @return number of elements in the list + */ +#define JSONSL_LIST_SIZE(st) ((st)->nelem) + +/**Gets the number of key-value pairs in an object + * @param st The state. Must be of type JSONSL_T_OBJECT + * @return the number of key-value pairs in the object + */ +#define JSONSL_OBJECT_SIZE(st) ((st)->nelem / 2) + +/**Gets the numeric value. + * @param st The state. Must be of type JSONSL_T_SPECIAL and + * special_flags must have the JSONSL_SPECIALf_NUMERIC flag + * set. + * @return the numeric value of the state. + */ +#define JSONSL_NUMERIC_VALUE(st) ((st)->nelem) + +/* + * So now we need some special structure for keeping the + * JPR info in sync. Preferrably all in a single block + * of memory (there's no need for separate allocations. + * So we will define a 'table' with the following layout + * + * Level nPosbl JPR1_last JPR2_last JPR3_last + * + * 0 1 NOMATCH POSSIBLE POSSIBLE + * 1 0 NOMATCH NOMATCH COMPLETE + * [ table ends here because no further path is possible] + * + * Where the JPR..n corresponds to the number of JPRs + * requested, and nPosble is a quick flag to determine + * + * the number of possibilities. In the future this might + * be made into a proper 'jump' table, + * + * Since we always mark JPRs from the higher levels descending + * into the lower ones, a prospective child match would first + * look at the parent table to check the possibilities, and then + * see which ones were possible.. + * + * Thus, the size of this blob would be (and these are all ints here) + * nLevels * nJPR * 2. + * + * the 'Width' of the table would be nJPR*2, and the 'height' would be + * nlevels + */ + +/** + * This is called when a stack change ocurs. + * + * @param jsn The lexer + * @param action The type of action, this can be PUSH or POP + * @param state A pointer to the stack currently affected by the action + * @param at A pointer to the position of the input buffer which triggered + * this action. + */ +typedef void (*jsonsl_stack_callback)( + jsonsl_t jsn, + jsonsl_action_t action, + struct jsonsl_state_st* state, + const jsonsl_char_t *at); + + +/** + * This is called when an error is encountered. + * Sometimes it's possible to 'erase' characters (by replacing them + * with whitespace). If you think you have corrected the error, you + * can return a true value, in which case the parser will backtrack + * and try again. + * + * @param jsn The lexer + * @param error The error which was thrown + * @param state the current state + * @param a pointer to the position of the input buffer which triggered + * the error. Note that this is not const, this is because you have the + * possibility of modifying the character in an attempt to correct the + * error + * + * @return zero to bail, nonzero to try again (this only makes sense if + * the input buffer has been modified by this callback) + */ +typedef int (*jsonsl_error_callback)( + jsonsl_t jsn, + jsonsl_error_t error, + struct jsonsl_state_st* state, + jsonsl_char_t *at); + +struct jsonsl_st { + /** Public, read-only */ + + /** This is the current level of the stack */ + unsigned int level; + + /** Flag set to indicate we should stop processing */ + unsigned int stopfl; + + /** + * This is the current position, relative to the beginning + * of the stream. + */ + size_t pos; + + /** This is the 'bytes' variable passed to feed() */ + const jsonsl_char_t *base; + + /** Callback invoked for PUSH actions */ + jsonsl_stack_callback action_callback_PUSH; + + /** Callback invoked for POP actions */ + jsonsl_stack_callback action_callback_POP; + + /** Default callback for any action, if neither PUSH or POP callbacks are defined */ + jsonsl_stack_callback action_callback; + + /** + * Do not invoke callbacks for objects deeper than this level. + * NOTE: This field establishes the lower bound for ignored callbacks, + * and is thus misnamed. `min_ignore_level` would actually make more + * sense, but we don't want to break API. + */ + unsigned int max_callback_level; + + /** The error callback. Invoked when an error happens. Should not be NULL */ + jsonsl_error_callback error_callback; + + /* these are boolean flags you can modify. You will be called + * about notification for each of these types if the corresponding + * variable is true. + */ + + /** + * @name Callback Booleans. + * These determine whether a callback is to be invoked for certain types of objects + * @{*/ + + /** Boolean flag to enable or disable the invokcation for events on this type*/ + int call_SPECIAL; + int call_OBJECT; + int call_LIST; + int call_STRING; + int call_HKEY; + /*@}*/ + + /** + * @name u-Escape handling + * Special handling for the \\u-f00d type sequences. These are meant + * to be translated back into the corresponding octet(s). + * A special callback (if set) is invoked with *at=='u'. An application + * may wish to temporarily suspend parsing and handle the 'u-' sequence + * internally (or not). + */ + + /*@{*/ + + /** Callback to be invoked for a u-escape */ + jsonsl_stack_callback action_callback_UESCAPE; + + /** Boolean flag, whether to invoke the callback */ + int call_UESCAPE; + + /** Boolean flag, whether we should return after encountering a u-escape: + * the callback is invoked and then we return if this is true + */ + int return_UESCAPE; + /*@}*/ + + struct { + int allow_trailing_comma; + } options; + + /** Put anything here */ + void *data; + + /*@{*/ + /** Private */ + int in_escape; + char expecting; + char tok_last; + int can_insert; + unsigned int levels_max; + +#ifndef JSONSL_NO_JPR + size_t jpr_count; + jsonsl_jpr_t *jprs; + + /* Root pointer for JPR matching information */ + size_t *jpr_root; +#endif /* JSONSL_NO_JPR */ + /*@}*/ + + /** + * This is the stack. Its upper bound is levels_max, or the + * nlevels argument passed to jsonsl_new. If you modify this structure, + * make sure that this member is last. + */ + struct jsonsl_state_st stack[1]; +}; + + +/** + * Creates a new lexer object, with capacity for recursion up to nlevels + * + * @param nlevels maximum recursion depth + */ +JSONSL_API +jsonsl_t jsonsl_new(int nlevels); + +/** + * Feeds data into the lexer. + * + * @param jsn the lexer object + * @param bytes new data to be fed + * @param nbytes size of new data + */ +JSONSL_API +void jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes); + +/** + * Resets the internal parser state. This does not free the parser + * but does clean it internally, so that the next time feed() is called, + * it will be treated as a new stream + * + * @param jsn the lexer + */ +JSONSL_API +void jsonsl_reset(jsonsl_t jsn); + +/** + * Frees the lexer, cleaning any allocated memory taken + * + * @param jsn the lexer + */ +JSONSL_API +void jsonsl_destroy(jsonsl_t jsn); + +/** + * Gets the 'parent' element, given the current one + * + * @param jsn the lexer + * @param cur the current nest, which should be a struct jsonsl_nest_st + */ +static JSONSL_INLINE +struct jsonsl_state_st *jsonsl_last_state(const jsonsl_t jsn, + const struct jsonsl_state_st *state) +{ + /* Don't complain about overriding array bounds */ + if (state->level > 1) { + return jsn->stack + state->level - 1; + } else { + return NULL; + } +} + +/** + * Gets the state of the last fully consumed child of this parent. This is + * only valid in the parent's POP callback. + * + * @param the lexer + * @return A pointer to the child. + */ +static JSONSL_INLINE +struct jsonsl_state_st *jsonsl_last_child(const jsonsl_t jsn, + const struct jsonsl_state_st *parent) +{ + return jsn->stack + (parent->level + 1); +} + +/**Call to instruct the parser to stop parsing and return. This is valid + * only from within a callback */ +static JSONSL_INLINE +void jsonsl_stop(jsonsl_t jsn) +{ + jsn->stopfl = 1; +} + +/** + * This enables receiving callbacks on all events. Doesn't do + * anything special but helps avoid some boilerplate. + * This does not touch the UESCAPE callbacks or flags. + */ +static JSONSL_INLINE +void jsonsl_enable_all_callbacks(jsonsl_t jsn) +{ + jsn->call_HKEY = 1; + jsn->call_STRING = 1; + jsn->call_OBJECT = 1; + jsn->call_SPECIAL = 1; + jsn->call_LIST = 1; +} + +/** + * A macro which returns true if the current state object can + * have children. This means a list type or an object type. + */ +#define JSONSL_STATE_IS_CONTAINER(state) \ + (state->type == JSONSL_T_OBJECT || state->type == JSONSL_T_LIST) + +/** + * These two functions, dump a string representation + * of the error or type, respectively. They will never + * return NULL + */ +JSONSL_API +const char* jsonsl_strerror(jsonsl_error_t err); +JSONSL_API +const char* jsonsl_strtype(jsonsl_type_t jt); + +/** + * Dumps global metrics to the screen. This is a noop unless + * jsonsl was compiled with JSONSL_USE_METRICS + */ +JSONSL_API +void jsonsl_dump_global_metrics(void); + +/* This macro just here for editors to do code folding */ +#ifndef JSONSL_NO_JPR + +/** + * @name JSON Pointer API + * + * JSONPointer API. This isn't really related to the lexer (at least not yet) + * JSONPointer provides an extremely simple specification for providing + * locations within JSON objects. We will extend it a bit and allow for + * providing 'wildcard' characters by which to be able to 'query' the stream. + * + * See http://tools.ietf.org/html/draft-pbryan-zyp-json-pointer-00 + * + * Currently I'm implementing the 'single query' API which can only use a single + * query component. In the future I will integrate my yet-to-be-published + * Boyer-Moore-esque prefix searching implementation, in order to allow + * multiple paths to be merged into one for quick and efficient searching. + * + * + * JPR (as we'll refer to it within the source) can be used by splitting + * the components into mutliple sections, and incrementally 'track' each + * component. When JSONSL delivers a 'pop' callback for a string, or a 'push' + * callback for an object, we will check to see whether the index matching + * the component corresponding to the current level contains a match + * for our path. + * + * In order to do this properly, a structure must be maintained within the + * parent indicating whether its children are possible matches. This flag + * will be 'inherited' by call children which may conform to the match + * specification, and discarded by all which do not (thereby eliminating + * their children from inheriting it). + * + * A successful match is a complete one. One can provide multiple paths with + * multiple levels of matches e.g. + * /foo/bar/baz/^/blah + * + * @{ + */ + +/** The wildcard character */ +#ifndef JSONSL_PATH_WILDCARD_CHAR +#define JSONSL_PATH_WILDCARD_CHAR '^' +#endif /* WILDCARD_CHAR */ + +#define JSONSL_XMATCH \ + X(COMPLETE,1) \ + X(POSSIBLE,0) \ + X(NOMATCH,-1) \ + X(TYPE_MISMATCH, -2) + +typedef enum { + +#define X(T,v) \ + JSONSL_MATCH_##T = v, + JSONSL_XMATCH + +#undef X + JSONSL_MATCH_UNKNOWN +} jsonsl_jpr_match_t; + +typedef enum { + JSONSL_PATH_STRING = 1, + JSONSL_PATH_WILDCARD, + JSONSL_PATH_NUMERIC, + JSONSL_PATH_ROOT, + + /* Special */ + JSONSL_PATH_INVALID = -1, + JSONSL_PATH_NONE = 0 +} jsonsl_jpr_type_t; + +struct jsonsl_jpr_component_st { + /** The string the component points to */ + char *pstr; + /** if this is a numeric type, the number is 'cached' here */ + unsigned long idx; + /** The length of the string */ + size_t len; + /** The type of component (NUMERIC or STRING) */ + jsonsl_jpr_type_t ptype; + + /** Set this to true to enforce type checking between dict keys and array + * indices. jsonsl_jpr_match() will return TYPE_MISMATCH if it detects + * that an array index is actually a child of a dictionary. */ + short is_arridx; + + /* Extra fields (for more advanced searches. Default is empty) */ + JSONSL_JPR_COMPONENT_USER_FIELDS +}; + +struct jsonsl_jpr_st { + /** Path components */ + struct jsonsl_jpr_component_st *components; + size_t ncomponents; + + /** Base of allocated string for components */ + char *basestr; + + /** The original match string. Useful for returning to the user */ + char *orig; + size_t norig; +}; + + + +/** + * Create a new JPR object. + * + * @param path the JSONPointer path specification. + * @param errp a pointer to a jsonsl_error_t. If this function returns NULL, + * then more details will be in this variable. + * + * @return a new jsonsl_jpr_t object, or NULL on error. + */ +JSONSL_API +jsonsl_jpr_t jsonsl_jpr_new(const char *path, jsonsl_error_t *errp); + +/** + * Destroy a JPR object + */ +JSONSL_API +void jsonsl_jpr_destroy(jsonsl_jpr_t jpr); + +/** + * Match a JSON object against a type and specific level + * + * @param jpr the JPR object + * @param parent_type the type of the parent (should be T_LIST or T_OBJECT) + * @param parent_level the level of the parent + * @param key the 'key' of the child. If the parent is an array, this should be + * empty. + * @param nkey - the length of the key. If the parent is an array (T_LIST), then + * this should be the current index. + * + * NOTE: The key of the child means any kind of associative data related to the + * element. Thus: <<< { "foo" : [ >>, + * the opening array's key is "foo". + * + * @return a status constant. This indicates whether a match was excluded, possible, + * or successful. + */ +JSONSL_API +jsonsl_jpr_match_t jsonsl_jpr_match(jsonsl_jpr_t jpr, + unsigned int parent_type, + unsigned int parent_level, + const char *key, size_t nkey); + + +/** + * Associate a set of JPR objects with a lexer instance. + * This should be called before the lexer has been fed any data (and + * behavior is undefined if you don't adhere to this). + * + * After using this function, you may subsequently call match_state() on + * given states (presumably from within the callbacks). + * + * Note that currently the first JPR is the quickest and comes + * pre-allocated with the state structure. Further JPR objects + * are chained. + * + * @param jsn The lexer + * @param jprs An array of jsonsl_jpr_t objects + * @param njprs How many elements in the jprs array. + */ +JSONSL_API +void jsonsl_jpr_match_state_init(jsonsl_t jsn, + jsonsl_jpr_t *jprs, + size_t njprs); + +/** + * This follows the same semantics as the normal match, + * except we infer parent and type information from the relevant state objects. + * The match status (for all possible JPR objects) is set in the *out parameter. + * + * If a match has succeeded, then its JPR object will be returned. In all other + * instances, NULL is returned; + * + * @param jpr The jsonsl_jpr_t handle + * @param state The jsonsl_state_st which is a candidate + * @param key The hash key (if applicable, can be NULL if parent is list) + * @param nkey Length of hash key (if applicable, can be zero if parent is list) + * @param out A pointer to a jsonsl_jpr_match_t. This will be populated with + * the match result + * + * @return If a match was completed in full, then the JPR object containing + * the matching path will be returned. Otherwise, the return is NULL (note, this + * does not mean matching has failed, it can still be part of the match: check + * the out parameter). + */ +JSONSL_API +jsonsl_jpr_t jsonsl_jpr_match_state(jsonsl_t jsn, + struct jsonsl_state_st *state, + const char *key, + size_t nkey, + jsonsl_jpr_match_t *out); + + +/** + * Cleanup any memory allocated and any states set by + * match_state_init() and match_state() + * @param jsn The lexer + */ +JSONSL_API +void jsonsl_jpr_match_state_cleanup(jsonsl_t jsn); + +/** + * Return a string representation of the match result returned by match() + */ +JSONSL_API +const char *jsonsl_strmatchtype(jsonsl_jpr_match_t match); + +/* @}*/ + +/** + * Utility function to convert escape sequences into their original form. + * + * The decoders I've sampled do not seem to specify a standard behavior of what + * to escape/unescape. + * + * RFC 4627 Mandates only that the quoute, backslash, and ASCII control + * characters (0x00-0x1f) be escaped. It is often common for applications + * to escape a '/' - however this may also be desired behavior. the JSON + * spec is not clear on this, and therefore jsonsl leaves it up to you. + * + * @param in The input string. + * @param out An allocated output (should be the same size as in) + * @param len the size of the buffer + * @param toEscape - A sparse array of characters to unescape. Characters + * which are not present in this array, e.g. toEscape['c'] == 0 will be + * ignored and passed to the output in their original form. + * @param oflags If not null, and a \uXXXX escape expands to a non-ascii byte, + * then this variable will have the SPECIALf_NONASCII flag on. + * + * @param err A pointer to an error variable. If an error ocurrs, it will be + * set in this variable + * @param errat If not null and an error occurs, this will be set to point + * to the position within the string at which the offending character was + * encountered. + * + * @return The effective size of the output buffer. + */ +JSONSL_API +size_t jsonsl_util_unescape_ex(const char *in, + char *out, + size_t len, + const int toEscape[128], + unsigned *oflags, + jsonsl_error_t *err, + const char **errat); + +/** + * Convenience macro to avoid passing too many parameters + */ +#define jsonsl_util_unescape(in, out, len, toEscape, err) \ + jsonsl_util_unescape_ex(in, out, len, toEscape, NULL, err, NULL) + +#endif /* JSONSL_NO_JPR */ + +/** + * HERE BE CHARACTER TABLES! + */ +#define JSONSL_CHARTABLE_string_nopass \ +/* 0x00 */ 1 /* */, /* 0x00 */ \ +/* 0x01 */ 1 /* */, /* 0x01 */ \ +/* 0x02 */ 1 /* */, /* 0x02 */ \ +/* 0x03 */ 1 /* */, /* 0x03 */ \ +/* 0x04 */ 1 /* */, /* 0x04 */ \ +/* 0x05 */ 1 /* */, /* 0x05 */ \ +/* 0x06 */ 1 /* */, /* 0x06 */ \ +/* 0x07 */ 1 /* */, /* 0x07 */ \ +/* 0x08 */ 1 /* */, /* 0x08 */ \ +/* 0x09 */ 1 /* */, /* 0x09 */ \ +/* 0x0a */ 1 /* */, /* 0x0a */ \ +/* 0x0b */ 1 /* */, /* 0x0b */ \ +/* 0x0c */ 1 /* */, /* 0x0c */ \ +/* 0x0d */ 1 /* */, /* 0x0d */ \ +/* 0x0e */ 1 /* */, /* 0x0e */ \ +/* 0x0f */ 1 /* */, /* 0x0f */ \ +/* 0x10 */ 1 /* */, /* 0x10 */ \ +/* 0x11 */ 1 /* */, /* 0x11 */ \ +/* 0x12 */ 1 /* */, /* 0x12 */ \ +/* 0x13 */ 1 /* */, /* 0x13 */ \ +/* 0x14 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x21 */ \ +/* 0x22 */ 1 /* <"> */, /* 0x22 */ \ +/* 0x23 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x42 */ \ +/* 0x43 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5b */ \ +/* 0x5c */ 1 /* <\> */, /* 0x5c */ \ +/* 0x5d */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x7c */ \ +/* 0x7d */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x9c */ \ +/* 0x9d */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xbc */ \ +/* 0xbd */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xdc */ \ +/* 0xdd */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xfc */ \ +/* 0xfd */ 0,0 /* 0xfe */ \ + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* JSONSL_H_ */ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/CMakeLists.txt b/couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/CMakeLists.txt new file mode 100644 index 00000000..4500b214 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/CMakeLists.txt @@ -0,0 +1,6 @@ +ADD_LIBRARY(lcb_jsoncpp OBJECT lcb-jsoncpp.cpp) +ADD_DEFINITIONS(-DJSON_USE_EXCEPTIONS=0) +SET_TARGET_PROPERTIES(lcb_jsoncpp + PROPERTIES + POSITION_INDEPENDENT_CODE TRUE + COMPILE_FLAGS "${LCB_CORE_CXXFLAGS}") diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/LICENSE b/couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/LICENSE new file mode 100644 index 00000000..ca2bfe1a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/LICENSE @@ -0,0 +1,55 @@ +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/lcb-jsoncpp-forwards.h b/couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/lcb-jsoncpp-forwards.h new file mode 100644 index 00000000..2fda67d1 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/lcb-jsoncpp-forwards.h @@ -0,0 +1,255 @@ +/// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/). +/// It is intended to be used with #include "lcb-jsoncpp-forwards.h" +/// This header provides forward declaration for all JsonCpp types. + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + +#ifndef JSON_FORWARD_AMALGATED_H_INCLUDED +# define JSON_FORWARD_AMALGATED_H_INCLUDED +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +#define JSON_IS_AMALGAMATION + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +#define JSON_CONFIG_H_INCLUDED + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of +/// std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 + +// If non-zero, the library uses exceptions to report bad input instead of C +// assertion macros. The default is to use exceptions. +#ifndef JSON_USE_EXCEPTION +#define JSON_USE_EXCEPTION 1 +#endif + +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgated header. +// #define JSON_IS_AMALGAMATION + +#ifdef JSON_IN_CPPTL +#include +#ifndef JSON_USE_CPPTL +#define JSON_USE_CPPTL 1 +#endif +#endif + +#ifdef JSON_IN_CPPTL +#define JSON_API CPPTL_API +#elif defined(JSON_DLL_BUILD) +#if defined(_MSC_VER) +#define JSON_API __declspec(dllexport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#elif defined(JSON_DLL) +#if defined(_MSC_VER) +#define JSON_API __declspec(dllimport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#endif // ifdef JSON_IN_CPPTL +#if !defined(JSON_API) +#define JSON_API +#endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for +// integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 +// Microsoft Visual Studio 6 only support conversion from __int64 to double +// (no conversion from unsigned __int64). +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' +// characters in the debug information) +// All projects I've ever seen with VS6 were using this globally (not bothering +// with pragma push/pop). +#pragma warning(disable : 4786) +#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 + +#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 +/// Indicates that the following function is deprecated. +#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#elif defined(__clang__) && defined(__has_feature) +#if __has_feature(attribute_deprecated_with_message) +#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +#endif +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +#elif defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) +#endif + +#if !defined(JSONCPP_DEPRECATED) +#define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +namespace Json { +typedef int Int; +typedef unsigned int UInt; +#if defined(JSON_NO_INT64) +typedef int LargestInt; +typedef unsigned int LargestUInt; +#undef JSON_HAS_INT64 +#else // if defined(JSON_NO_INT64) +// For Microsoft Visual use specific types as long long is not supported +#if defined(_MSC_VER) // Microsoft Visual Studio +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else // if defined(_MSC_VER) // Other platforms, use long long +typedef long long int Int64; +typedef unsigned long long int UInt64; +#endif // if defined(_MSC_VER) +typedef Int64 LargestInt; +typedef UInt64 LargestUInt; +#define JSON_HAS_INT64 +#endif // if defined(JSON_NO_INT64) +} // end namespace Json + +#endif // JSON_CONFIG_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +#define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// writer.h +class FastWriter; +class StyledWriter; + +// reader.h +class Reader; + +// features.h +class Features; + +// value.h +typedef unsigned int ArrayIndex; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; + +} // namespace Json + +#endif // JSON_FORWARDS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + + + + + +#endif //ifndef JSON_FORWARD_AMALGATED_H_INCLUDED diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/lcb-jsoncpp.cpp b/couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/lcb-jsoncpp.cpp new file mode 100644 index 00000000..0be40fe9 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/lcb-jsoncpp.cpp @@ -0,0 +1,4892 @@ +/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/). +/// It is intended to be used with #include "lcb-jsoncpp.h" + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + + +#include "lcb-jsoncpp.h" + +#ifndef JSON_IS_AMALGAMATION +#error "Compile with -I PATH_TO_JSON_DIRECTORY" +#endif + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_tool.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED +#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +/* This header provides common string manipulation support, such as UTF-8, + * portable conversion from/to string... + * + * It is an internal header that must not be exposed. + */ + +namespace Json { + +/// Converts a unicode code-point to UTF-8. +static inline std::string codePointToUTF8(unsigned int cp) { + std::string result; + + // based on description from http://en.wikipedia.org/wiki/UTF-8 + + if (cp <= 0x7f) { + result.resize(1); + result[0] = static_cast(cp); + } else if (cp <= 0x7FF) { + result.resize(2); + result[1] = static_cast(0x80 | (0x3f & cp)); + result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); + } else if (cp <= 0xFFFF) { + result.resize(3); + result[2] = static_cast(0x80 | (0x3f & cp)); + result[1] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[0] = static_cast(0xE0 | (0xf & (cp >> 12))); + } else if (cp <= 0x10FFFF) { + result.resize(4); + result[3] = static_cast(0x80 | (0x3f & cp)); + result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); + result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); + } + + return result; +} + +/// Returns true if ch is a control character (in range [1,31]). +static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; } + +enum { + /// Constant that specify the size of the buffer that must be passed to + /// uintToString. + uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1 +}; + +// Defines a char buffer for use with uintToString(). +typedef char UIntToStringBuffer[uintToStringBufferSize]; + +/** Converts an unsigned integer to string. + * @param value Unsigned interger to convert to string + * @param current Input/Output string buffer. + * Must have at least uintToStringBufferSize chars free. + */ +static inline void uintToString(LargestUInt value, char*& current) { + *--current = 0; + do { + *--current = static_cast(value % 10U + static_cast('0')); + value /= 10; + } while (value != 0); +} + +/** Change ',' to '.' everywhere in buffer. + * + * We had a sophisticated way, but it did not work in WinCE. + * @see https://github.com/open-source-parsers/jsoncpp/pull/9 + */ +static inline void fixNumericLocale(char* begin, char* end) { + while (begin < end) { + if (*begin == ',') { + *begin = '.'; + } + ++begin; + } +} + +} // namespace Json { + +#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_tool.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_reader.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below +#define snprintf _snprintf +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +static int const stackLimit_g = 1000; +static int stackDepth_g = 0; // see readValue() + +namespace Json { + +typedef std::auto_ptr CharReaderPtr; + +// Implementation of class Features +// //////////////////////////////// + +Features::Features() + : allowComments_(true), strictRoot_(false) +{} +Features Features::all() { return Features(); } + +Features Features::strictMode() { + Features features; + features.allowComments_ = false; + features.strictRoot_ = true; + return features; +} + +// Implementation of class Reader +// //////////////////////////////// + +static bool containsNewLine(Reader::Location begin, Reader::Location end) { + for (; begin < end; ++begin) + if (*begin == '\n' || *begin == '\r') + return true; + return false; +} + +// Class Reader +// ////////////////////////////////////////////////////////////////// + +Reader::Reader() + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(Features::all()), + collectComments_() {} + +Reader::Reader(const Features& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(features), collectComments_() { +} + +bool +Reader::parse(const std::string& document, Value& root, bool collectComments) { + document_ = document; + const char* begin = document_.c_str(); + const char* end = begin + document_.length(); + return parse(begin, end, root, collectComments); +} + +bool Reader::parse(std::istream& sin, Value& root, bool collectComments) { + // std::istream_iterator begin(sin); + // std::istream_iterator end; + // Those would allow streamed input from a file, if parse() were a + // template function. + + // Since std::string is reference-counted, this at least does not + // create an extra copy. + std::string doc; + std::getline(sin, doc, (char)EOF); + return parse(doc, root, collectComments); +} + +bool Reader::parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + stackDepth_g = 0; // Yes, this is bad coding, but options are limited. + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool Reader::readValue() { + // This is a non-reentrant way to support a stackLimit. Terrible! + // But this deprecated class has a security problem: Bad input can + // cause a seg-fault. This seems like a fair, binary-compatible way + // to prevent the problem. + if (stackDepth_g >= stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue()."); + ++stackDepth_g; + + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_ = ""; + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + break; + case tokenArrayBegin: + successful = readArray(token); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: + { + Value v(true); + currentValue().swapPayload(v); + } + break; + case tokenFalse: + { + Value v(false); + currentValue().swapPayload(v); + } + break; + case tokenNull: + { + Value v; + currentValue().swapPayload(v); + } + break; + // Else, fall through... + default: + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + --stackDepth_g; + return successful; +} + +void Reader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool Reader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void Reader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool Reader::match(Location pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool Reader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +static std::string normalizeEOL(Reader::Location begin, Reader::Location end) { + std::string normalized; + normalized.reserve(end - begin); + Reader::Location current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') { + if (current != end && *current == '\n') + // convert dos EOL + ++current; + // convert Mac EOL + normalized += '\n'; + } else { + normalized += c; + } + } + return normalized; +} + +void +Reader::addComment(Location begin, Location end, CommentPlacement placement) { + assert(collectComments_); + const std::string& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool Reader::readCStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool Reader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +void Reader::readNumber() { + const char *p = current_; + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : 0; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : 0; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : 0; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + } +} + +bool Reader::readString() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + +bool Reader::readObject(Token& /*tokenStart*/) { + Token tokenName; + std::string name; + Value init(objectValue); + currentValue().swapPayload(init); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + return true; + name = ""; + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else { + break; + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover( + "Missing ':' after object member name", colon, tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover( + "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover( + "Missing '}' or object member name", tokenName, tokenObjectEnd); +} + +bool Reader::readArray(Token& /*tokenStart*/) { + Value init(arrayValue); + currentValue().swapPayload(init); + skipSpaces(); + if (*current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token token; + // Accept Comment after last item in the array. + ok = readToken(token); + while (token.type_ == tokenComment && ok) { + ok = readToken(token); + } + bool badTokenType = + (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover( + "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + } + if (token.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool Reader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + return true; +} + +bool Reader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + // TODO: Help the compiler do the div and mod at compile time or get rid of them. + Value::LargestUInt maxIntegerValue = + isNegative ? Value::LargestUInt(-Value::minLargestInt) + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + Value::UInt digit(c - '0'); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > maxIntegerValue % 10) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool Reader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + return true; +} + +bool Reader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + std::string buffer(token.start_, token.end_); + std::istringstream is(buffer); + if (!(is >> value)) + return addError("'" + std::string(token.start_, token.end_) + + "' is not a number.", + token); + decoded = value; + return true; +} + +bool Reader::decodeString(Token& token) { + std::string decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + return true; +} + +bool Reader::decodeString(Token& token, std::string& decoded) { + decoded.reserve(token.end_ - token.start_ - 2); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + else if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool Reader::decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, + current); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++) == 'u') { + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, + current); + } + return true; +} + +bool Reader::decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", + token, + current); + unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, + current); + } + return true; +} + +bool +Reader::addError(const std::string& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool Reader::recoverFromError(TokenType skipUntilToken) { + int errorCount = int(errors_.size()); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool Reader::addErrorAndRecover(const std::string& message, + Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& Reader::currentValue() { return *(nodes_.top()); } + +Reader::Char Reader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void Reader::getLocationLineAndColumn(Location location, + int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +std::string Reader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; +#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) +#if defined(WINCE) + _snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#else + sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#endif +#else + snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#endif + return buffer; +} + +// Deprecated. Preserved for backward compatibility +std::string Reader::getFormatedErrorMessages() const { + return getFormattedErrorMessages(); +} + +std::string Reader::getFormattedErrorMessages() const { + std::string formattedMessage; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +// Reader +///////////////////////// + +// exact copy of Features +class OurFeatures { +public: + static OurFeatures all(); + OurFeatures(); + bool allowComments_; + bool strictRoot_; + bool allowDroppedNullPlaceholders_; + bool allowNumericKeys_; + bool allowSingleQuotes_; + bool failIfExtra_; + bool rejectDupKeys_; + int stackLimit_; +}; // OurFeatures + +// exact copy of Implementation of class Features +// //////////////////////////////// + +OurFeatures::OurFeatures() + : allowComments_(true), strictRoot_(false) + , allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) + , allowSingleQuotes_(false) + , failIfExtra_(false) +{ +} + +OurFeatures OurFeatures::all() { return OurFeatures(); } + +// Implementation of class Reader +// //////////////////////////////// + +// exact copy of Reader, renamed to OurReader +class OurReader { +public: + typedef char Char; + typedef const Char* Location; + struct StructuredError { + size_t offset_start; + size_t offset_limit; + std::string message; + }; + + OurReader(OurFeatures const& features); + bool parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments = true); + std::string getFormattedErrorMessages() const; + +private: + OurReader(OurReader const&); // no impl + void operator=(OurReader const&); // no impl + + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + std::string message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool readToken(Token& token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + bool readStringSingleQuote(); + void readNumber(); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, std::string& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool addError(const std::string& message, Token& token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const std::string& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + std::string getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + std::string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value* lastValue_; + std::string commentsBefore_; + int stackDepth_; + + OurFeatures const features_; + bool collectComments_; +}; // OurReader + +// complete copy of Read impl, for OurReader + +OurReader::OurReader(OurFeatures const& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(features), collectComments_() { +} + +bool OurReader::parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + stackDepth_ = 0; + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (features_.failIfExtra_) { + if (token.type_ != tokenError && token.type_ != tokenEndOfStream) { + addError("Extra non-whitespace after JSON value.", token); + return false; + } + } + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool OurReader::readValue() { + if (stackDepth_ >= features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue()."); + ++stackDepth_; + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_ = ""; + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + break; + case tokenArrayBegin: + successful = readArray(token); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: + { + Value v(true); + currentValue().swapPayload(v); + } + break; + case tokenFalse: + { + Value v(false); + currentValue().swapPayload(v); + } + break; + case tokenNull: + { + Value v; + currentValue().swapPayload(v); + } + break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + break; + } // else, fall through ... + default: + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + --stackDepth_; + return successful; +} + +void OurReader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool OurReader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '\'': + if (features_.allowSingleQuotes_) { + token.type_ = tokenString; + ok = readStringSingleQuote(); + break; + } // else continue + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void OurReader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool OurReader::match(Location pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool OurReader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +void +OurReader::addComment(Location begin, Location end, CommentPlacement placement) { + assert(collectComments_); + const std::string& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool OurReader::readCStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool OurReader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +void OurReader::readNumber() { + const char *p = current_; + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : 0; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : 0; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : 0; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + } +} +bool OurReader::readString() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + + +bool OurReader::readStringSingleQuote() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '\'') + break; + } + return c == '\''; +} + +bool OurReader::readObject(Token& /*tokenStart*/) { + Token tokenName; + std::string name; + Value init(objectValue); + currentValue().swapPayload(init); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + return true; + name = ""; + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = numberName.asString(); + } else { + break; + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover( + "Missing ':' after object member name", colon, tokenObjectEnd); + } + if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30"); + if (features_.rejectDupKeys_ && currentValue().isMember(name)) { + std::string msg = "Duplicate key: '" + name + "'"; + return addErrorAndRecover( + msg, tokenName, tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover( + "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover( + "Missing '}' or object member name", tokenName, tokenObjectEnd); +} + +bool OurReader::readArray(Token& /*tokenStart*/) { + Value init(arrayValue); + currentValue().swapPayload(init); + skipSpaces(); + if (*current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token token; + // Accept Comment after last item in the array. + ok = readToken(token); + while (token.type_ == tokenComment && ok) { + ok = readToken(token); + } + bool badTokenType = + (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover( + "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + } + if (token.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool OurReader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + return true; +} + +bool OurReader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + // TODO: Help the compiler do the div and mod at compile time or get rid of them. + Value::LargestUInt maxIntegerValue = + isNegative ? Value::LargestUInt(-Value::minLargestInt) + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + Value::UInt digit(c - '0'); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > maxIntegerValue % 10) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool OurReader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + return true; +} + +bool OurReader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + const int bufferSize = 32; + int count; + int length = int(token.end_ - token.start_); + + // Sanity check to avoid buffer overflow exploits. + if (length < 0) { + return addError("Unable to parse token length", token); + } + + // Avoid using a string constant for the format control string given to + // sscanf, as this can cause hard to debug crashes on OS X. See here for more + // info: + // + // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html + char format[] = "%lf"; + + if (length <= bufferSize) { + Char buffer[bufferSize + 1]; + memcpy(buffer, token.start_, length); + buffer[length] = 0; + count = sscanf(buffer, format, &value); + } else { + std::string buffer(token.start_, token.end_); + count = sscanf(buffer.c_str(), format, &value); + } + + if (count != 1) + return addError("'" + std::string(token.start_, token.end_) + + "' is not a number.", + token); + decoded = value; + return true; +} + +bool OurReader::decodeString(Token& token) { + std::string decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + return true; +} + +bool OurReader::decodeString(Token& token, std::string& decoded) { + decoded.reserve(token.end_ - token.start_ - 2); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + else if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool OurReader::decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, + current); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++) == 'u') { + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, + current); + } + return true; +} + +bool OurReader::decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", + token, + current); + unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, + current); + } + return true; +} + +bool +OurReader::addError(const std::string& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool OurReader::recoverFromError(TokenType skipUntilToken) { + int errorCount = int(errors_.size()); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool OurReader::addErrorAndRecover(const std::string& message, + Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& OurReader::currentValue() { return *(nodes_.top()); } + +OurReader::Char OurReader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void OurReader::getLocationLineAndColumn(Location location, + int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +std::string OurReader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; +#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) +#if defined(WINCE) + _snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#else + sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#endif +#else + snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#endif + return buffer; +} + +std::string OurReader::getFormattedErrorMessages() const { + std::string formattedMessage; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + + +class OurCharReader : public CharReader { + bool const collectComments_; + OurReader reader_; +public: + OurCharReader( + bool collectComments, + OurFeatures const& features) + : collectComments_(collectComments) + , reader_(features) + {} + virtual bool parse( + char const* beginDoc, char const* endDoc, + Value* root, std::string* errs) { + bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); + if (errs) { + *errs = reader_.getFormattedErrorMessages(); + } + return ok; + } +}; + +CharReaderBuilder::CharReaderBuilder() +{ + setDefaults(&settings_); +} +CharReaderBuilder::~CharReaderBuilder() +{} +CharReader* CharReaderBuilder::newCharReader() const +{ + bool collectComments = settings_["collectComments"].asBool(); + OurFeatures features = OurFeatures::all(); + features.allowComments_ = settings_["allowComments"].asBool(); + features.strictRoot_ = settings_["strictRoot"].asBool(); + features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool(); + features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); + features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool(); + features.stackLimit_ = settings_["stackLimit"].asInt(); + features.failIfExtra_ = settings_["failIfExtra"].asBool(); + features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); + return new OurCharReader(collectComments, features); +} +static void getValidReaderKeys(std::set* valid_keys) +{ + valid_keys->clear(); + valid_keys->insert("collectComments"); + valid_keys->insert("allowComments"); + valid_keys->insert("strictRoot"); + valid_keys->insert("allowDroppedNullPlaceholders"); + valid_keys->insert("allowNumericKeys"); + valid_keys->insert("allowSingleQuotes"); + valid_keys->insert("stackLimit"); + valid_keys->insert("failIfExtra"); + valid_keys->insert("rejectDupKeys"); +} +bool CharReaderBuilder::validate(Json::Value* invalid) const +{ + Json::Value my_invalid; + if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL + Json::Value& inv = *invalid; + std::set valid_keys; + getValidReaderKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + std::string const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return 0u == inv.size(); +} +Value& CharReaderBuilder::operator[](std::string key) +{ + return settings_[key]; +} +// static +void CharReaderBuilder::strictMode(Json::Value* settings) +{ +//! [CharReaderBuilderStrictMode] + (*settings)["allowComments"] = false; + (*settings)["strictRoot"] = true; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["failIfExtra"] = true; + (*settings)["rejectDupKeys"] = true; +//! [CharReaderBuilderStrictMode] +} +// static +void CharReaderBuilder::setDefaults(Json::Value* settings) +{ +//! [CharReaderBuilderDefaults] + (*settings)["collectComments"] = true; + (*settings)["allowComments"] = true; + (*settings)["strictRoot"] = false; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = false; + (*settings)["rejectDupKeys"] = false; +//! [CharReaderBuilderDefaults] +} + +////////////////////////////////// +// global functions + +bool parseFromStream( + CharReader::Factory const& fact, std::istream& sin, + Value* root, std::string* errs) +{ + std::ostringstream ssin; + ssin << sin.rdbuf(); + std::string doc = ssin.str(); + char const* begin = doc.data(); + char const* end = begin + doc.size(); + // Note that we do not actually need a null-terminator. + CharReaderPtr const reader(fact.newCharReader()); + return reader->parse(begin, end, root, errs); +} + +std::istream& operator>>(std::istream& sin, Value& root) { + CharReaderBuilder b; + std::string errs; + bool ok = parseFromStream(b, sin, &root, &errs); + if (!ok) { + fprintf(stderr, + "Error from reader: %s", + errs.c_str()); + + throwRuntimeError("reader error"); + } + return sin; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_reader.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_valueiterator.inl +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIteratorBase +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIteratorBase::ValueIteratorBase() + : current_(), isNull_(true) { +} + +ValueIteratorBase::ValueIteratorBase( + const Value::ObjectValues::iterator& current) + : current_(current), isNull_(false) {} + +Value& ValueIteratorBase::deref() const { + return current_->second; +} + +void ValueIteratorBase::increment() { + ++current_; +} + +void ValueIteratorBase::decrement() { + --current_; +} + +ValueIteratorBase::difference_type +ValueIteratorBase::computeDistance(const SelfType& other) const { +#ifdef JSON_USE_CPPTL_SMALLMAP + return other.current_ - current_; +#else + // Iterator for null value are initialized using the default + // constructor, which initialize current_ to the default + // std::map::iterator. As begin() and end() are two instance + // of the default std::map::iterator, they can not be compared. + // To allow this, we handle this comparison specifically. + if (isNull_ && other.isNull_) { + return 0; + } + + // Usage of std::distance is not portable (does not compile with Sun Studio 12 + // RogueWave STL, + // which is the one used by default). + // Using a portable hand-made version for non random iterator instead: + // return difference_type( std::distance( current_, other.current_ ) ); + difference_type myDistance = 0; + for (Value::ObjectValues::iterator it = current_; it != other.current_; + ++it) { + ++myDistance; + } + return myDistance; +#endif +} + +bool ValueIteratorBase::isEqual(const SelfType& other) const { + if (isNull_) { + return other.isNull_; + } + return current_ == other.current_; +} + +void ValueIteratorBase::copy(const SelfType& other) { + current_ = other.current_; + isNull_ = other.isNull_; +} + +Value ValueIteratorBase::key() const { + const Value::CZString czstring = (*current_).first; + if (czstring.data()) { + if (czstring.isStaticString()) + return Value(StaticString(czstring.data())); + return Value(czstring.data(), czstring.data() + czstring.length()); + } + return Value(czstring.index()); +} + +UInt ValueIteratorBase::index() const { + const Value::CZString czstring = (*current_).first; + if (!czstring.data()) + return czstring.index(); + return Value::UInt(-1); +} + +std::string ValueIteratorBase::name() const { + char const* keey; + char const* end; + keey = memberName(&end); + if (!keey) return std::string(); + return std::string(keey, end); +} + +char const* ValueIteratorBase::memberName() const { + const char* cname = (*current_).first.data(); + return cname ? cname : ""; +} + +char const* ValueIteratorBase::memberName(char const** end) const { + const char* cname = (*current_).first.data(); + if (!cname) { + *end = NULL; + return NULL; + } + *end = cname + (*current_).first.length(); + return cname; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueConstIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueConstIterator::ValueConstIterator() {} + +ValueConstIterator::ValueConstIterator( + const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} + +ValueConstIterator& ValueConstIterator:: +operator=(const ValueIteratorBase& other) { + copy(other); + return *this; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIterator::ValueIterator() {} + +ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} + +ValueIterator::ValueIterator(const ValueConstIterator& other) + : ValueIteratorBase(other) {} + +ValueIterator::ValueIterator(const ValueIterator& other) + : ValueIteratorBase(other) {} + +ValueIterator& ValueIterator::operator=(const SelfType& other) { + copy(other); + return *this; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_valueiterator.inl +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_value.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#ifdef JSON_USE_CPPTL +#include +#endif +#include // size_t +#include // min() + +#define JSON_ASSERT_UNREACHABLE assert(false) + +namespace Json { + +// This is a walkaround to avoid the static initialization of Value::null. +// kNull must be word-aligned to avoid crashing on ARM. We use an alignment of +// 8 (instead of 4) as a bit of future-proofing. +#if defined(__ARMEL__) +#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) +#else +// This exists for binary compatibility only. Use nullRef. +const Value Value::null; +#define ALIGNAS(byte_alignment) +#endif +static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 }; +const unsigned char& kNullRef = kNull[0]; +const Value& Value::nullRef = reinterpret_cast(kNullRef); + +const Int Value::minInt = Int(~(UInt(-1) / 2)); +const Int Value::maxInt = Int(UInt(-1) / 2); +const UInt Value::maxUInt = UInt(-1); +#if defined(JSON_HAS_INT64) +const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2)); +const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2); +const UInt64 Value::maxUInt64 = UInt64(-1); +// The constant is hard-coded because some compiler have trouble +// converting Value::maxUInt64 to a double correctly (AIX/xlC). +// Assumes that UInt64 is a 64 bits integer. +static const double maxUInt64AsDouble = 18446744073709551615.0; +#endif // defined(JSON_HAS_INT64) +const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2)); +const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2); +const LargestUInt Value::maxLargestUInt = LargestUInt(-1); + +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +template +static inline bool InRange(double d, T min, U max) { + return d >= min && d <= max; +} +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +static inline double integerToDouble(Json::UInt64 value) { + return static_cast(Int64(value / 2)) * 2.0 + Int64(value & 1); +} + +template static inline double integerToDouble(T value) { + return static_cast(value); +} + +template +static inline bool InRange(double d, T min, U max) { + return d >= integerToDouble(min) && d <= integerToDouble(max); +} +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + +/** Duplicates the specified string value. + * @param value Pointer to the string to duplicate. Must be zero-terminated if + * length is "unknown". + * @param length Length of the value. if equals to unknown, then it will be + * computed using strlen(value). + * @return Pointer on the duplicate instance of string. + */ +static inline char* duplicateStringValue(const char* value, + size_t length) { + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + if (length >= (size_t)Value::maxInt) + length = Value::maxInt - 1; + + char* newString = static_cast(malloc(length + 1)); + if (newString == NULL) { + throwRuntimeError( + "in Json::Value::duplicateStringValue(): " + "Failed to allocate string value buffer"); + } + memcpy(newString, value, length); + newString[length] = 0; + return newString; +} + +/* Record the length as a prefix. + */ +static inline char* duplicateAndPrefixStringValue( + const char* value, + unsigned int length) +{ + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + JSON_ASSERT_MESSAGE(length <= (unsigned)Value::maxInt - sizeof(unsigned) - 1U, + "in Json::Value::duplicateAndPrefixStringValue(): " + "length too big for prefixing"); + unsigned actualLength = length + static_cast(sizeof(unsigned)) + 1U; + char* newString = static_cast(malloc(actualLength)); + if (newString == 0) { + throwRuntimeError( + "in Json::Value::duplicateAndPrefixStringValue(): " + "Failed to allocate string value buffer"); + } + *reinterpret_cast(newString) = length; + memcpy(newString + sizeof(unsigned), value, length); + newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later + return newString; +} +inline static void decodePrefixedString( + bool isPrefixed, char const* prefixed, + unsigned* length, char const** value) +{ + if (!isPrefixed) { + *length = static_cast(strlen(prefixed)); + *value = prefixed; + } else { + *length = *reinterpret_cast(prefixed); + *value = prefixed + sizeof(unsigned); + } +} +/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue(). + */ +static inline void releaseStringValue(char* value) { free(value); } + +} // namespace Json + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ValueInternals... +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#if !defined(JSON_IS_AMALGAMATION) + +#include "json_valueiterator.inl" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +Exception::Exception(std::string const& msg) + : msg_(msg) +{} +Exception::~Exception() throw() +{} +char const* Exception::what() const throw() +{ + return msg_.c_str(); +} +RuntimeError::RuntimeError(std::string const& msg) + : Exception(msg) +{} +LogicError::LogicError(std::string const& msg) + : Exception(msg) +{} +void throwRuntimeError(std::string const& msg) +{ + assert(false); +// throw RuntimeError(msg); +} +void throwLogicError(std::string const& msg) +{ + assert(false); +// throw LogicError(msg); +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CommentInfo +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +Value::CommentInfo::CommentInfo() : comment_(0) {} + +Value::CommentInfo::~CommentInfo() { + if (comment_) + releaseStringValue(comment_); +} + +void Value::CommentInfo::setComment(const char* text, size_t len) { + if (comment_) { + releaseStringValue(comment_); + comment_ = 0; + } + JSON_ASSERT(text != 0); + JSON_ASSERT_MESSAGE( + text[0] == '\0' || text[0] == '/', + "in Json::Value::setComment(): Comments must start with /"); + // It seems that /**/ style comments are acceptable as well. + comment_ = duplicateStringValue(text, len); +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CZString +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +// Notes: policy_ indicates if the string was allocated when +// a string is stored. + +Value::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {} + +Value::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate) + : cstr_(str) +{ + // allocate != duplicate + storage_.policy_ = allocate & 0x3; + storage_.length_ = ulength & 0x3FFFFFFF; +} + +Value::CZString::CZString(const CZString& other) + : cstr_(other.storage_.policy_ != noDuplication && other.cstr_ != 0 + ? duplicateStringValue(other.cstr_, other.storage_.length_) + : other.cstr_) +{ + storage_.policy_ = (other.cstr_ + ? (static_cast(other.storage_.policy_) == noDuplication + ? noDuplication : duplicate) + : static_cast(other.storage_.policy_)); + storage_.length_ = other.storage_.length_; +} + +Value::CZString::~CZString() { + if (cstr_ && storage_.policy_ == duplicate) + releaseStringValue(const_cast(cstr_)); +} + +void Value::CZString::swap(CZString& other) { + std::swap(cstr_, other.cstr_); + std::swap(index_, other.index_); +} + +Value::CZString& Value::CZString::operator=(CZString other) { + swap(other); + return *this; +} + +bool Value::CZString::operator<(const CZString& other) const { + if (!cstr_) return index_ < other.index_; + //return strcmp(cstr_, other.cstr_) < 0; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + unsigned min_len = std::min(this_len, other_len); + int comp = memcmp(this->cstr_, other.cstr_, min_len); + if (comp < 0) return true; + if (comp > 0) return false; + return (this_len < other_len); +} + +bool Value::CZString::operator==(const CZString& other) const { + if (!cstr_) return index_ == other.index_; + //return strcmp(cstr_, other.cstr_) == 0; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + if (this_len != other_len) return false; + int comp = memcmp(this->cstr_, other.cstr_, this_len); + return comp == 0; +} + +ArrayIndex Value::CZString::index() const { return index_; } + +//const char* Value::CZString::c_str() const { return cstr_; } +const char* Value::CZString::data() const { return cstr_; } +unsigned Value::CZString::length() const { return storage_.length_; } +bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; } + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::Value +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/*! \internal Default constructor initialization must be equivalent to: + * memset( this, 0, sizeof(Value) ) + * This optimization is used in ValueInternalMap fast allocator. + */ +Value::Value(ValueType vtype) { + initBasic(vtype); + switch (vtype) { + case nullValue: + break; + case intValue: + case uintValue: + value_.int_ = 0; + break; + case realValue: + value_.real_ = 0.0; + break; + case stringValue: + value_.string_ = 0; + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; + case booleanValue: + value_.bool_ = false; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +Value::Value(Int value) { + initBasic(intValue); + value_.int_ = value; +} + +Value::Value(UInt value) { + initBasic(uintValue); + value_.uint_ = value; +} +#if defined(JSON_HAS_INT64) +Value::Value(Int64 value) { + initBasic(intValue); + value_.int_ = value; +} +Value::Value(UInt64 value) { + initBasic(uintValue); + value_.uint_ = value; +} +#endif // defined(JSON_HAS_INT64) + +Value::Value(double value) { + initBasic(realValue); + value_.real_ = value; +} + +Value::Value(const char* value) { + initBasic(stringValue, true); + value_.string_ = duplicateAndPrefixStringValue(value, static_cast(strlen(value))); +} + +Value::Value(const char* beginValue, const char* endValue) { + initBasic(stringValue, true); + value_.string_ = + duplicateAndPrefixStringValue(beginValue, static_cast(endValue - beginValue)); +} + +Value::Value(const std::string& value) { + initBasic(stringValue, true); + value_.string_ = + duplicateAndPrefixStringValue(value.data(), static_cast(value.length())); +} + +Value::Value(const StaticString& value) { + initBasic(stringValue); + value_.string_ = const_cast(value.c_str()); +} + +#ifdef JSON_USE_CPPTL +Value::Value(const CppTL::ConstString& value) { + initBasic(stringValue, true); + value_.string_ = duplicateAndPrefixStringValue(value, static_cast(value.length())); +} +#endif + +Value::Value(bool value) { + initBasic(booleanValue); + value_.bool_ = value; +} + +Value::Value(Value const& other) + : type_(other.type_), allocated_(false) + , + comments_(0) +{ + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if (other.value_.string_ && other.allocated_) { + unsigned len; + char const* str; + decodePrefixedString(other.allocated_, other.value_.string_, + &len, &str); + value_.string_ = duplicateAndPrefixStringValue(str, len); + allocated_ = true; + } else { + value_.string_ = other.value_.string_; + allocated_ = false; + } + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(*other.value_.map_); + break; + default: + JSON_ASSERT_UNREACHABLE; + } + if (other.comments_) { + comments_ = new CommentInfo[numberOfCommentPlacement]; + for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { + const CommentInfo& otherComment = other.comments_[comment]; + if (otherComment.comment_) + comments_[comment].setComment( + otherComment.comment_, strlen(otherComment.comment_)); + } + } +} + +Value::~Value() { + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if (allocated_) + releaseStringValue(value_.string_); + break; + case arrayValue: + case objectValue: + delete value_.map_; + break; + default: + JSON_ASSERT_UNREACHABLE; + } + + if (comments_) + delete[] comments_; +} + +Value &Value::operator=(const Value &other) { + Value temp(other); + swap(temp); + return *this; +} + +void Value::swapPayload(Value& other) { + ValueType temp = type_; + type_ = other.type_; + other.type_ = temp; + std::swap(value_, other.value_); + int temp2 = allocated_; + allocated_ = other.allocated_; + other.allocated_ = temp2 & 0x1; +} + +void Value::swap(Value& other) { + swapPayload(other); + std::swap(comments_, other.comments_); +} + +ValueType Value::type() const { return type_; } + +int Value::compare(const Value& other) const { + if (*this < other) + return -1; + if (*this > other) + return 1; + return 0; +} + +bool Value::operator<(const Value& other) const { + int typeDelta = type_ - other.type_; + if (typeDelta) + return typeDelta < 0 ? true : false; + switch (type_) { + case nullValue: + return false; + case intValue: + return value_.int_ < other.value_.int_; + case uintValue: + return value_.uint_ < other.value_.uint_; + case realValue: + return value_.real_ < other.value_.real_; + case booleanValue: + return value_.bool_ < other.value_.bool_; + case stringValue: + { + if ((value_.string_ == 0) || (other.value_.string_ == 0)) { + if (other.value_.string_) return true; + else return false; + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); + unsigned min_len = std::min(this_len, other_len); + int comp = memcmp(this_str, other_str, min_len); + if (comp < 0) return true; + if (comp > 0) return false; + return (this_len < other_len); + } + case arrayValue: + case objectValue: { + int delta = int(value_.map_->size() - other.value_.map_->size()); + if (delta) + return delta < 0; + return (*value_.map_) < (*other.value_.map_); + } + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator<=(const Value& other) const { return !(other < *this); } + +bool Value::operator>=(const Value& other) const { return !(*this < other); } + +bool Value::operator>(const Value& other) const { return other < *this; } + +bool Value::operator==(const Value& other) const { + // if ( type_ != other.type_ ) + // GCC 2.95.3 says: + // attempt to take address of bit-field structure member `Json::Value::type_' + // Beats me, but a temp solves the problem. + int temp = other.type_; + if (type_ != temp) + return false; + switch (type_) { + case nullValue: + return true; + case intValue: + return value_.int_ == other.value_.int_; + case uintValue: + return value_.uint_ == other.value_.uint_; + case realValue: + return value_.real_ == other.value_.real_; + case booleanValue: + return value_.bool_ == other.value_.bool_; + case stringValue: + { + if ((value_.string_ == 0) || (other.value_.string_ == 0)) { + return (value_.string_ == other.value_.string_); + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); + if (this_len != other_len) return false; + int comp = memcmp(this_str, other_str, this_len); + return comp == 0; + } + case arrayValue: + case objectValue: + return value_.map_->size() == other.value_.map_->size() && + (*value_.map_) == (*other.value_.map_); + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator!=(const Value& other) const { return !(*this == other); } + +const char* Value::asCString() const { + JSON_ASSERT_MESSAGE(type_ == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == 0) return 0; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + return this_str; +} + +bool Value::getString(char const** str, char const** cend) const { + if (type_ != stringValue) return false; + if (value_.string_ == 0) return false; + unsigned length; + decodePrefixedString(this->allocated_, this->value_.string_, &length, str); + *cend = *str + length; + return true; +} + +std::string Value::asString() const { + switch (type_) { + case nullValue: + return ""; + case stringValue: + { + if (value_.string_ == 0) return ""; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + return std::string(this_str, this_len); + } + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + return valueToString(value_.int_); + case uintValue: + return valueToString(value_.uint_); + case realValue: + return valueToString(value_.real_); + default: + JSON_FAIL_MESSAGE("Type is not convertible to string"); + } +} + +#ifdef JSON_USE_CPPTL +CppTL::ConstString Value::asConstString() const { + unsigned len; + char const* str; + decodePrefixedString(allocated_, value_.string_, + &len, &str); + return CppTL::ConstString(str, len); +} +#endif + +Value::Int Value::asInt() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); + return Int(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range"); + return Int(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), + "double out of Int range"); + return Int(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int."); +} + +Value::UInt Value::asUInt() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); + return UInt(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range"); + return UInt(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), + "double out of UInt range"); + return UInt(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt."); +} + +#if defined(JSON_HAS_INT64) + +Value::Int64 Value::asInt64() const { + switch (type_) { + case intValue: + return Int64(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); + return Int64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), + "double out of Int64 range"); + return Int64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int64."); +} + +Value::UInt64 Value::asUInt64() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); + return UInt64(value_.int_); + case uintValue: + return UInt64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), + "double out of UInt64 range"); + return UInt64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt64."); +} +#endif // if defined(JSON_HAS_INT64) + +LargestInt Value::asLargestInt() const { +#if defined(JSON_NO_INT64) + return asInt(); +#else + return asInt64(); +#endif +} + +LargestUInt Value::asLargestUInt() const { +#if defined(JSON_NO_INT64) + return asUInt(); +#else + return asUInt64(); +#endif +} + +double Value::asDouble() const { + switch (type_) { + case intValue: + return static_cast(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return integerToDouble(value_.uint_); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return value_.real_; + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0 : 0.0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to double."); +} + +float Value::asFloat() const { + switch (type_) { + case intValue: + return static_cast(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return integerToDouble(value_.uint_); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return static_cast(value_.real_); + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0f : 0.0f; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to float."); +} + +bool Value::asBool() const { + switch (type_) { + case booleanValue: + return value_.bool_; + case nullValue: + return false; + case intValue: + return value_.int_ ? true : false; + case uintValue: + return value_.uint_ ? true : false; + case realValue: + // This is kind of strange. Not recommended. + return (value_.real_ != 0.0) ? true : false; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to bool."); +} + +bool Value::isConvertibleTo(ValueType other) const { + switch (other) { + case nullValue: + return (isNumeric() && asDouble() == 0.0) || + (type_ == booleanValue && value_.bool_ == false) || + (type_ == stringValue && asString() == "") || + (type_ == arrayValue && value_.map_->size() == 0) || + (type_ == objectValue && value_.map_->size() == 0) || + type_ == nullValue; + case intValue: + return isInt() || + (type_ == realValue && InRange(value_.real_, minInt, maxInt)) || + type_ == booleanValue || type_ == nullValue; + case uintValue: + return isUInt() || + (type_ == realValue && InRange(value_.real_, 0, maxUInt)) || + type_ == booleanValue || type_ == nullValue; + case realValue: + return isNumeric() || type_ == booleanValue || type_ == nullValue; + case booleanValue: + return isNumeric() || type_ == booleanValue || type_ == nullValue; + case stringValue: + return isNumeric() || type_ == booleanValue || type_ == stringValue || + type_ == nullValue; + case arrayValue: + return type_ == arrayValue || type_ == nullValue; + case objectValue: + return type_ == objectValue || type_ == nullValue; + } + JSON_ASSERT_UNREACHABLE; + return false; +} + +/// Number of values in array or object +ArrayIndex Value::size() const { + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + case stringValue: + return 0; + case arrayValue: // size of the array is highest index + 1 + if (!value_.map_->empty()) { + ObjectValues::const_iterator itLast = value_.map_->end(); + --itLast; + return (*itLast).first.index() + 1; + } + return 0; + case objectValue: + return ArrayIndex(value_.map_->size()); + } + JSON_ASSERT_UNREACHABLE; + return 0; // unreachable; +} + +bool Value::empty() const { + if (isNull() || isArray() || isObject()) + return size() == 0u; + else + return false; +} + +bool Value::operator!() const { return isNull(); } + +void Value::clear() { + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue || + type_ == objectValue, + "in Json::Value::clear(): requires complex value"); + switch (type_) { + case arrayValue: + case objectValue: + value_.map_->clear(); + break; + default: + break; + } +} + +void Value::resize(ArrayIndex newSize) { + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, + "in Json::Value::resize(): requires arrayValue"); + if (type_ == nullValue) + *this = Value(arrayValue); + ArrayIndex oldSize = size(); + if (newSize == 0) + clear(); + else if (newSize > oldSize) + (*this)[newSize - 1]; + else { + for (ArrayIndex index = newSize; index < oldSize; ++index) { + value_.map_->erase(index); + } + assert(size() == newSize); + } +} + +Value& Value::operator[](ArrayIndex index) { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == arrayValue, + "in Json::Value::operator[](ArrayIndex): requires arrayValue"); + if (type_ == nullValue) + *this = Value(arrayValue); + CZString key(index); + ObjectValues::iterator it = value_.map_->lower_bound(key); + if (it != value_.map_->end() && (*it).first == key) + return (*it).second; + + ObjectValues::value_type defaultValue(key, nullRef); + it = value_.map_->insert(it, defaultValue); + return (*it).second; +} + +Value& Value::operator[](int index) { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index): index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +const Value& Value::operator[](ArrayIndex index) const { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == arrayValue, + "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); + if (type_ == nullValue) + return nullRef; + CZString key(index); + ObjectValues::const_iterator it = value_.map_->find(key); + if (it == value_.map_->end()) + return nullRef; + return (*it).second; +} + +const Value& Value::operator[](int index) const { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index) const: index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +void Value::initBasic(ValueType vtype, bool allocated) { + type_ = vtype; + allocated_ = allocated; + comments_ = 0; +} + +// Access an object value by name, create a null member if it does not exist. +// @pre Type of '*this' is object or null. +// @param key is null-terminated. +Value& Value::resolveReference(const char* key) { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::resolveReference(): requires objectValue"); + if (type_ == nullValue) + *this = Value(objectValue); + CZString actualKey( + key, static_cast(strlen(key)), CZString::noDuplication); // NOTE! + ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullRef); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +// @param key is not null-terminated. +Value& Value::resolveReference(char const* key, char const* cend) +{ + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::resolveReference(key, end): requires objectValue"); + if (type_ == nullValue) + *this = Value(objectValue); + CZString actualKey( + key, static_cast(cend-key), CZString::duplicateOnCopy); + ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullRef); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +Value Value::get(ArrayIndex index, const Value& defaultValue) const { + const Value* value = &((*this)[index]); + return value == &nullRef ? defaultValue : *value; +} + +bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } + +Value const* Value::find(char const* key, char const* cend) const +{ + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::find(key, end, found): requires objectValue or nullValue"); + if (type_ == nullValue) return NULL; + CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); + ObjectValues::const_iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) return NULL; + return &(*it).second; +} +const Value& Value::operator[](const char* key) const +{ + Value const* found = find(key, key + strlen(key)); + if (!found) return nullRef; + return *found; +} +Value const& Value::operator[](std::string const& key) const +{ + Value const* found = find(key.data(), key.data() + key.length()); + if (!found) return nullRef; + return *found; +} + +Value& Value::operator[](const char* key) { + return resolveReference(key, key + strlen(key)); +} + +Value& Value::operator[](const std::string& key) { + return resolveReference(key.data(), key.data() + key.length()); +} + +Value& Value::operator[](const StaticString& key) { + return resolveReference(key.c_str()); +} + +#ifdef JSON_USE_CPPTL +Value& Value::operator[](const CppTL::ConstString& key) { + return resolveReference(key.c_str(), key.end_c_str()); +} +Value const& Value::operator[](CppTL::ConstString const& key) const +{ + Value const* found = find(key.c_str(), key.end_c_str()); + if (!found) return nullRef; + return *found; +} +#endif + +Value& Value::append(const Value& value) { return (*this)[size()] = value; } + +Value Value::get(char const* key, char const* cend, Value const& defaultValue) const +{ + Value const* found = find(key, cend); + return !found ? defaultValue : *found; +} +Value Value::get(char const* key, Value const& defaultValue) const +{ + return get(key, key + strlen(key), defaultValue); +} +Value Value::get(std::string const& key, Value const& defaultValue) const +{ + return get(key.data(), key.data() + key.length(), defaultValue); +} + + +bool Value::removeMember(const char* key, const char* cend, Value* removed) +{ + if (type_ != objectValue) { + return false; + } + CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); + ObjectValues::iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return false; + *removed = it->second; + value_.map_->erase(it); + return true; +} +bool Value::removeMember(const char* key, Value* removed) +{ + return removeMember(key, key + strlen(key), removed); +} +bool Value::removeMember(std::string const& key, Value* removed) +{ + return removeMember(key.data(), key.data() + key.length(), removed); +} +Value Value::removeMember(const char* key) +{ + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, + "in Json::Value::removeMember(): requires objectValue"); + if (type_ == nullValue) + return nullRef; + + Value removed; // null + removeMember(key, key + strlen(key), &removed); + return removed; // still null if removeMember() did nothing +} +Value Value::removeMember(const std::string& key) +{ + return removeMember(key.c_str()); +} + +bool Value::removeIndex(ArrayIndex index, Value* removed) { + if (type_ != arrayValue) { + return false; + } + CZString key(index); + ObjectValues::iterator it = value_.map_->find(key); + if (it == value_.map_->end()) { + return false; + } + *removed = it->second; + ArrayIndex oldSize = size(); + // shift left all items left, into the place of the "removed" + for (ArrayIndex i = index; i < (oldSize - 1); ++i){ + CZString keey(i); + (*value_.map_)[keey] = (*this)[i + 1]; + } + // erase the last one ("leftover") + CZString keyLast(oldSize - 1); + ObjectValues::iterator itLast = value_.map_->find(keyLast); + value_.map_->erase(itLast); + return true; +} + +#ifdef JSON_USE_CPPTL +Value Value::get(const CppTL::ConstString& key, + const Value& defaultValue) const { + return get(key.c_str(), key.end_c_str(), defaultValue); +} +#endif + +bool Value::isMember(char const* key, char const* cend) const +{ + Value const* value = find(key, cend); + return NULL != value; +} +bool Value::isMember(char const* key) const +{ + return isMember(key, key + strlen(key)); +} +bool Value::isMember(std::string const& key) const +{ + return isMember(key.data(), key.data() + key.length()); +} + +#ifdef JSON_USE_CPPTL +bool Value::isMember(const CppTL::ConstString& key) const { + return isMember(key.c_str(), key.end_c_str()); +} +#endif + +Value::Members Value::getMemberNames() const { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::getMemberNames(), value must be objectValue"); + if (type_ == nullValue) + return Value::Members(); + Members members; + members.reserve(value_.map_->size()); + ObjectValues::const_iterator it = value_.map_->begin(); + ObjectValues::const_iterator itEnd = value_.map_->end(); + for (; it != itEnd; ++it) { + members.push_back(std::string((*it).first.data(), + (*it).first.length())); + } + return members; +} +// +//# ifdef JSON_USE_CPPTL +// EnumMemberNames +// Value::enumMemberNames() const +//{ +// if ( type_ == objectValue ) +// { +// return CppTL::Enum::any( CppTL::Enum::transform( +// CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), +// MemberNamesTransform() ) ); +// } +// return EnumMemberNames(); +//} +// +// +// EnumValues +// Value::enumValues() const +//{ +// if ( type_ == objectValue || type_ == arrayValue ) +// return CppTL::Enum::anyValues( *(value_.map_), +// CppTL::Type() ); +// return EnumValues(); +//} +// +//# endif + +static bool IsIntegral(double d) { + double integral_part; + return modf(d, &integral_part) == 0.0; +} + +bool Value::isNull() const { return type_ == nullValue; } + +bool Value::isBool() const { return type_ == booleanValue; } + +bool Value::isInt() const { + switch (type_) { + case intValue: + return value_.int_ >= minInt && value_.int_ <= maxInt; + case uintValue: + return value_.uint_ <= UInt(maxInt); + case realValue: + return value_.real_ >= minInt && value_.real_ <= maxInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isUInt() const { + switch (type_) { + case intValue: + return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); + case uintValue: + return value_.uint_ <= maxUInt; + case realValue: + return value_.real_ >= 0 && value_.real_ <= maxUInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isInt64() const { +#if defined(JSON_HAS_INT64) + switch (type_) { + case intValue: + return true; + case uintValue: + return value_.uint_ <= UInt64(maxInt64); + case realValue: + // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a + // double, so double(maxInt64) will be rounded up to 2^63. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && + value_.real_ < double(maxInt64) && IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isUInt64() const { +#if defined(JSON_HAS_INT64) + switch (type_) { + case intValue: + return value_.int_ >= 0; + case uintValue: + return true; + case realValue: + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble && + IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isIntegral() const { +#if defined(JSON_HAS_INT64) + return isInt64() || isUInt64(); +#else + return isInt() || isUInt(); +#endif +} + +bool Value::isDouble() const { return type_ == realValue || isIntegral(); } + +bool Value::isNumeric() const { return isIntegral() || isDouble(); } + +bool Value::isString() const { return type_ == stringValue; } + +bool Value::isArray() const { return type_ == arrayValue; } + +bool Value::isObject() const { return type_ == objectValue; } + +void Value::setComment(const char* comment, size_t len, CommentPlacement placement) { + if (!comments_) + comments_ = new CommentInfo[numberOfCommentPlacement]; + if ((len > 0) && (comment[len-1] == '\n')) { + // Always discard trailing newline, to aid indentation. + len -= 1; + } + comments_[placement].setComment(comment, len); +} + +void Value::setComment(const char* comment, CommentPlacement placement) { + setComment(comment, strlen(comment), placement); +} + +void Value::setComment(const std::string& comment, CommentPlacement placement) { + setComment(comment.c_str(), comment.length(), placement); +} + +bool Value::hasComment(CommentPlacement placement) const { + return comments_ != 0 && comments_[placement].comment_ != 0; +} + +std::string Value::getComment(CommentPlacement placement) const { + if (hasComment(placement)) + return comments_[placement].comment_; + return ""; +} + +std::string Value::toStyledString() const { + StyledWriter writer; + return writer.write(*this); +} + +Value::const_iterator Value::begin() const { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->begin()); + break; + default: + break; + } + return const_iterator(); +} + +Value::const_iterator Value::end() const { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->end()); + break; + default: + break; + } + return const_iterator(); +} + +Value::iterator Value::begin() { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->begin()); + break; + default: + break; + } + return iterator(); +} + +Value::iterator Value::end() { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->end()); + break; + default: + break; + } + return iterator(); +} + +// class PathArgument +// ////////////////////////////////////////////////////////////////// + +PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {} + +PathArgument::PathArgument(ArrayIndex index) + : key_(), index_(index), kind_(kindIndex) {} + +PathArgument::PathArgument(const char* key) + : key_(key), index_(), kind_(kindKey) {} + +PathArgument::PathArgument(const std::string& key) + : key_(key.c_str()), index_(), kind_(kindKey) {} + +// class Path +// ////////////////////////////////////////////////////////////////// + +Path::Path(const std::string& path, + const PathArgument& a1, + const PathArgument& a2, + const PathArgument& a3, + const PathArgument& a4, + const PathArgument& a5) { + InArgs in; + in.push_back(&a1); + in.push_back(&a2); + in.push_back(&a3); + in.push_back(&a4); + in.push_back(&a5); + makePath(path, in); +} + +void Path::makePath(const std::string& path, const InArgs& in) { + const char* current = path.c_str(); + const char* end = current + path.length(); + InArgs::const_iterator itInArg = in.begin(); + while (current != end) { + if (*current == '[') { + ++current; + if (*current == '%') + addPathInArg(path, in, itInArg, PathArgument::kindIndex); + else { + ArrayIndex index = 0; + for (; current != end && *current >= '0' && *current <= '9'; ++current) + index = index * 10 + ArrayIndex(*current - '0'); + args_.push_back(index); + } + if (current == end || *current++ != ']') + invalidPath(path, int(current - path.c_str())); + } else if (*current == '%') { + addPathInArg(path, in, itInArg, PathArgument::kindKey); + ++current; + } else if (*current == '.') { + ++current; + } else { + const char* beginName = current; + while (current != end && !strchr("[.", *current)) + ++current; + args_.push_back(std::string(beginName, current)); + } + } +} + +void Path::addPathInArg(const std::string& /*path*/, + const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind) { + if (itInArg == in.end()) { + // Error: missing argument %d + } else if ((*itInArg)->kind_ != kind) { + // Error: bad argument type + } else { + args_.push_back(**itInArg); + } +} + +void Path::invalidPath(const std::string& /*path*/, int /*location*/) { + // Error: invalid path. +} + +const Value& Path::resolve(const Value& root) const { + const Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) { + // Error: unable to resolve path (array value expected at position... + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: unable to resolve path (object value expected at position...) + } + node = &((*node)[arg.key_]); + if (node == &Value::nullRef) { + // Error: unable to resolve path (object has no member named '' at + // position...) + } + } + } + return *node; +} + +Value Path::resolve(const Value& root, const Value& defaultValue) const { + const Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) + return defaultValue; + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) + return defaultValue; + node = &((*node)[arg.key_]); + if (node == &Value::nullRef) + return defaultValue; + } + } + return *node; +} + +Value& Path::make(Value& root) const { + Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray()) { + // Error: node is not an array at position ... + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: node is not an object at position... + } + node = &((*node)[arg.key_]); + } + } + return *node; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_value.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_writer.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0 +#include +#define isfinite _finite +#elif defined(__sun) && defined(__SVR4) //Solaris +#include +#define isfinite finite +#else +#include +#define isfinite std::isfinite +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below +#define snprintf _snprintf +#elif defined(__ANDROID__) +#define snprintf snprintf +#elif __cplusplus >= 201103L +#define snprintf std::snprintf +#endif + +#if defined(__BORLANDC__) +#include +#define isfinite _finite +#define snprintf _snprintf +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +namespace Json { + +typedef std::auto_ptr StreamWriterPtr; + +static bool containsControlCharacter(const char* str) { + while (*str) { + if (isControlCharacter(*(str++))) + return true; + } + return false; +} + +static bool containsControlCharacter0(const char* str, unsigned len) { + char const* end = str + len; + while (end != str) { + if (isControlCharacter(*str) || 0==*str) + return true; + ++str; + } + return false; +} + +std::string valueToString(LargestInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + bool isNegative = value < 0; + if (isNegative) + value = -value; + uintToString(LargestUInt(value), current); + if (isNegative) + *--current = '-'; + assert(current >= buffer); + return current; +} + +std::string valueToString(LargestUInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + uintToString(value, current); + assert(current >= buffer); + return current; +} + +#if defined(JSON_HAS_INT64) + +std::string valueToString(Int value) { + return valueToString(LargestInt(value)); +} + +std::string valueToString(UInt value) { + return valueToString(LargestUInt(value)); +} + +#endif // # if defined(JSON_HAS_INT64) + +std::string valueToString(double value) { + // Allocate a buffer that is more than large enough to store the 16 digits of + // precision requested below. + char buffer[32]; + int len = -1; + +// Print into the buffer. We need not request the alternative representation +// that always has a decimal point because JSON doesn't distingish the +// concepts of reals and integers. +#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with + // visual studio 2005 to + // avoid warning. +#if defined(WINCE) + len = _snprintf(buffer, sizeof(buffer), "%.17g", value); +#else + len = sprintf_s(buffer, sizeof(buffer), "%.17g", value); +#endif +#else + if (isfinite(value)) { + len = snprintf(buffer, sizeof(buffer), "%.17g", value); + } else { + // IEEE standard states that NaN values will not compare to themselves + if (value != value) { + len = snprintf(buffer, sizeof(buffer), "null"); + } else if (value < 0) { + len = snprintf(buffer, sizeof(buffer), "-1e+9999"); + } else { + len = snprintf(buffer, sizeof(buffer), "1e+9999"); + } + // For those, we do not need to call fixNumLoc, but it is fast. + } +#endif + assert(len >= 0); + fixNumericLocale(buffer, buffer + len); + return buffer; +} + +std::string valueToString(bool value) { return value ? "true" : "false"; } + +std::string valueToQuotedString(const char* value) { + if (value == NULL) + return ""; + // Not sure how to handle unicode... + if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && + !containsControlCharacter(value)) + return std::string("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to std::string is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + std::string::size_type maxsize = + strlen(value) * 2 + 3; // allescaped+quotes+NULL + std::string result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + for (const char* c = value; *c != 0; ++c) { + switch (*c) { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + // case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something. + // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); + result += oss.str(); + } else { + result += *c; + } + break; + } + } + result += "\""; + return result; +} + +// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp +static char const* strnpbrk(char const* s, char const* accept, size_t n) { + assert((s || !n) && accept); + + char const* const end = s + n; + for (char const* cur = s; cur < end; ++cur) { + int const c = *cur; + for (char const* a = accept; *a; ++a) { + if (*a == c) { + return cur; + } + } + } + return NULL; +} +static std::string valueToQuotedStringN(const char* value, unsigned length) { + if (value == NULL) + return ""; + // Not sure how to handle unicode... + if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL && + !containsControlCharacter0(value, length)) + return std::string("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to std::string is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + std::string::size_type maxsize = + length * 2 + 3; // allescaped+quotes+NULL + std::string result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + char const* end = value + length; + for (const char* c = value; c != end; ++c) { + switch (*c) { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + // case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something.) + // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); + result += oss.str(); + } else { + result += *c; + } + break; + } + } + result += "\""; + return result; +} + +// Class Writer +// ////////////////////////////////////////////////////////////////// +Writer::~Writer() {} + +// Class FastWriter +// ////////////////////////////////////////////////////////////////// + +FastWriter::FastWriter() + : yamlCompatiblityEnabled_(false) {} + +void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; } + +std::string FastWriter::write(const Value& root) { + document_ = ""; + writeValue(root); + document_ += "\n"; + return document_; +} + +void FastWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + document_ += "null"; + break; + case intValue: + document_ += valueToString(value.asLargestInt()); + break; + case uintValue: + document_ += valueToString(value.asLargestUInt()); + break; + case realValue: + document_ += valueToString(value.asDouble()); + break; + case stringValue: + { + // Is NULL possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) document_ += valueToQuotedStringN(str, static_cast(end-str)); + break; + } + case booleanValue: + document_ += valueToString(value.asBool()); + break; + case arrayValue: { + document_ += '['; + int size = value.size(); + for (int index = 0; index < size; ++index) { + if (index > 0) + document_ += ','; + writeValue(value[index]); + } + document_ += ']'; + } break; + case objectValue: { + Value::Members members(value.getMemberNames()); + document_ += '{'; + for (Value::Members::iterator it = members.begin(); it != members.end(); + ++it) { + const std::string& name = *it; + if (it != members.begin()) + document_ += ','; + document_ += valueToQuotedStringN(name.data(), static_cast(name.length())); + document_ += yamlCompatiblityEnabled_ ? ": " : ":"; + writeValue(value[name]); + } + document_ += '}'; + } break; + } +} + +// Class StyledWriter +// ////////////////////////////////////////////////////////////////// + +StyledWriter::StyledWriter() + : rightMargin_(74), indentSize_(3), addChildValues_() {} + +std::string StyledWriter::write(const Value& root) { + document_ = ""; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue(root); + writeValue(root); + writeCommentAfterValueOnSameLine(root); + document_ += "\n"; + return document_; +} + +void StyledWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + { + // Is NULL possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + const std::string& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + document_ += " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + writeIndent(); + writeValue(childValue); + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + document_ += "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + document_ += ", "; + document_ += childValues_[index]; + } + document_ += " ]"; + } + } +} + +bool StyledWriter::isMultineArray(const Value& value) { + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = + isMultiLine || ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (int index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += int(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledWriter::pushValue(const std::string& value) { + if (addChildValues_) + childValues_.push_back(value); + else + document_ += value; +} + +void StyledWriter::writeIndent() { + if (!document_.empty()) { + char last = document_[document_.length() - 1]; + if (last == ' ') // already indented + return; + if (last != '\n') // Comments may add new-line + document_ += '\n'; + } + document_ += indentString_; +} + +void StyledWriter::writeWithIndent(const std::string& value) { + writeIndent(); + document_ += value; +} + +void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); } + +void StyledWriter::unindent() { + assert(int(indentString_.size()) >= indentSize_); + indentString_.resize(indentString_.size() - indentSize_); +} + +void StyledWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + + document_ += "\n"; + writeIndent(); + const std::string& comment = root.getComment(commentBefore); + std::string::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + document_ += *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + writeIndent(); + ++iter; + } + + // Comments are stripped of trailing newlines, so add one here + document_ += "\n"; +} + +void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + document_ += " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + document_ += "\n"; + document_ += root.getComment(commentAfter); + document_ += "\n"; + } +} + +bool StyledWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +// Class StyledStreamWriter +// ////////////////////////////////////////////////////////////////// + +StyledStreamWriter::StyledStreamWriter(std::string indentation) + : document_(NULL), rightMargin_(74), indentation_(indentation), + addChildValues_() {} + +void StyledStreamWriter::write(std::ostream& out, const Value& root) { + document_ = &out; + addChildValues_ = false; + indentString_ = ""; + indented_ = true; + writeCommentBeforeValue(root); + if (!indented_) writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *document_ << "\n"; + document_ = NULL; // Forget the stream, for safety. +} + +void StyledStreamWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + { + // Is NULL possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + const std::string& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + *document_ << " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledStreamWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *document_ << "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *document_ << ", "; + *document_ << childValues_[index]; + } + *document_ << " ]"; + } + } +} + +bool StyledStreamWriter::isMultineArray(const Value& value) { + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = + isMultiLine || ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (int index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += int(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledStreamWriter::pushValue(const std::string& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *document_ << value; +} + +void StyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + *document_ << '\n' << indentString_; +} + +void StyledStreamWriter::writeWithIndent(const std::string& value) { + if (!indented_) writeIndent(); + *document_ << value; + indented_ = false; +} + +void StyledStreamWriter::indent() { indentString_ += indentation_; } + +void StyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) writeIndent(); + const std::string& comment = root.getComment(commentBefore); + std::string::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *document_ << *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would include newline + *document_ << indentString_; + ++iter; + } + indented_ = false; +} + +void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + *document_ << ' ' << root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *document_ << root.getComment(commentAfter); + } + indented_ = false; +} + +bool StyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +////////////////////////// +// BuiltStyledStreamWriter + +/// Scoped enums are not available until C++11. +struct CommentStyle { + /// Decide whether to write comments. + enum Enum { + None, ///< Drop all comments. + Most, ///< Recover odd behavior of previous versions (not implemented yet). + All ///< Keep all comments. + }; +}; + +struct BuiltStyledStreamWriter : public StreamWriter +{ + BuiltStyledStreamWriter( + std::string const& indentation, + CommentStyle::Enum cs, + std::string const& colonSymbol, + std::string const& nullSymbol, + std::string const& endingLineFeedSymbol); + virtual int write(Value const& root, std::ostream* sout); +private: + void writeValue(Value const& value); + void writeArrayValue(Value const& value); + bool isMultineArray(Value const& value); + void pushValue(std::string const& value); + void writeIndent(); + void writeWithIndent(std::string const& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(Value const& root); + void writeCommentAfterValueOnSameLine(Value const& root); + static bool hasCommentForValue(const Value& value); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + CommentStyle::Enum cs_; + std::string colonSymbol_; + std::string nullSymbol_; + std::string endingLineFeedSymbol_; + bool addChildValues_ : 1; + bool indented_ : 1; +}; +BuiltStyledStreamWriter::BuiltStyledStreamWriter( + std::string const& indentation, + CommentStyle::Enum cs, + std::string const& colonSymbol, + std::string const& nullSymbol, + std::string const& endingLineFeedSymbol) + : rightMargin_(74) + , indentation_(indentation) + , cs_(cs) + , colonSymbol_(colonSymbol) + , nullSymbol_(nullSymbol) + , endingLineFeedSymbol_(endingLineFeedSymbol) + , addChildValues_(false) + , indented_(false) +{ +} +int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout) +{ + sout_ = sout; + addChildValues_ = false; + indented_ = true; + indentString_ = ""; + writeCommentBeforeValue(root); + if (!indented_) writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *sout_ << endingLineFeedSymbol_; + sout_ = NULL; + return 0; +} +void BuiltStyledStreamWriter::writeValue(Value const& value) { + switch (value.type()) { + case nullValue: + pushValue(nullSymbol_); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + { + // Is NULL is possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + std::string const& name = *it; + Value const& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedStringN(name.data(), static_cast(name.length()))); + *sout_ << colonSymbol_; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value); + if (isMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + Value const& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *sout_ << "["; + if (!indentation_.empty()) *sout_ << " "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *sout_ << ", "; + *sout_ << childValues_[index]; + } + if (!indentation_.empty()) *sout_ << " "; + *sout_ << "]"; + } + } +} + +bool BuiltStyledStreamWriter::isMultineArray(Value const& value) { + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) { + Value const& childValue = value[index]; + isMultiLine = + isMultiLine || ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (int index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += int(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void BuiltStyledStreamWriter::pushValue(std::string const& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *sout_ << value; +} + +void BuiltStyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + + if (!indentation_.empty()) { + // In this case, drop newlines too. + *sout_ << '\n' << indentString_; + } +} + +void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) { + if (!indented_) writeIndent(); + *sout_ << value; + indented_ = false; +} + +void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } + +void BuiltStyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { + if (cs_ == CommentStyle::None) return; + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) writeIndent(); + const std::string& comment = root.getComment(commentBefore); + std::string::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *sout_ << *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would write extra newline + *sout_ << indentString_; + ++iter; + } + indented_ = false; +} + +void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { + if (cs_ == CommentStyle::None) return; + if (root.hasComment(commentAfterOnSameLine)) + *sout_ << " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *sout_ << root.getComment(commentAfter); + } +} + +// static +bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +/////////////// +// StreamWriter + +StreamWriter::StreamWriter() + : sout_(NULL) +{ +} +StreamWriter::~StreamWriter() +{ +} +StreamWriter::Factory::~Factory() +{} +StreamWriterBuilder::StreamWriterBuilder() +{ + setDefaults(&settings_); +} +StreamWriterBuilder::~StreamWriterBuilder() +{} +StreamWriter* StreamWriterBuilder::newStreamWriter() const +{ + std::string indentation = settings_["indentation"].asString(); + std::string cs_str = settings_["commentStyle"].asString(); + bool eyc = settings_["enableYAMLCompatibility"].asBool(); + bool dnp = settings_["dropNullPlaceholders"].asBool(); + CommentStyle::Enum cs = CommentStyle::All; + if (cs_str == "All") { + cs = CommentStyle::All; + } else if (cs_str == "None") { + cs = CommentStyle::None; + } else { + throwRuntimeError("commentStyle must be 'All' or 'None'"); + } + std::string colonSymbol = " : "; + if (eyc) { + colonSymbol = ": "; + } else if (indentation.empty()) { + colonSymbol = ":"; + } + std::string nullSymbol = "null"; + if (dnp) { + nullSymbol = ""; + } + std::string endingLineFeedSymbol = ""; + return new BuiltStyledStreamWriter( + indentation, cs, + colonSymbol, nullSymbol, endingLineFeedSymbol); +} +static void getValidWriterKeys(std::set* valid_keys) +{ + valid_keys->clear(); + valid_keys->insert("indentation"); + valid_keys->insert("commentStyle"); + valid_keys->insert("enableYAMLCompatibility"); + valid_keys->insert("dropNullPlaceholders"); +} +bool StreamWriterBuilder::validate(Json::Value* invalid) const +{ + Json::Value my_invalid; + if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL + Json::Value& inv = *invalid; + std::set valid_keys; + getValidWriterKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + std::string const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return 0u == inv.size(); +} +Value& StreamWriterBuilder::operator[](std::string key) +{ + return settings_[key]; +} +// static +void StreamWriterBuilder::setDefaults(Json::Value* settings) +{ + //! [StreamWriterBuilderDefaults] + (*settings)["commentStyle"] = "All"; + (*settings)["indentation"] = "\t"; + (*settings)["enableYAMLCompatibility"] = false; + (*settings)["dropNullPlaceholders"] = false; + //! [StreamWriterBuilderDefaults] +} + +std::string writeString(StreamWriter::Factory const& builder, Value const& root) { + std::ostringstream sout; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout.str(); +} + +std::ostream& operator<<(std::ostream& sout, Value const& root) { + StreamWriterBuilder builder; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_writer.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/lcb-jsoncpp.h b/couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/lcb-jsoncpp.h new file mode 100644 index 00000000..edd9eac8 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/lcb-jsoncpp/lcb-jsoncpp.h @@ -0,0 +1,1961 @@ +/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/). +/// It is intended to be used with #include "lcb-jsoncpp.h" + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + +#ifndef JSON_AMALGATED_H_INCLUDED +# define JSON_AMALGATED_H_INCLUDED +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +#define JSON_IS_AMALGAMATION + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/version.h +// ////////////////////////////////////////////////////////////////////// + +// DO NOT EDIT. This file (and "version") is generated by CMake. +// Run CMake configure step to update it. +#ifndef JSON_VERSION_H_INCLUDED +# define JSON_VERSION_H_INCLUDED + +# define JSONCPP_VERSION_STRING "0.10.5" +# define JSONCPP_VERSION_MAJOR 0 +# define JSONCPP_VERSION_MINOR 10 +# define JSONCPP_VERSION_PATCH 5 +# define JSONCPP_VERSION_QUALIFIER +# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) + +#endif // JSON_VERSION_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/version.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +#define JSON_CONFIG_H_INCLUDED + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of +/// std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 + +// If non-zero, the library uses exceptions to report bad input instead of C +// assertion macros. The default is to use exceptions. +#ifndef JSON_USE_EXCEPTION +#define JSON_USE_EXCEPTION 1 +#endif + +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgated header. +// #define JSON_IS_AMALGAMATION + +#ifdef JSON_IN_CPPTL +#include +#ifndef JSON_USE_CPPTL +#define JSON_USE_CPPTL 1 +#endif +#endif + +#ifdef JSON_IN_CPPTL +#define JSON_API CPPTL_API +#elif defined(JSON_DLL_BUILD) +#if defined(_MSC_VER) +#define JSON_API __declspec(dllexport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#elif defined(JSON_DLL) +#if defined(_MSC_VER) +#define JSON_API __declspec(dllimport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#endif // ifdef JSON_IN_CPPTL +#if !defined(JSON_API) +#define JSON_API +#endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for +// integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 +// Microsoft Visual Studio 6 only support conversion from __int64 to double +// (no conversion from unsigned __int64). +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' +// characters in the debug information) +// All projects I've ever seen with VS6 were using this globally (not bothering +// with pragma push/pop). +#pragma warning(disable : 4786) +#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 + +#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 +/// Indicates that the following function is deprecated. +#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#elif defined(__clang__) && defined(__has_feature) +#if __has_feature(attribute_deprecated_with_message) +#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +#endif +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +#elif defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) +#endif + +#if !defined(JSONCPP_DEPRECATED) +#define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +namespace Json { +typedef int Int; +typedef unsigned int UInt; +#if defined(JSON_NO_INT64) +typedef int LargestInt; +typedef unsigned int LargestUInt; +#undef JSON_HAS_INT64 +#else // if defined(JSON_NO_INT64) +// For Microsoft Visual use specific types as long long is not supported +#if defined(_MSC_VER) // Microsoft Visual Studio +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else // if defined(_MSC_VER) // Other platforms, use long long +typedef long long int Int64; +typedef unsigned long long int UInt64; +#endif // if defined(_MSC_VER) +typedef Int64 LargestInt; +typedef UInt64 LargestUInt; +#define JSON_HAS_INT64 +#endif // if defined(JSON_NO_INT64) +} // end namespace Json + +#endif // JSON_CONFIG_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +#define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// writer.h +class FastWriter; +class StyledWriter; + +// reader.h +class Reader; + +// features.h +class Features; + +// value.h +typedef unsigned int ArrayIndex; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; + +} // namespace Json + +#endif // JSON_FORWARDS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/features.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_FEATURES_H_INCLUDED +#define CPPTL_JSON_FEATURES_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +/** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ +class JSON_API Features { +public: + /** \brief A configuration that allows all features and assumes all strings + * are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON + * specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_; + + /// \c true if root must be either an array or an object value. Default: \c + /// false. + bool strictRoot_; +}; + +} // namespace Json + +#endif // CPPTL_JSON_FEATURES_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/features.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/value.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_H_INCLUDED +#define CPPTL_JSON_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include + +#ifndef JSON_USE_CPPTL_SMALLMAP +#include +#else +#include +#endif +#ifdef JSON_USE_CPPTL +#include +#endif + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +/** \brief JSON (JavaScript Object Notation). + */ +namespace Json { + +/** Base class for all exceptions we throw. + * + * We use nothing but these internally. Of course, STL can throw others. + */ +class JSON_API Exception : public std::exception { +public: + Exception(std::string const& msg); + virtual ~Exception() throw(); + virtual char const* what() const throw(); +protected: + std::string const msg_; +}; + +/** Exceptions which the user cannot easily avoid. + * + * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input + * + * \remark derived from Json::Exception + */ +class JSON_API RuntimeError : public Exception { +public: + RuntimeError(std::string const& msg); +}; + +/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. + * + * These are precondition-violations (user bugs) and internal errors (our bugs). + * + * \remark derived from Json::Exception + */ +class JSON_API LogicError : public Exception { +public: + LogicError(std::string const& msg); +}; + +/// used internally +void throwRuntimeError(std::string const& msg); +/// used internally +void throwLogicError(std::string const& msg); + +/** \brief Type of the value held by a Value object. + */ +enum ValueType { + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). +}; + +enum CommentPlacement { + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for + /// root value) + numberOfCommentPlacement +}; + +//# ifdef JSON_USE_CPPTL +// typedef CppTL::AnyEnumerator EnumMemberNames; +// typedef CppTL::AnyEnumerator EnumValues; +//# endif + +/** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignement takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ +class JSON_API StaticString { +public: + explicit StaticString(const char* czstring) : c_str_(czstring) {} + + operator const char*() const { return c_str_; } + + const char* c_str() const { return c_str_; } + +private: + const char* c_str_; +}; + +/** \brief Represents a JSON value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * Values of an #objectValue or #arrayValue can be accessed using operator[]() + * methods. + * Non-const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resized and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtain default value in the case the + * required element does not exist. + * + * It is possible to iterate over the list of a #objectValue values using + * the getMemberNames() method. + * + * \note #Value string-length fit in size_t, but keys must be < 2^30. + * (The reason is an implementation detail.) A #CharReader will raise an + * exception if a bound is exceeded to avoid security holes in your app, + * but the Value API does *not* check bounds. That is the responsibility + * of the caller. + */ +class JSON_API Value { + friend class ValueIteratorBase; +public: + typedef std::vector Members; + typedef ValueIterator iterator; + typedef ValueConstIterator const_iterator; + typedef Json::UInt UInt; + typedef Json::Int Int; +#if defined(JSON_HAS_INT64) + typedef Json::UInt64 UInt64; + typedef Json::Int64 Int64; +#endif // defined(JSON_HAS_INT64) + typedef Json::LargestInt LargestInt; + typedef Json::LargestUInt LargestUInt; + typedef Json::ArrayIndex ArrayIndex; + + static const Value& nullRef; +#if !defined(__ARMEL__) + /// \deprecated This exists for binary compatibility only. Use nullRef. + static const Value null; +#endif + /// Minimum signed integer value that can be stored in a Json::Value. + static const LargestInt minLargestInt; + /// Maximum signed integer value that can be stored in a Json::Value. + static const LargestInt maxLargestInt; + /// Maximum unsigned integer value that can be stored in a Json::Value. + static const LargestUInt maxLargestUInt; + + /// Minimum signed int value that can be stored in a Json::Value. + static const Int minInt; + /// Maximum signed int value that can be stored in a Json::Value. + static const Int maxInt; + /// Maximum unsigned int value that can be stored in a Json::Value. + static const UInt maxUInt; + +#if defined(JSON_HAS_INT64) + /// Minimum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 minInt64; + /// Maximum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 maxInt64; + /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. + static const UInt64 maxUInt64; +#endif // defined(JSON_HAS_INT64) + +private: +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + class CZString { + public: + enum DuplicationPolicy { + noDuplication = 0, + duplicate, + duplicateOnCopy + }; + CZString(ArrayIndex index); + CZString(char const* str, unsigned length, DuplicationPolicy allocate); + CZString(CZString const& other); + ~CZString(); + CZString& operator=(CZString other); + bool operator<(CZString const& other) const; + bool operator==(CZString const& other) const; + ArrayIndex index() const; + //const char* c_str() const; ///< \deprecated + char const* data() const; + unsigned length() const; + bool isStaticString() const; + + private: + void swap(CZString& other); + + struct StringStorage { + unsigned policy_: 2; + unsigned length_: 30; // 1GB max + }; + + char const* cstr_; // actually, a prefixed string, unless policy is noDup + union { + ArrayIndex index_; + StringStorage storage_; + }; + }; + +public: +#ifndef JSON_USE_CPPTL_SMALLMAP + typedef std::map ObjectValues; +#else + typedef CppTL::SmallMap ObjectValues; +#endif // ifndef JSON_USE_CPPTL_SMALLMAP +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +public: + /** \brief Create a default Value of the given type. + + This is a very useful constructor. + To create an empty array, pass arrayValue. + To create an empty object, pass objectValue. + Another Value can then be set to this one by assignment. +This is useful since clear() and resize() will not alter types. + + Examples: +\code +Json::Value null_value; // null +Json::Value arr_value(Json::arrayValue); // [] +Json::Value obj_value(Json::objectValue); // {} +\endcode + */ + Value(ValueType type = nullValue); + Value(Int value); + Value(UInt value); +#if defined(JSON_HAS_INT64) + Value(Int64 value); + Value(UInt64 value); +#endif // if defined(JSON_HAS_INT64) + Value(double value); + Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) + Value(const char* begin, const char* end); ///< Copy all, incl zeroes. + /** \brief Constructs a value from a static string. + + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to this + * constructor. + * \note This works only for null-terminated strings. (We cannot change the + * size of this class, so we have nowhere to store the length, + * which might be computed later for various operations.) + * + * Example of usage: + * \code + * static StaticString foo("some text"); + * Json::Value aValue(foo); + * \endcode + */ + Value(const StaticString& value); + Value(const std::string& value); ///< Copy data() til size(). Embedded zeroes too. +#ifdef JSON_USE_CPPTL + Value(const CppTL::ConstString& value); +#endif + Value(bool value); + /// Deep copy. + Value(const Value& other); + ~Value(); + + /// Deep copy, then swap(other). + /// \note Over-write existing comments. To preserve comments, use #swapPayload(). + Value &operator=(const Value &other); + /// Swap everything. + void swap(Value& other); + /// Swap values but leave comments and source offsets in place. + void swapPayload(Value& other); + + ValueType type() const; + + /// Compare payload only, not comments etc. + bool operator<(const Value& other) const; + bool operator<=(const Value& other) const; + bool operator>=(const Value& other) const; + bool operator>(const Value& other) const; + bool operator==(const Value& other) const; + bool operator!=(const Value& other) const; + int compare(const Value& other) const; + + const char* asCString() const; ///< Embedded zeroes could cause you trouble! + std::string asString() const; ///< Embedded zeroes are possible. + /** Get raw char* of string-value. + * \return false if !string. (Seg-fault if str or end are NULL.) + */ + bool getString( + char const** begin, char const** end) const; +#ifdef JSON_USE_CPPTL + CppTL::ConstString asConstString() const; +#endif + Int asInt() const; + UInt asUInt() const; +#if defined(JSON_HAS_INT64) + Int64 asInt64() const; + UInt64 asUInt64() const; +#endif // if defined(JSON_HAS_INT64) + LargestInt asLargestInt() const; + LargestUInt asLargestUInt() const; + float asFloat() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isInt64() const; + bool isUInt() const; + bool isUInt64() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + bool isConvertibleTo(ValueType other) const; + + /// Number of values in array or object + ArrayIndex size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return isNull() + bool operator!() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to size elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize(ArrayIndex size); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](ArrayIndex index); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](int index); + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](ArrayIndex index) const; + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](int index) const; + + /// If the array contains at least index+1 elements, returns the element + /// value, + /// otherwise returns defaultValue. + Value get(ArrayIndex index, const Value& defaultValue) const; + /// Return true if index < size(). + bool isValidIndex(ArrayIndex index) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + Value& append(const Value& value); + + /// Access an object value by name, create a null member if it does not exist. + /// \note Because of our implementation, keys are limited to 2^30 -1 chars. + /// Exceeding that will cause an exception. + Value& operator[](const char* key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const char* key) const; + /// Access an object value by name, create a null member if it does not exist. + /// \param key may contain embedded nulls. + Value& operator[](const std::string& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + /// \param key may contain embedded nulls. + const Value& operator[](const std::string& key) const; + /** \brief Access an object value by name, create a null member if it does not + exist. + + * If the object has no entry for that name, then the member name used to store + * the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + Value& operator[](const StaticString& key); +#ifdef JSON_USE_CPPTL + /// Access an object value by name, create a null member if it does not exist. + Value& operator[](const CppTL::ConstString& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const CppTL::ConstString& key) const; +#endif + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + Value get(const char* key, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \note key may contain embedded nulls. + Value get(const char* begin, const char* end, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \param key may contain embedded nulls. + Value get(const std::string& key, const Value& defaultValue) const; +#ifdef JSON_USE_CPPTL + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + Value get(const CppTL::ConstString& key, const Value& defaultValue) const; +#endif + /// Most general and efficient version of isMember()const, get()const, + /// and operator[]const + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + Value const* find(char const* begin, char const* end) const; + /// Most general and efficient version of object-mutators. + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. + Value const* demand(char const* begin, char const* end); + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \return the removed Value, or null. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + /// \deprecated + Value removeMember(const char* key); + /// Same as removeMember(const char*) + /// \param key may contain embedded nulls. + /// \deprecated + Value removeMember(const std::string& key); + /// Same as removeMember(const char* begin, const char* end, Value* removed), + /// but 'key' is null-terminated. + bool removeMember(const char* key, Value* removed); + /** \brief Remove the named map member. + + Update 'removed' iff removed. + \param key may contain embedded nulls. + \return true iff removed (no exceptions) + */ + bool removeMember(std::string const& key, Value* removed); + /// Same as removeMember(std::string const& key, Value* removed) + bool removeMember(const char* begin, const char* end, Value* removed); + /** \brief Remove the indexed array element. + + O(n) expensive operations. + Update 'removed' iff removed. + \return true iff removed (no exceptions) + */ + bool removeIndex(ArrayIndex i, Value* removed); + + /// Return true if the object has a member named key. + /// \note 'key' must be null-terminated. + bool isMember(const char* key) const; + /// Return true if the object has a member named key. + /// \param key may contain embedded nulls. + bool isMember(const std::string& key) const; + /// Same as isMember(std::string const& key)const + bool isMember(const char* begin, const char* end) const; +#ifdef JSON_USE_CPPTL + /// Return true if the object has a member named key. + bool isMember(const CppTL::ConstString& key) const; +#endif + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + + //# ifdef JSON_USE_CPPTL + // EnumMemberNames enumMemberNames() const; + // EnumValues enumValues() const; + //# endif + + /// \deprecated Always pass len. + JSONCPP_DEPRECATED("Use setComment(std::string const&) instead.") + void setComment(const char* comment, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const char* comment, size_t len, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const std::string& comment, CommentPlacement placement); + bool hasComment(CommentPlacement placement) const; + /// Include delimiters and embedded newlines. + std::string getComment(CommentPlacement placement) const; + + std::string toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + +private: + void initBasic(ValueType type, bool allocated = false); + + Value& resolveReference(const char* key); + Value& resolveReference(const char* key, const char* end); + + struct CommentInfo { + CommentInfo(); + ~CommentInfo(); + + void setComment(const char* text, size_t len); + + char* comment_; + }; + + // struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + union ValueHolder { + LargestInt int_; + LargestUInt uint_; + double real_; + bool bool_; + char* string_; // actually ptr to unsigned, followed by str, unless !allocated_ + ObjectValues* map_; + } value_; + ValueType type_ : 8; + unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. + // If not allocated_, string_ must be null-terminated. + CommentInfo* comments_; +}; + +/** \brief Experimental and untested: represents an element of the "path" to + * access a node. + */ +class JSON_API PathArgument { +public: + friend class Path; + + PathArgument(); + PathArgument(ArrayIndex index); + PathArgument(const char* key); + PathArgument(const std::string& key); + +private: + enum Kind { + kindNone = 0, + kindIndex, + kindKey + }; + std::string key_; + ArrayIndex index_; + Kind kind_; +}; + +/** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provied as parameter + */ +class JSON_API Path { +public: + Path(const std::string& path, + const PathArgument& a1 = PathArgument(), + const PathArgument& a2 = PathArgument(), + const PathArgument& a3 = PathArgument(), + const PathArgument& a4 = PathArgument(), + const PathArgument& a5 = PathArgument()); + + const Value& resolve(const Value& root) const; + Value resolve(const Value& root, const Value& defaultValue) const; + /// Creates the "path" to access the specified node and returns a reference on + /// the node. + Value& make(Value& root) const; + +private: + typedef std::vector InArgs; + typedef std::vector Args; + + void makePath(const std::string& path, const InArgs& in); + void addPathInArg(const std::string& path, + const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind); + void invalidPath(const std::string& path, int location); + + Args args_; +}; + +/** \brief base class for Value iterators. + * + */ +class JSON_API ValueIteratorBase { +public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef unsigned int size_t; + typedef int difference_type; + typedef ValueIteratorBase SelfType; + + bool operator==(const SelfType& other) const { return isEqual(other); } + + bool operator!=(const SelfType& other) const { return !isEqual(other); } + + difference_type operator-(const SelfType& other) const { + return other.computeDistance(*this); + } + + /// Return either the index or the member name of the referenced value as a + /// Value. + Value key() const; + + /// Return the index of the referenced Value, or -1 if it is not an arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value, or "" if it is not an + /// objectValue. + /// \note Avoid `c_str()` on result, as embedded zeroes are possible. + std::string name() const; + + /// Return the member name of the referenced Value. "" if it is not an + /// objectValue. + /// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls. + JSONCPP_DEPRECATED("Use `key = name();` instead.") + char const* memberName() const; + /// Return the member name of the referenced Value, or NULL if it is not an + /// objectValue. + /// \note Better version than memberName(). Allows embedded nulls. + char const* memberName(char const** end) const; + +protected: + Value& deref() const; + + void increment(); + + void decrement(); + + difference_type computeDistance(const SelfType& other) const; + + bool isEqual(const SelfType& other) const; + + void copy(const SelfType& other); + +private: + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_; + +public: + // For some reason, BORLAND needs these at the end, rather + // than earlier. No idea why. + ValueIteratorBase(); + explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); +}; + +/** \brief const iterator for object and array value. + * + */ +class JSON_API ValueConstIterator : public ValueIteratorBase { + friend class Value; + +public: + typedef const Value value_type; + //typedef unsigned int size_t; + //typedef int difference_type; + typedef const Value& reference; + typedef const Value* pointer; + typedef ValueConstIterator SelfType; + + ValueConstIterator(); + +private: +/*! \internal Use by Value to create an iterator. + */ + explicit ValueConstIterator(const Value::ObjectValues::iterator& current); +public: + SelfType& operator=(const ValueIteratorBase& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +/** \brief Iterator for object and array value. + */ +class JSON_API ValueIterator : public ValueIteratorBase { + friend class Value; + +public: + typedef Value value_type; + typedef unsigned int size_t; + typedef int difference_type; + typedef Value& reference; + typedef Value* pointer; + typedef ValueIterator SelfType; + + ValueIterator(); + ValueIterator(const ValueConstIterator& other); + ValueIterator(const ValueIterator& other); + +private: +/*! \internal Use by Value to create an iterator. + */ + explicit ValueIterator(const Value::ObjectValues::iterator& current); +public: + SelfType& operator=(const SelfType& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +} // namespace Json + + +namespace std { +/// Specialize std::swap() for Json::Value. +template<> +inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } +} + + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/value.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/reader.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_READER_H_INCLUDED +#define CPPTL_JSON_READER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "features.h" +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +namespace Json { + +/** \brief Unserialize a JSON document into a + *Value. + * + * \deprecated Use CharReader and CharReaderBuilder. + */ +class JSON_API Reader { +public: + typedef char Char; + typedef const Char* Location; + + /** \brief Constructs a Reader allowing all features + * for parsing. + */ + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set + * for parsing. + */ + Reader(const Features& features); + + /** \brief Read a Value from a JSON + * document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + * back during + * serialization, \c false to discard comments. + * This parameter is ignored if + * Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + * error occurred. + */ + bool + parse(const std::string& document, Value& root, bool collectComments = true); + + /** \brief Read a Value from a JSON + document. + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + * Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + back during + * serialization, \c false to discard comments. + * This parameter is ignored if + Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + bool parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments = true); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value&). + bool parse(std::istream& is, Value& root, bool collectComments = true); + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + * \deprecated Use getFormattedErrorMessages() instead (typo fix). + */ + JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") + std::string getFormatedErrorMessages() const; + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + */ + std::string getFormattedErrorMessages() const; + +private: + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + std::string message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool readToken(Token& token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + void readNumber(); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, std::string& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool addError(const std::string& message, Token& token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const std::string& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + std::string getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + std::string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value* lastValue_; + std::string commentsBefore_; + Features features_; + bool collectComments_; +}; // Reader + +/** Interface for reading JSON from a char array. + */ +class JSON_API CharReader { +public: + virtual ~CharReader() {} + /** \brief Read a Value from a JSON + document. + * The document must be a UTF-8 encoded string containing the document to read. + * + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + * Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param errs [out] Formatted error messages (if not NULL) + * a user friendly string that lists errors in the parsed + * document. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + virtual bool parse( + char const* beginDoc, char const* endDoc, + Value* root, std::string* errs) = 0; + + class Factory { + public: + virtual ~Factory() {} + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual CharReader* newCharReader() const = 0; + }; // Factory +}; // CharReader + +/** \brief Build a CharReader implementation. + +Usage: +\code + using namespace Json; + CharReaderBuilder builder; + builder["collectComments"] = false; + Value value; + std::string errs; + bool ok = parseFromStream(builder, std::cin, &value, &errs); +\endcode +*/ +class JSON_API CharReaderBuilder : public CharReader::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + These are case-sensitive. + Available settings (case-sensitive): + - `"collectComments": false or true` + - true to collect comment and allow writing them + back during serialization, false to discard comments. + This parameter is ignored if allowComments is false. + - `"allowComments": false or true` + - true if comments are allowed. + - `"strictRoot": false or true` + - true if root must be either an array or an object value + - `"allowDroppedNullPlaceholders": false or true` + - true if dropped null placeholders are allowed. (See StreamWriterBuilder.) + - `"allowNumericKeys": false or true` + - true if numeric object keys are allowed. + - `"allowSingleQuotes": false or true` + - true if '' are allowed for strings (both keys and values) + - `"stackLimit": integer` + - Exceeding stackLimit (recursive depth of `readValue()`) will + cause an exception. + - This is a security issue (seg-faults caused by deeply nested JSON), + so the default is low. + - `"failIfExtra": false or true` + - If true, `parse()` returns false when extra non-whitespace trails + the JSON value in the input string. + - `"rejectDupKeys": false or true` + - If true, `parse()` returns false when a key is duplicated within an object. + + You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + \sa setDefaults() + */ + Json::Value settings_; + + CharReaderBuilder(); + virtual ~CharReaderBuilder(); + + virtual CharReader* newCharReader() const; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + + /** A simple way to update a specific setting. + */ + Value& operator[](std::string key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults + */ + static void setDefaults(Json::Value* settings); + /** Same as old Features::strictMode(). + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode + */ + static void strictMode(Json::Value* settings); +}; + +/** Consume entire stream and use its begin/end. + * Someday we might have a real StreamReader, but for now this + * is convenient. + */ +bool JSON_API parseFromStream( + CharReader::Factory const&, + std::istream&, + Value* root, std::string* errs); + +/** \brief Read from 'sin' into 'root'. + + Always keep comments from the input JSON. + + This can be used to read a file into a particular sub-object. + For example: + \code + Json::Value root; + cin >> root["dir"]["file"]; + cout << root; + \endcode + Result: + \verbatim + { + "dir": { + "file": { + // The input stream JSON would be nested here. + } + } + } + \endverbatim + \throw std::exception on parse error. + \see Json::operator<<() +*/ +JSON_API std::istream& operator>>(std::istream&, Value&); + +} // namespace Json + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_READER_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/reader.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/writer.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_WRITER_H_INCLUDED +#define JSON_WRITER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +namespace Json { + +class Value; + +/** + +Usage: +\code + using namespace Json; + void writeToStdout(StreamWriter::Factory const& factory, Value const& value) { + std::unique_ptr const writer( + factory.newStreamWriter()); + writer->write(value, &std::cout); + std::cout << std::endl; // add lf and flush + } +\endcode +*/ +class JSON_API StreamWriter { +protected: + std::ostream* sout_; // not owned; will not delete +public: + StreamWriter(); + virtual ~StreamWriter(); + /** Write Value into document as configured in sub-class. + Do not take ownership of sout, but maintain a reference during function. + \pre sout != NULL + \return zero on success (For now, we always return zero, so check the stream instead.) + \throw std::exception possibly, depending on configuration + */ + virtual int write(Value const& root, std::ostream* sout) = 0; + + /** \brief A simple abstract factory. + */ + class JSON_API Factory { + public: + virtual ~Factory(); + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual StreamWriter* newStreamWriter() const = 0; + }; // Factory +}; // StreamWriter + +/** \brief Write into stringstream, then return string, for convenience. + * A StreamWriter will be created from the factory, used, and then deleted. + */ +std::string JSON_API writeString(StreamWriter::Factory const& factory, Value const& root); + + +/** \brief Build a StreamWriter implementation. + +Usage: +\code + using namespace Json; + Value value = ...; + StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = " "; // or whatever you like + std::unique_ptr writer( + builder.newStreamWriter()); + writer->write(value, &std::cout); + std::cout << std::endl; // add lf and flush +\endcode +*/ +class JSON_API StreamWriterBuilder : public StreamWriter::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + Available settings (case-sensitive): + - "commentStyle": "None" or "All" + - "indentation": "" + - "enableYAMLCompatibility": false or true + - slightly change the whitespace around colons + - "dropNullPlaceholders": false or true + - Drop the "null" string from the writer's output for nullValues. + Strictly speaking, this is not valid JSON. But when the output is being + fed to a browser's Javascript, it makes for smaller output and the + browser can handle the output just fine. + + You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + \sa setDefaults() + */ + Json::Value settings_; + + StreamWriterBuilder(); + virtual ~StreamWriterBuilder(); + + /** + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual StreamWriter* newStreamWriter() const; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + /** A simple way to update a specific setting. + */ + Value& operator[](std::string key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults + */ + static void setDefaults(Json::Value* settings); +}; + +/** \brief Abstract class for writers. + * \deprecated Use StreamWriter. (And really, this is an implementation detail.) + */ +class JSON_API Writer { +public: + virtual ~Writer(); + + virtual std::string write(const Value& root) = 0; +}; + +/** \brief Outputs a Value in JSON format + *without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' + *consumption, + * but may be usefull to support feature such as RPC where bandwith is limited. + * \sa Reader, Value + * \deprecated Use StreamWriterBuilder. + */ +class JSON_API FastWriter : public Writer { + +public: + FastWriter(); + virtual ~FastWriter() {} + + void enableYAMLCompatibility(); + +public: // overridden from Writer + virtual std::string write(const Value& root); + +private: + void writeValue(const Value& value); + + std::string document_; + bool yamlCompatiblityEnabled_; +}; + +/** \brief Writes a Value in JSON format in a + *human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + *line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + *types, + * and all the values fit on one lines, then print the array on a single + *line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + *#CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +class JSON_API StyledWriter : public Writer { +public: + StyledWriter(); + virtual ~StyledWriter() {} + +public: // overridden from Writer + /** \brief Serialize a Value in JSON format. + * \param root Value to serialize. + * \return String containing the JSON document that represents the root value. + */ + virtual std::string write(const Value& root); + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultineArray(const Value& value); + void pushValue(const std::string& value); + void writeIndent(); + void writeWithIndent(const std::string& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + bool hasCommentForValue(const Value& value); + static std::string normalizeEOL(const std::string& text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::string document_; + std::string indentString_; + int rightMargin_; + int indentSize_; + bool addChildValues_; +}; + +/** \brief Writes a Value in JSON format in a + human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + types, + * and all the values fit on one lines, then print the array on a single + line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + #CommentPlacement. + * + * \param indentation Each level will be indented by this amount extra. + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +class JSON_API StyledStreamWriter { +public: + StyledStreamWriter(std::string indentation = "\t"); + ~StyledStreamWriter() {} + +public: + /** \brief Serialize a Value in JSON format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not + * return a value. + */ + void write(std::ostream& out, const Value& root); + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultineArray(const Value& value); + void pushValue(const std::string& value); + void writeIndent(); + void writeWithIndent(const std::string& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + bool hasCommentForValue(const Value& value); + static std::string normalizeEOL(const std::string& text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::ostream* document_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + bool addChildValues_ : 1; + bool indented_ : 1; +}; + +#if defined(JSON_HAS_INT64) +std::string JSON_API valueToString(Int value); +std::string JSON_API valueToString(UInt value); +#endif // if defined(JSON_HAS_INT64) +std::string JSON_API valueToString(LargestInt value); +std::string JSON_API valueToString(LargestUInt value); +std::string JSON_API valueToString(double value); +std::string JSON_API valueToString(bool value); +std::string JSON_API valueToQuotedString(const char* value); + +/// \brief Output using the StyledStreamWriter. +/// \see Json::operator>>() +JSON_API std::ostream& operator<<(std::ostream&, const Value& root); + +} // namespace Json + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // JSON_WRITER_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/writer.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/assertions.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED +#define CPPTL_JSON_ASSERTIONS_H_INCLUDED + +#include +#include + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +/** It should not be possible for a maliciously designed file to + * cause an abort() or seg-fault, so these macros are used only + * for pre-condition violations and internal logic errors. + */ +#if JSON_USE_EXCEPTION + +// @todo <= add detail about condition in exception +# define JSON_ASSERT(condition) \ + {if (!(condition)) {Json::throwLogicError( "assert json failed" );}} + +# define JSON_FAIL_MESSAGE(message) \ + { \ + std::ostringstream oss; oss << message; \ + Json::throwLogicError(oss.str()); \ + abort(); \ + } + +#else // JSON_USE_EXCEPTION + +# define JSON_ASSERT(condition) assert(condition) + +// The call to assert() will show the failure message in debug builds. In +// release builds we abort, for a core-dump or debugger. +# define JSON_FAIL_MESSAGE(message) \ + { \ + std::ostringstream oss; oss << message; \ + assert(false && oss.str().c_str()); \ + abort(); \ + } + + +#endif + +#define JSON_ASSERT_MESSAGE(condition, message) \ + if (!(condition)) { \ + JSON_FAIL_MESSAGE(message); \ + } + +#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/assertions.h +// ////////////////////////////////////////////////////////////////////// + + + + + +#endif //ifndef JSON_AMALGATED_H_INCLUDED diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/CMakeLists.txt b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/CMakeLists.txt new file mode 100644 index 00000000..d39eff5c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/CMakeLists.txt @@ -0,0 +1,8 @@ +PROJECT(lcbsnappy) +FILE(GLOB SNAPPY_SRC *.cc) +ADD_LIBRARY(lcbsnappy STATIC ${SNAPPY_SRC}) + +SET_TARGET_PROPERTIES(lcbsnappy + PROPERTIES + POSITION_INDEPENDENT_CODE TRUE + COMPILE_FLAGS "${LCB_CORE_CXXFLAGS}") diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/COPYING b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/COPYING new file mode 100644 index 00000000..8d6bd9fe --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/COPYING @@ -0,0 +1,28 @@ +Copyright 2011, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-c.cc b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-c.cc new file mode 100644 index 00000000..473a0b09 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-c.cc @@ -0,0 +1,90 @@ +// Copyright 2011 Martin Gieseking . +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "snappy.h" +#include "snappy-c.h" + +extern "C" { + +snappy_status snappy_compress(const char* input, + size_t input_length, + char* compressed, + size_t *compressed_length) { + if (*compressed_length < snappy_max_compressed_length(input_length)) { + return SNAPPY_BUFFER_TOO_SMALL; + } + snappy::RawCompress(input, input_length, compressed, compressed_length); + return SNAPPY_OK; +} + +snappy_status snappy_uncompress(const char* compressed, + size_t compressed_length, + char* uncompressed, + size_t* uncompressed_length) { + size_t real_uncompressed_length; + if (!snappy::GetUncompressedLength(compressed, + compressed_length, + &real_uncompressed_length)) { + return SNAPPY_INVALID_INPUT; + } + if (*uncompressed_length < real_uncompressed_length) { + return SNAPPY_BUFFER_TOO_SMALL; + } + if (!snappy::RawUncompress(compressed, compressed_length, uncompressed)) { + return SNAPPY_INVALID_INPUT; + } + *uncompressed_length = real_uncompressed_length; + return SNAPPY_OK; +} + +size_t snappy_max_compressed_length(size_t source_length) { + return snappy::MaxCompressedLength(source_length); +} + +snappy_status snappy_uncompressed_length(const char *compressed, + size_t compressed_length, + size_t *result) { + if (snappy::GetUncompressedLength(compressed, + compressed_length, + result)) { + return SNAPPY_OK; + } else { + return SNAPPY_INVALID_INPUT; + } +} + +snappy_status snappy_validate_compressed_buffer(const char *compressed, + size_t compressed_length) { + if (snappy::IsValidCompressedBuffer(compressed, compressed_length)) { + return SNAPPY_OK; + } else { + return SNAPPY_INVALID_INPUT; + } +} + +} // extern "C" diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-c.h b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-c.h new file mode 100644 index 00000000..c6c2a860 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-c.h @@ -0,0 +1,138 @@ +/* + * Copyright 2011 Martin Gieseking . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Plain C interface (a wrapper around the C++ implementation). + */ + +#ifndef UTIL_SNAPPY_OPENSOURCE_SNAPPY_C_H_ +#define UTIL_SNAPPY_OPENSOURCE_SNAPPY_C_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + * Return values; see the documentation for each function to know + * what each can return. + */ +typedef enum { + SNAPPY_OK = 0, + SNAPPY_INVALID_INPUT = 1, + SNAPPY_BUFFER_TOO_SMALL = 2 +} snappy_status; + +/* + * Takes the data stored in "input[0..input_length-1]" and stores + * it in the array pointed to by "compressed". + * + * signals the space available in "compressed". + * If it is not at least equal to "snappy_max_compressed_length(input_length)", + * SNAPPY_BUFFER_TOO_SMALL is returned. After successful compression, + * contains the true length of the compressed output, + * and SNAPPY_OK is returned. + * + * Example: + * size_t output_length = snappy_max_compressed_length(input_length); + * char* output = (char*)malloc(output_length); + * if (snappy_compress(input, input_length, output, &output_length) + * == SNAPPY_OK) { + * ... Process(output, output_length) ... + * } + * free(output); + */ +snappy_status snappy_compress(const char* input, + size_t input_length, + char* compressed, + size_t* compressed_length); + +/* + * Given data in "compressed[0..compressed_length-1]" generated by + * calling the snappy_compress routine, this routine stores + * the uncompressed data to + * uncompressed[0..uncompressed_length-1]. + * Returns failure (a value not equal to SNAPPY_OK) if the message + * is corrupted and could not be decrypted. + * + * signals the space available in "uncompressed". + * If it is not at least equal to the value returned by + * snappy_uncompressed_length for this stream, SNAPPY_BUFFER_TOO_SMALL + * is returned. After successful decompression, + * contains the true length of the decompressed output. + * + * Example: + * size_t output_length; + * if (snappy_uncompressed_length(input, input_length, &output_length) + * != SNAPPY_OK) { + * ... fail ... + * } + * char* output = (char*)malloc(output_length); + * if (snappy_uncompress(input, input_length, output, &output_length) + * == SNAPPY_OK) { + * ... Process(output, output_length) ... + * } + * free(output); + */ +snappy_status snappy_uncompress(const char* compressed, + size_t compressed_length, + char* uncompressed, + size_t* uncompressed_length); + +/* + * Returns the maximal size of the compressed representation of + * input data that is "source_length" bytes in length. + */ +size_t snappy_max_compressed_length(size_t source_length); + +/* + * REQUIRES: "compressed[]" was produced by snappy_compress() + * Returns SNAPPY_OK and stores the length of the uncompressed data in + * *result normally. Returns SNAPPY_INVALID_INPUT on parsing error. + * This operation takes O(1) time. + */ +snappy_status snappy_uncompressed_length(const char* compressed, + size_t compressed_length, + size_t* result); + +/* + * Check if the contents of "compressed[]" can be uncompressed successfully. + * Does not return the uncompressed data; if so, returns SNAPPY_OK, + * or if not, returns SNAPPY_INVALID_INPUT. + * Takes time proportional to compressed_length, but is usually at least a + * factor of four faster than actual decompression. + */ +snappy_status snappy_validate_compressed_buffer(const char* compressed, + size_t compressed_length); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* UTIL_SNAPPY_OPENSOURCE_SNAPPY_C_H_ */ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-internal.h b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-internal.h new file mode 100644 index 00000000..c99d3313 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-internal.h @@ -0,0 +1,150 @@ +// Copyright 2008 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Internals shared between the Snappy implementation and its unittest. + +#ifndef UTIL_SNAPPY_SNAPPY_INTERNAL_H_ +#define UTIL_SNAPPY_SNAPPY_INTERNAL_H_ + +#include "snappy-stubs-internal.h" + +namespace snappy { +namespace internal { + +class WorkingMemory { + public: + WorkingMemory() : large_table_(NULL) { } + ~WorkingMemory() { delete[] large_table_; } + + // Allocates and clears a hash table using memory in "*this", + // stores the number of buckets in "*table_size" and returns a pointer to + // the base of the hash table. + uint16* GetHashTable(size_t input_size, int* table_size); + + private: + uint16 small_table_[1<<10]; // 2KB + uint16* large_table_; // Allocated only when needed + + DISALLOW_COPY_AND_ASSIGN(WorkingMemory); +}; + +// Flat array compression that does not emit the "uncompressed length" +// prefix. Compresses "input" string to the "*op" buffer. +// +// REQUIRES: "input_length <= kBlockSize" +// REQUIRES: "op" points to an array of memory that is at least +// "MaxCompressedLength(input_length)" in size. +// REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero. +// REQUIRES: "table_size" is a power of two +// +// Returns an "end" pointer into "op" buffer. +// "end - op" is the compressed size of "input". +char* CompressFragment(const char* input, + size_t input_length, + char* op, + uint16* table, + const int table_size); + +// Return the largest n such that +// +// s1[0,n-1] == s2[0,n-1] +// and n <= (s2_limit - s2). +// +// Does not read *s2_limit or beyond. +// Does not read *(s1 + (s2_limit - s2)) or beyond. +// Requires that s2_limit >= s2. +// +// Separate implementation for x86_64, for speed. Uses the fact that +// x86_64 is little endian. +#if defined(ARCH_K8) +static inline int FindMatchLength(const char* s1, + const char* s2, + const char* s2_limit) { + assert(s2_limit >= s2); + int matched = 0; + + // Find out how long the match is. We loop over the data 64 bits at a + // time until we find a 64-bit block that doesn't match; then we find + // the first non-matching bit and use that to calculate the total + // length of the match. + while (PREDICT_TRUE(s2 <= s2_limit - 8)) { + if (PREDICT_FALSE(UNALIGNED_LOAD64(s2) == UNALIGNED_LOAD64(s1 + matched))) { + s2 += 8; + matched += 8; + } else { + // On current (mid-2008) Opteron models there is a 3% more + // efficient code sequence to find the first non-matching byte. + // However, what follows is ~10% better on Intel Core 2 and newer, + // and we expect AMD's bsf instruction to improve. + uint64 x = UNALIGNED_LOAD64(s2) ^ UNALIGNED_LOAD64(s1 + matched); + int matching_bits = Bits::FindLSBSetNonZero64(x); + matched += matching_bits >> 3; + return matched; + } + } + while (PREDICT_TRUE(s2 < s2_limit)) { + if (PREDICT_TRUE(s1[matched] == *s2)) { + ++s2; + ++matched; + } else { + return matched; + } + } + return matched; +} +#else +static inline int FindMatchLength(const char* s1, + const char* s2, + const char* s2_limit) { + // Implementation based on the x86-64 version, above. + assert(s2_limit >= s2); + int matched = 0; + + while (s2 <= s2_limit - 4 && + UNALIGNED_LOAD32(s2) == UNALIGNED_LOAD32(s1 + matched)) { + s2 += 4; + matched += 4; + } + if (LittleEndian::IsLittleEndian() && s2 <= s2_limit - 4) { + uint32 x = UNALIGNED_LOAD32(s2) ^ UNALIGNED_LOAD32(s1 + matched); + int matching_bits = Bits::FindLSBSetNonZero(x); + matched += matching_bits >> 3; + } else { + while ((s2 < s2_limit) && (s1[matched] == *s2)) { + ++s2; + ++matched; + } + } + return matched; +} +#endif + +} // end namespace internal +} // end namespace snappy + +#endif // UTIL_SNAPPY_SNAPPY_INTERNAL_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-lcb-msvc.h b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-lcb-msvc.h new file mode 100644 index 00000000..ae5ae773 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-lcb-msvc.h @@ -0,0 +1,5 @@ +#ifdef _MSC_VER +#define NOMINMAX +#include +typedef SSIZE_T ssize_t; +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-sinksource.cc b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-sinksource.cc new file mode 100644 index 00000000..5844552c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-sinksource.cc @@ -0,0 +1,71 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "snappy-sinksource.h" + +namespace snappy { + +Source::~Source() { } + +Sink::~Sink() { } + +char* Sink::GetAppendBuffer(size_t length, char* scratch) { + return scratch; +} + +ByteArraySource::~ByteArraySource() { } + +size_t ByteArraySource::Available() const { return left_; } + +const char* ByteArraySource::Peek(size_t* len) { + *len = left_; + return ptr_; +} + +void ByteArraySource::Skip(size_t n) { + left_ -= n; + ptr_ += n; +} + +UncheckedByteArraySink::~UncheckedByteArraySink() { } + +void UncheckedByteArraySink::Append(const char* data, size_t n) { + // Do no copying if the caller filled in the result of GetAppendBuffer() + if (data != dest_) { + memcpy(dest_, data, n); + } + dest_ += n; +} + +char* UncheckedByteArraySink::GetAppendBuffer(size_t len, char* scratch) { + return dest_; +} + +} diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-sinksource.h b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-sinksource.h new file mode 100644 index 00000000..faabfa1e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-sinksource.h @@ -0,0 +1,137 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef UTIL_SNAPPY_SNAPPY_SINKSOURCE_H_ +#define UTIL_SNAPPY_SNAPPY_SINKSOURCE_H_ + +#include + + +namespace snappy { + +// A Sink is an interface that consumes a sequence of bytes. +class Sink { + public: + Sink() { } + virtual ~Sink(); + + // Append "bytes[0,n-1]" to this. + virtual void Append(const char* bytes, size_t n) = 0; + + // Returns a writable buffer of the specified length for appending. + // May return a pointer to the caller-owned scratch buffer which + // must have at least the indicated length. The returned buffer is + // only valid until the next operation on this Sink. + // + // After writing at most "length" bytes, call Append() with the + // pointer returned from this function and the number of bytes + // written. Many Append() implementations will avoid copying + // bytes if this function returned an internal buffer. + // + // If a non-scratch buffer is returned, the caller may only pass a + // prefix of it to Append(). That is, it is not correct to pass an + // interior pointer of the returned array to Append(). + // + // The default implementation always returns the scratch buffer. + virtual char* GetAppendBuffer(size_t length, char* scratch); + + + private: + // No copying + Sink(const Sink&); + void operator=(const Sink&); +}; + +// A Source is an interface that yields a sequence of bytes +class Source { + public: + Source() { } + virtual ~Source(); + + // Return the number of bytes left to read from the source + virtual size_t Available() const = 0; + + // Peek at the next flat region of the source. Does not reposition + // the source. The returned region is empty iff Available()==0. + // + // Returns a pointer to the beginning of the region and store its + // length in *len. + // + // The returned region is valid until the next call to Skip() or + // until this object is destroyed, whichever occurs first. + // + // The returned region may be larger than Available() (for example + // if this ByteSource is a view on a substring of a larger source). + // The caller is responsible for ensuring that it only reads the + // Available() bytes. + virtual const char* Peek(size_t* len) = 0; + + // Skip the next n bytes. Invalidates any buffer returned by + // a previous call to Peek(). + // REQUIRES: Available() >= n + virtual void Skip(size_t n) = 0; + + private: + // No copying + Source(const Source&); + void operator=(const Source&); +}; + +// A Source implementation that yields the contents of a flat array +class ByteArraySource : public Source { + public: + ByteArraySource(const char* p, size_t n) : ptr_(p), left_(n) { } + virtual ~ByteArraySource(); + virtual size_t Available() const; + virtual const char* Peek(size_t* len); + virtual void Skip(size_t n); + private: + const char* ptr_; + size_t left_; +}; + +// A Sink implementation that writes to a flat array without any bound checks. +class UncheckedByteArraySink : public Sink { + public: + explicit UncheckedByteArraySink(char* dest) : dest_(dest) { } + virtual ~UncheckedByteArraySink(); + virtual void Append(const char* data, size_t n); + virtual char* GetAppendBuffer(size_t len, char* scratch); + + // Return the current output pointer so that a caller can see how + // many bytes were produced. + // Note: this is not a Sink method. + char* CurrentDestination() const { return dest_; } + private: + char* dest_; +}; + + +} + +#endif // UTIL_SNAPPY_SNAPPY_SINKSOURCE_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-stubs-internal.cc b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-stubs-internal.cc new file mode 100644 index 00000000..6ed33437 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-stubs-internal.cc @@ -0,0 +1,42 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include + +#include "snappy-stubs-internal.h" + +namespace snappy { + +void Varint::Append32(string* s, uint32 value) { + char buf[Varint::kMax32]; + const char* p = Varint::Encode32(buf, value); + s->append(buf, p - buf); +} + +} // namespace snappy diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-stubs-internal.h b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-stubs-internal.h new file mode 100644 index 00000000..12393b62 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-stubs-internal.h @@ -0,0 +1,491 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Various stubs for the open-source version of Snappy. + +#ifndef UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_ +#define UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include + +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#include "snappy-stubs-public.h" + +#if defined(__x86_64__) + +// Enable 64-bit optimized versions of some routines. +#define ARCH_K8 1 + +#endif + +// Needed by OS X, among others. +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif + +// Pull in std::min, std::ostream, and the likes. This is safe because this +// header file is never used from any public header files. +using namespace std; + +// The size of an array, if known at compile-time. +// Will give unexpected results if used on a pointer. +// We undefine it first, since some compilers already have a definition. +#ifdef ARRAYSIZE +#undef ARRAYSIZE +#endif +#define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a))) + +// Static prediction hints. +#ifdef HAVE_BUILTIN_EXPECT +#define PREDICT_FALSE(x) (__builtin_expect(x, 0)) +#define PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) +#else +#define PREDICT_FALSE(x) x +#define PREDICT_TRUE(x) x +#endif + +// This is only used for recomputing the tag byte table used during +// decompression; for simplicity we just remove it from the open-source +// version (anyone who wants to regenerate it can just do the call +// themselves within main()). +#define DEFINE_bool(flag_name, default_value, description) \ + bool FLAGS_ ## flag_name = default_value +#define DECLARE_bool(flag_name) \ + extern bool FLAGS_ ## flag_name + +namespace snappy { + +static const uint32 kuint32max = static_cast(0xFFFFFFFF); +static const int64 kint64max = static_cast(0x7FFFFFFFFFFFFFFFLL); + +// Potentially unaligned loads and stores. + +// x86 and PowerPC can simply do these loads and stores native. + +#if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) + +#define UNALIGNED_LOAD16(_p) (*reinterpret_cast(_p)) +#define UNALIGNED_LOAD32(_p) (*reinterpret_cast(_p)) +#define UNALIGNED_LOAD64(_p) (*reinterpret_cast(_p)) + +#define UNALIGNED_STORE16(_p, _val) (*reinterpret_cast(_p) = (_val)) +#define UNALIGNED_STORE32(_p, _val) (*reinterpret_cast(_p) = (_val)) +#define UNALIGNED_STORE64(_p, _val) (*reinterpret_cast(_p) = (_val)) + +// ARMv7 and newer support native unaligned accesses, but only of 16-bit +// and 32-bit values (not 64-bit); older versions either raise a fatal signal, +// do an unaligned read and rotate the words around a bit, or do the reads very +// slowly (trip through kernel mode). There's no simple #define that says just +// “ARMv7 or higher”, so we have to filter away all ARMv5 and ARMv6 +// sub-architectures. +// +// This is a mess, but there's not much we can do about it. + +#elif defined(__arm__) && \ + !defined(__ARM_ARCH_4__) && \ + !defined(__ARM_ARCH_4T__) && \ + !defined(__ARM_ARCH_5__) && \ + !defined(__ARM_ARCH_5T__) && \ + !defined(__ARM_ARCH_5TE__) && \ + !defined(__ARM_ARCH_5TEJ__) && \ + !defined(__ARM_ARCH_6__) && \ + !defined(__ARM_ARCH_6J__) && \ + !defined(__ARM_ARCH_6K__) && \ + !defined(__ARM_ARCH_6Z__) && \ + !defined(__ARM_ARCH_6ZK__) && \ + !defined(__ARM_ARCH_6T2__) + +#define UNALIGNED_LOAD16(_p) (*reinterpret_cast(_p)) +#define UNALIGNED_LOAD32(_p) (*reinterpret_cast(_p)) + +#define UNALIGNED_STORE16(_p, _val) (*reinterpret_cast(_p) = (_val)) +#define UNALIGNED_STORE32(_p, _val) (*reinterpret_cast(_p) = (_val)) + +// TODO(user): NEON supports unaligned 64-bit loads and stores. +// See if that would be more efficient on platforms supporting it, +// at least for copies. + +inline uint64 UNALIGNED_LOAD64(const void *p) { + uint64 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline void UNALIGNED_STORE64(void *p, uint64 v) { + memcpy(p, &v, sizeof v); +} + +#else + +// These functions are provided for architectures that don't support +// unaligned loads and stores. + +inline uint16 UNALIGNED_LOAD16(const void *p) { + uint16 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline uint32 UNALIGNED_LOAD32(const void *p) { + uint32 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline uint64 UNALIGNED_LOAD64(const void *p) { + uint64 t; + memcpy(&t, p, sizeof t); + return t; +} + +inline void UNALIGNED_STORE16(void *p, uint16 v) { + memcpy(p, &v, sizeof v); +} + +inline void UNALIGNED_STORE32(void *p, uint32 v) { + memcpy(p, &v, sizeof v); +} + +inline void UNALIGNED_STORE64(void *p, uint64 v) { + memcpy(p, &v, sizeof v); +} + +#endif + +// This can be more efficient than UNALIGNED_LOAD64 + UNALIGNED_STORE64 +// on some platforms, in particular ARM. +inline void UnalignedCopy64(const void *src, void *dst) { + if (sizeof(void *) == 8) { + UNALIGNED_STORE64(dst, UNALIGNED_LOAD64(src)); + } else { + const char *src_char = reinterpret_cast(src); + char *dst_char = reinterpret_cast(dst); + + UNALIGNED_STORE32(dst_char, UNALIGNED_LOAD32(src_char)); + UNALIGNED_STORE32(dst_char + 4, UNALIGNED_LOAD32(src_char + 4)); + } +} + +// The following guarantees declaration of the byte swap functions. +#ifdef WORDS_BIGENDIAN + +#ifdef HAVE_SYS_BYTEORDER_H +#include +#endif + +#ifdef HAVE_SYS_ENDIAN_H +#include +#endif + +#ifdef _MSC_VER +#include +#define bswap_16(x) _byteswap_ushort(x) +#define bswap_32(x) _byteswap_ulong(x) +#define bswap_64(x) _byteswap_uint64(x) + +#elif defined(__APPLE__) +// Mac OS X / Darwin features +#include +#define bswap_16(x) OSSwapInt16(x) +#define bswap_32(x) OSSwapInt32(x) +#define bswap_64(x) OSSwapInt64(x) + +#elif defined(HAVE_BYTESWAP_H) +#include + +#elif defined(bswap32) +// FreeBSD defines bswap{16,32,64} in (already #included). +#define bswap_16(x) bswap16(x) +#define bswap_32(x) bswap32(x) +#define bswap_64(x) bswap64(x) + +#elif defined(BSWAP_64) +// Solaris 10 defines BSWAP_{16,32,64} in (already #included). +#define bswap_16(x) BSWAP_16(x) +#define bswap_32(x) BSWAP_32(x) +#define bswap_64(x) BSWAP_64(x) + +#else + +inline uint16 bswap_16(uint16 x) { + return (x << 8) | (x >> 8); +} + +inline uint32 bswap_32(uint32 x) { + x = ((x & 0xff00ff00UL) >> 8) | ((x & 0x00ff00ffUL) << 8); + return (x >> 16) | (x << 16); +} + +inline uint64 bswap_64(uint64 x) { + x = ((x & 0xff00ff00ff00ff00ULL) >> 8) | ((x & 0x00ff00ff00ff00ffULL) << 8); + x = ((x & 0xffff0000ffff0000ULL) >> 16) | ((x & 0x0000ffff0000ffffULL) << 16); + return (x >> 32) | (x << 32); +} + +#endif + +#endif // WORDS_BIGENDIAN + +// Convert to little-endian storage, opposite of network format. +// Convert x from host to little endian: x = LittleEndian.FromHost(x); +// convert x from little endian to host: x = LittleEndian.ToHost(x); +// +// Store values into unaligned memory converting to little endian order: +// LittleEndian.Store16(p, x); +// +// Load unaligned values stored in little endian converting to host order: +// x = LittleEndian.Load16(p); +class LittleEndian { + public: + // Conversion functions. +#ifdef WORDS_BIGENDIAN + + static uint16 FromHost16(uint16 x) { return bswap_16(x); } + static uint16 ToHost16(uint16 x) { return bswap_16(x); } + + static uint32 FromHost32(uint32 x) { return bswap_32(x); } + static uint32 ToHost32(uint32 x) { return bswap_32(x); } + + static bool IsLittleEndian() { return false; } + +#else // !defined(WORDS_BIGENDIAN) + + static uint16 FromHost16(uint16 x) { return x; } + static uint16 ToHost16(uint16 x) { return x; } + + static uint32 FromHost32(uint32 x) { return x; } + static uint32 ToHost32(uint32 x) { return x; } + + static bool IsLittleEndian() { return true; } + +#endif // !defined(WORDS_BIGENDIAN) + + // Functions to do unaligned loads and stores in little-endian order. + static uint16 Load16(const void *p) { + return ToHost16(UNALIGNED_LOAD16(p)); + } + + static void Store16(void *p, uint16 v) { + UNALIGNED_STORE16(p, FromHost16(v)); + } + + static uint32 Load32(const void *p) { + return ToHost32(UNALIGNED_LOAD32(p)); + } + + static void Store32(void *p, uint32 v) { + UNALIGNED_STORE32(p, FromHost32(v)); + } +}; + +// Some bit-manipulation functions. +class Bits { + public: + // Return floor(log2(n)) for positive integer n. Returns -1 iff n == 0. + static int Log2Floor(uint32 n); + + // Return the first set least / most significant bit, 0-indexed. Returns an + // undefined value if n == 0. FindLSBSetNonZero() is similar to ffs() except + // that it's 0-indexed. + static int FindLSBSetNonZero(uint32 n); + static int FindLSBSetNonZero64(uint64 n); + + private: + DISALLOW_COPY_AND_ASSIGN(Bits); +}; + +#ifdef HAVE_BUILTIN_CTZ + +inline int Bits::Log2Floor(uint32 n) { + return n == 0 ? -1 : 31 ^ __builtin_clz(n); +} + +inline int Bits::FindLSBSetNonZero(uint32 n) { + return __builtin_ctz(n); +} + +inline int Bits::FindLSBSetNonZero64(uint64 n) { + return __builtin_ctzll(n); +} + +#else // Portable versions. + +inline int Bits::Log2Floor(uint32 n) { + if (n == 0) + return -1; + int log = 0; + uint32 value = n; + for (int i = 4; i >= 0; --i) { + int shift = (1 << i); + uint32 x = value >> shift; + if (x != 0) { + value = x; + log += shift; + } + } + assert(value == 1); + return log; +} + +inline int Bits::FindLSBSetNonZero(uint32 n) { + int rc = 31; + for (int i = 4, shift = 1 << 4; i >= 0; --i) { + const uint32 x = n << shift; + if (x != 0) { + n = x; + rc -= shift; + } + shift >>= 1; + } + return rc; +} + +// FindLSBSetNonZero64() is defined in terms of FindLSBSetNonZero(). +inline int Bits::FindLSBSetNonZero64(uint64 n) { + const uint32 bottombits = static_cast(n); + if (bottombits == 0) { + // Bottom bits are zero, so scan in top bits + return 32 + FindLSBSetNonZero(static_cast(n >> 32)); + } else { + return FindLSBSetNonZero(bottombits); + } +} + +#endif // End portable versions. + +// Variable-length integer encoding. +class Varint { + public: + // Maximum lengths of varint encoding of uint32. + static const int kMax32 = 5; + + // Attempts to parse a varint32 from a prefix of the bytes in [ptr,limit-1]. + // Never reads a character at or beyond limit. If a valid/terminated varint32 + // was found in the range, stores it in *OUTPUT and returns a pointer just + // past the last byte of the varint32. Else returns NULL. On success, + // "result <= limit". + static const char* Parse32WithLimit(const char* ptr, const char* limit, + uint32* OUTPUT); + + // REQUIRES "ptr" points to a buffer of length sufficient to hold "v". + // EFFECTS Encodes "v" into "ptr" and returns a pointer to the + // byte just past the last encoded byte. + static char* Encode32(char* ptr, uint32 v); + + // EFFECTS Appends the varint representation of "value" to "*s". + static void Append32(string* s, uint32 value); +}; + +inline const char* Varint::Parse32WithLimit(const char* p, + const char* l, + uint32* OUTPUT) { + const unsigned char* ptr = reinterpret_cast(p); + const unsigned char* limit = reinterpret_cast(l); + uint32 b, result; + if (ptr >= limit) return NULL; + b = *(ptr++); result = b & 127; if (b < 128) goto done; + if (ptr >= limit) return NULL; + b = *(ptr++); result |= (b & 127) << 7; if (b < 128) goto done; + if (ptr >= limit) return NULL; + b = *(ptr++); result |= (b & 127) << 14; if (b < 128) goto done; + if (ptr >= limit) return NULL; + b = *(ptr++); result |= (b & 127) << 21; if (b < 128) goto done; + if (ptr >= limit) return NULL; + b = *(ptr++); result |= (b & 127) << 28; if (b < 16) goto done; + return NULL; // Value is too long to be a varint32 + done: + *OUTPUT = result; + return reinterpret_cast(ptr); +} + +inline char* Varint::Encode32(char* sptr, uint32 v) { + // Operate on characters as unsigneds + unsigned char* ptr = reinterpret_cast(sptr); + static const int B = 128; + if (v < (1<<7)) { + *(ptr++) = v; + } else if (v < (1<<14)) { + *(ptr++) = v | B; + *(ptr++) = v>>7; + } else if (v < (1<<21)) { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = v>>14; + } else if (v < (1<<28)) { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = (v>>14) | B; + *(ptr++) = v>>21; + } else { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = (v>>14) | B; + *(ptr++) = (v>>21) | B; + *(ptr++) = v>>28; + } + return reinterpret_cast(ptr); +} + +// If you know the internal layout of the std::string in use, you can +// replace this function with one that resizes the string without +// filling the new space with zeros (if applicable) -- +// it will be non-portable but faster. +inline void STLStringResizeUninitialized(string* s, size_t new_size) { + s->resize(new_size); +} + +// Return a mutable char* pointing to a string's internal buffer, +// which may not be null-terminated. Writing through this pointer will +// modify the string. +// +// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the +// next call to a string method that invalidates iterators. +// +// As of 2006-04, there is no standard-blessed way of getting a +// mutable reference to a string's internal buffer. However, issue 530 +// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-defects.html#530) +// proposes this as the method. It will officially be part of the standard +// for C++0x. This should already work on all current implementations. +inline char* string_as_array(string* str) { + return str->empty() ? NULL : &*str->begin(); +} + +} // namespace snappy + +#endif // UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-stubs-public.h b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-stubs-public.h new file mode 100644 index 00000000..ecda4398 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy-stubs-public.h @@ -0,0 +1,98 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Author: sesse@google.com (Steinar H. Gunderson) +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Various type stubs for the open-source version of Snappy. +// +// This file cannot include config.h, as it is included from snappy.h, +// which is a public header. Instead, snappy-stubs-public.h is generated by +// from snappy-stubs-public.h.in at configure time. + +#ifndef UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ +#define UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ + +#if 1 +#include +#endif + +#if 1 +#include +#endif + +#if 0 +#include +#endif + +#define SNAPPY_MAJOR 1 +#define SNAPPY_MINOR 1 +#define SNAPPY_PATCHLEVEL 1 +#define SNAPPY_VERSION \ + ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL) + +#include + +namespace snappy { + +#if 1 +typedef int8_t int8; +typedef uint8_t uint8; +typedef int16_t int16; +typedef uint16_t uint16; +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; +#else +typedef signed char int8; +typedef unsigned char uint8; +typedef short int16; +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; +typedef long long int64; +typedef unsigned long long uint64; +#endif + +typedef std::string string; + +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +#if !0 +// Windows does not have an iovec type, yet the concept is universally useful. +// It is simple to define it ourselves, so we put it inside our own namespace. +struct iovec { + void* iov_base; + size_t iov_len; +}; +#endif + +} // namespace snappy + +#endif // UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy.cc b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy.cc new file mode 100644 index 00000000..0bf80f91 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy.cc @@ -0,0 +1,1307 @@ +// Copyright 2005 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "snappy.h" +#include "snappy-internal.h" +#include "snappy-sinksource.h" +#include "snappy-lcb-msvc.h" // Added by libcouchbase + +#include + +#include +#include +#include + + +namespace snappy { + +// Any hash function will produce a valid compressed bitstream, but a good +// hash function reduces the number of collisions and thus yields better +// compression for compressible input, and more speed for incompressible +// input. Of course, it doesn't hurt if the hash function is reasonably fast +// either, as it gets called a lot. +static inline uint32 HashBytes(uint32 bytes, int shift) { + uint32 kMul = 0x1e35a7bd; + return (bytes * kMul) >> shift; +} +static inline uint32 Hash(const char* p, int shift) { + return HashBytes(UNALIGNED_LOAD32(p), shift); +} + +size_t MaxCompressedLength(size_t source_len) { + // Compressed data can be defined as: + // compressed := item* literal* + // item := literal* copy + // + // The trailing literal sequence has a space blowup of at most 62/60 + // since a literal of length 60 needs one tag byte + one extra byte + // for length information. + // + // Item blowup is trickier to measure. Suppose the "copy" op copies + // 4 bytes of data. Because of a special check in the encoding code, + // we produce a 4-byte copy only if the offset is < 65536. Therefore + // the copy op takes 3 bytes to encode, and this type of item leads + // to at most the 62/60 blowup for representing literals. + // + // Suppose the "copy" op copies 5 bytes of data. If the offset is big + // enough, it will take 5 bytes to encode the copy op. Therefore the + // worst case here is a one-byte literal followed by a five-byte copy. + // I.e., 6 bytes of input turn into 7 bytes of "compressed" data. + // + // This last factor dominates the blowup, so the final estimate is: + return 32 + source_len + source_len/6; +} + +enum { + LITERAL = 0, + COPY_1_BYTE_OFFSET = 1, // 3 bit length + 3 bits of offset in opcode + COPY_2_BYTE_OFFSET = 2, + COPY_4_BYTE_OFFSET = 3 +}; +static const int kMaximumTagLength = 5; // COPY_4_BYTE_OFFSET plus the actual offset. + +// Copy "len" bytes from "src" to "op", one byte at a time. Used for +// handling COPY operations where the input and output regions may +// overlap. For example, suppose: +// src == "ab" +// op == src + 2 +// len == 20 +// After IncrementalCopy(src, op, len), the result will have +// eleven copies of "ab" +// ababababababababababab +// Note that this does not match the semantics of either memcpy() +// or memmove(). +static inline void IncrementalCopy(const char* src, char* op, ssize_t len) { + assert(len > 0); + do { + *op++ = *src++; + } while (--len > 0); +} + +// Equivalent to IncrementalCopy except that it can write up to ten extra +// bytes after the end of the copy, and that it is faster. +// +// The main part of this loop is a simple copy of eight bytes at a time until +// we've copied (at least) the requested amount of bytes. However, if op and +// src are less than eight bytes apart (indicating a repeating pattern of +// length < 8), we first need to expand the pattern in order to get the correct +// results. For instance, if the buffer looks like this, with the eight-byte +// and patterns marked as intervals: +// +// abxxxxxxxxxxxx +// [------] src +// [------] op +// +// a single eight-byte copy from to will repeat the pattern once, +// after which we can move two bytes without moving : +// +// ababxxxxxxxxxx +// [------] src +// [------] op +// +// and repeat the exercise until the two no longer overlap. +// +// This allows us to do very well in the special case of one single byte +// repeated many times, without taking a big hit for more general cases. +// +// The worst case of extra writing past the end of the match occurs when +// op - src == 1 and len == 1; the last copy will read from byte positions +// [0..7] and write to [4..11], whereas it was only supposed to write to +// position 1. Thus, ten excess bytes. + +namespace { + +const int kMaxIncrementCopyOverflow = 10; + +inline void IncrementalCopyFastPath(const char* src, char* op, ssize_t len) { + while (op - src < 8) { + UnalignedCopy64(src, op); + len -= op - src; + op += op - src; + } + while (len > 0) { + UnalignedCopy64(src, op); + src += 8; + op += 8; + len -= 8; + } +} + +} // namespace + +static inline char* EmitLiteral(char* op, + const char* literal, + int len, + bool allow_fast_path) { + int n = len - 1; // Zero-length literals are disallowed + if (n < 60) { + // Fits in tag byte + *op++ = LITERAL | (n << 2); + + // The vast majority of copies are below 16 bytes, for which a + // call to memcpy is overkill. This fast path can sometimes + // copy up to 15 bytes too much, but that is okay in the + // main loop, since we have a bit to go on for both sides: + // + // - The input will always have kInputMarginBytes = 15 extra + // available bytes, as long as we're in the main loop, and + // if not, allow_fast_path = false. + // - The output will always have 32 spare bytes (see + // MaxCompressedLength). + if (allow_fast_path && len <= 16) { + UnalignedCopy64(literal, op); + UnalignedCopy64(literal + 8, op + 8); + return op + len; + } + } else { + // Encode in upcoming bytes + char* base = op; + int count = 0; + op++; + while (n > 0) { + *op++ = n & 0xff; + n >>= 8; + count++; + } + assert(count >= 1); + assert(count <= 4); + *base = LITERAL | ((59+count) << 2); + } + memcpy(op, literal, len); + return op + len; +} + +static inline char* EmitCopyLessThan64(char* op, size_t offset, int len) { + assert(len <= 64); + assert(len >= 4); + assert(offset < 65536); + + if ((len < 12) && (offset < 2048)) { + size_t len_minus_4 = len - 4; + assert(len_minus_4 < 8); // Must fit in 3 bits + *op++ = COPY_1_BYTE_OFFSET + ((len_minus_4) << 2) + ((offset >> 8) << 5); + *op++ = offset & 0xff; + } else { + *op++ = COPY_2_BYTE_OFFSET + ((len-1) << 2); + LittleEndian::Store16(op, offset); + op += 2; + } + return op; +} + +static inline char* EmitCopy(char* op, size_t offset, int len) { + // Emit 64 byte copies but make sure to keep at least four bytes reserved + while (len >= 68) { + op = EmitCopyLessThan64(op, offset, 64); + len -= 64; + } + + // Emit an extra 60 byte copy if have too much data to fit in one copy + if (len > 64) { + op = EmitCopyLessThan64(op, offset, 60); + len -= 60; + } + + // Emit remainder + op = EmitCopyLessThan64(op, offset, len); + return op; +} + + +bool GetUncompressedLength(const char* start, size_t n, size_t* result) { + uint32 v = 0; + const char* limit = start + n; + if (Varint::Parse32WithLimit(start, limit, &v) != NULL) { + *result = v; + return true; + } else { + return false; + } +} + +namespace internal { +uint16* WorkingMemory::GetHashTable(size_t input_size, int* table_size) { + // Use smaller hash table when input.size() is smaller, since we + // fill the table, incurring O(hash table size) overhead for + // compression, and if the input is short, we won't need that + // many hash table entries anyway. + assert(kMaxHashTableSize >= 256); + size_t htsize = 256; + while (htsize < kMaxHashTableSize && htsize < input_size) { + htsize <<= 1; + } + + uint16* table; + if (htsize <= ARRAYSIZE(small_table_)) { + table = small_table_; + } else { + if (large_table_ == NULL) { + large_table_ = new uint16[kMaxHashTableSize]; + } + table = large_table_; + } + + *table_size = htsize; + memset(table, 0, htsize * sizeof(*table)); + return table; +} +} // end namespace internal + +// For 0 <= offset <= 4, GetUint32AtOffset(GetEightBytesAt(p), offset) will +// equal UNALIGNED_LOAD32(p + offset). Motivation: On x86-64 hardware we have +// empirically found that overlapping loads such as +// UNALIGNED_LOAD32(p) ... UNALIGNED_LOAD32(p+1) ... UNALIGNED_LOAD32(p+2) +// are slower than UNALIGNED_LOAD64(p) followed by shifts and casts to uint32. +// +// We have different versions for 64- and 32-bit; ideally we would avoid the +// two functions and just inline the UNALIGNED_LOAD64 call into +// GetUint32AtOffset, but GCC (at least not as of 4.6) is seemingly not clever +// enough to avoid loading the value multiple times then. For 64-bit, the load +// is done when GetEightBytesAt() is called, whereas for 32-bit, the load is +// done at GetUint32AtOffset() time. + +#ifdef ARCH_K8 + +typedef uint64 EightBytesReference; + +static inline EightBytesReference GetEightBytesAt(const char* ptr) { + return UNALIGNED_LOAD64(ptr); +} + +static inline uint32 GetUint32AtOffset(uint64 v, int offset) { + assert(offset >= 0); + assert(offset <= 4); + return v >> (LittleEndian::IsLittleEndian() ? 8 * offset : 32 - 8 * offset); +} + +#else + +typedef const char* EightBytesReference; + +static inline EightBytesReference GetEightBytesAt(const char* ptr) { + return ptr; +} + +static inline uint32 GetUint32AtOffset(const char* v, int offset) { + assert(offset >= 0); + assert(offset <= 4); + return UNALIGNED_LOAD32(v + offset); +} + +#endif + +// Flat array compression that does not emit the "uncompressed length" +// prefix. Compresses "input" string to the "*op" buffer. +// +// REQUIRES: "input" is at most "kBlockSize" bytes long. +// REQUIRES: "op" points to an array of memory that is at least +// "MaxCompressedLength(input.size())" in size. +// REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero. +// REQUIRES: "table_size" is a power of two +// +// Returns an "end" pointer into "op" buffer. +// "end - op" is the compressed size of "input". +namespace internal { +char* CompressFragment(const char* input, + size_t input_size, + char* op, + uint16* table, + const int table_size) { + // "ip" is the input pointer, and "op" is the output pointer. + const char* ip = input; + assert(input_size <= kBlockSize); + assert((table_size & (table_size - 1)) == 0); // table must be power of two + const int shift = 32 - Bits::Log2Floor(table_size); + assert(static_cast(kuint32max >> shift) == table_size - 1); + const char* ip_end = input + input_size; + const char* base_ip = ip; + // Bytes in [next_emit, ip) will be emitted as literal bytes. Or + // [next_emit, ip_end) after the main loop. + const char* next_emit = ip; + + const size_t kInputMarginBytes = 15; + if (PREDICT_TRUE(input_size >= kInputMarginBytes)) { + const char* ip_limit = input + input_size - kInputMarginBytes; + + for (uint32 next_hash = Hash(++ip, shift); ; ) { + assert(next_emit < ip); + // The body of this loop calls EmitLiteral once and then EmitCopy one or + // more times. (The exception is that when we're close to exhausting + // the input we goto emit_remainder.) + // + // In the first iteration of this loop we're just starting, so + // there's nothing to copy, so calling EmitLiteral once is + // necessary. And we only start a new iteration when the + // current iteration has determined that a call to EmitLiteral will + // precede the next call to EmitCopy (if any). + // + // Step 1: Scan forward in the input looking for a 4-byte-long match. + // If we get close to exhausting the input then goto emit_remainder. + // + // Heuristic match skipping: If 32 bytes are scanned with no matches + // found, start looking only at every other byte. If 32 more bytes are + // scanned, look at every third byte, etc.. When a match is found, + // immediately go back to looking at every byte. This is a small loss + // (~5% performance, ~0.1% density) for compressible data due to more + // bookkeeping, but for non-compressible data (such as JPEG) it's a huge + // win since the compressor quickly "realizes" the data is incompressible + // and doesn't bother looking for matches everywhere. + // + // The "skip" variable keeps track of how many bytes there are since the + // last match; dividing it by 32 (ie. right-shifting by five) gives the + // number of bytes to move ahead for each iteration. + uint32 skip = 32; + + const char* next_ip = ip; + const char* candidate; + do { + ip = next_ip; + uint32 hash = next_hash; + assert(hash == Hash(ip, shift)); + uint32 bytes_between_hash_lookups = skip++ >> 5; + next_ip = ip + bytes_between_hash_lookups; + if (PREDICT_FALSE(next_ip > ip_limit)) { + goto emit_remainder; + } + next_hash = Hash(next_ip, shift); + candidate = base_ip + table[hash]; + assert(candidate >= base_ip); + assert(candidate < ip); + + table[hash] = ip - base_ip; + } while (PREDICT_TRUE(UNALIGNED_LOAD32(ip) != + UNALIGNED_LOAD32(candidate))); + + // Step 2: A 4-byte match has been found. We'll later see if more + // than 4 bytes match. But, prior to the match, input + // bytes [next_emit, ip) are unmatched. Emit them as "literal bytes." + assert(next_emit + 16 <= ip_end); + op = EmitLiteral(op, next_emit, ip - next_emit, true); + + // Step 3: Call EmitCopy, and then see if another EmitCopy could + // be our next move. Repeat until we find no match for the + // input immediately after what was consumed by the last EmitCopy call. + // + // If we exit this loop normally then we need to call EmitLiteral next, + // though we don't yet know how big the literal will be. We handle that + // by proceeding to the next iteration of the main loop. We also can exit + // this loop via goto if we get close to exhausting the input. + EightBytesReference input_bytes; + uint32 candidate_bytes = 0; + + do { + // We have a 4-byte match at ip, and no need to emit any + // "literal bytes" prior to ip. + const char* base = ip; + int matched = 4 + FindMatchLength(candidate + 4, ip + 4, ip_end); + ip += matched; + size_t offset = base - candidate; + assert(0 == memcmp(base, candidate, matched)); + op = EmitCopy(op, offset, matched); + // We could immediately start working at ip now, but to improve + // compression we first update table[Hash(ip - 1, ...)]. + const char* insert_tail = ip - 1; + next_emit = ip; + if (PREDICT_FALSE(ip >= ip_limit)) { + goto emit_remainder; + } + input_bytes = GetEightBytesAt(insert_tail); + uint32 prev_hash = HashBytes(GetUint32AtOffset(input_bytes, 0), shift); + table[prev_hash] = ip - base_ip - 1; + uint32 cur_hash = HashBytes(GetUint32AtOffset(input_bytes, 1), shift); + candidate = base_ip + table[cur_hash]; + candidate_bytes = UNALIGNED_LOAD32(candidate); + table[cur_hash] = ip - base_ip; + } while (GetUint32AtOffset(input_bytes, 1) == candidate_bytes); + + next_hash = HashBytes(GetUint32AtOffset(input_bytes, 2), shift); + ++ip; + } + } + + emit_remainder: + // Emit the remaining bytes as a literal + if (next_emit < ip_end) { + op = EmitLiteral(op, next_emit, ip_end - next_emit, false); + } + + return op; +} +} // end namespace internal + +// Signature of output types needed by decompression code. +// The decompression code is templatized on a type that obeys this +// signature so that we do not pay virtual function call overhead in +// the middle of a tight decompression loop. +// +// class DecompressionWriter { +// public: +// // Called before decompression +// void SetExpectedLength(size_t length); +// +// // Called after decompression +// bool CheckLength() const; +// +// // Called repeatedly during decompression +// bool Append(const char* ip, size_t length); +// bool AppendFromSelf(uint32 offset, size_t length); +// +// // The rules for how TryFastAppend differs from Append are somewhat +// // convoluted: +// // +// // - TryFastAppend is allowed to decline (return false) at any +// // time, for any reason -- just "return false" would be +// // a perfectly legal implementation of TryFastAppend. +// // The intention is for TryFastAppend to allow a fast path +// // in the common case of a small append. +// // - TryFastAppend is allowed to read up to bytes +// // from the input buffer, whereas Append is allowed to read +// // . However, if it returns true, it must leave +// // at least five (kMaximumTagLength) bytes in the input buffer +// // afterwards, so that there is always enough space to read the +// // next tag without checking for a refill. +// // - TryFastAppend must always return decline (return false) +// // if is 61 or more, as in this case the literal length is not +// // decoded fully. In practice, this should not be a big problem, +// // as it is unlikely that one would implement a fast path accepting +// // this much data. +// // +// bool TryFastAppend(const char* ip, size_t available, size_t length); +// }; + +// ----------------------------------------------------------------------- +// Lookup table for decompression code. Generated by ComputeTable() below. +// ----------------------------------------------------------------------- + +// Mapping from i in range [0,4] to a mask to extract the bottom 8*i bits +static const uint32 wordmask[] = { + 0u, 0xffu, 0xffffu, 0xffffffu, 0xffffffffu +}; + +// Data stored per entry in lookup table: +// Range Bits-used Description +// ------------------------------------ +// 1..64 0..7 Literal/copy length encoded in opcode byte +// 0..7 8..10 Copy offset encoded in opcode byte / 256 +// 0..4 11..13 Extra bytes after opcode +// +// We use eight bits for the length even though 7 would have sufficed +// because of efficiency reasons: +// (1) Extracting a byte is faster than a bit-field +// (2) It properly aligns copy offset so we do not need a <<8 +static const uint16 char_table[256] = { + 0x0001, 0x0804, 0x1001, 0x2001, 0x0002, 0x0805, 0x1002, 0x2002, + 0x0003, 0x0806, 0x1003, 0x2003, 0x0004, 0x0807, 0x1004, 0x2004, + 0x0005, 0x0808, 0x1005, 0x2005, 0x0006, 0x0809, 0x1006, 0x2006, + 0x0007, 0x080a, 0x1007, 0x2007, 0x0008, 0x080b, 0x1008, 0x2008, + 0x0009, 0x0904, 0x1009, 0x2009, 0x000a, 0x0905, 0x100a, 0x200a, + 0x000b, 0x0906, 0x100b, 0x200b, 0x000c, 0x0907, 0x100c, 0x200c, + 0x000d, 0x0908, 0x100d, 0x200d, 0x000e, 0x0909, 0x100e, 0x200e, + 0x000f, 0x090a, 0x100f, 0x200f, 0x0010, 0x090b, 0x1010, 0x2010, + 0x0011, 0x0a04, 0x1011, 0x2011, 0x0012, 0x0a05, 0x1012, 0x2012, + 0x0013, 0x0a06, 0x1013, 0x2013, 0x0014, 0x0a07, 0x1014, 0x2014, + 0x0015, 0x0a08, 0x1015, 0x2015, 0x0016, 0x0a09, 0x1016, 0x2016, + 0x0017, 0x0a0a, 0x1017, 0x2017, 0x0018, 0x0a0b, 0x1018, 0x2018, + 0x0019, 0x0b04, 0x1019, 0x2019, 0x001a, 0x0b05, 0x101a, 0x201a, + 0x001b, 0x0b06, 0x101b, 0x201b, 0x001c, 0x0b07, 0x101c, 0x201c, + 0x001d, 0x0b08, 0x101d, 0x201d, 0x001e, 0x0b09, 0x101e, 0x201e, + 0x001f, 0x0b0a, 0x101f, 0x201f, 0x0020, 0x0b0b, 0x1020, 0x2020, + 0x0021, 0x0c04, 0x1021, 0x2021, 0x0022, 0x0c05, 0x1022, 0x2022, + 0x0023, 0x0c06, 0x1023, 0x2023, 0x0024, 0x0c07, 0x1024, 0x2024, + 0x0025, 0x0c08, 0x1025, 0x2025, 0x0026, 0x0c09, 0x1026, 0x2026, + 0x0027, 0x0c0a, 0x1027, 0x2027, 0x0028, 0x0c0b, 0x1028, 0x2028, + 0x0029, 0x0d04, 0x1029, 0x2029, 0x002a, 0x0d05, 0x102a, 0x202a, + 0x002b, 0x0d06, 0x102b, 0x202b, 0x002c, 0x0d07, 0x102c, 0x202c, + 0x002d, 0x0d08, 0x102d, 0x202d, 0x002e, 0x0d09, 0x102e, 0x202e, + 0x002f, 0x0d0a, 0x102f, 0x202f, 0x0030, 0x0d0b, 0x1030, 0x2030, + 0x0031, 0x0e04, 0x1031, 0x2031, 0x0032, 0x0e05, 0x1032, 0x2032, + 0x0033, 0x0e06, 0x1033, 0x2033, 0x0034, 0x0e07, 0x1034, 0x2034, + 0x0035, 0x0e08, 0x1035, 0x2035, 0x0036, 0x0e09, 0x1036, 0x2036, + 0x0037, 0x0e0a, 0x1037, 0x2037, 0x0038, 0x0e0b, 0x1038, 0x2038, + 0x0039, 0x0f04, 0x1039, 0x2039, 0x003a, 0x0f05, 0x103a, 0x203a, + 0x003b, 0x0f06, 0x103b, 0x203b, 0x003c, 0x0f07, 0x103c, 0x203c, + 0x0801, 0x0f08, 0x103d, 0x203d, 0x1001, 0x0f09, 0x103e, 0x203e, + 0x1801, 0x0f0a, 0x103f, 0x203f, 0x2001, 0x0f0b, 0x1040, 0x2040 +}; + +// In debug mode, allow optional computation of the table at startup. +// Also, check that the decompression table is correct. +#ifndef NDEBUG +DEFINE_bool(snappy_dump_decompression_table, false, + "If true, we print the decompression table at startup."); + +static uint16 MakeEntry(unsigned int extra, + unsigned int len, + unsigned int copy_offset) { + // Check that all of the fields fit within the allocated space + assert(extra == (extra & 0x7)); // At most 3 bits + assert(copy_offset == (copy_offset & 0x7)); // At most 3 bits + assert(len == (len & 0x7f)); // At most 7 bits + return len | (copy_offset << 8) | (extra << 11); +} + +static void ComputeTable() { + uint16 dst[256]; + + // Place invalid entries in all places to detect missing initialization + int assigned = 0; + for (int i = 0; i < 256; i++) { + dst[i] = 0xffff; + } + + // Small LITERAL entries. We store (len-1) in the top 6 bits. + for (unsigned int len = 1; len <= 60; len++) { + dst[LITERAL | ((len-1) << 2)] = MakeEntry(0, len, 0); + assigned++; + } + + // Large LITERAL entries. We use 60..63 in the high 6 bits to + // encode the number of bytes of length info that follow the opcode. + for (unsigned int extra_bytes = 1; extra_bytes <= 4; extra_bytes++) { + // We set the length field in the lookup table to 1 because extra + // bytes encode len-1. + dst[LITERAL | ((extra_bytes+59) << 2)] = MakeEntry(extra_bytes, 1, 0); + assigned++; + } + + // COPY_1_BYTE_OFFSET. + // + // The tag byte in the compressed data stores len-4 in 3 bits, and + // offset/256 in 5 bits. offset%256 is stored in the next byte. + // + // This format is used for length in range [4..11] and offset in + // range [0..2047] + for (unsigned int len = 4; len < 12; len++) { + for (unsigned int offset = 0; offset < 2048; offset += 256) { + dst[COPY_1_BYTE_OFFSET | ((len-4)<<2) | ((offset>>8)<<5)] = + MakeEntry(1, len, offset>>8); + assigned++; + } + } + + // COPY_2_BYTE_OFFSET. + // Tag contains len-1 in top 6 bits, and offset in next two bytes. + for (unsigned int len = 1; len <= 64; len++) { + dst[COPY_2_BYTE_OFFSET | ((len-1)<<2)] = MakeEntry(2, len, 0); + assigned++; + } + + // COPY_4_BYTE_OFFSET. + // Tag contents len-1 in top 6 bits, and offset in next four bytes. + for (unsigned int len = 1; len <= 64; len++) { + dst[COPY_4_BYTE_OFFSET | ((len-1)<<2)] = MakeEntry(4, len, 0); + assigned++; + } + + // Check that each entry was initialized exactly once. + if (assigned != 256) { + fprintf(stderr, "ComputeTable: assigned only %d of 256\n", assigned); + abort(); + } + for (int i = 0; i < 256; i++) { + if (dst[i] == 0xffff) { + fprintf(stderr, "ComputeTable: did not assign byte %d\n", i); + abort(); + } + } + + if (FLAGS_snappy_dump_decompression_table) { + printf("static const uint16 char_table[256] = {\n "); + for (int i = 0; i < 256; i++) { + printf("0x%04x%s", + dst[i], + ((i == 255) ? "\n" : (((i%8) == 7) ? ",\n " : ", "))); + } + printf("};\n"); + } + + // Check that computed table matched recorded table + for (int i = 0; i < 256; i++) { + if (dst[i] != char_table[i]) { + fprintf(stderr, "ComputeTable: byte %d: computed (%x), expect (%x)\n", + i, static_cast(dst[i]), static_cast(char_table[i])); + abort(); + } + } +} +#endif /* !NDEBUG */ + +// Helper class for decompression +class SnappyDecompressor { + private: + Source* reader_; // Underlying source of bytes to decompress + const char* ip_; // Points to next buffered byte + const char* ip_limit_; // Points just past buffered bytes + uint32 peeked_; // Bytes peeked from reader (need to skip) + bool eof_; // Hit end of input without an error? + char scratch_[kMaximumTagLength]; // See RefillTag(). + + // Ensure that all of the tag metadata for the next tag is available + // in [ip_..ip_limit_-1]. Also ensures that [ip,ip+4] is readable even + // if (ip_limit_ - ip_ < 5). + // + // Returns true on success, false on error or end of input. + bool RefillTag(); + + public: + explicit SnappyDecompressor(Source* reader) + : reader_(reader), + ip_(NULL), + ip_limit_(NULL), + peeked_(0), + eof_(false) { + } + + ~SnappyDecompressor() { + // Advance past any bytes we peeked at from the reader + reader_->Skip(peeked_); + } + + // Returns true iff we have hit the end of the input without an error. + bool eof() const { + return eof_; + } + + // Read the uncompressed length stored at the start of the compressed data. + // On succcess, stores the length in *result and returns true. + // On failure, returns false. + bool ReadUncompressedLength(uint32* result) { + assert(ip_ == NULL); // Must not have read anything yet + // Length is encoded in 1..5 bytes + *result = 0; + uint32 shift = 0; + while (true) { + if (shift >= 32) return false; + size_t n; + const char* ip = reader_->Peek(&n); + if (n == 0) return false; + const unsigned char c = *(reinterpret_cast(ip)); + reader_->Skip(1); + *result |= static_cast(c & 0x7f) << shift; + if (c < 128) { + break; + } + shift += 7; + } + return true; + } + + // Process the next item found in the input. + // Returns true if successful, false on error or end of input. + template + void DecompressAllTags(Writer* writer) { + const char* ip = ip_; + + // We could have put this refill fragment only at the beginning of the loop. + // However, duplicating it at the end of each branch gives the compiler more + // scope to optimize the expression based on the local + // context, which overall increases speed. + #define MAYBE_REFILL() \ + if (ip_limit_ - ip < kMaximumTagLength) { \ + ip_ = ip; \ + if (!RefillTag()) return; \ + ip = ip_; \ + } + + MAYBE_REFILL(); + for ( ;; ) { + const unsigned char c = *(reinterpret_cast(ip++)); + + if ((c & 0x3) == LITERAL) { + size_t literal_length = (c >> 2) + 1u; + if (writer->TryFastAppend(ip, ip_limit_ - ip, literal_length)) { + assert(literal_length < 61); + ip += literal_length; + // NOTE(user): There is no MAYBE_REFILL() here, as TryFastAppend() + // will not return true unless there's already at least five spare + // bytes in addition to the literal. + continue; + } + if (PREDICT_FALSE(literal_length >= 61)) { + // Long literal. + const size_t literal_length_length = literal_length - 60; + literal_length = + (LittleEndian::Load32(ip) & wordmask[literal_length_length]) + 1; + ip += literal_length_length; + } + + size_t avail = ip_limit_ - ip; + while (avail < literal_length) { + if (!writer->Append(ip, avail)) return; + literal_length -= avail; + reader_->Skip(peeked_); + size_t n; + ip = reader_->Peek(&n); + avail = n; + peeked_ = avail; + if (avail == 0) return; // Premature end of input + ip_limit_ = ip + avail; + } + if (!writer->Append(ip, literal_length)) { + return; + } + ip += literal_length; + MAYBE_REFILL(); + } else { + const uint32 entry = char_table[c]; + const uint32 trailer = LittleEndian::Load32(ip) & wordmask[entry >> 11]; + const uint32 length = entry & 0xff; + ip += entry >> 11; + + // copy_offset/256 is encoded in bits 8..10. By just fetching + // those bits, we get copy_offset (since the bit-field starts at + // bit 8). + const uint32 copy_offset = entry & 0x700; + if (!writer->AppendFromSelf(copy_offset + trailer, length)) { + return; + } + MAYBE_REFILL(); + } + } + +#undef MAYBE_REFILL + } +}; + +bool SnappyDecompressor::RefillTag() { + const char* ip = ip_; + if (ip == ip_limit_) { + // Fetch a new fragment from the reader + reader_->Skip(peeked_); // All peeked bytes are used up + size_t n; + ip = reader_->Peek(&n); + peeked_ = n; + if (n == 0) { + eof_ = true; + return false; + } + ip_limit_ = ip + n; + } + + // Read the tag character + assert(ip < ip_limit_); + const unsigned char c = *(reinterpret_cast(ip)); + const uint32 entry = char_table[c]; + const uint32 needed = (entry >> 11) + 1; // +1 byte for 'c' + assert(needed <= sizeof(scratch_)); + + // Read more bytes from reader if needed + uint32 nbuf = ip_limit_ - ip; + if (nbuf < needed) { + // Stitch together bytes from ip and reader to form the word + // contents. We store the needed bytes in "scratch_". They + // will be consumed immediately by the caller since we do not + // read more than we need. + memmove(scratch_, ip, nbuf); + reader_->Skip(peeked_); // All peeked bytes are used up + peeked_ = 0; + while (nbuf < needed) { + size_t length; + const char* src = reader_->Peek(&length); + if (length == 0) return false; + uint32 to_add = min(needed - nbuf, length); + memcpy(scratch_ + nbuf, src, to_add); + nbuf += to_add; + reader_->Skip(to_add); + } + assert(nbuf == needed); + ip_ = scratch_; + ip_limit_ = scratch_ + needed; + } else if (nbuf < kMaximumTagLength) { + // Have enough bytes, but move into scratch_ so that we do not + // read past end of input + memmove(scratch_, ip, nbuf); + reader_->Skip(peeked_); // All peeked bytes are used up + peeked_ = 0; + ip_ = scratch_; + ip_limit_ = scratch_ + nbuf; + } else { + // Pass pointer to buffer returned by reader_. + ip_ = ip; + } + return true; +} + +template +static bool InternalUncompress(Source* r, Writer* writer) { + // Read the uncompressed length from the front of the compressed input + SnappyDecompressor decompressor(r); + uint32 uncompressed_len = 0; + if (!decompressor.ReadUncompressedLength(&uncompressed_len)) return false; + return InternalUncompressAllTags(&decompressor, writer, uncompressed_len); +} + +template +static bool InternalUncompressAllTags(SnappyDecompressor* decompressor, + Writer* writer, + uint32 uncompressed_len) { + writer->SetExpectedLength(uncompressed_len); + + // Process the entire input + decompressor->DecompressAllTags(writer); + return (decompressor->eof() && writer->CheckLength()); +} + +bool GetUncompressedLength(Source* source, uint32* result) { + SnappyDecompressor decompressor(source); + return decompressor.ReadUncompressedLength(result); +} + +size_t Compress(Source* reader, Sink* writer) { + size_t written = 0; + size_t N = reader->Available(); + char ulength[Varint::kMax32]; + char* p = Varint::Encode32(ulength, N); + writer->Append(ulength, p-ulength); + written += (p - ulength); + + internal::WorkingMemory wmem; + char* scratch = NULL; + char* scratch_output = NULL; + + while (N > 0) { + // Get next block to compress (without copying if possible) + size_t fragment_size; + const char* fragment = reader->Peek(&fragment_size); + assert(fragment_size != 0); // premature end of input + const size_t num_to_read = min(N, kBlockSize); + size_t bytes_read = fragment_size; + + size_t pending_advance = 0; + if (bytes_read >= num_to_read) { + // Buffer returned by reader is large enough + pending_advance = num_to_read; + fragment_size = num_to_read; + } else { + // Read into scratch buffer + if (scratch == NULL) { + // If this is the last iteration, we want to allocate N bytes + // of space, otherwise the max possible kBlockSize space. + // num_to_read contains exactly the correct value + scratch = new char[num_to_read]; + } + memcpy(scratch, fragment, bytes_read); + reader->Skip(bytes_read); + + while (bytes_read < num_to_read) { + fragment = reader->Peek(&fragment_size); + size_t n = min(fragment_size, num_to_read - bytes_read); + memcpy(scratch + bytes_read, fragment, n); + bytes_read += n; + reader->Skip(n); + } + assert(bytes_read == num_to_read); + fragment = scratch; + fragment_size = num_to_read; + } + assert(fragment_size == num_to_read); + + // Get encoding table for compression + int table_size; + uint16* table = wmem.GetHashTable(num_to_read, &table_size); + + // Compress input_fragment and append to dest + const int max_output = MaxCompressedLength(num_to_read); + + // Need a scratch buffer for the output, in case the byte sink doesn't + // have room for us directly. + if (scratch_output == NULL) { + scratch_output = new char[max_output]; + } else { + // Since we encode kBlockSize regions followed by a region + // which is <= kBlockSize in length, a previously allocated + // scratch_output[] region is big enough for this iteration. + } + char* dest = writer->GetAppendBuffer(max_output, scratch_output); + char* end = internal::CompressFragment(fragment, fragment_size, + dest, table, table_size); + writer->Append(dest, end - dest); + written += (end - dest); + + N -= num_to_read; + reader->Skip(pending_advance); + } + + delete[] scratch; + delete[] scratch_output; + + return written; +} + +// ----------------------------------------------------------------------- +// IOVec interfaces +// ----------------------------------------------------------------------- + +// A type that writes to an iovec. +// Note that this is not a "ByteSink", but a type that matches the +// Writer template argument to SnappyDecompressor::DecompressAllTags(). +class SnappyIOVecWriter { + private: + const struct iovec* output_iov_; + const size_t output_iov_count_; + + // We are currently writing into output_iov_[curr_iov_index_]. + int curr_iov_index_; + + // Bytes written to output_iov_[curr_iov_index_] so far. + size_t curr_iov_written_; + + // Total bytes decompressed into output_iov_ so far. + size_t total_written_; + + // Maximum number of bytes that will be decompressed into output_iov_. + size_t output_limit_; + + inline char* GetIOVecPointer(int index, size_t offset) { + return reinterpret_cast(output_iov_[index].iov_base) + + offset; + } + + public: + // Does not take ownership of iov. iov must be valid during the + // entire lifetime of the SnappyIOVecWriter. + inline SnappyIOVecWriter(const struct iovec* iov, size_t iov_count) + : output_iov_(iov), + output_iov_count_(iov_count), + curr_iov_index_(0), + curr_iov_written_(0), + total_written_(0), + output_limit_(-1) { + } + + inline void SetExpectedLength(size_t len) { + output_limit_ = len; + } + + inline bool CheckLength() const { + return total_written_ == output_limit_; + } + + inline bool Append(const char* ip, size_t len) { + if (total_written_ + len > output_limit_) { + return false; + } + + while (len > 0) { + assert(curr_iov_written_ <= output_iov_[curr_iov_index_].iov_len); + if (curr_iov_written_ >= output_iov_[curr_iov_index_].iov_len) { + // This iovec is full. Go to the next one. + if (curr_iov_index_ + 1 >= output_iov_count_) { + return false; + } + curr_iov_written_ = 0; + ++curr_iov_index_; + } + + const size_t to_write = std::min( + len, output_iov_[curr_iov_index_].iov_len - curr_iov_written_); + memcpy(GetIOVecPointer(curr_iov_index_, curr_iov_written_), + ip, + to_write); + curr_iov_written_ += to_write; + total_written_ += to_write; + ip += to_write; + len -= to_write; + } + + return true; + } + + inline bool TryFastAppend(const char* ip, size_t available, size_t len) { + const size_t space_left = output_limit_ - total_written_; + if (len <= 16 && available >= 16 + kMaximumTagLength && space_left >= 16 && + output_iov_[curr_iov_index_].iov_len - curr_iov_written_ >= 16) { + // Fast path, used for the majority (about 95%) of invocations. + char* ptr = GetIOVecPointer(curr_iov_index_, curr_iov_written_); + UnalignedCopy64(ip, ptr); + UnalignedCopy64(ip + 8, ptr + 8); + curr_iov_written_ += len; + total_written_ += len; + return true; + } + + return false; + } + + inline bool AppendFromSelf(size_t offset, size_t len) { + if (offset > total_written_ || offset == 0) { + return false; + } + const size_t space_left = output_limit_ - total_written_; + if (len > space_left) { + return false; + } + + // Locate the iovec from which we need to start the copy. + int from_iov_index = curr_iov_index_; + size_t from_iov_offset = curr_iov_written_; + while (offset > 0) { + if (from_iov_offset >= offset) { + from_iov_offset -= offset; + break; + } + + offset -= from_iov_offset; + --from_iov_index; + assert(from_iov_index >= 0); + from_iov_offset = output_iov_[from_iov_index].iov_len; + } + + // Copy bytes starting from the iovec pointed to by from_iov_index to + // the current iovec. + while (len > 0) { + assert(from_iov_index <= curr_iov_index_); + if (from_iov_index != curr_iov_index_) { + const size_t to_copy = std::min( + output_iov_[from_iov_index].iov_len - from_iov_offset, + len); + Append(GetIOVecPointer(from_iov_index, from_iov_offset), to_copy); + len -= to_copy; + if (len > 0) { + ++from_iov_index; + from_iov_offset = 0; + } + } else { + assert(curr_iov_written_ <= output_iov_[curr_iov_index_].iov_len); + size_t to_copy = std::min(output_iov_[curr_iov_index_].iov_len - + curr_iov_written_, + len); + if (to_copy == 0) { + // This iovec is full. Go to the next one. + if (curr_iov_index_ + 1 >= output_iov_count_) { + return false; + } + ++curr_iov_index_; + curr_iov_written_ = 0; + continue; + } + if (to_copy > len) { + to_copy = len; + } + IncrementalCopy(GetIOVecPointer(from_iov_index, from_iov_offset), + GetIOVecPointer(curr_iov_index_, curr_iov_written_), + to_copy); + curr_iov_written_ += to_copy; + from_iov_offset += to_copy; + total_written_ += to_copy; + len -= to_copy; + } + } + + return true; + } + +}; + +bool RawUncompressToIOVec(const char* compressed, size_t compressed_length, + const struct iovec* iov, size_t iov_cnt) { + ByteArraySource reader(compressed, compressed_length); + return RawUncompressToIOVec(&reader, iov, iov_cnt); +} + +bool RawUncompressToIOVec(Source* compressed, const struct iovec* iov, + size_t iov_cnt) { + SnappyIOVecWriter output(iov, iov_cnt); + return InternalUncompress(compressed, &output); +} + +// ----------------------------------------------------------------------- +// Flat array interfaces +// ----------------------------------------------------------------------- + +// A type that writes to a flat array. +// Note that this is not a "ByteSink", but a type that matches the +// Writer template argument to SnappyDecompressor::DecompressAllTags(). +class SnappyArrayWriter { + private: + char* base_; + char* op_; + char* op_limit_; + + public: + inline explicit SnappyArrayWriter(char* dst) + : base_(dst), + op_(dst) { + } + + inline void SetExpectedLength(size_t len) { + op_limit_ = op_ + len; + } + + inline bool CheckLength() const { + return op_ == op_limit_; + } + + inline bool Append(const char* ip, size_t len) { + char* op = op_; + const size_t space_left = op_limit_ - op; + if (space_left < len) { + return false; + } + memcpy(op, ip, len); + op_ = op + len; + return true; + } + + inline bool TryFastAppend(const char* ip, size_t available, size_t len) { + char* op = op_; + const size_t space_left = op_limit_ - op; + if (len <= 16 && available >= 16 + kMaximumTagLength && space_left >= 16) { + // Fast path, used for the majority (about 95%) of invocations. + UnalignedCopy64(ip, op); + UnalignedCopy64(ip + 8, op + 8); + op_ = op + len; + return true; + } else { + return false; + } + } + + inline bool AppendFromSelf(size_t offset, size_t len) { + char* op = op_; + const size_t space_left = op_limit_ - op; + + // Check if we try to append from before the start of the buffer. + // Normally this would just be a check for "produced < offset", + // but "produced <= offset - 1u" is equivalent for every case + // except the one where offset==0, where the right side will wrap around + // to a very big number. This is convenient, as offset==0 is another + // invalid case that we also want to catch, so that we do not go + // into an infinite loop. + assert(op >= base_); + size_t produced = op - base_; + if (produced <= offset - 1u) { + return false; + } + if (len <= 16 && offset >= 8 && space_left >= 16) { + // Fast path, used for the majority (70-80%) of dynamic invocations. + UnalignedCopy64(op - offset, op); + UnalignedCopy64(op - offset + 8, op + 8); + } else { + if (space_left >= len + kMaxIncrementCopyOverflow) { + IncrementalCopyFastPath(op - offset, op, len); + } else { + if (space_left < len) { + return false; + } + IncrementalCopy(op - offset, op, len); + } + } + + op_ = op + len; + return true; + } +}; + +bool RawUncompress(const char* compressed, size_t n, char* uncompressed) { + ByteArraySource reader(compressed, n); + return RawUncompress(&reader, uncompressed); +} + +bool RawUncompress(Source* compressed, char* uncompressed) { + SnappyArrayWriter output(uncompressed); + return InternalUncompress(compressed, &output); +} + +bool Uncompress(const char* compressed, size_t n, string* uncompressed) { + size_t ulength; + if (!GetUncompressedLength(compressed, n, &ulength)) { + return false; + } + // On 32-bit builds: max_size() < kuint32max. Check for that instead + // of crashing (e.g., consider externally specified compressed data). + if (ulength > uncompressed->max_size()) { + return false; + } + STLStringResizeUninitialized(uncompressed, ulength); + return RawUncompress(compressed, n, string_as_array(uncompressed)); +} + + +// A Writer that drops everything on the floor and just does validation +class SnappyDecompressionValidator { + private: + size_t expected_; + size_t produced_; + + public: + inline SnappyDecompressionValidator() : produced_(0) { } + inline void SetExpectedLength(size_t len) { + expected_ = len; + } + inline bool CheckLength() const { + return expected_ == produced_; + } + inline bool Append(const char* ip, size_t len) { + produced_ += len; + return produced_ <= expected_; + } + inline bool TryFastAppend(const char* ip, size_t available, size_t length) { + return false; + } + inline bool AppendFromSelf(size_t offset, size_t len) { + // See SnappyArrayWriter::AppendFromSelf for an explanation of + // the "offset - 1u" trick. + if (produced_ <= offset - 1u) return false; + produced_ += len; + return produced_ <= expected_; + } +}; + +bool IsValidCompressedBuffer(const char* compressed, size_t n) { + ByteArraySource reader(compressed, n); + SnappyDecompressionValidator writer; + return InternalUncompress(&reader, &writer); +} + +void RawCompress(const char* input, + size_t input_length, + char* compressed, + size_t* compressed_length) { + ByteArraySource reader(input, input_length); + UncheckedByteArraySink writer(compressed); + Compress(&reader, &writer); + + // Compute how many bytes were added + *compressed_length = (writer.CurrentDestination() - compressed); +} + +size_t Compress(const char* input, size_t input_length, string* compressed) { + // Pre-grow the buffer to the max length of the compressed output + compressed->resize(MaxCompressedLength(input_length)); + + size_t compressed_length; + RawCompress(input, input_length, string_as_array(compressed), + &compressed_length); + compressed->resize(compressed_length); + return compressed_length; +} + + +} // end namespace snappy + diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy.h b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy.h new file mode 100644 index 00000000..e879e794 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/snappy/snappy.h @@ -0,0 +1,184 @@ +// Copyright 2005 and onwards Google Inc. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// A light-weight compression algorithm. It is designed for speed of +// compression and decompression, rather than for the utmost in space +// savings. +// +// For getting better compression ratios when you are compressing data +// with long repeated sequences or compressing data that is similar to +// other data, while still compressing fast, you might look at first +// using BMDiff and then compressing the output of BMDiff with +// Snappy. + +#ifndef UTIL_SNAPPY_SNAPPY_H__ +#define UTIL_SNAPPY_SNAPPY_H__ + +#include +#include + +#include "snappy-stubs-public.h" + +namespace snappy { + class Source; + class Sink; + + // ------------------------------------------------------------------------ + // Generic compression/decompression routines. + // ------------------------------------------------------------------------ + + // Compress the bytes read from "*source" and append to "*sink". Return the + // number of bytes written. + size_t Compress(Source* source, Sink* sink); + + // Find the uncompressed length of the given stream, as given by the header. + // Note that the true length could deviate from this; the stream could e.g. + // be truncated. + // + // Also note that this leaves "*source" in a state that is unsuitable for + // further operations, such as RawUncompress(). You will need to rewind + // or recreate the source yourself before attempting any further calls. + bool GetUncompressedLength(Source* source, uint32* result); + + // ------------------------------------------------------------------------ + // Higher-level string based routines (should be sufficient for most users) + // ------------------------------------------------------------------------ + + // Sets "*output" to the compressed version of "input[0,input_length-1]". + // Original contents of *output are lost. + // + // REQUIRES: "input[]" is not an alias of "*output". + size_t Compress(const char* input, size_t input_length, string* output); + + // Decompresses "compressed[0,compressed_length-1]" to "*uncompressed". + // Original contents of "*uncompressed" are lost. + // + // REQUIRES: "compressed[]" is not an alias of "*uncompressed". + // + // returns false if the message is corrupted and could not be decompressed + bool Uncompress(const char* compressed, size_t compressed_length, + string* uncompressed); + + + // ------------------------------------------------------------------------ + // Lower-level character array based routines. May be useful for + // efficiency reasons in certain circumstances. + // ------------------------------------------------------------------------ + + // REQUIRES: "compressed" must point to an area of memory that is at + // least "MaxCompressedLength(input_length)" bytes in length. + // + // Takes the data stored in "input[0..input_length]" and stores + // it in the array pointed to by "compressed". + // + // "*compressed_length" is set to the length of the compressed output. + // + // Example: + // char* output = new char[snappy::MaxCompressedLength(input_length)]; + // size_t output_length; + // RawCompress(input, input_length, output, &output_length); + // ... Process(output, output_length) ... + // delete [] output; + void RawCompress(const char* input, + size_t input_length, + char* compressed, + size_t* compressed_length); + + // Given data in "compressed[0..compressed_length-1]" generated by + // calling the Snappy::Compress routine, this routine + // stores the uncompressed data to + // uncompressed[0..GetUncompressedLength(compressed)-1] + // returns false if the message is corrupted and could not be decrypted + bool RawUncompress(const char* compressed, size_t compressed_length, + char* uncompressed); + + // Given data from the byte source 'compressed' generated by calling + // the Snappy::Compress routine, this routine stores the uncompressed + // data to + // uncompressed[0..GetUncompressedLength(compressed,compressed_length)-1] + // returns false if the message is corrupted and could not be decrypted + bool RawUncompress(Source* compressed, char* uncompressed); + + // Given data in "compressed[0..compressed_length-1]" generated by + // calling the Snappy::Compress routine, this routine + // stores the uncompressed data to the iovec "iov". The number of physical + // buffers in "iov" is given by iov_cnt and their cumulative size + // must be at least GetUncompressedLength(compressed). The individual buffers + // in "iov" must not overlap with each other. + // + // returns false if the message is corrupted and could not be decrypted + bool RawUncompressToIOVec(const char* compressed, size_t compressed_length, + const struct iovec* iov, size_t iov_cnt); + + // Given data from the byte source 'compressed' generated by calling + // the Snappy::Compress routine, this routine stores the uncompressed + // data to the iovec "iov". The number of physical + // buffers in "iov" is given by iov_cnt and their cumulative size + // must be at least GetUncompressedLength(compressed). The individual buffers + // in "iov" must not overlap with each other. + // + // returns false if the message is corrupted and could not be decrypted + bool RawUncompressToIOVec(Source* compressed, const struct iovec* iov, + size_t iov_cnt); + + // Returns the maximal size of the compressed representation of + // input data that is "source_bytes" bytes in length; + size_t MaxCompressedLength(size_t source_bytes); + + // REQUIRES: "compressed[]" was produced by RawCompress() or Compress() + // Returns true and stores the length of the uncompressed data in + // *result normally. Returns false on parsing error. + // This operation takes O(1) time. + bool GetUncompressedLength(const char* compressed, size_t compressed_length, + size_t* result); + + // Returns true iff the contents of "compressed[]" can be uncompressed + // successfully. Does not return the uncompressed data. Takes + // time proportional to compressed_length, but is usually at least + // a factor of four faster than actual decompression. + bool IsValidCompressedBuffer(const char* compressed, + size_t compressed_length); + + // The size of a compression block. Note that many parts of the compression + // code assumes that kBlockSize <= 65536; in particular, the hash table + // can only store 16-bit offsets, and EmitCopy() also assumes the offset + // is 65535 bytes or less. Note also that if you change this, it will + // affect the framing format (see framing_format.txt). + // + // Note that there might be older data around that is compressed with larger + // block sizes, so the decompression code should not rely on the + // non-existence of long backreferences. + static const int kBlockLog = 16; + static const size_t kBlockSize = 1 << kBlockLog; + + static const int kMaxHashTableBits = 14; + static const size_t kMaxHashTableSize = 1 << kMaxHashTableBits; +} // end namespace snappy + + +#endif // UTIL_SNAPPY_SNAPPY_H__ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/win32-defs/iocpdefs.h b/couchbase-sys/libcouchbase-2.7.0/contrib/win32-defs/iocpdefs.h new file mode 100644 index 00000000..d1f7097b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/win32-defs/iocpdefs.h @@ -0,0 +1,133 @@ +/** + * This file loosely based on the one from the UV project, whose copyright + * notice reads below + */ + +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef LCB_IOCP_DEFS_H +#define LCB_IOCP_DEFS_H + +#include +#include +#include +#include + +/* + * Guids and typedefs for winsock extension functions + * Mingw32 doesn't have these :-( + */ + +#ifndef WSAID_ACCEPTEX +# define WSAID_ACCEPTEX \ + {0xb5367df1, 0xcbac, 0x11cf, \ + {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} + +# define WSAID_CONNECTEX \ + {0x25a207b9, 0xddf3, 0x4660, \ + {0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e}} + +# define WSAID_GETACCEPTEXSOCKADDRS \ + {0xb5367df2, 0xcbac, 0x11cf, \ + {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} + +# define WSAID_DISCONNECTEX \ + {0x7fda2e11, 0x8630, 0x436f, \ + {0xa0, 0x31, 0xf5, 0x36, 0xa6, 0xee, 0xc1, 0x57}} + +# define WSAID_TRANSMITFILE \ + {0xb5367df0, 0xcbac, 0x11cf, \ + {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} + +typedef BOOL PASCAL(*LPFN_ACCEPTEX)(SOCKET sListenSocket, + SOCKET sAcceptSocket, + PVOID lpOutputBuffer, + DWORD dwReceiveDataLength, + DWORD dwLocalAddressLength, + DWORD dwRemoteAddressLength, + LPDWORD lpdwBytesReceived, + LPOVERLAPPED lpOverlapped); + +typedef BOOL PASCAL(*LPFN_CONNECTEX)(SOCKET s, + const struct sockaddr *name, + int namelen, + PVOID lpSendBuffer, + DWORD dwSendDataLength, + LPDWORD lpdwBytesSent, + LPOVERLAPPED lpOverlapped); + +typedef void PASCAL(*LPFN_GETACCEPTEXSOCKADDRS)(PVOID lpOutputBuffer, + DWORD dwReceiveDataLength, + DWORD dwLocalAddressLength, + DWORD dwRemoteAddressLength, + LPSOCKADDR *LocalSockaddr, + LPINT LocalSockaddrLength, + LPSOCKADDR *RemoteSockaddr, + LPINT RemoteSockaddrLength); + +typedef BOOL PASCAL(*LPFN_DISCONNECTEX)(SOCKET hSocket, + LPOVERLAPPED lpOverlapped, + DWORD dwFlags, + DWORD reserved); + +typedef BOOL PASCAL(*LPFN_TRANSMITFILE)(SOCKET hSocket, + HANDLE hFile, + DWORD nNumberOfBytesToWrite, + DWORD nNumberOfBytesPerSend, + LPOVERLAPPED lpOverlapped, + LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers, + DWORD dwFlags); + +typedef PVOID RTL_SRWLOCK; +typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK; +#endif + +typedef int (WSAAPI *LPFN_WSARECV)(SOCKET socket, + LPWSABUF buffers, + DWORD buffer_count, + LPDWORD bytes, + LPDWORD flags, + LPWSAOVERLAPPED overlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine); + +typedef int (WSAAPI *LPFN_WSARECVFROM)(SOCKET socket, + LPWSABUF buffers, + DWORD buffer_count, + LPDWORD bytes, + LPDWORD flags, + struct sockaddr *addr, + LPINT addr_len, + LPWSAOVERLAPPED overlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine); + +typedef BOOL (WINAPI *sGetQueuedCompletionStatusEx)(HANDLE CompletionPort, + LPOVERLAPPED_ENTRY lpCompletionPortEntries, + ULONG ulCount, + PULONG ulNumEntriesRemoved, + DWORD dwMilliseconds, + BOOL fAlertable); + +typedef BOOL (WINAPI *sCancelIoEx)(HANDLE hFile, + LPOVERLAPPED lpOverlapped); + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/win32-defs/mingwdefs.h b/couchbase-sys/libcouchbase-2.7.0/contrib/win32-defs/mingwdefs.h new file mode 100644 index 00000000..ad0c0bbf --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/win32-defs/mingwdefs.h @@ -0,0 +1,4396 @@ +/** + * This file is a partly modified version of 'winapi.h' from UV (copyright notice + * below). + * It mainly contains definitions for NT status codes for mingw + */ + +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef LCB_WIN_STATUS_H +#define LCB_WIN_STATUS_H +#include + +#ifndef _NTDEF_ +typedef LONG NTSTATUS; +typedef NTSTATUS *PNTSTATUS; +#endif + +/* + * Ntdll headers + */ +#ifndef STATUS_SEVERITY_SUCCESS +# define STATUS_SEVERITY_SUCCESS 0x0 +#endif + +#ifndef STATUS_SEVERITY_INFORMATIONAL +# define STATUS_SEVERITY_INFORMATIONAL 0x1 +#endif + +#ifndef STATUS_SEVERITY_WARNING +# define STATUS_SEVERITY_WARNING 0x2 +#endif + +#ifndef STATUS_SEVERITY_ERROR +# define STATUS_SEVERITY_ERROR 0x3 +#endif + +#ifndef FACILITY_NTWIN32 +# define FACILITY_NTWIN32 0x7 +#endif + +#ifndef NT_SUCCESS +# define NT_SUCCESS(status) (((NTSTATUS) (status)) >= 0) +#endif + +#ifndef STATUS_SUCCESS +# define STATUS_SUCCESS ((NTSTATUS) 0x00000000L) +#endif + +#ifndef STATUS_WAIT_0 +# define STATUS_WAIT_0 ((NTSTATUS) 0x00000000L) +#endif + +#ifndef STATUS_WAIT_1 +# define STATUS_WAIT_1 ((NTSTATUS) 0x00000001L) +#endif + +#ifndef STATUS_WAIT_2 +# define STATUS_WAIT_2 ((NTSTATUS) 0x00000002L) +#endif + +#ifndef STATUS_WAIT_3 +# define STATUS_WAIT_3 ((NTSTATUS) 0x00000003L) +#endif + +#ifndef STATUS_WAIT_63 +# define STATUS_WAIT_63 ((NTSTATUS) 0x0000003FL) +#endif + +#ifndef STATUS_ABANDONED +# define STATUS_ABANDONED ((NTSTATUS) 0x00000080L) +#endif + +#ifndef STATUS_ABANDONED_WAIT_0 +# define STATUS_ABANDONED_WAIT_0 ((NTSTATUS) 0x00000080L) +#endif + +#ifndef STATUS_ABANDONED_WAIT_63 +# define STATUS_ABANDONED_WAIT_63 ((NTSTATUS) 0x000000BFL) +#endif + +#ifndef STATUS_USER_APC +# define STATUS_USER_APC ((NTSTATUS) 0x000000C0L) +#endif + +#ifndef STATUS_KERNEL_APC +# define STATUS_KERNEL_APC ((NTSTATUS) 0x00000100L) +#endif + +#ifndef STATUS_ALERTED +# define STATUS_ALERTED ((NTSTATUS) 0x00000101L) +#endif + +#ifndef STATUS_TIMEOUT +# define STATUS_TIMEOUT ((NTSTATUS) 0x00000102L) +#endif + +#ifndef STATUS_PENDING +# define STATUS_PENDING ((NTSTATUS) 0x00000103L) +#endif + +#ifndef STATUS_REPARSE +# define STATUS_REPARSE ((NTSTATUS) 0x00000104L) +#endif + +#ifndef STATUS_MORE_ENTRIES +# define STATUS_MORE_ENTRIES ((NTSTATUS) 0x00000105L) +#endif + +#ifndef STATUS_NOT_ALL_ASSIGNED +# define STATUS_NOT_ALL_ASSIGNED ((NTSTATUS) 0x00000106L) +#endif + +#ifndef STATUS_SOME_NOT_MAPPED +# define STATUS_SOME_NOT_MAPPED ((NTSTATUS) 0x00000107L) +#endif + +#ifndef STATUS_OPLOCK_BREAK_IN_PROGRESS +# define STATUS_OPLOCK_BREAK_IN_PROGRESS ((NTSTATUS) 0x00000108L) +#endif + +#ifndef STATUS_VOLUME_MOUNTED +# define STATUS_VOLUME_MOUNTED ((NTSTATUS) 0x00000109L) +#endif + +#ifndef STATUS_RXACT_COMMITTED +# define STATUS_RXACT_COMMITTED ((NTSTATUS) 0x0000010AL) +#endif + +#ifndef STATUS_NOTIFY_CLEANUP +# define STATUS_NOTIFY_CLEANUP ((NTSTATUS) 0x0000010BL) +#endif + +#ifndef STATUS_NOTIFY_ENUM_DIR +# define STATUS_NOTIFY_ENUM_DIR ((NTSTATUS) 0x0000010CL) +#endif + +#ifndef STATUS_NO_QUOTAS_FOR_ACCOUNT +# define STATUS_NO_QUOTAS_FOR_ACCOUNT ((NTSTATUS) 0x0000010DL) +#endif + +#ifndef STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED +# define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED ((NTSTATUS) 0x0000010EL) +#endif + +#ifndef STATUS_PAGE_FAULT_TRANSITION +# define STATUS_PAGE_FAULT_TRANSITION ((NTSTATUS) 0x00000110L) +#endif + +#ifndef STATUS_PAGE_FAULT_DEMAND_ZERO +# define STATUS_PAGE_FAULT_DEMAND_ZERO ((NTSTATUS) 0x00000111L) +#endif + +#ifndef STATUS_PAGE_FAULT_COPY_ON_WRITE +# define STATUS_PAGE_FAULT_COPY_ON_WRITE ((NTSTATUS) 0x00000112L) +#endif + +#ifndef STATUS_PAGE_FAULT_GUARD_PAGE +# define STATUS_PAGE_FAULT_GUARD_PAGE ((NTSTATUS) 0x00000113L) +#endif + +#ifndef STATUS_PAGE_FAULT_PAGING_FILE +# define STATUS_PAGE_FAULT_PAGING_FILE ((NTSTATUS) 0x00000114L) +#endif + +#ifndef STATUS_CACHE_PAGE_LOCKED +# define STATUS_CACHE_PAGE_LOCKED ((NTSTATUS) 0x00000115L) +#endif + +#ifndef STATUS_CRASH_DUMP +# define STATUS_CRASH_DUMP ((NTSTATUS) 0x00000116L) +#endif + +#ifndef STATUS_BUFFER_ALL_ZEROS +# define STATUS_BUFFER_ALL_ZEROS ((NTSTATUS) 0x00000117L) +#endif + +#ifndef STATUS_REPARSE_OBJECT +# define STATUS_REPARSE_OBJECT ((NTSTATUS) 0x00000118L) +#endif + +#ifndef STATUS_RESOURCE_REQUIREMENTS_CHANGED +# define STATUS_RESOURCE_REQUIREMENTS_CHANGED ((NTSTATUS) 0x00000119L) +#endif + +#ifndef STATUS_TRANSLATION_COMPLETE +# define STATUS_TRANSLATION_COMPLETE ((NTSTATUS) 0x00000120L) +#endif + +#ifndef STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY +# define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY ((NTSTATUS) 0x00000121L) +#endif + +#ifndef STATUS_NOTHING_TO_TERMINATE +# define STATUS_NOTHING_TO_TERMINATE ((NTSTATUS) 0x00000122L) +#endif + +#ifndef STATUS_PROCESS_NOT_IN_JOB +# define STATUS_PROCESS_NOT_IN_JOB ((NTSTATUS) 0x00000123L) +#endif + +#ifndef STATUS_PROCESS_IN_JOB +# define STATUS_PROCESS_IN_JOB ((NTSTATUS) 0x00000124L) +#endif + +#ifndef STATUS_VOLSNAP_HIBERNATE_READY +# define STATUS_VOLSNAP_HIBERNATE_READY ((NTSTATUS) 0x00000125L) +#endif + +#ifndef STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY +# define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY ((NTSTATUS) 0x00000126L) +#endif + +#ifndef STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED +# define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED ((NTSTATUS) 0x00000127L) +#endif + +#ifndef STATUS_INTERRUPT_STILL_CONNECTED +# define STATUS_INTERRUPT_STILL_CONNECTED ((NTSTATUS) 0x00000128L) +#endif + +#ifndef STATUS_PROCESS_CLONED +# define STATUS_PROCESS_CLONED ((NTSTATUS) 0x00000129L) +#endif + +#ifndef STATUS_FILE_LOCKED_WITH_ONLY_READERS +# define STATUS_FILE_LOCKED_WITH_ONLY_READERS ((NTSTATUS) 0x0000012AL) +#endif + +#ifndef STATUS_FILE_LOCKED_WITH_WRITERS +# define STATUS_FILE_LOCKED_WITH_WRITERS ((NTSTATUS) 0x0000012BL) +#endif + +#ifndef STATUS_RESOURCEMANAGER_READ_ONLY +# define STATUS_RESOURCEMANAGER_READ_ONLY ((NTSTATUS) 0x00000202L) +#endif + +#ifndef STATUS_RING_PREVIOUSLY_EMPTY +# define STATUS_RING_PREVIOUSLY_EMPTY ((NTSTATUS) 0x00000210L) +#endif + +#ifndef STATUS_RING_PREVIOUSLY_FULL +# define STATUS_RING_PREVIOUSLY_FULL ((NTSTATUS) 0x00000211L) +#endif + +#ifndef STATUS_RING_PREVIOUSLY_ABOVE_QUOTA +# define STATUS_RING_PREVIOUSLY_ABOVE_QUOTA ((NTSTATUS) 0x00000212L) +#endif + +#ifndef STATUS_RING_NEWLY_EMPTY +# define STATUS_RING_NEWLY_EMPTY ((NTSTATUS) 0x00000213L) +#endif + +#ifndef STATUS_RING_SIGNAL_OPPOSITE_ENDPOINT +# define STATUS_RING_SIGNAL_OPPOSITE_ENDPOINT ((NTSTATUS) 0x00000214L) +#endif + +#ifndef STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE +# define STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE ((NTSTATUS) 0x00000215L) +#endif + +#ifndef STATUS_OPLOCK_HANDLE_CLOSED +# define STATUS_OPLOCK_HANDLE_CLOSED ((NTSTATUS) 0x00000216L) +#endif + +#ifndef STATUS_WAIT_FOR_OPLOCK +# define STATUS_WAIT_FOR_OPLOCK ((NTSTATUS) 0x00000367L) +#endif + +#ifndef STATUS_OBJECT_NAME_EXISTS +# define STATUS_OBJECT_NAME_EXISTS ((NTSTATUS) 0x40000000L) +#endif + +#ifndef STATUS_THREAD_WAS_SUSPENDED +# define STATUS_THREAD_WAS_SUSPENDED ((NTSTATUS) 0x40000001L) +#endif + +#ifndef STATUS_WORKING_SET_LIMIT_RANGE +# define STATUS_WORKING_SET_LIMIT_RANGE ((NTSTATUS) 0x40000002L) +#endif + +#ifndef STATUS_IMAGE_NOT_AT_BASE +# define STATUS_IMAGE_NOT_AT_BASE ((NTSTATUS) 0x40000003L) +#endif + +#ifndef STATUS_RXACT_STATE_CREATED +# define STATUS_RXACT_STATE_CREATED ((NTSTATUS) 0x40000004L) +#endif + +#ifndef STATUS_SEGMENT_NOTIFICATION +# define STATUS_SEGMENT_NOTIFICATION ((NTSTATUS) 0x40000005L) +#endif + +#ifndef STATUS_LOCAL_USER_SESSION_KEY +# define STATUS_LOCAL_USER_SESSION_KEY ((NTSTATUS) 0x40000006L) +#endif + +#ifndef STATUS_BAD_CURRENT_DIRECTORY +# define STATUS_BAD_CURRENT_DIRECTORY ((NTSTATUS) 0x40000007L) +#endif + +#ifndef STATUS_SERIAL_MORE_WRITES +# define STATUS_SERIAL_MORE_WRITES ((NTSTATUS) 0x40000008L) +#endif + +#ifndef STATUS_REGISTRY_RECOVERED +# define STATUS_REGISTRY_RECOVERED ((NTSTATUS) 0x40000009L) +#endif + +#ifndef STATUS_FT_READ_RECOVERY_FROM_BACKUP +# define STATUS_FT_READ_RECOVERY_FROM_BACKUP ((NTSTATUS) 0x4000000AL) +#endif + +#ifndef STATUS_FT_WRITE_RECOVERY +# define STATUS_FT_WRITE_RECOVERY ((NTSTATUS) 0x4000000BL) +#endif + +#ifndef STATUS_SERIAL_COUNTER_TIMEOUT +# define STATUS_SERIAL_COUNTER_TIMEOUT ((NTSTATUS) 0x4000000CL) +#endif + +#ifndef STATUS_NULL_LM_PASSWORD +# define STATUS_NULL_LM_PASSWORD ((NTSTATUS) 0x4000000DL) +#endif + +#ifndef STATUS_IMAGE_MACHINE_TYPE_MISMATCH +# define STATUS_IMAGE_MACHINE_TYPE_MISMATCH ((NTSTATUS) 0x4000000EL) +#endif + +#ifndef STATUS_RECEIVE_PARTIAL +# define STATUS_RECEIVE_PARTIAL ((NTSTATUS) 0x4000000FL) +#endif + +#ifndef STATUS_RECEIVE_EXPEDITED +# define STATUS_RECEIVE_EXPEDITED ((NTSTATUS) 0x40000010L) +#endif + +#ifndef STATUS_RECEIVE_PARTIAL_EXPEDITED +# define STATUS_RECEIVE_PARTIAL_EXPEDITED ((NTSTATUS) 0x40000011L) +#endif + +#ifndef STATUS_EVENT_DONE +# define STATUS_EVENT_DONE ((NTSTATUS) 0x40000012L) +#endif + +#ifndef STATUS_EVENT_PENDING +# define STATUS_EVENT_PENDING ((NTSTATUS) 0x40000013L) +#endif + +#ifndef STATUS_CHECKING_FILE_SYSTEM +# define STATUS_CHECKING_FILE_SYSTEM ((NTSTATUS) 0x40000014L) +#endif + +#ifndef STATUS_FATAL_APP_EXIT +# define STATUS_FATAL_APP_EXIT ((NTSTATUS) 0x40000015L) +#endif + +#ifndef STATUS_PREDEFINED_HANDLE +# define STATUS_PREDEFINED_HANDLE ((NTSTATUS) 0x40000016L) +#endif + +#ifndef STATUS_WAS_UNLOCKED +# define STATUS_WAS_UNLOCKED ((NTSTATUS) 0x40000017L) +#endif + +#ifndef STATUS_SERVICE_NOTIFICATION +# define STATUS_SERVICE_NOTIFICATION ((NTSTATUS) 0x40000018L) +#endif + +#ifndef STATUS_WAS_LOCKED +# define STATUS_WAS_LOCKED ((NTSTATUS) 0x40000019L) +#endif + +#ifndef STATUS_LOG_HARD_ERROR +# define STATUS_LOG_HARD_ERROR ((NTSTATUS) 0x4000001AL) +#endif + +#ifndef STATUS_ALREADY_WIN32 +# define STATUS_ALREADY_WIN32 ((NTSTATUS) 0x4000001BL) +#endif + +#ifndef STATUS_WX86_UNSIMULATE +# define STATUS_WX86_UNSIMULATE ((NTSTATUS) 0x4000001CL) +#endif + +#ifndef STATUS_WX86_CONTINUE +# define STATUS_WX86_CONTINUE ((NTSTATUS) 0x4000001DL) +#endif + +#ifndef STATUS_WX86_SINGLE_STEP +# define STATUS_WX86_SINGLE_STEP ((NTSTATUS) 0x4000001EL) +#endif + +#ifndef STATUS_WX86_BREAKPOINT +# define STATUS_WX86_BREAKPOINT ((NTSTATUS) 0x4000001FL) +#endif + +#ifndef STATUS_WX86_EXCEPTION_CONTINUE +# define STATUS_WX86_EXCEPTION_CONTINUE ((NTSTATUS) 0x40000020L) +#endif + +#ifndef STATUS_WX86_EXCEPTION_LASTCHANCE +# define STATUS_WX86_EXCEPTION_LASTCHANCE ((NTSTATUS) 0x40000021L) +#endif + +#ifndef STATUS_WX86_EXCEPTION_CHAIN +# define STATUS_WX86_EXCEPTION_CHAIN ((NTSTATUS) 0x40000022L) +#endif + +#ifndef STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE +# define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE ((NTSTATUS) 0x40000023L) +#endif + +#ifndef STATUS_NO_YIELD_PERFORMED +# define STATUS_NO_YIELD_PERFORMED ((NTSTATUS) 0x40000024L) +#endif + +#ifndef STATUS_TIMER_RESUME_IGNORED +# define STATUS_TIMER_RESUME_IGNORED ((NTSTATUS) 0x40000025L) +#endif + +#ifndef STATUS_ARBITRATION_UNHANDLED +# define STATUS_ARBITRATION_UNHANDLED ((NTSTATUS) 0x40000026L) +#endif + +#ifndef STATUS_CARDBUS_NOT_SUPPORTED +# define STATUS_CARDBUS_NOT_SUPPORTED ((NTSTATUS) 0x40000027L) +#endif + +#ifndef STATUS_WX86_CREATEWX86TIB +# define STATUS_WX86_CREATEWX86TIB ((NTSTATUS) 0x40000028L) +#endif + +#ifndef STATUS_MP_PROCESSOR_MISMATCH +# define STATUS_MP_PROCESSOR_MISMATCH ((NTSTATUS) 0x40000029L) +#endif + +#ifndef STATUS_HIBERNATED +# define STATUS_HIBERNATED ((NTSTATUS) 0x4000002AL) +#endif + +#ifndef STATUS_RESUME_HIBERNATION +# define STATUS_RESUME_HIBERNATION ((NTSTATUS) 0x4000002BL) +#endif + +#ifndef STATUS_FIRMWARE_UPDATED +# define STATUS_FIRMWARE_UPDATED ((NTSTATUS) 0x4000002CL) +#endif + +#ifndef STATUS_DRIVERS_LEAKING_LOCKED_PAGES +# define STATUS_DRIVERS_LEAKING_LOCKED_PAGES ((NTSTATUS) 0x4000002DL) +#endif + +#ifndef STATUS_MESSAGE_RETRIEVED +# define STATUS_MESSAGE_RETRIEVED ((NTSTATUS) 0x4000002EL) +#endif + +#ifndef STATUS_SYSTEM_POWERSTATE_TRANSITION +# define STATUS_SYSTEM_POWERSTATE_TRANSITION ((NTSTATUS) 0x4000002FL) +#endif + +#ifndef STATUS_ALPC_CHECK_COMPLETION_LIST +# define STATUS_ALPC_CHECK_COMPLETION_LIST ((NTSTATUS) 0x40000030L) +#endif + +#ifndef STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION +# define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION ((NTSTATUS) 0x40000031L) +#endif + +#ifndef STATUS_ACCESS_AUDIT_BY_POLICY +# define STATUS_ACCESS_AUDIT_BY_POLICY ((NTSTATUS) 0x40000032L) +#endif + +#ifndef STATUS_ABANDON_HIBERFILE +# define STATUS_ABANDON_HIBERFILE ((NTSTATUS) 0x40000033L) +#endif + +#ifndef STATUS_BIZRULES_NOT_ENABLED +# define STATUS_BIZRULES_NOT_ENABLED ((NTSTATUS) 0x40000034L) +#endif + +#ifndef STATUS_GUARD_PAGE_VIOLATION +# define STATUS_GUARD_PAGE_VIOLATION ((NTSTATUS) 0x80000001L) +#endif + +#ifndef STATUS_DATATYPE_MISALIGNMENT +# define STATUS_DATATYPE_MISALIGNMENT ((NTSTATUS) 0x80000002L) +#endif + +#ifndef STATUS_BREAKPOINT +# define STATUS_BREAKPOINT ((NTSTATUS) 0x80000003L) +#endif + +#ifndef STATUS_SINGLE_STEP +# define STATUS_SINGLE_STEP ((NTSTATUS) 0x80000004L) +#endif + +#ifndef STATUS_BUFFER_OVERFLOW +# define STATUS_BUFFER_OVERFLOW ((NTSTATUS) 0x80000005L) +#endif + +#ifndef STATUS_NO_MORE_FILES +# define STATUS_NO_MORE_FILES ((NTSTATUS) 0x80000006L) +#endif + +#ifndef STATUS_WAKE_SYSTEM_DEBUGGER +# define STATUS_WAKE_SYSTEM_DEBUGGER ((NTSTATUS) 0x80000007L) +#endif + +#ifndef STATUS_HANDLES_CLOSED +# define STATUS_HANDLES_CLOSED ((NTSTATUS) 0x8000000AL) +#endif + +#ifndef STATUS_NO_INHERITANCE +# define STATUS_NO_INHERITANCE ((NTSTATUS) 0x8000000BL) +#endif + +#ifndef STATUS_GUID_SUBSTITUTION_MADE +# define STATUS_GUID_SUBSTITUTION_MADE ((NTSTATUS) 0x8000000CL) +#endif + +#ifndef STATUS_PARTIAL_COPY +# define STATUS_PARTIAL_COPY ((NTSTATUS) 0x8000000DL) +#endif + +#ifndef STATUS_DEVICE_PAPER_EMPTY +# define STATUS_DEVICE_PAPER_EMPTY ((NTSTATUS) 0x8000000EL) +#endif + +#ifndef STATUS_DEVICE_POWERED_OFF +# define STATUS_DEVICE_POWERED_OFF ((NTSTATUS) 0x8000000FL) +#endif + +#ifndef STATUS_DEVICE_OFF_LINE +# define STATUS_DEVICE_OFF_LINE ((NTSTATUS) 0x80000010L) +#endif + +#ifndef STATUS_DEVICE_BUSY +# define STATUS_DEVICE_BUSY ((NTSTATUS) 0x80000011L) +#endif + +#ifndef STATUS_NO_MORE_EAS +# define STATUS_NO_MORE_EAS ((NTSTATUS) 0x80000012L) +#endif + +#ifndef STATUS_INVALID_EA_NAME +# define STATUS_INVALID_EA_NAME ((NTSTATUS) 0x80000013L) +#endif + +#ifndef STATUS_EA_LIST_INCONSISTENT +# define STATUS_EA_LIST_INCONSISTENT ((NTSTATUS) 0x80000014L) +#endif + +#ifndef STATUS_INVALID_EA_FLAG +# define STATUS_INVALID_EA_FLAG ((NTSTATUS) 0x80000015L) +#endif + +#ifndef STATUS_VERIFY_REQUIRED +# define STATUS_VERIFY_REQUIRED ((NTSTATUS) 0x80000016L) +#endif + +#ifndef STATUS_EXTRANEOUS_INFORMATION +# define STATUS_EXTRANEOUS_INFORMATION ((NTSTATUS) 0x80000017L) +#endif + +#ifndef STATUS_RXACT_COMMIT_NECESSARY +# define STATUS_RXACT_COMMIT_NECESSARY ((NTSTATUS) 0x80000018L) +#endif + +#ifndef STATUS_NO_MORE_ENTRIES +# define STATUS_NO_MORE_ENTRIES ((NTSTATUS) 0x8000001AL) +#endif + +#ifndef STATUS_FILEMARK_DETECTED +# define STATUS_FILEMARK_DETECTED ((NTSTATUS) 0x8000001BL) +#endif + +#ifndef STATUS_MEDIA_CHANGED +# define STATUS_MEDIA_CHANGED ((NTSTATUS) 0x8000001CL) +#endif + +#ifndef STATUS_BUS_RESET +# define STATUS_BUS_RESET ((NTSTATUS) 0x8000001DL) +#endif + +#ifndef STATUS_END_OF_MEDIA +# define STATUS_END_OF_MEDIA ((NTSTATUS) 0x8000001EL) +#endif + +#ifndef STATUS_BEGINNING_OF_MEDIA +# define STATUS_BEGINNING_OF_MEDIA ((NTSTATUS) 0x8000001FL) +#endif + +#ifndef STATUS_MEDIA_CHECK +# define STATUS_MEDIA_CHECK ((NTSTATUS) 0x80000020L) +#endif + +#ifndef STATUS_SETMARK_DETECTED +# define STATUS_SETMARK_DETECTED ((NTSTATUS) 0x80000021L) +#endif + +#ifndef STATUS_NO_DATA_DETECTED +# define STATUS_NO_DATA_DETECTED ((NTSTATUS) 0x80000022L) +#endif + +#ifndef STATUS_REDIRECTOR_HAS_OPEN_HANDLES +# define STATUS_REDIRECTOR_HAS_OPEN_HANDLES ((NTSTATUS) 0x80000023L) +#endif + +#ifndef STATUS_SERVER_HAS_OPEN_HANDLES +# define STATUS_SERVER_HAS_OPEN_HANDLES ((NTSTATUS) 0x80000024L) +#endif + +#ifndef STATUS_ALREADY_DISCONNECTED +# define STATUS_ALREADY_DISCONNECTED ((NTSTATUS) 0x80000025L) +#endif + +#ifndef STATUS_LONGJUMP +# define STATUS_LONGJUMP ((NTSTATUS) 0x80000026L) +#endif + +#ifndef STATUS_CLEANER_CARTRIDGE_INSTALLED +# define STATUS_CLEANER_CARTRIDGE_INSTALLED ((NTSTATUS) 0x80000027L) +#endif + +#ifndef STATUS_PLUGPLAY_QUERY_VETOED +# define STATUS_PLUGPLAY_QUERY_VETOED ((NTSTATUS) 0x80000028L) +#endif + +#ifndef STATUS_UNWIND_CONSOLIDATE +# define STATUS_UNWIND_CONSOLIDATE ((NTSTATUS) 0x80000029L) +#endif + +#ifndef STATUS_REGISTRY_HIVE_RECOVERED +# define STATUS_REGISTRY_HIVE_RECOVERED ((NTSTATUS) 0x8000002AL) +#endif + +#ifndef STATUS_DLL_MIGHT_BE_INSECURE +# define STATUS_DLL_MIGHT_BE_INSECURE ((NTSTATUS) 0x8000002BL) +#endif + +#ifndef STATUS_DLL_MIGHT_BE_INCOMPATIBLE +# define STATUS_DLL_MIGHT_BE_INCOMPATIBLE ((NTSTATUS) 0x8000002CL) +#endif + +#ifndef STATUS_STOPPED_ON_SYMLINK +# define STATUS_STOPPED_ON_SYMLINK ((NTSTATUS) 0x8000002DL) +#endif + +#ifndef STATUS_CANNOT_GRANT_REQUESTED_OPLOCK +# define STATUS_CANNOT_GRANT_REQUESTED_OPLOCK ((NTSTATUS) 0x8000002EL) +#endif + +#ifndef STATUS_NO_ACE_CONDITION +# define STATUS_NO_ACE_CONDITION ((NTSTATUS) 0x8000002FL) +#endif + +#ifndef STATUS_UNSUCCESSFUL +# define STATUS_UNSUCCESSFUL ((NTSTATUS) 0xC0000001L) +#endif + +#ifndef STATUS_NOT_IMPLEMENTED +# define STATUS_NOT_IMPLEMENTED ((NTSTATUS) 0xC0000002L) +#endif + +#ifndef STATUS_INVALID_INFO_CLASS +# define STATUS_INVALID_INFO_CLASS ((NTSTATUS) 0xC0000003L) +#endif + +#ifndef STATUS_INFO_LENGTH_MISMATCH +# define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS) 0xC0000004L) +#endif + +#ifndef STATUS_ACCESS_VIOLATION +# define STATUS_ACCESS_VIOLATION ((NTSTATUS) 0xC0000005L) +#endif + +#ifndef STATUS_IN_PAGE_ERROR +# define STATUS_IN_PAGE_ERROR ((NTSTATUS) 0xC0000006L) +#endif + +#ifndef STATUS_PAGEFILE_QUOTA +# define STATUS_PAGEFILE_QUOTA ((NTSTATUS) 0xC0000007L) +#endif + +#ifndef STATUS_INVALID_HANDLE +# define STATUS_INVALID_HANDLE ((NTSTATUS) 0xC0000008L) +#endif + +#ifndef STATUS_BAD_INITIAL_STACK +# define STATUS_BAD_INITIAL_STACK ((NTSTATUS) 0xC0000009L) +#endif + +#ifndef STATUS_BAD_INITIAL_PC +# define STATUS_BAD_INITIAL_PC ((NTSTATUS) 0xC000000AL) +#endif + +#ifndef STATUS_INVALID_CID +# define STATUS_INVALID_CID ((NTSTATUS) 0xC000000BL) +#endif + +#ifndef STATUS_TIMER_NOT_CANCELED +# define STATUS_TIMER_NOT_CANCELED ((NTSTATUS) 0xC000000CL) +#endif + +#ifndef STATUS_INVALID_PARAMETER +# define STATUS_INVALID_PARAMETER ((NTSTATUS) 0xC000000DL) +#endif + +#ifndef STATUS_NO_SUCH_DEVICE +# define STATUS_NO_SUCH_DEVICE ((NTSTATUS) 0xC000000EL) +#endif + +#ifndef STATUS_NO_SUCH_FILE +# define STATUS_NO_SUCH_FILE ((NTSTATUS) 0xC000000FL) +#endif + +#ifndef STATUS_INVALID_DEVICE_REQUEST +# define STATUS_INVALID_DEVICE_REQUEST ((NTSTATUS) 0xC0000010L) +#endif + +#ifndef STATUS_END_OF_FILE +# define STATUS_END_OF_FILE ((NTSTATUS) 0xC0000011L) +#endif + +#ifndef STATUS_WRONG_VOLUME +# define STATUS_WRONG_VOLUME ((NTSTATUS) 0xC0000012L) +#endif + +#ifndef STATUS_NO_MEDIA_IN_DEVICE +# define STATUS_NO_MEDIA_IN_DEVICE ((NTSTATUS) 0xC0000013L) +#endif + +#ifndef STATUS_UNRECOGNIZED_MEDIA +# define STATUS_UNRECOGNIZED_MEDIA ((NTSTATUS) 0xC0000014L) +#endif + +#ifndef STATUS_NONEXISTENT_SECTOR +# define STATUS_NONEXISTENT_SECTOR ((NTSTATUS) 0xC0000015L) +#endif + +#ifndef STATUS_MORE_PROCESSING_REQUIRED +# define STATUS_MORE_PROCESSING_REQUIRED ((NTSTATUS) 0xC0000016L) +#endif + +#ifndef STATUS_NO_MEMORY +# define STATUS_NO_MEMORY ((NTSTATUS) 0xC0000017L) +#endif + +#ifndef STATUS_CONFLICTING_ADDRESSES +# define STATUS_CONFLICTING_ADDRESSES ((NTSTATUS) 0xC0000018L) +#endif + +#ifndef STATUS_NOT_MAPPED_VIEW +# define STATUS_NOT_MAPPED_VIEW ((NTSTATUS) 0xC0000019L) +#endif + +#ifndef STATUS_UNABLE_TO_FREE_VM +# define STATUS_UNABLE_TO_FREE_VM ((NTSTATUS) 0xC000001AL) +#endif + +#ifndef STATUS_UNABLE_TO_DELETE_SECTION +# define STATUS_UNABLE_TO_DELETE_SECTION ((NTSTATUS) 0xC000001BL) +#endif + +#ifndef STATUS_INVALID_SYSTEM_SERVICE +# define STATUS_INVALID_SYSTEM_SERVICE ((NTSTATUS) 0xC000001CL) +#endif + +#ifndef STATUS_ILLEGAL_INSTRUCTION +# define STATUS_ILLEGAL_INSTRUCTION ((NTSTATUS) 0xC000001DL) +#endif + +#ifndef STATUS_INVALID_LOCK_SEQUENCE +# define STATUS_INVALID_LOCK_SEQUENCE ((NTSTATUS) 0xC000001EL) +#endif + +#ifndef STATUS_INVALID_VIEW_SIZE +# define STATUS_INVALID_VIEW_SIZE ((NTSTATUS) 0xC000001FL) +#endif + +#ifndef STATUS_INVALID_FILE_FOR_SECTION +# define STATUS_INVALID_FILE_FOR_SECTION ((NTSTATUS) 0xC0000020L) +#endif + +#ifndef STATUS_ALREADY_COMMITTED +# define STATUS_ALREADY_COMMITTED ((NTSTATUS) 0xC0000021L) +#endif + +#ifndef STATUS_ACCESS_DENIED +# define STATUS_ACCESS_DENIED ((NTSTATUS) 0xC0000022L) +#endif + +#ifndef STATUS_BUFFER_TOO_SMALL +# define STATUS_BUFFER_TOO_SMALL ((NTSTATUS) 0xC0000023L) +#endif + +#ifndef STATUS_OBJECT_TYPE_MISMATCH +# define STATUS_OBJECT_TYPE_MISMATCH ((NTSTATUS) 0xC0000024L) +#endif + +#ifndef STATUS_NONCONTINUABLE_EXCEPTION +# define STATUS_NONCONTINUABLE_EXCEPTION ((NTSTATUS) 0xC0000025L) +#endif + +#ifndef STATUS_INVALID_DISPOSITION +# define STATUS_INVALID_DISPOSITION ((NTSTATUS) 0xC0000026L) +#endif + +#ifndef STATUS_UNWIND +# define STATUS_UNWIND ((NTSTATUS) 0xC0000027L) +#endif + +#ifndef STATUS_BAD_STACK +# define STATUS_BAD_STACK ((NTSTATUS) 0xC0000028L) +#endif + +#ifndef STATUS_INVALID_UNWIND_TARGET +# define STATUS_INVALID_UNWIND_TARGET ((NTSTATUS) 0xC0000029L) +#endif + +#ifndef STATUS_NOT_LOCKED +# define STATUS_NOT_LOCKED ((NTSTATUS) 0xC000002AL) +#endif + +#ifndef STATUS_PARITY_ERROR +# define STATUS_PARITY_ERROR ((NTSTATUS) 0xC000002BL) +#endif + +#ifndef STATUS_UNABLE_TO_DECOMMIT_VM +# define STATUS_UNABLE_TO_DECOMMIT_VM ((NTSTATUS) 0xC000002CL) +#endif + +#ifndef STATUS_NOT_COMMITTED +# define STATUS_NOT_COMMITTED ((NTSTATUS) 0xC000002DL) +#endif + +#ifndef STATUS_INVALID_PORT_ATTRIBUTES +# define STATUS_INVALID_PORT_ATTRIBUTES ((NTSTATUS) 0xC000002EL) +#endif + +#ifndef STATUS_PORT_MESSAGE_TOO_LONG +# define STATUS_PORT_MESSAGE_TOO_LONG ((NTSTATUS) 0xC000002FL) +#endif + +#ifndef STATUS_INVALID_PARAMETER_MIX +# define STATUS_INVALID_PARAMETER_MIX ((NTSTATUS) 0xC0000030L) +#endif + +#ifndef STATUS_INVALID_QUOTA_LOWER +# define STATUS_INVALID_QUOTA_LOWER ((NTSTATUS) 0xC0000031L) +#endif + +#ifndef STATUS_DISK_CORRUPT_ERROR +# define STATUS_DISK_CORRUPT_ERROR ((NTSTATUS) 0xC0000032L) +#endif + +#ifndef STATUS_OBJECT_NAME_INVALID +# define STATUS_OBJECT_NAME_INVALID ((NTSTATUS) 0xC0000033L) +#endif + +#ifndef STATUS_OBJECT_NAME_NOT_FOUND +# define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS) 0xC0000034L) +#endif + +#ifndef STATUS_OBJECT_NAME_COLLISION +# define STATUS_OBJECT_NAME_COLLISION ((NTSTATUS) 0xC0000035L) +#endif + +#ifndef STATUS_PORT_DISCONNECTED +# define STATUS_PORT_DISCONNECTED ((NTSTATUS) 0xC0000037L) +#endif + +#ifndef STATUS_DEVICE_ALREADY_ATTACHED +# define STATUS_DEVICE_ALREADY_ATTACHED ((NTSTATUS) 0xC0000038L) +#endif + +#ifndef STATUS_OBJECT_PATH_INVALID +# define STATUS_OBJECT_PATH_INVALID ((NTSTATUS) 0xC0000039L) +#endif + +#ifndef STATUS_OBJECT_PATH_NOT_FOUND +# define STATUS_OBJECT_PATH_NOT_FOUND ((NTSTATUS) 0xC000003AL) +#endif + +#ifndef STATUS_OBJECT_PATH_SYNTAX_BAD +# define STATUS_OBJECT_PATH_SYNTAX_BAD ((NTSTATUS) 0xC000003BL) +#endif + +#ifndef STATUS_DATA_OVERRUN +# define STATUS_DATA_OVERRUN ((NTSTATUS) 0xC000003CL) +#endif + +#ifndef STATUS_DATA_LATE_ERROR +# define STATUS_DATA_LATE_ERROR ((NTSTATUS) 0xC000003DL) +#endif + +#ifndef STATUS_DATA_ERROR +# define STATUS_DATA_ERROR ((NTSTATUS) 0xC000003EL) +#endif + +#ifndef STATUS_CRC_ERROR +# define STATUS_CRC_ERROR ((NTSTATUS) 0xC000003FL) +#endif + +#ifndef STATUS_SECTION_TOO_BIG +# define STATUS_SECTION_TOO_BIG ((NTSTATUS) 0xC0000040L) +#endif + +#ifndef STATUS_PORT_CONNECTION_REFUSED +# define STATUS_PORT_CONNECTION_REFUSED ((NTSTATUS) 0xC0000041L) +#endif + +#ifndef STATUS_INVALID_PORT_HANDLE +# define STATUS_INVALID_PORT_HANDLE ((NTSTATUS) 0xC0000042L) +#endif + +#ifndef STATUS_SHARING_VIOLATION +# define STATUS_SHARING_VIOLATION ((NTSTATUS) 0xC0000043L) +#endif + +#ifndef STATUS_QUOTA_EXCEEDED +# define STATUS_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000044L) +#endif + +#ifndef STATUS_INVALID_PAGE_PROTECTION +# define STATUS_INVALID_PAGE_PROTECTION ((NTSTATUS) 0xC0000045L) +#endif + +#ifndef STATUS_MUTANT_NOT_OWNED +# define STATUS_MUTANT_NOT_OWNED ((NTSTATUS) 0xC0000046L) +#endif + +#ifndef STATUS_SEMAPHORE_LIMIT_EXCEEDED +# define STATUS_SEMAPHORE_LIMIT_EXCEEDED ((NTSTATUS) 0xC0000047L) +#endif + +#ifndef STATUS_PORT_ALREADY_SET +# define STATUS_PORT_ALREADY_SET ((NTSTATUS) 0xC0000048L) +#endif + +#ifndef STATUS_SECTION_NOT_IMAGE +# define STATUS_SECTION_NOT_IMAGE ((NTSTATUS) 0xC0000049L) +#endif + +#ifndef STATUS_SUSPEND_COUNT_EXCEEDED +# define STATUS_SUSPEND_COUNT_EXCEEDED ((NTSTATUS) 0xC000004AL) +#endif + +#ifndef STATUS_THREAD_IS_TERMINATING +# define STATUS_THREAD_IS_TERMINATING ((NTSTATUS) 0xC000004BL) +#endif + +#ifndef STATUS_BAD_WORKING_SET_LIMIT +# define STATUS_BAD_WORKING_SET_LIMIT ((NTSTATUS) 0xC000004CL) +#endif + +#ifndef STATUS_INCOMPATIBLE_FILE_MAP +# define STATUS_INCOMPATIBLE_FILE_MAP ((NTSTATUS) 0xC000004DL) +#endif + +#ifndef STATUS_SECTION_PROTECTION +# define STATUS_SECTION_PROTECTION ((NTSTATUS) 0xC000004EL) +#endif + +#ifndef STATUS_EAS_NOT_SUPPORTED +# define STATUS_EAS_NOT_SUPPORTED ((NTSTATUS) 0xC000004FL) +#endif + +#ifndef STATUS_EA_TOO_LARGE +# define STATUS_EA_TOO_LARGE ((NTSTATUS) 0xC0000050L) +#endif + +#ifndef STATUS_NONEXISTENT_EA_ENTRY +# define STATUS_NONEXISTENT_EA_ENTRY ((NTSTATUS) 0xC0000051L) +#endif + +#ifndef STATUS_NO_EAS_ON_FILE +# define STATUS_NO_EAS_ON_FILE ((NTSTATUS) 0xC0000052L) +#endif + +#ifndef STATUS_EA_CORRUPT_ERROR +# define STATUS_EA_CORRUPT_ERROR ((NTSTATUS) 0xC0000053L) +#endif + +#ifndef STATUS_FILE_LOCK_CONFLICT +# define STATUS_FILE_LOCK_CONFLICT ((NTSTATUS) 0xC0000054L) +#endif + +#ifndef STATUS_LOCK_NOT_GRANTED +# define STATUS_LOCK_NOT_GRANTED ((NTSTATUS) 0xC0000055L) +#endif + +#ifndef STATUS_DELETE_PENDING +# define STATUS_DELETE_PENDING ((NTSTATUS) 0xC0000056L) +#endif + +#ifndef STATUS_CTL_FILE_NOT_SUPPORTED +# define STATUS_CTL_FILE_NOT_SUPPORTED ((NTSTATUS) 0xC0000057L) +#endif + +#ifndef STATUS_UNKNOWN_REVISION +# define STATUS_UNKNOWN_REVISION ((NTSTATUS) 0xC0000058L) +#endif + +#ifndef STATUS_REVISION_MISMATCH +# define STATUS_REVISION_MISMATCH ((NTSTATUS) 0xC0000059L) +#endif + +#ifndef STATUS_INVALID_OWNER +# define STATUS_INVALID_OWNER ((NTSTATUS) 0xC000005AL) +#endif + +#ifndef STATUS_INVALID_PRIMARY_GROUP +# define STATUS_INVALID_PRIMARY_GROUP ((NTSTATUS) 0xC000005BL) +#endif + +#ifndef STATUS_NO_IMPERSONATION_TOKEN +# define STATUS_NO_IMPERSONATION_TOKEN ((NTSTATUS) 0xC000005CL) +#endif + +#ifndef STATUS_CANT_DISABLE_MANDATORY +# define STATUS_CANT_DISABLE_MANDATORY ((NTSTATUS) 0xC000005DL) +#endif + +#ifndef STATUS_NO_LOGON_SERVERS +# define STATUS_NO_LOGON_SERVERS ((NTSTATUS) 0xC000005EL) +#endif + +#ifndef STATUS_NO_SUCH_LOGON_SESSION +# define STATUS_NO_SUCH_LOGON_SESSION ((NTSTATUS) 0xC000005FL) +#endif + +#ifndef STATUS_NO_SUCH_PRIVILEGE +# define STATUS_NO_SUCH_PRIVILEGE ((NTSTATUS) 0xC0000060L) +#endif + +#ifndef STATUS_PRIVILEGE_NOT_HELD +# define STATUS_PRIVILEGE_NOT_HELD ((NTSTATUS) 0xC0000061L) +#endif + +#ifndef STATUS_INVALID_ACCOUNT_NAME +# define STATUS_INVALID_ACCOUNT_NAME ((NTSTATUS) 0xC0000062L) +#endif + +#ifndef STATUS_USER_EXISTS +# define STATUS_USER_EXISTS ((NTSTATUS) 0xC0000063L) +#endif + +#ifndef STATUS_NO_SUCH_USER +# define STATUS_NO_SUCH_USER ((NTSTATUS) 0xC0000064L) +#endif + +#ifndef STATUS_GROUP_EXISTS +# define STATUS_GROUP_EXISTS ((NTSTATUS) 0xC0000065L) +#endif + +#ifndef STATUS_NO_SUCH_GROUP +# define STATUS_NO_SUCH_GROUP ((NTSTATUS) 0xC0000066L) +#endif + +#ifndef STATUS_MEMBER_IN_GROUP +# define STATUS_MEMBER_IN_GROUP ((NTSTATUS) 0xC0000067L) +#endif + +#ifndef STATUS_MEMBER_NOT_IN_GROUP +# define STATUS_MEMBER_NOT_IN_GROUP ((NTSTATUS) 0xC0000068L) +#endif + +#ifndef STATUS_LAST_ADMIN +# define STATUS_LAST_ADMIN ((NTSTATUS) 0xC0000069L) +#endif + +#ifndef STATUS_WRONG_PASSWORD +# define STATUS_WRONG_PASSWORD ((NTSTATUS) 0xC000006AL) +#endif + +#ifndef STATUS_ILL_FORMED_PASSWORD +# define STATUS_ILL_FORMED_PASSWORD ((NTSTATUS) 0xC000006BL) +#endif + +#ifndef STATUS_PASSWORD_RESTRICTION +# define STATUS_PASSWORD_RESTRICTION ((NTSTATUS) 0xC000006CL) +#endif + +#ifndef STATUS_LOGON_FAILURE +# define STATUS_LOGON_FAILURE ((NTSTATUS) 0xC000006DL) +#endif + +#ifndef STATUS_ACCOUNT_RESTRICTION +# define STATUS_ACCOUNT_RESTRICTION ((NTSTATUS) 0xC000006EL) +#endif + +#ifndef STATUS_INVALID_LOGON_HOURS +# define STATUS_INVALID_LOGON_HOURS ((NTSTATUS) 0xC000006FL) +#endif + +#ifndef STATUS_INVALID_WORKSTATION +# define STATUS_INVALID_WORKSTATION ((NTSTATUS) 0xC0000070L) +#endif + +#ifndef STATUS_PASSWORD_EXPIRED +# define STATUS_PASSWORD_EXPIRED ((NTSTATUS) 0xC0000071L) +#endif + +#ifndef STATUS_ACCOUNT_DISABLED +# define STATUS_ACCOUNT_DISABLED ((NTSTATUS) 0xC0000072L) +#endif + +#ifndef STATUS_NONE_MAPPED +# define STATUS_NONE_MAPPED ((NTSTATUS) 0xC0000073L) +#endif + +#ifndef STATUS_TOO_MANY_LUIDS_REQUESTED +# define STATUS_TOO_MANY_LUIDS_REQUESTED ((NTSTATUS) 0xC0000074L) +#endif + +#ifndef STATUS_LUIDS_EXHAUSTED +# define STATUS_LUIDS_EXHAUSTED ((NTSTATUS) 0xC0000075L) +#endif + +#ifndef STATUS_INVALID_SUB_AUTHORITY +# define STATUS_INVALID_SUB_AUTHORITY ((NTSTATUS) 0xC0000076L) +#endif + +#ifndef STATUS_INVALID_ACL +# define STATUS_INVALID_ACL ((NTSTATUS) 0xC0000077L) +#endif + +#ifndef STATUS_INVALID_SID +# define STATUS_INVALID_SID ((NTSTATUS) 0xC0000078L) +#endif + +#ifndef STATUS_INVALID_SECURITY_DESCR +# define STATUS_INVALID_SECURITY_DESCR ((NTSTATUS) 0xC0000079L) +#endif + +#ifndef STATUS_PROCEDURE_NOT_FOUND +# define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS) 0xC000007AL) +#endif + +#ifndef STATUS_INVALID_IMAGE_FORMAT +# define STATUS_INVALID_IMAGE_FORMAT ((NTSTATUS) 0xC000007BL) +#endif + +#ifndef STATUS_NO_TOKEN +# define STATUS_NO_TOKEN ((NTSTATUS) 0xC000007CL) +#endif + +#ifndef STATUS_BAD_INHERITANCE_ACL +# define STATUS_BAD_INHERITANCE_ACL ((NTSTATUS) 0xC000007DL) +#endif + +#ifndef STATUS_RANGE_NOT_LOCKED +# define STATUS_RANGE_NOT_LOCKED ((NTSTATUS) 0xC000007EL) +#endif + +#ifndef STATUS_DISK_FULL +# define STATUS_DISK_FULL ((NTSTATUS) 0xC000007FL) +#endif + +#ifndef STATUS_SERVER_DISABLED +# define STATUS_SERVER_DISABLED ((NTSTATUS) 0xC0000080L) +#endif + +#ifndef STATUS_SERVER_NOT_DISABLED +# define STATUS_SERVER_NOT_DISABLED ((NTSTATUS) 0xC0000081L) +#endif + +#ifndef STATUS_TOO_MANY_GUIDS_REQUESTED +# define STATUS_TOO_MANY_GUIDS_REQUESTED ((NTSTATUS) 0xC0000082L) +#endif + +#ifndef STATUS_GUIDS_EXHAUSTED +# define STATUS_GUIDS_EXHAUSTED ((NTSTATUS) 0xC0000083L) +#endif + +#ifndef STATUS_INVALID_ID_AUTHORITY +# define STATUS_INVALID_ID_AUTHORITY ((NTSTATUS) 0xC0000084L) +#endif + +#ifndef STATUS_AGENTS_EXHAUSTED +# define STATUS_AGENTS_EXHAUSTED ((NTSTATUS) 0xC0000085L) +#endif + +#ifndef STATUS_INVALID_VOLUME_LABEL +# define STATUS_INVALID_VOLUME_LABEL ((NTSTATUS) 0xC0000086L) +#endif + +#ifndef STATUS_SECTION_NOT_EXTENDED +# define STATUS_SECTION_NOT_EXTENDED ((NTSTATUS) 0xC0000087L) +#endif + +#ifndef STATUS_NOT_MAPPED_DATA +# define STATUS_NOT_MAPPED_DATA ((NTSTATUS) 0xC0000088L) +#endif + +#ifndef STATUS_RESOURCE_DATA_NOT_FOUND +# define STATUS_RESOURCE_DATA_NOT_FOUND ((NTSTATUS) 0xC0000089L) +#endif + +#ifndef STATUS_RESOURCE_TYPE_NOT_FOUND +# define STATUS_RESOURCE_TYPE_NOT_FOUND ((NTSTATUS) 0xC000008AL) +#endif + +#ifndef STATUS_RESOURCE_NAME_NOT_FOUND +# define STATUS_RESOURCE_NAME_NOT_FOUND ((NTSTATUS) 0xC000008BL) +#endif + +#ifndef STATUS_ARRAY_BOUNDS_EXCEEDED +# define STATUS_ARRAY_BOUNDS_EXCEEDED ((NTSTATUS) 0xC000008CL) +#endif + +#ifndef STATUS_FLOAT_DENORMAL_OPERAND +# define STATUS_FLOAT_DENORMAL_OPERAND ((NTSTATUS) 0xC000008DL) +#endif + +#ifndef STATUS_FLOAT_DIVIDE_BY_ZERO +# define STATUS_FLOAT_DIVIDE_BY_ZERO ((NTSTATUS) 0xC000008EL) +#endif + +#ifndef STATUS_FLOAT_INEXACT_RESULT +# define STATUS_FLOAT_INEXACT_RESULT ((NTSTATUS) 0xC000008FL) +#endif + +#ifndef STATUS_FLOAT_INVALID_OPERATION +# define STATUS_FLOAT_INVALID_OPERATION ((NTSTATUS) 0xC0000090L) +#endif + +#ifndef STATUS_FLOAT_OVERFLOW +# define STATUS_FLOAT_OVERFLOW ((NTSTATUS) 0xC0000091L) +#endif + +#ifndef STATUS_FLOAT_STACK_CHECK +# define STATUS_FLOAT_STACK_CHECK ((NTSTATUS) 0xC0000092L) +#endif + +#ifndef STATUS_FLOAT_UNDERFLOW +# define STATUS_FLOAT_UNDERFLOW ((NTSTATUS) 0xC0000093L) +#endif + +#ifndef STATUS_INTEGER_DIVIDE_BY_ZERO +# define STATUS_INTEGER_DIVIDE_BY_ZERO ((NTSTATUS) 0xC0000094L) +#endif + +#ifndef STATUS_INTEGER_OVERFLOW +# define STATUS_INTEGER_OVERFLOW ((NTSTATUS) 0xC0000095L) +#endif + +#ifndef STATUS_PRIVILEGED_INSTRUCTION +# define STATUS_PRIVILEGED_INSTRUCTION ((NTSTATUS) 0xC0000096L) +#endif + +#ifndef STATUS_TOO_MANY_PAGING_FILES +# define STATUS_TOO_MANY_PAGING_FILES ((NTSTATUS) 0xC0000097L) +#endif + +#ifndef STATUS_FILE_INVALID +# define STATUS_FILE_INVALID ((NTSTATUS) 0xC0000098L) +#endif + +#ifndef STATUS_ALLOTTED_SPACE_EXCEEDED +# define STATUS_ALLOTTED_SPACE_EXCEEDED ((NTSTATUS) 0xC0000099L) +#endif + +#ifndef STATUS_INSUFFICIENT_RESOURCES +# define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS) 0xC000009AL) +#endif + +#ifndef STATUS_DFS_EXIT_PATH_FOUND +# define STATUS_DFS_EXIT_PATH_FOUND ((NTSTATUS) 0xC000009BL) +#endif + +#ifndef STATUS_DEVICE_DATA_ERROR +# define STATUS_DEVICE_DATA_ERROR ((NTSTATUS) 0xC000009CL) +#endif + +#ifndef STATUS_DEVICE_NOT_CONNECTED +# define STATUS_DEVICE_NOT_CONNECTED ((NTSTATUS) 0xC000009DL) +#endif + +#ifndef STATUS_DEVICE_POWER_FAILURE +# define STATUS_DEVICE_POWER_FAILURE ((NTSTATUS) 0xC000009EL) +#endif + +#ifndef STATUS_FREE_VM_NOT_AT_BASE +# define STATUS_FREE_VM_NOT_AT_BASE ((NTSTATUS) 0xC000009FL) +#endif + +#ifndef STATUS_MEMORY_NOT_ALLOCATED +# define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS) 0xC00000A0L) +#endif + +#ifndef STATUS_WORKING_SET_QUOTA +# define STATUS_WORKING_SET_QUOTA ((NTSTATUS) 0xC00000A1L) +#endif + +#ifndef STATUS_MEDIA_WRITE_PROTECTED +# define STATUS_MEDIA_WRITE_PROTECTED ((NTSTATUS) 0xC00000A2L) +#endif + +#ifndef STATUS_DEVICE_NOT_READY +# define STATUS_DEVICE_NOT_READY ((NTSTATUS) 0xC00000A3L) +#endif + +#ifndef STATUS_INVALID_GROUP_ATTRIBUTES +# define STATUS_INVALID_GROUP_ATTRIBUTES ((NTSTATUS) 0xC00000A4L) +#endif + +#ifndef STATUS_BAD_IMPERSONATION_LEVEL +# define STATUS_BAD_IMPERSONATION_LEVEL ((NTSTATUS) 0xC00000A5L) +#endif + +#ifndef STATUS_CANT_OPEN_ANONYMOUS +# define STATUS_CANT_OPEN_ANONYMOUS ((NTSTATUS) 0xC00000A6L) +#endif + +#ifndef STATUS_BAD_VALIDATION_CLASS +# define STATUS_BAD_VALIDATION_CLASS ((NTSTATUS) 0xC00000A7L) +#endif + +#ifndef STATUS_BAD_TOKEN_TYPE +# define STATUS_BAD_TOKEN_TYPE ((NTSTATUS) 0xC00000A8L) +#endif + +#ifndef STATUS_BAD_MASTER_BOOT_RECORD +# define STATUS_BAD_MASTER_BOOT_RECORD ((NTSTATUS) 0xC00000A9L) +#endif + +#ifndef STATUS_INSTRUCTION_MISALIGNMENT +# define STATUS_INSTRUCTION_MISALIGNMENT ((NTSTATUS) 0xC00000AAL) +#endif + +#ifndef STATUS_INSTANCE_NOT_AVAILABLE +# define STATUS_INSTANCE_NOT_AVAILABLE ((NTSTATUS) 0xC00000ABL) +#endif + +#ifndef STATUS_PIPE_NOT_AVAILABLE +# define STATUS_PIPE_NOT_AVAILABLE ((NTSTATUS) 0xC00000ACL) +#endif + +#ifndef STATUS_INVALID_PIPE_STATE +# define STATUS_INVALID_PIPE_STATE ((NTSTATUS) 0xC00000ADL) +#endif + +#ifndef STATUS_PIPE_BUSY +# define STATUS_PIPE_BUSY ((NTSTATUS) 0xC00000AEL) +#endif + +#ifndef STATUS_ILLEGAL_FUNCTION +# define STATUS_ILLEGAL_FUNCTION ((NTSTATUS) 0xC00000AFL) +#endif + +#ifndef STATUS_PIPE_DISCONNECTED +# define STATUS_PIPE_DISCONNECTED ((NTSTATUS) 0xC00000B0L) +#endif + +#ifndef STATUS_PIPE_CLOSING +# define STATUS_PIPE_CLOSING ((NTSTATUS) 0xC00000B1L) +#endif + +#ifndef STATUS_PIPE_CONNECTED +# define STATUS_PIPE_CONNECTED ((NTSTATUS) 0xC00000B2L) +#endif + +#ifndef STATUS_PIPE_LISTENING +# define STATUS_PIPE_LISTENING ((NTSTATUS) 0xC00000B3L) +#endif + +#ifndef STATUS_INVALID_READ_MODE +# define STATUS_INVALID_READ_MODE ((NTSTATUS) 0xC00000B4L) +#endif + +#ifndef STATUS_IO_TIMEOUT +# define STATUS_IO_TIMEOUT ((NTSTATUS) 0xC00000B5L) +#endif + +#ifndef STATUS_FILE_FORCED_CLOSED +# define STATUS_FILE_FORCED_CLOSED ((NTSTATUS) 0xC00000B6L) +#endif + +#ifndef STATUS_PROFILING_NOT_STARTED +# define STATUS_PROFILING_NOT_STARTED ((NTSTATUS) 0xC00000B7L) +#endif + +#ifndef STATUS_PROFILING_NOT_STOPPED +# define STATUS_PROFILING_NOT_STOPPED ((NTSTATUS) 0xC00000B8L) +#endif + +#ifndef STATUS_COULD_NOT_INTERPRET +# define STATUS_COULD_NOT_INTERPRET ((NTSTATUS) 0xC00000B9L) +#endif + +#ifndef STATUS_FILE_IS_A_DIRECTORY +# define STATUS_FILE_IS_A_DIRECTORY ((NTSTATUS) 0xC00000BAL) +#endif + +#ifndef STATUS_NOT_SUPPORTED +# define STATUS_NOT_SUPPORTED ((NTSTATUS) 0xC00000BBL) +#endif + +#ifndef STATUS_REMOTE_NOT_LISTENING +# define STATUS_REMOTE_NOT_LISTENING ((NTSTATUS) 0xC00000BCL) +#endif + +#ifndef STATUS_DUPLICATE_NAME +# define STATUS_DUPLICATE_NAME ((NTSTATUS) 0xC00000BDL) +#endif + +#ifndef STATUS_BAD_NETWORK_PATH +# define STATUS_BAD_NETWORK_PATH ((NTSTATUS) 0xC00000BEL) +#endif + +#ifndef STATUS_NETWORK_BUSY +# define STATUS_NETWORK_BUSY ((NTSTATUS) 0xC00000BFL) +#endif + +#ifndef STATUS_DEVICE_DOES_NOT_EXIST +# define STATUS_DEVICE_DOES_NOT_EXIST ((NTSTATUS) 0xC00000C0L) +#endif + +#ifndef STATUS_TOO_MANY_COMMANDS +# define STATUS_TOO_MANY_COMMANDS ((NTSTATUS) 0xC00000C1L) +#endif + +#ifndef STATUS_ADAPTER_HARDWARE_ERROR +# define STATUS_ADAPTER_HARDWARE_ERROR ((NTSTATUS) 0xC00000C2L) +#endif + +#ifndef STATUS_INVALID_NETWORK_RESPONSE +# define STATUS_INVALID_NETWORK_RESPONSE ((NTSTATUS) 0xC00000C3L) +#endif + +#ifndef STATUS_UNEXPECTED_NETWORK_ERROR +# define STATUS_UNEXPECTED_NETWORK_ERROR ((NTSTATUS) 0xC00000C4L) +#endif + +#ifndef STATUS_BAD_REMOTE_ADAPTER +# define STATUS_BAD_REMOTE_ADAPTER ((NTSTATUS) 0xC00000C5L) +#endif + +#ifndef STATUS_PRINT_QUEUE_FULL +# define STATUS_PRINT_QUEUE_FULL ((NTSTATUS) 0xC00000C6L) +#endif + +#ifndef STATUS_NO_SPOOL_SPACE +# define STATUS_NO_SPOOL_SPACE ((NTSTATUS) 0xC00000C7L) +#endif + +#ifndef STATUS_PRINT_CANCELLED +# define STATUS_PRINT_CANCELLED ((NTSTATUS) 0xC00000C8L) +#endif + +#ifndef STATUS_NETWORK_NAME_DELETED +# define STATUS_NETWORK_NAME_DELETED ((NTSTATUS) 0xC00000C9L) +#endif + +#ifndef STATUS_NETWORK_ACCESS_DENIED +# define STATUS_NETWORK_ACCESS_DENIED ((NTSTATUS) 0xC00000CAL) +#endif + +#ifndef STATUS_BAD_DEVICE_TYPE +# define STATUS_BAD_DEVICE_TYPE ((NTSTATUS) 0xC00000CBL) +#endif + +#ifndef STATUS_BAD_NETWORK_NAME +# define STATUS_BAD_NETWORK_NAME ((NTSTATUS) 0xC00000CCL) +#endif + +#ifndef STATUS_TOO_MANY_NAMES +# define STATUS_TOO_MANY_NAMES ((NTSTATUS) 0xC00000CDL) +#endif + +#ifndef STATUS_TOO_MANY_SESSIONS +# define STATUS_TOO_MANY_SESSIONS ((NTSTATUS) 0xC00000CEL) +#endif + +#ifndef STATUS_SHARING_PAUSED +# define STATUS_SHARING_PAUSED ((NTSTATUS) 0xC00000CFL) +#endif + +#ifndef STATUS_REQUEST_NOT_ACCEPTED +# define STATUS_REQUEST_NOT_ACCEPTED ((NTSTATUS) 0xC00000D0L) +#endif + +#ifndef STATUS_REDIRECTOR_PAUSED +# define STATUS_REDIRECTOR_PAUSED ((NTSTATUS) 0xC00000D1L) +#endif + +#ifndef STATUS_NET_WRITE_FAULT +# define STATUS_NET_WRITE_FAULT ((NTSTATUS) 0xC00000D2L) +#endif + +#ifndef STATUS_PROFILING_AT_LIMIT +# define STATUS_PROFILING_AT_LIMIT ((NTSTATUS) 0xC00000D3L) +#endif + +#ifndef STATUS_NOT_SAME_DEVICE +# define STATUS_NOT_SAME_DEVICE ((NTSTATUS) 0xC00000D4L) +#endif + +#ifndef STATUS_FILE_RENAMED +# define STATUS_FILE_RENAMED ((NTSTATUS) 0xC00000D5L) +#endif + +#ifndef STATUS_VIRTUAL_CIRCUIT_CLOSED +# define STATUS_VIRTUAL_CIRCUIT_CLOSED ((NTSTATUS) 0xC00000D6L) +#endif + +#ifndef STATUS_NO_SECURITY_ON_OBJECT +# define STATUS_NO_SECURITY_ON_OBJECT ((NTSTATUS) 0xC00000D7L) +#endif + +#ifndef STATUS_CANT_WAIT +# define STATUS_CANT_WAIT ((NTSTATUS) 0xC00000D8L) +#endif + +#ifndef STATUS_PIPE_EMPTY +# define STATUS_PIPE_EMPTY ((NTSTATUS) 0xC00000D9L) +#endif + +#ifndef STATUS_CANT_ACCESS_DOMAIN_INFO +# define STATUS_CANT_ACCESS_DOMAIN_INFO ((NTSTATUS) 0xC00000DAL) +#endif + +#ifndef STATUS_CANT_TERMINATE_SELF +# define STATUS_CANT_TERMINATE_SELF ((NTSTATUS) 0xC00000DBL) +#endif + +#ifndef STATUS_INVALID_SERVER_STATE +# define STATUS_INVALID_SERVER_STATE ((NTSTATUS) 0xC00000DCL) +#endif + +#ifndef STATUS_INVALID_DOMAIN_STATE +# define STATUS_INVALID_DOMAIN_STATE ((NTSTATUS) 0xC00000DDL) +#endif + +#ifndef STATUS_INVALID_DOMAIN_ROLE +# define STATUS_INVALID_DOMAIN_ROLE ((NTSTATUS) 0xC00000DEL) +#endif + +#ifndef STATUS_NO_SUCH_DOMAIN +# define STATUS_NO_SUCH_DOMAIN ((NTSTATUS) 0xC00000DFL) +#endif + +#ifndef STATUS_DOMAIN_EXISTS +# define STATUS_DOMAIN_EXISTS ((NTSTATUS) 0xC00000E0L) +#endif + +#ifndef STATUS_DOMAIN_LIMIT_EXCEEDED +# define STATUS_DOMAIN_LIMIT_EXCEEDED ((NTSTATUS) 0xC00000E1L) +#endif + +#ifndef STATUS_OPLOCK_NOT_GRANTED +# define STATUS_OPLOCK_NOT_GRANTED ((NTSTATUS) 0xC00000E2L) +#endif + +#ifndef STATUS_INVALID_OPLOCK_PROTOCOL +# define STATUS_INVALID_OPLOCK_PROTOCOL ((NTSTATUS) 0xC00000E3L) +#endif + +#ifndef STATUS_INTERNAL_DB_CORRUPTION +# define STATUS_INTERNAL_DB_CORRUPTION ((NTSTATUS) 0xC00000E4L) +#endif + +#ifndef STATUS_INTERNAL_ERROR +# define STATUS_INTERNAL_ERROR ((NTSTATUS) 0xC00000E5L) +#endif + +#ifndef STATUS_GENERIC_NOT_MAPPED +# define STATUS_GENERIC_NOT_MAPPED ((NTSTATUS) 0xC00000E6L) +#endif + +#ifndef STATUS_BAD_DESCRIPTOR_FORMAT +# define STATUS_BAD_DESCRIPTOR_FORMAT ((NTSTATUS) 0xC00000E7L) +#endif + +#ifndef STATUS_INVALID_USER_BUFFER +# define STATUS_INVALID_USER_BUFFER ((NTSTATUS) 0xC00000E8L) +#endif + +#ifndef STATUS_UNEXPECTED_IO_ERROR +# define STATUS_UNEXPECTED_IO_ERROR ((NTSTATUS) 0xC00000E9L) +#endif + +#ifndef STATUS_UNEXPECTED_MM_CREATE_ERR +# define STATUS_UNEXPECTED_MM_CREATE_ERR ((NTSTATUS) 0xC00000EAL) +#endif + +#ifndef STATUS_UNEXPECTED_MM_MAP_ERROR +# define STATUS_UNEXPECTED_MM_MAP_ERROR ((NTSTATUS) 0xC00000EBL) +#endif + +#ifndef STATUS_UNEXPECTED_MM_EXTEND_ERR +# define STATUS_UNEXPECTED_MM_EXTEND_ERR ((NTSTATUS) 0xC00000ECL) +#endif + +#ifndef STATUS_NOT_LOGON_PROCESS +# define STATUS_NOT_LOGON_PROCESS ((NTSTATUS) 0xC00000EDL) +#endif + +#ifndef STATUS_LOGON_SESSION_EXISTS +# define STATUS_LOGON_SESSION_EXISTS ((NTSTATUS) 0xC00000EEL) +#endif + +#ifndef STATUS_INVALID_PARAMETER_1 +# define STATUS_INVALID_PARAMETER_1 ((NTSTATUS) 0xC00000EFL) +#endif + +#ifndef STATUS_INVALID_PARAMETER_2 +# define STATUS_INVALID_PARAMETER_2 ((NTSTATUS) 0xC00000F0L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_3 +# define STATUS_INVALID_PARAMETER_3 ((NTSTATUS) 0xC00000F1L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_4 +# define STATUS_INVALID_PARAMETER_4 ((NTSTATUS) 0xC00000F2L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_5 +# define STATUS_INVALID_PARAMETER_5 ((NTSTATUS) 0xC00000F3L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_6 +# define STATUS_INVALID_PARAMETER_6 ((NTSTATUS) 0xC00000F4L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_7 +# define STATUS_INVALID_PARAMETER_7 ((NTSTATUS) 0xC00000F5L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_8 +# define STATUS_INVALID_PARAMETER_8 ((NTSTATUS) 0xC00000F6L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_9 +# define STATUS_INVALID_PARAMETER_9 ((NTSTATUS) 0xC00000F7L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_10 +# define STATUS_INVALID_PARAMETER_10 ((NTSTATUS) 0xC00000F8L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_11 +# define STATUS_INVALID_PARAMETER_11 ((NTSTATUS) 0xC00000F9L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_12 +# define STATUS_INVALID_PARAMETER_12 ((NTSTATUS) 0xC00000FAL) +#endif + +#ifndef STATUS_REDIRECTOR_NOT_STARTED +# define STATUS_REDIRECTOR_NOT_STARTED ((NTSTATUS) 0xC00000FBL) +#endif + +#ifndef STATUS_REDIRECTOR_STARTED +# define STATUS_REDIRECTOR_STARTED ((NTSTATUS) 0xC00000FCL) +#endif + +#ifndef STATUS_STACK_OVERFLOW +# define STATUS_STACK_OVERFLOW ((NTSTATUS) 0xC00000FDL) +#endif + +#ifndef STATUS_NO_SUCH_PACKAGE +# define STATUS_NO_SUCH_PACKAGE ((NTSTATUS) 0xC00000FEL) +#endif + +#ifndef STATUS_BAD_FUNCTION_TABLE +# define STATUS_BAD_FUNCTION_TABLE ((NTSTATUS) 0xC00000FFL) +#endif + +#ifndef STATUS_VARIABLE_NOT_FOUND +# define STATUS_VARIABLE_NOT_FOUND ((NTSTATUS) 0xC0000100L) +#endif + +#ifndef STATUS_DIRECTORY_NOT_EMPTY +# define STATUS_DIRECTORY_NOT_EMPTY ((NTSTATUS) 0xC0000101L) +#endif + +#ifndef STATUS_FILE_CORRUPT_ERROR +# define STATUS_FILE_CORRUPT_ERROR ((NTSTATUS) 0xC0000102L) +#endif + +#ifndef STATUS_NOT_A_DIRECTORY +# define STATUS_NOT_A_DIRECTORY ((NTSTATUS) 0xC0000103L) +#endif + +#ifndef STATUS_BAD_LOGON_SESSION_STATE +# define STATUS_BAD_LOGON_SESSION_STATE ((NTSTATUS) 0xC0000104L) +#endif + +#ifndef STATUS_LOGON_SESSION_COLLISION +# define STATUS_LOGON_SESSION_COLLISION ((NTSTATUS) 0xC0000105L) +#endif + +#ifndef STATUS_NAME_TOO_LONG +# define STATUS_NAME_TOO_LONG ((NTSTATUS) 0xC0000106L) +#endif + +#ifndef STATUS_FILES_OPEN +# define STATUS_FILES_OPEN ((NTSTATUS) 0xC0000107L) +#endif + +#ifndef STATUS_CONNECTION_IN_USE +# define STATUS_CONNECTION_IN_USE ((NTSTATUS) 0xC0000108L) +#endif + +#ifndef STATUS_MESSAGE_NOT_FOUND +# define STATUS_MESSAGE_NOT_FOUND ((NTSTATUS) 0xC0000109L) +#endif + +#ifndef STATUS_PROCESS_IS_TERMINATING +# define STATUS_PROCESS_IS_TERMINATING ((NTSTATUS) 0xC000010AL) +#endif + +#ifndef STATUS_INVALID_LOGON_TYPE +# define STATUS_INVALID_LOGON_TYPE ((NTSTATUS) 0xC000010BL) +#endif + +#ifndef STATUS_NO_GUID_TRANSLATION +# define STATUS_NO_GUID_TRANSLATION ((NTSTATUS) 0xC000010CL) +#endif + +#ifndef STATUS_CANNOT_IMPERSONATE +# define STATUS_CANNOT_IMPERSONATE ((NTSTATUS) 0xC000010DL) +#endif + +#ifndef STATUS_IMAGE_ALREADY_LOADED +# define STATUS_IMAGE_ALREADY_LOADED ((NTSTATUS) 0xC000010EL) +#endif + +#ifndef STATUS_ABIOS_NOT_PRESENT +# define STATUS_ABIOS_NOT_PRESENT ((NTSTATUS) 0xC000010FL) +#endif + +#ifndef STATUS_ABIOS_LID_NOT_EXIST +# define STATUS_ABIOS_LID_NOT_EXIST ((NTSTATUS) 0xC0000110L) +#endif + +#ifndef STATUS_ABIOS_LID_ALREADY_OWNED +# define STATUS_ABIOS_LID_ALREADY_OWNED ((NTSTATUS) 0xC0000111L) +#endif + +#ifndef STATUS_ABIOS_NOT_LID_OWNER +# define STATUS_ABIOS_NOT_LID_OWNER ((NTSTATUS) 0xC0000112L) +#endif + +#ifndef STATUS_ABIOS_INVALID_COMMAND +# define STATUS_ABIOS_INVALID_COMMAND ((NTSTATUS) 0xC0000113L) +#endif + +#ifndef STATUS_ABIOS_INVALID_LID +# define STATUS_ABIOS_INVALID_LID ((NTSTATUS) 0xC0000114L) +#endif + +#ifndef STATUS_ABIOS_SELECTOR_NOT_AVAILABLE +# define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE ((NTSTATUS) 0xC0000115L) +#endif + +#ifndef STATUS_ABIOS_INVALID_SELECTOR +# define STATUS_ABIOS_INVALID_SELECTOR ((NTSTATUS) 0xC0000116L) +#endif + +#ifndef STATUS_NO_LDT +# define STATUS_NO_LDT ((NTSTATUS) 0xC0000117L) +#endif + +#ifndef STATUS_INVALID_LDT_SIZE +# define STATUS_INVALID_LDT_SIZE ((NTSTATUS) 0xC0000118L) +#endif + +#ifndef STATUS_INVALID_LDT_OFFSET +# define STATUS_INVALID_LDT_OFFSET ((NTSTATUS) 0xC0000119L) +#endif + +#ifndef STATUS_INVALID_LDT_DESCRIPTOR +# define STATUS_INVALID_LDT_DESCRIPTOR ((NTSTATUS) 0xC000011AL) +#endif + +#ifndef STATUS_INVALID_IMAGE_NE_FORMAT +# define STATUS_INVALID_IMAGE_NE_FORMAT ((NTSTATUS) 0xC000011BL) +#endif + +#ifndef STATUS_RXACT_INVALID_STATE +# define STATUS_RXACT_INVALID_STATE ((NTSTATUS) 0xC000011CL) +#endif + +#ifndef STATUS_RXACT_COMMIT_FAILURE +# define STATUS_RXACT_COMMIT_FAILURE ((NTSTATUS) 0xC000011DL) +#endif + +#ifndef STATUS_MAPPED_FILE_SIZE_ZERO +# define STATUS_MAPPED_FILE_SIZE_ZERO ((NTSTATUS) 0xC000011EL) +#endif + +#ifndef STATUS_TOO_MANY_OPENED_FILES +# define STATUS_TOO_MANY_OPENED_FILES ((NTSTATUS) 0xC000011FL) +#endif + +#ifndef STATUS_CANCELLED +# define STATUS_CANCELLED ((NTSTATUS) 0xC0000120L) +#endif + +#ifndef STATUS_CANNOT_DELETE +# define STATUS_CANNOT_DELETE ((NTSTATUS) 0xC0000121L) +#endif + +#ifndef STATUS_INVALID_COMPUTER_NAME +# define STATUS_INVALID_COMPUTER_NAME ((NTSTATUS) 0xC0000122L) +#endif + +#ifndef STATUS_FILE_DELETED +# define STATUS_FILE_DELETED ((NTSTATUS) 0xC0000123L) +#endif + +#ifndef STATUS_SPECIAL_ACCOUNT +# define STATUS_SPECIAL_ACCOUNT ((NTSTATUS) 0xC0000124L) +#endif + +#ifndef STATUS_SPECIAL_GROUP +# define STATUS_SPECIAL_GROUP ((NTSTATUS) 0xC0000125L) +#endif + +#ifndef STATUS_SPECIAL_USER +# define STATUS_SPECIAL_USER ((NTSTATUS) 0xC0000126L) +#endif + +#ifndef STATUS_MEMBERS_PRIMARY_GROUP +# define STATUS_MEMBERS_PRIMARY_GROUP ((NTSTATUS) 0xC0000127L) +#endif + +#ifndef STATUS_FILE_CLOSED +# define STATUS_FILE_CLOSED ((NTSTATUS) 0xC0000128L) +#endif + +#ifndef STATUS_TOO_MANY_THREADS +# define STATUS_TOO_MANY_THREADS ((NTSTATUS) 0xC0000129L) +#endif + +#ifndef STATUS_THREAD_NOT_IN_PROCESS +# define STATUS_THREAD_NOT_IN_PROCESS ((NTSTATUS) 0xC000012AL) +#endif + +#ifndef STATUS_TOKEN_ALREADY_IN_USE +# define STATUS_TOKEN_ALREADY_IN_USE ((NTSTATUS) 0xC000012BL) +#endif + +#ifndef STATUS_PAGEFILE_QUOTA_EXCEEDED +# define STATUS_PAGEFILE_QUOTA_EXCEEDED ((NTSTATUS) 0xC000012CL) +#endif + +#ifndef STATUS_COMMITMENT_LIMIT +# define STATUS_COMMITMENT_LIMIT ((NTSTATUS) 0xC000012DL) +#endif + +#ifndef STATUS_INVALID_IMAGE_LE_FORMAT +# define STATUS_INVALID_IMAGE_LE_FORMAT ((NTSTATUS) 0xC000012EL) +#endif + +#ifndef STATUS_INVALID_IMAGE_NOT_MZ +# define STATUS_INVALID_IMAGE_NOT_MZ ((NTSTATUS) 0xC000012FL) +#endif + +#ifndef STATUS_INVALID_IMAGE_PROTECT +# define STATUS_INVALID_IMAGE_PROTECT ((NTSTATUS) 0xC0000130L) +#endif + +#ifndef STATUS_INVALID_IMAGE_WIN_16 +# define STATUS_INVALID_IMAGE_WIN_16 ((NTSTATUS) 0xC0000131L) +#endif + +#ifndef STATUS_LOGON_SERVER_CONFLICT +# define STATUS_LOGON_SERVER_CONFLICT ((NTSTATUS) 0xC0000132L) +#endif + +#ifndef STATUS_TIME_DIFFERENCE_AT_DC +# define STATUS_TIME_DIFFERENCE_AT_DC ((NTSTATUS) 0xC0000133L) +#endif + +#ifndef STATUS_SYNCHRONIZATION_REQUIRED +# define STATUS_SYNCHRONIZATION_REQUIRED ((NTSTATUS) 0xC0000134L) +#endif + +#ifndef STATUS_DLL_NOT_FOUND +# define STATUS_DLL_NOT_FOUND ((NTSTATUS) 0xC0000135L) +#endif + +#ifndef STATUS_OPEN_FAILED +# define STATUS_OPEN_FAILED ((NTSTATUS) 0xC0000136L) +#endif + +#ifndef STATUS_IO_PRIVILEGE_FAILED +# define STATUS_IO_PRIVILEGE_FAILED ((NTSTATUS) 0xC0000137L) +#endif + +#ifndef STATUS_ORDINAL_NOT_FOUND +# define STATUS_ORDINAL_NOT_FOUND ((NTSTATUS) 0xC0000138L) +#endif + +#ifndef STATUS_ENTRYPOINT_NOT_FOUND +# define STATUS_ENTRYPOINT_NOT_FOUND ((NTSTATUS) 0xC0000139L) +#endif + +#ifndef STATUS_CONTROL_C_EXIT +# define STATUS_CONTROL_C_EXIT ((NTSTATUS) 0xC000013AL) +#endif + +#ifndef STATUS_LOCAL_DISCONNECT +# define STATUS_LOCAL_DISCONNECT ((NTSTATUS) 0xC000013BL) +#endif + +#ifndef STATUS_REMOTE_DISCONNECT +# define STATUS_REMOTE_DISCONNECT ((NTSTATUS) 0xC000013CL) +#endif + +#ifndef STATUS_REMOTE_RESOURCES +# define STATUS_REMOTE_RESOURCES ((NTSTATUS) 0xC000013DL) +#endif + +#ifndef STATUS_LINK_FAILED +# define STATUS_LINK_FAILED ((NTSTATUS) 0xC000013EL) +#endif + +#ifndef STATUS_LINK_TIMEOUT +# define STATUS_LINK_TIMEOUT ((NTSTATUS) 0xC000013FL) +#endif + +#ifndef STATUS_INVALID_CONNECTION +# define STATUS_INVALID_CONNECTION ((NTSTATUS) 0xC0000140L) +#endif + +#ifndef STATUS_INVALID_ADDRESS +# define STATUS_INVALID_ADDRESS ((NTSTATUS) 0xC0000141L) +#endif + +#ifndef STATUS_DLL_INIT_FAILED +# define STATUS_DLL_INIT_FAILED ((NTSTATUS) 0xC0000142L) +#endif + +#ifndef STATUS_MISSING_SYSTEMFILE +# define STATUS_MISSING_SYSTEMFILE ((NTSTATUS) 0xC0000143L) +#endif + +#ifndef STATUS_UNHANDLED_EXCEPTION +# define STATUS_UNHANDLED_EXCEPTION ((NTSTATUS) 0xC0000144L) +#endif + +#ifndef STATUS_APP_INIT_FAILURE +# define STATUS_APP_INIT_FAILURE ((NTSTATUS) 0xC0000145L) +#endif + +#ifndef STATUS_PAGEFILE_CREATE_FAILED +# define STATUS_PAGEFILE_CREATE_FAILED ((NTSTATUS) 0xC0000146L) +#endif + +#ifndef STATUS_NO_PAGEFILE +# define STATUS_NO_PAGEFILE ((NTSTATUS) 0xC0000147L) +#endif + +#ifndef STATUS_INVALID_LEVEL +# define STATUS_INVALID_LEVEL ((NTSTATUS) 0xC0000148L) +#endif + +#ifndef STATUS_WRONG_PASSWORD_CORE +# define STATUS_WRONG_PASSWORD_CORE ((NTSTATUS) 0xC0000149L) +#endif + +#ifndef STATUS_ILLEGAL_FLOAT_CONTEXT +# define STATUS_ILLEGAL_FLOAT_CONTEXT ((NTSTATUS) 0xC000014AL) +#endif + +#ifndef STATUS_PIPE_BROKEN +# define STATUS_PIPE_BROKEN ((NTSTATUS) 0xC000014BL) +#endif + +#ifndef STATUS_REGISTRY_CORRUPT +# define STATUS_REGISTRY_CORRUPT ((NTSTATUS) 0xC000014CL) +#endif + +#ifndef STATUS_REGISTRY_IO_FAILED +# define STATUS_REGISTRY_IO_FAILED ((NTSTATUS) 0xC000014DL) +#endif + +#ifndef STATUS_NO_EVENT_PAIR +# define STATUS_NO_EVENT_PAIR ((NTSTATUS) 0xC000014EL) +#endif + +#ifndef STATUS_UNRECOGNIZED_VOLUME +# define STATUS_UNRECOGNIZED_VOLUME ((NTSTATUS) 0xC000014FL) +#endif + +#ifndef STATUS_SERIAL_NO_DEVICE_INITED +# define STATUS_SERIAL_NO_DEVICE_INITED ((NTSTATUS) 0xC0000150L) +#endif + +#ifndef STATUS_NO_SUCH_ALIAS +# define STATUS_NO_SUCH_ALIAS ((NTSTATUS) 0xC0000151L) +#endif + +#ifndef STATUS_MEMBER_NOT_IN_ALIAS +# define STATUS_MEMBER_NOT_IN_ALIAS ((NTSTATUS) 0xC0000152L) +#endif + +#ifndef STATUS_MEMBER_IN_ALIAS +# define STATUS_MEMBER_IN_ALIAS ((NTSTATUS) 0xC0000153L) +#endif + +#ifndef STATUS_ALIAS_EXISTS +# define STATUS_ALIAS_EXISTS ((NTSTATUS) 0xC0000154L) +#endif + +#ifndef STATUS_LOGON_NOT_GRANTED +# define STATUS_LOGON_NOT_GRANTED ((NTSTATUS) 0xC0000155L) +#endif + +#ifndef STATUS_TOO_MANY_SECRETS +# define STATUS_TOO_MANY_SECRETS ((NTSTATUS) 0xC0000156L) +#endif + +#ifndef STATUS_SECRET_TOO_LONG +# define STATUS_SECRET_TOO_LONG ((NTSTATUS) 0xC0000157L) +#endif + +#ifndef STATUS_INTERNAL_DB_ERROR +# define STATUS_INTERNAL_DB_ERROR ((NTSTATUS) 0xC0000158L) +#endif + +#ifndef STATUS_FULLSCREEN_MODE +# define STATUS_FULLSCREEN_MODE ((NTSTATUS) 0xC0000159L) +#endif + +#ifndef STATUS_TOO_MANY_CONTEXT_IDS +# define STATUS_TOO_MANY_CONTEXT_IDS ((NTSTATUS) 0xC000015AL) +#endif + +#ifndef STATUS_LOGON_TYPE_NOT_GRANTED +# define STATUS_LOGON_TYPE_NOT_GRANTED ((NTSTATUS) 0xC000015BL) +#endif + +#ifndef STATUS_NOT_REGISTRY_FILE +# define STATUS_NOT_REGISTRY_FILE ((NTSTATUS) 0xC000015CL) +#endif + +#ifndef STATUS_NT_CROSS_ENCRYPTION_REQUIRED +# define STATUS_NT_CROSS_ENCRYPTION_REQUIRED ((NTSTATUS) 0xC000015DL) +#endif + +#ifndef STATUS_DOMAIN_CTRLR_CONFIG_ERROR +# define STATUS_DOMAIN_CTRLR_CONFIG_ERROR ((NTSTATUS) 0xC000015EL) +#endif + +#ifndef STATUS_FT_MISSING_MEMBER +# define STATUS_FT_MISSING_MEMBER ((NTSTATUS) 0xC000015FL) +#endif + +#ifndef STATUS_ILL_FORMED_SERVICE_ENTRY +# define STATUS_ILL_FORMED_SERVICE_ENTRY ((NTSTATUS) 0xC0000160L) +#endif + +#ifndef STATUS_ILLEGAL_CHARACTER +# define STATUS_ILLEGAL_CHARACTER ((NTSTATUS) 0xC0000161L) +#endif + +#ifndef STATUS_UNMAPPABLE_CHARACTER +# define STATUS_UNMAPPABLE_CHARACTER ((NTSTATUS) 0xC0000162L) +#endif + +#ifndef STATUS_UNDEFINED_CHARACTER +# define STATUS_UNDEFINED_CHARACTER ((NTSTATUS) 0xC0000163L) +#endif + +#ifndef STATUS_FLOPPY_VOLUME +# define STATUS_FLOPPY_VOLUME ((NTSTATUS) 0xC0000164L) +#endif + +#ifndef STATUS_FLOPPY_ID_MARK_NOT_FOUND +# define STATUS_FLOPPY_ID_MARK_NOT_FOUND ((NTSTATUS) 0xC0000165L) +#endif + +#ifndef STATUS_FLOPPY_WRONG_CYLINDER +# define STATUS_FLOPPY_WRONG_CYLINDER ((NTSTATUS) 0xC0000166L) +#endif + +#ifndef STATUS_FLOPPY_UNKNOWN_ERROR +# define STATUS_FLOPPY_UNKNOWN_ERROR ((NTSTATUS) 0xC0000167L) +#endif + +#ifndef STATUS_FLOPPY_BAD_REGISTERS +# define STATUS_FLOPPY_BAD_REGISTERS ((NTSTATUS) 0xC0000168L) +#endif + +#ifndef STATUS_DISK_RECALIBRATE_FAILED +# define STATUS_DISK_RECALIBRATE_FAILED ((NTSTATUS) 0xC0000169L) +#endif + +#ifndef STATUS_DISK_OPERATION_FAILED +# define STATUS_DISK_OPERATION_FAILED ((NTSTATUS) 0xC000016AL) +#endif + +#ifndef STATUS_DISK_RESET_FAILED +# define STATUS_DISK_RESET_FAILED ((NTSTATUS) 0xC000016BL) +#endif + +#ifndef STATUS_SHARED_IRQ_BUSY +# define STATUS_SHARED_IRQ_BUSY ((NTSTATUS) 0xC000016CL) +#endif + +#ifndef STATUS_FT_ORPHANING +# define STATUS_FT_ORPHANING ((NTSTATUS) 0xC000016DL) +#endif + +#ifndef STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT +# define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT ((NTSTATUS) 0xC000016EL) +#endif + +#ifndef STATUS_PARTITION_FAILURE +# define STATUS_PARTITION_FAILURE ((NTSTATUS) 0xC0000172L) +#endif + +#ifndef STATUS_INVALID_BLOCK_LENGTH +# define STATUS_INVALID_BLOCK_LENGTH ((NTSTATUS) 0xC0000173L) +#endif + +#ifndef STATUS_DEVICE_NOT_PARTITIONED +# define STATUS_DEVICE_NOT_PARTITIONED ((NTSTATUS) 0xC0000174L) +#endif + +#ifndef STATUS_UNABLE_TO_LOCK_MEDIA +# define STATUS_UNABLE_TO_LOCK_MEDIA ((NTSTATUS) 0xC0000175L) +#endif + +#ifndef STATUS_UNABLE_TO_UNLOAD_MEDIA +# define STATUS_UNABLE_TO_UNLOAD_MEDIA ((NTSTATUS) 0xC0000176L) +#endif + +#ifndef STATUS_EOM_OVERFLOW +# define STATUS_EOM_OVERFLOW ((NTSTATUS) 0xC0000177L) +#endif + +#ifndef STATUS_NO_MEDIA +# define STATUS_NO_MEDIA ((NTSTATUS) 0xC0000178L) +#endif + +#ifndef STATUS_NO_SUCH_MEMBER +# define STATUS_NO_SUCH_MEMBER ((NTSTATUS) 0xC000017AL) +#endif + +#ifndef STATUS_INVALID_MEMBER +# define STATUS_INVALID_MEMBER ((NTSTATUS) 0xC000017BL) +#endif + +#ifndef STATUS_KEY_DELETED +# define STATUS_KEY_DELETED ((NTSTATUS) 0xC000017CL) +#endif + +#ifndef STATUS_NO_LOG_SPACE +# define STATUS_NO_LOG_SPACE ((NTSTATUS) 0xC000017DL) +#endif + +#ifndef STATUS_TOO_MANY_SIDS +# define STATUS_TOO_MANY_SIDS ((NTSTATUS) 0xC000017EL) +#endif + +#ifndef STATUS_LM_CROSS_ENCRYPTION_REQUIRED +# define STATUS_LM_CROSS_ENCRYPTION_REQUIRED ((NTSTATUS) 0xC000017FL) +#endif + +#ifndef STATUS_KEY_HAS_CHILDREN +# define STATUS_KEY_HAS_CHILDREN ((NTSTATUS) 0xC0000180L) +#endif + +#ifndef STATUS_CHILD_MUST_BE_VOLATILE +# define STATUS_CHILD_MUST_BE_VOLATILE ((NTSTATUS) 0xC0000181L) +#endif + +#ifndef STATUS_DEVICE_CONFIGURATION_ERROR +# define STATUS_DEVICE_CONFIGURATION_ERROR ((NTSTATUS) 0xC0000182L) +#endif + +#ifndef STATUS_DRIVER_INTERNAL_ERROR +# define STATUS_DRIVER_INTERNAL_ERROR ((NTSTATUS) 0xC0000183L) +#endif + +#ifndef STATUS_INVALID_DEVICE_STATE +# define STATUS_INVALID_DEVICE_STATE ((NTSTATUS) 0xC0000184L) +#endif + +#ifndef STATUS_IO_DEVICE_ERROR +# define STATUS_IO_DEVICE_ERROR ((NTSTATUS) 0xC0000185L) +#endif + +#ifndef STATUS_DEVICE_PROTOCOL_ERROR +# define STATUS_DEVICE_PROTOCOL_ERROR ((NTSTATUS) 0xC0000186L) +#endif + +#ifndef STATUS_BACKUP_CONTROLLER +# define STATUS_BACKUP_CONTROLLER ((NTSTATUS) 0xC0000187L) +#endif + +#ifndef STATUS_LOG_FILE_FULL +# define STATUS_LOG_FILE_FULL ((NTSTATUS) 0xC0000188L) +#endif + +#ifndef STATUS_TOO_LATE +# define STATUS_TOO_LATE ((NTSTATUS) 0xC0000189L) +#endif + +#ifndef STATUS_NO_TRUST_LSA_SECRET +# define STATUS_NO_TRUST_LSA_SECRET ((NTSTATUS) 0xC000018AL) +#endif + +#ifndef STATUS_NO_TRUST_SAM_ACCOUNT +# define STATUS_NO_TRUST_SAM_ACCOUNT ((NTSTATUS) 0xC000018BL) +#endif + +#ifndef STATUS_TRUSTED_DOMAIN_FAILURE +# define STATUS_TRUSTED_DOMAIN_FAILURE ((NTSTATUS) 0xC000018CL) +#endif + +#ifndef STATUS_TRUSTED_RELATIONSHIP_FAILURE +# define STATUS_TRUSTED_RELATIONSHIP_FAILURE ((NTSTATUS) 0xC000018DL) +#endif + +#ifndef STATUS_EVENTLOG_FILE_CORRUPT +# define STATUS_EVENTLOG_FILE_CORRUPT ((NTSTATUS) 0xC000018EL) +#endif + +#ifndef STATUS_EVENTLOG_CANT_START +# define STATUS_EVENTLOG_CANT_START ((NTSTATUS) 0xC000018FL) +#endif + +#ifndef STATUS_TRUST_FAILURE +# define STATUS_TRUST_FAILURE ((NTSTATUS) 0xC0000190L) +#endif + +#ifndef STATUS_MUTANT_LIMIT_EXCEEDED +# define STATUS_MUTANT_LIMIT_EXCEEDED ((NTSTATUS) 0xC0000191L) +#endif + +#ifndef STATUS_NETLOGON_NOT_STARTED +# define STATUS_NETLOGON_NOT_STARTED ((NTSTATUS) 0xC0000192L) +#endif + +#ifndef STATUS_ACCOUNT_EXPIRED +# define STATUS_ACCOUNT_EXPIRED ((NTSTATUS) 0xC0000193L) +#endif + +#ifndef STATUS_POSSIBLE_DEADLOCK +# define STATUS_POSSIBLE_DEADLOCK ((NTSTATUS) 0xC0000194L) +#endif + +#ifndef STATUS_NETWORK_CREDENTIAL_CONFLICT +# define STATUS_NETWORK_CREDENTIAL_CONFLICT ((NTSTATUS) 0xC0000195L) +#endif + +#ifndef STATUS_REMOTE_SESSION_LIMIT +# define STATUS_REMOTE_SESSION_LIMIT ((NTSTATUS) 0xC0000196L) +#endif + +#ifndef STATUS_EVENTLOG_FILE_CHANGED +# define STATUS_EVENTLOG_FILE_CHANGED ((NTSTATUS) 0xC0000197L) +#endif + +#ifndef STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT +# define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT ((NTSTATUS) 0xC0000198L) +#endif + +#ifndef STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT +# define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT ((NTSTATUS) 0xC0000199L) +#endif + +#ifndef STATUS_NOLOGON_SERVER_TRUST_ACCOUNT +# define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT ((NTSTATUS) 0xC000019AL) +#endif + +#ifndef STATUS_DOMAIN_TRUST_INCONSISTENT +# define STATUS_DOMAIN_TRUST_INCONSISTENT ((NTSTATUS) 0xC000019BL) +#endif + +#ifndef STATUS_FS_DRIVER_REQUIRED +# define STATUS_FS_DRIVER_REQUIRED ((NTSTATUS) 0xC000019CL) +#endif + +#ifndef STATUS_IMAGE_ALREADY_LOADED_AS_DLL +# define STATUS_IMAGE_ALREADY_LOADED_AS_DLL ((NTSTATUS) 0xC000019DL) +#endif + +#ifndef STATUS_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING +# define STATUS_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING ((NTSTATUS) 0xC000019EL) +#endif + +#ifndef STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME +# define STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME ((NTSTATUS) 0xC000019FL) +#endif + +#ifndef STATUS_SECURITY_STREAM_IS_INCONSISTENT +# define STATUS_SECURITY_STREAM_IS_INCONSISTENT ((NTSTATUS) 0xC00001A0L) +#endif + +#ifndef STATUS_INVALID_LOCK_RANGE +# define STATUS_INVALID_LOCK_RANGE ((NTSTATUS) 0xC00001A1L) +#endif + +#ifndef STATUS_INVALID_ACE_CONDITION +# define STATUS_INVALID_ACE_CONDITION ((NTSTATUS) 0xC00001A2L) +#endif + +#ifndef STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT +# define STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT ((NTSTATUS) 0xC00001A3L) +#endif + +#ifndef STATUS_NOTIFICATION_GUID_ALREADY_DEFINED +# define STATUS_NOTIFICATION_GUID_ALREADY_DEFINED ((NTSTATUS) 0xC00001A4L) +#endif + +#ifndef STATUS_NETWORK_OPEN_RESTRICTION +# define STATUS_NETWORK_OPEN_RESTRICTION ((NTSTATUS) 0xC0000201L) +#endif + +#ifndef STATUS_NO_USER_SESSION_KEY +# define STATUS_NO_USER_SESSION_KEY ((NTSTATUS) 0xC0000202L) +#endif + +#ifndef STATUS_USER_SESSION_DELETED +# define STATUS_USER_SESSION_DELETED ((NTSTATUS) 0xC0000203L) +#endif + +#ifndef STATUS_RESOURCE_LANG_NOT_FOUND +# define STATUS_RESOURCE_LANG_NOT_FOUND ((NTSTATUS) 0xC0000204L) +#endif + +#ifndef STATUS_INSUFF_SERVER_RESOURCES +# define STATUS_INSUFF_SERVER_RESOURCES ((NTSTATUS) 0xC0000205L) +#endif + +#ifndef STATUS_INVALID_BUFFER_SIZE +# define STATUS_INVALID_BUFFER_SIZE ((NTSTATUS) 0xC0000206L) +#endif + +#ifndef STATUS_INVALID_ADDRESS_COMPONENT +# define STATUS_INVALID_ADDRESS_COMPONENT ((NTSTATUS) 0xC0000207L) +#endif + +#ifndef STATUS_INVALID_ADDRESS_WILDCARD +# define STATUS_INVALID_ADDRESS_WILDCARD ((NTSTATUS) 0xC0000208L) +#endif + +#ifndef STATUS_TOO_MANY_ADDRESSES +# define STATUS_TOO_MANY_ADDRESSES ((NTSTATUS) 0xC0000209L) +#endif + +#ifndef STATUS_ADDRESS_ALREADY_EXISTS +# define STATUS_ADDRESS_ALREADY_EXISTS ((NTSTATUS) 0xC000020AL) +#endif + +#ifndef STATUS_ADDRESS_CLOSED +# define STATUS_ADDRESS_CLOSED ((NTSTATUS) 0xC000020BL) +#endif + +#ifndef STATUS_CONNECTION_DISCONNECTED +# define STATUS_CONNECTION_DISCONNECTED ((NTSTATUS) 0xC000020CL) +#endif + +#ifndef STATUS_CONNECTION_RESET +# define STATUS_CONNECTION_RESET ((NTSTATUS) 0xC000020DL) +#endif + +#ifndef STATUS_TOO_MANY_NODES +# define STATUS_TOO_MANY_NODES ((NTSTATUS) 0xC000020EL) +#endif + +#ifndef STATUS_TRANSACTION_ABORTED +# define STATUS_TRANSACTION_ABORTED ((NTSTATUS) 0xC000020FL) +#endif + +#ifndef STATUS_TRANSACTION_TIMED_OUT +# define STATUS_TRANSACTION_TIMED_OUT ((NTSTATUS) 0xC0000210L) +#endif + +#ifndef STATUS_TRANSACTION_NO_RELEASE +# define STATUS_TRANSACTION_NO_RELEASE ((NTSTATUS) 0xC0000211L) +#endif + +#ifndef STATUS_TRANSACTION_NO_MATCH +# define STATUS_TRANSACTION_NO_MATCH ((NTSTATUS) 0xC0000212L) +#endif + +#ifndef STATUS_TRANSACTION_RESPONDED +# define STATUS_TRANSACTION_RESPONDED ((NTSTATUS) 0xC0000213L) +#endif + +#ifndef STATUS_TRANSACTION_INVALID_ID +# define STATUS_TRANSACTION_INVALID_ID ((NTSTATUS) 0xC0000214L) +#endif + +#ifndef STATUS_TRANSACTION_INVALID_TYPE +# define STATUS_TRANSACTION_INVALID_TYPE ((NTSTATUS) 0xC0000215L) +#endif + +#ifndef STATUS_NOT_SERVER_SESSION +# define STATUS_NOT_SERVER_SESSION ((NTSTATUS) 0xC0000216L) +#endif + +#ifndef STATUS_NOT_CLIENT_SESSION +# define STATUS_NOT_CLIENT_SESSION ((NTSTATUS) 0xC0000217L) +#endif + +#ifndef STATUS_CANNOT_LOAD_REGISTRY_FILE +# define STATUS_CANNOT_LOAD_REGISTRY_FILE ((NTSTATUS) 0xC0000218L) +#endif + +#ifndef STATUS_DEBUG_ATTACH_FAILED +# define STATUS_DEBUG_ATTACH_FAILED ((NTSTATUS) 0xC0000219L) +#endif + +#ifndef STATUS_SYSTEM_PROCESS_TERMINATED +# define STATUS_SYSTEM_PROCESS_TERMINATED ((NTSTATUS) 0xC000021AL) +#endif + +#ifndef STATUS_DATA_NOT_ACCEPTED +# define STATUS_DATA_NOT_ACCEPTED ((NTSTATUS) 0xC000021BL) +#endif + +#ifndef STATUS_NO_BROWSER_SERVERS_FOUND +# define STATUS_NO_BROWSER_SERVERS_FOUND ((NTSTATUS) 0xC000021CL) +#endif + +#ifndef STATUS_VDM_HARD_ERROR +# define STATUS_VDM_HARD_ERROR ((NTSTATUS) 0xC000021DL) +#endif + +#ifndef STATUS_DRIVER_CANCEL_TIMEOUT +# define STATUS_DRIVER_CANCEL_TIMEOUT ((NTSTATUS) 0xC000021EL) +#endif + +#ifndef STATUS_REPLY_MESSAGE_MISMATCH +# define STATUS_REPLY_MESSAGE_MISMATCH ((NTSTATUS) 0xC000021FL) +#endif + +#ifndef STATUS_MAPPED_ALIGNMENT +# define STATUS_MAPPED_ALIGNMENT ((NTSTATUS) 0xC0000220L) +#endif + +#ifndef STATUS_IMAGE_CHECKSUM_MISMATCH +# define STATUS_IMAGE_CHECKSUM_MISMATCH ((NTSTATUS) 0xC0000221L) +#endif + +#ifndef STATUS_LOST_WRITEBEHIND_DATA +# define STATUS_LOST_WRITEBEHIND_DATA ((NTSTATUS) 0xC0000222L) +#endif + +#ifndef STATUS_CLIENT_SERVER_PARAMETERS_INVALID +# define STATUS_CLIENT_SERVER_PARAMETERS_INVALID ((NTSTATUS) 0xC0000223L) +#endif + +#ifndef STATUS_PASSWORD_MUST_CHANGE +# define STATUS_PASSWORD_MUST_CHANGE ((NTSTATUS) 0xC0000224L) +#endif + +#ifndef STATUS_NOT_FOUND +# define STATUS_NOT_FOUND ((NTSTATUS) 0xC0000225L) +#endif + +#ifndef STATUS_NOT_TINY_STREAM +# define STATUS_NOT_TINY_STREAM ((NTSTATUS) 0xC0000226L) +#endif + +#ifndef STATUS_RECOVERY_FAILURE +# define STATUS_RECOVERY_FAILURE ((NTSTATUS) 0xC0000227L) +#endif + +#ifndef STATUS_STACK_OVERFLOW_READ +# define STATUS_STACK_OVERFLOW_READ ((NTSTATUS) 0xC0000228L) +#endif + +#ifndef STATUS_FAIL_CHECK +# define STATUS_FAIL_CHECK ((NTSTATUS) 0xC0000229L) +#endif + +#ifndef STATUS_DUPLICATE_OBJECTID +# define STATUS_DUPLICATE_OBJECTID ((NTSTATUS) 0xC000022AL) +#endif + +#ifndef STATUS_OBJECTID_EXISTS +# define STATUS_OBJECTID_EXISTS ((NTSTATUS) 0xC000022BL) +#endif + +#ifndef STATUS_CONVERT_TO_LARGE +# define STATUS_CONVERT_TO_LARGE ((NTSTATUS) 0xC000022CL) +#endif + +#ifndef STATUS_RETRY +# define STATUS_RETRY ((NTSTATUS) 0xC000022DL) +#endif + +#ifndef STATUS_FOUND_OUT_OF_SCOPE +# define STATUS_FOUND_OUT_OF_SCOPE ((NTSTATUS) 0xC000022EL) +#endif + +#ifndef STATUS_ALLOCATE_BUCKET +# define STATUS_ALLOCATE_BUCKET ((NTSTATUS) 0xC000022FL) +#endif + +#ifndef STATUS_PROPSET_NOT_FOUND +# define STATUS_PROPSET_NOT_FOUND ((NTSTATUS) 0xC0000230L) +#endif + +#ifndef STATUS_MARSHALL_OVERFLOW +# define STATUS_MARSHALL_OVERFLOW ((NTSTATUS) 0xC0000231L) +#endif + +#ifndef STATUS_INVALID_VARIANT +# define STATUS_INVALID_VARIANT ((NTSTATUS) 0xC0000232L) +#endif + +#ifndef STATUS_DOMAIN_CONTROLLER_NOT_FOUND +# define STATUS_DOMAIN_CONTROLLER_NOT_FOUND ((NTSTATUS) 0xC0000233L) +#endif + +#ifndef STATUS_ACCOUNT_LOCKED_OUT +# define STATUS_ACCOUNT_LOCKED_OUT ((NTSTATUS) 0xC0000234L) +#endif + +#ifndef STATUS_HANDLE_NOT_CLOSABLE +# define STATUS_HANDLE_NOT_CLOSABLE ((NTSTATUS) 0xC0000235L) +#endif + +#ifndef STATUS_CONNECTION_REFUSED +# define STATUS_CONNECTION_REFUSED ((NTSTATUS) 0xC0000236L) +#endif + +#ifndef STATUS_GRACEFUL_DISCONNECT +# define STATUS_GRACEFUL_DISCONNECT ((NTSTATUS) 0xC0000237L) +#endif + +#ifndef STATUS_ADDRESS_ALREADY_ASSOCIATED +# define STATUS_ADDRESS_ALREADY_ASSOCIATED ((NTSTATUS) 0xC0000238L) +#endif + +#ifndef STATUS_ADDRESS_NOT_ASSOCIATED +# define STATUS_ADDRESS_NOT_ASSOCIATED ((NTSTATUS) 0xC0000239L) +#endif + +#ifndef STATUS_CONNECTION_INVALID +# define STATUS_CONNECTION_INVALID ((NTSTATUS) 0xC000023AL) +#endif + +#ifndef STATUS_CONNECTION_ACTIVE +# define STATUS_CONNECTION_ACTIVE ((NTSTATUS) 0xC000023BL) +#endif + +#ifndef STATUS_NETWORK_UNREACHABLE +# define STATUS_NETWORK_UNREACHABLE ((NTSTATUS) 0xC000023CL) +#endif + +#ifndef STATUS_HOST_UNREACHABLE +# define STATUS_HOST_UNREACHABLE ((NTSTATUS) 0xC000023DL) +#endif + +#ifndef STATUS_PROTOCOL_UNREACHABLE +# define STATUS_PROTOCOL_UNREACHABLE ((NTSTATUS) 0xC000023EL) +#endif + +#ifndef STATUS_PORT_UNREACHABLE +# define STATUS_PORT_UNREACHABLE ((NTSTATUS) 0xC000023FL) +#endif + +#ifndef STATUS_REQUEST_ABORTED +# define STATUS_REQUEST_ABORTED ((NTSTATUS) 0xC0000240L) +#endif + +#ifndef STATUS_CONNECTION_ABORTED +# define STATUS_CONNECTION_ABORTED ((NTSTATUS) 0xC0000241L) +#endif + +#ifndef STATUS_BAD_COMPRESSION_BUFFER +# define STATUS_BAD_COMPRESSION_BUFFER ((NTSTATUS) 0xC0000242L) +#endif + +#ifndef STATUS_USER_MAPPED_FILE +# define STATUS_USER_MAPPED_FILE ((NTSTATUS) 0xC0000243L) +#endif + +#ifndef STATUS_AUDIT_FAILED +# define STATUS_AUDIT_FAILED ((NTSTATUS) 0xC0000244L) +#endif + +#ifndef STATUS_TIMER_RESOLUTION_NOT_SET +# define STATUS_TIMER_RESOLUTION_NOT_SET ((NTSTATUS) 0xC0000245L) +#endif + +#ifndef STATUS_CONNECTION_COUNT_LIMIT +# define STATUS_CONNECTION_COUNT_LIMIT ((NTSTATUS) 0xC0000246L) +#endif + +#ifndef STATUS_LOGIN_TIME_RESTRICTION +# define STATUS_LOGIN_TIME_RESTRICTION ((NTSTATUS) 0xC0000247L) +#endif + +#ifndef STATUS_LOGIN_WKSTA_RESTRICTION +# define STATUS_LOGIN_WKSTA_RESTRICTION ((NTSTATUS) 0xC0000248L) +#endif + +#ifndef STATUS_IMAGE_MP_UP_MISMATCH +# define STATUS_IMAGE_MP_UP_MISMATCH ((NTSTATUS) 0xC0000249L) +#endif + +#ifndef STATUS_INSUFFICIENT_LOGON_INFO +# define STATUS_INSUFFICIENT_LOGON_INFO ((NTSTATUS) 0xC0000250L) +#endif + +#ifndef STATUS_BAD_DLL_ENTRYPOINT +# define STATUS_BAD_DLL_ENTRYPOINT ((NTSTATUS) 0xC0000251L) +#endif + +#ifndef STATUS_BAD_SERVICE_ENTRYPOINT +# define STATUS_BAD_SERVICE_ENTRYPOINT ((NTSTATUS) 0xC0000252L) +#endif + +#ifndef STATUS_LPC_REPLY_LOST +# define STATUS_LPC_REPLY_LOST ((NTSTATUS) 0xC0000253L) +#endif + +#ifndef STATUS_IP_ADDRESS_CONFLICT1 +# define STATUS_IP_ADDRESS_CONFLICT1 ((NTSTATUS) 0xC0000254L) +#endif + +#ifndef STATUS_IP_ADDRESS_CONFLICT2 +# define STATUS_IP_ADDRESS_CONFLICT2 ((NTSTATUS) 0xC0000255L) +#endif + +#ifndef STATUS_REGISTRY_QUOTA_LIMIT +# define STATUS_REGISTRY_QUOTA_LIMIT ((NTSTATUS) 0xC0000256L) +#endif + +#ifndef STATUS_PATH_NOT_COVERED +# define STATUS_PATH_NOT_COVERED ((NTSTATUS) 0xC0000257L) +#endif + +#ifndef STATUS_NO_CALLBACK_ACTIVE +# define STATUS_NO_CALLBACK_ACTIVE ((NTSTATUS) 0xC0000258L) +#endif + +#ifndef STATUS_LICENSE_QUOTA_EXCEEDED +# define STATUS_LICENSE_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000259L) +#endif + +#ifndef STATUS_PWD_TOO_SHORT +# define STATUS_PWD_TOO_SHORT ((NTSTATUS) 0xC000025AL) +#endif + +#ifndef STATUS_PWD_TOO_RECENT +# define STATUS_PWD_TOO_RECENT ((NTSTATUS) 0xC000025BL) +#endif + +#ifndef STATUS_PWD_HISTORY_CONFLICT +# define STATUS_PWD_HISTORY_CONFLICT ((NTSTATUS) 0xC000025CL) +#endif + +#ifndef STATUS_PLUGPLAY_NO_DEVICE +# define STATUS_PLUGPLAY_NO_DEVICE ((NTSTATUS) 0xC000025EL) +#endif + +#ifndef STATUS_UNSUPPORTED_COMPRESSION +# define STATUS_UNSUPPORTED_COMPRESSION ((NTSTATUS) 0xC000025FL) +#endif + +#ifndef STATUS_INVALID_HW_PROFILE +# define STATUS_INVALID_HW_PROFILE ((NTSTATUS) 0xC0000260L) +#endif + +#ifndef STATUS_INVALID_PLUGPLAY_DEVICE_PATH +# define STATUS_INVALID_PLUGPLAY_DEVICE_PATH ((NTSTATUS) 0xC0000261L) +#endif + +#ifndef STATUS_DRIVER_ORDINAL_NOT_FOUND +# define STATUS_DRIVER_ORDINAL_NOT_FOUND ((NTSTATUS) 0xC0000262L) +#endif + +#ifndef STATUS_DRIVER_ENTRYPOINT_NOT_FOUND +# define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND ((NTSTATUS) 0xC0000263L) +#endif + +#ifndef STATUS_RESOURCE_NOT_OWNED +# define STATUS_RESOURCE_NOT_OWNED ((NTSTATUS) 0xC0000264L) +#endif + +#ifndef STATUS_TOO_MANY_LINKS +# define STATUS_TOO_MANY_LINKS ((NTSTATUS) 0xC0000265L) +#endif + +#ifndef STATUS_QUOTA_LIST_INCONSISTENT +# define STATUS_QUOTA_LIST_INCONSISTENT ((NTSTATUS) 0xC0000266L) +#endif + +#ifndef STATUS_FILE_IS_OFFLINE +# define STATUS_FILE_IS_OFFLINE ((NTSTATUS) 0xC0000267L) +#endif + +#ifndef STATUS_EVALUATION_EXPIRATION +# define STATUS_EVALUATION_EXPIRATION ((NTSTATUS) 0xC0000268L) +#endif + +#ifndef STATUS_ILLEGAL_DLL_RELOCATION +# define STATUS_ILLEGAL_DLL_RELOCATION ((NTSTATUS) 0xC0000269L) +#endif + +#ifndef STATUS_LICENSE_VIOLATION +# define STATUS_LICENSE_VIOLATION ((NTSTATUS) 0xC000026AL) +#endif + +#ifndef STATUS_DLL_INIT_FAILED_LOGOFF +# define STATUS_DLL_INIT_FAILED_LOGOFF ((NTSTATUS) 0xC000026BL) +#endif + +#ifndef STATUS_DRIVER_UNABLE_TO_LOAD +# define STATUS_DRIVER_UNABLE_TO_LOAD ((NTSTATUS) 0xC000026CL) +#endif + +#ifndef STATUS_DFS_UNAVAILABLE +# define STATUS_DFS_UNAVAILABLE ((NTSTATUS) 0xC000026DL) +#endif + +#ifndef STATUS_VOLUME_DISMOUNTED +# define STATUS_VOLUME_DISMOUNTED ((NTSTATUS) 0xC000026EL) +#endif + +#ifndef STATUS_WX86_INTERNAL_ERROR +# define STATUS_WX86_INTERNAL_ERROR ((NTSTATUS) 0xC000026FL) +#endif + +#ifndef STATUS_WX86_FLOAT_STACK_CHECK +# define STATUS_WX86_FLOAT_STACK_CHECK ((NTSTATUS) 0xC0000270L) +#endif + +#ifndef STATUS_VALIDATE_CONTINUE +# define STATUS_VALIDATE_CONTINUE ((NTSTATUS) 0xC0000271L) +#endif + +#ifndef STATUS_NO_MATCH +# define STATUS_NO_MATCH ((NTSTATUS) 0xC0000272L) +#endif + +#ifndef STATUS_NO_MORE_MATCHES +# define STATUS_NO_MORE_MATCHES ((NTSTATUS) 0xC0000273L) +#endif + +#ifndef STATUS_NOT_A_REPARSE_POINT +# define STATUS_NOT_A_REPARSE_POINT ((NTSTATUS) 0xC0000275L) +#endif + +#ifndef STATUS_IO_REPARSE_TAG_INVALID +# define STATUS_IO_REPARSE_TAG_INVALID ((NTSTATUS) 0xC0000276L) +#endif + +#ifndef STATUS_IO_REPARSE_TAG_MISMATCH +# define STATUS_IO_REPARSE_TAG_MISMATCH ((NTSTATUS) 0xC0000277L) +#endif + +#ifndef STATUS_IO_REPARSE_DATA_INVALID +# define STATUS_IO_REPARSE_DATA_INVALID ((NTSTATUS) 0xC0000278L) +#endif + +#ifndef STATUS_IO_REPARSE_TAG_NOT_HANDLED +# define STATUS_IO_REPARSE_TAG_NOT_HANDLED ((NTSTATUS) 0xC0000279L) +#endif + +#ifndef STATUS_REPARSE_POINT_NOT_RESOLVED +# define STATUS_REPARSE_POINT_NOT_RESOLVED ((NTSTATUS) 0xC0000280L) +#endif + +#ifndef STATUS_DIRECTORY_IS_A_REPARSE_POINT +# define STATUS_DIRECTORY_IS_A_REPARSE_POINT ((NTSTATUS) 0xC0000281L) +#endif + +#ifndef STATUS_RANGE_LIST_CONFLICT +# define STATUS_RANGE_LIST_CONFLICT ((NTSTATUS) 0xC0000282L) +#endif + +#ifndef STATUS_SOURCE_ELEMENT_EMPTY +# define STATUS_SOURCE_ELEMENT_EMPTY ((NTSTATUS) 0xC0000283L) +#endif + +#ifndef STATUS_DESTINATION_ELEMENT_FULL +# define STATUS_DESTINATION_ELEMENT_FULL ((NTSTATUS) 0xC0000284L) +#endif + +#ifndef STATUS_ILLEGAL_ELEMENT_ADDRESS +# define STATUS_ILLEGAL_ELEMENT_ADDRESS ((NTSTATUS) 0xC0000285L) +#endif + +#ifndef STATUS_MAGAZINE_NOT_PRESENT +# define STATUS_MAGAZINE_NOT_PRESENT ((NTSTATUS) 0xC0000286L) +#endif + +#ifndef STATUS_REINITIALIZATION_NEEDED +# define STATUS_REINITIALIZATION_NEEDED ((NTSTATUS) 0xC0000287L) +#endif + +#ifndef STATUS_DEVICE_REQUIRES_CLEANING +# define STATUS_DEVICE_REQUIRES_CLEANING ((NTSTATUS) 0x80000288L) +#endif + +#ifndef STATUS_DEVICE_DOOR_OPEN +# define STATUS_DEVICE_DOOR_OPEN ((NTSTATUS) 0x80000289L) +#endif + +#ifndef STATUS_ENCRYPTION_FAILED +# define STATUS_ENCRYPTION_FAILED ((NTSTATUS) 0xC000028AL) +#endif + +#ifndef STATUS_DECRYPTION_FAILED +# define STATUS_DECRYPTION_FAILED ((NTSTATUS) 0xC000028BL) +#endif + +#ifndef STATUS_RANGE_NOT_FOUND +# define STATUS_RANGE_NOT_FOUND ((NTSTATUS) 0xC000028CL) +#endif + +#ifndef STATUS_NO_RECOVERY_POLICY +# define STATUS_NO_RECOVERY_POLICY ((NTSTATUS) 0xC000028DL) +#endif + +#ifndef STATUS_NO_EFS +# define STATUS_NO_EFS ((NTSTATUS) 0xC000028EL) +#endif + +#ifndef STATUS_WRONG_EFS +# define STATUS_WRONG_EFS ((NTSTATUS) 0xC000028FL) +#endif + +#ifndef STATUS_NO_USER_KEYS +# define STATUS_NO_USER_KEYS ((NTSTATUS) 0xC0000290L) +#endif + +#ifndef STATUS_FILE_NOT_ENCRYPTED +# define STATUS_FILE_NOT_ENCRYPTED ((NTSTATUS) 0xC0000291L) +#endif + +#ifndef STATUS_NOT_EXPORT_FORMAT +# define STATUS_NOT_EXPORT_FORMAT ((NTSTATUS) 0xC0000292L) +#endif + +#ifndef STATUS_FILE_ENCRYPTED +# define STATUS_FILE_ENCRYPTED ((NTSTATUS) 0xC0000293L) +#endif + +#ifndef STATUS_WAKE_SYSTEM +# define STATUS_WAKE_SYSTEM ((NTSTATUS) 0x40000294L) +#endif + +#ifndef STATUS_WMI_GUID_NOT_FOUND +# define STATUS_WMI_GUID_NOT_FOUND ((NTSTATUS) 0xC0000295L) +#endif + +#ifndef STATUS_WMI_INSTANCE_NOT_FOUND +# define STATUS_WMI_INSTANCE_NOT_FOUND ((NTSTATUS) 0xC0000296L) +#endif + +#ifndef STATUS_WMI_ITEMID_NOT_FOUND +# define STATUS_WMI_ITEMID_NOT_FOUND ((NTSTATUS) 0xC0000297L) +#endif + +#ifndef STATUS_WMI_TRY_AGAIN +# define STATUS_WMI_TRY_AGAIN ((NTSTATUS) 0xC0000298L) +#endif + +#ifndef STATUS_SHARED_POLICY +# define STATUS_SHARED_POLICY ((NTSTATUS) 0xC0000299L) +#endif + +#ifndef STATUS_POLICY_OBJECT_NOT_FOUND +# define STATUS_POLICY_OBJECT_NOT_FOUND ((NTSTATUS) 0xC000029AL) +#endif + +#ifndef STATUS_POLICY_ONLY_IN_DS +# define STATUS_POLICY_ONLY_IN_DS ((NTSTATUS) 0xC000029BL) +#endif + +#ifndef STATUS_VOLUME_NOT_UPGRADED +# define STATUS_VOLUME_NOT_UPGRADED ((NTSTATUS) 0xC000029CL) +#endif + +#ifndef STATUS_REMOTE_STORAGE_NOT_ACTIVE +# define STATUS_REMOTE_STORAGE_NOT_ACTIVE ((NTSTATUS) 0xC000029DL) +#endif + +#ifndef STATUS_REMOTE_STORAGE_MEDIA_ERROR +# define STATUS_REMOTE_STORAGE_MEDIA_ERROR ((NTSTATUS) 0xC000029EL) +#endif + +#ifndef STATUS_NO_TRACKING_SERVICE +# define STATUS_NO_TRACKING_SERVICE ((NTSTATUS) 0xC000029FL) +#endif + +#ifndef STATUS_SERVER_SID_MISMATCH +# define STATUS_SERVER_SID_MISMATCH ((NTSTATUS) 0xC00002A0L) +#endif + +#ifndef STATUS_DS_NO_ATTRIBUTE_OR_VALUE +# define STATUS_DS_NO_ATTRIBUTE_OR_VALUE ((NTSTATUS) 0xC00002A1L) +#endif + +#ifndef STATUS_DS_INVALID_ATTRIBUTE_SYNTAX +# define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX ((NTSTATUS) 0xC00002A2L) +#endif + +#ifndef STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED +# define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED ((NTSTATUS) 0xC00002A3L) +#endif + +#ifndef STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS +# define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS ((NTSTATUS) 0xC00002A4L) +#endif + +#ifndef STATUS_DS_BUSY +# define STATUS_DS_BUSY ((NTSTATUS) 0xC00002A5L) +#endif + +#ifndef STATUS_DS_UNAVAILABLE +# define STATUS_DS_UNAVAILABLE ((NTSTATUS) 0xC00002A6L) +#endif + +#ifndef STATUS_DS_NO_RIDS_ALLOCATED +# define STATUS_DS_NO_RIDS_ALLOCATED ((NTSTATUS) 0xC00002A7L) +#endif + +#ifndef STATUS_DS_NO_MORE_RIDS +# define STATUS_DS_NO_MORE_RIDS ((NTSTATUS) 0xC00002A8L) +#endif + +#ifndef STATUS_DS_INCORRECT_ROLE_OWNER +# define STATUS_DS_INCORRECT_ROLE_OWNER ((NTSTATUS) 0xC00002A9L) +#endif + +#ifndef STATUS_DS_RIDMGR_INIT_ERROR +# define STATUS_DS_RIDMGR_INIT_ERROR ((NTSTATUS) 0xC00002AAL) +#endif + +#ifndef STATUS_DS_OBJ_CLASS_VIOLATION +# define STATUS_DS_OBJ_CLASS_VIOLATION ((NTSTATUS) 0xC00002ABL) +#endif + +#ifndef STATUS_DS_CANT_ON_NON_LEAF +# define STATUS_DS_CANT_ON_NON_LEAF ((NTSTATUS) 0xC00002ACL) +#endif + +#ifndef STATUS_DS_CANT_ON_RDN +# define STATUS_DS_CANT_ON_RDN ((NTSTATUS) 0xC00002ADL) +#endif + +#ifndef STATUS_DS_CANT_MOD_OBJ_CLASS +# define STATUS_DS_CANT_MOD_OBJ_CLASS ((NTSTATUS) 0xC00002AEL) +#endif + +#ifndef STATUS_DS_CROSS_DOM_MOVE_FAILED +# define STATUS_DS_CROSS_DOM_MOVE_FAILED ((NTSTATUS) 0xC00002AFL) +#endif + +#ifndef STATUS_DS_GC_NOT_AVAILABLE +# define STATUS_DS_GC_NOT_AVAILABLE ((NTSTATUS) 0xC00002B0L) +#endif + +#ifndef STATUS_DIRECTORY_SERVICE_REQUIRED +# define STATUS_DIRECTORY_SERVICE_REQUIRED ((NTSTATUS) 0xC00002B1L) +#endif + +#ifndef STATUS_REPARSE_ATTRIBUTE_CONFLICT +# define STATUS_REPARSE_ATTRIBUTE_CONFLICT ((NTSTATUS) 0xC00002B2L) +#endif + +#ifndef STATUS_CANT_ENABLE_DENY_ONLY +# define STATUS_CANT_ENABLE_DENY_ONLY ((NTSTATUS) 0xC00002B3L) +#endif + +#ifndef STATUS_FLOAT_MULTIPLE_FAULTS +# define STATUS_FLOAT_MULTIPLE_FAULTS ((NTSTATUS) 0xC00002B4L) +#endif + +#ifndef STATUS_FLOAT_MULTIPLE_TRAPS +# define STATUS_FLOAT_MULTIPLE_TRAPS ((NTSTATUS) 0xC00002B5L) +#endif + +#ifndef STATUS_DEVICE_REMOVED +# define STATUS_DEVICE_REMOVED ((NTSTATUS) 0xC00002B6L) +#endif + +#ifndef STATUS_JOURNAL_DELETE_IN_PROGRESS +# define STATUS_JOURNAL_DELETE_IN_PROGRESS ((NTSTATUS) 0xC00002B7L) +#endif + +#ifndef STATUS_JOURNAL_NOT_ACTIVE +# define STATUS_JOURNAL_NOT_ACTIVE ((NTSTATUS) 0xC00002B8L) +#endif + +#ifndef STATUS_NOINTERFACE +# define STATUS_NOINTERFACE ((NTSTATUS) 0xC00002B9L) +#endif + +#ifndef STATUS_DS_ADMIN_LIMIT_EXCEEDED +# define STATUS_DS_ADMIN_LIMIT_EXCEEDED ((NTSTATUS) 0xC00002C1L) +#endif + +#ifndef STATUS_DRIVER_FAILED_SLEEP +# define STATUS_DRIVER_FAILED_SLEEP ((NTSTATUS) 0xC00002C2L) +#endif + +#ifndef STATUS_MUTUAL_AUTHENTICATION_FAILED +# define STATUS_MUTUAL_AUTHENTICATION_FAILED ((NTSTATUS) 0xC00002C3L) +#endif + +#ifndef STATUS_CORRUPT_SYSTEM_FILE +# define STATUS_CORRUPT_SYSTEM_FILE ((NTSTATUS) 0xC00002C4L) +#endif + +#ifndef STATUS_DATATYPE_MISALIGNMENT_ERROR +# define STATUS_DATATYPE_MISALIGNMENT_ERROR ((NTSTATUS) 0xC00002C5L) +#endif + +#ifndef STATUS_WMI_READ_ONLY +# define STATUS_WMI_READ_ONLY ((NTSTATUS) 0xC00002C6L) +#endif + +#ifndef STATUS_WMI_SET_FAILURE +# define STATUS_WMI_SET_FAILURE ((NTSTATUS) 0xC00002C7L) +#endif + +#ifndef STATUS_COMMITMENT_MINIMUM +# define STATUS_COMMITMENT_MINIMUM ((NTSTATUS) 0xC00002C8L) +#endif + +#ifndef STATUS_REG_NAT_CONSUMPTION +# define STATUS_REG_NAT_CONSUMPTION ((NTSTATUS) 0xC00002C9L) +#endif + +#ifndef STATUS_TRANSPORT_FULL +# define STATUS_TRANSPORT_FULL ((NTSTATUS) 0xC00002CAL) +#endif + +#ifndef STATUS_DS_SAM_INIT_FAILURE +# define STATUS_DS_SAM_INIT_FAILURE ((NTSTATUS) 0xC00002CBL) +#endif + +#ifndef STATUS_ONLY_IF_CONNECTED +# define STATUS_ONLY_IF_CONNECTED ((NTSTATUS) 0xC00002CCL) +#endif + +#ifndef STATUS_DS_SENSITIVE_GROUP_VIOLATION +# define STATUS_DS_SENSITIVE_GROUP_VIOLATION ((NTSTATUS) 0xC00002CDL) +#endif + +#ifndef STATUS_PNP_RESTART_ENUMERATION +# define STATUS_PNP_RESTART_ENUMERATION ((NTSTATUS) 0xC00002CEL) +#endif + +#ifndef STATUS_JOURNAL_ENTRY_DELETED +# define STATUS_JOURNAL_ENTRY_DELETED ((NTSTATUS) 0xC00002CFL) +#endif + +#ifndef STATUS_DS_CANT_MOD_PRIMARYGROUPID +# define STATUS_DS_CANT_MOD_PRIMARYGROUPID ((NTSTATUS) 0xC00002D0L) +#endif + +#ifndef STATUS_SYSTEM_IMAGE_BAD_SIGNATURE +# define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE ((NTSTATUS) 0xC00002D1L) +#endif + +#ifndef STATUS_PNP_REBOOT_REQUIRED +# define STATUS_PNP_REBOOT_REQUIRED ((NTSTATUS) 0xC00002D2L) +#endif + +#ifndef STATUS_POWER_STATE_INVALID +# define STATUS_POWER_STATE_INVALID ((NTSTATUS) 0xC00002D3L) +#endif + +#ifndef STATUS_DS_INVALID_GROUP_TYPE +# define STATUS_DS_INVALID_GROUP_TYPE ((NTSTATUS) 0xC00002D4L) +#endif + +#ifndef STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN +# define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN ((NTSTATUS) 0xC00002D5L) +#endif + +#ifndef STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN +# define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN ((NTSTATUS) 0xC00002D6L) +#endif + +#ifndef STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER +# define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER ((NTSTATUS) 0xC00002D7L) +#endif + +#ifndef STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER +# define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER ((NTSTATUS) 0xC00002D8L) +#endif + +#ifndef STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER +# define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER ((NTSTATUS) 0xC00002D9L) +#endif + +#ifndef STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER +# define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER ((NTSTATUS) 0xC00002DAL) +#endif + +#ifndef STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER +# define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER ((NTSTATUS) 0xC00002DBL) +#endif + +#ifndef STATUS_DS_HAVE_PRIMARY_MEMBERS +# define STATUS_DS_HAVE_PRIMARY_MEMBERS ((NTSTATUS) 0xC00002DCL) +#endif + +#ifndef STATUS_WMI_NOT_SUPPORTED +# define STATUS_WMI_NOT_SUPPORTED ((NTSTATUS) 0xC00002DDL) +#endif + +#ifndef STATUS_INSUFFICIENT_POWER +# define STATUS_INSUFFICIENT_POWER ((NTSTATUS) 0xC00002DEL) +#endif + +#ifndef STATUS_SAM_NEED_BOOTKEY_PASSWORD +# define STATUS_SAM_NEED_BOOTKEY_PASSWORD ((NTSTATUS) 0xC00002DFL) +#endif + +#ifndef STATUS_SAM_NEED_BOOTKEY_FLOPPY +# define STATUS_SAM_NEED_BOOTKEY_FLOPPY ((NTSTATUS) 0xC00002E0L) +#endif + +#ifndef STATUS_DS_CANT_START +# define STATUS_DS_CANT_START ((NTSTATUS) 0xC00002E1L) +#endif + +#ifndef STATUS_DS_INIT_FAILURE +# define STATUS_DS_INIT_FAILURE ((NTSTATUS) 0xC00002E2L) +#endif + +#ifndef STATUS_SAM_INIT_FAILURE +# define STATUS_SAM_INIT_FAILURE ((NTSTATUS) 0xC00002E3L) +#endif + +#ifndef STATUS_DS_GC_REQUIRED +# define STATUS_DS_GC_REQUIRED ((NTSTATUS) 0xC00002E4L) +#endif + +#ifndef STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY +# define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY ((NTSTATUS) 0xC00002E5L) +#endif + +#ifndef STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS +# define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS ((NTSTATUS) 0xC00002E6L) +#endif + +#ifndef STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED +# define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED ((NTSTATUS) 0xC00002E7L) +#endif + +#ifndef STATUS_MULTIPLE_FAULT_VIOLATION +# define STATUS_MULTIPLE_FAULT_VIOLATION ((NTSTATUS) 0xC00002E8L) +#endif + +#ifndef STATUS_CURRENT_DOMAIN_NOT_ALLOWED +# define STATUS_CURRENT_DOMAIN_NOT_ALLOWED ((NTSTATUS) 0xC00002E9L) +#endif + +#ifndef STATUS_CANNOT_MAKE +# define STATUS_CANNOT_MAKE ((NTSTATUS) 0xC00002EAL) +#endif + +#ifndef STATUS_SYSTEM_SHUTDOWN +# define STATUS_SYSTEM_SHUTDOWN ((NTSTATUS) 0xC00002EBL) +#endif + +#ifndef STATUS_DS_INIT_FAILURE_CONSOLE +# define STATUS_DS_INIT_FAILURE_CONSOLE ((NTSTATUS) 0xC00002ECL) +#endif + +#ifndef STATUS_DS_SAM_INIT_FAILURE_CONSOLE +# define STATUS_DS_SAM_INIT_FAILURE_CONSOLE ((NTSTATUS) 0xC00002EDL) +#endif + +#ifndef STATUS_UNFINISHED_CONTEXT_DELETED +# define STATUS_UNFINISHED_CONTEXT_DELETED ((NTSTATUS) 0xC00002EEL) +#endif + +#ifndef STATUS_NO_TGT_REPLY +# define STATUS_NO_TGT_REPLY ((NTSTATUS) 0xC00002EFL) +#endif + +#ifndef STATUS_OBJECTID_NOT_FOUND +# define STATUS_OBJECTID_NOT_FOUND ((NTSTATUS) 0xC00002F0L) +#endif + +#ifndef STATUS_NO_IP_ADDRESSES +# define STATUS_NO_IP_ADDRESSES ((NTSTATUS) 0xC00002F1L) +#endif + +#ifndef STATUS_WRONG_CREDENTIAL_HANDLE +# define STATUS_WRONG_CREDENTIAL_HANDLE ((NTSTATUS) 0xC00002F2L) +#endif + +#ifndef STATUS_CRYPTO_SYSTEM_INVALID +# define STATUS_CRYPTO_SYSTEM_INVALID ((NTSTATUS) 0xC00002F3L) +#endif + +#ifndef STATUS_MAX_REFERRALS_EXCEEDED +# define STATUS_MAX_REFERRALS_EXCEEDED ((NTSTATUS) 0xC00002F4L) +#endif + +#ifndef STATUS_MUST_BE_KDC +# define STATUS_MUST_BE_KDC ((NTSTATUS) 0xC00002F5L) +#endif + +#ifndef STATUS_STRONG_CRYPTO_NOT_SUPPORTED +# define STATUS_STRONG_CRYPTO_NOT_SUPPORTED ((NTSTATUS) 0xC00002F6L) +#endif + +#ifndef STATUS_TOO_MANY_PRINCIPALS +# define STATUS_TOO_MANY_PRINCIPALS ((NTSTATUS) 0xC00002F7L) +#endif + +#ifndef STATUS_NO_PA_DATA +# define STATUS_NO_PA_DATA ((NTSTATUS) 0xC00002F8L) +#endif + +#ifndef STATUS_PKINIT_NAME_MISMATCH +# define STATUS_PKINIT_NAME_MISMATCH ((NTSTATUS) 0xC00002F9L) +#endif + +#ifndef STATUS_SMARTCARD_LOGON_REQUIRED +# define STATUS_SMARTCARD_LOGON_REQUIRED ((NTSTATUS) 0xC00002FAL) +#endif + +#ifndef STATUS_KDC_INVALID_REQUEST +# define STATUS_KDC_INVALID_REQUEST ((NTSTATUS) 0xC00002FBL) +#endif + +#ifndef STATUS_KDC_UNABLE_TO_REFER +# define STATUS_KDC_UNABLE_TO_REFER ((NTSTATUS) 0xC00002FCL) +#endif + +#ifndef STATUS_KDC_UNKNOWN_ETYPE +# define STATUS_KDC_UNKNOWN_ETYPE ((NTSTATUS) 0xC00002FDL) +#endif + +#ifndef STATUS_SHUTDOWN_IN_PROGRESS +# define STATUS_SHUTDOWN_IN_PROGRESS ((NTSTATUS) 0xC00002FEL) +#endif + +#ifndef STATUS_SERVER_SHUTDOWN_IN_PROGRESS +# define STATUS_SERVER_SHUTDOWN_IN_PROGRESS ((NTSTATUS) 0xC00002FFL) +#endif + +#ifndef STATUS_NOT_SUPPORTED_ON_SBS +# define STATUS_NOT_SUPPORTED_ON_SBS ((NTSTATUS) 0xC0000300L) +#endif + +#ifndef STATUS_WMI_GUID_DISCONNECTED +# define STATUS_WMI_GUID_DISCONNECTED ((NTSTATUS) 0xC0000301L) +#endif + +#ifndef STATUS_WMI_ALREADY_DISABLED +# define STATUS_WMI_ALREADY_DISABLED ((NTSTATUS) 0xC0000302L) +#endif + +#ifndef STATUS_WMI_ALREADY_ENABLED +# define STATUS_WMI_ALREADY_ENABLED ((NTSTATUS) 0xC0000303L) +#endif + +#ifndef STATUS_MFT_TOO_FRAGMENTED +# define STATUS_MFT_TOO_FRAGMENTED ((NTSTATUS) 0xC0000304L) +#endif + +#ifndef STATUS_COPY_PROTECTION_FAILURE +# define STATUS_COPY_PROTECTION_FAILURE ((NTSTATUS) 0xC0000305L) +#endif + +#ifndef STATUS_CSS_AUTHENTICATION_FAILURE +# define STATUS_CSS_AUTHENTICATION_FAILURE ((NTSTATUS) 0xC0000306L) +#endif + +#ifndef STATUS_CSS_KEY_NOT_PRESENT +# define STATUS_CSS_KEY_NOT_PRESENT ((NTSTATUS) 0xC0000307L) +#endif + +#ifndef STATUS_CSS_KEY_NOT_ESTABLISHED +# define STATUS_CSS_KEY_NOT_ESTABLISHED ((NTSTATUS) 0xC0000308L) +#endif + +#ifndef STATUS_CSS_SCRAMBLED_SECTOR +# define STATUS_CSS_SCRAMBLED_SECTOR ((NTSTATUS) 0xC0000309L) +#endif + +#ifndef STATUS_CSS_REGION_MISMATCH +# define STATUS_CSS_REGION_MISMATCH ((NTSTATUS) 0xC000030AL) +#endif + +#ifndef STATUS_CSS_RESETS_EXHAUSTED +# define STATUS_CSS_RESETS_EXHAUSTED ((NTSTATUS) 0xC000030BL) +#endif + +#ifndef STATUS_PKINIT_FAILURE +# define STATUS_PKINIT_FAILURE ((NTSTATUS) 0xC0000320L) +#endif + +#ifndef STATUS_SMARTCARD_SUBSYSTEM_FAILURE +# define STATUS_SMARTCARD_SUBSYSTEM_FAILURE ((NTSTATUS) 0xC0000321L) +#endif + +#ifndef STATUS_NO_KERB_KEY +# define STATUS_NO_KERB_KEY ((NTSTATUS) 0xC0000322L) +#endif + +#ifndef STATUS_HOST_DOWN +# define STATUS_HOST_DOWN ((NTSTATUS) 0xC0000350L) +#endif + +#ifndef STATUS_UNSUPPORTED_PREAUTH +# define STATUS_UNSUPPORTED_PREAUTH ((NTSTATUS) 0xC0000351L) +#endif + +#ifndef STATUS_EFS_ALG_BLOB_TOO_BIG +# define STATUS_EFS_ALG_BLOB_TOO_BIG ((NTSTATUS) 0xC0000352L) +#endif + +#ifndef STATUS_PORT_NOT_SET +# define STATUS_PORT_NOT_SET ((NTSTATUS) 0xC0000353L) +#endif + +#ifndef STATUS_DEBUGGER_INACTIVE +# define STATUS_DEBUGGER_INACTIVE ((NTSTATUS) 0xC0000354L) +#endif + +#ifndef STATUS_DS_VERSION_CHECK_FAILURE +# define STATUS_DS_VERSION_CHECK_FAILURE ((NTSTATUS) 0xC0000355L) +#endif + +#ifndef STATUS_AUDITING_DISABLED +# define STATUS_AUDITING_DISABLED ((NTSTATUS) 0xC0000356L) +#endif + +#ifndef STATUS_PRENT4_MACHINE_ACCOUNT +# define STATUS_PRENT4_MACHINE_ACCOUNT ((NTSTATUS) 0xC0000357L) +#endif + +#ifndef STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER +# define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER ((NTSTATUS) 0xC0000358L) +#endif + +#ifndef STATUS_INVALID_IMAGE_WIN_32 +# define STATUS_INVALID_IMAGE_WIN_32 ((NTSTATUS) 0xC0000359L) +#endif + +#ifndef STATUS_INVALID_IMAGE_WIN_64 +# define STATUS_INVALID_IMAGE_WIN_64 ((NTSTATUS) 0xC000035AL) +#endif + +#ifndef STATUS_BAD_BINDINGS +# define STATUS_BAD_BINDINGS ((NTSTATUS) 0xC000035BL) +#endif + +#ifndef STATUS_NETWORK_SESSION_EXPIRED +# define STATUS_NETWORK_SESSION_EXPIRED ((NTSTATUS) 0xC000035CL) +#endif + +#ifndef STATUS_APPHELP_BLOCK +# define STATUS_APPHELP_BLOCK ((NTSTATUS) 0xC000035DL) +#endif + +#ifndef STATUS_ALL_SIDS_FILTERED +# define STATUS_ALL_SIDS_FILTERED ((NTSTATUS) 0xC000035EL) +#endif + +#ifndef STATUS_NOT_SAFE_MODE_DRIVER +# define STATUS_NOT_SAFE_MODE_DRIVER ((NTSTATUS) 0xC000035FL) +#endif + +#ifndef STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT +# define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT ((NTSTATUS) 0xC0000361L) +#endif + +#ifndef STATUS_ACCESS_DISABLED_BY_POLICY_PATH +# define STATUS_ACCESS_DISABLED_BY_POLICY_PATH ((NTSTATUS) 0xC0000362L) +#endif + +#ifndef STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER +# define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER ((NTSTATUS) 0xC0000363L) +#endif + +#ifndef STATUS_ACCESS_DISABLED_BY_POLICY_OTHER +# define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER ((NTSTATUS) 0xC0000364L) +#endif + +#ifndef STATUS_FAILED_DRIVER_ENTRY +# define STATUS_FAILED_DRIVER_ENTRY ((NTSTATUS) 0xC0000365L) +#endif + +#ifndef STATUS_DEVICE_ENUMERATION_ERROR +# define STATUS_DEVICE_ENUMERATION_ERROR ((NTSTATUS) 0xC0000366L) +#endif + +#ifndef STATUS_MOUNT_POINT_NOT_RESOLVED +# define STATUS_MOUNT_POINT_NOT_RESOLVED ((NTSTATUS) 0xC0000368L) +#endif + +#ifndef STATUS_INVALID_DEVICE_OBJECT_PARAMETER +# define STATUS_INVALID_DEVICE_OBJECT_PARAMETER ((NTSTATUS) 0xC0000369L) +#endif + +#ifndef STATUS_MCA_OCCURED +# define STATUS_MCA_OCCURED ((NTSTATUS) 0xC000036AL) +#endif + +#ifndef STATUS_DRIVER_BLOCKED_CRITICAL +# define STATUS_DRIVER_BLOCKED_CRITICAL ((NTSTATUS) 0xC000036BL) +#endif + +#ifndef STATUS_DRIVER_BLOCKED +# define STATUS_DRIVER_BLOCKED ((NTSTATUS) 0xC000036CL) +#endif + +#ifndef STATUS_DRIVER_DATABASE_ERROR +# define STATUS_DRIVER_DATABASE_ERROR ((NTSTATUS) 0xC000036DL) +#endif + +#ifndef STATUS_SYSTEM_HIVE_TOO_LARGE +# define STATUS_SYSTEM_HIVE_TOO_LARGE ((NTSTATUS) 0xC000036EL) +#endif + +#ifndef STATUS_INVALID_IMPORT_OF_NON_DLL +# define STATUS_INVALID_IMPORT_OF_NON_DLL ((NTSTATUS) 0xC000036FL) +#endif + +#ifndef STATUS_DS_SHUTTING_DOWN +# define STATUS_DS_SHUTTING_DOWN ((NTSTATUS) 0x40000370L) +#endif + +#ifndef STATUS_NO_SECRETS +# define STATUS_NO_SECRETS ((NTSTATUS) 0xC0000371L) +#endif + +#ifndef STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY +# define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY ((NTSTATUS) 0xC0000372L) +#endif + +#ifndef STATUS_FAILED_STACK_SWITCH +# define STATUS_FAILED_STACK_SWITCH ((NTSTATUS) 0xC0000373L) +#endif + +#ifndef STATUS_HEAP_CORRUPTION +# define STATUS_HEAP_CORRUPTION ((NTSTATUS) 0xC0000374L) +#endif + +#ifndef STATUS_SMARTCARD_WRONG_PIN +# define STATUS_SMARTCARD_WRONG_PIN ((NTSTATUS) 0xC0000380L) +#endif + +#ifndef STATUS_SMARTCARD_CARD_BLOCKED +# define STATUS_SMARTCARD_CARD_BLOCKED ((NTSTATUS) 0xC0000381L) +#endif + +#ifndef STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED +# define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED ((NTSTATUS) 0xC0000382L) +#endif + +#ifndef STATUS_SMARTCARD_NO_CARD +# define STATUS_SMARTCARD_NO_CARD ((NTSTATUS) 0xC0000383L) +#endif + +#ifndef STATUS_SMARTCARD_NO_KEY_CONTAINER +# define STATUS_SMARTCARD_NO_KEY_CONTAINER ((NTSTATUS) 0xC0000384L) +#endif + +#ifndef STATUS_SMARTCARD_NO_CERTIFICATE +# define STATUS_SMARTCARD_NO_CERTIFICATE ((NTSTATUS) 0xC0000385L) +#endif + +#ifndef STATUS_SMARTCARD_NO_KEYSET +# define STATUS_SMARTCARD_NO_KEYSET ((NTSTATUS) 0xC0000386L) +#endif + +#ifndef STATUS_SMARTCARD_IO_ERROR +# define STATUS_SMARTCARD_IO_ERROR ((NTSTATUS) 0xC0000387L) +#endif + +#ifndef STATUS_DOWNGRADE_DETECTED +# define STATUS_DOWNGRADE_DETECTED ((NTSTATUS) 0xC0000388L) +#endif + +#ifndef STATUS_SMARTCARD_CERT_REVOKED +# define STATUS_SMARTCARD_CERT_REVOKED ((NTSTATUS) 0xC0000389L) +#endif + +#ifndef STATUS_ISSUING_CA_UNTRUSTED +# define STATUS_ISSUING_CA_UNTRUSTED ((NTSTATUS) 0xC000038AL) +#endif + +#ifndef STATUS_REVOCATION_OFFLINE_C +# define STATUS_REVOCATION_OFFLINE_C ((NTSTATUS) 0xC000038BL) +#endif + +#ifndef STATUS_PKINIT_CLIENT_FAILURE +# define STATUS_PKINIT_CLIENT_FAILURE ((NTSTATUS) 0xC000038CL) +#endif + +#ifndef STATUS_SMARTCARD_CERT_EXPIRED +# define STATUS_SMARTCARD_CERT_EXPIRED ((NTSTATUS) 0xC000038DL) +#endif + +#ifndef STATUS_DRIVER_FAILED_PRIOR_UNLOAD +# define STATUS_DRIVER_FAILED_PRIOR_UNLOAD ((NTSTATUS) 0xC000038EL) +#endif + +#ifndef STATUS_SMARTCARD_SILENT_CONTEXT +# define STATUS_SMARTCARD_SILENT_CONTEXT ((NTSTATUS) 0xC000038FL) +#endif + +#ifndef STATUS_PER_USER_TRUST_QUOTA_EXCEEDED +# define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000401L) +#endif + +#ifndef STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED +# define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000402L) +#endif + +#ifndef STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED +# define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000403L) +#endif + +#ifndef STATUS_DS_NAME_NOT_UNIQUE +# define STATUS_DS_NAME_NOT_UNIQUE ((NTSTATUS) 0xC0000404L) +#endif + +#ifndef STATUS_DS_DUPLICATE_ID_FOUND +# define STATUS_DS_DUPLICATE_ID_FOUND ((NTSTATUS) 0xC0000405L) +#endif + +#ifndef STATUS_DS_GROUP_CONVERSION_ERROR +# define STATUS_DS_GROUP_CONVERSION_ERROR ((NTSTATUS) 0xC0000406L) +#endif + +#ifndef STATUS_VOLSNAP_PREPARE_HIBERNATE +# define STATUS_VOLSNAP_PREPARE_HIBERNATE ((NTSTATUS) 0xC0000407L) +#endif + +#ifndef STATUS_USER2USER_REQUIRED +# define STATUS_USER2USER_REQUIRED ((NTSTATUS) 0xC0000408L) +#endif + +#ifndef STATUS_STACK_BUFFER_OVERRUN +# define STATUS_STACK_BUFFER_OVERRUN ((NTSTATUS) 0xC0000409L) +#endif + +#ifndef STATUS_NO_S4U_PROT_SUPPORT +# define STATUS_NO_S4U_PROT_SUPPORT ((NTSTATUS) 0xC000040AL) +#endif + +#ifndef STATUS_CROSSREALM_DELEGATION_FAILURE +# define STATUS_CROSSREALM_DELEGATION_FAILURE ((NTSTATUS) 0xC000040BL) +#endif + +#ifndef STATUS_REVOCATION_OFFLINE_KDC +# define STATUS_REVOCATION_OFFLINE_KDC ((NTSTATUS) 0xC000040CL) +#endif + +#ifndef STATUS_ISSUING_CA_UNTRUSTED_KDC +# define STATUS_ISSUING_CA_UNTRUSTED_KDC ((NTSTATUS) 0xC000040DL) +#endif + +#ifndef STATUS_KDC_CERT_EXPIRED +# define STATUS_KDC_CERT_EXPIRED ((NTSTATUS) 0xC000040EL) +#endif + +#ifndef STATUS_KDC_CERT_REVOKED +# define STATUS_KDC_CERT_REVOKED ((NTSTATUS) 0xC000040FL) +#endif + +#ifndef STATUS_PARAMETER_QUOTA_EXCEEDED +# define STATUS_PARAMETER_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000410L) +#endif + +#ifndef STATUS_HIBERNATION_FAILURE +# define STATUS_HIBERNATION_FAILURE ((NTSTATUS) 0xC0000411L) +#endif + +#ifndef STATUS_DELAY_LOAD_FAILED +# define STATUS_DELAY_LOAD_FAILED ((NTSTATUS) 0xC0000412L) +#endif + +#ifndef STATUS_AUTHENTICATION_FIREWALL_FAILED +# define STATUS_AUTHENTICATION_FIREWALL_FAILED ((NTSTATUS) 0xC0000413L) +#endif + +#ifndef STATUS_VDM_DISALLOWED +# define STATUS_VDM_DISALLOWED ((NTSTATUS) 0xC0000414L) +#endif + +#ifndef STATUS_HUNG_DISPLAY_DRIVER_THREAD +# define STATUS_HUNG_DISPLAY_DRIVER_THREAD ((NTSTATUS) 0xC0000415L) +#endif + +#ifndef STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE +# define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE ((NTSTATUS) 0xC0000416L) +#endif + +#ifndef STATUS_INVALID_CRUNTIME_PARAMETER +# define STATUS_INVALID_CRUNTIME_PARAMETER ((NTSTATUS) 0xC0000417L) +#endif + +#ifndef STATUS_NTLM_BLOCKED +# define STATUS_NTLM_BLOCKED ((NTSTATUS) 0xC0000418L) +#endif + +#ifndef STATUS_DS_SRC_SID_EXISTS_IN_FOREST +# define STATUS_DS_SRC_SID_EXISTS_IN_FOREST ((NTSTATUS) 0xC0000419L) +#endif + +#ifndef STATUS_DS_DOMAIN_NAME_EXISTS_IN_FOREST +# define STATUS_DS_DOMAIN_NAME_EXISTS_IN_FOREST ((NTSTATUS) 0xC000041AL) +#endif + +#ifndef STATUS_DS_FLAT_NAME_EXISTS_IN_FOREST +# define STATUS_DS_FLAT_NAME_EXISTS_IN_FOREST ((NTSTATUS) 0xC000041BL) +#endif + +#ifndef STATUS_INVALID_USER_PRINCIPAL_NAME +# define STATUS_INVALID_USER_PRINCIPAL_NAME ((NTSTATUS) 0xC000041CL) +#endif + +#ifndef STATUS_FATAL_USER_CALLBACK_EXCEPTION +# define STATUS_FATAL_USER_CALLBACK_EXCEPTION ((NTSTATUS) 0xC000041DL) +#endif + +#ifndef STATUS_ASSERTION_FAILURE +# define STATUS_ASSERTION_FAILURE ((NTSTATUS) 0xC0000420L) +#endif + +#ifndef STATUS_VERIFIER_STOP +# define STATUS_VERIFIER_STOP ((NTSTATUS) 0xC0000421L) +#endif + +#ifndef STATUS_CALLBACK_POP_STACK +# define STATUS_CALLBACK_POP_STACK ((NTSTATUS) 0xC0000423L) +#endif + +#ifndef STATUS_INCOMPATIBLE_DRIVER_BLOCKED +# define STATUS_INCOMPATIBLE_DRIVER_BLOCKED ((NTSTATUS) 0xC0000424L) +#endif + +#ifndef STATUS_HIVE_UNLOADED +# define STATUS_HIVE_UNLOADED ((NTSTATUS) 0xC0000425L) +#endif + +#ifndef STATUS_COMPRESSION_DISABLED +# define STATUS_COMPRESSION_DISABLED ((NTSTATUS) 0xC0000426L) +#endif + +#ifndef STATUS_FILE_SYSTEM_LIMITATION +# define STATUS_FILE_SYSTEM_LIMITATION ((NTSTATUS) 0xC0000427L) +#endif + +#ifndef STATUS_INVALID_IMAGE_HASH +# define STATUS_INVALID_IMAGE_HASH ((NTSTATUS) 0xC0000428L) +#endif + +#ifndef STATUS_NOT_CAPABLE +# define STATUS_NOT_CAPABLE ((NTSTATUS) 0xC0000429L) +#endif + +#ifndef STATUS_REQUEST_OUT_OF_SEQUENCE +# define STATUS_REQUEST_OUT_OF_SEQUENCE ((NTSTATUS) 0xC000042AL) +#endif + +#ifndef STATUS_IMPLEMENTATION_LIMIT +# define STATUS_IMPLEMENTATION_LIMIT ((NTSTATUS) 0xC000042BL) +#endif + +#ifndef STATUS_ELEVATION_REQUIRED +# define STATUS_ELEVATION_REQUIRED ((NTSTATUS) 0xC000042CL) +#endif + +#ifndef STATUS_NO_SECURITY_CONTEXT +# define STATUS_NO_SECURITY_CONTEXT ((NTSTATUS) 0xC000042DL) +#endif + +#ifndef STATUS_PKU2U_CERT_FAILURE +# define STATUS_PKU2U_CERT_FAILURE ((NTSTATUS) 0xC000042FL) +#endif + +#ifndef STATUS_BEYOND_VDL +# define STATUS_BEYOND_VDL ((NTSTATUS) 0xC0000432L) +#endif + +#ifndef STATUS_ENCOUNTERED_WRITE_IN_PROGRESS +# define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS ((NTSTATUS) 0xC0000433L) +#endif + +#ifndef STATUS_PTE_CHANGED +# define STATUS_PTE_CHANGED ((NTSTATUS) 0xC0000434L) +#endif + +#ifndef STATUS_PURGE_FAILED +# define STATUS_PURGE_FAILED ((NTSTATUS) 0xC0000435L) +#endif + +#ifndef STATUS_CRED_REQUIRES_CONFIRMATION +# define STATUS_CRED_REQUIRES_CONFIRMATION ((NTSTATUS) 0xC0000440L) +#endif + +#ifndef STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE +# define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE ((NTSTATUS) 0xC0000441L) +#endif + +#ifndef STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER +# define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER ((NTSTATUS) 0xC0000442L) +#endif + +#ifndef STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE +# define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE ((NTSTATUS) 0xC0000443L) +#endif + +#ifndef STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE +# define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE ((NTSTATUS) 0xC0000444L) +#endif + +#ifndef STATUS_CS_ENCRYPTION_FILE_NOT_CSE +# define STATUS_CS_ENCRYPTION_FILE_NOT_CSE ((NTSTATUS) 0xC0000445L) +#endif + +#ifndef STATUS_INVALID_LABEL +# define STATUS_INVALID_LABEL ((NTSTATUS) 0xC0000446L) +#endif + +#ifndef STATUS_DRIVER_PROCESS_TERMINATED +# define STATUS_DRIVER_PROCESS_TERMINATED ((NTSTATUS) 0xC0000450L) +#endif + +#ifndef STATUS_AMBIGUOUS_SYSTEM_DEVICE +# define STATUS_AMBIGUOUS_SYSTEM_DEVICE ((NTSTATUS) 0xC0000451L) +#endif + +#ifndef STATUS_SYSTEM_DEVICE_NOT_FOUND +# define STATUS_SYSTEM_DEVICE_NOT_FOUND ((NTSTATUS) 0xC0000452L) +#endif + +#ifndef STATUS_RESTART_BOOT_APPLICATION +# define STATUS_RESTART_BOOT_APPLICATION ((NTSTATUS) 0xC0000453L) +#endif + +#ifndef STATUS_INSUFFICIENT_NVRAM_RESOURCES +# define STATUS_INSUFFICIENT_NVRAM_RESOURCES ((NTSTATUS) 0xC0000454L) +#endif + +#ifndef STATUS_INVALID_TASK_NAME +# define STATUS_INVALID_TASK_NAME ((NTSTATUS) 0xC0000500L) +#endif + +#ifndef STATUS_INVALID_TASK_INDEX +# define STATUS_INVALID_TASK_INDEX ((NTSTATUS) 0xC0000501L) +#endif + +#ifndef STATUS_THREAD_ALREADY_IN_TASK +# define STATUS_THREAD_ALREADY_IN_TASK ((NTSTATUS) 0xC0000502L) +#endif + +#ifndef STATUS_CALLBACK_BYPASS +# define STATUS_CALLBACK_BYPASS ((NTSTATUS) 0xC0000503L) +#endif + +#ifndef STATUS_FAIL_FAST_EXCEPTION +# define STATUS_FAIL_FAST_EXCEPTION ((NTSTATUS) 0xC0000602L) +#endif + +#ifndef STATUS_IMAGE_CERT_REVOKED +# define STATUS_IMAGE_CERT_REVOKED ((NTSTATUS) 0xC0000603L) +#endif + +#ifndef STATUS_PORT_CLOSED +# define STATUS_PORT_CLOSED ((NTSTATUS) 0xC0000700L) +#endif + +#ifndef STATUS_MESSAGE_LOST +# define STATUS_MESSAGE_LOST ((NTSTATUS) 0xC0000701L) +#endif + +#ifndef STATUS_INVALID_MESSAGE +# define STATUS_INVALID_MESSAGE ((NTSTATUS) 0xC0000702L) +#endif + +#ifndef STATUS_REQUEST_CANCELED +# define STATUS_REQUEST_CANCELED ((NTSTATUS) 0xC0000703L) +#endif + +#ifndef STATUS_RECURSIVE_DISPATCH +# define STATUS_RECURSIVE_DISPATCH ((NTSTATUS) 0xC0000704L) +#endif + +#ifndef STATUS_LPC_RECEIVE_BUFFER_EXPECTED +# define STATUS_LPC_RECEIVE_BUFFER_EXPECTED ((NTSTATUS) 0xC0000705L) +#endif + +#ifndef STATUS_LPC_INVALID_CONNECTION_USAGE +# define STATUS_LPC_INVALID_CONNECTION_USAGE ((NTSTATUS) 0xC0000706L) +#endif + +#ifndef STATUS_LPC_REQUESTS_NOT_ALLOWED +# define STATUS_LPC_REQUESTS_NOT_ALLOWED ((NTSTATUS) 0xC0000707L) +#endif + +#ifndef STATUS_RESOURCE_IN_USE +# define STATUS_RESOURCE_IN_USE ((NTSTATUS) 0xC0000708L) +#endif + +#ifndef STATUS_HARDWARE_MEMORY_ERROR +# define STATUS_HARDWARE_MEMORY_ERROR ((NTSTATUS) 0xC0000709L) +#endif + +#ifndef STATUS_THREADPOOL_HANDLE_EXCEPTION +# define STATUS_THREADPOOL_HANDLE_EXCEPTION ((NTSTATUS) 0xC000070AL) +#endif + +#ifndef STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED +# define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070BL) +#endif + +#ifndef STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED +# define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070CL) +#endif + +#ifndef STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED +# define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070DL) +#endif + +#ifndef STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED +# define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070EL) +#endif + +#ifndef STATUS_THREADPOOL_RELEASED_DURING_OPERATION +# define STATUS_THREADPOOL_RELEASED_DURING_OPERATION ((NTSTATUS) 0xC000070FL) +#endif + +#ifndef STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING +# define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING ((NTSTATUS) 0xC0000710L) +#endif + +#ifndef STATUS_APC_RETURNED_WHILE_IMPERSONATING +# define STATUS_APC_RETURNED_WHILE_IMPERSONATING ((NTSTATUS) 0xC0000711L) +#endif + +#ifndef STATUS_PROCESS_IS_PROTECTED +# define STATUS_PROCESS_IS_PROTECTED ((NTSTATUS) 0xC0000712L) +#endif + +#ifndef STATUS_MCA_EXCEPTION +# define STATUS_MCA_EXCEPTION ((NTSTATUS) 0xC0000713L) +#endif + +#ifndef STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE +# define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE ((NTSTATUS) 0xC0000714L) +#endif + +#ifndef STATUS_SYMLINK_CLASS_DISABLED +# define STATUS_SYMLINK_CLASS_DISABLED ((NTSTATUS) 0xC0000715L) +#endif + +#ifndef STATUS_INVALID_IDN_NORMALIZATION +# define STATUS_INVALID_IDN_NORMALIZATION ((NTSTATUS) 0xC0000716L) +#endif + +#ifndef STATUS_NO_UNICODE_TRANSLATION +# define STATUS_NO_UNICODE_TRANSLATION ((NTSTATUS) 0xC0000717L) +#endif + +#ifndef STATUS_ALREADY_REGISTERED +# define STATUS_ALREADY_REGISTERED ((NTSTATUS) 0xC0000718L) +#endif + +#ifndef STATUS_CONTEXT_MISMATCH +# define STATUS_CONTEXT_MISMATCH ((NTSTATUS) 0xC0000719L) +#endif + +#ifndef STATUS_PORT_ALREADY_HAS_COMPLETION_LIST +# define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST ((NTSTATUS) 0xC000071AL) +#endif + +#ifndef STATUS_CALLBACK_RETURNED_THREAD_PRIORITY +# define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY ((NTSTATUS) 0xC000071BL) +#endif + +#ifndef STATUS_INVALID_THREAD +# define STATUS_INVALID_THREAD ((NTSTATUS) 0xC000071CL) +#endif + +#ifndef STATUS_CALLBACK_RETURNED_TRANSACTION +# define STATUS_CALLBACK_RETURNED_TRANSACTION ((NTSTATUS) 0xC000071DL) +#endif + +#ifndef STATUS_CALLBACK_RETURNED_LDR_LOCK +# define STATUS_CALLBACK_RETURNED_LDR_LOCK ((NTSTATUS) 0xC000071EL) +#endif + +#ifndef STATUS_CALLBACK_RETURNED_LANG +# define STATUS_CALLBACK_RETURNED_LANG ((NTSTATUS) 0xC000071FL) +#endif + +#ifndef STATUS_CALLBACK_RETURNED_PRI_BACK +# define STATUS_CALLBACK_RETURNED_PRI_BACK ((NTSTATUS) 0xC0000720L) +#endif + +#ifndef STATUS_CALLBACK_RETURNED_THREAD_AFFINITY +# define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY ((NTSTATUS) 0xC0000721L) +#endif + +#ifndef STATUS_DISK_REPAIR_DISABLED +# define STATUS_DISK_REPAIR_DISABLED ((NTSTATUS) 0xC0000800L) +#endif + +#ifndef STATUS_DS_DOMAIN_RENAME_IN_PROGRESS +# define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS ((NTSTATUS) 0xC0000801L) +#endif + +#ifndef STATUS_DISK_QUOTA_EXCEEDED +# define STATUS_DISK_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000802L) +#endif + +#ifndef STATUS_DATA_LOST_REPAIR +# define STATUS_DATA_LOST_REPAIR ((NTSTATUS) 0x80000803L) +#endif + +#ifndef STATUS_CONTENT_BLOCKED +# define STATUS_CONTENT_BLOCKED ((NTSTATUS) 0xC0000804L) +#endif + +#ifndef STATUS_BAD_CLUSTERS +# define STATUS_BAD_CLUSTERS ((NTSTATUS) 0xC0000805L) +#endif + +#ifndef STATUS_VOLUME_DIRTY +# define STATUS_VOLUME_DIRTY ((NTSTATUS) 0xC0000806L) +#endif + +#ifndef STATUS_FILE_CHECKED_OUT +# define STATUS_FILE_CHECKED_OUT ((NTSTATUS) 0xC0000901L) +#endif + +#ifndef STATUS_CHECKOUT_REQUIRED +# define STATUS_CHECKOUT_REQUIRED ((NTSTATUS) 0xC0000902L) +#endif + +#ifndef STATUS_BAD_FILE_TYPE +# define STATUS_BAD_FILE_TYPE ((NTSTATUS) 0xC0000903L) +#endif + +#ifndef STATUS_FILE_TOO_LARGE +# define STATUS_FILE_TOO_LARGE ((NTSTATUS) 0xC0000904L) +#endif + +#ifndef STATUS_FORMS_AUTH_REQUIRED +# define STATUS_FORMS_AUTH_REQUIRED ((NTSTATUS) 0xC0000905L) +#endif + +#ifndef STATUS_VIRUS_INFECTED +# define STATUS_VIRUS_INFECTED ((NTSTATUS) 0xC0000906L) +#endif + +#ifndef STATUS_VIRUS_DELETED +# define STATUS_VIRUS_DELETED ((NTSTATUS) 0xC0000907L) +#endif + +#ifndef STATUS_BAD_MCFG_TABLE +# define STATUS_BAD_MCFG_TABLE ((NTSTATUS) 0xC0000908L) +#endif + +#ifndef STATUS_CANNOT_BREAK_OPLOCK +# define STATUS_CANNOT_BREAK_OPLOCK ((NTSTATUS) 0xC0000909L) +#endif + +#ifndef STATUS_WOW_ASSERTION +# define STATUS_WOW_ASSERTION ((NTSTATUS) 0xC0009898L) +#endif + +#ifndef STATUS_INVALID_SIGNATURE +# define STATUS_INVALID_SIGNATURE ((NTSTATUS) 0xC000A000L) +#endif + +#ifndef STATUS_HMAC_NOT_SUPPORTED +# define STATUS_HMAC_NOT_SUPPORTED ((NTSTATUS) 0xC000A001L) +#endif + +#ifndef STATUS_AUTH_TAG_MISMATCH +# define STATUS_AUTH_TAG_MISMATCH ((NTSTATUS) 0xC000A002L) +#endif + +#ifndef STATUS_IPSEC_QUEUE_OVERFLOW +# define STATUS_IPSEC_QUEUE_OVERFLOW ((NTSTATUS) 0xC000A010L) +#endif + +#ifndef STATUS_ND_QUEUE_OVERFLOW +# define STATUS_ND_QUEUE_OVERFLOW ((NTSTATUS) 0xC000A011L) +#endif + +#ifndef STATUS_HOPLIMIT_EXCEEDED +# define STATUS_HOPLIMIT_EXCEEDED ((NTSTATUS) 0xC000A012L) +#endif + +#ifndef STATUS_PROTOCOL_NOT_SUPPORTED +# define STATUS_PROTOCOL_NOT_SUPPORTED ((NTSTATUS) 0xC000A013L) +#endif + +#ifndef STATUS_FASTPATH_REJECTED +# define STATUS_FASTPATH_REJECTED ((NTSTATUS) 0xC000A014L) +#endif + +#ifndef STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED +# define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED ((NTSTATUS) 0xC000A080L) +#endif + +#ifndef STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR +# define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR ((NTSTATUS) 0xC000A081L) +#endif + +#ifndef STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR +# define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR ((NTSTATUS) 0xC000A082L) +#endif + +#ifndef STATUS_XML_PARSE_ERROR +# define STATUS_XML_PARSE_ERROR ((NTSTATUS) 0xC000A083L) +#endif + +#ifndef STATUS_XMLDSIG_ERROR +# define STATUS_XMLDSIG_ERROR ((NTSTATUS) 0xC000A084L) +#endif + +#ifndef STATUS_WRONG_COMPARTMENT +# define STATUS_WRONG_COMPARTMENT ((NTSTATUS) 0xC000A085L) +#endif + +#ifndef STATUS_AUTHIP_FAILURE +# define STATUS_AUTHIP_FAILURE ((NTSTATUS) 0xC000A086L) +#endif + +#ifndef STATUS_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS +# define STATUS_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS ((NTSTATUS) 0xC000A087L) +#endif + +#ifndef STATUS_DS_OID_NOT_FOUND +# define STATUS_DS_OID_NOT_FOUND ((NTSTATUS) 0xC000A088L) +#endif + +#ifndef STATUS_HASH_NOT_SUPPORTED +# define STATUS_HASH_NOT_SUPPORTED ((NTSTATUS) 0xC000A100L) +#endif + +#ifndef STATUS_HASH_NOT_PRESENT +# define STATUS_HASH_NOT_PRESENT ((NTSTATUS) 0xC000A101L) +#endif + +/* This is not the NTSTATUS_FROM_WIN32 that the DDK provides, because the DDK got it wrong! */ +#ifdef NTSTATUS_FROM_WIN32 +# undef NTSTATUS_FROM_WIN32 +#endif +#define NTSTATUS_FROM_WIN32(error) ((NTSTATUS) (error) <= 0 ? \ + ((NTSTATUS) (error)) : ((NTSTATUS) (((error) & 0x0000FFFF) | \ + (FACILITY_NTWIN32 << 16) | ERROR_SEVERITY_WARNING))) + +/* from ntifs.h */ +/* MinGW already has it, mingw-w64 does not. */ +#if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR) +typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + } DUMMYUNIONNAME; +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; +#endif + +typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + } DUMMYUNIONNAME; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef struct _FILE_PIPE_LOCAL_INFORMATION { + ULONG NamedPipeType; + ULONG NamedPipeConfiguration; + ULONG MaximumInstances; + ULONG CurrentInstances; + ULONG InboundQuota; + ULONG ReadDataAvailable; + ULONG OutboundQuota; + ULONG WriteQuotaAvailable; + ULONG NamedPipeState; + ULONG NamedPipeEnd; +} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; + +typedef struct _FILE_BASIC_INFORMATION { + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + DWORD FileAttributes; +} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION; + +typedef struct _FILE_DISPOSITION_INFORMATION { + BOOLEAN DeleteFile; +} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION; + +typedef struct _FILE_MODE_INFORMATION { + ULONG Mode; +} FILE_MODE_INFORMATION, *PFILE_MODE_INFORMATION; + +typedef struct _FILE_END_OF_FILE_INFORMATION { + LARGE_INTEGER EndOfFile; +} FILE_END_OF_FILE_INFORMATION, *PFILE_END_OF_FILE_INFORMATION; + +#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010 +#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 + +typedef enum _FILE_INFORMATION_CLASS { + FileDirectoryInformation = 1, + FileFullDirectoryInformation, + FileBothDirectoryInformation, + FileBasicInformation, + FileStandardInformation, + FileInternalInformation, + FileEaInformation, + FileAccessInformation, + FileNameInformation, + FileRenameInformation, + FileLinkInformation, + FileNamesInformation, + FileDispositionInformation, + FilePositionInformation, + FileFullEaInformation, + FileModeInformation, + FileAlignmentInformation, + FileAllInformation, + FileAllocationInformation, + FileEndOfFileInformation, + FileAlternateNameInformation, + FileStreamInformation, + FilePipeInformation, + FilePipeLocalInformation, + FilePipeRemoteInformation, + FileMailslotQueryInformation, + FileMailslotSetInformation, + FileCompressionInformation, + FileObjectIdInformation, + FileCompletionInformation, + FileMoveClusterInformation, + FileQuotaInformation, + FileReparsePointInformation, + FileNetworkOpenInformation, + FileAttributeTagInformation, + FileTrackingInformation, + FileIdBothDirectoryInformation, + FileIdFullDirectoryInformation, + FileValidDataLengthInformation, + FileShortNameInformation, + FileIoCompletionNotificationInformation, + FileIoStatusBlockRangeInformation, + FileIoPriorityHintInformation, + FileSfioReserveInformation, + FileSfioVolumeInformation, + FileHardLinkInformation, + FileProcessIdsUsingFileInformation, + FileNormalizedNameInformation, + FileNetworkPhysicalNameInformation, + FileIdGlobalTxDirectoryInformation, + FileIsRemoteDeviceInformation, + FileAttributeCacheInformation, + FileNumaNodeInformation, + FileStandardLinkInformation, + FileRemoteProtocolInformation, + FileMaximumInformation +} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; + +typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { + LARGE_INTEGER IdleTime; + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER DpcTime; + LARGE_INTEGER InterruptTime; + ULONG InterruptCount; +} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, *PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; + +#ifndef SystemProcessorPerformanceInformation +# define SystemProcessorPerformanceInformation 8 +#endif + +#ifndef DEVICE_TYPE +# define DEVICE_TYPE DWORD +#endif + +#ifndef FILE_DEVICE_FILE_SYSTEM +# define FILE_DEVICE_FILE_SYSTEM 0x00000009 +#endif + +#ifndef FILE_DEVICE_NETWORK +# define FILE_DEVICE_NETWORK 0x00000012 +#endif + +#ifndef METHOD_BUFFERED +# define METHOD_BUFFERED 0 +#endif + +#ifndef METHOD_IN_DIRECT +# define METHOD_IN_DIRECT 1 +#endif + +#ifndef METHOD_OUT_DIRECT +# define METHOD_OUT_DIRECT 2 +#endif + +#ifndef METHOD_NEITHER +#define METHOD_NEITHER 3 +#endif + +#ifndef METHOD_DIRECT_TO_HARDWARE +# define METHOD_DIRECT_TO_HARDWARE METHOD_IN_DIRECT +#endif + +#ifndef METHOD_DIRECT_FROM_HARDWARE +# define METHOD_DIRECT_FROM_HARDWARE METHOD_OUT_DIRECT +#endif + +#ifndef FILE_ANY_ACCESS +# define FILE_ANY_ACCESS 0 +#endif + +#ifndef FILE_SPECIAL_ACCESS +# define FILE_SPECIAL_ACCESS (FILE_ANY_ACCESS) +#endif + +#ifndef FILE_READ_ACCESS +# define FILE_READ_ACCESS 0x0001 +#endif + +#ifndef FILE_WRITE_ACCESS +# define FILE_WRITE_ACCESS 0x0002 +#endif + +#ifndef CTL_CODE +# define CTL_CODE(device_type, function, method, access) \ + (((device_type) << 16) | ((access) << 14) | ((function) << 2) | (method)) +#endif + +#ifndef FSCTL_SET_REPARSE_POINT +# define FSCTL_SET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, \ + 41, \ + METHOD_BUFFERED, \ + FILE_SPECIAL_ACCESS) +#endif + +#ifndef FSCTL_GET_REPARSE_POINT +# define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, \ + 42, \ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) +#endif + +#ifndef FSCTL_DELETE_REPARSE_POINT +# define FSCTL_DELETE_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, \ + 43, \ + METHOD_BUFFERED, \ + FILE_SPECIAL_ACCESS) +#endif + +#ifndef IO_REPARSE_TAG_SYMLINK +# define IO_REPARSE_TAG_SYMLINK (0xA000000CL) +#endif + +typedef VOID (NTAPI *PIO_APC_ROUTINE)(PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG Reserved); + +typedef ULONG(NTAPI *sRtlNtStatusToDosError)(NTSTATUS Status); + +typedef NTSTATUS(NTAPI *sNtDeviceIoControlFile)(HANDLE FileHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG IoControlCode, + PVOID InputBuffer, + ULONG InputBufferLength, + PVOID OutputBuffer, + ULONG OutputBufferLength); + +typedef NTSTATUS(NTAPI *sNtQueryInformationFile)(HANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID FileInformation, + ULONG Length, + FILE_INFORMATION_CLASS FileInformationClass); + +typedef NTSTATUS(NTAPI *sNtSetInformationFile)(HANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID FileInformation, + ULONG Length, + FILE_INFORMATION_CLASS FileInformationClass); + +typedef NTSTATUS(NTAPI *sNtQuerySystemInformation)(UINT SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength); + + +/* + * Kernel32 headers + */ +#ifndef FILE_SKIP_COMPLETION_PORT_ON_SUCCESS +# define FILE_SKIP_COMPLETION_PORT_ON_SUCCESS 0x1 +#endif + +#ifndef FILE_SKIP_SET_EVENT_ON_HANDLE +# define FILE_SKIP_SET_EVENT_ON_HANDLE 0x2 +#endif + +#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY +# define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1 +#endif + +#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) +typedef struct _OVERLAPPED_ENTRY { + ULONG_PTR lpCompletionKey; + LPOVERLAPPED lpOverlapped; + ULONG_PTR Internal; + DWORD dwNumberOfBytesTransferred; +} OVERLAPPED_ENTRY, *LPOVERLAPPED_ENTRY; +#endif + +/* from wincon.h */ +#ifndef ENABLE_INSERT_MODE +# define ENABLE_INSERT_MODE 0x20 +#endif + +#ifndef ENABLE_QUICK_EDIT_MODE +# define ENABLE_QUICK_EDIT_MODE 0x40 +#endif + +#ifndef ENABLE_EXTENDED_FLAGS +# define ENABLE_EXTENDED_FLAGS 0x80 +#endif + +/* from winerror.h */ +#ifndef ERROR_SYMLINK_NOT_SUPPORTED +# define ERROR_SYMLINK_NOT_SUPPORTED 1464 +#endif + +/** Mingw doesn't have this */ +#ifndef SO_UPDATE_CONNECT_CONTEXT +#define SO_UPDATE_CONNECT_CONTEXT 0x7010 +#endif + + + +#endif /* UV_WIN_WINAPI_H_ */ diff --git a/couchbase-sys/libcouchbase-2.7.0/contrib/win32-defs/win_stdint.h b/couchbase-sys/libcouchbase-2.7.0/contrib/win32-defs/win_stdint.h new file mode 100644 index 00000000..61791d8a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/contrib/win32-defs/win_stdint.h @@ -0,0 +1,258 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2009 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef _MSC_VER // [ +#define _MSC_STDINT_H_ +#include +#endif // _MSC_VER ] + +#if _MSC_VER >= 1600 +#include +#else + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include + +// For Visual Studio 6 in C++ mode wrap include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#if (_MSC_VER < 1300) && defined(__cplusplus) +extern "C++" { +#endif +# include +#if (_MSC_VER < 1300) && defined(__cplusplus) +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types +typedef __int8 int8_t; +typedef __int16 int16_t; +#ifndef int32_t +typedef __int32 int32_t; +#endif +#ifndef int64_t +typedef __int64 int64_t; +#endif +#ifndef uint8_t +typedef unsigned __int8 uint8_t; +#endif +#ifndef u_char +typedef unsigned __int8 u_char; +#endif +typedef unsigned __int16 uint16_t; +#ifndef uint32_t +typedef unsigned __int32 uint32_t; +#endif +typedef unsigned __int64 uint64_t; + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint32_t u_int32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ +typedef __int64 intptr_t; +typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ +typedef _W64 int intptr_t; +typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +#define INTMAX_C INT64_C +#define UINTMAX_C UINT64_C + +#if _MSC_VER < 1600 +static __inline int64_t llabs(int64_t i) +{ + return i >= 0 ? i : -i; +} +#endif + +#endif // __STDC_CONSTANT_MACROS ] + +#endif // _MSC_VER < 1600 + +#endif // _MSC_STDINT_H_ ] diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/Doxyfile b/couchbase-sys/libcouchbase-2.7.0/doc/Doxyfile new file mode 100644 index 00000000..026a549e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/Doxyfile @@ -0,0 +1,1840 @@ +# Doxyfile 1.8.1.2 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = "Couchbase C Client" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 2.7.0 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +# PROJECT_BRIEF = "Asynchronous C Client for Couchbase" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = +ALIASES += "committed=\par Stability\n\xrefitem lcb_apiattr_committed \"Committed\" \"Committed Interfaces\"" +ALIASES += "uncommitted=\par Stability\n\xrefitem lcb_apiattr_uncommitted \"Uncommitted\" \"Unommitted Interfaces\"" +ALIASES += "volatile=\par Stability\n\xrefitem lcb_apiattr_volatile \"Volatile\" \"Volatile Interfaces\"" +ALIASES += "commited=committed" +ALISES += "comitted=committed" +ALIASES += "uncommited=uncommitted" +ALIASES += "uncomitted=uncommitted" +ALIASES += "lcb_see_detailed_neterr=\n\note This error code is returned only if \ref LCB_CNTL_DETAILED_ERRCODES is set\n" + +# Make the lcb_cntl documentation less wordy in the header +ALIASES += "cntl_arg_both{1}=@par Argument\nModes|Arg\n---|---\nGet, Set|\1\n" +ALIASES += "cntl_arg_getonly{1}=@par Argument\nModes|Arg\n---|---\nGet|\1\n" +ALIASES += "cntl_arg_setonly{1}=@par Argument\nModes|Arg\n---|---\nSet|\1\n" +ALIASES += "cntl_arg_get_and_set{2}=@par Argument\nModes|Arg\n---|---\nGet|\1\nSet|\2\n" +ALIASES += "return_rc=\return Status code indicating whether the operation was scheduled or not. See 'exceptions' section for errors received in callbacks" +ALIASES += "cb_err=\li" + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = YES + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = YES + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +SYMBOL_CACHE_SIZE = 0 + +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = NO + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 0 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = doc/DoxygenLayout.xml + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = NO + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = include/libcouchbase/couchbase.h +INPUT += include/libcouchbase +INPUT += plugins/io/libevent/libevent_io_opts.h +INPUT += plugins/io/libev/libev_io_opts.h +INPUT += plugins/io/libuv/libuv_io_opts.h +INPUT += doc/apiattr.h doc/mainpage.h doc/intro.h doc/environment.h +INPUT += doc/cbc.markdown doc/cbc-pillowfight.markdown doc/cbcrc.markdown +INPUT += doc/cbc-n1qlback.markdown + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = contrib \ + gtest* \ + tests \ + include/libcouchbase/cxxwrap.h + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = example/ doc/example + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = *.c + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = YES + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = doc/header.html + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = doc/footer.html + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# style sheet in the HTML output directory as well, or it will be erased! + +HTML_EXTRA_STYLESHEET = doc/style.css + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = YES + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 0 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 150 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = NO + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = YES + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = LCB_CREATE_V2_FIELDS= \ + __LCB_DOXYGEN__ \ + LCB_CMDRESP_LABEL \ + LCB_DEPRECATED \ + LIBCOUCHBASE_API \ + LCB_DEPR_API=/**@deprecated*/ \ + LCB__HKFIELDS \ + LCB_DEPR_CTORS_GET \ + LCB_DEPR_CTORS_RGET \ + LCB_DEPR_CTORS_UNL \ + LCB_DEPR_CTORS_STORE \ + LCB_DEPR_CTORS_ARITH \ + LCB_DEPR_CTORS_OBS \ + LCB_DEPR_CTORS_RM \ + LCB_DEPR_CTORS_STATS \ + LCB_DEPR_CTORS_VERBOSITY \ + LCB_DEPR_CTORS_VERSIONS \ + LCB_DEPR_CTORS_FLUSH \ + LCB_DEPR_CTORS_HTTP \ + LCB_DEPR_CTORS_CRST + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = LCB_XERR X \ + LCB_CMD_BASE \ + LCB_OBSERVE_FIELDS_COMMON LCB_RESP_BASE LCB_CMD_BASE LCB_RESP_SERVER_FIELDS + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# managable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = YES + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/DoxygenLayout.xml b/couchbase-sys/libcouchbase-2.7.0/doc/DoxygenLayout.xml new file mode 100644 index 00000000..4e397775 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/DoxygenLayout.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/Makefile b/couchbase-sys/libcouchbase-2.7.0/doc/Makefile new file mode 100644 index 00000000..406b507e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/Makefile @@ -0,0 +1,45 @@ +SRCROOT=$(shell pwd) +VERSINFO=$(shell $(SRCROOT)/packaging/parse-git-describe.pl --tar) +ifneq ($(strip $(VERSINFO)),) + VERLINE=PROJECT_NUMBER=$(VERSINFO) +endif + +DOXYGEN?=doxygen +DOXYFILE=doc/Doxyfile +OUTDIR_PUB = doc/public +OUTDIR_PRIV = doc/internal +INTERNAL_SRC = \ + src/rdb \ + src/bootstrap.h \ + src/lcbio \ + src/netbuf \ + src/mc \ + src/retryq.h \ + src/bucketconfig/clconfig.h \ + src/mcserver \ + src/simplestring.h \ + src/list.h \ + src/sllist.h \ + src/sllist-inl.h \ + src/hostlist.h \ + include/memcached/protocol_binary.h + +all: public internal +public: + (cat $(DOXYFILE);\ + echo $(VERLINE);\ + echo OUTPUT_DIRECTORY=$(OUTDIR_PUB)) | $(DOXYGEN) - + cp doc/style.css $(OUTDIR_PUB)/html/style.css + + + +internal: + (cat $(DOXYFILE);\ + echo $(VERLINE); \ + echo OUTPUT_DIRECTORY=$(OUTDIR_PRIV); \ + echo INTERNAL_DOCS=yes; \ + echo INPUT += $(INTERNAL_SRC);) | $(DOXYGEN) - + cp doc/style.css $(OUTDIR_PRIV)/html/style.css + +clean: + rm -r -f $(OUTDIR_PRIV)/html $(OUTDIR_PUB)/html diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/apiattr.h b/couchbase-sys/libcouchbase-2.7.0/doc/apiattr.h new file mode 100644 index 00000000..9331612c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/apiattr.h @@ -0,0 +1,89 @@ +/** + * @page lcb_attributes Interface Attributes + * + * Libcouchbase tries to follow the _release early, release often_ + * philosophy. As part of that we want people to be able to try out new + * interfaces before we "stick" to them. Unfortunately this means that we + * discover that we need to change them in incompatible ways. + * + * To aid developers to make reasonable decisions on how likely we are to + * change these interfaces all functions in libcouchbase have an + * associated Interface Stability tag. If you find undocumented structs, + * functions or files you should *not* use them. They may be changed in + * incompatible ways without any notice. Unless explicitly noted the + * interface stability applies to both source code and binaries. + * + * The following classifications exist: + * + * ### Committed + * + * A committed interface is the highest grade of stability, and is the + * preferred attribute level for consumers of the library. Couchbase + * tries at best effort to preseve committed interfaces between major + * versions of libcouchbase, and changes to committed interfaces within a + * major version is highly exceptional. Such exceptions may include + * situations where the interface may lead to data corruption, security + * holes etc. + * + * _This is the default interface level for an API, unless the API is specifically + * marked otherwise._ + * + * ### Uncommitted interface + * + * No commitment is made about the interface (in binary or source + * form). It may be changed in incompatible ways and dropped from one + * release to another. The difference between an uncommitted interface + * and a volatile interface is its maturity and likelyhood of being + * changed. Uncommitted interfaces may mature into committed interfaces. + * + * ### Volatile interfaces + * + * Volatile interfaces can change at any time and for any reason. + * + * Interfaces may be volatile for reasons including: + * + * * Interface depends on specific implementation detail within the library + * which may change in the future. + * + * * Interface depends on specific implementation detail within the server + * which may change in the future. + * + * * Interface has been introduced as part of a trial phase for the specific + * feature. + * + * ### Deprecated + * + * The interface is subject to be removed from future versions of + * libcouchbase. Interfaces may be deprecated for a variety of reasons, such + * as specific bugs found within the API itself, or a more uniform and/or + * direct method of achieving the same goal. + * + * ### Private + * + * Private interfaces is used internally in libcouchbase and should not + * be used elsewhere. Doing so may cause libcouchbase to misbehave. + * + * Unless otherwise noted, any API not found in the + * directory is considered to be private + * + * The listing of interfaces may be found here: + * + * * @subpage lcb_apiattr_committed + * * @subpage lcb_apiattr_uncommitted + * * @subpage lcb_apiattr_volatile + * * @subpage deprecated + **/ + +/** + * @page lcb_apiattr_committed Committed Interfaces + * @see @ref lcb_attributes + * + * @page lcb_apiattr_uncommitted Uncomitted Interfaces + * @see @ref lcb_attributes + * + * @page lcb_apiattr_volatile Volatile Interfaces + * @see @ref lcb_attributes + * + * @page deprecated Deprecated Interfaces + * @see @ref lcb_attributes + */ diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/cbc-n1qlback.markdown b/couchbase-sys/libcouchbase-2.7.0/doc/cbc-n1qlback.markdown new file mode 100644 index 00000000..2284f466 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/cbc-n1qlback.markdown @@ -0,0 +1,143 @@ +# cbc-n1qlback(1) - Stress Test for Couchbase Query (N1QL) + +## SYNOPSIS + +`cbc-n1qlback` -f QUERYFILE [_OPTIONS_] + +## DESCRIPTION + +`cbc-n1qlback` creates a specified number of threads each executing a set +of user defined queries. + +`cbc-n1qlback` requires that it be passed the path to a file containing +the queries to execute; one per line. The query should be in the format of +the actual HTTP POST body (in JSON format) to be sent to the server. +For simple queries, only the `statement` field needs to be set: + + {"statement":"SELECT country FROM `travel-sample`"} + {"statement":"SELECT country, COUNT(country) FROM `travel-sample` GROUP BY country"} + + +For more complex queries (for example, placeholders, custom options), you may +refer to the N1QL REST API reference. + +`n1qlback` requires that any resources (data items, indexes) are already +defined. + +## OPTIONS + +The following options control workload generation: + +* `-f` `--queryfile`=_PATH_: + Path to a file containing the query bodies to execute in JSON format, one + query per line. See above for the format. + +* `-t`, `--num-threads`=_NTHREADS_: + Set the number of threads (and thus the number of client instances) to run + concurrently. Each thread is assigned its own client object. + + +The following options control how `cbc-n1qlback` connects to the cluster + +* `-U`, `--spec`=_SPEC_: + A string describing the cluster to connect to. The string is in a URI-like syntax, + and may also contain other options. See the [EXAMPLES](#examples) section for information. + Typically such a URI will look like `couchbase://host1,host2,host3/bucket`. + + The default for this option is `couchbase://localhost/default` + +* `-u`, `--username`=_USERNAME_: + Specify the _username_ for the bucket. As of Couchbase Server 2.5 this field + should be either left empty or set to the name of the bucket itself. + +* `-P`, `--password`=_SASLPASS_: +* `-P -`, `--password=-`: + Specify the SASL password for the bucket. This is only needed if the bucket is + protected with a password. Note that this is _not_ the administrative password + used to log into the web interface. + + Specifying the `-` as the password indicates that the program should prompt for the + password. You may also specify the password on the commandline, directly, + but is insecure as command line arguments are visible via commands such as `ps`. + +* `-T`, `--timings`: + Dump command timings at the end of execution. This will display a histogram + showing the latencies for the commands executed. + +* `-v`, `--verbose`: + Specify more information to standard error about what the client is doing. You may + specify this option multiple times for increased output detail. + +* `-D`, `--cparam`=OPTION=VALUE: + Provide additional client options. Acceptable options can also be placed + in the connection string, however this option is provided as a convenience. + This option may be specified multiple times, each time specifying a key=value + pair (for example, `-Doperation_timeout=10 -Dconfig_cache=/foo/bar/baz`). + See [ADDITIONAL OPTIONS](#additional-options) for more information + + +## ADDITIONAL OPTIONS + +The following options may be included in the connection string (via the `-U` +option) as URI-style query params (e.g. +`couchbase://host/bucket?option1=value1&option2=value2`) or as individual +key=value pairs passed to the `-D` switch (e.g. `-Doption1=value1 +-Doption2=value`). The `-D` will internally build the connection string, +and is provided as a convenience for options to be easily passed on the +command-line + +* `operation_timeout=SECONDS`: + Specify the operation timeout in seconds. This is the time the client will + wait for an operation to complete before timing it out. The default is `2.5` +* `config_cache=PATH`: + Enables the client to make use of a file based configuration cache rather + than connecting for the bootstrap operation. If the file does not exist, the + client will first connect to the cluster and then cache the bootstrap information + in the file. +* `certpath=PATH`: + The path to the server's SSL certificate. This is typically required for SSL + connectivity unless the certificate has already been added to the openssl + installation on the system (only applicable with `couchbases://` scheme) +* `ssl=no_verify`: + Temporarily disable certificate verification for SSL (only applicable with + `couchbases://` scheme). This should only be used for quickly debugging SSL + functionality. +* `sasl_mech_force=MECHANISM`: + Force a specific _SASL_ mechanism to be used when performing the initial + connection. This should only need to be modified for debugging purposes. + The currently supported mechanisms are `PLAIN` and `CRAM-MD5` +* `bootstrap_on=`: + Specify the bootstrap protocol the client should use when attempting to connect + to the cluster. Options are: `cccp`: Bootstrap using the Memcached protocol + (supported on clusters 2.5 and greater); `http`: Bootstrap using the HTTP REST + protocol (supported on any cluster version); and `both`: First attempt bootstrap + over the Memcached protocol, and use the HTTP protocol if Memcached bootstrap fails. + The default is `both` + +## EXAMPLES + +The following will create a file with 3 queries and 5 threads alternating +between them. It also creates indexes on the `travel-sample` bucket + + cbc n1ql -U couchbase://192.168.72.101/a_bucket 'CREATE INDEX ix_name ON `travel-sample`(name)' + cbc n1ql -U couchbase://192.168.72.101/a_bucket 'CREATE INDEX ix_country ON `travel-sample`(country)' + + cat queries.txt < +## ADDITIONAL OPTIONS + +The following options may be included in the connection string (via the `-U` +option) as URI-style query params (e.g. +`couchbase://host/bucket?option1=value1&option2=value2`) or as individual +key=value pairs passed to the `-D` switch (e.g. `-Doption1=value1 +-Doption2=value`). The `-D` will internally build the connection string, +and is provided as a convenience for options to be easily passed on the +command-line + +* `operation_timeout=SECONDS`: + Specify the operation timeout in seconds. This is the time the client will + wait for an operation to complete before timing it out. The default is `2.5` +* `config_cache=PATH`: + Enables the client to make use of a file based configuration cache rather + than connecting for the bootstrap operation. If the file does not exist, the + client will first connect to the cluster and then cache the bootstrap information + in the file. +* `certpath=PATH`: + The path to the server's SSL certificate. This is typically required for SSL + connectivity unless the certificate has already been added to the openssl + installation on the system (only applicable with `couchbases://` scheme) +* `ssl=no_verify`: + Temporarily disable certificate verification for SSL (only applicable with + `couchbases://` scheme). This should only be used for quickly debugging SSL + functionality. +* `sasl_mech_force=MECHANISM`: + Force a specific _SASL_ mechanism to be used when performing the initial + connection. This should only need to be modified for debugging purposes. + The currently supported mechanisms are `PLAIN` and `CRAM-MD5` +* `bootstrap_on=`: + Specify the bootstrap protocol the client should use when attempting to connect + to the cluster. Options are: `cccp`: Bootstrap using the Memcached protocol + (supported on clusters 2.5 and greater); `http`: Bootstrap using the HTTP REST + protocol (supported on any cluster version); and `both`: First attempt bootstrap + over the Memcached protocol, and use the HTTP protocol if Memcached bootstrap fails. + The default is `both` + +## EXAMPLES + +### CONNECTION EXAMPLES + +The following examples show how to connect `pillowfight` to different types +of cluster configurations. + +Run against a bucket (`a_bucket`) on a cluster on a remote host: + + cbc cat key -U couchbase://192.168.33.101/a_bucket + +Connect to an SSL cluster at `secure.net`. The certificate for the cluster is +stored locally at `/home/couchbase/couchbase_cert.pem`: + + cbc cat key -U couchbases://secure.net/topsecret_bucket?certpath=/home/couchbase/couchbase_cert.pem + +Connect to an SSL cluster at `secure.net`, ignoring certificate verification. +This is insecure but handy for testing: + + cbc cat key -U couchbases://secure.net/topsecret_bucket?ssl=no_verify + +Connect to a password protected bucket (`protected`) on a remote host: + + cbc cat key -U couchbase://remote.host.net/protected -P - + Bucket password: + ... + +Connect to a password protected bucket, specifying the password on the +command line (INSECURE, but useful for testing dummy environments) + + cbc cat key -U couchbase://remote.host.net/protected -P t0ps3cr3t + +Connect to a bucket running on a cluster with a custom REST API port + + cbc cat key -U http://localhost:9000/default + +Connec to bucket running on a cluster with a custom memcached port + + cbc cat key -U couchbase://localhost:12000/default + +Connect to a *memcached* (http://memcached.org) +cluster using the binary protocol. A vanilla memcached cluster is not the same +as a memcached bucket residing within a couchbase cluster (use the normal +`couchbase://` scheme for that): + + cbc cat key -U memcached://host1,host2,host3,host4 + +Connect to an SSL cluster at `secure.net`: + + cbc-pillowfight -U couchbases://secure.net/topsecret_bucket + + +Run against a bucket (`a_bucket`) on a cluster on a remote host: + + cbc-pillowfight -U couchbase://192.168.33.101/a_bucket + +### BENCHMARK EXAMPLES + +The following examples show how to configure different types of workloads with +pillowfight. + +Run with 20 threads/instances, each doing one operation at a time: + + cbc-pillowfight -t 20 -B 1 + +Run 100 iterations of 2MB item sizes, using a dataset of 50 items + + cbc-pillowfight -M $(1024*1024) -m $(1024*1024) -c 100 -I 50 + +Use JSON documents of 100k each + + cbc-pillowfight --json -m 100000 -M 100000 + +Stress-test sub-document mutations + + cbc-pillowfight --json --subdoc --set-pct 100 + + +## TODO + +Rather than spawning threads for multiple instances, offer a way to have multiple +instances function cooperatively inside an event loop. + +## BUGS + +This command's options are subject to change. + +## SEE ALSO + +cbc(1), cbcrc(4) + +## HISTORY + +The `cbc-pillowfight` tool was first introduced in libcouchbase 2.0.7 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/cbc.markdown b/couchbase-sys/libcouchbase-2.7.0/doc/cbc.markdown new file mode 100644 index 00000000..0b10ad5d --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/cbc.markdown @@ -0,0 +1,600 @@ +# cbc(1) - Couchbase Client Commandline Utility + +## SYNOPSIS + +`cbc` _COMMAND_ [_OPTIONS_]
+`cbc help`
+`cbc version`
+`cbc cat` _KEYS_ ... [_OPTIONS_]
+`cbc create` _KEY_ _-V VALUE_ [_OPTIONS_]
+`cbc create` _KEY_ [_OPTIONS_]
+`cbc cp` _FILES_ ... [_OPTIONS_]
+`cbc incr` _KEY_ [_OPTIONS_]
+`cbc decr` _KEY_ [_OPTIONS_]
+`cbc rm` _KEY_ [_OPTIONS_]
+`cbc hash` _KEY_ [_OPTIONS_]
+`cbc stats` _KEYS_ ... [_OPTIONS_]
+`cbc observe` _KEYS_ ... [_OPTIONS_]
+`cbc view` _VIEWPATH_ [_OPTIONS_]
+`cbc lock` _KEY_ [_OPTIONS_]
+`cbc unlock` _KEY_ _CAS_ [_OPTIONS_]
+`cbc admin` _-P PASSWORD_ _RESTAPI_ [_OPTIONS_]
+`cbc bucket-create` _-P PASSWORD_ _NAME_ [_OPTIONS_]
+`cbc bucket-delete` _-P PASSWORD_ _NAME_ [_OPTIONS_]
+`cbc bucket-flush` _NAME_ [_OPTIONS_]
+`cbc connstr` _SPEC_
+`cbc n1ql` _QUERY_ ... [_OPTIONS_]
+ +## DESCRIPTION + +`cbc` is a utility for communicating with a Couchbase cluster. + +`cbc` should be invoked with the command name first and then a series of command +options appropriate for the specific command. `cbc help` will always show the full +list of available commands. + + +## OPTIONS + +Options may be read either from the command line, or from a configuration file +(see cbcrc(4)): + +The following common options may be applied to most of the commands + +* `-U`, `--spec`=_SPEC_: + A string describing the cluster to connect to. The string is in a URI-like syntax, + and may also contain other options. See the [EXAMPLES](#examples) section for information. + Typically such a URI will look like `couchbase://host1,host2,host3/bucket`. + + The default for this option is `couchbase://localhost/default` + +* `-u`, `--username`=_USERNAME_: + Specify the _username_ for the bucket. As of Couchbase Server 2.5 this field + should be either left empty or set to the name of the bucket itself. + +* `-P`, `--password`=_SASLPASS_: +* `-P -`, `--password=-`: + Specify the SASL password for the bucket. This is only needed if the bucket is + protected with a password. Note that this is _not_ the administrative password + used to log into the web interface. + + Specifying the `-` as the password indicates that the program should prompt for the + password. You may also specify the password on the commandline, directly, + but is insecure as command line arguments are visible via commands such as `ps`. + +* `-T`, `--timings`: + Dump command timings at the end of execution. This will display a histogram + showing the latencies for the commands executed. + +* `-v`, `--verbose`: + Specify more information to standard error about what the client is doing. You may + specify this option multiple times for increased output detail. + +* `-D`, `--cparam`=OPTION=VALUE: + Provide additional client options. Acceptable options can also be placed + in the connection string, however this option is provided as a convenience. + This option may be specified multiple times, each time specifying a key=value + pair (for example, `-Doperation_timeout=10 -Dconfig_cache=/foo/bar/baz`). + See [ADDITIONAL OPTIONS](#additional-options) for more information + + + +## ADDITIONAL OPTIONS + +The following options may be included in the connection string (via the `-U` +option) as URI-style query params (e.g. +`couchbase://host/bucket?option1=value1&option2=value2`) or as individual +key=value pairs passed to the `-D` switch (e.g. `-Doption1=value1 +-Doption2=value`). The `-D` will internally build the connection string, +and is provided as a convenience for options to be easily passed on the +command-line + +* `operation_timeout=SECONDS`: + Specify the operation timeout in seconds. This is the time the client will + wait for an operation to complete before timing it out. The default is `2.5` +* `config_cache=PATH`: + Enables the client to make use of a file based configuration cache rather + than connecting for the bootstrap operation. If the file does not exist, the + client will first connect to the cluster and then cache the bootstrap information + in the file. +* `certpath=PATH`: + The path to the server's SSL certificate. This is typically required for SSL + connectivity unless the certificate has already been added to the openssl + installation on the system (only applicable with `couchbases://` scheme) +* `ssl=no_verify`: + Temporarily disable certificate verification for SSL (only applicable with + `couchbases://` scheme). This should only be used for quickly debugging SSL + functionality. +* `sasl_mech_force=MECHANISM`: + Force a specific _SASL_ mechanism to be used when performing the initial + connection. This should only need to be modified for debugging purposes. + The currently supported mechanisms are `PLAIN` and `CRAM-MD5` +* `bootstrap_on=`: + Specify the bootstrap protocol the client should use when attempting to connect + to the cluster. Options are: `cccp`: Bootstrap using the Memcached protocol + (supported on clusters 2.5 and greater); `http`: Bootstrap using the HTTP REST + protocol (supported on any cluster version); and `both`: First attempt bootstrap + over the Memcached protocol, and use the HTTP protocol if Memcached bootstrap fails. + The default is `both` + +## COMMANDS + +The following commands are supported by `cbc`. Unless otherwise specified, each +command supports all of the options above. + +### cat + +Write the value of keys to standard output. + +This command requires that at least one key may be passed to it, but may accept +multiple keys. The keys should be specified as positional arguments after the +command. + +In addition to the options in the [OPTIONS](#OPTIONS) section, the following options are supported: + +* `r`, `--replica`=_all|INDEX_: + Read the value from a replica server. The value for this option can either be + the string `all` which will cause the client to request the value from each + replica, or `INDEX` where `INDEX` is a 0-based replica index. + +* `e`, `--expiry`=_EXPIRATION_: + Specify that this operation should be a _get-and-touch_ operation in which the + key's expiry time is updated along with retrieving the item. + + +### create + +### cp + +Create a new item in the cluster, or update the value of an existing item. +By default this command will read the value from standard input unless the +`--value` option is specified. + +The `cp` command functions the same, except it operates on a list of files. Each file is +stored in the cluster under the name specified on the command line. + +In addition to the options in the [OPTIONS](#OPTIONS) section, the following options are supported: + +* `-V`, `--value`=_VALUE_: + The value to store in the cluster. If omitted, the value is read from standard input. This + option is valid only for the `create` command. + +* `f`, `--flags`=_ITEMFLAGS_: + A 32 bit unsigned integer to be stored alongside the value. This number is returned + when the item is retrieved again. Other clients commonly use this value to determine + the type of item being stored. + +* `e`, `--expiry`=_EXPIRATION_: + The number of time in seconds from now at which the item should expire. + +* `M`, `--mode`=_upsert|insert|replace_: + Specify the storage mode. Mode can be one of `insert` (store item if it does + not yet exist), `replace` (only store item if key already exists), or + `upsert` (unconditionally store item) + +* `p`, `--persist-to`=_NUMNODES_: + Wait until the item has been persisted to at least `NUMNODES` nodes' disk. If + `NUMNODES` is 1 then wait until only the master node has persisted the item for + this key. You may not specify a number greater than the number of nodes actually + in the cluster. + +* `r` `--replicate-to`=_NREPLICAS_: + Wait until the item has been replicated to at least `NREPLICAS` replica nodes. + The bucket must be configured with at least one replica, and at least `NREPLICAS` + replica nodes must be online. + + +### observe + +Retrieve persistence and replication information for items. + +This command will print the status of each key to standard error. + +See the [OPTIONS](#OPTIONS) for accepted options + +### incr + +### decr + +These commands increment or decrement a _counter_ item in the cluster. A _counter_ +is a value stored as an ASCII string which is readable as a number, thus for example +`42`. + +These commands will by default refuse to operate on an item which does not exist in +the cluster. + +The `incr` and `decr` command differ with how they treat the `--delta` argument. The +`incr` command will treat the value as a _positive_ offset and increment the current +value by the amount specified, whereas the `decr` command will treat the value as a +_negative_ offset and decrement the value by the amount specified. + +In addition to [OPTIONS](#OPTIONS), the following options are supported: + +* `--initial=_DEFAULT_`: + Set the initial value for the item if it does not exist in the cluster. The value + should be an unsigned 64 bit integer. If this option is not specified and the item + does not exist, the operation will fail. If the item _does_ exist, this option is + ignored. + +* `--delta`=_DELTA_: + Set the absolute delta by which the value should change. If the command is `incr` + then the value will be _incremented_ by this amount. If the command is `decr` then + the value will be _decremented_ by this amount. The default value for this option is + `1`. + +* `-e`, `--expiry`=_EXPIRATION_: + Set the expiration time for the key, in terms of seconds from now. + + +### hash + +Display mapping information for a key. + +This command diplays mapping information about a key. The mapping information +indicates which _vBucket_ the key is mapped to, and which server is currently the +master node for the given _vBucket_. + +See the [OPTIONS](#OPTIONS) for accepted options + + +### lock + +Lock an item in the cluster. + +This will retrieve and lock an item in the cluster, making it inaccessible for +modification until it is unlocked (see [unlock](#unlock)). + +In addition to the common options ([OPTIONS](#OPTIONS)), this command accepts the following +options: + +* `e`, `--expiry`=_LOCKTIME_: + Specify the amount of time the lock should be held for. If not specified, it will + default to the server side maximum of 15 seconds. + + +### unlock + +Unlock a previously locked item. + +This command accepts two mandatory positional arguments which are the key and _CAS_ value. +The _CAS_ value should be specified as printed from the [lock][] command (i.e. with the +leading `0x` hexadecimal prefix). + +See the [OPTIONS](#OPTIONS) for accepted options + + +### rm + +Remove an item from the cluster. + +This command will remove an item from the cluster. If the item does not exist, the +operation will fail. + + +See the [OPTIONS](#OPTIONS) for accepted options + + +### stats + +Retrieve a list of cluster statistics. If positional arguments are passed to this +command, only the statistics classified under those keys will be retrieved. See the +server documentation for a full list of possible statistics categories. + +This command will contact each server in the cluster and retrieve that node's own set +of statistics. + +The statistics are printed to standard output in the form of `SERVER STATISTIC VALUE` +where _SERVER_ is the _host:port_ representation of the node from which has provided this +statistic, _STATISTIC_ is the name of the current statistical key, and _VALUE_ is the +value for this statistic. + + +See the [OPTIONS](#OPTIONS) for accepted options + +### version + +Display information about the underlying version of _libcouchbase_ to which the +`cbc` binary is linked. + +### verbosity + +Set the memcached logging versbosity on the cluster. This affects how the memcached +processes write their logs. This command accepts a single positional argument which +is a string describing the verbosity level to be set. The options are `detail`, `debug` +`info`, and `warning`. + +### mcflush + +Flush a _memcached_ bucket. This command takes no arguments, and will fail if the +bucket specified is not a memcached bucket. You may also use [bucket-flush](#bucket-flush) +to flush any bucket (including a couchbase bucket). The `mcflush` command may be +quicker for memcached buckets, though. + +### view + +Execute an HTTP request against the server's view (CAPI) interface. + +The request may be one to create a design document, view a design document, or query a +view. + +To create a design document, the definition of the document (in JSON) should be piped +to the command on standard input. + +This command accepts one positional argument which is the _path_ (relative to the +bucket) to execute. Thus to query the `brewery_beers` view in the `beer` design +document within the `beer-sample` bucket one would do: + cbc view -U couchbase://localhost/beer-sample _design/beer/_view/brewery_beers + +In addition to the [OPTIONS](#OPTIONS) specified above, the following options are recognized: + +* `-X`, `--method`=_GET|PUT|POST|DELETE_: + Specify the HTTP method to use for the specific request. The default method is `GET` + to query a view. To delete an existing design document, specify `DELETE`, and to + create a new design document, specify `PUT`. + +### n1ql + +Execute a N1QL Query. The cluster must have at least one query node enabled. + +The query itself is passed as a positional argument on the commandline. The +query may contain named placeholders (in the format of `$param`), whose values +may be supplied later on using the `--qarg='$param=value'` syntax. + +It is recommended to place the statement in single quotes to avoid shell +expansion. + +In addition to the [OPTIONS](#OPTIONS) specified above, the following options +are recognized: + +* `-Q`, `--qopt`=_SETTING=VALUE_: + Specify additional options controlling the execution of the query. This can + be used for example, to set the `scan_consistency` of the query. + +* `-A`, `--qarg`=_PLACEHOLDER=VALUE_: + Supply values for placeholders found in the query string. The placeholders + must evaluate to valid JSON values. + +### admin + +Execute an administrative request against the management REST API. +Note that in order to perform an administrative API you will need to provide +_administrative_ credentials to `cbc admin`. This means the username and password +used to log into the administration console. + +This command accepts a single positional argument which is the REST API endpoint +(i.e. HTTP path) to execute. + +If the request requires a _body_, it should be supplied via standard input + +In addition to the [OPTIONS](#OPTIONS) specified above, the following options are recognized: + +* `-X`, `--method`=_GET|PUT|POST|DELETE_: + Specify the HTTP method to use for the specific request. The default method is + `GET`. + +### bucket-create + +Create a bucket in the cluster. + +This command will create a bucket with the name specified as the lone positional +argument on the command line. + +As this is an administrative command, the `--username` and `--password` options should +be supplied administrative credentials. + +In addition to the [OPTIONS](#OPTIONS) specified above, the following options are recognized: + +* `--bucket-type`=_couchbase|memcached_: + Specify the type of bucket to create. A _couchbase_ bucket has persistence to disk and + replication. A _memached_ bucket is in-memory only and does not replicate. + +* `--ram-quota`=_QUOTA_: + Specify the maximum amount of memory the bucket should occupy (per node) in megabytes. + If not specified, the default is _512_. + +* `--bucket-password`=_PASSWORD_: + Specify the password to secure this bucket. If passed, this password will be required + by all clients attempting to connect to the bucket. If ommitted, this bucket may be + accessible to everyone for both read and write access. + +* `--num-replicas`=_REPLICAS_: + Specify the amount of replicas the bucket should have. This will set the number of nodes + each item will be replicated to. If not specified the default is _1_. + + +### bucket-flush + +This command will flush the bucket with the name specified as the lone positional +argument on the command line. + +This command does not require administrative level credentials, however it does +require that _flush_ be enabled for the bucket. + +See the [OPTIONS](#OPTIONS) for accepted options + +### connstr + +This command will parse a connection string into its constituent parts and +display them on the screen. The command takes a single positional argument +which is the string to parse. + +## EXAMPLES + + +### CONNECTION EXAMPLES + +The following shows how to connect to various types of buckets. These examples +all show how to retrieve the key `key`. See +[OPERATION EXAMPLES](#OPERATION EXAMPLES) for more information on specific +sub-commands. + + +Run against a bucket (`a_bucket`) on a cluster on a remote host: + + cbc cat key -U couchbase://192.168.33.101/a_bucket + +Connect to an SSL cluster at `secure.net`. The certificate for the cluster is +stored locally at `/home/couchbase/couchbase_cert.pem`: + + cbc cat key -U couchbases://secure.net/topsecret_bucket?certpath=/home/couchbase/couchbase_cert.pem + +Connect to an SSL cluster at `secure.net`, ignoring certificate verification. +This is insecure but handy for testing: + + cbc cat key -U couchbases://secure.net/topsecret_bucket?ssl=no_verify + +Connect to a password protected bucket (`protected`) on a remote host: + + cbc cat key -U couchbase://remote.host.net/protected -P- + Bucket password: + ... + +Connect to a password protected bucket, specifying the password on the +command line (INSECURE, but useful for testing dummy environments) + + cbc cat key -U couchbase://remote.host.net/protected -P t0ps3cr3t + +Connect to a bucket running on a cluster with a custom REST API port + + cbc cat key -U http://localhost:9000/default + +Connec to bucket running on a cluster with a custom memcached port + + cbc cat key -U couchbase://localhost:12000/default + +Connect to a *memcached* (http://memcached.org) +cluster using the binary protocol. A vanilla memcached cluster is not the same +as a memcached bucket residing within a couchbase cluster (use the normal +`couchbase://` scheme for that): + + cbc cat key -U memcached://host1,host2,host3,host4 + + +Connect to a cluster using the HTTP protocol for bootstrap, and set the +operation timeout to 5 seconds + + cbc cat key -U couchbase://host/bucket -Dbootstrap_on=http -Doperation_timeout=5 + + +### OPERATION EXAMPLES + +Store a file to the cluster: + + $ cbc cp mystuff.txt + mystuff.txt Stored. CAS=0xe15dbe22efc1e00 + + +Retrieve persistence/replication information about an item (note that _Status_ +is a set of bits): + + $ cbc observe mystuff.txt + mystuff [Master] Status=0x80, CAS=0x0 + + +Display mapping information about keys: + + $cbc hash foo bar baz + foo: [vBucket=115, Index=3] Server: cbnode3:11210, CouchAPI: http://cbnode3:8092/default + bar: [vBucket=767, Index=0] Server: cbnode1:11210, CouchAPI: http://cbnode1:8092/default + baz: [vBucket=36, Index=2] Server: cbnode2:11210, CouchAPI: http://cbnode2:8092/default + +Create a bucket: + + $ cbc bucket-create --bucket-type=memcached --ram-quota=100 --password=letmein -u Administrator -P 123456 mybucket + Requesting /pools/default/buckets + 202 + Cache-Control: no-cache + Content-Length: 0 + Date: Sun, 22 Jun 2014 22:43:56 GMT + Location: /pools/default/buckets/mybucket + Pragma: no-cache + Server: Couchbase Server + +Flush a bucket: + + $ cbc bucket-flush default + Requesting /pools/default/buckets/default/controller/doFlush + + + 200 + Cache-Control: no-cache + Content-Length: 0 + Date: Sun, 22 Jun 2014 22:53:44 GMT + Pragma: no-cache + Server: Couchbase Server + +Delete a bucket: + + $ cbc bucket-delete mybucket -P123456 + Requesting /pools/default/buckets/mybucket + 200 + Cache-Control: no-cache + Content-Length: 0 + Date: Sun, 22 Jun 2014 22:55:58 GMT + Pragma: no-cache + Server: Couchbase Server + +Use `cbc stats` to determine the minimum and maximum timeouts for a lock operation: + + $ cbc stats | grep ep_getl + localhost:11210 ep_getl_default_timeout 15 + localhost:11210 ep_getl_max_timeout 30 + + +Create a design document: + + $ echo '{"views":{"all":{"map":"function(doc,meta){emit(meta.id,null)}"}}}' | cbc view -X PUT _design/blog + 201 + Cache-Control: must-revalidate + Content-Length: 32 + Content-Type: application/json + Date: Sun, 22 Jun 2014 23:03:40 GMT + Location: http://localhost:8092/default/_design/blog + Server: MochiWeb/1.0 (Any of you quaids got a smint?) + {"ok":true,"id":"_design/blog"} + + +Query a view: + + $ cbc view _design/blog/_view/all?limit=5 + 200 + Cache-Control: must-revalidate + Content-Type: application/json + Date: Sun, 22 Jun 2014 23:06:09 GMT + Server: MochiWeb/1.0 (Any of you quaids got a smint?) + Transfer-Encoding: chunked + {"total_rows":20,"rows":[ + {"id":"bin","key":"bin","value":null}, + {"id":"check-all-libev-unit-tests.log","key":"check-all-libev-unit-tests.log","value":null}, + {"id":"check-all-libevent-unit-tests.log","key":"check-all-libevent-unit-tests.log","value":null}, + {"id":"check-all-select-unit-tests.log","key":"check-all-select-unit-tests.log","value":null}, + {"id":"cmake_install.cmake","key":"cmake_install.cmake","value":null} + ] + } + + +Issue a N1QL query: + + $ cbc n1ql 'SELECT * FROM `travel-sample` WHERE type="airport" AND city=$city' -Qscan_consistency=request_plus -A'$city=\"Reno\"' + +## FILES + +cbc(1) and cbc-pillowfight(1) may also read options from cbcrc(4). The default +path for `cbcrc` is `$HOME/.cbcrc`, but may be overridden by setting the +`CBC_CONFIG` evironment variable to an alternate path. + +## BUGS + +The options in this utility and their behavior are subject to change. This script +should be used for experiemntation only and not inside production scripts. + + +## SEE ALSO + +cbc-pillowfight(1), cbcrc(4) + + +## History + +The cbc command first appeared in version 0.3.0 of the library. It was significantly +rewritten in version 2.4.0 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/cbcrc.markdown b/couchbase-sys/libcouchbase-2.7.0/doc/cbcrc.markdown new file mode 100644 index 00000000..85d73c60 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/cbcrc.markdown @@ -0,0 +1,49 @@ +# cbcrc(4) - Configuration file for Couchbase command line tools + +## SYNOPSIS + +`~/.cbcrc` + +## DESCRIPTION + +cbcrc is an optional configuration file used to provide default values for the +cbc(1) and cbc-pillowfight(1) utilities. It should be placed in + +Each entry in the cbcrc file is a line with a key-value pair in the following +form: + + # optional comments + key=value + +The keys may be specified in random order, and if you specify the same +key multiple times the last value "wins". The following keys exists: + +* `connstr`: + This is the URI-like string used to connect to the cluster. Its format + consists of a _scheme_ followed by a list of hosts, an optional + bucket for the path and an optional `?` followed by key-value options. + + Using custom REST API ports + + http://localhost:9000,localhost:9001,localhost:9002 + + Using custom memcached ports: + + couchbase://localhost:9100,localhost:9200,localhost:9300 + +* `password`: + This is the password used during authentication to your bucket + +* `timeout`: + The timeout value to use for the operations. + +## NOTES + +* You can generate such a file from the cbc(1) itself using the `write-config` + subcommand + +* Most other options can be specified within the connection string + +## SEE ALSO + +cbc(1), cbc-pillowfight(1) diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/environment.h b/couchbase-sys/libcouchbase-2.7.0/doc/environment.h new file mode 100644 index 00000000..59fe3ecb --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/environment.h @@ -0,0 +1,93 @@ +/** + * @page lcb-env-vars-page Environment Variables + * + * @brief Environment variables controlling the behavior of the library + * + * @details + * While normally you would configure the library programmatically, there are + * some environment variables which may affect the library's behavior. + * + * Environment variables are intended to aid in debugging and otherwise modifying + * the library's behavior on a temporary basis. Typically this may be used to + * increase logging, or to modify/enable a feature which has deliberately been + * hidden from the normal API. + * + * + * @section lcb-env-vars Environment Variables + * + * @subsection LCB_IOPS_NAME + * Specify the name of the backend to load. This can be one of the built-in + * names, or it can be a path to a plugin which should be loaded dynamically. + * + * The built-in names are: + * + * * `libevent` + * * `libev` + * * `select` + * * `libuv` + * * `iocp` (Windows only) + * + * @committed + * + * + * @subsection LCB_IOPS_SYMBOL + * The symbol inside the shared object (specified as `LCB_IOPS_NAME`) that should + * contain the plugin initialization function. By default this is in the form of + * `libcouchbase_create_${NAME}_io_opts` + * + * @committed + * + * @subsection LCB_DLOPEN_DEBUG + * + * Print verbose information to the screen when attempting to log the plugins. + * This can help determine why a specific plugin is not being loaded + * + * @committed + * + * @subsection LCB_NO_HTTP + * + * Never bootstrap using the HTTP protocol. + * @volatile + * + * @subsection LCB_NO_CCCP + * + * Never bootstrap using the memcached protocol + * @volatile + * + * @subsection LCB_LOGLEVEL + * + * Enable the console logger and specify its verbosity level. The verbosity + * level is a number between 1-5 with higher numbers being more verbose + * + * @committed + * + * @subsection LCB_SSL_MODE + * + * Specify the _mode_ to use for SSL. Mode can either be `0` (for no SSL), + * `1` (for SSL with peer certificate verification), or `3` (for SSL + * with no certificate verification) + * + * @volatile + * + * @subsection LCB_SSL_CACERT + * + * Specify the path to the CA certificate to be used in order to validate + * the server's certificate + * + * @volatile + * + * @section LCB_INTERNAL_ENVVARS Internal Environment Variables + * + * @note + * This section will appear empty unless you have built internal documentation. + * + * @internal + * + * + * @subsection LCB_OPTIONS + * + * A string containing extra options for the connection string. These options + * are processed right after the existing connection string options (if any). + * + * @endinternal + */ diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/example/threads.c b/couchbase-sys/libcouchbase-2.7.0/doc/example/threads.c new file mode 100644 index 00000000..c6a4a44f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/example/threads.c @@ -0,0 +1,77 @@ +#include +#include +#include + +typedef struct { + lcb_t instance; + pthread_mutex_t mutex; +} my_CTX; + +/* + * This function uses the same instance between threads. A lock + * is required for every operation + */ +static void * +thrfunc_locked(void *arg) +{ + my_CTX *ctx = arg; + lcb_CMDGET cmd = { 0 }; + LCB_CMD_SET_KEY(&cmd, "Hello", strlen("Hello")); + + pthread_mutex_lock(&ctx->mutex); + lcb_get3(ctx->instance, NULL, &cmd); + lcb_wait(ctx->instance); + pthread_mutex_unlock(&ctx->mutex); + return NULL; +} + +/* + * This function uses an instance per thread. Since no other thread + * is using the instance, locking is not required + */ +static void * +thrfunc_unlocked(void *arg) +{ + lcb_t instance; + lcb_create(&instance, NULL); + lcb_connect(instance); + lcb_wait(instance); + lcb_get_cmd_t cmd = { 0 }, *cmdp = &cmd; + lcb_get(instance, NULL, 1, &cmdp); + lcb_wait(instance); + lcb_destroy(instance); + return NULL; +} + +int main(void) +{ + pthread_t thrs[10]; + my_CTX ctx; + int ii; + + lcb_create(&ctx.instance, NULL); + lcb_connect(ctx.instance); + lcb_wait(ctx.instance); + pthread_mutex_init(&ctx.mutex, NULL); + + for (ii = 0; ii < 10; ii++) { + pthread_create(&thrs[ii], NULL, thrfunc_locked, &ctx); + } + + for (ii = 0; ii < 10; ii++) { + void *ign; + pthread_join(thrs[ii], &ign); + } + + lcb_destroy(ctx.instance); + pthread_mutex_destroy(&ctx.mutex); + + for (ii = 0; ii < 10; ii++) { + pthread_create(&thrs[ii], NULL, thrfunc_unlocked, NULL); + } + for (ii = 0; ii < 10; ii++) { + void *ign; + pthread_join(thrs[ii], &ign); + } + return 0; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/footer.html b/couchbase-sys/libcouchbase-2.7.0/doc/footer.html new file mode 100644 index 00000000..87edfef3 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/footer.html @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/genman.sh b/couchbase-sys/libcouchbase-2.7.0/doc/genman.sh new file mode 100755 index 00000000..8bded04d --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/genman.sh @@ -0,0 +1,20 @@ +#!/bin/sh +set -e +set -x + +# Run from the top level source directory +OUTDIR=man +SRCDIR=. + +ronn --pipe --roff $SRCDIR/cbc.markdown > $OUTDIR/cbc.1 +ronn --pipe --roff $SRCDIR/cbc-pillowfight.markdown > $OUTDIR/cbc-pillowfight.1 +ronn --pipe --roff $SRCDIR/cbc-n1qlback.markdown > $OUTDIR/cbc-n1qlback.1 +ronn --pipe --roff $SRCDIR/cbcrc.markdown > $OUTDIR/cbcrc.4 + +MANLINKS="cat cp create observe flush hash lock unlock rm stats" +MANLINKS="$MANLINKS version verbosity view admin bucket-create bucket-delete connstr" + +for link in $MANLINKS; do + dest="$OUTDIR/cbc-${link}.1" + echo ".so man1/cbc.1" > "$dest" +done diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/header.html b/couchbase-sys/libcouchbase-2.7.0/doc/header.html new file mode 100644 index 00000000..6358b75f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/header.html @@ -0,0 +1,52 @@ + + + + + +$projectname: $title +$title + + + +$treeview +$search +$mathjax + + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + +
+
$projectname +  $projectnumber +
+
$projectbrief
+
+
$projectbrief
+
$searchbox
+
+ + diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/intro.h b/couchbase-sys/libcouchbase-2.7.0/doc/intro.h new file mode 100644 index 00000000..91c23845 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/intro.h @@ -0,0 +1,131 @@ +/** + * @page intro_sec Introduction + * + * libcouchbase is an asynchronous library for connecting to a Couchbase + * server and performing data operations. + * + * This contains the API documentation for the library. The documentation + * consists of both _internal_ and _public_ interfaces. + * + * Using the library is comprised of these steps: + * + * 1. Create an instance (See lcb_create()) + * 2. Install callbacks (See lcb_set_get_callback()) + * 3. Schedule an operation (e.g. lcb_get()) + * 4. Wait for the operation to complete (lcb_wait()) + * + * _libcouchbase_ is an asynchronous library which means that operation + * results are passed to callbacks you define rather than being returned from + * functions. + * + * Callbacks are passed a `cookie` parameter which is a user-defined pointer + * (i.e. your own pointer which can be `NULL`) to associate a specific command + * with a specific callback invocation. + * + * For simple synchronous use, you will need to call lcb_wait() after each + * set of scheduled operations. During lcb_wait() the library will block for + * I/O and invoke your callbacks as the results for the operations arrive. + * + * For non-synchronous use cases you can integrate with a variety of event loops + * via the various plugins, or integrate one yourself via the `IOPS` API + * (see @ref lcb-io-plugin-api) + * + * Modifying the library's settings (for example, timeout settings) may be done + * via the lcb_cntl() interface (see @ref lcb-cntl-settings) or via some environment + * variables (see @ref lcb-env-vars) + * + * + * + * ## Using the headers and libraries + * + * Using the libcouchbase headers is simple. Simply: + * @code{.c} + * #include + * @endcode + * + * into your application. + * + * To link, simply link against `libcouchbase` (e.g. `-lcouchbase`). + * + * + * See @ref lcb_attributes for interface stability taxonomy and @ref lcb_thrsafe + * for information on programming in threaded environments + * + * + * + * ## Minimal example usage + * @include example/minimal/minimal.c + * + * ## Configuring and Tuning the library + * + * The library may be configured either programmatically via lcb_cntl(), + * or via the environment (see @ref lcb-env-vars) + * + * + * ## Simple Usage Steps + * + * 1. Create an `lcb_t` handle. This is done via lcb_create() + * 2. Schedule the initial connection, this is done via lcb_connect() + * 3. Wait for the initial connection to complete, via lcb_wait() + * 4. Install the callbacks for retrieval and storage (lcb_set_get_callback(), + * lcb_set_stor_callback()). + * 5. Set up a command structure for storing an item, i.e. @ref lcb_store_cmd_t. + * 6. Schedule the operation via lcb_store(). You will also likely want to + * pass a `cookie` parameter along with it so that you can associate your + * application's structures via the callback. + * 7. Invoke lcb_wait(). Your callback will be invoked with the result of + * the storage operation. + * + * + * + * + * @internal + * ## Public vs Internal APIs + * + * The @ref lcb-public-api section is where you should begin browsing to develop + * with the library. Any sections not contained within the public API are + * internal and are provided to aid in developing new features and fixing bugs + * within the library itself. + * + * ## Internal Header Layouts + * + * The internal headers are organized like so: + * + * * - I/O Core + * * - Memcached packet codecs + * * - Write buffer implementation + * * - Read buffer implementation + * * - Memcached client I/O + * * - Memcached client initial SASL handling + * * - Couchbase cluster configuration retrieval + * * - Utility for response packets + * * - Retry queue for failed packets + * * - Other internal functions not in the above categories + * + * In addition to these files, there are several non-core files which exist + * to provide simple utilities which are not specific to the library: + * + * * - Double-linked list + * * , - Single linked list + * * - Hashtable + * * - Set of unique elements + * * - Host/Port structures and lists + * * - string structure with dynamically expanding storage + * + * @endinternal + * + * ## Prerequisite Knowledge + * + * libcouchbase is a cross platform C library used to interact with Couchbase + * Server. It is assumed that you know about: + * + * * Key-Value stores + * * The C language + * * Asynchronous and non-blocking programming. + * + * To develop with the I/O integration APIs, you will need to know about: + * + * * Socket APIs + * * Event loops + * + */ diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/mainpage.h b/couchbase-sys/libcouchbase-2.7.0/doc/mainpage.h new file mode 100644 index 00000000..73ecdd98 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/mainpage.h @@ -0,0 +1,98 @@ +/** + * + * @mainpage + * + * If you're new to this page, you may wish to read the @ref intro_sec first + * to get started. If you're coming back here for reference, here are some + * handy links to look at. + * + * * @subpage lcb-init + * * @subpage lcb-kv-api + * * @subpage lcb-cntl-settings + * + * You may read about related Couchbase software at http://docs.couchbase.com/ + * + * + * @section lcb_examples Examples + * + * * @ref example/minimal/minimal.c - Minimal example for connecting to a cluster + * and performing operations + * + * * @ref example/libeventdirect/main.c - Shows how to integrate with an external + * event library (libevent, in this case). + * + * + * Some more extensive examples may be observed in the SDKs wrapping libcouchbase + * to expose interfaces in their native languages. + * + * * Couchbase Python SDK (http://github.com/couchbase/couchbase-python-client). + * * Couchbase node.js SDK (http://github.com/couchbase/couchnode) + * * Couchbase Ruby SDK (http://github.com/couchbase/couchbase-ruby-client) + * + * + * @section lcb_jira Reporting Issues + * + * If you think you've found an issue, please file a bug on + * http://couchbase.com/issues. Select the _Couchbase C Client_ project. Before + * filing an issue, search for existing issues to determine if your issue has + * not yet been fixed in a newer version. + * + */ + +/** + * @example example/minimal/minimal.c + * Shows how to connect to the cluster and perform operations + * + * @example example/libeventdirect/main.c + * Shows how to integrate the library with an external event loop + */ + +/** + * @internal + * @defgroup lcb-public-api Public API + * @brief Public API Routines + * @details + * + * This covers the functions and structures of the library which are public + * interfaces. These consist of functions decorated with `LIBCOUCHBASE_API` + * and which are defined in the `libcouchbase` header directory. + */ + +/** + * @internal + * @defgroup lcb-generics Generic Types + * @brief Generic utilities and containers + * @addtogroup lcb-generics + * @{ + * @file src/simplestring.h + * @file src/list.h + * @file src/sllist.h + * @file src/sllist-inl.h + * @file src/hostlist.h + * @} + * + * + * @defgroup lcb-clconfig Bucket/Cluster Configuration + * @brief This module retrieves and processes cluster configurations from a + * variety of sources + * @addtogroup lcb-clconfig + * @{ + * @file src/bucketconfig/clconfig.h + * @} + */ + + +/** + * @page lcb_thrsafe Thread Safety + * + * The library uses no internal locking and is thus not safe to be used + * concurrently from multiple threads. As the library contains no globals + * you may call into the library from multiple threads so long as the same data + * structure (specifically, the same `lcb_t`) is not used. + * + * @include doc/example/threads.c + * + * In this quick mockup example, the same `lcb_t` is being used from multiple + * threads and thus requires locking. Now if each thread created its own `lcb_t` + * it would be free to operate upon it without locking. + */ diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-admin.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-admin.1 new file mode 100644 index 00000000..9001523b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-admin.1 @@ -0,0 +1 @@ +.so man1/cbc.1 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-bucket-create.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-bucket-create.1 new file mode 100644 index 00000000..9001523b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-bucket-create.1 @@ -0,0 +1 @@ +.so man1/cbc.1 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-bucket-delete.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-bucket-delete.1 new file mode 100644 index 00000000..9001523b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-bucket-delete.1 @@ -0,0 +1 @@ +.so man1/cbc.1 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-cat.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-cat.1 new file mode 100644 index 00000000..9001523b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-cat.1 @@ -0,0 +1 @@ +.so man1/cbc.1 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-connstr.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-connstr.1 new file mode 100644 index 00000000..9001523b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-connstr.1 @@ -0,0 +1 @@ +.so man1/cbc.1 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-cp.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-cp.1 new file mode 100644 index 00000000..9001523b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-cp.1 @@ -0,0 +1 @@ +.so man1/cbc.1 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-create.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-create.1 new file mode 100644 index 00000000..9001523b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-create.1 @@ -0,0 +1 @@ +.so man1/cbc.1 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-dsn.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-dsn.1 new file mode 100644 index 00000000..9001523b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-dsn.1 @@ -0,0 +1 @@ +.so man1/cbc.1 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-flush.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-flush.1 new file mode 100644 index 00000000..9001523b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-flush.1 @@ -0,0 +1 @@ +.so man1/cbc.1 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-hash.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-hash.1 new file mode 100644 index 00000000..9001523b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-hash.1 @@ -0,0 +1 @@ +.so man1/cbc.1 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-lock.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-lock.1 new file mode 100644 index 00000000..9001523b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-lock.1 @@ -0,0 +1 @@ +.so man1/cbc.1 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-n1qlback.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-n1qlback.1 new file mode 100644 index 00000000..d2c4124c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-n1qlback.1 @@ -0,0 +1,142 @@ +.\" generated with Ronn/v0.7.3 +.\" http://github.com/rtomayko/ronn/tree/0.7.3 +. +.TH "CBC\-N1QLBACK" "1" "July 2016" "" "" +. +.SH "NAME" +\fBcbc\-n1qlback\fR \- Stress Test for Couchbase Query (N1QL) +. +.SH "SYNOPSIS" +\fBcbc\-n1qlback\fR \-f QUERYFILE [\fIOPTIONS\fR] +. +.SH "DESCRIPTION" +\fBcbc\-n1qlback\fR creates a specified number of threads each executing a set of user defined queries\. +. +.P +\fBcbc\-n1qlback\fR requires that it be passed the path to a file containing the queries to execute; one per line\. The query should be in the format of the actual HTTP POST body (in JSON format) to be sent to the server\. For simple queries, only the \fBstatement\fR field needs to be set: +. +.IP "" 4 +. +.nf + +{"statement":"SELECT country FROM `travel\-sample`"} +{"statement":"SELECT country, COUNT(country) FROM `travel\-sample` GROUP BY country"} +. +.fi +. +.IP "" 0 +. +.P +For more complex queries (for example, placeholders, custom options), you may refer to the N1QL REST API reference\. +. +.P +\fBn1qlback\fR requires that any resources (data items, indexes) are already defined\. +. +.SH "OPTIONS" +The following options control workload generation: +. +.TP +\fB\-f\fR \fB\-\-queryfile\fR=\fIPATH\fR +Path to a file containing the query bodies to execute in JSON format, one query per line\. See above for the format\. +. +.TP +\fB\-t\fR, \fB\-\-num\-threads\fR=\fINTHREADS\fR +Set the number of threads (and thus the number of client instances) to run concurrently\. Each thread is assigned its own client object\. +. +.P +The following options control how \fBcbc\-n1qlback\fR connects to the cluster +. +.TP +\fB\-U\fR, \fB\-\-spec\fR=\fISPEC\fR +A string describing the cluster to connect to\. The string is in a URI\-like syntax, and may also contain other options\. See the \fIEXAMPLES\fR section for information\. Typically such a URI will look like \fBcouchbase://host1,host2,host3/bucket\fR\. +. +.IP +The default for this option is \fBcouchbase://localhost/default\fR +. +.TP +\fB\-u\fR, \fB\-\-username\fR=\fIUSERNAME\fR +Specify the \fIusername\fR for the bucket\. As of Couchbase Server 2\.5 this field should be either left empty or set to the name of the bucket itself\. +. +.TP +\fB\-P\fR, \fB\-\-password\fR=\fISASLPASS\fR: + +. +.TP +\fB\-P \-\fR, \fB\-\-password=\-\fR +Specify the SASL password for the bucket\. This is only needed if the bucket is protected with a password\. Note that this is \fInot\fR the administrative password used to log into the web interface\. +. +.IP +Specifying the \fB\-\fR as the password indicates that the program should prompt for the password\. You may also specify the password on the commandline, directly, but is insecure as command line arguments are visible via commands such as \fBps\fR\. +. +.TP +\fB\-T\fR, \fB\-\-timings\fR +Dump command timings at the end of execution\. This will display a histogram showing the latencies for the commands executed\. +. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Specify more information to standard error about what the client is doing\. You may specify this option multiple times for increased output detail\. +. +.TP +\fB\-D\fR, \fB\-\-cparam\fR=OPTION=VALUE +Provide additional client options\. Acceptable options can also be placed in the connection string, however this option is provided as a convenience\. This option may be specified multiple times, each time specifying a key=value pair (for example, \fB\-Doperation_timeout=10 \-Dconfig_cache=/foo/bar/baz\fR)\. See \fIADDITIONAL OPTIONS\fR for more information +. +.P + \fI\fR +. +.SH "ADDITIONAL OPTIONS" +The following options may be included in the connection string (via the \fB\-U\fR option) as URI\-style query params (e\.g\. \fBcouchbase://host/bucket?option1=value1&option2=value2\fR) or as individual key=value pairs passed to the \fB\-D\fR switch (e\.g\. \fB\-Doption1=value1 \-Doption2=value\fR)\. The \fB\-D\fR will internally build the connection string, and is provided as a convenience for options to be easily passed on the command\-line +. +.TP +\fBoperation_timeout=SECONDS\fR +Specify the operation timeout in seconds\. This is the time the client will wait for an operation to complete before timing it out\. The default is \fB2\.5\fR +. +.TP +\fBconfig_cache=PATH\fR +Enables the client to make use of a file based configuration cache rather than connecting for the bootstrap operation\. If the file does not exist, the client will first connect to the cluster and then cache the bootstrap information in the file\. +. +.TP +\fBcertpath=PATH\fR +The path to the server\'s SSL certificate\. This is typically required for SSL connectivity unless the certificate has already been added to the openssl installation on the system (only applicable with \fBcouchbases://\fR scheme) +. +.TP +\fBssl=no_verify\fR +Temporarily disable certificate verification for SSL (only applicable with \fBcouchbases://\fR scheme)\. This should only be used for quickly debugging SSL functionality\. +. +.TP +\fBsasl_mech_force=MECHANISM\fR +Force a specific \fISASL\fR mechanism to be used when performing the initial connection\. This should only need to be modified for debugging purposes\. The currently supported mechanisms are \fBPLAIN\fR and \fBCRAM\-MD5\fR +. +.TP +\fBbootstrap_on=\fR +Specify the bootstrap protocol the client should use when attempting to connect to the cluster\. Options are: \fBcccp\fR: Bootstrap using the Memcached protocol (supported on clusters 2\.5 and greater); \fBhttp\fR: Bootstrap using the HTTP REST protocol (supported on any cluster version); and \fBboth\fR: First attempt bootstrap over the Memcached protocol, and use the HTTP protocol if Memcached bootstrap fails\. The default is \fBboth\fR +. +.SH "EXAMPLES" +The following will create a file with 3 queries and 5 threads alternating between them\. It also creates indexes on the \fBtravel\-sample\fR bucket +. +.IP "" 4 +. +.nf + +cbc n1ql \-U couchbase://192\.168\.72\.101/a_bucket \'CREATE INDEX ix_name ON `travel\-sample`(name)\' +cbc n1ql \-U couchbase://192\.168\.72\.101/a_bucket \'CREATE INDEX ix_country ON `travel\-sample`(country)\' + +cat queries\.txt <\fR +Specify the bootstrap protocol the client should use when attempting to connect to the cluster\. Options are: \fBcccp\fR: Bootstrap using the Memcached protocol (supported on clusters 2\.5 and greater); \fBhttp\fR: Bootstrap using the HTTP REST protocol (supported on any cluster version); and \fBboth\fR: First attempt bootstrap over the Memcached protocol, and use the HTTP protocol if Memcached bootstrap fails\. The default is \fBboth\fR +. +.SH "EXAMPLES" +. +.SS "CONNECTION EXAMPLES" +The following examples show how to connect \fBpillowfight\fR to different types of cluster configurations\. +. +.P +Run against a bucket (\fBa_bucket\fR) on a cluster on a remote host: +. +.IP "" 4 +. +.nf + +cbc cat key \-U couchbase://192\.168\.33\.101/a_bucket +. +.fi +. +.IP "" 0 +. +.P +Connect to an SSL cluster at \fBsecure\.net\fR\. The certificate for the cluster is stored locally at \fB/home/couchbase/couchbase_cert\.pem\fR: +. +.IP "" 4 +. +.nf + +cbc cat key \-U couchbases://secure\.net/topsecret_bucket?certpath=/home/couchbase/couchbase_cert\.pem +. +.fi +. +.IP "" 0 +. +.P +Connect to an SSL cluster at \fBsecure\.net\fR, ignoring certificate verification\. This is insecure but handy for testing: +. +.IP "" 4 +. +.nf + +cbc cat key \-U couchbases://secure\.net/topsecret_bucket?ssl=no_verify +. +.fi +. +.IP "" 0 +. +.P +Connect to a password protected bucket (\fBprotected\fR) on a remote host: +. +.IP "" 4 +. +.nf + +cbc cat key \-U couchbase://remote\.host\.net/protected \-P \- +Bucket password: +\.\.\. +. +.fi +. +.IP "" 0 +. +.P +Connect to a password protected bucket, specifying the password on the command line (INSECURE, but useful for testing dummy environments) +. +.IP "" 4 +. +.nf + +cbc cat key \-U couchbase://remote\.host\.net/protected \-P t0ps3cr3t +. +.fi +. +.IP "" 0 +. +.P +Connect to a bucket running on a cluster with a custom REST API port +. +.IP "" 4 +. +.nf + +cbc cat key \-U http://localhost:9000/default +. +.fi +. +.IP "" 0 +. +.P +Connec to bucket running on a cluster with a custom memcached port +. +.IP "" 4 +. +.nf + +cbc cat key \-U couchbase://localhost:12000/default +. +.fi +. +.IP "" 0 +. +.P +Connect to a \fImemcached\fR (http://memcached\.org) cluster using the binary protocol\. A vanilla memcached cluster is not the same as a memcached bucket residing within a couchbase cluster (use the normal \fBcouchbase://\fR scheme for that): +. +.IP "" 4 +. +.nf + +cbc cat key \-U memcached://host1,host2,host3,host4 +. +.fi +. +.IP "" 0 +. +.P +Connect to an SSL cluster at \fBsecure\.net\fR: +. +.IP "" 4 +. +.nf + +cbc\-pillowfight \-U couchbases://secure\.net/topsecret_bucket +. +.fi +. +.IP "" 0 +. +.P +Run against a bucket (\fBa_bucket\fR) on a cluster on a remote host: +. +.IP "" 4 +. +.nf + +cbc\-pillowfight \-U couchbase://192\.168\.33\.101/a_bucket +. +.fi +. +.IP "" 0 +. +.SS "BENCHMARK EXAMPLES" +The following examples show how to configure different types of workloads with pillowfight\. +. +.P +Run with 20 threads/instances, each doing one operation at a time: +. +.IP "" 4 +. +.nf + +cbc\-pillowfight \-t 20 \-B 1 +. +.fi +. +.IP "" 0 +. +.P +Run 100 iterations of 2MB item sizes, using a dataset of 50 items +. +.IP "" 4 +. +.nf + +cbc\-pillowfight \-M $(1024*1024) \-m $(1024*1024) \-c 100 \-I 50 +. +.fi +. +.IP "" 0 +. +.P +Use JSON documents of 100k each +. +.IP "" 4 +. +.nf + +cbc\-pillowfight \-\-json \-m 100000 \-M 100000 +. +.fi +. +.IP "" 0 +. +.P +Stress\-test sub\-document mutations +. +.IP "" 4 +. +.nf + +cbc\-pillowfight \-\-json \-\-subdoc \-\-set\-pct 100 +. +.fi +. +.IP "" 0 +. +.SH "TODO" +Rather than spawning threads for multiple instances, offer a way to have multiple instances function cooperatively inside an event loop\. +. +.SH "BUGS" +This command\'s options are subject to change\. +. +.SH "SEE ALSO" +cbc(1), cbcrc(4) +. +.SH "HISTORY" +The \fBcbc\-pillowfight\fR tool was first introduced in libcouchbase 2\.0\.7 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-rm.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-rm.1 new file mode 100644 index 00000000..9001523b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-rm.1 @@ -0,0 +1 @@ +.so man1/cbc.1 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-stats.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-stats.1 new file mode 100644 index 00000000..9001523b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-stats.1 @@ -0,0 +1 @@ +.so man1/cbc.1 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-unlock.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-unlock.1 new file mode 100644 index 00000000..9001523b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-unlock.1 @@ -0,0 +1 @@ +.so man1/cbc.1 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-verbosity.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-verbosity.1 new file mode 100644 index 00000000..9001523b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-verbosity.1 @@ -0,0 +1 @@ +.so man1/cbc.1 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-version.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-version.1 new file mode 100644 index 00000000..9001523b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-version.1 @@ -0,0 +1 @@ +.so man1/cbc.1 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-view.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-view.1 new file mode 100644 index 00000000..9001523b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc-view.1 @@ -0,0 +1 @@ +.so man1/cbc.1 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc.1 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc.1 new file mode 100644 index 00000000..acb8abe7 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbc.1 @@ -0,0 +1,725 @@ +.\" generated with Ronn/v0.7.3 +.\" http://github.com/rtomayko/ronn/tree/0.7.3 +. +.TH "CBC" "1" "July 2015" "" "" +. +.SH "NAME" +\fBcbc\fR \- Couchbase Client Commandline Utility +. +.SH "SYNOPSIS" +\fBcbc\fR \fICOMMAND\fR [\fIOPTIONS\fR] +. +.br +\fBcbc help\fR +. +.br +\fBcbc version\fR +. +.br +\fBcbc cat\fR \fIKEYS\fR \.\.\. [\fIOPTIONS\fR] +. +.br +\fBcbc create\fR \fIKEY\fR \fI\-V VALUE\fR [\fIOPTIONS\fR] +. +.br +\fBcbc create\fR \fIKEY\fR [\fIOPTIONS\fR] +. +.br +\fBcbc cp\fR \fIFILES\fR \.\.\. [\fIOPTIONS\fR] +. +.br +\fBcbc incr\fR \fIKEY\fR [\fIOPTIONS\fR] +. +.br +\fBcbc decr\fR \fIKEY\fR [\fIOPTIONS\fR] +. +.br +\fBcbc rm\fR \fIKEY\fR [\fIOPTIONS\fR] +. +.br +\fBcbc hash\fR \fIKEY\fR [\fIOPTIONS\fR] +. +.br +\fBcbc stats\fR \fIKEYS\fR \.\.\. [\fIOPTIONS\fR] +. +.br +\fBcbc observe\fR \fIKEYS\fR \.\.\. [\fIOPTIONS\fR] +. +.br +\fBcbc view\fR \fIVIEWPATH\fR [\fIOPTIONS\fR] +. +.br +\fBcbc lock\fR \fIKEY\fR [\fIOPTIONS\fR] +. +.br +\fBcbc unlock\fR \fIKEY\fR \fICAS\fR [\fIOPTIONS\fR] +. +.br +\fBcbc admin\fR \fI\-P PASSWORD\fR \fIRESTAPI\fR [\fIOPTIONS\fR] +. +.br +\fBcbc bucket\-create\fR \fI\-P PASSWORD\fR \fINAME\fR [\fIOPTIONS\fR] +. +.br +\fBcbc bucket\-delete\fR \fI\-P PASSWORD\fR \fINAME\fR [\fIOPTIONS\fR] +. +.br +\fBcbc bucket\-flush\fR \fINAME\fR [\fIOPTIONS\fR] +. +.br +\fBcbc connstr\fR \fISPEC\fR +. +.br +\fBcbc n1ql\fR \fIQUERY\fR \.\.\. [\fIOPTIONS\fR] +. +.br +. +.SH "DESCRIPTION" +\fBcbc\fR is a utility for communicating with a Couchbase cluster\. +. +.P +\fBcbc\fR should be invoked with the command name first and then a series of command options appropriate for the specific command\. \fBcbc help\fR will always show the full list of available commands\. +. +.P + \fI\fR +. +.SH "OPTIONS" +Options may be read either from the command line, or from a configuration file (see cbcrc(4)): +. +.P +The following common options may be applied to most of the commands +. +.TP +\fB\-U\fR, \fB\-\-spec\fR=\fISPEC\fR +A string describing the cluster to connect to\. The string is in a URI\-like syntax, and may also contain other options\. See the \fIEXAMPLES\fR section for information\. Typically such a URI will look like \fBcouchbase://host1,host2,host3/bucket\fR\. +. +.IP +The default for this option is \fBcouchbase://localhost/default\fR +. +.TP +\fB\-u\fR, \fB\-\-username\fR=\fIUSERNAME\fR +Specify the \fIusername\fR for the bucket\. As of Couchbase Server 2\.5 this field should be either left empty or set to the name of the bucket itself\. +. +.TP +\fB\-P\fR, \fB\-\-password\fR=\fISASLPASS\fR: + +. +.TP +\fB\-P \-\fR, \fB\-\-password=\-\fR +Specify the SASL password for the bucket\. This is only needed if the bucket is protected with a password\. Note that this is \fInot\fR the administrative password used to log into the web interface\. +. +.IP +Specifying the \fB\-\fR as the password indicates that the program should prompt for the password\. You may also specify the password on the commandline, directly, but is insecure as command line arguments are visible via commands such as \fBps\fR\. +. +.TP +\fB\-T\fR, \fB\-\-timings\fR +Dump command timings at the end of execution\. This will display a histogram showing the latencies for the commands executed\. +. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Specify more information to standard error about what the client is doing\. You may specify this option multiple times for increased output detail\. +. +.TP +\fB\-D\fR, \fB\-\-cparam\fR=OPTION=VALUE +Provide additional client options\. Acceptable options can also be placed in the connection string, however this option is provided as a convenience\. This option may be specified multiple times, each time specifying a key=value pair (for example, \fB\-Doperation_timeout=10 \-Dconfig_cache=/foo/bar/baz\fR)\. See \fIADDITIONAL OPTIONS\fR for more information +. +.P + \fI\fR +. +.SH "ADDITIONAL OPTIONS" +The following options may be included in the connection string (via the \fB\-U\fR option) as URI\-style query params (e\.g\. \fBcouchbase://host/bucket?option1=value1&option2=value2\fR) or as individual key=value pairs passed to the \fB\-D\fR switch (e\.g\. \fB\-Doption1=value1 \-Doption2=value\fR)\. The \fB\-D\fR will internally build the connection string, and is provided as a convenience for options to be easily passed on the command\-line +. +.TP +\fBoperation_timeout=SECONDS\fR +Specify the operation timeout in seconds\. This is the time the client will wait for an operation to complete before timing it out\. The default is \fB2\.5\fR +. +.TP +\fBconfig_cache=PATH\fR +Enables the client to make use of a file based configuration cache rather than connecting for the bootstrap operation\. If the file does not exist, the client will first connect to the cluster and then cache the bootstrap information in the file\. +. +.TP +\fBcertpath=PATH\fR +The path to the server\'s SSL certificate\. This is typically required for SSL connectivity unless the certificate has already been added to the openssl installation on the system (only applicable with \fBcouchbases://\fR scheme) +. +.TP +\fBssl=no_verify\fR +Temporarily disable certificate verification for SSL (only applicable with \fBcouchbases://\fR scheme)\. This should only be used for quickly debugging SSL functionality\. +. +.TP +\fBsasl_mech_force=MECHANISM\fR +Force a specific \fISASL\fR mechanism to be used when performing the initial connection\. This should only need to be modified for debugging purposes\. The currently supported mechanisms are \fBPLAIN\fR and \fBCRAM\-MD5\fR +. +.TP +\fBbootstrap_on=\fR +Specify the bootstrap protocol the client should use when attempting to connect to the cluster\. Options are: \fBcccp\fR: Bootstrap using the Memcached protocol (supported on clusters 2\.5 and greater); \fBhttp\fR: Bootstrap using the HTTP REST protocol (supported on any cluster version); and \fBboth\fR: First attempt bootstrap over the Memcached protocol, and use the HTTP protocol if Memcached bootstrap fails\. The default is \fBboth\fR +. +.SH "COMMANDS" +The following commands are supported by \fBcbc\fR\. Unless otherwise specified, each command supports all of the options above\. +. +.SS "cat" +Write the value of keys to standard output\. +. +.P +This command requires that at least one key may be passed to it, but may accept multiple keys\. The keys should be specified as positional arguments after the command\. +. +.P +In addition to the options in the \fIOPTIONS\fR section, the following options are supported: +. +.TP +\fBr\fR, \fB\-\-replica\fR=\fIall|INDEX\fR +Read the value from a replica server\. The value for this option can either be the string \fBall\fR which will cause the client to request the value from each replica, or \fBINDEX\fR where \fBINDEX\fR is a 0\-based replica index\. +. +.TP +\fBe\fR, \fB\-\-expiry\fR=\fIEXPIRATION\fR +Specify that this operation should be a \fIget\-and\-touch\fR operation in which the key\'s expiry time is updated along with retrieving the item\. +. +.SS "create" +. +.SS "cp" +Create a new item in the cluster, or update the value of an existing item\. By default this command will read the value from standard input unless the \fB\-\-value\fR option is specified\. +. +.P +The \fBcp\fR command functions the same, except it operates on a list of files\. Each file is stored in the cluster under the name specified on the command line\. +. +.P +In addition to the options in the \fIOPTIONS\fR section, the following options are supported: +. +.TP +\fB\-V\fR, \fB\-\-value\fR=\fIVALUE\fR +The value to store in the cluster\. If omitted, the value is read from standard input\. This option is valid only for the \fBcreate\fR command\. +. +.TP +\fBf\fR, \fB\-\-flags\fR=\fIITEMFLAGS\fR +A 32 bit unsigned integer to be stored alongside the value\. This number is returned when the item is retrieved again\. Other clients commonly use this value to determine the type of item being stored\. +. +.TP +\fBe\fR, \fB\-\-expiry\fR=\fIEXPIRATION\fR +The number of time in seconds from now at which the item should expire\. +. +.TP +\fBM\fR, \fB\-\-mode\fR=\fIupsert|insert|replace\fR +Specify the storage mode\. Mode can be one of \fBinsert\fR (store item if it does not yet exist), \fBreplace\fR (only store item if key already exists), or \fBupsert\fR (unconditionally store item) +. +.TP +\fBp\fR, \fB\-\-persist\-to\fR=\fINUMNODES\fR +Wait until the item has been persisted to at least \fBNUMNODES\fR nodes\' disk\. If \fBNUMNODES\fR is 1 then wait until only the master node has persisted the item for this key\. You may not specify a number greater than the number of nodes actually in the cluster\. +. +.TP +\fBr\fR \fB\-\-replicate\-to\fR=\fINREPLICAS\fR +Wait until the item has been replicated to at least \fBNREPLICAS\fR replica nodes\. The bucket must be configured with at least one replica, and at least \fBNREPLICAS\fR replica nodes must be online\. +. +.SS "observe" +Retrieve persistence and replication information for items\. +. +.P +This command will print the status of each key to standard error\. +. +.P +See the \fIOPTIONS\fR for accepted options +. +.SS "incr" +. +.SS "decr" +These commands increment or decrement a \fIcounter\fR item in the cluster\. A \fIcounter\fR is a value stored as an ASCII string which is readable as a number, thus for example \fB42\fR\. +. +.P +These commands will by default refuse to operate on an item which does not exist in the cluster\. +. +.P +The \fBincr\fR and \fBdecr\fR command differ with how they treat the \fB\-\-delta\fR argument\. The \fBincr\fR command will treat the value as a \fIpositive\fR offset and increment the current value by the amount specified, whereas the \fBdecr\fR command will treat the value as a \fInegative\fR offset and decrement the value by the amount specified\. +. +.P +In addition to \fIOPTIONS\fR, the following options are supported: +. +.TP +\fB\-\-initial=_DEFAULT_\fR +Set the initial value for the item if it does not exist in the cluster\. The value should be an unsigned 64 bit integer\. If this option is not specified and the item does not exist, the operation will fail\. If the item \fIdoes\fR exist, this option is ignored\. +. +.TP +\fB\-\-delta\fR=\fIDELTA\fR +Set the absolute delta by which the value should change\. If the command is \fBincr\fR then the value will be \fIincremented\fR by this amount\. If the command is \fBdecr\fR then the value will be \fIdecremented\fR by this amount\. The default value for this option is \fB1\fR\. +. +.TP +\fB\-e\fR, \fB\-\-expiry\fR=\fIEXPIRATION\fR +Set the expiration time for the key, in terms of seconds from now\. +. +.SS "hash" +Display mapping information for a key\. +. +.P +This command diplays mapping information about a key\. The mapping information indicates which \fIvBucket\fR the key is mapped to, and which server is currently the master node for the given \fIvBucket\fR\. +. +.P +See the \fIOPTIONS\fR for accepted options +. +.P + \fI\fR +. +.SS "lock" +Lock an item in the cluster\. +. +.P +This will retrieve and lock an item in the cluster, making it inaccessible for modification until it is unlocked (see \fIunlock\fR)\. +. +.P +In addition to the common options (\fIOPTIONS\fR), this command accepts the following options: +. +.TP +\fBe\fR, \fB\-\-expiry\fR=\fILOCKTIME\fR +Specify the amount of time the lock should be held for\. If not specified, it will default to the server side maximum of 15 seconds\. +. +.P + \fI\fR +. +.SS "unlock" +Unlock a previously locked item\. +. +.P +This command accepts two mandatory positional arguments which are the key and \fICAS\fR value\. The \fICAS\fR value should be specified as printed from the \fIlock\fR command (i\.e\. with the leading \fB0x\fR hexadecimal prefix)\. +. +.P +See the \fIOPTIONS\fR for accepted options +. +.SS "rm" +Remove an item from the cluster\. +. +.P +This command will remove an item from the cluster\. If the item does not exist, the operation will fail\. +. +.P +See the \fIOPTIONS\fR for accepted options +. +.SS "stats" +Retrieve a list of cluster statistics\. If positional arguments are passed to this command, only the statistics classified under those keys will be retrieved\. See the server documentation for a full list of possible statistics categories\. +. +.P +This command will contact each server in the cluster and retrieve that node\'s own set of statistics\. +. +.P +The statistics are printed to standard output in the form of \fBSERVER STATISTIC VALUE\fR where \fISERVER\fR is the \fIhost:port\fR representation of the node from which has provided this statistic, \fISTATISTIC\fR is the name of the current statistical key, and \fIVALUE\fR is the value for this statistic\. +. +.P +See the \fIOPTIONS\fR for accepted options +. +.SS "version" +Display information about the underlying version of \fIlibcouchbase\fR to which the \fBcbc\fR binary is linked\. +. +.SS "verbosity" +Set the memcached logging versbosity on the cluster\. This affects how the memcached processes write their logs\. This command accepts a single positional argument which is a string describing the verbosity level to be set\. The options are \fBdetail\fR, \fBdebug\fR \fBinfo\fR, and \fBwarning\fR\. +. +.SS "mcflush" +Flush a \fImemcached\fR bucket\. This command takes no arguments, and will fail if the bucket specified is not a memcached bucket\. You may also use \fIbucket\-flush\fR to flush any bucket (including a couchbase bucket)\. The \fBmcflush\fR command may be quicker for memcached buckets, though\. +. +.SS "view" +Execute an HTTP request against the server\'s view (CAPI) interface\. +. +.P +The request may be one to create a design document, view a design document, or query a view\. +. +.P +To create a design document, the definition of the document (in JSON) should be piped to the command on standard input\. +. +.P +This command accepts one positional argument which is the \fIpath\fR (relative to the bucket) to execute\. Thus to query the \fBbrewery_beers\fR view in the \fBbeer\fR design document within the \fBbeer\-sample\fR bucket one would do: cbc view \-U couchbase://localhost/beer\-sample \fIdesign/beer/\fRview/brewery_beers +. +.P +In addition to the \fIOPTIONS\fR specified above, the following options are recognized: +. +.TP +\fB\-X\fR, \fB\-\-method\fR=\fIGET|PUT|POST|DELETE\fR +Specify the HTTP method to use for the specific request\. The default method is \fBGET\fR to query a view\. To delete an existing design document, specify \fBDELETE\fR, and to create a new design document, specify \fBPUT\fR\. +. +.SS "n1ql" +Execute a N1QL Query\. The cluster must have at least one query node enabled\. +. +.P +The query itself is passed as a positional argument on the commandline\. The query may contain named placeholders (in the format of \fB$param\fR), whose values may be supplied later on using the \fB\-\-qarg=\'$param=value\'\fR syntax\. +. +.P +It is recommended to place the statement in single quotes to avoid shell expansion\. +. +.P +In addition to the \fIOPTIONS\fR specified above, the following options are recognized: +. +.TP +\fB\-Q\fR, \fB\-\-qopt\fR=\fISETTING=VALUE\fR +Specify additional options controlling the execution of the query\. This can be used for example, to set the \fBscan_consistency\fR of the query\. +. +.TP +\fB\-A\fR, \fB\-\-qarg\fR=\fIPLACEHOLDER=VALUE\fR +Supply values for placeholders found in the query string\. The placeholders must evaluate to valid JSON values\. +. +.SS "admin" +Execute an administrative request against the management REST API\. Note that in order to perform an administrative API you will need to provide \fIadministrative\fR credentials to \fBcbc admin\fR\. This means the username and password used to log into the administration console\. +. +.P +This command accepts a single positional argument which is the REST API endpoint (i\.e\. HTTP path) to execute\. +. +.P +If the request requires a \fIbody\fR, it should be supplied via standard input +. +.P +In addition to the \fIOPTIONS\fR specified above, the following options are recognized: +. +.TP +\fB\-X\fR, \fB\-\-method\fR=\fIGET|PUT|POST|DELETE\fR +Specify the HTTP method to use for the specific request\. The default method is \fBGET\fR\. +. +.SS "bucket\-create" +Create a bucket in the cluster\. +. +.P +This command will create a bucket with the name specified as the lone positional argument on the command line\. +. +.P +As this is an administrative command, the \fB\-\-username\fR and \fB\-\-password\fR options should be supplied administrative credentials\. +. +.P +In addition to the \fIOPTIONS\fR specified above, the following options are recognized: +. +.TP +\fB\-\-bucket\-type\fR=\fIcouchbase|memcached\fR +Specify the type of bucket to create\. A \fIcouchbase\fR bucket has persistence to disk and replication\. A \fImemached\fR bucket is in\-memory only and does not replicate\. +. +.TP +\fB\-\-ram\-quota\fR=\fIQUOTA\fR +Specify the maximum amount of memory the bucket should occupy (per node) in megabytes\. If not specified, the default is \fI512\fR\. +. +.TP +\fB\-\-bucket\-password\fR=\fIPASSWORD\fR +Specify the password to secure this bucket\. If passed, this password will be required by all clients attempting to connect to the bucket\. If ommitted, this bucket may be accessible to everyone for both read and write access\. +. +.TP +\fB\-\-num\-replicas\fR=\fIREPLICAS\fR +Specify the amount of replicas the bucket should have\. This will set the number of nodes each item will be replicated to\. If not specified the default is \fI1\fR\. +. +.SS "bucket\-flush" +This command will flush the bucket with the name specified as the lone positional argument on the command line\. +. +.P +This command does not require administrative level credentials, however it does require that \fIflush\fR be enabled for the bucket\. +. +.P +See the \fIOPTIONS\fR for accepted options +. +.SS "connstr" +This command will parse a connection string into its constituent parts and display them on the screen\. The command takes a single positional argument which is the string to parse\. +. +.SH "EXAMPLES" +. +.SS "CONNECTION EXAMPLES" +The following shows how to connect to various types of buckets\. These examples all show how to retrieve the key \fBkey\fR\. See \fIOPERATION EXAMPLES\fR for more information on specific sub\-commands\. +. +.P +Run against a bucket (\fBa_bucket\fR) on a cluster on a remote host: +. +.IP "" 4 +. +.nf + +cbc cat key \-U couchbase://192\.168\.33\.101/a_bucket +. +.fi +. +.IP "" 0 +. +.P +Connect to an SSL cluster at \fBsecure\.net\fR\. The certificate for the cluster is stored locally at \fB/home/couchbase/couchbase_cert\.pem\fR: +. +.IP "" 4 +. +.nf + +cbc cat key \-U couchbases://secure\.net/topsecret_bucket?certpath=/home/couchbase/couchbase_cert\.pem +. +.fi +. +.IP "" 0 +. +.P +Connect to an SSL cluster at \fBsecure\.net\fR, ignoring certificate verification\. This is insecure but handy for testing: +. +.IP "" 4 +. +.nf + +cbc cat key \-U couchbases://secure\.net/topsecret_bucket?ssl=no_verify +. +.fi +. +.IP "" 0 +. +.P +Connect to a password protected bucket (\fBprotected\fR) on a remote host: +. +.IP "" 4 +. +.nf + +cbc cat key \-U couchbase://remote\.host\.net/protected \-P\- +Bucket password: +\.\.\. +. +.fi +. +.IP "" 0 +. +.P +Connect to a password protected bucket, specifying the password on the command line (INSECURE, but useful for testing dummy environments) +. +.IP "" 4 +. +.nf + +cbc cat key \-U couchbase://remote\.host\.net/protected \-P t0ps3cr3t +. +.fi +. +.IP "" 0 +. +.P +Connect to a bucket running on a cluster with a custom REST API port +. +.IP "" 4 +. +.nf + +cbc cat key \-U http://localhost:9000/default +. +.fi +. +.IP "" 0 +. +.P +Connec to bucket running on a cluster with a custom memcached port +. +.IP "" 4 +. +.nf + +cbc cat key \-U couchbase://localhost:12000/default +. +.fi +. +.IP "" 0 +. +.P +Connect to a \fImemcached\fR (http://memcached\.org) cluster using the binary protocol\. A vanilla memcached cluster is not the same as a memcached bucket residing within a couchbase cluster (use the normal \fBcouchbase://\fR scheme for that): +. +.IP "" 4 +. +.nf + +cbc cat key \-U memcached://host1,host2,host3,host4 +. +.fi +. +.IP "" 0 +. +.P +Connect to a cluster using the HTTP protocol for bootstrap, and set the operation timeout to 5 seconds +. +.IP "" 4 +. +.nf + +cbc cat key \-U couchbase://host/bucket \-Dbootstrap_on=http \-Doperation_timeout=5 +. +.fi +. +.IP "" 0 +. +.SS "OPERATION EXAMPLES" +Store a file to the cluster: +. +.IP "" 4 +. +.nf + +$ cbc cp mystuff\.txt +mystuff\.txt Stored\. CAS=0xe15dbe22efc1e00 +. +.fi +. +.IP "" 0 +. +.P +Retrieve persistence/replication information about an item (note that \fIStatus\fR is a set of bits): +. +.IP "" 4 +. +.nf + +$ cbc observe mystuff\.txt +mystuff [Master] Status=0x80, CAS=0x0 +. +.fi +. +.IP "" 0 +. +.P +Display mapping information about keys: +. +.IP "" 4 +. +.nf + +$cbc hash foo bar baz +foo: [vBucket=115, Index=3] Server: cbnode3:11210, CouchAPI: http://cbnode3:8092/default +bar: [vBucket=767, Index=0] Server: cbnode1:11210, CouchAPI: http://cbnode1:8092/default +baz: [vBucket=36, Index=2] Server: cbnode2:11210, CouchAPI: http://cbnode2:8092/default +. +.fi +. +.IP "" 0 +. +.P +Create a bucket: +. +.IP "" 4 +. +.nf + +$ cbc bucket\-create \-\-bucket\-type=memcached \-\-ram\-quota=100 \-\-password=letmein \-u Administrator \-P 123456 mybucket +Requesting /pools/default/buckets +202 + Cache\-Control: no\-cache + Content\-Length: 0 + Date: Sun, 22 Jun 2014 22:43:56 GMT + Location: /pools/default/buckets/mybucket + Pragma: no\-cache + Server: Couchbase Server +. +.fi +. +.IP "" 0 +. +.P +Flush a bucket: +. +.IP "" 4 +. +.nf + +$ cbc bucket\-flush default +Requesting /pools/default/buckets/default/controller/doFlush + + +200 + Cache\-Control: no\-cache + Content\-Length: 0 + Date: Sun, 22 Jun 2014 22:53:44 GMT + Pragma: no\-cache + Server: Couchbase Server +. +.fi +. +.IP "" 0 +. +.P +Delete a bucket: +. +.IP "" 4 +. +.nf + +$ cbc bucket\-delete mybucket \-P123456 +Requesting /pools/default/buckets/mybucket +200 + Cache\-Control: no\-cache + Content\-Length: 0 + Date: Sun, 22 Jun 2014 22:55:58 GMT + Pragma: no\-cache + Server: Couchbase Server +. +.fi +. +.IP "" 0 +. +.P +Use \fBcbc stats\fR to determine the minimum and maximum timeouts for a lock operation: +. +.IP "" 4 +. +.nf + +$ cbc stats | grep ep_getl +localhost:11210 ep_getl_default_timeout 15 +localhost:11210 ep_getl_max_timeout 30 +. +.fi +. +.IP "" 0 +. +.P +Create a design document: +. +.IP "" 4 +. +.nf + +$ echo \'{"views":{"all":{"map":"function(doc,meta){emit(meta\.id,null)}"}}}\' | cbc view \-X PUT _design/blog +201 + Cache\-Control: must\-revalidate + Content\-Length: 32 + Content\-Type: application/json + Date: Sun, 22 Jun 2014 23:03:40 GMT + Location: http://localhost:8092/default/_design/blog + Server: MochiWeb/1\.0 (Any of you quaids got a smint?) +{"ok":true,"id":"_design/blog"} +. +.fi +. +.IP "" 0 +. +.P +Query a view: +. +.IP "" 4 +. +.nf + +$ cbc view _design/blog/_view/all?limit=5 +200 + Cache\-Control: must\-revalidate + Content\-Type: application/json + Date: Sun, 22 Jun 2014 23:06:09 GMT + Server: MochiWeb/1\.0 (Any of you quaids got a smint?) + Transfer\-Encoding: chunked +{"total_rows":20,"rows":[ +{"id":"bin","key":"bin","value":null}, +{"id":"check\-all\-libev\-unit\-tests\.log","key":"check\-all\-libev\-unit\-tests\.log","value":null}, +{"id":"check\-all\-libevent\-unit\-tests\.log","key":"check\-all\-libevent\-unit\-tests\.log","value":null}, +{"id":"check\-all\-select\-unit\-tests\.log","key":"check\-all\-select\-unit\-tests\.log","value":null}, +{"id":"cmake_install\.cmake","key":"cmake_install\.cmake","value":null} +] +} +. +.fi +. +.IP "" 0 +. +.P +Issue a N1QL query: +. +.IP "" 4 +. +.nf + +$ cbc n1ql \'SELECT * FROM `travel\-sample` WHERE type="airport" AND city=$city\' \-Qscan_consistency=request_plus \-A\'$city=\e"Reno\e"\' +. +.fi +. +.IP "" 0 +. +.SH "FILES" +cbc(1) and cbc\-pillowfight(1) may also read options from cbcrc(4)\. The default path for \fBcbcrc\fR is \fB$HOME/\.cbcrc\fR, but may be overridden by setting the \fBCBC_CONFIG\fR evironment variable to an alternate path\. +. +.SH "BUGS" +The options in this utility and their behavior are subject to change\. This script should be used for experiemntation only and not inside production scripts\. +. +.SH "SEE ALSO" +cbc\-pillowfight(1), cbcrc(4) +. +.SH "History" +The cbc command first appeared in version 0\.3\.0 of the library\. It was significantly rewritten in version 2\.4\.0 diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/man/cbcrc.4 b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbcrc.4 new file mode 100644 index 00000000..f8b388a2 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/man/cbcrc.4 @@ -0,0 +1,67 @@ +.\" generated with Ronn/v0.7.3 +.\" http://github.com/rtomayko/ronn/tree/0.7.3 +. +.TH "CBCRC" "4" "November 2014" "" "" +. +.SH "NAME" +\fBcbcrc\fR \- Configuration file for Couchbase command line tools +. +.SH "SYNOPSIS" +\fB~/\.cbcrc\fR +. +.SH "DESCRIPTION" +cbcrc is an optional configuration file used to provide default values for the cbc(1) and cbc\-pillowfight(1) utilities\. It should be placed in +. +.P +Each entry in the cbcrc file is a line with a key\-value pair in the following form: +. +.IP "" 4 +. +.nf + +# optional comments +key=value +. +.fi +. +.IP "" 0 +. +.P +The keys may be specified in random order, and if you specify the same key multiple times the last value "wins"\. The following keys exists: +. +.TP +\fBconnstr\fR +This is the URI\-like string used to connect to the cluster\. Its format consists of a \fIscheme\fR followed by a list of hosts, an optional bucket for the path and an optional \fB?\fR followed by key\-value options\. +. +.IP +Using custom REST API ports +. +.IP +http://localhost:9000,localhost:9001,localhost:9002 +. +.IP +Using custom memcached ports: +. +.IP +couchbase://localhost:9100,localhost:9200,localhost:9300 +. +.TP +\fBpassword\fR +This is the password used during authentication to your bucket +. +.TP +\fBtimeout\fR +The timeout value to use for the operations\. +. +.SH "NOTES" +. +.IP "\(bu" 4 +You can generate such a file from the cbc(1) itself using the \fBwrite\-config\fR subcommand +. +.IP "\(bu" 4 +Most other options can be specified within the connection string +. +.IP "" 0 +. +.SH "SEE ALSO" +cbc(1), cbc\-pillowfight(1) diff --git a/couchbase-sys/libcouchbase-2.7.0/doc/style.css b/couchbase-sys/libcouchbase-2.7.0/doc/style.css new file mode 100644 index 00000000..34359817 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/doc/style.css @@ -0,0 +1,1162 @@ +/* The standard CSS for doxygen */ + +body, table, div, p, dl { + font: 400 14px/19px Roboto,sans-serif; +} + +/* @group Heading Levels */ + +h1 { + font-size: 150%; +} + +.title { + font-size: 150%; + font-weight: bold; + margin: 10px 2px; +} + +h2 { + border-bottom: 1px solid #879ECB; + color: #354C7B; + font-size: 150%; + font-weight: normal; + margin-top: 1.75em; + padding-top: 8px; + padding-bottom: 4px; + width: 100%; +} + +h3 { + font-size: 100%; +} + +h1, h2, h3, h4, h5, h6 { + -webkit-transition: text-shadow 0.5s linear; + -moz-transition: text-shadow 0.5s linear; + -ms-transition: text-shadow 0.5s linear; + -o-transition: text-shadow 0.5s linear; + transition: text-shadow 0.5s linear; + margin-right: 15px; +} + +h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { + text-shadow: 0 0 15px cyan; +} + +dt { + font-weight: bold; +} + +div.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; +} + +p.startli, p.startdd, p.starttd { + margin-top: 2px; +} + +p.endli { + margin-bottom: 0px; +} + +p.enddd { + margin-bottom: 4px; +} + +p.endtd { + margin-bottom: 2px; +} + +/* @end */ + +caption { + font-weight: bold; +} + +span.legend { + font-size: 70%; + text-align: center; +} + +h3.version { + font-size: 90%; + text-align: center; +} + +div.qindex, div.navtab{ + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; +} + +div.qindex, div.navpath { + width: 100%; + line-height: 140%; +} + +div.navtab { + margin-right: 15px; +} + +/* @group Link Styling */ + +a { + color: #3D578C; + font-weight: normal; + text-decoration: none; +} + +.contents a:visited { + color: #4665A2; +} + +a:hover { + text-decoration: underline; +} + +a.qindex { + font-weight: bold; +} + +a.qindexHL { + font-weight: bold; + background-color: #9CAFD4; + color: #ffffff; + border: 1px double #869DCA; +} + +.contents a.qindexHL:visited { + color: #ffffff; +} + +a.el { + font-weight: bold; +} + +a.elRef { +} + +a.code, a.code:visited { + color: #4665A2; +} + +a.codeRef, a.codeRef:visited { + color: #4665A2; +} + +/* @end */ + +dl.el { + margin-left: -1cm; +} + +pre.fragment { + border: 1px solid #C4CFE5; + background-color: #FBFCFD; + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + font-size: 9pt; + line-height: 125%; + font-family: monospace, fixed; + font-size: 105%; +} + +div.fragment { + padding: 4px; + margin: 4px; + background-color: #FBFCFD; + border: 1px solid #C4CFE5; +} + +div.line { + font-family: monospace, fixed; + font-size: 13px; + min-height: 13px; + line-height: 1.0; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; /* Moz */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + white-space: pre-wrap; /* CSS3 */ + word-wrap: break-word; /* IE 5.5+ */ + text-indent: -53px; + padding-left: 53px; + padding-bottom: 0px; + margin: 0px; + -webkit-transition-property: background-color, box-shadow; + -webkit-transition-duration: 0.5s; + -moz-transition-property: background-color, box-shadow; + -moz-transition-duration: 0.5s; + -ms-transition-property: background-color, box-shadow; + -ms-transition-duration: 0.5s; + -o-transition-property: background-color, box-shadow; + -o-transition-duration: 0.5s; + transition-property: background-color, box-shadow; + transition-duration: 0.5s; +} + +div.line.glow { + background-color: cyan; + box-shadow: 0 0 10px cyan; +} + + +span.lineno { + padding-right: 4px; + text-align: right; + border-right: 2px solid #0F0; + background-color: #E8E8E8; + white-space: pre; +} +span.lineno a { + background-color: #D8D8D8; +} + +span.lineno a:hover { + background-color: #C8C8C8; +} + +div.ah { + background-color: black; + font-weight: bold; + color: #ffffff; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border: solid thin #333; + border-radius: 0.5em; + -webkit-border-radius: .5em; + -moz-border-radius: .5em; + box-shadow: 2px 2px 3px #999; + -webkit-box-shadow: 2px 2px 3px #999; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); + background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); +} + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; +} + +div.groupText { + margin-left: 16px; + font-style: italic; +} + +body { + background-color: white; + color: black; + margin: 0; +} + +div.contents { + margin-top: 10px; + margin-left: 12px; + margin-right: 8px; +} + +td.indexkey { + background-color: #EBEFF6; + font-weight: bold; + border: 1px solid #C4CFE5; + margin: 2px 0px 2px 0; + padding: 2px 10px; + white-space: nowrap; + vertical-align: top; +} + +td.indexvalue { + background-color: #EBEFF6; + border: 1px solid #C4CFE5; + padding: 2px 10px; + margin: 2px 0px; +} + +tr.memlist { + background-color: #EEF1F7; +} + +p.formulaDsp { + text-align: center; +} + +img.formulaDsp { + +} + +img.formulaInl { + vertical-align: middle; +} + +div.center { + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; +} + +div.center img { + border: 0px; +} + +address.footer { + text-align: right; + padding-right: 12px; +} + +img.footer { + border: 0px; + vertical-align: middle; +} + +/* @group Code Colorization */ + +span.keyword { + color: #008000 +} + +span.keywordtype { + color: #604020 +} + +span.keywordflow { + color: #e08000 +} + +span.comment { + color: #800000 +} + +span.preprocessor { + color: #806020 +} + +span.stringliteral { + color: #002080 +} + +span.charliteral { + color: #008080 +} + +span.vhdldigit { + color: #ff00ff +} + +span.vhdlchar { + color: #000000 +} + +span.vhdlkeyword { + color: #700070 +} + +span.vhdllogic { + color: #ff0000 +} + +blockquote { + background-color: #F7F8FB; + border-left: 2px solid #9CAFD4; + margin: 0 24px 0 4px; + padding: 0 12px 0 16px; +} + +/* @end */ + +/* +.search { + color: #003399; + font-weight: bold; +} + +form.search { + margin-bottom: 0px; + margin-top: 0px; +} + +input.search { + font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #e8eef2; +} +*/ + +td.tiny { + font-size: 75%; +} + +.dirtab { + padding: 4px; + border-collapse: collapse; + border: 1px solid #A3B4D7; +} + +th.dirtab { + background: #EBEFF6; + font-weight: bold; +} + +hr { + height: 0px; + border: none; + border-top: 1px solid #4A6AAA; +} + +hr.footer { + height: 1px; +} + +/* @group Member Descriptions */ + +table.memberdecls { + border-spacing: 0px; + padding: 0px; +} + +.memberdecls td, .fieldtable tr { + -webkit-transition-property: background-color, box-shadow; + -webkit-transition-duration: 0.5s; + -moz-transition-property: background-color, box-shadow; + -moz-transition-duration: 0.5s; + -ms-transition-property: background-color, box-shadow; + -ms-transition-duration: 0.5s; + -o-transition-property: background-color, box-shadow; + -o-transition-duration: 0.5s; + transition-property: background-color, box-shadow; + transition-duration: 0.5s; +} + +.memberdecls td.glow, .fieldtable tr.glow { + background-color: cyan; + box-shadow: 0 0 15px cyan; +} + +.mdescLeft, .mdescRight, +.memItemLeft, .memItemRight, +.memTemplItemLeft, .memTemplItemRight, .memTemplParams { + background-color: #F9FAFC; + border: none; + margin: 4px; + padding: 1px 0 0 8px; +} + +.mdescLeft, .mdescRight { + padding: 0px 8px 4px 8px; + color: #555; +} + +.memItemLeft, .memItemRight, .memTemplParams { + border-bottom: 1px solid #DEE4F0; +} + +.memItemLeft, .memTemplItemLeft { + white-space: nowrap; +} + +.memItemRight { + width: 100%; +} + +.memTemplParams { + color: #4665A2; + white-space: nowrap; +} + +/* @end */ + +/* @group Member Details */ + +/* Styles for detailed member documentation */ + +.memtemplate { + font-size: 80%; + color: #4665A2; + font-weight: normal; + margin-left: 9px; +} + +.memnav { + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} + +.mempage { + width: 100%; +} + +.memitem { + padding: 0; + margin-bottom: 10px; + margin-right: 5px; + -webkit-transition: box-shadow 0.5s linear; + -moz-transition: box-shadow 0.5s linear; + -ms-transition: box-shadow 0.5s linear; + -o-transition: box-shadow 0.5s linear; + transition: box-shadow 0.5s linear; + display: table !important; + width: 100%; +} + +.memitem.glow { + box-shadow: 0 0 15px cyan; +} + +.memname { + font-weight: bold; + margin-left: 6px; +} + +.memname td { + vertical-align: bottom; +} + +.memproto, dl.reflist dt { + border-top: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 6px 0px 6px 0px; + color: #253555; + font-weight: bold; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #E2E8F2; + /* opera specific markup */ + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + border-top-right-radius: 4px; + border-top-left-radius: 4px; + /* firefox specific markup */ + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + -moz-border-radius-topright: 4px; + -moz-border-radius-topleft: 4px; + /* webkit specific markup */ + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + -webkit-border-top-right-radius: 4px; + -webkit-border-top-left-radius: 4px; + +} + +.memdoc, dl.reflist dd { + border-bottom: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 6px 10px 2px 10px; + background-color: #FBFCFD; + border-top-width: 0; + background-image:url('nav_g.png'); + background-repeat:repeat-x; + background-color: #FFFFFF; + /* opera specific markup */ + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + /* firefox specific markup */ + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-bottomright: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + /* webkit specific markup */ + -webkit-border-bottom-left-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); +} + +dl.reflist dt { + padding: 5px; +} + +dl.reflist dd { + margin: 0px 0px 10px 0px; + padding: 5px; +} + +.paramkey { + text-align: right; +} + +.paramtype { + white-space: nowrap; +} + +.paramname { + color: #602020; + white-space: nowrap; +} +.paramname em { + font-style: normal; +} +.paramname code { + line-height: 14px; +} + +.params, .retval, .exception, .tparams { + margin-left: 0px; + padding-left: 0px; +} + +.params .paramname, .retval .paramname { + font-weight: bold; + vertical-align: top; +} + +.params .paramtype { + font-style: italic; + vertical-align: top; +} + +.params .paramdir { + font-family: "courier new",courier,monospace; + vertical-align: top; +} + +table.mlabels { + border-spacing: 0px; +} + +td.mlabels-left { + width: 100%; + padding: 0px; +} + +td.mlabels-right { + vertical-align: bottom; + padding: 0px; + white-space: nowrap; +} + +span.mlabels { + margin-left: 8px; +} + +span.mlabel { + background-color: #728DC1; + border-top:1px solid #5373B4; + border-left:1px solid #5373B4; + border-right:1px solid #C4CFE5; + border-bottom:1px solid #C4CFE5; + text-shadow: none; + color: white; + margin-right: 4px; + padding: 2px 3px; + border-radius: 3px; + font-size: 7pt; + white-space: nowrap; +} + + + +/* @end */ + +/* these are for tree view when not used as main index */ + +div.directory { + margin: 10px 0px; + border-top: 1px solid #A8B8D9; + border-bottom: 1px solid #A8B8D9; + width: 100%; +} + +.directory table { + border-collapse:collapse; +} + +.directory td { + margin: 0px; + padding: 0px; + vertical-align: top; +} + +.directory td.entry { + white-space: nowrap; + padding-right: 6px; +} + +.directory td.entry a { + outline:none; +} + +.directory td.entry a img { + border: none; +} + +.directory td.desc { + width: 100%; + padding-left: 6px; + padding-right: 6px; + padding-top: 3px; + border-left: 1px solid rgba(0,0,0,0.05); +} + +.directory tr.even { + padding-left: 6px; + background-color: #F7F8FB; +} + +.directory img { + vertical-align: -30%; +} + +.directory .levels { + white-space: nowrap; + width: 100%; + text-align: right; + font-size: 9pt; +} + +.directory .levels span { + cursor: pointer; + padding-left: 2px; + padding-right: 2px; + color: #3D578C; +} + +div.dynheader { + margin-top: 8px; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +address { + font-style: normal; + color: #2A3D61; +} + +table.doxtable { + border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; +} + +table.doxtable td, table.doxtable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +table.doxtable th { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; +} + +table.fieldtable { + width: 100%; + margin-bottom: 10px; + border: 1px solid #A8B8D9; + border-spacing: 0px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); +} + +.fieldtable td, .fieldtable th { + padding: 3px 7px 2px; +} + +.fieldtable td.fieldtype, .fieldtable td.fieldname { + white-space: nowrap; + border-right: 1px solid #A8B8D9; + border-bottom: 1px solid #A8B8D9; + vertical-align: top; +} + +.fieldtable td.fielddoc { + border-bottom: 1px solid #A8B8D9; + width: 100%; +} + +.fieldtable tr:last-child td { + border-bottom: none; +} + +.fieldtable th { + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #E2E8F2; + font-size: 90%; + color: #253555; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; + -moz-border-radius-topleft: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: 1px solid #A8B8D9; +} + + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + background-image: url('tab_b.png'); + z-index: 101; + overflow: hidden; + font-size: 13px; +} + +.navpath ul +{ + font-size: 11px; + background-image:url('tab_b.png'); + background-repeat:repeat-x; + height:30px; + line-height:30px; + color:#8AA0CC; + border:solid 1px #C2CDE4; + overflow:hidden; + margin:0px; + padding:0px; +} + +.navpath li +{ + list-style-type:none; + float:left; + padding-left:10px; + padding-right:15px; + background-image:url('bc_s.png'); + background-repeat:no-repeat; + background-position:right; + color:#364D7C; +} + +.navpath li.navelem a +{ + height:32px; + display:block; + text-decoration: none; + outline: none; + font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; +} + +.navpath li.navelem a:hover +{ + color:#6884BD; +} + +.navpath li.footer +{ + list-style-type:none; + float:right; + padding-left:10px; + padding-right:15px; + background-image:none; + background-repeat:no-repeat; + background-position:right; + color:#364D7C; + font-size: 8pt; +} + + +div.summary +{ + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; +} + +div.summary a +{ + white-space: nowrap; +} + +div.ingroups +{ + font-size: 8pt; + width: 50%; + text-align: left; +} + +div.ingroups a +{ + white-space: nowrap; +} + +div.header +{ + background-image:url('nav_h.png'); + background-repeat:repeat-x; + background-color: #F9FAFC; + margin: 0px; + border-bottom: 1px solid #C4CFE5; +} + +div.headertitle +{ + padding: 5px 5px 5px 10px; +} + +dl +{ + padding: 0 0 0 10px; +} + +/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug */ +dl.section +{ + margin-left: 0px; + padding-left: 0px; +} + +dl.note +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #D0C000; +} + +dl.warning, dl.attention +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #FF0000; +} + +dl.pre, dl.post, dl.invariant +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #00D000; +} + +dl.deprecated +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #505050; +} + +dl.todo +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #00C0E0; +} + +dl.test +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #3030E0; +} + +dl.bug +{ + margin-left:-7px; + padding-left: 3px; + border-left:4px solid; + border-color: #C08050; +} + +dl.section dd { + margin-bottom: 6px; +} + + +#projectlogo +{ + text-align: center; + vertical-align: bottom; + border-collapse: separate; +} + +#projectlogo img +{ + border: 0px none; +} + +#projectname +{ + font: 150% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 2px 0px; +} + +#projectbrief +{ + font: 120% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#projectnumber +{ + font: 50% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#titlearea +{ + padding: 0px; + margin: 0px; + width: 100%; + border-bottom: 1px solid #5373B4; +} + +.image +{ + text-align: center; +} + +.dotgraph +{ + text-align: center; +} + +.mscgraph +{ + text-align: center; +} + +.caption +{ + font-weight: bold; +} + +div.zoom +{ + border: 1px solid #90A5CE; +} + +dl.citelist { + margin-bottom:50px; +} + +dl.citelist dt { + color:#334975; + float:left; + font-weight:bold; + margin-right:10px; + padding:5px; +} + +dl.citelist dd { + margin:2px 0; + padding:5px 0; +} + +div.toc { + padding: 14px 25px; + background-color: #F4F6FA; + border: 1px solid #D8DFEE; + border-radius: 7px 7px 7px 7px; + float: right; + height: auto; + margin: 0 20px 10px 10px; + width: 200px; +} + +div.toc li { + background: url("bdwn.png") no-repeat scroll 0 5px transparent; + font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif; + margin-top: 5px; + padding-left: 10px; + padding-top: 2px; +} + +div.toc h3 { + font: bold 12px/1.2 Arial,FreeSans,sans-serif; + color: #4665A2; + border-bottom: 0 none; + margin: 0; +} + +div.toc ul { + list-style: none outside none; + border: medium none; + padding: 0px; +} + +div.toc li.level1 { + margin-left: 0px; +} + +div.toc li.level2 { + margin-left: 15px; +} + +div.toc li.level3 { + margin-left: 30px; +} + +div.toc li.level4 { + margin-left: 45px; +} + +.inherit_header { + font-weight: bold; + color: gray; + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.inherit_header td { + padding: 6px 0px 2px 5px; +} + +.inherit { + display: none; +} + +tr.heading h2 { + margin-top: 12px; + margin-bottom: 4px; +} + +@media print +{ + #top { display: none; } + #side-nav { display: none; } + #nav-path { display: none; } + body { overflow:visible; } + h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } + .summary { display: none; } + .memitem { page-break-inside: avoid; } + #doc-content + { + margin-left:0 !important; + height:auto !important; + width:auto !important; + overflow:inherit; + display:inline; + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/example/CMakeLists.txt b/couchbase-sys/libcouchbase-2.7.0/example/CMakeLists.txt new file mode 100644 index 00000000..0e482c2d --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/example/CMakeLists.txt @@ -0,0 +1,37 @@ +# This builds the examples; all these targets are optional +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/examples") + +MACRO(ADD_EXAMPLE name dir linkspec incspec) + SET(dir "${CMAKE_CURRENT_SOURCE_DIR}/${dir}") + FILE(GLOB _cur_src ${dir}/*.cc ${dir}/*.c ${dir}/*.cpp) + ADD_EXECUTABLE(${name} ${_cur_src}) + TARGET_LINK_LIBRARIES(${name} couchbase ${linkspec}) + + GET_TARGET_PROPERTY(_tmp_inc ${name} INCLUDE_DIRECTORIES) + LIST(APPEND ${_tmp_inc} ${dir} ${incspec}) + SET_TARGET_PROPERTIES(${name} PROPERTIES INCLUDE_DIRECTORIES "${_tmp_inc}") +ENDMACRO() + +# ADD_EXAMPLE(db db "" "") +ADD_EXECUTABLE(db db/db.c) +ADD_EXECUTABLE(vb db/vb.c) +TARGET_LINK_LIBRARIES(db couchbase) +TARGET_LINK_LIBRARIES(vb couchbase) + +ADD_EXAMPLE(mcc mcc "" "") +ADD_EXAMPLE(minimal minimal "" "") +ADD_EXAMPLE(observe observe "" "") +ADD_EXAMPLE(views-example views "" "") +ADD_EXAMPLE(tick tick "" "") + +ADD_EXECUTABLE(subdoc-simple subdoc/subdoc-simple.cc) +ADD_EXECUTABLE(subdoc-multi subdoc/subdoc-multi.cc) +TARGET_LINK_LIBRARIES(subdoc-simple couchbase) +TARGET_LINK_LIBRARIES(subdoc-multi couchbase) + +IF(NOT WIN32) + ADD_EXAMPLE(instancepool instancepool pthread "") +ENDIF() +IF(HAVE_LIBEVENT2) + ADD_EXAMPLE(libevent-direct libeventdirect ${LIBEVENT_LIBRARIES} ${LIBEVENT_INCLUDE_DIR}) +ENDIF() diff --git a/couchbase-sys/libcouchbase-2.7.0/example/README.markdown b/couchbase-sys/libcouchbase-2.7.0/example/README.markdown new file mode 100644 index 00000000..5a8cf1b6 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/example/README.markdown @@ -0,0 +1,47 @@ +Examples +======== + +An example says more than a 1000 words (and its way easier to write an +example than trying to explain something). In this directory you'll +find a varity of examples that will show you different use patterns of +libcoucbase. + +libeventdirect +-------------- + +This example programs shows you how you may integrate libcouchbase +into your own event loop (in this example libevent). + +syncmode +-------- + +This example shows you how you may use the synchronous interface of +libcouchbase. + +yajl +---- + +This is an example that shows you how to work with views and json + +pillowfight +----------- + +This is an example that implements a small test program to show +you some of the functionalities in libcouchbase. + +minimal +------- + +This is an minimal single-file example which works both unix-like and windows OS. +It accepts three arguments: "host:port", "bucket" and "password". + +Build: + + gcc -lcouchbase -o minimal minimal.c + cl /DWIN32 /Iinclude lib\libcouchbase.lib minimal.c + +Execute: + + valgrind -v --tool=memcheck --leak-check=full --show-reachable=yes ./minimal + ./minimal + mininal.exe diff --git a/couchbase-sys/libcouchbase-2.7.0/example/db/db.c b/couchbase-sys/libcouchbase-2.7.0/example/db/db.c new file mode 100644 index 00000000..721fc34a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/example/db/db.c @@ -0,0 +1,167 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Dumb Benchmark. This application is using libcouchbase to store + * single key and then get this key back infinitely. + * + * BUILD: + * + * cc -o db db.c -lcouchbase + * cl /DWIN32 /Iinclude db.c lib\libcouchbase.lib + * + * RUN: + * + * valgrind -v --tool=memcheck --leak-check=full --show-reachable=yes ./db + * ./db key size + * db.exe key size + */ +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#define PRIu64 "I64u" +#else +#include +#include +#endif + +#ifndef _WIN32 +static void handle_sigint(int sig) +{ + (void)sig; + printf("Exiting on SIGINT\n"); + exit(0); +} + +#define INSTALL_SIGINT_HANDLER() signal(SIGINT, handle_sigint) +#else +#define INSTALL_SIGINT_HANDLER() +#endif + +static void store_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb) +{ + if (rb->rc != LCB_SUCCESS) { + fprintf(stderr, "Couldn't perform initial storage: %s\n", lcb_strerror(NULL, rb->rc)); + exit(EXIT_FAILURE); + } + (void)cbtype; /* unused argument */ +} + +static void get_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb) +{ + if (rb->rc == LCB_SUCCESS) { + lcb_CMDGET gcmd = { 0 }; + lcb_error_t rc; + LCB_CMD_SET_KEY(&gcmd, rb->key, rb->nkey); + rc = lcb_get3(instance, NULL, &gcmd); + if (rc != LCB_SUCCESS) { + fprintf(stderr, "Failed to schedule get operation: %s\n", lcb_strerror(NULL, rc)); + exit(EXIT_FAILURE); + } + } else { + fprintf(stderr, "Failed to retrieve key: %s\n", lcb_strerror(NULL, rb->rc)); + } +} + +int main(int argc, char *argv[]) +{ + lcb_error_t err; + lcb_t instance; + struct lcb_create_st create_options; + const char *key = "foo"; + size_t nkey = strlen(key); + void *bytes; + long nbytes = 6; /* the size of the value */ + + memset(&create_options, 0, sizeof(create_options)); + create_options.version = 3; + + if (argc > 1) { + key = argv[1]; + nkey = strlen(key); + } + if (argc > 2) { + nbytes = atol(argv[2]); + } + if (argc > 3) { + create_options.v.v3.connstr = argv[3]; + } + if (argc > 4) { + create_options.v.v3.passwd = argv[4]; + } + + INSTALL_SIGINT_HANDLER(); + + err = lcb_create(&instance, &create_options); + if (err != LCB_SUCCESS) { + fprintf(stderr, "Failed to create libcouchbase instance: %s\n", lcb_strerror(NULL, err)); + exit(EXIT_FAILURE); + } + + /* Initiate the connect sequence in libcouchbase */ + if ((err = lcb_connect(instance)) != LCB_SUCCESS) { + fprintf(stderr, "Failed to initiate connect: %s\n", lcb_strerror(NULL, err)); + lcb_destroy(instance); + exit(EXIT_FAILURE); + } + lcb_wait3(instance, LCB_WAIT_NOCHECK); + if ((err = lcb_get_bootstrap_status(instance)) != LCB_SUCCESS) { + fprintf(stderr, "Couldn't establish connection to cluster: %s\n", lcb_strerror(NULL, err)); + lcb_destroy(instance); + exit(EXIT_FAILURE); + } + + lcb_install_callback3(instance, LCB_CALLBACK_GET, get_callback); + lcb_install_callback3(instance, LCB_CALLBACK_STORE, store_callback); + + fprintf(stderr, "key: \"%s\"\n", key); + fprintf(stderr, "value size: %ld\n", nbytes); + fprintf(stderr, "connection string: %s\n", create_options.v.v3.connstr ? create_options.v.v3.connstr : ""); + fprintf(stderr, "password: %s\n", create_options.v.v0.passwd ? create_options.v.v3.passwd : ""); + + bytes = malloc(nbytes); + + { + lcb_CMDSTORE cmd = { 0 }; + cmd.operation = LCB_SET; + LCB_CMD_SET_KEY(&cmd, key, nkey); + LCB_CMD_SET_VALUE(&cmd, bytes, nbytes); + err = lcb_store3(instance, NULL, &cmd); + if (err != LCB_SUCCESS) { + fprintf(stderr, "Failed to store: %s\n", lcb_strerror(NULL, err)); + exit(EXIT_FAILURE); + } + } + lcb_wait3(instance, LCB_WAIT_NOCHECK); + fprintf(stderr, "Benchmarking... CTRL-C to stop\n"); + { + lcb_CMDGET cmd = { 0 }; + LCB_CMD_SET_KEY(&cmd, key, nkey); + err = lcb_get3(instance, NULL, &cmd); + if (err != LCB_SUCCESS) { + fprintf(stderr, "Failed to get: %s\n", lcb_strerror(NULL, err)); + exit(EXIT_FAILURE); + } + } + lcb_wait3(instance, LCB_WAIT_NOCHECK); + lcb_destroy(instance); + + exit(EXIT_SUCCESS); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/example/db/vb.c b/couchbase-sys/libcouchbase-2.7.0/example/db/vb.c new file mode 100644 index 00000000..3834e539 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/example/db/vb.c @@ -0,0 +1,227 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* View Benchmark. This application is using libcouchbase to store + * single key and then get this key back infinitely through the view + * through the views. + * + * BUILD: + * + * cc -o vb vb.c -lcouchbase + * cl /DWIN32 /Iinclude vb.c lib\libcouchbase.lib + * + * RUN: + * + * valgrind -v --tool=memcheck --leak-check=full --show-reachable=yes ./vb + * ./vb key size + * vb.exe key size + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#define PRIu64 "I64u" +#else +#include +#include +#endif + +#ifndef _WIN32 +static void handle_sigint(int sig) +{ + (void)sig; + printf("Exiting on SIGINT\n"); + exit(0); +} + +#define INSTALL_SIGINT_HANDLER() signal(SIGINT, handle_sigint) +#else +#define INSTALL_SIGINT_HANDLER() +#endif + + +static void +store_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb) +{ + if (rb->rc == LCB_SUCCESS) { + fprintf(stderr, "STORED \"%.*s\" CAS: %"PRIu64"\n", (int)rb->nkey, rb->key, rb->cas); + } else { + fprintf(stderr, "STORE ERROR: %s (0x%x)\n", lcb_strerror(instance, rb->rc), rb->rc); + exit(EXIT_FAILURE); + } + (void)cbtype; +} + +const char *view; +const char *design; + +static void +do_query_view(lcb_t instance); + +static void +viewrow_callback(lcb_t instance, int cbtype, const lcb_RESPVIEWQUERY *resp) +{ + if (resp->rflags & LCB_RESP_F_FINAL) { + if (resp->rc == LCB_SUCCESS) { + do_query_view(instance); + } else { + fprintf(stderr, "Couldn't query view: %s (0x%x)\n", lcb_strerror(NULL, resp->rc), resp->rc); + if (resp->htresp != NULL) { + fprintf(stderr, "HTTP Status: %u\n", resp->htresp->htstatus); + fprintf(stderr, "HTTP Body: %.*s\n", (int)resp->htresp->nbody, resp->htresp->body); + } + exit(EXIT_FAILURE); + } + } + (void)cbtype; +} + +static void +http_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb) +{ + const lcb_RESPHTTP *rh = (const lcb_RESPHTTP *)rb; + fprintf(stderr, "%.*s... %d\n", (int)rb->nkey, rh->key, rh->htstatus); + if (rh->rc != LCB_SUCCESS) { + fprintf(stderr, "Couldn't issue HTTP request: %s\n", lcb_strerror(NULL, rh->rc)); + exit(EXIT_FAILURE); + } else if (rh->htstatus != 201) { + fprintf(stderr, "Negative reply from server!\n"); + fprintf(stderr, "%*.s\n", (int)rh->nbody, rh->body); + exit(EXIT_FAILURE); + } + + (void)cbtype; +} + +static void +do_query_view(lcb_t instance) +{ + lcb_CMDVIEWQUERY cmd = { 0 }; + lcb_error_t err; + lcb_view_query_initcmd(&cmd, design, view, NULL, viewrow_callback); + cmd.cmdflags |= LCB_CMDVIEWQUERY_F_INCLUDE_DOCS; + err = lcb_view_query(instance, NULL, &cmd); + if (err != LCB_SUCCESS) { + fprintf(stderr, "Couldn't schedule view query: %s\n", lcb_strerror(NULL, err)); + exit(EXIT_FAILURE); + } +} + +int main(int argc, char *argv[]) +{ + lcb_error_t err; + lcb_t instance; + struct lcb_create_st create_options; + const char *key = "foo"; + size_t nkey = strlen(key); + void *bytes; + size_t nbytes = 6; /* the size of the value */ + + memset(&create_options, 0, sizeof(create_options)); + create_options.version = 3; + + if (argc > 1) { + key = argv[1]; + nkey = strlen(key); + } + if (argc > 2) { + nbytes = atol(argv[2]); + } + if (argc > 3) { + create_options.v.v3.connstr = argv[3]; + } + if (argc > 4) { + create_options.v.v3.passwd = argv[4]; + } + + INSTALL_SIGINT_HANDLER(); + + err = lcb_create(&instance, &create_options); + if (err != LCB_SUCCESS) { + fprintf(stderr, "Failed to create libcouchbase instance: %s\n", + lcb_strerror(NULL, err)); + exit(EXIT_FAILURE); + } + /* Initiate the connect sequence in libcouchbase */ + if ((err = lcb_connect(instance)) != LCB_SUCCESS) { + fprintf(stderr, "Failed to initiate connect: %s\n", lcb_strerror(NULL, err)); + lcb_destroy(instance); + exit(EXIT_FAILURE); + } + lcb_wait(instance); + if ((err = lcb_get_bootstrap_status(instance)) != LCB_SUCCESS) { + fprintf(stderr, "Failed to establish connection to cluster: %s\n", lcb_strerror(NULL, err)); + exit(EXIT_FAILURE); + } + lcb_install_callback3(instance, LCB_CALLBACK_HTTP, http_callback); + lcb_install_callback3(instance, LCB_CALLBACK_STORE, store_callback); + + fprintf(stderr, "key: \"%s\"\n", key); + fprintf(stderr, "value size: %ld\n", nbytes); + fprintf(stderr, "connection string: %s\n", create_options.v.v3.connstr ? create_options.v.v3.connstr : ""); + fprintf(stderr, "password: %s\n", create_options.v.v0.passwd ? create_options.v.v3.passwd : ""); + bytes = malloc(nbytes); + + { + lcb_CMDSTORE cmd = { 0 }; + cmd.operation = LCB_SET; + LCB_CMD_SET_KEY(&cmd, key, nkey); + LCB_CMD_SET_VALUE(&cmd, bytes, nbytes); + err = lcb_store3(instance, NULL, &cmd); + if (err != LCB_SUCCESS) { + fprintf(stderr, "Failed to store: %s\n", lcb_strerror(NULL, err)); + exit(EXIT_FAILURE); + } + } + lcb_wait(instance); + + /* Set view and design name: */ + view = "all"; + design = key; + + { + char design_path[64] = { 0 }; + char doc[256] = { 0 }; + lcb_CMDHTTP cmd = { 0 }; + sprintf(design_path, "_design/%s", design); + sprintf(doc, "{\"views\":{\"all\":{\"map\":\"function(doc,meta){if(meta.id=='%s'){emit(meta.id)}}\"}}}", key); + + LCB_CMD_SET_KEY(&cmd, design_path, strlen(design_path)); + cmd.body = doc; + cmd.nbody = strlen(doc); + cmd.method = LCB_HTTP_METHOD_PUT; + cmd.type = LCB_HTTP_TYPE_VIEW; + cmd.content_type = "application/json"; + err = lcb_http3(instance, NULL, &cmd); + if (err != LCB_SUCCESS) { + fprintf(stderr, "Failed to create design document: %s (0x%02x)\n", lcb_strerror(NULL, err), err); + exit(EXIT_FAILURE); + } + } + lcb_wait(instance); + + do_query_view(instance); + lcb_wait(instance); + lcb_destroy(instance); + + exit(EXIT_SUCCESS); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/example/instancepool/main.cc b/couchbase-sys/libcouchbase-2.7.0/example/instancepool/main.cc new file mode 100644 index 00000000..4c74c4c3 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/example/instancepool/main.cc @@ -0,0 +1,102 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "pool.h" +#include +#include + +using namespace lcb; + +extern "C" { +static void get_callback(lcb_t instance, int, const lcb_RESPBASE *rb) +{ + const lcb_RESPGET *rg = reinterpret_cast(rb); + if (rb->rc != LCB_SUCCESS) { + fprintf(stderr, "%p: Couldn't get key", instance); + } else { + fprintf(stderr, "%p: Got key %.*s with value %.*s\n", instance, + (int)rg->nkey, rg->key, (int)rg->nvalue, rg->value); + } +} +} + +class MyPool : public Pool { +public: + MyPool(const lcb_create_st& opts, size_t items) : Pool(opts, items) {} +protected: + void initialize(lcb_t instance) { + // We override the initialize function to set the proper callback we + // care about + fprintf(stderr, "Initializing %p\n", instance); + lcb_install_callback3(instance, LCB_CALLBACK_GET, get_callback); + } +}; + +extern "C" { +static void * +pthr_func(void *arg) { + Pool *pool = reinterpret_cast(arg); + lcb_CMDGET gcmd = { 0 }; + LCB_CMD_SET_KEY(&gcmd, "foo", 3); + + // Get an instance to use + lcb_t instance = pool->pop(); + + // Issue the command + lcb_get3(instance, NULL, &gcmd); + + // Wait for the command to complete + lcb_wait(instance); + + // Release back to pool + pool->push(instance); + + return NULL; +} +} + +#define NUM_WORKERS 20 +int main(void) { + lcb_create_st options; + pthread_t workers[NUM_WORKERS]; + Pool *pool; + lcb_error_t err; + + // set up the options to represent your cluster (hostname etc) + memset(&options, 0, sizeof options); + options.version = 3; + options.v.v3.connstr = "couchbase://localhost/default"; + pool = new MyPool(options, 5); + + err = pool->connect(); + if (err != LCB_SUCCESS) { + fprintf(stderr, "Couldn't connect all instances: %s\n", lcb_strerror(NULL, err)); + exit(EXIT_FAILURE); + } + + for (size_t ii = 0; ii < NUM_WORKERS; ii++) { + pthread_create(&workers[ii], NULL, pthr_func, pool); + } + + for (size_t ii = 0; ii < NUM_WORKERS; ii++) { + void *unused; + pthread_join(workers[ii], &unused); + } + + delete pool; + return 0; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/example/instancepool/pool.cc b/couchbase-sys/libcouchbase-2.7.0/example/instancepool/pool.cc new file mode 100644 index 00000000..aa9f87ea --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/example/instancepool/pool.cc @@ -0,0 +1,102 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "pool.h" + +using namespace lcb; +using std::queue; +using std::vector; + +Pool::Pool(const lcb_create_st& options, size_t nitems) : initial_size(0) +{ + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(&cond, NULL); + for (size_t ii = 0; ii < nitems; ii++) { + lcb_t cur; + lcb_error_t err = lcb_create(&cur, &options); + if (err != LCB_SUCCESS) { + throw err; + } + + instances.push(cur); + all_instances.push_back(cur); + initial_size++; + } +} + +lcb_error_t +Pool::connect() +{ + vector::const_iterator ii = all_instances.begin(); + for (; ii != all_instances.end(); ii++) { + lcb_error_t err; + initialize(*ii); + if ((err = lcb_connect(*ii)) != LCB_SUCCESS) { + return err; + } + lcb_wait(*ii); + if ((err = lcb_get_bootstrap_status(*ii)) != LCB_SUCCESS) { + return err; + } + } + return LCB_SUCCESS; +} + +Pool::~Pool() +{ + pthread_mutex_lock(&mutex); + while (instances.size() < initial_size) { + pthread_cond_wait(&cond, &mutex); + } + vector::const_iterator ii = all_instances.begin(); + for (; ii != all_instances.end(); ii++) { + lcb_destroy(*ii); + } + pthread_mutex_unlock(&mutex); + pthread_mutex_destroy(&mutex); + pthread_cond_destroy(&cond); +} + +lcb_t +Pool::pop() +{ + lcb_t ret = NULL; + + // Need to lock the mutex to the pool structure itself + pthread_mutex_lock(&mutex); + + while (instances.empty()) { + pthread_cond_wait(&cond, &mutex); + } + + ret = instances.front(); + instances.pop(); + pthread_mutex_unlock(&mutex); + + // Note that the instance itself does not need a mutex as long as it is not + // used between multiple threads concurrently. + return ret; +} + +void +Pool::push(lcb_t instance) +{ + pthread_mutex_lock(&mutex); + instances.push(instance); + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/example/instancepool/pool.h b/couchbase-sys/libcouchbase-2.7.0/example/instancepool/pool.h new file mode 100644 index 00000000..f1e34427 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/example/instancepool/pool.h @@ -0,0 +1,69 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef POOL_H +#define POOL_H + +#include +#include +#include +#include +#include + +namespace lcb { + +class Pool { +public: + /** + * Create a new pool to use across threads + * @param options The options used to initialize the instance + * @param items How many items should be in the pool + */ + Pool(const lcb_create_st& options, size_t items = 10); + virtual ~Pool(); + + /**Get an instance from the pool. You should call #push() when you are + * done with the instance + * @return an lcb_t instance */ + lcb_t pop(); + + /**Release an instance back into the pool + * @param instance The instance to release */ + void push(lcb_t instance); + + // Connect all the instances in the pool. This should be called once the + // pool has been constructed + lcb_error_t connect(); + +protected: + /**Function called after the instance is created. You may + * customize the instance here with e.g. lcb_set_cookie() + * @param instance the newly created instance */ + virtual void initialize(lcb_t instance) = 0; + +private: + pthread_mutex_t mutex; + pthread_cond_t cond; + std::queue instances; + + // List of all instances + std::vector all_instances; + size_t initial_size; +}; +} // namespace + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/example/libeventdirect/main.c b/couchbase-sys/libcouchbase-2.7.0/example/libeventdirect/main.c new file mode 100644 index 00000000..5075ac88 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/example/libeventdirect/main.c @@ -0,0 +1,148 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +static void +bootstrap_callback(lcb_t instance, lcb_error_t err) +{ + lcb_CMDSTORE cmd = { 0 }; + if (err != LCB_SUCCESS) { + fprintf(stderr, "ERROR: %s\n", lcb_strerror(instance, err)); + exit(EXIT_FAILURE); + } + /* Since we've got our configuration, let's go ahead and store a value */ + LCB_CMD_SET_KEY(&cmd, "foo", 3); + LCB_CMD_SET_VALUE(&cmd, "bar", 3); + cmd.operation = LCB_SET; + err = lcb_store3(instance, NULL, &cmd); + if (err != LCB_SUCCESS) { + fprintf(stderr, "Failed to set up store request: %s\n", lcb_strerror(instance, err)); + exit(EXIT_FAILURE); + } +} + +static void get_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb) +{ + const lcb_RESPGET *rg = (const lcb_RESPGET *)rb; + if (rg->rc != LCB_SUCCESS) { + fprintf(stderr, "Failed to get key: %s\n", lcb_strerror(instance, rg->rc)); + exit(EXIT_FAILURE); + } + + fprintf(stdout, "I stored and retrieved the key 'foo'. Value: %.*s. Terminate program\n", (int)rg->nvalue, rg->value); + event_base_loopbreak((void *)lcb_get_cookie(instance)); + (void)cbtype; +} + +static void store_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb) +{ + lcb_error_t rc; + lcb_CMDGET gcmd = { 0 }; + + if (rb->rc != LCB_SUCCESS) { + fprintf(stderr, "Failed to store key: %s\n", lcb_strerror(instance, rb->rc)); + exit(EXIT_FAILURE); + } + + LCB_CMD_SET_KEY(&gcmd, rb->key, rb->nkey); + rc = lcb_get3(instance, NULL, &gcmd); + if (rc != LCB_SUCCESS) { + fprintf(stderr, "Failed to schedule get request: %s\n", lcb_strerror(NULL, rc)); + exit(EXIT_FAILURE); + } + (void)cbtype; +} + +static lcb_io_opt_t +create_libevent_io_ops(struct event_base *evbase) +{ + struct lcb_create_io_ops_st ciops; + lcb_io_opt_t ioops; + lcb_error_t error; + + memset(&ciops, 0, sizeof(ciops)); + ciops.v.v0.type = LCB_IO_OPS_LIBEVENT; + ciops.v.v0.cookie = evbase; + + error = lcb_create_io_ops(&ioops, &ciops); + if (error != LCB_SUCCESS) { + fprintf(stderr, "Failed to create an IOOPS structure for libevent: %s\n", lcb_strerror(NULL, error)); + exit(EXIT_FAILURE); + } + + return ioops; +} + +static lcb_t +create_libcouchbase_handle(lcb_io_opt_t ioops) +{ + lcb_t instance; + lcb_error_t error; + struct lcb_create_st copts; + + memset(&copts, 0, sizeof(copts)); + + /* If NULL, will default to localhost */ + copts.v.v0.host = getenv("LCB_EVENT_SERVER"); + copts.v.v0.io = ioops; + error = lcb_create(&instance, &copts); + + if (error != LCB_SUCCESS) { + fprintf(stderr, "Failed to create a libcouchbase instance: %s\n", lcb_strerror(NULL, error)); + exit(EXIT_FAILURE); + } + + /* Set up the callbacks */ + lcb_set_bootstrap_callback(instance, bootstrap_callback); + lcb_install_callback3(instance, LCB_CALLBACK_GET, get_callback); + lcb_install_callback3(instance, LCB_CALLBACK_STORE, store_callback); + + if ((error = lcb_connect(instance)) != LCB_SUCCESS) { + fprintf(stderr, "Failed to connect libcouchbase instance: %s\n", lcb_strerror(NULL, error)); + lcb_destroy(instance); + exit(EXIT_FAILURE); + } + + return instance; +} + +/* This example shows how we can hook ourself into an external event loop. + * You may find more information in the blogpost: http://goo.gl/fCTrX */ +int main(void) +{ + struct event_base *evbase = event_base_new(); + lcb_io_opt_t ioops = create_libevent_io_ops(evbase); + lcb_t instance = create_libcouchbase_handle(ioops); + + /*Store the event base as the user cookie in our instance so that + * we may terminate the program when we're done */ + lcb_set_cookie(instance, evbase); + + /* Run the event loop */ + event_base_loop(evbase, 0); + + /* Cleanup */ + event_base_free(evbase); + lcb_destroy(instance); + exit(EXIT_SUCCESS); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/example/mcc/mcc.cc b/couchbase-sys/libcouchbase-2.7.0/example/mcc/mcc.cc new file mode 100644 index 00000000..adfa33d0 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/example/mcc/mcc.cc @@ -0,0 +1,246 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +static void op_callback(lcb_t, int, const lcb_RESPBASE*); +} + +class MultiClusterClient { +public: + class Operation { + public: + Operation(MultiClusterClient *r) : + root(r), + error(LCB_SUCCESS), + numReferences(r->instances.size() + 1), + numResponses(0) + { + } + + void response(lcb_error_t err, const std::string &value) { + if (err == LCB_SUCCESS) { + values.push_back(value); + } else { + // @todo handle retry etc + error = err; + } + + // @todo figure out the number you want before you want + // the wait to resume + if (++numResponses == 1) { + root->resume(); + } + + --numReferences; + maybeNukeMe(); + } + + lcb_error_t getErrorCode(void) { + return error; + } + + std::string getValue(void) { + return values[0]; + } + + void release(void) { + --numReferences; + maybeNukeMe(); + } + + private: + void maybeNukeMe(void) { + if (numReferences == 0) { + delete this; + } + } + + MultiClusterClient *root; + lcb_error_t error; + int numReferences; + int numResponses; + std::vector values; + }; + + +public: + MultiClusterClient(std::list clusters) { + lcb_error_t err; + if ((err = lcb_create_io_ops(&iops, NULL)) != LCB_SUCCESS) { + std::cerr <<"Failed to create io ops: " << lcb_strerror(NULL, err) + << std::endl; + exit(1); + } + + for (std::list::iterator iter = clusters.begin(); + iter != clusters.end(); + ++iter) { + std::cout<< "Creating instance for cluster " << *iter; + std::cout.flush(); + lcb_create_st options(iter->c_str(), NULL, NULL, NULL, iops); + lcb_t instance; + if ((err = lcb_create(&instance, &options)) != LCB_SUCCESS) { + std::cerr <<"Failed to create instance: " + << lcb_strerror(NULL, err) + << std::endl; + exit(1); + } + lcb_install_callback3(instance, LCB_CALLBACK_GET, op_callback); + lcb_install_callback3(instance, LCB_CALLBACK_STORE, op_callback); + + + lcb_connect(instance); + lcb_wait(instance); + if ((err = lcb_get_bootstrap_status(instance)) != LCB_SUCCESS) { + std::cerr << "Failed to bootstrap: " + << lcb_strerror(instance, err) + << std::endl; + exit(1); + } + std::cout << " done" << std::endl; + + instances.push_back(instance); + } + } + + lcb_error_t store(const std::string &key, const std::string &value) { + lcb_CMDSTORE scmd = { 0 }; + LCB_CMD_SET_KEY(&scmd, key.c_str(), key.size()); + LCB_CMD_SET_VALUE(&scmd, value.c_str(), value.size()); + scmd.operation = LCB_SET; + Operation *oper = new Operation(this); + lcb_error_t error; + for (std::list::iterator iter = instances.begin(); + iter != instances.end(); + ++iter) { + + if ((error = lcb_store3(*iter, oper, &scmd)) != LCB_SUCCESS) { + oper->response(error, ""); + } + } + + wait(); + lcb_error_t ret = oper->getErrorCode(); + oper->release(); + return ret; + } + + lcb_error_t get(const std::string &key, std::string &value) { + lcb_CMDGET gcmd = { 0 }; + LCB_CMD_SET_KEY(&gcmd, key.c_str(), key.size()); + Operation *oper = new Operation(this); + lcb_error_t error; + for (std::list::iterator iter = instances.begin(); + iter != instances.end(); + ++iter) { + + if ((error = lcb_get3(*iter, oper, &gcmd)) != LCB_SUCCESS) { + oper->response(error, ""); + } + } + + wait(); + value = oper->getValue(); + lcb_error_t ret = oper->getErrorCode(); + oper->release(); + return ret; + } + +private: + void wait(void) { + lcb_run_loop(instances.front()); + } + + void resume(void) { + lcb_stop_loop(instances.front()); + } + + lcb_io_opt_t iops; + std::list instances; +}; + +static void op_callback(lcb_t, int cbtype, const lcb_RESPBASE *rb) +{ + MultiClusterClient::Operation *o = + reinterpret_cast(rb->cookie); + if (rb->rc != LCB_SUCCESS) { + o->response(rb->rc, ""); + } else if (cbtype == LCB_CALLBACK_GET) { + const lcb_RESPGET *rg = reinterpret_cast(rb); + std::string value(reinterpret_cast(rg->value), rg->nvalue); + o->response(rb->rc, value); + } +} + +int main(int argc, char **argv) +{ + std::list clusters; + int cmd; + std::string key; + std::string value; + + while ((cmd = getopt(argc, argv, "h:k:v:")) != -1) { + switch (cmd) { + case 'h' : + clusters.push_back(optarg); + break; + case 'k': + key.assign(optarg); + break; + case 'v': + value.assign(optarg); + break; + default: + std::cerr << "Usage: mcc [-h clusterurl]+ -k key -v value" + << std::endl; + exit(EXIT_FAILURE); + } + } + + if (clusters.empty()) { + std::cerr << "No clusters specified" << std::endl; + exit(EXIT_FAILURE); + } + + if (key.empty()) { + std::cerr << "No key specified" << std::endl; + exit(EXIT_FAILURE); + } + + MultiClusterClient client(clusters); + std::cout << "Storing kv-pair: [\"" << key << "\", \"" << value << "\"]: "; + std::cout.flush(); + std::cout << lcb_strerror(NULL, client.store(key, value)) << std::endl; + + std::cout << "Retrieving key \"" << key << "\": "; + std::cout.flush(); + lcb_error_t err = client.get(key, value); + std::cout << lcb_strerror(NULL, err) << std::endl; + if (err == LCB_SUCCESS) { + std::cout << "\tValue: \"" << value << "\"" << std::endl; + } + + exit(EXIT_SUCCESS); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/example/minimal/minimal.c b/couchbase-sys/libcouchbase-2.7.0/example/minimal/minimal.c new file mode 100644 index 00000000..6d9136f3 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/example/minimal/minimal.c @@ -0,0 +1,130 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012-2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * + * This is a minimal example file showing how to connect to a cluster and + * set and retrieve a single item. + */ + +#include +#include +#include +#include +#include /* strlen */ +#ifdef _WIN32 +#define PRIx64 "I64x" +#else +#include +#endif + +static void +die(lcb_t instance, const char *msg, lcb_error_t err) +{ + fprintf(stderr, "%s. Received code 0x%X (%s)\n", + msg, err, lcb_strerror(instance, err)); + exit(EXIT_FAILURE); +} + +static void +op_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb) +{ + fprintf(stderr, "=== %s ===\n", lcb_strcbtype(cbtype)); + if (rb->rc == LCB_SUCCESS) { + fprintf(stderr, "KEY: %.*s\n", (int)rb->nkey, rb->key); + fprintf(stderr, "CAS: 0x%"PRIx64"\n", rb->cas); + if (cbtype == LCB_CALLBACK_GET) { + const lcb_RESPGET *rg = (const lcb_RESPGET *)rb; + fprintf(stderr, "VALUE: %.*s\n", (int)rg->nvalue, rg->value); + fprintf(stderr, "FLAGS: 0x%x\n", rg->itmflags); + } + } else { + die(instance, lcb_strcbtype(rb->rc), rb->rc); + } + (void)instance; +} + +int main(int argc, char *argv[]) +{ + lcb_error_t err; + lcb_t instance; + struct lcb_create_st create_options = { 0 }; + lcb_CMDSTORE scmd = { 0 }; + lcb_CMDGET gcmd = { 0 }; + + create_options.version = 3; + + if (argc < 2) { + fprintf(stderr, "Usage: %s couchbase://host/bucket [ password ]\n", argv[0]); + exit(EXIT_FAILURE); + } + + create_options.v.v3.connstr = argv[1]; + if (argc >= 3) { + create_options.v.v3.passwd = argv[2]; + } + + err = lcb_create(&instance, &create_options); + if (err != LCB_SUCCESS) { + die(NULL, "Couldn't create couchbase handle", err); + } + + err = lcb_connect(instance); + if (err != LCB_SUCCESS) { + die(instance, "Couldn't schedule connection", err); + } + + lcb_wait(instance); + + err = lcb_get_bootstrap_status(instance); + if (err != LCB_SUCCESS) { + die(instance, "Couldn't bootstrap from cluster", err); + } + + /* Assign the handlers to be called for the operation types */ + lcb_install_callback3(instance, LCB_CALLBACK_GET, op_callback); + lcb_install_callback3(instance, LCB_CALLBACK_STORE, op_callback); + + LCB_CMD_SET_KEY(&scmd, "key", strlen("key")); + LCB_CMD_SET_VALUE(&scmd, "value", strlen("value")); + scmd.operation = LCB_SET; + + err = lcb_store3(instance, NULL, &scmd); + if (err != LCB_SUCCESS) { + die(instance, "Couldn't schedule storage operation", err); + } + + /* The store_callback is invoked from lcb_wait() */ + fprintf(stderr, "Will wait for storage operation to complete..\n"); + lcb_wait(instance); + + /* Now fetch the item back */ + LCB_CMD_SET_KEY(&gcmd, "foo", strlen("foo")); + err = lcb_get3(instance, NULL, &gcmd); + if (err != LCB_SUCCESS) { + die(instance, "Couldn't schedule retrieval operation", err); + } + + /* Likewise, the get_callback is invoked from here */ + fprintf(stderr, "Will wait to retrieve item..\n"); + lcb_wait(instance); + + /* Now that we're all done, close down the connection handle */ + lcb_destroy(instance); + return 0; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/example/observe/observe.c b/couchbase-sys/libcouchbase-2.7.0/example/observe/observe.c new file mode 100644 index 00000000..df5d6629 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/example/observe/observe.c @@ -0,0 +1,146 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * BUILD: `cc -o observe observe.c -lcouchbase` + * RUN: `./observe key` + */ +#include +#include +#include +#include +#include +#include + +#define fail(msg) \ + fprintf(stderr, "%s\n", msg); \ + exit(EXIT_FAILURE); + +#define fail2(msg, err) \ + fprintf(stderr, "%s\n", msg); \ + fprintf(stderr, "Error was 0x%x (%s)\n", err, lcb_strerror(NULL, err)) + +typedef struct { + int master; + lcb_U8 status; + lcb_U64 cas; +} node_info; + +typedef struct { + unsigned nresp; + node_info *nodeinfo; +} observe_info; + +static void +observe_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb) +{ + const lcb_RESPOBSERVE *resp = (const lcb_RESPOBSERVE*)rb; + observe_info *obs_info = (observe_info *)rb->cookie; + node_info *ni = &obs_info->nodeinfo[obs_info->nresp]; + + if (rb->nkey == 0) { + fprintf(stderr, "All nodes have replied\n"); + return; + } + + if (rb->rc != LCB_SUCCESS) { + fprintf(stderr, "Failed to observe key from node. 0x%x (%s)\n", + rb->rc, lcb_strerror(instance, rb->rc)); + obs_info->nresp++; + return; + } + + /* Copy over the fields we care about */ + ni->cas = resp->cas; + ni->status = resp->status; + ni->master = resp->ismaster; + + /* Increase the response counter */ + obs_info->nresp++; +} + +int main(int argc, char *argv[]) +{ + lcb_t instance; + lcb_error_t err; + lcb_CMDOBSERVE cmd = { 0 }; + lcb_MULTICMD_CTX *mctx = NULL; + observe_info obs_info; + unsigned nservers, ii; + + if (argc != 2) { + fail("requires key as argument"); + } + + if ((err = lcb_create(&instance, NULL)) != LCB_SUCCESS) { + fail2("cannot create connection instance", err); + } + if ((err = lcb_connect(instance)) != LCB_SUCCESS) { + fail2("Couldn't schedule connection", err); + } + lcb_wait(instance); + if ((err = lcb_get_bootstrap_status(instance)) != LCB_SUCCESS) { + fail2("Couldn't get initial cluster configuration", err); + } + lcb_install_callback3(instance, LCB_CALLBACK_OBSERVE, observe_callback); + + nservers = lcb_get_num_nodes(instance); + obs_info.nodeinfo = calloc(nservers, sizeof (*obs_info.nodeinfo)); + obs_info.nresp = 0; + + mctx = lcb_observe3_ctxnew(instance); + LCB_CMD_SET_KEY(&cmd, argv[1], strlen(argv[1])); + mctx->addcmd(mctx, (const lcb_CMDBASE*)&cmd); + + printf("observing the state of '%s':\n", argv[1]); + if ((err = mctx->done(mctx, &obs_info)) != LCB_SUCCESS) { + fail2("Couldn't schedule observe request", err); + } + + lcb_wait(instance); + for (ii = 0; ii < obs_info.nresp; ii++) { + node_info *ni = &obs_info.nodeinfo[ii]; + fprintf(stderr, "Got status from %s node:\n", ni->master ? "master" : "replica"); + fprintf(stderr, "\tCAS: 0x0%llx\n", ni->cas); + fprintf(stderr, "\tStatus (RAW): 0x%02x\n", ni->status); + fprintf(stderr, "\tExists [CACHE]: %s\n", ni->status & LCB_OBSERVE_NOT_FOUND ? "No" : "Yes"); + fprintf(stderr, "\tExists [DISK]: %s\n", ni->status & LCB_OBSERVE_PERSISTED ? "Yes" : "No"); + fprintf(stderr, "\n"); + } + + /* The next example shows how to use lcb_observe() to only request the + * CAS from the master node */ + obs_info.nresp = 0; + memset(obs_info.nodeinfo, 0, sizeof(obs_info.nodeinfo[0]) * nservers); + + fprintf(stderr, "Will request CAS from master...\n"); + cmd.cmdflags |= LCB_CMDOBSERVE_F_MASTER_ONLY; + mctx = lcb_observe3_ctxnew(instance); + mctx->addcmd(mctx, (const lcb_CMDBASE*)&cmd); + if ((err = mctx->done(mctx, &obs_info)) != LCB_SUCCESS) { + fail2("Couldn't schedule observe request!\n", err); + } + + lcb_wait(instance); + + assert(obs_info.nresp == 1 && obs_info.nodeinfo[0].master); + fprintf(stderr, "CAS on master is 0x%llx\n", obs_info.nodeinfo[0].cas); + + lcb_destroy(instance); + free(obs_info.nodeinfo); + return EXIT_SUCCESS; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/example/subdoc/subdoc-multi.cc b/couchbase-sys/libcouchbase-2.7.0/example/subdoc/subdoc-multi.cc new file mode 100644 index 00000000..915ae130 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/example/subdoc/subdoc-multi.cc @@ -0,0 +1,132 @@ +#define LCB_NO_DEPR_CXX_CTORS +#undef NDEBUG + +#include +#include +#include +#include +#include +#include + +static void generic_callback(lcb_t, int type, const lcb_RESPBASE *rb) +{ + printf("Got callback for %s\n", lcb_strcbtype(type)); + + if (rb->rc != LCB_SUCCESS && rb->rc != LCB_SUBDOC_MULTI_FAILURE) { + printf("Failure: 0x%x\n", rb->rc); + abort(); + } + + if (type == LCB_CALLBACK_GET) { + const lcb_RESPGET *rg = (const lcb_RESPGET *)rb; + printf("Result is: %.*s\n", (int)rg->nvalue, rg->value); + } else if (type == LCB_CALLBACK_SDLOOKUP || type == LCB_CALLBACK_SDMUTATE) { + lcb_SDENTRY ent; + size_t iter = 0; + size_t oix = 0; + const lcb_RESPSUBDOC *resp = reinterpret_cast(rb); + while (lcb_sdresult_next(resp, &ent, &iter)) { + size_t index = oix++; + if (type == LCB_CALLBACK_SDMUTATE) { + index = ent.index; + } + printf("[%lu]: 0x%x. %.*s\n", + index, ent.status, (int)ent.nvalue, ent.value); + } + } +} + +// cluster_run mode +#define DEFAULT_CONNSTR "couchbase://localhost" + +int main(int argc, char **argv) { + lcb_create_st crst = { 0 }; + crst.version = 3; + if (argc > 1) { + crst.v.v3.connstr = argv[1]; + } else { + crst.v.v3.connstr = DEFAULT_CONNSTR; + } + + lcb_t instance; + lcb_error_t rc = lcb_create(&instance, &crst); + assert(rc == LCB_SUCCESS); + + rc = lcb_connect(instance); + assert(rc == LCB_SUCCESS); + lcb_wait(instance); + rc = lcb_get_bootstrap_status(instance); + assert(rc == LCB_SUCCESS); + + // Install generic callback + lcb_install_callback3(instance, LCB_CALLBACK_DEFAULT, generic_callback); + + // Store an item + lcb_CMDSTORE scmd = { 0 }; + scmd.operation = LCB_SET; + LCB_CMD_SET_KEY(&scmd, "key", 3); + const char *initval = "{\"hello\":\"world\"}"; + LCB_CMD_SET_VALUE(&scmd, initval, strlen(initval)); + rc = lcb_store3(instance, NULL, &scmd); + assert(rc == LCB_SUCCESS); + + lcb_CMDSUBDOC mcmd = { 0 }; + LCB_CMD_SET_KEY(&mcmd, "key", 3); + + std::vector specs; + std::string bufs[10]; + + // Add some mutations + for (int ii = 0; ii < 5; ii++) { + std::string& path = bufs[ii * 2]; + std::string& val = bufs[(ii * 2) + 1]; + char pbuf[24], vbuf[24]; + + sprintf(pbuf, "pth%d", ii); + sprintf(vbuf, "\"Value_%d\"", ii); + path = pbuf; + val = vbuf; + + lcb_SDSPEC spec = { 0 }; + LCB_SDSPEC_SET_PATH(&spec, path.c_str(), path.size()); + LCB_CMD_SET_VALUE(&spec, val.c_str(), val.size()); + spec.sdcmd = LCB_SDCMD_DICT_UPSERT; + specs.push_back(spec); + } + + mcmd.specs = specs.data(); + mcmd.nspecs = specs.size(); + rc = lcb_subdoc3(instance, NULL, &mcmd); + assert(rc == LCB_SUCCESS); + + // Reset the specs + specs.clear(); + for (int ii = 0; ii < 5; ii++) { + char pbuf[24]; + std::string& path = bufs[ii]; + sprintf(pbuf, "pth%d", ii); + path = pbuf; + + lcb_SDSPEC spec = { 0 }; + LCB_SDSPEC_SET_PATH(&spec, path.c_str(), path.size()); + spec.sdcmd = LCB_SDCMD_GET; + specs.push_back(spec); + } + + lcb_SDSPEC spec2 = { 0 }; + LCB_SDSPEC_SET_PATH(&spec2, "dummy", 5); + spec2.sdcmd = LCB_SDCMD_GET; + specs.push_back(spec2); + mcmd.specs = specs.data(); + mcmd.nspecs = specs.size(); + rc = lcb_subdoc3(instance, NULL, &mcmd); + assert(rc == LCB_SUCCESS); + + lcb_CMDGET gcmd = { 0 }; + LCB_CMD_SET_KEY(&gcmd, "key", 3); + rc = lcb_get3(instance, NULL, &gcmd); + assert(rc == LCB_SUCCESS); + + lcb_wait(instance); + lcb_destroy(instance); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/example/subdoc/subdoc-simple.cc b/couchbase-sys/libcouchbase-2.7.0/example/subdoc/subdoc-simple.cc new file mode 100644 index 00000000..7e11a1b7 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/example/subdoc/subdoc-simple.cc @@ -0,0 +1,191 @@ +#define LCB_NO_DEPR_CXX_CTORS +#undef NDEBUG + +#include +#include +#include +#include + +static void +op_callback(lcb_t, int cbtype, const lcb_RESPBASE *rb) +{ + fprintf(stderr, "Got callback for %s.. ", lcb_strcbtype(cbtype)); + if (rb->rc != LCB_SUCCESS && rb->rc != LCB_SUBDOC_MULTI_FAILURE) { + fprintf(stderr, "Operation failed (%s)\n", lcb_strerror(NULL, rb->rc)); + return; + } + + if (cbtype == LCB_CALLBACK_GET) { + const lcb_RESPGET *rg = reinterpret_cast(rb); + fprintf(stderr, "Value %.*s\n", (int)rg->nvalue, rg->value); + } else if (cbtype == LCB_CALLBACK_SDMUTATE || cbtype == LCB_CALLBACK_SDLOOKUP) { + const lcb_RESPSUBDOC *resp = reinterpret_cast(rb); + lcb_SDENTRY ent; + size_t iter = 0; + if (lcb_sdresult_next(resp, &ent, &iter)) { + fprintf(stderr, "Status: 0x%x. Value: %.*s\n", ent.status, (int)ent.nvalue, ent.value); + } else { + fprintf(stderr, "No result!\n"); + } + } else { + fprintf(stderr, "OK\n"); + } +} + +// Function to issue an lcb_get3() (and print the state of the document) +static void +demoKey(lcb_t instance, const char *key) +{ + printf("Retrieving '%s'\n", key); + printf("====\n"); + lcb_CMDGET gcmd = { 0 }; + LCB_CMD_SET_KEY(&gcmd, key, strlen(key)); + lcb_error_t rc = lcb_get3(instance, NULL, &gcmd); + assert(rc == LCB_SUCCESS); + lcb_wait(instance); + printf("====\n\n"); +} + +// cluster_run mode +#define DEFAULT_CONNSTR "couchbase://localhost" +int main(int argc, char **argv) +{ + lcb_create_st crst = { 0 }; + crst.version = 3; + if (argc > 1) { + crst.v.v3.connstr = argv[1]; + } else { + crst.v.v3.connstr = DEFAULT_CONNSTR; + } + + lcb_t instance; + lcb_error_t rc = lcb_create(&instance, &crst); + assert(rc == LCB_SUCCESS); + + rc = lcb_connect(instance); + assert(rc == LCB_SUCCESS); + + lcb_wait(instance); + + rc = lcb_get_bootstrap_status(instance); + assert(rc == LCB_SUCCESS); + + lcb_install_callback3(instance, LCB_CALLBACK_DEFAULT, op_callback); + + // Store the initial document. Subdocument operations cannot create + // documents + printf("Storing the initial item..\n"); + // Store an item + lcb_CMDSTORE scmd = { 0 }; + scmd.operation = LCB_SET; + LCB_CMD_SET_KEY(&scmd, "key", 3); + const char *initval = "{\"hello\":\"world\"}"; + LCB_CMD_SET_VALUE(&scmd, initval, strlen(initval)); + rc = lcb_store3(instance, NULL, &scmd); + assert(rc == LCB_SUCCESS); + lcb_wait(instance); + + lcb_CMDSUBDOC cmd; + lcb_SDSPEC spec; + memset(&cmd, 0, sizeof cmd); + memset(&spec, 0, sizeof spec); + + /** + * Retrieve a single item from a document + */ + printf("Getting the 'hello' path from the document\n"); + LCB_CMD_SET_KEY(&cmd, "key", 3); + // Subdocument commands are composed of one or more lcb_SDSPEC objects + // Assign the spec to the command. In this case our "list" is a single spec. + // See subdoc-multi.cc for an example using multiple specs + cmd.specs = &spec; + cmd.nspecs = 1; + // Populate the spec + spec.sdcmd = LCB_SDCMD_GET; + LCB_SDSPEC_SET_PATH(&spec, "hello", 5); + rc = lcb_subdoc3(instance, NULL, &cmd); + assert(rc == LCB_SUCCESS); + lcb_wait(instance); + + /** + * Set a dictionary/object field + */ + memset(&cmd, 0, sizeof cmd); + memset(&spec, 0, sizeof spec); + + printf("Adding new 'goodbye' path to document\n"); + LCB_CMD_SET_KEY(&cmd, "key", 3); + cmd.specs = &spec; + cmd.nspecs = 1; + + spec.sdcmd = LCB_SDCMD_DICT_UPSERT; + LCB_SDSPEC_SET_PATH(&spec, "goodbye", 7); + LCB_SDSPEC_SET_VALUE(&spec, "\"world\"", 7); + + rc = lcb_subdoc3(instance, NULL, &cmd); + assert(rc == LCB_SUCCESS); + lcb_wait(instance); + demoKey(instance, "key"); + + /** + * Add new element to end of an array + */ + memset(&cmd, 0, sizeof cmd); + memset(&spec, 0, sizeof spec); + // Options can also be used + printf("Appending element to array (array might be missing)\n"); + LCB_CMD_SET_KEY(&cmd, "key", 3); + cmd.specs = &spec; + cmd.nspecs = 1; + + // "push" to the end of the array + spec.sdcmd = LCB_SDCMD_ARRAY_ADD_LAST; + + // Create the array if it doesn't exist. This option can be used with + // other commands as well.. + spec.options = LCB_SDSPEC_F_MKINTERMEDIATES; + + LCB_SDSPEC_SET_PATH(&spec, "array", 5); + LCB_SDSPEC_SET_VALUE(&spec, "1", 1); + rc = lcb_subdoc3(instance, NULL, &cmd); + assert(rc == LCB_SUCCESS); + lcb_wait(instance); + demoKey(instance, "key"); + + /** + * Add element to the beginning of an array + */ + memset(&spec, 0, sizeof spec); + memset(&cmd, 0, sizeof cmd); + printf("Prepending element to array (array must exist)\n"); + LCB_CMD_SET_KEY(&cmd, "key", 3); + cmd.specs = &spec; + cmd.nspecs = 1; + + spec.sdcmd = LCB_SDCMD_ARRAY_ADD_FIRST; + LCB_SDSPEC_SET_PATH(&spec, "array", 5); + LCB_SDSPEC_SET_VALUE(&spec, "2", 1); + rc = lcb_subdoc3(instance, NULL, &cmd); + assert(rc == LCB_SUCCESS); + lcb_wait(instance); + demoKey(instance, "key"); + + /** + * Get the first element back.. + */ + memset(&spec, 0, sizeof spec); + memset(&cmd, 0, sizeof cmd); + printf("Getting first array element...\n"); + LCB_CMD_SET_KEY(&cmd, "key", 3); + cmd.specs = &spec; + cmd.nspecs = 1; + + spec.sdcmd = LCB_SDCMD_GET; + LCB_SDSPEC_SET_PATH(&spec, "array[0]", strlen("array[0]")); + rc = lcb_subdoc3(instance, NULL, &cmd); + assert(rc == LCB_SUCCESS); + lcb_wait(instance); + + lcb_destroy(instance); + return 0; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/example/tick/tick.c b/couchbase-sys/libcouchbase-2.7.0/example/tick/tick.c new file mode 100644 index 00000000..21ef429d --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/example/tick/tick.c @@ -0,0 +1,119 @@ +/* + * Copyright 2015 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define VALUE_SIZE 1048576 +static int counter = 0; +static const char *key = "Hello"; +static char value[VALUE_SIZE]; + +static void store_cb(lcb_t instance, int cbtype, const lcb_RESPSTORE *resp) +{ + assert(resp->rc == LCB_SUCCESS); + counter--; + printf("-"); + fflush(stdout); +} + +int main(int argc, char **argv) +{ + lcb_t instance; + lcb_error_t rc; + int ii; + struct lcb_create_st options = { 0 }; + lcb_CMDSTORE cmd = { 0 }; + + if (argc != 2) { + fprintf(stderr, "Must have connection string!\n"); + exit(EXIT_FAILURE); + } + + options.version = 3; + options.v.v3.connstr = argv[1]; + + rc = lcb_create(&instance, &options); + assert(rc == LCB_SUCCESS); + + rc = lcb_cntl_string(instance, "operation_timeout", "120"); + assert(rc == LCB_SUCCESS); + + rc = lcb_connect(instance); + assert(rc == LCB_SUCCESS); + + lcb_wait(instance); + rc = lcb_get_bootstrap_status(instance); + assert(rc == LCB_SUCCESS); + + lcb_install_callback3(instance, LCB_CALLBACK_STORE, (lcb_RESPCALLBACK)store_cb); + + // fill the value so valgrind doesn't warn about unitialized buffers + for (ii = 0; ii < VALUE_SIZE; ii++) { + value[ii] = '*'; + } + + LCB_CMD_SET_KEY(&cmd, key, strlen(key)); + LCB_CMD_SET_VALUE(&cmd, value, VALUE_SIZE); + cmd.operation = LCB_SET; + + printf("Running sample. This will schedule 1000 operations, invoking \n"); + printf("an event loop tick after each one. The tick is non-blocking\n"); + printf("It will sleep 500 microseconds between each operation to allow\n"); + printf("for the asynchronous sending of the buffer's contents to the\n"); + printf("server.\n\n"); + printf("LEGEND:\n"); + printf(" + => Operation Scheduled\n"); + printf(" - => Operation Completed\n"); + + for (ii = 0; ii < 1000; ii++) { + lcb_sched_enter(instance); + + // Note: lcb_store() implicitly does lcb_sched_enter(), lcb_store3(), + // and lcb_sched_leave(). + rc = lcb_store3(instance, NULL, &cmd); + assert(rc == LCB_SUCCESS); + lcb_sched_leave(instance); + counter++; + + // This is like lcb_wait(), except it does not block. + lcb_tick_nowait(instance); + + // Sleep to demonstrate.. Naturally the longer the wait time, the + // clearer the difference between the tick and non-tick versions + usleep(100); + printf("+"); + fflush(stdout); + } + + printf("\nCalling lcb_wait()\n"); + lcb_wait(instance); + printf("\n"); + lcb_destroy(instance); +} + +/** + * Sample output + * +++++++++++++--+----+----+-++++++++++++++-++----------+++-+--+-------++++++++--+------++-+++++++++-+++++-++++-+++++-+++++-++++-+++++-++++-++++++++++-++-++++-++++-++++-++++++-+++++-+++++-+++++-+++++-+++++-++++++++---++-++-++-+-+++-+-+++-+-+++-+-++-+-+-+++-+++-+-++-++-++-+--+-+-++-+++-+--+-+-++-+-+++++++++++++++-+-+-+-+--++-++++++++++++-++++-+++++++++-++++-++-++++++-+++++-+++-+++++-+++-+++++-++++-++++--++-++-++-+-++++++++-+---+++--++-+-+-+++--+-+--+-++++--++--+-+-+-+++++-+-+--++-+++-+-+--+--+-+--+++-+-++-+--+-++++-+--++++++++--+-++---++-++-++---+-+--+-++++--+++--+-+-+--+-+++-++++++---+------------+-----------------------------------------------------------++++----------------------------------------------------------------------------------------------------++++-+++++++--+-+--+-+++++-+--+--+--++++-+-++--+-+-+--++++-+--+++-+-+--+-+--+-++++++++-+----++-+-------------------------+------------------------------++++++++++-+-+++-+-+--+-+---++-+-++++-+-+-+-+--+++-+--------+------++---++++-+++-+-+-+-+--+++-+++-+--+-+-+++-+-++-+-+-+--+-++----+---+------+++++++--+++-+++++-------+-++++++---------+++++-+---+-+-+++-++-----+-----+++++-+-+-++++++--+-+-+--+-+-+-++++--+-+--+-+++++--+--+-++-+----+-----++++++---++++-+++++-++++++-++++-++++-+++++-++++++++-+++++-+++++-+++++-++++-++++-+++++-+++++-+++++-+++++-++++-++++-++++++-+++++-++++-+++-++++-+++++-++++-+++-++++++-+++-++-+++-+--++-++-++-+--+++++----+++-+--+--+-+--++-+++-+-+--+--++-+++-+--+-++++--+-+-+-++-+++-+--+-+--+-+-++++--+-+--+-+-+-+++-+--+-+--+-++-+-++-+-+-+-+-+--+-++-+-+-------+-----+------+------------------+--------------------+------------------------------------------------------+---++++++++-++++-+++++++-++-+++++-+++++-++++-+++++-++++++-++++-+++-++-+-+-+++-++-+-+++-+-++-+++++++++------++-++-+--+-+--++++++++-----+-+++--+-+-+--+-+-+++-++-+-+-+--+-++-++++-+--+--+-+-+++-+++-+--+-+--+-++-+------+--------------+------------------------------------------------------+-------+++++++++-++++++-+++-++++-++++++-+ + * Calling lcb_wait() + * ---------------------------- + * + */ diff --git a/couchbase-sys/libcouchbase-2.7.0/example/views/views-example.cc b/couchbase-sys/libcouchbase-2.7.0/example/views/views-example.cc new file mode 100644 index 00000000..73598137 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/example/views/views-example.cc @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static int cbCounter = 0; + +extern "C" { +static void viewCallback(lcb_t, int, const lcb_RESPVIEWQUERY *rv) +{ + if (rv->rflags & LCB_RESP_F_FINAL) { + printf("*** META FROM VIEWS ***\n"); + fprintf(stderr, "%.*s\n", (int)rv->nvalue, rv->value); + return; + } + + printf("Got row callback from LCB: RC=0x%X, DOCID=%.*s. KEY=%.*s\n", + rv->rc, + (int)rv->ndocid, rv->docid, + (int)rv->nkey, rv->key); + + if (rv->docresp) { + printf(" Document for response. RC=0x%X. CAS=0x%llx\n", + rv->docresp->rc, rv->docresp->cas); + } + + cbCounter++; +} +} + +int main(int argc, const char **argv) +{ + lcb_t instance; + lcb_create_st cropts; + memset(&cropts, 0, sizeof cropts); + const char *connstr = "couchbase://localhost/beer-sample"; + + if (argc > 1) { + if (strcmp(argv[1], "--help") == 0) { + fprintf(stderr, "Usage: %s CONNSTR\n", argv[0]); + exit(EXIT_SUCCESS); + } else { + connstr = argv[1]; + } + } + + cropts.version = 3; + cropts.v.v3.connstr = connstr; + lcb_error_t rc; + rc = lcb_create(&instance, &cropts); + assert(rc == LCB_SUCCESS); + rc = lcb_connect(instance); + assert(rc == LCB_SUCCESS); + lcb_wait(instance); + assert(lcb_get_bootstrap_status(instance) == LCB_SUCCESS); + + // Nao, set up the views.. + lcb_CMDVIEWQUERY vq = { 0 }; + std::string dName = "beer"; + std::string vName = "by_location"; + std::string options = "reduce=false"; + + vq.callback = viewCallback; + vq.ddoc = dName.c_str(); + vq.nddoc = dName.length(); + vq.view = vName.c_str(); + vq.nview = vName.length(); + vq.optstr = options.c_str(); + vq.noptstr = options.size(); + + vq.cmdflags = LCB_CMDVIEWQUERY_F_INCLUDE_DOCS; + + rc = lcb_view_query(instance, NULL, &vq); + assert(rc == LCB_SUCCESS); + lcb_wait(instance); + lcb_destroy(instance); + printf("Total Invocations=%d\n", cbCounter); + return 0; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/_cxxwrap.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/_cxxwrap.h new file mode 100644 index 00000000..7d10c9a3 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/_cxxwrap.h @@ -0,0 +1,150 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_CXXWRAP_H +#define LCB_CXXWRAP_H + +/* Note that the contents of this file are here only to provide inline + * definitions for backwards compatibility with older code. Users of the library + * should assume that structures do _not_ behave differently under C++ and must + * be explicitly initialized with their appropriate members. + * + * For newer code which wants to use pure "C-Style" initializers, define the + * LCB_NO_DEPR_CXX_CTORS macro so that the structures remain to function + * as they do in plain C. + */ + +#if defined(__cplusplus) && !defined(LCB_NO_DEPR_CXX_CTORS) + +#include +#include + + +#define LCB_DEPR_CTORS_GET \ + lcb_get_cmd_st() { std::memset(this, 0, sizeof(*this)); } \ + lcb_get_cmd_st(const void *key, lcb_size_t nkey=0, lcb_time_t exptime=0, int lock=0) { \ + version = 0; \ + v.v0.key = key; \ + if (key != NULL && nkey == 0) { \ + v.v0.nkey = std::strlen((const char *)key); \ + } else { \ + v.v0.nkey = nkey; \ + } \ + v.v0.exptime = exptime; v.v0.lock = lock; v.v0.hashkey = NULL; v.v0.nhashkey = 0; \ + } + + +#define LCB_DEPR_CTORS_RGET \ + lcb_get_replica_cmd_st() { std::memset(this, 0, sizeof(*this)); } \ + lcb_get_replica_cmd_st(const void *key, lcb_size_t nkey, lcb_replica_t strategy=LCB_REPLICA_FIRST, int index=0) { \ + version = 1; v.v1.key = key; v.v1.nkey = nkey; v.v1.hashkey = NULL; v.v1.nhashkey = 0; v.v1.strategy = strategy; v.v1.index = index; \ + } + +#define LCB_DEPR_CTORS_UNL \ + lcb_unlock_cmd_st() { std::memset(this, 0, sizeof(*this)); } \ + lcb_unlock_cmd_st(const void *key, lcb_size_t nkey, lcb_cas_t cas) { \ + version = 0; v.v0.key = key; v.v0.nkey = nkey; v.v0.cas = cas; v.v0.hashkey = NULL; v.v0.nhashkey = 0; \ + } + +#define LCB_DEPR_CTORS_STORE \ + lcb_store_cmd_st() { std::memset(this, 0, sizeof(*this)); } \ + lcb_store_cmd_st(lcb_storage_t operation, const void *key, lcb_size_t nkey, \ + const void *bytes=NULL, lcb_size_t nbytes=0, lcb_uint32_t flags=0, \ + lcb_time_t exptime=0, lcb_cas_t cas=0, lcb_datatype_t datatype=0) { \ + version = 0; v.v0.operation = operation; v.v0.key = key; v.v0.nkey = nkey; v.v0.cas = cas; \ + v.v0.bytes = bytes; v.v0.nbytes = nbytes; v.v0.flags = flags; v.v0.datatype = datatype; \ + v.v0.exptime = exptime; v.v0.hashkey = NULL; v.v0.nhashkey = 0; \ + } + +#define LCB_DEPR_CTORS_ARITH \ + lcb_arithmetic_cmd_st() { std::memset(this, 0, sizeof(*this)); } \ + lcb_arithmetic_cmd_st(const void *key, lcb_size_t nkey, lcb_int64_t delta, int create=0, \ + lcb_uint64_t initial=0, lcb_time_t exptime=0) { \ + version = 0; v.v0.key = key; v.v0.nkey = nkey; v.v0.exptime = exptime; \ + v.v0.delta = delta; v.v0.create = create; v.v0.initial = initial; \ + v.v0.hashkey = NULL; v.v0.nhashkey = 0; \ + } + + +#define LCB_DEPR_CTORS_OBS \ + lcb_observe_cmd_st() { std::memset(this, 0, sizeof(*this)); } \ + lcb_observe_cmd_st(const void *key, lcb_size_t nkey) { \ + version = 0; v.v0.key = key; v.v0.nkey = nkey; v.v0.hashkey = NULL; v.v0.nhashkey = 0; \ + } + +#define LCB_DEPR_CTORS_RM \ + lcb_remove_cmd_st() { std::memset(this, 0, sizeof(*this)); } \ + lcb_remove_cmd_st(const void *key, lcb_size_t nkey=0, lcb_cas_t cas=0) { \ + version = 0; v.v0.key = key; \ + if (key != NULL && nkey == 0) { v.v0.nkey = std::strlen((const char *)key);}\ + else { v.v0.nkey = nkey; } \ + v.v0.cas = cas; v.v0.hashkey = NULL; v.v0.nhashkey = 0; \ + } + +#define LCB_DEPR_CTORS_STATS \ + lcb_server_stats_cmd_st(const char *name=NULL, lcb_size_t nname=0) { \ + version = 0; v.v0.name = name; v.v0.nname = nname; \ + if (name != NULL && nname == 0) { v.v0.nname = std::strlen(name); } \ + else { v.v0.nname = nname; } \ + } + +#define LCB_DEPR_CTORS_VERBOSITY \ + lcb_verbosity_cmd_st(lcb_verbosity_level_t level=LCB_VERBOSITY_WARNING, const char *server=NULL) { \ + version = 0; v.v0.server = server; v.v0.level = level; \ + } + +#define LCB_DEPR_CTORS_VERSIONS \ + lcb_server_version_cmd_st() { std::memset(this, 0, sizeof(*this)); } + +#define LCB_DEPR_CTORS_FLUSH \ + lcb_flush_cmd_st() { std::memset(this, 0, sizeof(*this)); } + +#define LCB_DEPR_CTORS_CRST \ + lcb_create_st(const char *host=NULL, const char *user=NULL,\ + const char *passwd=NULL, const char *bucket=NULL, \ + struct lcb_io_opt_st *io=NULL, lcb_type_t type=LCB_TYPE_BUCKET) { \ + version = 2; v.v2.host = host; v.v2.user = user; v.v2.passwd = passwd; \ + v.v2.bucket = bucket; v.v2.io = io; v.v2.type = type; v.v2.mchosts = NULL; \ + v.v2.transports = NULL; \ + } + +#define LCB_DEPR_CTORS_HTTP \ + lcb_http_cmd_st() { std::memset(this, 0, sizeof(*this)); } \ + lcb_http_cmd_st(const char *path, lcb_size_t npath, const void *body, \ + lcb_size_t nbody, lcb_http_method_t method, \ + int chunked, const char *content_type) { \ + version = 0; v.v0.path = path; v.v0.npath = npath; v.v0.body = body; \ + v.v0.nbody = nbody; v.v0.method = method; v.v0.chunked = chunked; \ + v.v0.content_type = content_type; \ + } +#else +/* Not C++ */ +#define LCB_DEPR_CTORS_GET +#define LCB_DEPR_CTORS_RGET +#define LCB_DEPR_CTORS_UNL +#define LCB_DEPR_CTORS_STORE +#define LCB_DEPR_CTORS_ARITH +#define LCB_DEPR_CTORS_OBS +#define LCB_DEPR_CTORS_RM +#define LCB_DEPR_CTORS_STATS +#define LCB_DEPR_CTORS_VERBOSITY +#define LCB_DEPR_CTORS_VERSIONS +#define LCB_DEPR_CTORS_FLUSH +#define LCB_DEPR_CTORS_HTTP +#define LCB_DEPR_CTORS_CRST +#endif +#endif /* LCB_CXXWRAP_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/api-legacy.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/api-legacy.h new file mode 100644 index 00000000..7ab8ac50 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/api-legacy.h @@ -0,0 +1,1689 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2016 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LCB_APILEGACY_H +#define LCB_APILEGACY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef LIBCOUCHBASE_COUCHBASE_H +#error "include first!" +#endif + + +/**@private + * Note that hashkey/groupid is not a supported feature of Couchbase Server + * and this client. It should be considered volatile and experimental. + * Using this could lead to an unbalanced cluster, inability to interoperate + * with the data from other languages, not being able to use the + * Couchbase Server UI to look up documents and other possible future + * upgrade/migration concerns. + */ +#define LCB__HKFIELDS \ + /** + @private + @volatile + Do not use. This field exists to support older code. Using a dedicated + hashkey will cause problems with your data in various systems. */ \ + const void *hashkey; \ + \ + lcb_SIZE nhashkey; /**<@private*/ + +/** + * @defgroup lcb-legacy-api Legacy Key-Value API + * @brief Operate on one or more key values + * @details + * + * The key-value APIs are high performance APIs utilizing the memcached protocol. + * Use these APIs to access data by its unique key. + * + * These APIs are designed so that each function is passed in one or more + * "Command Structures". A command structure is a small structure detailing a + * specific key and contains options and modifiers for the operation as it + * relates to that key. + * + * All the command structures are currently layed out like so: + * + * @code{.c} + * { + * int version; + * union { + * struct CMDv0 v0; + * struct CMDv1 v1; + * } v; + * } + * @endcode + * + * These top level structures are _wrapper_ structures and are present to ensure + * portability between different versions of the library. To employ these + * structures within the command, you may do: + * + * @code{.c} + * lcb_get_cmd_t gcmd_wrap = { 0 }, *cmdp_wrap = &gcmd_wrap; + * lcb_GETCMDv0 *gcmd = &gcmd_wrap->v.v0; + * gcmd->key = key; + * gcmd->nkey = strlen(key); + * lcb_get(instance, cookie, 1, &gcmd_wrap); + * @endcode + * + * @addtogroup lcb-legacy-api + * @{ + */ + +/** + * @brief Get Command Structure + */ +typedef struct { + const void *key; /**< Key to retrieve */ + lcb_SIZE nkey; /**< Key length */ + + /** + * If this parameter is specified and `lock` is _not_ set then the server + * will also update the object's expiration time while retrieving the key. + * If `lock` _is_ set then this is the maximum amount of time the lock + * may be held (before an unlock) before the server will forecfully unlock + * the key. + */ + lcb_time_t exptime; + + /** + * If this parameter is set then the server will in addition to retrieving + * the item also lock the item, making it so that subsequent attempts to + * lock and/or modify the same item will fail with an error + * (either @ref LCB_KEY_EEXISTS or @ref LCB_ETMPFAIL). + * + * The lock will be released when one of the following happens: + * + * 1. The item is explicitly unlocked (see lcb_unlock()) + * 2. The lock expires (See the #exptime parameter) + * 3. The item is modified using lcb_store(), and being provided with the + * correct _CAS_. + * + */ + int lock; + LCB__HKFIELDS +} lcb_GETCMDv0; + +/** + * @brief lcb_get() Command Wrapper Structure + * @see lcb_GETCMDv0 + */ +typedef struct lcb_get_cmd_st { + int version; + union { lcb_GETCMDv0 v0; } v; + LCB_DEPR_CTORS_GET +} lcb_get_cmd_t; + +/** + * @brief Inner response structure for a get operation + */ +typedef struct { + const void *key; + lcb_SIZE nkey; + const void *bytes; + lcb_SIZE nbytes; + lcb_U32 flags; /**< Server side flags stored with the item */ + lcb_cas_t cas; /**< CAS representing current mutation state of the item */ + lcb_U8 datatype; /**< @private */ +} lcb_GETRESPv0; + +/** + * @brief lcb_get() response wrapper structure + * @see lcb_GETRESPv0 + */ +typedef struct { + int version; + union { + lcb_GETRESPv0 v0; + } v; +} lcb_get_resp_t; + +/** + * The callback function for a "get-style" request. + * + * @param instance the instance performing the operation + * @param cookie the cookie associated with with the command + * @param error The status of the operation + * @param resp More information about the actual item (only key + * and nkey is valid if `error != LCB_SUCCESS`) + * @committed + */ +typedef void (*lcb_get_callback)( + lcb_t instance, const void *cookie, lcb_error_t error, const lcb_get_resp_t *resp); + +/** + * @brief Set the callback to be invoked when an item is received as a result + * of an lcb_get() operation. + * @param callback the new callback to install. Pass NULL to only query the + * current callback + * @return the previous callback + * @see lcb_get() + * @committed + */ +LIBCOUCHBASE_API +lcb_get_callback lcb_set_get_callback(lcb_t, lcb_get_callback callback); + +/** + * Get a number of values from the cache. + * + * If you specify a non-zero value for expiration, the server will + * update the expiration value on the item (refer to the + * documentation on lcb_store to see the meaning of the + * expiration). All other members should be set to zero. + * + * @code{.c} + * lcb_get_cmd_t *get = calloc(1, sizeof(*get)); + * get->version = 0; + * get->v.v0.key = "my-key"; + * get->v.v0.nkey = strlen(get->v.v0.key); + * // Set an expiration of 60 (optional) + * get->v.v0.exptime = 60; + * lcb_get_cmd_t* commands[] = { get }; + * lcb_get(instance, NULL, 1, commands); + * @endcode + * + * It is possible to get an item with a lock that has a timeout. It can + * then be unlocked with either a CAS operation or with an explicit + * unlock command. + * + * You may specify the expiration value for the lock in the + * expiration (setting it to 0 cause the server to use the default + * value). + * + * Get and lock the key: + * + * @code{.c} + * lcb_get_cmd_t *get = calloc(1, sizeof(*get)); + * get->version = 0; + * get->v.v0.key = "my-key"; + * get->v.v0.nkey = strlen(get->v.v0.key); + * // Set a lock expiration of 5 (optional) + * get->v.v0.lock = 1; + * get->v.v0.exptime = 5; + * lcb_get_cmd_t* commands[] = { get }; + * lcb_get(instance, NULL, 1, commands); + * @endcode + * + * @param instance the instance used to batch the requests from + * @param command_cookie A cookie passed to all of the notifications + * from this command + * @param num the total number of elements in the commands array + * @param commands the array containing the items to get + * @return_rc + * + * Operation-specific errors received in callbacks include: + * @cb_err ::LCB_KEY_ENOENT if the key does not exist + * @cb_err ::LCB_ETMPFAIL if the `lock` option was set in the command and the item + * was already locked. + * + * @committed + */ +LIBCOUCHBASE_API +lcb_error_t lcb_get(lcb_t instance, const void *command_cookie, lcb_SIZE num, + const lcb_get_cmd_t *const *commands); + +/**@}*/ + +/** + * @name Legacy get-from-replica API + * + * @{ + */ + + +typedef struct { const void *key; lcb_SIZE nkey; LCB__HKFIELDS } lcb_GETREPLICACMDv0; + +/** + * @brief Command for lcb_get_replica() + */ +typedef struct { + const void *key; + lcb_SIZE nkey; + LCB__HKFIELDS + lcb_replica_t strategy; /**< Strategy to use */ + /**If #strategy is LCB_REPLICA_SELECT, specific the replica index to use */ + int index; +} lcb_GETREPLICACMDv1; + +/** + * @brief wrapper structure for lcb_get_replica() + * @see lcb_GETREPLICACMDv1 + */ +typedef struct lcb_get_replica_cmd_st { + int version; + union { + lcb_GETREPLICACMDv0 v0; + lcb_GETREPLICACMDv1 v1; + } v; + LCB_DEPR_CTORS_RGET +} lcb_get_replica_cmd_t; + +/** + * Get a number of replca values from the cache. + * + * @brief Get items from replica. This is like lcb_get() but is useful when + * an item from the master cannot be retrieved. + * + * From command version 1, it is possible to select strategy of how to + * select the replica node. Currently three strategies are available: + * 1. LCB_REPLICA_FIRST: Previously accessible and default as of 2.0.8, + * the caller will get a reply from the first replica to successfully + * reply within the timeout for the operation or will receive an + * error. + * + * 2. LCB_REPLICA_ALL: Ask all replicas to send documents/item back. + * + * 3. LCB_REPLICA_SELECT: Select one replica by the index in the + * configuration starting from zero. This approach can more quickly + * receive all possible replies for a given topology, but it can + * also generate false negatives. + * + * @note + * applications should not assume the order of the + * replicas indicates more recent data is at a lower index number. + * It is up to the application to determine which version of a + * document/item it may wish to use in the case of retrieving data from a replica. + * + * ### Examples + * + * #### Get document from the second replica + * + * @code{.c} + * lcb_get_replica_cmd_t *get = calloc(1, sizeof(*get)); + * get->version = 1; + * get->v.v1.key = "my-key"; + * get->v.v1.nkey = strlen(get->v.v1.key); + * get->v.v1.strategy = LCB_REPLICA_SELECT; + * get->v.v1.index = 2; + * lcb_get_replica_cmd_st* commands[] = { get }; + * lcb_get_replica(instance, NULL, 1, commands); + * @endcode + * + * #### Get document from the first available replica + * @code{.c} + * get->v.v1.strategy = LCB_REPLICA_FIRST; + * lcb_get_replica_cmd_st* commands[] = { get }; + * lcb_get_replica(instance, NULL, 1, commands); + * @endcode + * + * #### Get document from all replicas + * This will will generate lcb_get_num_replicas() responses + * + * @code{.c} + * get->v.v1.strategy = LCB_REPLICA_ALL; + * lcb_get_replica_cmd_st* commands[] = { get }; + * lcb_get_replica(instance, NULL, 1, commands); + * @endcode + * + * + * @param instance the instance used to batch the requests from + * @param command_cookie A cookie passed to all of the notifications + * from this command + * @param num the total number of elements in the commands array + * @param commands the array containing the items to get + * @return_rc + * + * For operation-specific error codes received in the callback, see lcb_get() + * + * @committed + */ +LIBCOUCHBASE_API +lcb_error_t lcb_get_replica(lcb_t instance, + const void *command_cookie, + lcb_SIZE num, + const lcb_get_replica_cmd_t *const *commands); + +/** + * @brief lcb_unlock() Command structure + * @see lcb_GETRESPv0 + */ +typedef struct { + const void *key; + lcb_SIZE nkey; + lcb_cas_t cas; /**< You _must_ populate this with the CAS */ + LCB__HKFIELDS +} lcb_UNLOCKCMDv0; + +/**@brief lcb_unlock() Wrapper structure + * @see lcb_UNLOCKCMDv0 */ +typedef struct lcb_unlock_cmd_st { + int version; + union { + lcb_UNLOCKCMDv0 v0; + } v; + LCB_DEPR_CTORS_UNL +} lcb_unlock_cmd_t; + +/** @brief lcb_unlock() response structure */ +typedef struct { + const void *key; + lcb_SIZE nkey; +} lcb_UNLOCKRESPv0; + +/**@brief lcb_unlock() wrapper response structure + * @see lcb_UNLOCKRESPv0 */ +typedef struct { + int version; + union { + lcb_UNLOCKRESPv0 v0; + } v; +} lcb_unlock_resp_t; + +/** + * The callback function for an unlock request. + * + * @param instance the instance performing the operation + * @param cookie the cookie associated with with the command + * @param error The status of the operation + * @param resp More information about the operation + * @committed + */ +typedef void (*lcb_unlock_callback)(lcb_t instance, + const void *cookie, + lcb_error_t error, + const lcb_unlock_resp_t *resp); +/**@committed*/ +LIBCOUCHBASE_API +lcb_unlock_callback lcb_set_unlock_callback(lcb_t, lcb_unlock_callback); + +/** + * Unlock the key locked with lcb_get() with the lcb_GETCMDv0::lock option + * + * You should initialize the `key`, `nkey` and `cas` member in the + * lcb_item_st structure for the keys to get. All other + * members should be set to zero. + * + * @code{.c} + * lcb_unlock_cmd_t *unlock = calloc(1, sizeof(*unlock)); + * unlock->version = 0; + * unlock->v.v0.key = "my-key"; + * unlock->v.v0.nkey = strlen(unlock->v.v0.key); + * unlock->v.v0.cas = 0x666; + * lcb_unlock_cmd_t* commands[] = { unlock }; + * lcb_unlock(instance, NULL, 1, commands); + * @endcode + * + * @param instance the handle to lcb + * @param command_cookie A cookie passed to all of the notifications + * from this command + * @param num the total number of elements in the commands array + * @param commands the array containing the items to unlock + * @return The status of the operation + * @return_rc + * + * Operation specific error codes: + * @cb_err ::LCB_ETMPFAIL if the item is not locked, or if the wrong CAS was + * specified + * + * @committed + */ +LIBCOUCHBASE_API +lcb_error_t lcb_unlock(lcb_t instance, + const void *command_cookie, + lcb_SIZE num, + const lcb_unlock_cmd_t *const *commands); +/**@}*/ + +/** + * @brief lcb_store() Command structure + * + * This structure is used to define an item to be stored on the server. + */ +typedef struct { + const void *key; + lcb_SIZE nkey; + const void *bytes; /**< Value to store */ + lcb_SIZE nbytes; /**< Length of value to store */ + lcb_U32 flags; /**< User-defined flags stored along with the item */ + /**If present, the server will check that the item's _current_ CAS matches + * the value specified here. If this check fails the command will fail with + * an @ref LCB_KEY_EEXISTS error. + * + * @warning For @ref LCB_APPEND and @ref LCB_PREPEND, this field should be + * `0`. */ + lcb_cas_t cas; + lcb_U8 datatype; /**< @private */ + /**Expiration for the item. `0` means never expire. + * @warning for @ref LCB_APPEND and @ref LCB_PREPEND, this field should be + * `0`. */ + lcb_time_t exptime; + lcb_storage_t operation; /**< **Mandatory**. Mutation type */ + LCB__HKFIELDS +} lcb_STORECMDv0; + +/** @brief Wrapper structure for lcb_STORECMDv0 */ +typedef struct lcb_store_cmd_st { + int version; + union { + lcb_STORECMDv0 v0; + } v; + LCB_DEPR_CTORS_STORE +} lcb_store_cmd_t; + + + +typedef struct { + const void *key; /**< Key that was stored */ + lcb_SIZE nkey; /**< Size of key that was stored */ + lcb_cas_t cas; /**< Cas representing current mutation */ + /** mutation tokenen for mutation. This is used with N1QL and durability */ + const lcb_MUTATION_TOKEN *mutation_token; +} lcb_STORERESPv0; + +/** @brief Wrapper structure for lcb_STORERESPv0 */ +typedef struct { + int version; + union { + lcb_STORERESPv0 v0; + } v; +} lcb_store_resp_t; + +/** + * The callback function for a storage request. + * + * @param instance the instance performing the operation + * @param operation the operation performed + * @param cookie the cookie associated with with the command + * @param error The status of the operation + * @param resp More information about the item related to the store + * operation. (only key and nkey is valid if + * error != LCB_SUCCESS) + * @committed + */ +typedef void (*lcb_store_callback)(lcb_t instance, const void *cookie, + lcb_storage_t operation, lcb_error_t error, const lcb_store_resp_t *resp); + +/** + * @brief Set the callback to be received when an item has been stored + * @param callback the new callback to install, or `NULL` to just query the + * current callback + * @return the previous callback + * @see lcb_store() + * @committed + */ +LIBCOUCHBASE_API +lcb_store_callback lcb_set_store_callback(lcb_t, lcb_store_callback callback); + +/** + * Store an item in the cluster. + * + * You may initialize all of the members in the the + * lcb_item_st structure with the values you want. + * Values larger than `30*24*60*60` seconds (30 days) are + * interpreted as absolute times (from the epoch). Unused members + * should be set to zero. + * + * @code{.c} + * lcb_store_cmd_st *store = calloc(1, sizeof(*store)); + * store->version = 0; + * store->v.v0.key = "my-key"; + * store->v.v0.nkey = strlen(store->v.v0.key); + * store->v.v0.bytes = "{ value:666 }" + * store->v.v0.nbytes = strlen(store->v.v0.bytes); + * store->v.v0.flags = 0xdeadcafe; + * store->v.v0.cas = 0x1234; + * store->v.v0.exptime = 0x666; + * store->v.v0.operation = LCB_REPLACE; + * lcb_store_cmd_st* commands[] = { store }; + * lcb_store(instance, NULL, 1, commands); + * @endcode + * + * @param instance the instance used to batch the requests from + * @param command_cookie A cookie passed to all of the notifications + * from this command + * @param num the total number of elements in the commands array + * @param commands the array containing the items to store + * @return_rc + * + * Operation-specific error codes include: + * @cb_err ::LCB_KEY_ENOENT if ::LCB_REPLACE was used and the key does not exist + * @cb_err ::LCB_KEY_EEXISTS if ::LCB_ADD was used and the key already exists + * @cb_err ::LCB_KEY_EEXISTS if the CAS was specified (for an operation other + * than ::LCB_ADD) and the item exists on the server with a different + * CAS + * @cb_err ::LCB_KEY_EEXISTS if the item was locked and the CAS supplied did + * not match the locked item's CAS (or if no CAS was supplied) + * @cb_err ::LCB_NOT_STORED if an ::LCB_APPEND or ::LCB_PREPEND operation was + * performed and the item did not exist on the server. + * @cb_err ::LCB_E2BIG if the size of the value exceeds the cluster per-item + * value limit (currently 20MB). + * + * @committed + */ +LIBCOUCHBASE_API +lcb_error_t lcb_store(lcb_t instance, const void *command_cookie, lcb_SIZE num, + const lcb_store_cmd_t *const *commands); + + +/**@brief Command structure for arithmetic operations + * This is contained within the @ref lcb_arithmetic_cmd_t structure */ +typedef struct { + const void *key; + lcb_SIZE nkey; + + /**Expiration time for the item. Note this is _only_ valid if #create is + * set to true. */ + lcb_time_t exptime; + + /** + * If the item does not exist on the server, set this to true to force + * the creation of the item. Otherwise the operation will fail with + * @ref LCB_KEY_ENOENT + */ + int create; + + /** + * This number will be added to the current value on the server; if this is + * negative then the current value will be decremented; if positive then + * the current value will be incremented. + * + * On the server, the counter value is a 64 bit unsigned integer, whose + * maximum value is `UINT64_MAX` If an integer overflow occurs as a result + * of adding the `delta` value to the existing value on the server, then the + * value on the server will wrap around; thus for example, if the existing + * value was `UINT64_MAX-1` and `delta` was supplied as `2`, the new value + * would be `1`. + */ + lcb_S64 delta; + + /** + * If the `create` field is true, this is the initial value for the counter + * iff the item does not yet exist. + */ + lcb_U64 initial; + LCB__HKFIELDS +} lcb_ARITHCMDv0; + +/** @brief Wrapper structure for @ref lcb_ARITHCMDv0 */ +typedef struct lcb_arithmetic_cmd_st { + int version; + /** @brief Wrapper union for @ref lcb_ARITHCMDv0 */ + union { /** @brief Fill this structure */ lcb_ARITHCMDv0 v0; } v; + + LCB_DEPR_CTORS_ARITH +} lcb_arithmetic_cmd_t; + +typedef struct { + const void *key; + lcb_SIZE nkey; + lcb_U64 value; /**< Current numerical value of the counter */ + lcb_cas_t cas; + /** mutation token for mutation. This is used with N1QL and durability */ + const lcb_MUTATION_TOKEN *mutation_token; +} lcb_ARITHRESPv0; + +typedef struct { + int version; + union { + lcb_ARITHRESPv0 v0; + } v; +} lcb_arithmetic_resp_t; + +/** + * The callback function for an arithmetic request. + * + * @param instance the instance performing the operation + * @param cookie the cookie associated with with the command + * @param error The status of the operation + * @param resp More information about the operation (only key + * and nkey is valid if error != LCB_SUCCESS) + * + * @committed + */ +typedef void (*lcb_arithmetic_callback) + (lcb_t instance, const void *cookie, lcb_error_t error, const lcb_arithmetic_resp_t *resp); + +/**@committed*/ +LIBCOUCHBASE_API +lcb_arithmetic_callback lcb_set_arithmetic_callback(lcb_t, lcb_arithmetic_callback); + +/** + * Perform arithmetic operation on a keys value. + * + * You should initialize the key, nkey and expiration member in + * the lcb_item_st structure for the keys to update. + * Values larger than 30*24*60*60 seconds (30 days) are + * interpreted as absolute times (from the epoch). All other + * members should be set to zero. + * + * @code{.c} + * lcb_arithmetic_cmd_t *arithmetic = calloc(1, sizeof(*arithmetic)); + * arithmetic->version = 0; + * arithmetic->v.v0.key = "counter"; + * arithmetic->v.v0.nkey = strlen(arithmetic->v.v0.key); + * arithmetic->v.v0.initial = 0x666; + * arithmetic->v.v0.create = 1; + * arithmetic->v.v0.delta = 1; + * lcb_arithmetic_cmd_t* commands[] = { arithmetic }; + * lcb_arithmetic(instance, NULL, 1, commands); + * @endcode + * + * @param instance the handle to lcb + * @param command_cookie A cookie passed to all of the notifications + * from this command + * @param num the total number of elements in the commands array + * @param commands the array containing the items to operate on + * @return_rc + * + * The following operation-specific error codes may be delivered in the callback: + * @cb_err ::LCB_KEY_ENOENT if the key does not exist (and `create` was not + * specified in the command + * @cb_err ::LCB_DELTA_BADVAL if the existing value could not be parsed into + * a number. + * @committed + */ +LIBCOUCHBASE_API +lcb_error_t lcb_arithmetic(lcb_t instance, + const void *command_cookie, + lcb_SIZE num, + const lcb_arithmetic_cmd_t *const *commands); + +typedef enum { + /** + * Only sends a command to the master. In this case the callback will + * be invoked only once for the master, and then another time with the + * NULL callback + */ + LCB_OBSERVE_MASTER_ONLY = 0x01 +} lcb_observe_options_t; + +#define LCB_OBSERVE_FIELDS_COMMON \ + const void *key; \ + lcb_SIZE nkey; \ + LCB__HKFIELDS /**<@private*/ + +typedef struct { + LCB_OBSERVE_FIELDS_COMMON +} lcb_OBSERVECMDv0; + +/**@brief lcb_observe() Command structure */ +typedef struct { + LCB_OBSERVE_FIELDS_COMMON + lcb_observe_options_t options; +} lcb_OBSERVECMDv1; + +/**@brief lcb_observe() Command wrapper structure + * @see lcb_OBSERVECMDv1 */ +typedef struct lcb_observe_cmd_st { + int version; + union { + lcb_OBSERVECMDv0 v0; + lcb_OBSERVECMDv1 v1; + } v; + + LCB_DEPR_CTORS_OBS +} lcb_observe_cmd_t; + +/** + * @brief Response Structure for lcb_observe() + */ +typedef struct { + const void *key; + lcb_SIZE nkey; + lcb_cas_t cas; /**< CAS of the item on this server */ + lcb_observe_t status; /**< Status flags */ + int from_master; /**< zero if key came from replica */ + lcb_time_t ttp; /**< average time to persist on this server */ + lcb_time_t ttr; /**< average time to replicate on this server */ +} lcb_OBSERVERESPv0; + +typedef struct { + int version; + union { + lcb_OBSERVERESPv0 v0; + } v; +} lcb_observe_resp_t; + +/** + * The callback function for an observe request. + * + * @param instance the instance performing the operation + * @param cookie the cookie associated with with the command + * @param error The status of the operation + * @param resp More information about the operation (only key + * and nkey is valid if error != LCB_SUCCESS) + */ +typedef void (*lcb_observe_callback)(lcb_t instance, + const void *cookie, + lcb_error_t error, + const lcb_observe_resp_t *resp); + +LIBCOUCHBASE_API +lcb_observe_callback lcb_set_observe_callback(lcb_t, lcb_observe_callback); + +/** + * Observe key + * + * @code{.c} + * lcb_observe_cmd_t *observe = calloc(1, sizeof(*observe)); + * observe->version = 0; + * observe->v.v0.key = "my-key"; + * observe->v.v0.nkey = strlen(observe->v.v0.key); + * lcb_observe_cmd_t* commands[] = { observe }; + * lcb_observe(instance, NULL, 1, commands); + * @endcode + * + * @param instance the instance used to batch the requests from + * @param command_cookie A cookie passed to all of the notifications + * from this command + * @param num the total number of elements in the commands array + * @param commands the array containing the items to observe + * @return_rc + * + * The following operation-specific error codes may be returned in the + * callback: + * + * @cb_err ::LCB_UNKNOWN_COMMAND, ::LCB_NOT_SUPPORTED if the cluster does not + * support this operation (such as a Couchbase cluster older than + * version 2.0, or a memcached bucket). + * + * @committed + */ +LIBCOUCHBASE_API +lcb_error_t lcb_observe(lcb_t instance, + const void *command_cookie, + lcb_SIZE num, + const lcb_observe_cmd_t *const *commands); + +/** + * @name Remove items from the cluster (Legacy API) + * Delete items from the cluster + * @{ + */ +typedef struct { + const void *key; + lcb_SIZE nkey; + lcb_cas_t cas; + LCB__HKFIELDS /**<@private*/ +} lcb_REMOVECMDv0; + +typedef struct lcb_remove_cmd_st { + int version; + union { + lcb_REMOVECMDv0 v0; + } v; + LCB_DEPR_CTORS_RM +} lcb_remove_cmd_t; + + +typedef struct { + const void *key; + lcb_SIZE nkey; + lcb_cas_t cas; + /** mutation token for mutation. This is used with N1QL and durability */ + const lcb_MUTATION_TOKEN *mutation_token; +} lcb_REMOVERESPv0; + +typedef struct { + int version; + union { + lcb_REMOVERESPv0 v0; + } v; +} lcb_remove_resp_t; + +/** + * The callback function for a remove request. + * + * @param instance the instance performing the operation + * @param cookie the cookie associated with with the command + * @param error The status of the operation + * @param resp More information about the operation + */ +typedef void (*lcb_remove_callback)(lcb_t instance, const void *cookie, + lcb_error_t error, const lcb_remove_resp_t *resp); + +LIBCOUCHBASE_API +lcb_remove_callback lcb_set_remove_callback(lcb_t, lcb_remove_callback); + +/** + * Remove a key from the cluster + * + * @code{.c} + * lcb_remove_cmd_t *remove = calloc(1, sizeof(*remove)); + * remove->version = 0; + * remove->v.v0.key = "my-key"; + * remove->v.v0.nkey = strlen(remove->v.v0.key); + * remove->v.v0.cas = 0x666; + * lcb_remove_cmd_t* commands[] = { remove }; + * lcb_remove(instance, NULL, 1, commands); + * @endcode + * + * @param instance the instance used to batch the requests from + * @param command_cookie A cookie passed to all of the notifications + * from this command + * @param num the total number of elements in the commands array + * @param commands the array containing the items to remove + * @return_rc + * + * The following operation-specific error codes are returned in the callback + * @cb_err ::LCB_KEY_ENOENT if the key does not exist + * @cb_err ::LCB_KEY_EEXISTS if the CAS was specified and it does not match the + * CAS on the server + * @cb_err ::LCB_KEY_EEXISTS if the item was locked and no CAS (or an incorrect + * CAS) was specified. + * + * @committed + */ +LIBCOUCHBASE_API +lcb_error_t lcb_remove(lcb_t instance, + const void *command_cookie, + lcb_SIZE num, + const lcb_remove_cmd_t *const *commands); + +/** + * @name Modify an item's expiration time + * Modify an item's expiration time, keeping it alive without modifying + * it + * @{ + */ +typedef lcb_get_cmd_t lcb_touch_cmd_t; +typedef struct { + const void *key; + lcb_SIZE nkey; + lcb_cas_t cas; +} lcb_TOUCHRESPv0; +typedef struct { + int version; + union { + lcb_TOUCHRESPv0 v0; + } v; +} lcb_touch_resp_t; + +/** + * The callback function for a touch request. + * + * @param instance the instance performing the operation + * @param cookie the cookie associated with with the command + * @param error The status of the operation + * @param resp More information about the operation + * @committed + */ +typedef void (*lcb_touch_callback)(lcb_t instance, + const void *cookie, + lcb_error_t error, + const lcb_touch_resp_t *resp); +/**@committed*/ +LIBCOUCHBASE_API +lcb_touch_callback lcb_set_touch_callback(lcb_t, lcb_touch_callback); + +/** + * Touch (set expiration time) on a number of values in the cache. + * + * Values larger than 30*24*60*60 seconds (30 days) are + * interpreted as absolute times (from the epoch). All other + * members should be set to zero. + * + * @par Example + * @code{.c} + * lcb_touch_cmd_t touch = { 0 }; + * lcb_touch_cmd_t *cmdlist = { &touch; } + * touch->v.v0.key = "my-key"; + * touch->v.v0.nkey = strlen(item->v.v0.key); + * touch->v.v0.exptime = 300; // 5 minutes + * lcb_touch(instance, NULL, 1, cmdlist); + * @endcode + * + * @param instance the instance used to batch the requests from + * @param cookie A cookie passed to all of the notifications from this command + * @param num the total number of elements in the commnands array + * @param commands the array containing the items to touch + * @return_rc + * + * Errors received in callbacks: + * @cb_err ::LCB_KEY_ENOENT if the item does not exist + * @cb_err ::LCB_KEY_EEXISTS if the item is locked + */ +LIBCOUCHBASE_API +lcb_error_t lcb_touch(lcb_t instance, + const void *cookie, + lcb_SIZE num, + const lcb_touch_cmd_t *const *commands); + +/** + * @{ + */ + +/** @brief Single-key command structure for lcb_durability_poll() */ +typedef struct { + const void *key; + size_t nkey; + LCB__HKFIELDS /**<@private*/ + + /** + * CAS to be checked against. If the key exists on the server + * with a different CAS, the error (in the response) is set to + * LCB_KEY_EEXISTS + */ + lcb_cas_t cas; + const lcb_MUTATION_TOKEN *mutation_token; +} lcb_DURABILITYCMDv0; + +/** + * @brief lcb_durability_poll() Command wrapper + * @see lcb_DURABILITYCMDv0 + */ +typedef struct lcb_durability_cmd_st { + int version; + union { + lcb_DURABILITYCMDv0 v0; + } v; +} lcb_durability_cmd_t; + + +/** @brief Response structure for lcb_durability_poll() */ +typedef struct { + const void *key; + lcb_SIZE nkey; + /** + * if this entry failed, this contains the reason, e.g. + * + * - `LCB_KEY_EEXISTS`: The key exists with a different CAS than expected + * - `LCB_KEY_ENOENT`: The key was not found in the master cache + * - `LCB_ETIMEDOUT`: The key may exist, but the required servers needed + * took too long to respond + */ + lcb_error_t err; + + /** if found with a different CAS, this is the CAS */ + lcb_cas_t cas; + + /** + * Whether the key was persisted to the master. + * For deletes, this means the key was removed from disk + */ + unsigned char persisted_master; + + /** + * Whether the key exists on the master. For deletes, this means + * the key does not exist in cache + */ + unsigned char exists_master; + + /** how many nodes (including master) this item was persisted to */ + unsigned char npersisted; + + /** how many nodes (excluding master) this item was replicated to */ + unsigned char nreplicated; + + /** + * Total number of observe responses received for the node. + * This can be used as a performance metric to determine how many + * total OBSERVE probes were sent until this key was 'done' + */ + unsigned short nresponses; +} lcb_DURABILITYRESPv0; + +typedef struct lcb_durability_resp_st { + int version; + union { + lcb_DURABILITYRESPv0 v0; + } v; +} lcb_durability_resp_t; + +/** + * Schedule a durability check on a set of keys. This callback wraps (somewhat) + * the lower-level OBSERVE (lcb_observe) operations so that users may check if + * a key is endured, e.g. if a key is persisted accross "at least" n number of + * servers + * + * When each key has its criteria satisfied, the durability callback (see above) + * is invoked for it. + * + * The callback may also be invoked when a condition is encountered that will + * prevent the key from ever satisfying the criteria. + * + * @param instance the lcb handle + * @param cookie a pointer to be received with each callback + * @param options a set of options and criteria for this durability check + * @param cmds a list of key specifications to check for + * @param ncmds how many key specifications reside in the list + * @return ::LCB_SUCCESS if scheduled successfuly + * @return ::LCB_DURABILITY_ETOOMANY if the criteria specified exceeds the + * current satisfiable limit (e.g. `persist_to` was set to 4, but + * there are only 2 servers online in the cluster) and `cap_max` + * was not specified. + * @return ::LCB_DUPLICATE_COMMANDS if the same key was found more than once + * in the command list + * + * The following error codes may be returned in the callback + * @cb_err ::LCB_ETIMEDOUT if the specified interval expired before the client + * could verify the durability requirements were satisfied. See + * @ref LCB_CNTL_DURABILITY_TIMEOUT and lcb_DURABILITYOPTSv0::timeout + * for more information on how to increase this interval. + * + * Example (after receiving a store callback) + * @code{.c} + * + * lcb_durability_cmd_t cmd, cmds[1]; + * lcb_durability_opts_t opts; + * lcb_error_t err; + * + * memset(&opts, 0, sizeof(opts); + * memset(&cmd, 0, sizeof(cmd); + * cmds[0] = &cmd; + * + * + * opts.persist_to = 2; + * opts.replicate_to = 1; + * + * cmd.v.v0.key = resp->v.v0.key; + * cmd.v.v0.nkey = resp->v.v0.nkey; + * cmd.v.v0.cas = resp->v.v0.cas; + * + * //schedule the command -- + * err = lcb_durability_poll(instance, cookie, &opts, &cmds, 1); + * // error checking omitted -- + * + * // later on, in the callback. resp is now a durability_resp_t* -- + * if (resp->v.v0.err == LCB_SUCCESS) { + * printf("Key was endured!\n"); + * } else { + * printf("Key did not endure in time\n"); + * printf("Replicated to: %u replica nodes\n", resp->v.v0.nreplicated); + * printf("Persisted to: %u total nodes\n", resp->v.v0.npersisted); + * printf("Did we persist to master? %u\n", + * resp->v.v0.persisted_master); + * printf("Does the key exist in the master's cache? %u\n", + * resp->v.v0.exists_master); + * + * switch (resp->v.v0.err) { + * + * case LCB_KEY_EEXISTS: + * printf("Seems like someone modified the key already...\n"); + * break; + * + * case LCB_ETIMEDOUT: + * printf("Either key does not exist, or the servers are too slow\n"); + * printf("If persisted_master or exists_master is true, then the" + * "server is simply slow.", + * "otherwise, the key does not exist\n"); + * + * break; + * + * default: + * printf("Got other error. This is probably a network error\n"); + * break; + * } + * } + * @endcode + * @committed + */ +LIBCOUCHBASE_API +lcb_error_t lcb_durability_poll(lcb_t instance, + const void *cookie, + const lcb_durability_opts_t *options, + lcb_SIZE ncmds, + const lcb_durability_cmd_t *const *cmds); + +/** + * Callback for durability status. The callback is invoked on completion + * of each key (i.e. only one callback is invoked per-key). + * + * @param lcb_t the instance + * @param cookie the user cookie + * @param err an error + * @param res a response containing information about the key. + */ +typedef void (*lcb_durability_callback)(lcb_t instance, + const void *cookie, + lcb_error_t err, + const lcb_durability_resp_t *res); + +LIBCOUCHBASE_API +lcb_durability_callback lcb_set_durability_callback(lcb_t, + lcb_durability_callback); + +/**@}*/ + +/** + * @name Retrieve statistics from the cluster + * @{ + */ +typedef struct { + const void *name; /**< The name of the stats group to get */ + lcb_SIZE nname; /**< The number of bytes in name */ +} lcb_STATSCMDv0; + +typedef struct lcb_server_stats_cmd_st { + int version; + union { lcb_STATSCMDv0 v0; } v; + LCB_DEPR_CTORS_STATS +} lcb_server_stats_cmd_t; + +/** + * @brief Per-server, per-stat response structure for lcb_server_stats() + * + * This structure is returned for each statistic requested by lcb_server_stats(). + * As both the number of servers replying to this function as well as the number + * of stats per server is variable, the application should pay attention to the + * following semantics: + * + * 1. A new statistic item is delivered with the `server_endpoint` and `key` + * being non-`NULL` + * 2. If a specific server encounters an error, the `key` and `bytes` fields + * will be NULL. + * 3. Once no more replies remain from any of the servers, a final callback + * will be delivered with the `server_endpoint` field set to `NULL`. + * + * It is recommended to index statistics twice; first based on the + * `server_endpoint` field and then on the `key` field. It is likely that the + * same `key` will be received multiple times for different `server_endpoints`. + */ +typedef struct { + const char *server_endpoint; /**< Server which the statistic is from */ + const void *key; /**< Statistic name */ + lcb_SIZE nkey; + const void *bytes; /**< Statistic value */ + lcb_SIZE nbytes; +} lcb_STATSRESPv0; + +/** @brief Wrapper structure for lcb_STATSRESPv0 */ +typedef struct lcb_server_stat_resp_st { + int version; + union { + lcb_STATSRESPv0 v0; + } v; +} lcb_server_stat_resp_t; + +/** + * The callback function for a stat request + * + * @param instance the instance performing the operation + * @param cookie the cookie associated with with the command + * @param error The status of the operation + * @param resp response data + */ +typedef void (*lcb_stat_callback)(lcb_t instance, + const void *cookie, + lcb_error_t error, + const lcb_server_stat_resp_t *resp); +LIBCOUCHBASE_API +lcb_stat_callback lcb_set_stat_callback(lcb_t, lcb_stat_callback); + +/** + * Request server statistics. Without a key specified the server will + * respond with a "default" set of statistics information. Each piece of + * statistical information is returned in its own packet (key contains + * the name of the statistical item and the body contains the value in + * ASCII format). The sequence of return packets is terminated with a + * packet that contains no key and no value. + * + * The command will signal about transfer completion by passing NULL as + * the server endpoint and 0 for key length. Note that key length will + * be zero when some server responds with error. In latter case server + * endpoint argument will indicate the server address. + * + * @code{.c} + * lcb_server_stats_cmd_t *cmd = calloc(1, sizeof(*cmd)); + * cmd->version = 0; + * cmd->v.v0.name = "tap"; + * cmd->v.v0.nname = strlen(cmd->v.v0.nname); + * lcb_server_stats_cmd_t* commands[] = { cmd }; + * lcb_server_stats(instance, NULL, 1, commands); + * @endcode + * + * @param instance the instance used to batch the requests from + * @param command_cookie a cookie passed to all of the notifications + * from this command + * @param num the total number of elements in the commands array + * @param commands the array containing the statistic to get + * @return_rc + * + * The following callbacks may be returned in the callback + * @cb_err ::LCB_KEY_ENOENT if key passed is unrecognized + * + * @todo Enumerate some useful stats here + */ +LIBCOUCHBASE_API +lcb_error_t lcb_server_stats(lcb_t instance, + const void *command_cookie, + lcb_SIZE num, + const lcb_server_stats_cmd_t *const *commands); + +/**@}*/ + + +typedef struct lcb_server_version_cmd_st { + int version; + union { struct { const void *notused; } v0; } v; + LCB_DEPR_CTORS_VERSIONS +} lcb_server_version_cmd_t; + +/** + * @brief Response structure for lcb_server_versions() + */ +typedef struct lcb_server_version_resp_st { + int version; + union { + struct { + const char *server_endpoint; + const char *vstring; + lcb_SIZE nvstring; + } v0; + } v; +} lcb_server_version_resp_t; + +/** + * Request server versions. The callback will be invoked with the + * instance, server address, version string, and version string length. + * + * When all server versions have been received, the callback is invoked + * with the server endpoint argument set to NULL + * + * @code{.c} + * lcb_server_version_cmd_t *cmd = calloc(1, sizeof(*cmd)); + * cmd->version = 0; + * lcb_server_version_cmd_t* commands[] = { cmd }; + * lcb_server_versions(instance, NULL, 1, commands); + * @endcode + * + * @param instance the instance used to batch the requests from + * @param command_cookie a cookie passed to all of the notifications + * from this command + * @param num the total number of elements in the commands array + * @param commands the array containing the version commands + * @return The status of the operation + * + * @attention + * The name of this function may be slightly misleading. This does **not** + * retrieve the Couchbase Server version, but only the version of its _memcached_ + * component. See lcb_server_stats() for a way to retrieve the server version + * @committed + */ +LIBCOUCHBASE_API +lcb_error_t lcb_server_versions(lcb_t instance, + const void *command_cookie, + lcb_SIZE num, + const lcb_server_version_cmd_t *const *commands); + +/** + * The callback function for a version request + * + * @param instance the instance performing the operation + * @param cookie the cookie associated with with the command + * @param error The status of the operation + * @param resp response data + */ +typedef void (*lcb_version_callback)(lcb_t instance, + const void *cookie, + lcb_error_t error, + const lcb_server_version_resp_t *resp); + +LIBCOUCHBASE_API +lcb_version_callback lcb_set_version_callback(lcb_t, lcb_version_callback); + +typedef struct { + const char *server; + lcb_verbosity_level_t level; +} lcb_VERBOSITYCMDv0; + +typedef struct lcb_verbosity_cmd_st { + int version; + union { + lcb_VERBOSITYCMDv0 v0; + } v; + LCB_DEPR_CTORS_VERBOSITY +} lcb_verbosity_cmd_t; + +typedef struct lcb_verbosity_resp_st { + int version; + union { + struct { + const char *server_endpoint; + } v0; + } v; +} lcb_verbosity_resp_t; + +/** + * Set the loglevel on the servers + * + * @code{.c} + * lcb_verbosity_cmd_t *cmd = calloc(1, sizeof(*cmd)); + * cmd->version = 0; + * cmd->v.v0.level = LCB_VERBOSITY_WARNING; + * lcb_verbosity_cmd_t* commands[] = { cmd }; + * lcb_set_verbosity(instance, NULL, 1, commands); + * @endcode + * + * @param instance the instance used to batch the requests from + * @param command_cookie A cookie passed to all of the notifications + * from this command + * @param num the total number of elements in the commands array + * @param commands the array containing the verbosity commands + * @return The status of the operation. + */ +LIBCOUCHBASE_API +lcb_error_t lcb_set_verbosity(lcb_t instance, + const void *command_cookie, + lcb_SIZE num, + const lcb_verbosity_cmd_t *const *commands); + +/** + * The callback function for a verbosity command + * + * @param instance the instance performing the operation + * @param cookie the cookie associated with with the command + * @param error The status of the operation + * @param resp response data + */ +typedef void (*lcb_verbosity_callback)(lcb_t instance, + const void *cookie, + lcb_error_t error, + const lcb_verbosity_resp_t *resp); + +LIBCOUCHBASE_API +lcb_verbosity_callback lcb_set_verbosity_callback(lcb_t, + lcb_verbosity_callback); +typedef struct lcb_flush_cmd_st { + int version; + union { struct { int unused; } v0; } v; + LCB_DEPR_CTORS_FLUSH +} lcb_flush_cmd_t; + +typedef struct lcb_flush_resp_st { + int version; + union { + struct { + const char *server_endpoint; + } v0; + } v; +} lcb_flush_resp_t; + +/** + * Flush the entire couchbase cluster! + * + * @warning + * From Couchbase Server 2.0 and higher, this command will only work on + * _memcached_ buckets. To flush a Couchbase bucket, use the HTTP REST + * API (See: http://docs.couchbase.com/admin/admin/REST/rest-bucket-flush.html) + * + * @code{.c} + * lcb_flush_cmd_t *cmd = calloc(1, sizeof(*cmd)); + * cmd->version = 0; + * lcb_flush_cmd_t* commands[] = { cmd }; + * lcb_flush(instance, NULL, 1, commands); + * @endcode + * + * @param instance the instance used to batch the requests from + * @param cookie A cookie passed to all of the notifications from this command + * @param num the total number of elements in the commands array + * @param commands the array containing the flush commands + * @return_rc + * + * The following error codes may be returned in the callback + * @cb_err ::LCB_NOT_SUPPORTED if trying to flush a Couchbase bucket. + * + * @committed + */ +LIBCOUCHBASE_API +lcb_error_t lcb_flush(lcb_t instance, const void *cookie, + lcb_SIZE num, + const lcb_flush_cmd_t *const *commands); + +/** + * The callback function for a flush request + * + * @param instance the instance performing the operation + * @param cookie the cookie associated with with the command + * @param error The status of the operation + * @param resp Response data + */ +typedef void (*lcb_flush_callback)(lcb_t instance, + const void *cookie, + lcb_error_t error, + const lcb_flush_resp_t *resp); +LIBCOUCHBASE_API +lcb_flush_callback lcb_set_flush_callback(lcb_t, lcb_flush_callback); + +/** + * @name HTTP Operations (Legacy API) + * Schedule HTTP requests to the server. This includes management + * and view requests + * @{ + */ + +/** + * @brief Structure for lcb_make_http_request() + */ +typedef struct { + /** A view path string with optional query params (e.g. skip, limit etc.) */ + const char *path; + lcb_SIZE npath; /**< Length of the path. Mandatory */ + const void *body; /**< The POST body for HTTP request */ + lcb_SIZE nbody; /**< Length of the body. Mandatory if `body != NULL`*/ + lcb_http_method_t method; + /**If true the client will use lcb_http_data_callback to + * notify about response and will call lcb_http_complete + * with empty data eventually. */ + int chunked; + /** The `Content-Type` header for request. For view requests + * it is usually `application/json`, for management -- + * `application/x-www-form-urlencoded`. */ + const char *content_type; +} lcb_HTTPCMDv0; + +/** + * v1 is used by the raw http requests. It is exactly the + * same layout as v0, but it contains an extra field; + * the hostname & port to use.... + */ +typedef struct { + const char *path; /**< @see lcb_HTTPCMDv0::path */ + lcb_SIZE npath; + const void *body; /**< @see lcb_HTTPCMDv0::body */ + lcb_SIZE nbody; + lcb_http_method_t method; + int chunked; + const char *content_type; + const char *host; + const char *username; + const char *password; +} lcb_HTTPCMDv1; + +/**@brief Wrapper structure for lcb_make_http_request + * @see lcb_HTTPCMDv0 + * @see lcb_HTTPCMDv1 + */ +typedef struct lcb_http_cmd_st { + int version; + union { + lcb_HTTPCMDv0 v0; + lcb_HTTPCMDv1 v1; + } v; + LCB_DEPR_CTORS_HTTP +} lcb_http_cmd_t; + +/** + * @brief Response structure received for HTTP requests + * + * The `headers` field is a list of key-value headers for HTTP, so it may + * be traversed like so: + * + * @code{.c} + * const char ** cur = resp->headers; + * for (; *cur; cur+=2) { + * printf("Header: %s:%s\n", cur[0], cur[1]); + * } + * @endcode + */ +typedef struct { + lcb_http_status_t status; /**< HTTP status code */ + const char *path; /**< Path used for request */ + lcb_SIZE npath; + const char *const *headers; /**< List of headers */ + const void *bytes; /**< Body (if any) */ + lcb_SIZE nbytes; +} lcb_HTTPRESPv0; + +typedef struct { + int version; + union { + lcb_HTTPRESPv0 v0; + } v; +} lcb_http_resp_t; + +/** + * Callback invoked for HTTP requests + * @param request Original request handle + * @param instance The instance on which the request was issued + * @param cookie Cookie associated with the request + * @param error Error code for request. Note that more information may likely + * be found within the response structure itself, specifically the + * lcb_HTTPRESPv0::status and lcb_HTTPRESPv0::bytes field + * + * @param resp The response structure + */ +typedef void (*lcb_http_res_callback)( + lcb_http_request_t request, lcb_t instance, const void *cookie, + lcb_error_t error, const lcb_http_resp_t *resp); + +typedef lcb_http_res_callback lcb_http_data_callback; +typedef lcb_http_res_callback lcb_http_complete_callback; + +/** + * @brief Set the HTTP completion callback for HTTP request completion + * + * This callback will be + * invoked once when the response is complete. If the lcb_HTTPCMDv0::chunked + * flag was set, the lcb_HTTRESPv0::bytes will be `NULL`, otherwise it will + * contain the fully buffered response. + */ +LIBCOUCHBASE_API +lcb_http_complete_callback +lcb_set_http_complete_callback(lcb_t, lcb_http_complete_callback); + +/** + * @brief Set the HTTP data stream callback for streaming responses + * + * This callback is invoked only if the lcb_HTTPCMDv0::chunked flag is true. + * The lcb_HTTRESPv0::bytes field will on each invocation contain a new + * fragment of data which should be processed by the client. When the request + * is complete, the the callback specified by lcb_set_http_complete_callback() + * will be invoked with the lcb_HTTPRESPv0::bytes field set to `NULL` + */ +LIBCOUCHBASE_API +lcb_http_data_callback +lcb_set_http_data_callback(lcb_t, lcb_http_data_callback); + +/** + * Execute HTTP request matching given path and yield JSON result object. + * Depending on type it could be: + * + * - LCB_HTTP_TYPE_VIEW + * + * The client should setup view_complete callback in order to fetch + * the result. Also he can setup view_data callback to fetch response + * body in chunks as soon as possible, it will be called each time the + * library receive a data chunk from socket. The empty bytes + * argument (NULL pointer and zero size) is the sign of end of + * response. Chunked callback allows to save memory on large datasets. + * + * - LCB_HTTP_TYPE_MANAGEMENT + * + * Management requests allow you to configure the cluster, add/remove + * buckets, rebalance etc. The result will be passed to management + * callbacks (data/complete). + * + * Fetch first 10 docs from `_design/test/_view/all` view + * @code{.c} + * lcb_http_request_t req; + * lcb_http_cmd_t *cmd = calloc(1, sizeof(lcb_http_cmd_t)); + * cmd->version = 0; + * cmd->v.v0.path = "_design/test/_view/all?limit=10"; + * cmd->v.v0.npath = strlen(item->v.v0.path); + * cmd->v.v0.body = NULL; + * cmd->v.v0.nbody = 0; + * cmd->v.v0.method = LCB_HTTP_METHOD_GET; + * cmd->v.v0.chunked = 1; + * cmd->v.v0.content_type = "application/json"; + * lcb_error_t err = lcb_make_http_request(instance, NULL, + * LCB_HTTP_TYPE_VIEW, &cmd, &req); + * if (err != LCB_SUCCESS) { + * .. failed to schedule request .. + * @endcode + * + * The same as above but with POST filter + * @code{.c} + * lcb_http_request_t req; + * lcb_http_cmd_t *cmd = calloc(1, sizeof(lcb_http_cmd_t)); + * cmd->version = 0; + * cmd->v.v0.path = "_design/test/_view/all?limit=10"; + * cmd->v.v0.npath = strlen(item->v.v0.path); + * cmd->v.v0.body = "{\"keys\": [\"test_1000\", \"test_10002\"]}" + * cmd->v.v0.nbody = strlen(item->v.v0.body); + * cmd->v.v0.method = LCB_HTTP_METHOD_POST; + * cmd->v.v0.chunked = 1; + * cmd->v.v0.content_type = "application/json"; + * lcb_error_t err = lcb_make_http_request(instance, NULL, + * LCB_HTTP_TYPE_VIEW, &cmd, &req); + * if (err != LCB_SUCCESS) { + * .. failed to schedule request .. + * @endcode + * + * @code{.c} Delete bucket via REST management API + * lcb_http_request_t req; + * lcb_http_cmd_t cmd; + * cmd->version = 0; + * cmd.v.v0.path = query.c_str(); + * cmd.v.v0.npath = query.length(); + * cmd.v.v0.body = NULL; + * cmd.v.v0.nbody = 0; + * cmd.v.v0.method = LCB_HTTP_METHOD_DELETE; + * cmd.v.v0.chunked = false; + * cmd.v.v0.content_type = "application/x-www-form-urlencoded"; + * lcb_error_t err = lcb_make_http_request(instance, NULL, + * LCB_HTTP_TYPE_MANAGEMENT, &cmd, &req); + * if (err != LCB_SUCCESS) { + * .. failed to schedule request .. + * @endcode + * + * @param instance The handle to lcb + * @param command_cookie A cookie passed to all of the notifications + * from this command + * @param type The type of the request needed. + * @param cmd The struct describing the command options + * @param request Where to store request handle + * + * @return_rc + * + * The following errors may be received in the callback. Note that ::LCB_SUCCESS + * will be delivered the callback so long as the operation received a full + * HTTP response. You should inspect the individual HTTP status code to determine + * if the actual HTTP request succeeded or not. + * + * @cb_err ::LCB_TOO_MANY_REDIRECTS if the request was redirected too many times. + * @cb_err ::LCB_PROTOCOL_ERROR if the endpoint did not send back a well formed + * HTTP response + * + * @committed + */ +LIBCOUCHBASE_API +lcb_error_t lcb_make_http_request(lcb_t instance, + const void *command_cookie, + lcb_http_type_t type, + const lcb_http_cmd_t *cmd, + lcb_http_request_t *request); + +/**@}*/ + +/**@}*/ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/api3.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/api3.h new file mode 100644 index 00000000..b3429995 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/api3.h @@ -0,0 +1,2 @@ +/* API3 is now the main API. refer to couchbase.h */ +#include diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/assert.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/assert.h new file mode 100644 index 00000000..9e4cc31f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/assert.h @@ -0,0 +1,44 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LIBCOUCHBASE_ASSERT_H +#define LIBCOUCHBASE_ASSERT_H 1 + +#include + +#ifdef NDEBUG +#include +#include + +#define lcb_assert(a) \ + if (!(a)) { \ + fprintf(stderr, "FATAL ERROR:\n"); \ + fprintf(stderr, " libcouchbase experienced an unrecoverable error");\ + fprintf(stderr, " and terminates the program\n"); \ + fprintf(stderr, " to avoid undefined behavior.\n"); \ + fprintf(stderr, " The program should have generated a "); \ + fprintf(stderr, "\"corefile\" which may used\n");\ + fprintf(stderr, " to gather more information about the problem.\n"); \ + fprintf(stderr, " If your system doesn't create \"corefiles\" I "); \ + fprintf(stderr, "can tell you that the\n"); \ + fprintf(stderr, " assertion failed in %s at line %d\n", __FILE__, \ + __LINE__); \ + } +#else +#define lcb_assert(a) assert(a) +#endif + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/auth.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/auth.h new file mode 100644 index 00000000..3bbb1cff --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/auth.h @@ -0,0 +1,101 @@ +#ifndef LCB_AUTH_H +#define LCB_AUTH_H + +#ifdef __cplusplus +namespace lcb { class Authenticator; } +typedef lcb::Authenticator lcb_AUTHENTICATOR; +extern "C" { +#else /* C only! */ +typedef struct lcb_AUTHENTICATOR_Cdummy lcb_AUTHENTICATOR; +#endif + +/** + * @class lcb_AUTHENTICATOR + * + * The lcb_AUTHENTICATOR object allows greater flexibility with regard to + * adding more than a single bucket/password credential pair. It also restores + * the ability to use "true" usernames (though these are not used at present + * yet). + */ + +/** + * @volatile + * Creates a new authenticator object. You may destroy it using lcbauth_unref(). + * The returned object initially has a refcount of 1. + * + * @return A new authenticator object. + */ +LIBCOUCHBASE_API +lcb_AUTHENTICATOR * +lcbauth_new(void); + +/** + * Flags to use when adding a new set of credentials to lcbauth_add_pass + */ +typedef enum { + /** User/Password is administrative; for cluster */ + LCBAUTH_F_CLUSTER = 1<<1, + + /** User is bucket name. Password is bucket password */ + LCBAUTH_F_BUCKET = 1<<2 +} lcbauth_ADDPASSFLAGS; + +/** + * @volatile + * + * Add a set of credentials + * @param auth + * @param user the username (or bucketname, if LCBAUTH_F_BUCKET is passed) + * @param pass the password. If the password is NULL, the credential is removed + * @param flags one of @ref LCBAUTH_F_CLUSTER or @ref LCBAUTH_F_BUCKET. + */ +LIBCOUCHBASE_API +lcb_error_t +lcbauth_add_pass(lcb_AUTHENTICATOR *auth, const char *user, const char *pass, int flags); + +/** + * @volatile + * + * Gets the global username and password. This is either the lone bucket + * password, or an explicit cluster password. + * @param auth + * @param[out] u Global username + * @param[out] p Global password + */ +void +lcbauth_get_upass(const lcb_AUTHENTICATOR *auth, const char **u, const char **p); + + +/** + * @private + * + * Get a user/bucket password + * @param auth the authenticator + * @param name the name of the bucket + * @return the password for the bucket, or NULL if the bucket has no password + * (or is unknown to the authenticator) + */ +const char * +lcbauth_get_bpass(const lcb_AUTHENTICATOR *auth, const char *name); + +/** + * @uncomitted + * Increments the refcount on the authenticator object + * @param auth + */ +LIBCOUCHBASE_API +void +lcbauth_ref(lcb_AUTHENTICATOR *auth); + +/** + * Decrements the refcount on the authenticator object + * @param auth + */ +LIBCOUCHBASE_API +void +lcbauth_unref(lcb_AUTHENTICATOR *auth); + +#ifdef __cplusplus +} +#endif +#endif /* LCB_AUTH_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/cbft.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/cbft.h new file mode 100644 index 00000000..ba7854cc --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/cbft.h @@ -0,0 +1,109 @@ +/* + * Copyright 2016 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +#ifndef LCB_CBFT_H +#define LCB_CBFT_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @ingroup lcb-public-api + * @defgroup lcb-cbft-api Full Text Search (Experimental) + * @brief Search for strings in documents and more + */ + +/** + * @addtogroup lcb-cbft-api + * @{ + */ + +/** + * Response structure for full-text searches. + */ +typedef struct { + #ifndef __LCB_DOXYGEN__ + LCB_RESP_BASE + #else + lcb_U16 rflags; /**< Flags for response structure */ + #endif + /** + * A query hit, or response metadta + * (if #rflags contains @ref LCB_RESP_F_FINAL). The format of the row will + * be JSON, and should be decoded by a JSON decoded in your application. + */ + const char *row; + /** Length of #row */ + size_t nrow; + /** Original HTTP response obejct */ + const lcb_RESPHTTP *htresp; +} lcb_RESPFTS; + +typedef void (*lcb_FTSCALLBACK)(lcb_t, int, const lcb_RESPFTS *); +typedef struct lcb_FTSREQ* lcb_FTSHANDLE; + +/** + * @brief Search Command + */ +typedef struct { + /** Modifiers for command. Currently none are defined */ + lcb_U32 cmdflags; + /** Encoded JSON query */ + const char *query; + /** Length of JSON query */ + size_t nquery; + /** Callback to be invoked. This must be supplied */ + lcb_FTSCALLBACK callback; + /** + * Optional pointer to store the handle. The handle may then be + * used for query cancellation via lcb_fts_cancel() + */ + lcb_FTSHANDLE *handle; +} lcb_CMDFTS; + +/** + * @volatile + * Issue a full-text query. The callback (lcb_CMDFTS::callback) will be invoked + * for each hit. It will then be invoked one last time with the result + * metadata (including any facets) and the lcb_RESPFTS::rflags field having + * the @ref LCB_RESP_F_FINAL bit set + * + * @param instance the instance + * @param cookie opaque user cookie to be set in the response object + * @param cmd command containing the query and callback + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_fts_query(lcb_t instance, const void *cookie, const lcb_CMDFTS *cmd); + +/** + * @volatile + * Cancel a full-text query in progress. The handle is usually obtained via the + * lcb_CMDFTS::handle pointer. + */ +LIBCOUCHBASE_API +void +lcb_fts_cancel(lcb_t, lcb_FTSHANDLE); + +/** + * @} + */ +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/cntl-private.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/cntl-private.h new file mode 100644 index 00000000..a37b4fa5 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/cntl-private.h @@ -0,0 +1,356 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/**@file*/ + +/** + * @internal + * @addtogroup lcb-cntl-settings + * @{ + */ + +/**@internal*/ +#define LCB_CNTL_SERVER_COMMON_FIELDS \ + /** Server index to query */ \ + int index; \ + \ + /** NUL-terminated string containing the address */ \ + const char *host; \ + /** NUL-terminated string containing the port */ \ + const char *port; \ + /** Whether the node is connected */ \ + int connected; \ + \ + /** + * Socket information. If a v0 IO plugin is being used, the sockfd + * is set to the socket descriptor. If a v1 plugin is being used, the + * sockptr is set to point to the appropriate structure. + * + * Note that you *MAY* perform various 'setsockopt' calls on the + * sockfd (though it is your responsibility to ensure those options + * are valid); however the actual socket descriptor may change + * in the case of a cluster configuration update. + */ \ + union { \ + lcb_socket_t sockfd; \ + lcb_sockdata_t *sockptr; \ + } sock; \ + +/** + * @internal + * @brief Information describing the server + */ +typedef struct lcb_cntl_server_st { + /** Structure version */ + int version; + + union { + struct { + LCB_CNTL_SERVER_COMMON_FIELDS + } v0; + + /** Current information here */ + struct { + LCB_CNTL_SERVER_COMMON_FIELDS + /** Chosen SASL mechanism */ + const char *sasl_mech; + } v1; + } v; +} lcb_cntl_server_t; +#undef LCB_CNTL_SERVER_COMMON_FIELDS + +/** + * @internal + * + * @brief Get information about a memcached node. + * + * This function will populate a structure containing various information + * about the specific host + * + * Note that all fields in the structure are only valid until the following + * happens (whichever is first) + * + * 1. Another libcouchbase API function is called + * 2. The event loop regains control + * + * @cntl_arg_getonly{lcb_cntl_server_t*} + * @volatile + */ +#define LCB_CNTL_MEMDNODE_INFO 0x08 + +/** + * @internal + * + * @brief Get information about the configuration node. + * + * Note that this may not be available if the configuration mode is not HTTP + * + * @cntl_arg_getonly{lcb_cntl_server_t*} + * @volatile + */ +#define LCB_CNTL_CONFIGNODE_INFO 0x09 + +/**@internal + * @brief Information about the I/O plugin + */ +struct lcb_cntl_iops_info_st { + int version; + union { + struct { + /** + * Pass here options, used to create IO structure with + * lcb_create_io_ops(3), to find out whether the library + * will override them in the current environment + */ + const struct lcb_create_io_ops_st *options; + + /** + * The default IO ops type. This is hard-coded into the library + * and is used if nothing else was specified in creation options + * or the environment + */ + lcb_io_ops_type_t os_default; + + /** + * The effective plugin type after reading environment variables. + * If this is set to 0, then a manual (non-builtin) plugin has been + * specified. + */ + lcb_io_ops_type_t effective; + } v0; + } v; +}; + +/** + * @internal + * @brief Get the default IOPS types for this build. + * + * This provides a convenient + * way to determine what libcouchbase will use for IO when not explicitly + * specifying an iops structure to lcb_create() + * + * @cntl_arg_getonly{lcb_cntl_io_ops_info_st*} + * + * @note You may pass NULL to lcb_cntl for the 'instance' parameter, + * as this does not read anything specific on the handle + * + * @uncommitted + */ +#define LCB_CNTL_IOPS_DEFAULT_TYPES 0x10 + +/** + * @internal + * + * @brief Set the nodes for the HTTP provider. + * + * @uncommitted + * + * This sets the initial list + * for the nodes to be used for bootstrapping the cluster. This may also + * be used subsequently in runtime to provide an updated list of nodes + * if the current list malfunctions. + * + * The argument for this cntl accepts a NUL-terminated string containing + * one or more nodes. The format for this string is the same as the + * `host` parameter in lcb_create_st + * + * Ports should specify the REST API port. + * @cntl_arg_setonly{char** (Array of strings)} + */ +#define LCB_CNTL_CONFIG_HTTP_NODES 0x1D + +/** + * @internal + * + * @brief Set the nodes for the CCCP provider. + * + * Similar to @ref LCB_CNTL_CONFIG_HTTP_NODES, but affects the CCCP provider + * instead. Ports should specify the _memcached_ port + * @cntl_arg_setonly{char** (Array of strings)} + * @uncomitted + */ +#define LCB_CNTL_CONFIG_CCCP_NODES 0x1E + +/** + * @internal + * + * @brief Set the config nodes for the relevant providers. + * + * This is passed an lcb_create_st2 structure which is used to initialize + * the providers. Useful if you wish to reinitialize or modify the + * provider settings _after_ the instance itself has already been + * constructed. + * + * Note that the username, password, bucket, and io fields are + * ignored. + * + * @cntl_arg_setonly{lcb_create_st2*} + * @uncommitted + */ +#define LCB_CNTL_CONFIG_ALL_NODES 0x20 + +/** + * @internal + * + * @volatile + * Reinitialize the instance using a connection string. Only options and + * the hostlists are used from this string. The bucket in the string (if specified) + * and any SSL options (i.e. `couchbases://` or `ssl=no_verify`) are ignored. + * + * + * This is the newer variant of @ref LCB_CNTL_CONFIG_ALL_NODES + * @cntl_arg_setonly{const char *} + */ +#define LCB_CNTL_REINIT_CONNSTR 0x2B + +/** + * Options for how to handle compression + */ +typedef enum { + /** Do not perform compression in any direction. Data which is received + * compressed via the server will be indicated as such by having the + * `LCB_VALUE_F_SNAPPYCOMP` flag set in the lcb_GETRESPv0::datatype field */ + LCB_COMPRESS_NONE = 0x00, + + /** + * Decompress incoming data, if the data has been compressed at the server. + * If this is set, the `datatype` field in responses will always be stripped + * of the `LCB_VALUE_F_SNAPPYCOMP` flag. + */ + LCB_COMPRESS_IN = 1 << 0, + + /** + * Compress outgoing data. Note that if the `datatype` field contains the + * `LCB_VALUE_F_SNAPPYCOMP` flag, then the data will never be compressed + * as it is assumed that it is already compressed. + */ + LCB_COMPRESS_OUT = 1 << 1, + + + LCB_COMPRESS_INOUT = (LCB_COMPRESS_IN|LCB_COMPRESS_OUT), + + /** + * By default the library will send a HELLO command to the server to + * determine whether compression is supported or not. Because commands may + * be pipelined prior to the scheduing of the HELLO command it is possible + * that the first few commands may not be compressed when schedule due to + * the library not yet having negotiated settings with the server. Setting + * this flag will force the client to assume that all servers support + * compression despite a HELLO not having been intially negotiated. + */ + LCB_COMPRESS_FORCE = 1 << 2 +} lcb_COMPRESSOPTS; + +/** + * @committed + * + * @brief Control how the library handles compression and deflation to and from + * the server. + * + * Starting in Couchbase Server 3.0, compression can optionally be applied to + * incoming and outcoming data. For incoming (i.e. `GET` requests) the data + * may be received in compressed format and then allow the client to inflate + * the data upon receipt. For outgoing (i.e. `SET` requests) the data may be + * compressed on the client side and then be stored and recognized on the + * server itself. + * + * The default behavior is to transparently handle compression for both incoming + * and outgoing data. + * + * Note that if the lcb_STORECMDv0::datatype field is set with compression + * flags, the data will _never_ be compressed by the library as this is an + * indication that it is _already_ compressed. + * + * @cntl_arg_both{`int*` (value is one of @ref lcb_COMPRESSOPTS)} + */ +#define LCB_CNTL_COMPRESSION_OPTS 0x26 + + +struct rdb_ALLOCATOR; +typedef struct rdb_ALLOCATOR* (*lcb_RDBALLOCFACTORY)(void); + +/**Structure being used because function pointers can't technically be cast + * to void* + */ +struct lcb_cntl_rdballocfactory { + lcb_RDBALLOCFACTORY factory; +}; +/** + * @volatile + * Set the allocator factory used by libcouchbase. The allocator factory is + * a function invoked with no arguments which yields a new rdb_ALLOCATOR + * object. Currently the use and API of this object is considered internal + * and its API and header files are in `src/rdb`. + * + * Mode|Arg + * ----|--- + * Set, Get | `lcb_cntl_rdballocfactory*` + */ +#define LCB_CNTL_RDBALLOCFACTORY 0x27 + + +typedef enum { + LCB_IPV6_DISABLED = 0x00, LCB_IPV6_ONLY = 0x1, LCB_IPV6_ALLOW = 0x02 +} lcb_ipv6_t; + +/** + * @brief IPv4/IPv6 selection policy + * + * Setting which controls whether hostname lookups should prefer IPv4 or IPv6 + * + * @cntl_arg_both{lcb_ipv6_t*} + * @uncommitted + */ +#define LCB_CNTL_IP6POLICY 0x0b + + +/** + * @volatile + * @brief Persist heuristic vbucket information across updates. + * + * As of version 2.4.8 this option no longer has any effect, and vBucket + * heuristics are always retained for a maximum of 20 seconds. + * @cntl_arg_both{int*} + */ +#define LCB_CNTL_VBGUESS_PERSIST 0x32 + +/** + * @volatile + * This is a collection of various options which sacrifice data safety for + * speed. + */ +#define LCB_CNTL_UNSAFE_OPTIMIZE 0x33 + +/** + * @volatile + * Disable or enable Nagle's algorithm. The default is to disable it, as it + * will typically reduce latency. In general it is recommended not to touch + * this setting. It is here mainly for debugging. + * + * Conventionally, the option to disable Nagle's algorithm is called "TCP_NODELAY", + * thus if this value is one, Nagle is off, and vice versa. + */ +#define LCB_CNTL_TCP_NODELAY 0x39 + +/** + * @volatile + * Get the lcb_HISTOGRAM object for key-value timings + * @cntl_arg_getonly{lcb_HISTOGRAM**} + */ +#define LCB_CNTL_KVTIMINGS 0x3C + +/**@}*/ diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/cntl.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/cntl.h new file mode 100644 index 00000000..20a9c018 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/cntl.h @@ -0,0 +1,937 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * @file + * @brief Command codes for libcouchbase. + * + * @details + * These codes may be passed to 'lcb_cntl'. + * + * Note that the constant values are also public API; thus allowing forwards + * and backwards compatibility. + */ + +#ifndef LCB_CNTL_H +#define LCB_CNTL_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @ingroup lcb-cntl + * @defgroup lcb-cntl-settings Setting List + * @brief Adjust tunables for the client + * @details + * + * The constants in this file are used to control the behavior of the library. + * All of the operations above may be passed as the `cmd` parameter to the + * lcb_cntl() function, thus: + * + * @code{.c} + * char something; + * lcb_error_t rv; + * rv = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_FOO, &something); + * @endcode + * + * will retrieve the setting of `LCB_CNTL_FOO` into `something`. + * + * You may also use the lcb_cntl_string() function, which operates on + * strings and can set various configuration properties fairly simply. Note + * however that string names are subject to change, and not all configuration + * directives have a string alias: + * + * @code{.c} + * rv = lcb_cntl_string("operation_timeout", "5.0"); + * @endcode + * + * Of the commands listed below, some will be read-only (i.e. you may only + * _read_ the setting using the @ref LCB_CNTL_GET `mode`), some will be write-only + * (i.e. you may only _modify_ the setting, and use @ref LCB_CNTL_SET for the `mode`) + * and some will be both readable and writable. + * + * Along the documentation of each specific command, there is a table displaying + * the modes supported and the expected pointer type to be passed as the `arg` + * value into lcb_cntl(). Note that some read-write commands require different + * pointer types depending on whether the `mode` is retrieval or storage. + * + * + * @section lcb-timeout-info Timeout Settings + * + * Timeout settings control how long the library will wait for a certain event + * before proceeding to the next course of action (which may either be to try + * a different operation or fail the current one, depending on the specific + * timeout). + * + * Timeouts are specified in _microseconds_ stored within an `lcb_U32`. + * + * Note that timeouts in libcouchbase are implemented via an event loop + * scheduler. As such their accuracy and promptness is limited by how + * often the event loop is invoked and how much wall time is spent in + * each of their handlers. Specifically if you issue long running blocking + * calls within any of the handlers (and this means any of the library's + * callbacks) then the timeout accuracy will be impacted. + * + * Further behavior is dependent on the event loop plugin itself and how + * it schedules timeouts. + * + * + * @par Configuration Stability Attributes + * Configuration parameters are still subject to the API classification used + * in @ref lcb_attributes. For _deprecated_ control commands, lcb_cntl() will + * either perform the operation, _or_ consider it a no-op, _or_ return an error + * code. + */ + +/** + * @addtogroup lcb-cntl-settings + * @{ + */ + +/** + * @name Modes + * Modes for the lcb_cntl() `mode` argument + * @{ + */ +#define LCB_CNTL_SET 0x01 /**< @brief Modify a setting */ +#define LCB_CNTL_GET 0x00 /**< @brief Retrieve a setting */ +/**@}*/ + +/** + * @brief Operation Timeout + * + * The operation timeout is the maximum amount of time the library will wait + * for an operation to receive a response before invoking its callback with + * a failure status. + * + * An operation may timeout if: + * + * * A server is taking too long to respond + * * An updated cluster configuration has not been promptly received + * + * @code{.c} + * lcb_U32 tmo = 3500000; + * lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_OP_TIMEOUT, &tmo); + * @endcode + * + * @cntl_arg_both{lcbU32*} + * @committed + * @see lcb-timeout-info + */ +#define LCB_CNTL_OP_TIMEOUT 0x00 + +/** + * @brief Views Timeout + * This is the I/O timeout for HTTP requests issues with LCB_HTTP_TYPE_VIEWS + * + * @cntl_arg_both{lcb_U32*} + * @committed + */ +#define LCB_CNTL_VIEW_TIMEOUT 0x01 + +/** + * @brief N1QL Timeout + * This is the I/O timeout for N1QL queries, issued via lcb_n1ql_query() + * + * @cntl_arg_both{lcb_U32*} + * @committed + */ +#define LCB_CNTL_N1QL_TIMEOUT 0x3D + +/** + * @brief Get the name of the bucket + * This returns the name of the bucket this instance is connected to, or `NULL` + * if not yet connected to a bucket + * + * @cntl_arg_getonly{`const char*`} + * @committed + */ +#define LCB_CNTL_BUCKETNAME 0x30 + +/** + * @brief Get the handle type. + * This returns the handle type - which is either LCB_TYPE_CLUSTER or + * LCB_TYPE_BUCKET + * + * @cntl_arg_getonly{lcb_type_t*} + * @uncommitted + */ +#define LCB_CNTL_HANDLETYPE 0x04 + +/**@brief Get the vBucket handle. + * Obtains the current cluster configuration from the client. + * + * @cntl_arg_getonly{lcbvb_CONFIG**} + * @uncommitted + */ +#define LCB_CNTL_VBCONFIG 0x05 + +/**@brief Get the iops implementation instance + * + * @cntl_arg_getonly{lcb_io_opt_t*} + * @uncommitted + */ +#define LCB_CNTL_IOPS 0x06 + + +/** @brief Structure containing mapping information for a key */ +typedef struct lcb_cntl_vbinfo_st { + int version; + + union { + /** v0 */ + struct { + const void *key; /**< **Input** Key */ + lcb_SIZE nkey; /**< **Input** Length of key */ + int vbucket; /**< **Output** Mapped vBucket */ + int server_index; /**< **Output** Server index for vBucket */ + } v0; + } v; +} lcb_cntl_vbinfo_t; + +/** + * @brief Get the vBucket ID for a given key, based on the current configuration + * + * @cntl_arg_getonly{lcb_cntl_vbinfo_t*} + * @committed + */ +#define LCB_CNTL_VBMAP 0x07 + +/** + * @brief Configuration error threshold. + * + * This number indicates how many + * network/mapping/not-my-vbucket errors are received before a configuration + * update is requested again. + * + * @cntl_arg_both{lcb_SIZE*} + * @uncommitted + */ +#define LCB_CNTL_CONFERRTHRESH 0x0c + +/** + * @brief Default timeout for lcb_durability_poll() + * @ingroup lcb-timeout-info + * + * This is the time the client will + * spend sending repeated probes to a given key's vBucket masters and replicas + * before they are deemed not to have satisfied the durability requirements + * + * @cntl_arg_both{lcb_U32*} + * @committed + */ +#define LCB_CNTL_DURABILITY_TIMEOUT 0x0d + +/**@brief Polling grace interval for lcb_durability_poll() + * + * This is the time the client will wait between repeated probes to + * a given server. + * + * @cntl_arg_both{lcb_U32*} + * @committed*/ +#define LCB_CNTL_DURABILITY_INTERVAL 0x0e + +/**@brief Timeout for non-views HTTP requests + * @cntl_arg_both{lcb_U32*} + * @committed*/ +#define LCB_CNTL_HTTP_TIMEOUT 0x0f + +/** + * @brief Print verbose plugin load information to console + * + * This modifies a static, global setting regarding whether to + * print verbose information when trying to dynamically load an IO plugin. + * The information printed can be useful in determining why a plugin failed + * to load. This setting can also be controlled via the + * "LIBCOUCHBASE_DLOPEN_DEBUG" environment variable (and if enabled from the + * environment, will override the setting mentioned here). + * + * @cntl_arg_both{int*} + * + * @note Pass NULL to lcb_cntl for the 'instance' parameter. + * @volatile + */ +#define LCB_CNTL_IOPS_DLOPEN_DEBUG 0x11 + +/**@brief Initial bootstrap timeout. + * This is how long the client will wait to obtain the initial configuration. + * + * @cntl_arg_both{lcb_U32*} + * @committed*/ +#define LCB_CNTL_CONFIGURATION_TIMEOUT 0x12 + +/** + * @brief Randomize order of bootstrap nodes. + * + * This controls whether the connection attempts for configuration retrievals + * should be done in the supplied order or whether they should be randomized. + * + * For the initial connection the supplied order is the list of hosts provided + * in the lcb_create_st structure. For subsequent connections this is the + * order of nodes as received by the server. + * + * @cntl_arg_both{int*} + * @committed + */ +#define LCB_CNTL_RANDOMIZE_BOOTSTRAP_HOSTS 0x14 + +/** + * @brief Determine if file-based configuration has been loaded + * + * If the configuration cache is in use, the argument pointer + * will be set to a true value. If the configuration cache was not used, + * the argument pointer will be set to false. + * + * A false value may indicates that the client will need to load the + * configuration from the network. This may be caused by the following: + * - The configuration cache did not exist or was empty + * - The configuration cache contained stale information + * + * @cntl_arg_getonly{int*} + * @uncommitted + */ +#define LCB_CNTL_CONFIG_CACHE_LOADED 0x15 + +/** + * @brief Force a specific SASL mechanism + * + * Force a specific SASL mechanism to use for authentication. This + * can allow a user to ensure a certain level of security and have the + * connection fail if the desired mechanism is not available. + * + * When setting this value, the arg parameter shall be a + * `NUL`-terminated string or a `NULL` pointer (to unset). When retrieving + * this value, the parameter shall be set to a `char **`. Note that this + * value (in LCB_CNTL_GET) is valid only until the next call to a + * libcouchbase API, after which it may have been freed. + * + * @cntl_arg_get_and_set{char**, char*} + * @uncommitted + */ +#define LCB_CNTL_FORCE_SASL_MECH 0x16 + +/** + * @brief Maximum number of HTTP redirects to follow + * Set how many redirects the library should follow for the single request. + * Set to -1 to remove limit at all. + * + * @cntl_arg_both{int*} + * @uncommitted + */ +#define LCB_CNTL_MAX_REDIRECTS 0x17 + +/** + * @name Logging + * + * Verbose logging may be enabled by default using the environment variable + * `LCB_LOGLEVEL` and setting it to a number > 1; higher values produce more + * verbose output. The maximum level is `5`. + * + * You may also install your own logger using lcb_cntl() and the + * @ref LCB_CNTL_LOGGER constant. Note that + * the logger functions will not be called rapidly from within hot paths. + * @{ + */ + +/** @brief Logging Levels */ +typedef enum { LCB_LOG_TRACE = 0, LCB_LOG_DEBUG, LCB_LOG_INFO, LCB_LOG_WARN, + LCB_LOG_ERROR, LCB_LOG_FATAL, LCB_LOG_MAX +} lcb_log_severity_t; + +struct lcb_logprocs_st; + +/** + * @brief Logger callback + * + * @uncommitted + * + * This callback is invoked for each logging message emitted + * @param procs the logging structure provided + * @param iid instance id + * @param subsys a string describing the module which emitted the message + * @param severity one of the LCB_LOG_* severity constants. + * @param srcfile the source file which emitted this message + * @param srcline the line of the file for the message + * @param fmt a printf format string + * @param ap a va_list for vprintf + */ +typedef void (*lcb_logging_callback)(struct lcb_logprocs_st *procs, + unsigned int iid, const char *subsys, int severity, const char *srcfile, + int srcline, const char *fmt, va_list ap); + +/** + * @brief Logging context + * @uncommitted + * + * This structure defines the logging handlers. Currently there is only + * a single field defined which is the default callback for the loggers. + * This API may change. + */ +typedef struct lcb_logprocs_st { + int version; + union { struct { lcb_logging_callback callback; } v0; } v; +} lcb_logprocs; + +/** + * @brief Access the lcb_logprocs structure + * @uncommitted + * + * The lcb_logoprocs structure passed must not be freed until the instance + * is completely destroyed. This will only happen once the destruction + * callback is called (see lcb_set_destroy_callback()). + * + * @cntl_arg_get_and_set{lcb_logprocs**,lcb_logprocs*}*/ +#define LCB_CNTL_LOGGER 0x18 +/**@}*/ + + +/** + * @brief Refresh Throttling + * + * Modify the amount of time (in microseconds) before the + * @ref LCB_CNTL_CONFERRTHRESH will forcefully be set to its maximum + * number forcing a configuration refresh. + * + * Note that if you expect a high number of timeouts in your operations, you + * should set this to a high number (along with `CONFERRTHRESH`). If you + * are using the default timeout setting, then this value is likely optimal. + * + * @cntl_arg_both{lcb_U32*} + * @see LCB_CNTL_CONFERRTHRESH + * @uncommitted + */ +#define LCB_CNTL_CONFDELAY_THRESH 0x19 + +/**@brief Get the transport used to fetch cluster configuration. + * @cntl_arg_getonly{lcb_config_transport_t*} + * @uncommitted*/ +#define LCB_CNTL_CONFIG_TRANSPORT 0x1A + +/** + * @brief Per-node configuration timeout. + * + * The per-node configuration timeout sets the amount of time to wait + * for each node within the bootstrap/configuration process. This interval + * is a subset of the @ref LCB_CNTL_CONFIGURATION_TIMEOUT + * option mentioned above and is intended + * to ensure that the bootstrap process does not wait too long for a given + * node. Nodes that are physically offline may never respond and it may take + * a long time until they are detected as being offline. + * See CCBC-261 and CCBC-313 for more reasons. + * + * @note the `CONFIGURATION_TIMEOUT` should be higher than this number. + * No check is made to ensure that this is the case, however. + * + * @cntl_arg_both{lcb_U32*} + * @see LCB_CNTL_CONFIGURATION_TIMEOUT + * @committed + */ +#define LCB_CNTL_CONFIG_NODE_TIMEOUT 0x1B + +/** + * @brief Idling/Persistence for HTTP bootstrap + * + * By default the behavior of the library for HTTP bootstrap is to keep the + * stream open at all times (opening a new stream on a different host if the + * existing one is broken) in order to proactively receive configuration + * updates. + * + * The default value for this setting is -1. Changing this to another number + * invokes the following semantics: + * + * - The configuration stream is not kept alive indefinitely. It is kept open + * for the number of seconds specified in this setting. The socket is closed + * after a period of inactivity (indicated by this setting). + * + * - If the stream is broken (and no current refresh was requested by the + * client) then a new stream is not opened. + * + * @cntl_arg_both{lcb_U32*} + * @volatile + */ +#define LCB_CNTL_HTCONFIG_IDLE_TIMEOUT 0x1C + +/**@brief Get the current SCM changeset for the library binary + * @cntl_arg_getonly{char**} + * @committed*/ +#define LCB_CNTL_CHANGESET 0x1F + +/** + * @brief file used for the configuration cache. + * + * The configuration + * cache allows to bootstrap from a cluster without using the initial + * bootstrap connection, considerably reducing latency. If the file passed + * does not exist, the normal bootstrap process is performed and the file + * is written to with the current information. + * + * @note The leading directories for the file must exist, otherwise the file + * will never be created. + * + * @note Configuration cache is not supported for memcached buckets + * @cntl_arg_get_and_set{char**, char*} + * @uncommitted + * @see LCB_CNTL_CONFIG_CACHE_LOADED + */ +#define LCB_CNTL_CONFIGCACHE 0x21 + +/** + * @brief File used for read-only configuration cache + * + * This is identical to the @ref LCB_CNTL_CONFIGCACHE directive, except that + * it guarantees that the library will never overwrite or otherwise modify + * the path specified. + * + * @see LCB_CNTL_CONFIGCACHE + */ +#define LCB_CNTL_CONFIGCACHE_RO 0x36 + +typedef enum { + LCB_SSL_ENABLED = 1 << 0, /**< Use SSL */ + LCB_SSL_NOVERIFY = 1 << 1, /**< Don't verify certificates */ + LCB_SSL_NOGLOBALINIT = 1 << 2 /**< Do not call SSL's global init functions */ +} lcb_SSLOPTS; + +/** + * @brief Get SSL Mode + * + * Retrieve the SSL mode currently in use by the library. This is a read-only + * setting. To set the SSL mode at the library, specify the appropriate values + * within the connection string. See @ref lcb_create_st3 for details. + * + * @cntl_arg_getonly{`int*` (value is one of @ref lcb_SSLOPTS)} + */ +#define LCB_CNTL_SSL_MODE 0x22 + +/** + * @brief Get SSL Certificate path + * + * Retrieve the path to the CA certificate (if any) being used. + * + * @cntl_arg_getonly{`char**`} + * @see LCB_CNTL_SSL_MODE + */ +#define LCB_CNTL_SSL_CERT 0x23 +/* For back compat */ +#define LCB_CNTL_SSL_CACERT LCB_CNTL_SSL_CERT + +/** + * @brief + * Select retry mode to manipulate + */ +typedef enum { + LCB_RETRY_ON_TOPOCHANGE = 0, /**< Select retry for topology */ + LCB_RETRY_ON_SOCKERR, /**< Select retry for network errors */ + LCB_RETRY_ON_VBMAPERR, /**< Select retry for NOT_MY_VBUCKET responses */ + + /** Retry when there is no node for the item. This case is special as the + * `cmd` setting is treated as a boolean rather than a bitmask*/ + LCB_RETRY_ON_MISSINGNODE, + LCB_RETRY_ON_MAX /**<< maximum index */ +} lcb_RETRYMODEOPTS; + +typedef enum { + /**Don't retry any commands. A command which has been forwarded to + * a server and a not-my-vbucket has been received in response for it + * will result in a failure.*/ + LCB_RETRY_CMDS_NONE = 0, + + /**Only retry simple retrieval operations (excludes touch, + * get-and-touch, and get-locked) which may be retried many numbers of times + * without risking unintended data manipulation. */ + LCB_RETRY_CMDS_GET = 0x01, + + /**Retry operations which may potentially fail because they have been + * accepted by a previous server, but will not silently corrupt data. + * Such commands include mutation operations containing a CAS.*/ + LCB_RETRY_CMDS_SAFE = 0x03, /* Includes 'GET', plus a new flag (e.g. 0x02|0x01) */ + + /**Retry all commands, disregarding any potential unintended receipt of + * errors or data mutation.*/ + LCB_RETRY_CMDS_ALL = 0x07 /* e.g. 0x01|0x03| NEW FLAG: 0x04 */ +} lcb_RETRYCMDOPTS; + +/**@brief Create a retry setting value + * @param mode the mode to set (@see lcb_RETRYMODEOPTS) + * @param policy the policy determining which commands should be retried + * (@see lcb_RETRYCMDOPTS) + * @return a value which can be assigned to an `lcb_U32` and passed to + * the @ref LCB_CNTL_RETRYMODE setting + */ +#define LCB_RETRYOPT_CREATE(mode, policy) (((mode) << 16) | policy) + +#define LCB_RETRYOPT_GETMODE(u) (u) >> 16 +#define LCB_RETRYOPT_GETPOLICY(u) (u) & 0xffff + +/** + * @volatile + * + * @brief Set retry policies + * + * This function sets the retry behavior. The retry behavior is the action the + * library should take when a command has failed because of a failure which + * may be a result of environmental and/or topology issues. In such cases it + * may be possible to retry the command internally and have it succeed a second + * time without propagating an error back to the application. + * + * The behavior consists of a _mode_ and _command_ selectors. The _command_ + * selector indicates which commands should be retried (and which should be + * propagated up to the user) whereas the _mode_ indicates under which + * circumstances should the _command_ policy be used. + * + * Disable retries anywhere: + * @code{.c} + * for (int ii = 0; ii < LCB_RETRY_ON_MAX; ++ii) { + * lcb_U32 val = LCB_RETRYOPT_CREATE(ii, LCB_RETRY_CMDS_NONE); + * lcb_error_t err = lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_RETRYMODE, &val); + * } + * @endcode + * + * Only retry simple GET operations when retry is needed because of topology + * changes: + * @code{.c} + * lcb_U32 val = LCB_RETRYOPT_CREATE(LCB_RETRY_ON_TOPOCHANGE, LCB_RETRY_CMDS_GET); + * lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_RETRYMODE, &val); + * @endcode + * + * Determine the behavior of the library when a `NOT_MY_VBUCKET` is received: + * @code{.c} + * lcb_U32 val = LCB_RETRYOPT_CREATE(LCB_RETRY_ON_VBMAPERR, 0); + * lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_RETRYMODE, &val); + * lcb_U32 policy = LCB_RETRYOPT_GETPOLICY(val); + * @endcode + * + * @cntl_arg_both{`lcb_U32 *`} + */ +#define LCB_CNTL_RETRYMODE 0x24 + +/** + * @brief Enumeration representing various URL forms to use for the configuration + * stream */ +typedef enum { + /** `/pools/default/b[s]/$bucket`: Introduced in Couchbase Server 2.5 */ + LCB_HTCONFIG_URLTYPE_25PLUS = 0x01, + + /** `/pools/default/buckets[Streaming]/$bucket`. */ + LCB_HTCONFIG_URLTYPE_COMPAT = 0x02, + + /** Try `25PLUS` first and fallback to `COMPAT` */ + LCB_HTCONFIG_URLTYPE_TRYALL = 0x03 +} lcb_HTCONFIG_URLTYPE; + +/** + * @volatile - Primarily here to support tests and buggy HTTP servers/proxies + * which do not like to maintain a connection upon receipt of a 404. + * + * @brief Set the URL selection mode. + * + * The URL type can be a mask of the lcb_HTCONFIG_URLTYPE constants which + * indicate which URLs the HTTP provider should use. + * + * The default is to use the `25PLUS` URI first, and fallback on the compat uri + * if the terse one fails with an HTTP 404 (Not Found). The new-style URI is + * considered more efficient on cluster resources and can help the cluster + * maintain many more streaming connections than the compat version, however + * it is only available in Couchbase Server 2.5 and greater. + * + * This setting is only used when CCCP is disabled. This will typically be for + * older clusters or for memcached buckets. + * @cntl_arg_both{`int*` (value is one of @ref lcb_HTCONFIG_URLTYPE)} + */ +#define LCB_CNTL_HTCONFIG_URLTYPE 0x25 + +/** + * @volatile + * Determines whether to run the event loop internally within lcb_destroy() + * until no more I/O resources remain for the library. This is usually only + * necessary if you are creating a lot of instances and/or are using memory + * leak analysis tools. + * + * @cntl_arg_both{`int*` (as a boolean)} + * @see lcb_destroy_async() and lcb_set_destroy_callback() + */ +#define LCB_CNTL_SYNCDESTROY 0x28 + +/** + * @committed + * + * Sets the logging level for the console logger. If a logger is already + * initialized (either from the environment, or via lcb_cntl_logger() then + * this operation does nothing. + * + * This is mainly useful for applications which want to proxy the built in + * logging options via command line options and the like, rather than setting + * it from the environment. + * + * The argument passed to lcb_cntl() is an integer of 0 until + * `LCB_LOGLEVEL_MAX`, though the actual type is of `lcb_U32` rather than + * an enum type. + * + * @cntl_arg_setonly{const lcb_U32 *} + * @see LCB_CNTL_LOGGER + */ +#define LCB_CNTL_CONLOGGER_LEVEL 0x29 + +/** + * @uncommitted + * + * Sets the output file (as a `FILE*`) for the console logger. Note that + * any existing file pointer will be cleared (but not `fclose()`d. + * + * If used with lcb_cntl_string(), (using the `console_log_file` parameter), + * the third argument is taken as the _name_ of a file. Note that the user + * is responsible for closing the file. + * + * This setting does not require a library handle and therefore the first + * argument to lcb_cntl() should be `NULL`. + * + * + * @cntl_arg_get_and_set{`FILE**`, `FILE*`} + * @see LCB_CNTL_LOGGER + * @see LCB_CNTL_CONLOGGER_LEVEL + */ +#define LCB_CNTL_CONLOGGER_FP 0x3B + +/** + * @committed + * + * Sets the behavior for reporting network errors. By default network errors + * are returned as `LCB_NETWORK_ERROR` return codes for compatibility reasons. + * More detailed error codes may be available by enabling this option which will + * return appropriate error codes which have a category of + * @ref LCB_ERRTYPE_NETWORK + * + * Using this option means your programming model is centered around the various + * LCB_EIF* macros (see ) rather than individual codes. + * + * @cntl_arg_both{int * (As a boolean)} + */ +#define LCB_CNTL_DETAILED_ERRCODES 0x2A + +/** + * @uncommitted + * + * Sets the interval at which the retry queue will attempt to resend a failed + * operation. When an operation fails and the retry policy (see + * @ref LCB_CNTL_RETRYMODE) allows the operation to be retried, it shall be + * placed into a queue, and then be retried within a given interval. + * + * Setting a high value will be friendlier on the network but also potentially + * increase latency, while setting this to a low value may cause unnecessary + * network traffic for operations which are not yet ready to be retried. + * + * @cntl_arg_both{lcb_U32* (microseconds)} + * + * @see LCB_CNTL_RETRY_BACKOFF + */ +#define LCB_CNTL_RETRY_INTERVAL 0x2C + +/** + * @uncommitted + * + * When an operation has been retried more than once and it has still not + * succeeded, the library will attempt to back off for the operation by + * scheduling it to be retried in `LCB_CNTL_RETRY_INTEVAL * ${n}` microseconds, + * where `${n}` is the factor controlled by this setting. + * + * @cntl_arg_both{float*} + */ +#define LCB_CNTL_RETRY_BACKOFF 0x2D + +/** + * @volatile + * Whether commands are retried immediately upon receipt of not-my-vbucket + * replies. + * + * Since version 2.4.8, packets by default are retried immediately on a + * different node if it had previously failed with a not-my-vbucket + * response, and is thus not subject to the @ref LCB_CNTL_RETRY_INTERVAL + * and @ref LCB_CNTL_RETRY_BACKOFF settings. Disabling this setting will + * restore the older behavior. This may be used in case there are problems + * with the default heuristic/retry algorithm. + */ +#define LCB_CNTL_RETRY_NMV_IMM 0x37 + +/** + * @volatile + * + * Set the maximum pool size for pooled http (view request) sockets. This should + * be set to 1 (the default) unless you plan to execute concurrent view requests. + * You may set this to 0 to disable pooling + * + * @cntl_arg_both{lcb_SIZE} + */ +#define LCB_CNTL_HTTP_POOLSIZE 0x2E + +/** + * @uncomitted + * Determine whether or not a new configuration should be received when an error + * is received over the HTTP API (i.e. via lcb_make_http_request(). + * + * The default value is true, however you may wish to disable this if you are + * expectedly issuing a lot of requests which may result in an error. + * + * @cntl_arg_both{int (as boolean)} + */ +#define LCB_CNTL_HTTP_REFRESH_CONFIG_ON_ERROR 0x2F + +/** + * @volatile + * Set the behavior of the lcb_sched_leave() API call. By default the + * lcb_sched_leave() will also set up the necessary requirements for flushing + * to the network. If this option is off then an explicit call to + * lcb_sched_flush() must be performed instead. + * + * @cntl_arg_both{int (as boolean)} + */ +#define LCB_CNTL_SCHED_IMPLICIT_FLUSH 0x31 + +/** + * @volatile + * + * Allow the server to return an additional 16 bytes of data for each + * mutation operation. This extra information may help with more reliable + * durability polling, but will also increase the size of the response packet. + * + * This should be set on the instance before issuing lcb_connect(). While this + * may also be set after lcb_connect() is called, it will currently only take + * effect when a server reconnects (which itself may be undefined). + * + * @cntl_arg_both{int (as boolean)} + */ +#define LCB_CNTL_FETCH_MUTATION_TOKENS 0x34 + +/** + * @volatile + * + * This setting determines whether the lcb_durability_poll() function will + * transparently attempt to use mutation token functionality (rather than checking + * the CAS). This option is most useful for older code which does + * explicitly use mutation tokens but would like to use its benefits when + * ensuring durability constraints are satisfied. + * + * This option is enabled by default. Users may wish to disable this if they + * are performing durability operations against items stored from different + * client instances, as this will make use of a client-global state which is + * derived on a per-vBucket basis. This means that the last mutation performed + * on a given vBucket for the client will be used, which in some cases may be + * older or newer than the mutations passed to the lcb_durability_poll() + * function. + * + * @cntl_arg_both{int (as boolean)} + */ +#define LCB_CNTL_DURABILITY_MUTATION_TOKENS 0x35 + +/** + * @volatile + * + * This read-only property determines if the mutation token mechanism is supported + * on the cluster itself. This will only be accurate once a single operation + * has been performed on the cluster - or in other words, once a connection + * to a data node has been established for the purposes of normal operations. + * + * @cntl_arg_getonly{int (as boolean)} + */ +#define LCB_CNTL_MUTATION_TOKENS_SUPPORTED 0x38 + + +/** + * @uncommitted + * This setting determines if calls to lcb_wait() and lcb_wait3() will reset + * the timeout of pending operations to the time that lcb_wait() was called, + * rather than having the operation maintain the time of the call which + * scheduled it. If the time between lcb_store3() and family and the lcb_wait() + * functions is long, it is recommended to disable this setting in order to + * avoid prematurely having operations time out. + * + * @cntl_arg_both{int (as boolean)} + * + * Use `"readj_wait_tmo"` for the string version + */ +#define LCB_CNTL_RESET_TIMEOUT_ON_WAIT 0x3A + +/** + * @volatile + * Clears the internal prepared statement cache for N1QL + * + * This does not take any arguments, and is valid only on @ref LCB_CNTL_SET + */ +#define LCB_CNTL_N1QL_CLEARACHE 0x3E + +/** + * @committed + * Sets additional text for negotiation. This allows wrappers or applications + * to add additional identifying information which can then be seen in the + * server logs. + * + * @cntl_arg_get_and_set{`const char**`, `const char*`} + * + * Use `"client_string"` for the string version + */ +#define LCB_CNTL_CLIENT_STRING 0x3F + +typedef const char *lcb_BUCKETCRED[2]; + +/** + * Set credentials for a bucket. This is used for N1QL and CBFT APIs to allow + * access to multiple buckets. It can also be used to set the password + * of the current bucket when reconnecting (in case it changes). + * + * The format for the credentials is an array of two nul-terminated strings, + * the first refers to the bucket and the second refers to the password. + */ +#define LCB_CNTL_BUCKET_CRED 0x40 + +/** + * Set the amount of time the client should wait before retrying a + * not-my-vbucket response packet. The default is 100ms. The value should + * be specified in microseconds + * + * @committed + * @cntl_arg_both{lcb_U32*} + * + * Use `"retry_nmv_interval"` with lcb_cntl_string() + */ +#define LCB_CNTL_RETRY_NMV_INTERVAL 0x41 + +/** + * Limit the number of bytes to be read (and thereby processed) during I/O + * read operations. This setting may be useful when the network is faster than + * processing resources. + * + * @note This setting only works for event-style I/O plugins. This means it + * has no effect on completion style plugins such as libuv or Windows IOCP + * + * @committed + * @cntl_arg_both{lcb_U32*} + */ +#define LCB_CNTL_READ_CHUNKSIZE 0x42 + +/** This is not a command, but rather an indicator of the last item */ +#define LCB_CNTL__MAX 0x43 +/**@}*/ + +#ifdef __cplusplus +} +#endif + +#include "cntl-private.h" + +#endif /* LCB_CNTL_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/configuration.h.in b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/configuration.h.in new file mode 100644 index 00000000..d897fabf --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/configuration.h.in @@ -0,0 +1,23 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBCOUCHBASE_CONFIGURATION_H +#define LIBCOUCHBASE_CONFIGURATION_H 1 +#define LCB_VERSION_STRING "@LCB_VERSION@" +#define LCB_VERSION @LCB_VERSION_HEX@ +#define LCB_VERSION_CHANGESET "@LCB_VERSION_CHANGESET@" +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/couchbase.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/couchbase.h new file mode 100644 index 00000000..3e8ce717 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/couchbase.h @@ -0,0 +1,3690 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBCOUCHBASE_COUCHBASE_H +#define LIBCOUCHBASE_COUCHBASE_H 1 + +/** + * @file + * Main header file for Couchbase + */ + +#define LCB_CONFIG_MCD_PORT 11210 +#define LCB_CONFIG_MCD_SSL_PORT 11207 +#define LCB_CONFIG_HTTP_PORT 8091 +#define LCB_CONFIG_HTTP_SSL_PORT 18091 +#define LCB_CONFIG_MCCOMPAT_PORT 11211 + +struct lcb_st; + +/** + * @ingroup lcb-init + * Library handle representing a connection to a data bucket. The contents + * of this structure are opaque. + * @see lcb_create + * @see lcb_destroy + */ +typedef struct lcb_st *lcb_t; +struct lcb_http_request_st; +typedef struct lcb_http_request_st *lcb_http_request_t; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C"{ +#endif + +typedef lcb_U8 lcb_datatype_t; +typedef lcb_U32 lcb_USECS; + + +/****************************************************************************** + ****************************************************************************** + ****************************************************************************** + ** INITIALIZATION ** + ****************************************************************************** + ****************************************************************************** + ******************************************************************************/ + +/** + * @ingroup lcb-public-api + * @defgroup lcb-init Initialization + * + * @details + * + * To communicate with a Couchbase cluster, a new library handle instance is + * created in the form of an lcb_t. To create such an object, the lcb_create() + * function is called, passing it a structure of type lcb_create_st. The structure + * acts as a container for a union of other structures which are extended + * as more features are added. This container is forwards and backwards + * compatible, meaning that if the structure is extended, you code and application + * will still function if using an older version of the structure. The current + * sub-field of the lcb_create_st structure is the `v3` field. + * + * Connecting to the cluster involes the client knowing the necessary + * information needed to actually locate its services and connect to it. + * + * A connection specification consists of: + * + * 1. One or more hosts which comprise the cluster + * 2. The name of the bucket to access and perform operations on + * 3. The credentials of the bucket + * + * All these options are specified within the form of a URI in the form of + * + * `couchbase://$HOSTS/$BUCKET?$OPTIONS` + * + * @note + * If any of the fields (hosts, bucket, options) contain the `/` character then + * it _must_ be url-encoded; thus a bucket named `foo/bar` would be specified + * as `couchbase:///foo%2Fbar` + * + * @par Hosts + * + * In the most typical use case, you would specify a list of several hostnames + * delimited by a comma (`,`); each host specified should be a member of the + * cluster. The library will use this list to initially connect to the cluster. + * + * Note that it is not necessary to specify _all_ the nodes of the cluster as in + * a normal situation the library will only initially connect to one of the nodes. + * Passing multiple nodes increases the chance of a connection succeeding even + * if some of the nodes are currently down. Once connected to the cluster, the + * client will update itself with the other nodes actually found within the + * cluster and discard the list passed to it + * + * You can specify multiple hosts like so: + * + * `couchbase://foo.com,bar.com,baz.com` + * + * Or a single host: + * + * `couchbase://localhost` + * + * #### Specifying Ports and Protocol Options + * + * The default `couchbase://` scheme will assume all hosts and/or ports + * specify the _memcached_ port. If no port is specified, it is assumed + * that the port is _11210). For more extended options there are additional + * schemes available: + * + * * `couchbases://` - Will assume all ports refer to the SSL-enabled memcached + * ports. This setting implicitly enables SSL on the instance as well. If no + * ports are provided for the hosts, the implicit port for each host will be + * _11207_. + * + * * `http://` - Will assume all ports refer to the HTTP REST API ports used + * by Couchbase 2.2 and lower. These are also used when connecting to a + * memcached bucket. If no port is specified it will be assumed the port is + * _8091_. + * + * ### Bucket + * + * A bucket may be specified by using the optional _path_ component of the URI + * For protected buckets a password will still need to be supplied out of band. + * + * * `couchbase://1.1.1.1,2.2.2.2,3.3.3.3/users` - Connect to the `users` + * bucket. + * + * ### Options + * + * @warning The key-value options here are considered to be a volatile interface + * as their names may change. + * + * Options can be specified as the _query_ part of the connection string, + * for example: + * + * `couchbase://cbnode.net/beer?operation_timeout=10000000`. + * + * Options may either be appropriate _key_ parameters for lcb_cntl_string() + * or one of the following: + * + * * `boostrap_on` - specify bootstrap protocols. Values can be `http` to force + * old-style bootstrap mode for legacy clusters, `cccp` to force bootstrap + * over the memcached port (For clusters 2.5 and above), or `all` to try with + * _cccp_ and revert to _http_ + * + * * `certpath` - Specify the path (on the local filesystem) to the server's + * SSL certificate. Only applicable if SSL is being used (i.e. the scheme is + * `couchbases`) + * + * ### Bucket Identification and Credentials + * + * The most common settings you will wish to modify are the bucket name + * and the credentials field (`user` and `passwd`). If a + * `bucket` is not specified it will revert to the `default` bucket (i.e. the + * bucket which is created when Couchbase Server is installed). + * + * The `user` and `passwd` fields authenticate for the bucket. This is only + * needed if you have configured your bucket to employ SASL auth. You can tell + * if the bucket has been configured with SASL auth by + * + * 1. Logging into the Couchbase Administration Console + * 2. Going to the _Data Buckets_ tab + * 3. Locate the row for your bucket + * 4. Expand the row into the detailed view (by clicking on the arrow at the + * left of the row) + * 5. Click on _Edit_ + * 6. Inspect the _Access Control_ section in the pop-up + * + * The bucket name is specified as the _path_ portion of the URI. + * + * For security purposes, the _user_ and _passwd_ cannot be specified within + * the URI + * + * + * @note + * You may not change the bucket or credentials after initializing the handle. + * + * #### Bootstrap Options + * + * The default configuration process will attempt to bootstrap first from + * the new memcached configuration protocol (CCCP) and if that fails, use + * the "HTTP" protocol via the REST API. + * + * The CCCP configuration will by default attempt to connect to one of + * the nodes specified on the port 11201. While normally the memcached port + * is determined by the configuration itself, this is not possible when + * the configuration has not been attained. You may specify a list of + * alternate memcached servers by using the 'mchosts' field. + * + * If you wish to modify the default bootstrap protocol selection, you + * can use the `bootstrap_on` option to specify the desired bootstrap + * specification + * to use for configuration (note that the ordering of this array is + * ignored). Using this mechanism, you can disable CCCP or HTTP. + * + * To force only "new-style" bootstrap, you may use `bootstrap_on=cccp`. + * To force only "old-style" bootstrap, use `bootstrap_on=http`. To force the + * default behavior, use `bootstrap_on=all` + * + * + * @addtogroup lcb-init + * @{ + */ + +/** @brief Handle types @see lcb_create_st3::type */ +typedef enum { + LCB_TYPE_BUCKET = 0x00, /**< Handle for data access (default) */ + LCB_TYPE_CLUSTER = 0x01 /**< Handle for administrative access */ +} lcb_type_t; + +#ifndef __LCB_DOXYGEN__ +/* These are definitions for some of the older fields of the `lcb_create_st` + * structure. They are here for backwards compatibility and should not be + * used by new code */ +typedef enum { LCB_CONFIG_TRANSPORT_LIST_END = 0, LCB_CONFIG_TRANSPORT_HTTP = 1, LCB_CONFIG_TRANSPORT_CCCP, LCB_CONFIG_TRANSPORT_MAX } lcb_config_transport_t; +#define LCB_CREATE_V0_FIELDS const char *host; const char *user; const char *passwd; const char *bucket; struct lcb_io_opt_st *io; +#define LCB_CREATE_V1_FIELDS LCB_CREATE_V0_FIELDS lcb_type_t type; +#define LCB_CREATE_V2_FIELDS LCB_CREATE_V1_FIELDS const char *mchosts; const lcb_config_transport_t* transports; +struct lcb_create_st0 { LCB_CREATE_V0_FIELDS }; +struct lcb_create_st1 { LCB_CREATE_V1_FIELDS }; +struct lcb_create_st2 { LCB_CREATE_V2_FIELDS }; +#endif + +/** + * @brief Innser structure for lcb_create(). + */ +struct lcb_create_st3 { + const char *connstr; /**< Connection string */ + const char *username; /**< Username for bucket. Unused as of Server 2.5 */ + const char *passwd; /**< Password for bucket */ + void *_pad_bucket; /**< @private */ + struct lcb_io_opt_st *io; /**< IO Options */ + lcb_type_t type; +}; + +/**@brief Wrapper structure for lcb_create() + * @see lcb_create_st3 */ +struct lcb_create_st { + /** Indicates which field in the @ref lcb_CRST_u union should be used. Set this to `3` */ + int version; + + /**This union contains the set of current and historical options. The + * The #v3 field should be used. */ + union lcb_CRST_u { + struct lcb_create_st0 v0; + struct lcb_create_st1 v1; + struct lcb_create_st2 v2; + struct lcb_create_st3 v3; /**< Use this field */ + } v; + LCB_DEPR_CTORS_CRST +}; + +/** + * @brief Create an instance of lcb. + * @param instance Where the instance should be returned + * @param options How to create the libcouchbase instance + * @return LCB_SUCCESS on success + * + * + * ### Examples + * Create an instance using the default values: + * + * @code{.c} + * lcb_t instance; + * lcb_error_t err = lcb_create(&instance, NULL); + * if (err != LCB_SUCCESS) { + * fprintf(stderr, "Failed to create instance: %s\n", lcb_strerror(NULL, err)); + * exit(EXIT_FAILURE); + * } + * @endcode + * + * Specify server list + * + * @code{.c} + * struct lcb_create_st options; + * memset(&options, 0, sizeof(options)); + * options.version = 3; + * options.v.v3.connstr = "couchbase://host1,host2,host3"; + * err = lcb_create(&instance, &options); + * @endcode + * + * + * Create a handle for data requests to protected bucket + * + * @code{.c} + * struct lcb_create_st options; + * memset(&options, 0, sizeof(options)); + * options.version = 3; + * options.v.v3.host = "couchbase://example.com,example.org/protected" + * options.v.v3.passwd = "secret"; + * err = lcb_create(&instance, &options); + * @endcode + * @committed + * @see lcb_create_st3 + */ +LIBCOUCHBASE_API +lcb_error_t lcb_create(lcb_t *instance, const struct lcb_create_st *options); + +/** + * @brief Schedule the initial connection + * This function will schedule the initial connection for the handle. This + * function _must_ be called before any operations can be performed. + * + * lcb_set_bootstrap_callback() or lcb_get_bootstrap_status() can be used to + * determine if the scheduled connection completed successfully. + * + * @par Synchronous Usage + * @code{.c} + * lcb_error_t rc = lcb_connect(instance); + * if (rc != LCB_SUCCESS) { + * your_error_handling(rc); + * } + * lcb_wait(instance); + * rc = lcb_get_bootstrap_status(instance); + * if (rc != LCB_SUCCESS) { + * your_error_handler(rc); + * } + * @endcode + * @committed + */ +LIBCOUCHBASE_API +lcb_error_t lcb_connect(lcb_t instance); + +/** + * Bootstrap callback. Invoked once the instance is ready to perform operations + * @param instance The instance which was bootstrapped + * @param err The error code received. If this is not LCB_SUCCESS then the + * instance is not bootstrapped and must be recreated + * + * @attention This callback only receives information during instantiation. + * @committed + */ +typedef void (*lcb_bootstrap_callback)(lcb_t instance, lcb_error_t err); + +/** + * @brief Set the callback for notification of success or failure of + * initial connection. + * + * @param instance the instance + * @param callback the callback to set. If `NULL`, return the existing callback + * @return The existing (and previous) callback. + * @see lcb_connect() + * @see lcb_get_bootstrap_status() + */ +LIBCOUCHBASE_API +lcb_bootstrap_callback +lcb_set_bootstrap_callback(lcb_t instance, lcb_bootstrap_callback callback); + +/** + * @brief Gets the initial bootstrap status + * + * This is an alternative to using the lcb_bootstrap_callback() and may be used + * after the initial lcb_connect() and lcb_wait() sequence. + * @param instance + * @return LCB_SUCCESS if properly bootstrapped or an error code otherwise. + * + * @attention + * Calling this function only makes sense during instantiation. + * @committed + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_get_bootstrap_status(lcb_t instance); + +/** + * Sets the authenticator object for the instance. This may be done anytime, but + * should probably be done before calling `lcb_connect()` for best effect. + * + * @param instance the handle + * @param auth the authenticator object used. The library will increase the + * refcount on the authenticator object. + */ +LIBCOUCHBASE_API +void +lcb_set_auth(lcb_t instance, lcb_AUTHENTICATOR *auth); +/**@}*/ + + + +/** + * @ingroup lcb-public-api + * @defgroup lcb-kv-api Key/Value + * + * @brief Preview APIs for performing commands + * + * @details + * Basic command and structure definitions for public API. This represents the + * "V3" API of libcouchbase. This API replaces the legacy API (which now wraps + * this one). It contains common definitions for scheduling, response structures + * and callback signatures. + * + * @addtogroup lcb-kv-api + * @{ + */ + +/** + * @name Creating Commands + * @details + * + * Issuing a command to the Cluster involves selecting the correct command + * structure, populating it with the data relevant for the command, optionally + * associating the command with your own application data, issuing the command + * to a spooling function, and finally receiving the response. + * + * Command structures all derive from the common @ref lcb_CMDBASE structure. This + * structure defines the common fields for all commands. + * + * Almost all commands need to contain a key, which should be assigned using + * the LCB_CMD_SET_KEY() macro. + * + * @{*/ + +#define LCB_CMD_BASE \ + /**Common flags for the command. These modify the command itself. Currently + the lower 16 bits of this field are reserved, and the higher 16 bits are + used for individual commands.*/ \ + lcb_U32 cmdflags; \ + \ + /**Specify the expiration time. This is either an absolute Unix time stamp + or a relative offset from now, in seconds. If the value of this number + is greater than the value of thirty days in seconds, then it is a Unix + timestamp. + + This field is used in mutation operations (lcb_store3()) to indicate + the lifetime of the item. It is used in lcb_get3() with the lcb_CMDGET::lock + option to indicate the lock expiration itself. */ \ + lcb_U32 exptime; \ + \ + /**The known CAS of the item. This is passed to mutation to commands to + ensure the item is only changed if the server-side CAS value matches the + one specified here. For other operations (such as lcb_CMDENDURE) this + is used to ensure that the item has been persisted/replicated to a number + of servers with the value specified here. */ \ + lcb_U64 cas; \ + \ + /**The key for the document itself. This should be set via LCB_CMD_SET_KEY() */ \ + lcb_KEYBUF key; \ + \ + /** \volatile */ \ + lcb_KEYBUF _hashkey + +/**@brief Common ABI header for all commands. _Any_ command may be safely + * casted to this type.*/ +typedef struct lcb_CMDBASE { + LCB_CMD_BASE; +} lcb_CMDBASE; + +/** + * @private + * Flag for lcb_CMDBASE::cmdflags which indicates that the lcb_CMDBASE::cookie + * is a special callback object. This flag is used internally within the + * library. + */ +#define LCB_CMD_F_INTERNAL_CALLBACK (1 << 0) + +/** + * If this flag is set, then multiple authentication credentials will be passed + * to the server. By default only the bucket's credentials (i.e. bucket SASL + * password) are passed. + */ +#define LCB_CMD_F_MULTIAUTH (1<<1) + +/** + * @committed + * + * Set the key for the command. + * @param cmd A command derived from lcb_CMDBASE + * @param keybuf the buffer for the key + * @param keylen the length of the key. + * + * @code{.c} + * lcb_CMDGET cmd = { 0 }; + * LCB_CMD_SET_KEY(&cmd, "key", strlen("key")); + * @endcode + * + * The storage for `keybuf` may be released or modified after the command has + * been spooled. + */ +#define LCB_CMD_SET_KEY(cmd, keybuf, keylen) \ + LCB_KREQ_SIMPLE(&(cmd)->key, keybuf, keylen) + +/** + * @volatile + * + * Sets the vBucket ID for the item. This accomplishes the same effect as + * _hashkey_ except that this assumes the vBucket has already been obtained. + * + * The intent of this API is to override the default vBucket hashing/calculation. + * + * @param cmd the command structure + * @param vbid the vBucket ID for the key. + */ +#define LCB_CMD__SETVBID(cmd, vbid) do { \ + (cmd)->_hashkey.type = LCB_KV_VBID; \ + (cmd)->_hashkey.contig.nbytes = vbid; \ +} while (0); +/**@}*/ + +/** + * @name Receiving Responses + * @details + * + * This section describes the APIs used in receiving responses. + * + * Each command will have a callback invoked (typically once, for some commands + * this may be more than once) with a response structure. The response structure + * will be of a type that extends lcb_RESPBASE. The response structure should + * not be modified and any of its fields should be considered to point to memory + * which will be released after the callback exits. + * + * The common response header contains the lcb_RESPBASE::cookie field which + * is the pointer to your application context (passed as the second argument + * to the spooling function) and allows you to associate a specific command + * with a specific response. + * + * The header will also contain the key (lcb_RESPBASE::key) field which can + * also help identify the specific command. This is useful if you maintain a + * single _cookie_ for multiple commands, and have per-item specific data + * you wish to associate within the _cookie_ itself. + * + * Success or failure of the operation is signalled through the lcb_RESPBASE::rc + * field. Note that even in the case of failure, the lcb_RESPBASE::cookie and + * lcb_RESPBASE::key fields will _always_ be populated. + * + * Most commands also return the CAS of the item (as it exists on the server) + * and this is placed inside the lcb_RESPBASE::cas field, however it is + * only valid in the case where lcb_RESPBASE::rc is LCB_SUCCESS. + * + * @{ + */ + +#define LCB_RESP_BASE \ + /** + Application-defined pointer passed as the `cookie` parameter when + scheduling the command. + */ \ + void *cookie; \ + const void *key; /**< Key for request */ \ + lcb_SIZE nkey; /**< Size of key */ \ + lcb_CAS cas; /**< CAS for response (if applicable) */ \ + lcb_error_t rc; /**< Status code */ \ + lcb_U16 version; /**< ABI version for response */ \ + lcb_U16 rflags; /**< Response specific flags. see ::lcb_RESPFLAGS */ + + +/** + * @brief + * Base response structure for callbacks. + * All responses structures derive from this ABI. + */ +typedef struct { + LCB_RESP_BASE +} lcb_RESPBASE; + + +#define LCB_RESP_SERVER_FIELDS \ + /** String containing the `host:port` of the server which sent this response */ \ + const char *server; + +/**@brief Base structure for informational commands from servers + * This contains an additional lcb_RESPSERVERBASE::server field containing the + * server which emitted this response. + */ +typedef struct { + LCB_RESP_BASE + LCB_RESP_SERVER_FIELDS +} lcb_RESPSERVERBASE; + +/** @ingroup lcb-mutation-tokens */ +typedef struct lcb_MUTATION_TOKEN { + lcb_U64 uuid_; /**< Use LCB_MUTATION_TOKEN_ID() */ + lcb_U64 seqno_; /**< Use LCB_MUTATION_TOKEN_SEQ() */ + lcb_U16 vbid_; /**< Use LCB_MUTATION_TOKEN_VB() */ +} lcb_MUTATION_TOKEN; + +/** + * @brief Response flags. + * These provide additional 'meta' information about the response + * One of more of these values can be set in @ref lcb_RESPBASE::rflags + */ +typedef enum { + /** No more responses are to be received for this request */ + LCB_RESP_F_FINAL = 0x01, + + /**The response was artificially generated inside the client. + * This does not contain reply data from the server for the command, but + * rather contains the basic fields to indicate success or failure and is + * otherwise empty. + */ + LCB_RESP_F_CLIENTGEN = 0x02, + + /**The response was a result of a not-my-vbucket error */ + LCB_RESP_F_NMVGEN = 0x04, + + /**The response has additional internal data. + * Used by lcb_resp_get_mutation_token() */ + LCB_RESP_F_EXTDATA = 0x08, + + /**Flag, only valid for subdoc responses, indicates that the response was + * processed using the single-operation protocol. */ + LCB_RESP_F_SDSINGLE = 0x010 +} lcb_RESPFLAGS; + +/** + * The type of response passed to the callback. This is used to install callbacks + * for the library and to distinguish between responses if a single callback + * is used for multiple response types. + * + * @note These callbacks may conflict with the older version 2 callbacks. The + * rules are as follows: + * * If a callback has been installed using lcb_install_callback3(), then + * the older version 2 callback will not be invoked for that operation. The order + * of installation does not matter. + * * If the LCB_CALLBACK_DEFAULT callback is installed, _none_ of the version 2 + * callbacks are invoked. + */ +typedef enum { + LCB_CALLBACK_DEFAULT = 0, /**< Default callback invoked as a fallback */ + LCB_CALLBACK_GET, /**< lcb_get3() */ + LCB_CALLBACK_STORE, /**< lcb_store3() */ + LCB_CALLBACK_COUNTER, /**< lcb_counter3() */ + LCB_CALLBACK_TOUCH, /**< lcb_touch3() */ + LCB_CALLBACK_REMOVE, /**< lcb_remove3() */ + LCB_CALLBACK_UNLOCK, /**< lcb_unlock3() */ + LCB_CALLBACK_STATS, /**< lcb_stats3() */ + LCB_CALLBACK_VERSIONS, /**< lcb_server_versions3() */ + LCB_CALLBACK_VERBOSITY, /**< lcb_server_verbosity3() */ + LCB_CALLBACK_FLUSH, /**< lcb_flush3() */ + LCB_CALLBACK_OBSERVE, /**< lcb_observe3_ctxnew() */ + LCB_CALLBACK_GETREPLICA, /**< lcb_rget3() */ + LCB_CALLBACK_ENDURE, /**< lcb_endure3_ctxnew() */ + LCB_CALLBACK_HTTP, /**< lcb_http3() */ + LCB_CALLBACK_CBFLUSH, /**< lcb_cbflush3() */ + LCB_CALLBACK_OBSEQNO, /**< For lcb_observe_seqno3() */ + LCB_CALLBACK_STOREDUR, /** + *
  • Get And Touch: + * + * It is possible to retrieve an item and concurrently modify its expiration + * time (thus keeping it "alive"). The item's expiry time can be set using + * the #exptime field. + *
  • + * + *
  • Lock + * If the #lock field is set to non-zero, the #exptime field indicates the amount + * of time the lock should be held for + *
  • + * + */ +typedef struct { + LCB_CMD_BASE; + /**If set to true, the `exptime` field inside `options` will take to mean + * the time the lock should be held. While the lock is held, other operations + * trying to access the key will fail with an `LCB_ETMPFAIL` error. The + * item may be unlocked either via `lcb_unlock3()` or via a mutation + * operation with a supplied CAS + */ + int lock; +} lcb_CMDGET; + +/** @brief Response structure when retrieving a single item */ +typedef struct { + LCB_RESP_BASE + const void *value; /**< Value buffer for the item */ + lcb_SIZE nvalue; /**< Length of value */ + void* bufh; + lcb_datatype_t datatype; /**< @private */ + lcb_U32 itmflags; /**< User-defined flags for the item */ +} lcb_RESPGET; + +/** + * @committed + * + * @brief Spool a single get operation + * @param instance the handle + * @param cookie a pointer to be associated with the command + * @param cmd the command structure + * @return LCB_SUCCESS if successful, an error code otherwise + * + * @par Request + * @code{.c} + * lcb_CMDGET cmd = { 0 }; + * LCB_CMD_SET_KEY(&cmd, "Hello", 5); + * lcb_get3(instance, cookie, &cmd); + * @endcode + * + * @par Response + * @code{.c} + * lcb_install_callback3(instance, LCB_CALLBACK_GET, get_callback); + * static void get_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb) { + * const lcb_RESPGET *resp = (const lcb_RESPGET*)rb; + * printf("Got response for key: %.*s\n", (int)resp->key, resp->nkey); + * + * if (resp->rc != LCB_SUCCESS) { + * printf("Couldn't get item: %s\n", lcb_strerror(NULL, resp->rc)); + * } else { + * printf("Got value: %.*s\n", (int)resp->nvalue, resp->value); + * printf("Got CAS: 0x%llx\n", resp->cas); + * printf("Got item/formatting flags: 0x%x\n", resp->itmflags); + * } + * } + * + * @endcode + * + * @par Errors + * @cb_err ::LCB_KEY_ENOENT if the item does not exist in the cluster + * @cb_err ::LCB_ETMPFAIL if the lcb_CMDGET::lock option was set but the item + * was already locked. Note that this error may also be returned (as a generic + * error) if there is a resource constraint within the server itself. + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_get3(lcb_t instance, const void *cookie, const lcb_CMDGET *cmd); +/**@}*/ + +/** + * @ingroup lcb-kv-api + * @defgroup lcb-get-replica Read (Replica) + * @brief Retrieve a document from a replica if it cannot be fetched from the + * primary + * @addtogroup lcb-get-replica + * @{ + */ + + +/**@brief Select get-replica mode + * @see lcb_rget3_cmd_t */ +typedef enum { + /**Query all the replicas sequentially, retrieving the first successful + * response */ + LCB_REPLICA_FIRST = 0x00, + + /**Query all the replicas concurrently, retrieving all the responses*/ + LCB_REPLICA_ALL = 0x01, + + /**Query the specific replica specified by the + * lcb_rget3_cmd_t#index field */ + LCB_REPLICA_SELECT = 0x02 +} lcb_replica_t; + +/** + * @brief Command for requesting an item from a replica + * @note The `options.exptime` and `options.cas` fields are ignored for this + * command. + * + * This structure is similar to @ref lcb_RESPGET with the addition of an + * `index` and `strategy` field which allow you to control and select how + * many replicas are queried. + * + * @see lcb_rget3(), lcb_RESPGET + */ +typedef struct { + LCB_CMD_BASE; + /** + * Strategy for selecting a replica. The default is ::LCB_REPLICA_FIRST + * which results in the client trying each replica in sequence until a + * successful reply is found, and returned in the callback. + * + * ::LCB_REPLICA_FIRST evaluates to 0. + * + * Other options include: + *
      + *
    • ::LCB_REPLICA_ALL - queries all replicas concurrently and dispatches + * a callback for each reply
    • + *
    • ::LCB_REPLICA_SELECT - queries a specific replica indicated in the + * #index field
    • + *
    + * + * @note When ::LCB_REPLICA_ALL is selected, the callback will be invoked + * multiple times, one for each replica. The final callback will have the + * ::LCB_RESP_F_FINAL bit set in the lcb_RESPBASE::rflags field. The final + * response will also contain the response from the last replica to + * respond. + */ + lcb_replica_t strategy; + + /** + * Valid only when #strategy is ::LCB_REPLICA_SELECT, specifies the replica + * index number to query. This should be no more than `nreplicas-1` + * where `nreplicas` is the number of replicas the bucket is configured with. + */ + int index; +} lcb_CMDGETREPLICA; + +/**@committed + * + * @brief Spool a single get-with-replica request + * @param instance + * @param cookie + * @param cmd + * @return LCB_SUCCESS on success, error code otherwise. + * + * When a response is received, the callback installed for ::LCB_CALLBACK_GETREPLICA + * will be invoked. The response will be an @ref lcb_RESPGET pointer. + * + * ### Request + * @code{.c} + * lcb_CMDGETREPLICA cmd = { 0 }; + * LCB_CMD_SET_KEY(&cmd, "key", 3); + * lcb_rget3(instance, cookie, &cmd); + * @endcode + * + * ### Response + * @code{.c} + * lcb_install_callback3(instance, LCB_CALLBACK_GETREPLICA, rget_callback); + * static void rget_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb) + * { + * const lcb_RESPGET *resp = (const lcb_RESPGET *)rb; + * printf("Got Get-From-Replica response for %.*s\n", (int)resp->key, resp->nkey); + * if (resp->rc == LCB_SUCCESS) { + * printf("Got response: %.*s\n", (int)resp->value, resp->nvalue); + * else { + * printf("Couldn't retrieve: %s\n", lcb_strerror(NULL, resp->rc)); + * } + * } + * @endcode + * + * @warning As this function queries a replica node for data it is possible + * that the returned document may not reflect the latest document in the server. + * + * @warning This function should only be used in cases where a normal lcb_get3() + * has failed, or where there is reason to believe it will fail. Because this + * function may query more than a single replica it may cause additional network + * and server-side CPU load. Use sparingly and only when necessary. + * + * @cb_err ::LCB_KEY_ENOENT if the key is not found on the replica(s), + * ::LCB_NO_MATCHING_SERVER if there are no replicas (either configured or online), + * or if the given replica + * (if lcb_CMDGETREPLICA::strategy is ::LCB_REPLICA_SELECT) is not available or + * is offline. + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_rget3(lcb_t instance, const void *cookie, const lcb_CMDGETREPLICA *cmd); +/**@}*/ + +/** + * @ingroup lcb-kv-api + * @defgroup lcb-store Create/Update + * @brief Set the value of a document + * @addtogroup lcb-store + * @{ + */ + +/** + * @brief Values for lcb_CMDSTORE::operation + * + * Storing an item in couchbase is only one operation with a different + * set of attributes / constraints. + */ +typedef enum { + /** + * Will cause the operation to fail if the key already exists in the + * cluster. + */ + LCB_ADD = 0x01, + + /** + * Will cause the operation to fail _unless_ the key already exists in the + * cluster. + */ + LCB_REPLACE = 0x02, + + /** Unconditionally store the item in the cluster */ + LCB_SET = 0x03, + + /** + * The default storage mode. This constant was added in version 2.6.2 for + * the sake of maintaining a default storage mode, eliminating the need + * for simple storage operations to explicitly define + * lcb_CMDSTORE::operation. Behaviorally it is identical to @ref LCB_SET + * in that it will make the server unconditionally store the item, whether + * it exists or not. + */ + LCB_UPSERT = 0x00, + + /** + * Rather than setting the contents of the entire document, take the value + * specified in lcb_CMDSTORE::value and _append_ it to the existing bytes in + * the value. + */ + LCB_APPEND = 0x04, + + /** + * Like ::LCB_APPEND, but prepends the new value to the existing value. + */ + LCB_PREPEND = 0x05 +} lcb_storage_t; + +/**@brief + * + * Command for storing an item to the server. This command must contain the + * key to mutate, the value which should be set (or appended/prepended) in the + * lcb_CMDSTORE::value field (see LCB_CMD_SET_VALUE()) and the operation indicating + * the mutation type (lcb_CMDSTORE::operation). + * + * @warning #exptime *cannot* be used with #operation set to @ref LCB_APPEND + * or @ref LCB_PREPEND. + */ +typedef struct { + LCB_CMD_BASE; + + /** + * Value to store on the server. The value may be set using the + * LCB_CMD_SET_VALUE() or LCB_CMD_SET_VALUEIOV() API + */ + lcb_VALBUF value; + + /** + * Format flags used by clients to determine the underlying encoding of + * the value. This value is also returned during retrieval operations in the + * lcb_RESPGET::itmflags field + */ + lcb_U32 flags; + + /** Do not set this value for now */ + lcb_datatype_t datatype; + + /** Controls *how* the operation is perfomed. See the documentation for + * @ref lcb_storage_t for the options. There is no default value for this + * field. + */ + lcb_storage_t operation; +} lcb_CMDSTORE; + +/** + * @brief Response structure for lcb_store3() + */ +typedef struct { + LCB_RESP_BASE + + /** The type of operation which was performed */ + lcb_storage_t op; +} lcb_RESPSTORE; + +/** + * @committed + * + * @brief Set the value buffer for the command. This may be used when the new + * value is a single contiguous buffer. + * + * @param scmd an lcb_CMDSTORE pointer + * @param valbuf the buffer for the value + * @param vallen the length of the buffer + * + * The buffer needs to remain valid only until the command is passed to the + * lcb_store3() function. + */ +#define LCB_CMD_SET_VALUE(scmd, valbuf, vallen) do { \ + (scmd)->value.vtype = LCB_KV_COPY; \ + (scmd)->value.u_buf.contig.bytes = valbuf; \ + (scmd)->value.u_buf.contig.nbytes = vallen; \ +} while (0); + +/** + * @committed + * + * @brief Set value from a series of input buffers. This may be used when the + * input buffer is not contiguous. Using this call eliminates the need for + * creating a temporary contiguous buffer in which to store the value. + * + * @param scmd the command which needs a value + * @param iovs an array of @ref lcb_IOV structures + * @param niovs number of items in the array. + * + * The buffers (and the IOV array itself) + * need to remain valid only until the scheduler function is called. Once the + * scheduling function is called, the buffer contents are copied into the + * library's internal buffers. + */ +#define LCB_CMD_SET_VALUEIOV(scmd, iovs, niovs) do { \ + (scmd)->value.vtype = LCB_KV_IOVCOPY; \ + (scmd)->value.u_buf.multi.iov = iovs; \ + (scmd)->value.u_buf.multi.niov = niovs; \ +} while (0); + +/** + * @committed + * @brief Schedule a single storage request + * @param instance the handle + * @param cookie pointer to associate with the command + * @param cmd the command structure + * @return LCB_SUCCESS on success, error code on failure + * + * ### Request + * + * @code{.c} + * lcb_CMDSTORE cmd = { 0 }; + * LCB_CMD_SET_KEY(&cmd, "Key", 3); + * LCB_CMD_SET_VALUE(&cmd, "value", 5); + * cmd.operation = LCB_ADD; // Only create if it does not exist + * cmd.exptime = 60; // expire in a minute + * lcb_store3(instance, cookie, &cmd); + * lcb_wait3(instance, LCB_WAIT_NOCHECK); + * @endcode + * + * ### Response + * @code{.c} + * lcb_install_callback3(instance, LCB_CALLBACK_STORE, store_callback); + * void store_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb) + * { + * if (rb->rc == LCB_SUCCESS) { + * printf("Store success: CAS=%llx\n", rb->cas); + * } else { + * printf("Store failed: %s\n", lcb_strerorr(NULL, rb->rc); + * } + * } + * @endcode + * + * Operation-specific error codes include: + * @cb_err ::LCB_KEY_ENOENT if ::LCB_REPLACE was used and the key does not exist + * @cb_err ::LCB_KEY_EEXISTS if ::LCB_ADD was used and the key already exists + * @cb_err ::LCB_KEY_EEXISTS if the CAS was specified (for an operation other + * than ::LCB_ADD) and the item exists on the server with a different + * CAS + * @cb_err ::LCB_KEY_EEXISTS if the item was locked and the CAS supplied did + * not match the locked item's CAS (or if no CAS was supplied) + * @cb_err ::LCB_NOT_STORED if an ::LCB_APPEND or ::LCB_PREPEND operation was + * performed and the item did not exist on the server. + * @cb_err ::LCB_E2BIG if the size of the value exceeds the cluster per-item + * value limit (currently 20MB). + * + * + * @note After a successful store operation you can use lcb_endure3_ctxnew() + * to wait for the item to be persisted and/or replicated to other nodes. + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_store3(lcb_t instance, const void *cookie, const lcb_CMDSTORE *cmd); +/**@}*/ + +/** + * @ingroup lcb-kv-api + * @defgroup lcb-remove Delete + * @brief Remove documents from the cluster + * @addtogroup lcb-remove + * @{ + */ + +/**@brief + * Command for removing an item from the server + * @note The lcb_CMDREMOVE::exptime field here does nothing. + * + * The lcb_CMDREMOVE::cas field may be + * set to the last CAS received from a previous operation if you wish to + * ensure the item is removed only if it has not been mutated since the last + * retrieval + */ +typedef lcb_CMDBASE lcb_CMDREMOVE; + +/**@brief + * Response structure for removal operation. The lcb_RESPREMOVE::cas field + * may be used to check that it no longer exists on any node's storage + * using the lcb_endure3_ctxnew() function. You can also use the + * @ref lcb_MUTATION_TOKEN (via lcb_resp_get_mutation_token) + * + * The lcb_RESPREMOVE::rc field may be set to ::LCB_KEY_ENOENT if the item does + * not exist, or ::LCB_KEY_EEXISTS if a CAS was specified and the item has since + * been mutated. + */ +typedef lcb_RESPBASE lcb_RESPREMOVE; + +/**@committed + * @brief Spool a removal of an item + * @param instance the handle + * @param cookie pointer to associate with the request + * @param cmd the command + * @return LCB_SUCCESS on success, other code on failure + * + * ### Request + * @code{.c} + * lcb_CMDREMOVE cmd = { 0 }; + * LCB_CMD_SET_KEY(&cmd, "deleteme", strlen("deleteme")); + * lcb_remove3(instance, cookie, &cmd); + * @endcode + * + * ### Response + * @code{.c} + * lcb_install_callback3(instance, LCB_CALLBACK_REMOVE, rm_callback); + * void rm_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb) + * { + * printf("Key: %.*s...", (int)resp->nkey, resp->key); + * if (rb->rc != LCB_SUCCESS) { + * printf("Failed to remove item!: %s\n", lcb_strerror(NULL, rb->rc)); + * } else { + * printf("Removed item!\n"); + * } + * } + * @endcode + * + * The following operation-specific error codes are returned in the callback + * @cb_err ::LCB_KEY_ENOENT if the key does not exist + * @cb_err ::LCB_KEY_EEXISTS if the CAS was specified and it does not match the + * CAS on the server + * @cb_err ::LCB_KEY_EEXISTS if the item was locked and no CAS (or an incorrect + * CAS) was specified. + * + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_remove3(lcb_t instance, const void *cookie, const lcb_CMDREMOVE * cmd); +/**@}*/ + +/** + * @ingroup lcb-kv-api + * @defgroup lcb-mctx MULTICMD API + * @addtogroup lcb-mctx + * @{ + */ +/** + * Multi Command Context API + * Some commands (notably, OBSERVE and its higher level equivalent, endue) + * are handled more efficiently at the cluster side by stuffing multiple + * items into a single packet. + * + * This structure defines three function pointers to invoke. The #addcmd() + * function will add a new command to the current packet, the #done() + * function will schedule the packet(s) into the current scheduling context + * and the #fail() function will destroy the context without progressing + * further. + * + * Some commands will return an lcb_MULTICMD_CTX object to be used for this + * purpose: + * + * @code{.c} + * lcb_MUTLICMD_CTX *ctx = lcb_observe3_ctxnew(instance); + * + * lcb_CMDOBSERVE cmd = { 0 }; + * LCB_CMD_SET_KEY(&cmd, "key1", strlen("key1")); + * ctx->addcmd(ctx, &cmd); + * LCB_CMD_SET_KEY(&cmd.key, "key2", strlen("key2")); + * ctx->addcmd(ctx, &cmd); + * LCB_CMD_SET_KEY(&cmd.key, "key3", strlen("key3")); + * ctx->addcmd(ctx, &cmd); + * + * ctx->done(ctx); + * lcb_wait(instance); + * @endcode + */ +typedef struct lcb_MULTICMD_CTX_st { + /** + * Add a command to the current context + * @param ctx the context + * @param cmd the command to add. Note that `cmd` may be a subclass of lcb_CMDBASE + * @return LCB_SUCCESS, or failure if a command could not be added. + */ + lcb_error_t (*addcmd)(struct lcb_MULTICMD_CTX_st *ctx, const lcb_CMDBASE *cmd); + + /** + * Indicate that no more commands are added to this context, and that the + * context should assemble the packets and place them in the current + * scheduling context + * @param ctx The multi context + * @param cookie The cookie for all commands + * @return LCB_SUCCESS if scheduled successfully, or an error code if there + * was a problem constructing the packet(s). + */ + lcb_error_t (*done)(struct lcb_MULTICMD_CTX_st *ctx, const void *cookie); + + /** + * Indicate that no more commands should be added to this context, and that + * the context should not add its contents to the packet queues, but rather + * release its resources. Called if you don't want to actually perform + * the operations. + * @param ctx + */ + void (*fail)(struct lcb_MULTICMD_CTX_st *ctx); +} lcb_MULTICMD_CTX; +/**@}*/ + +/** + * @ingroup lcb-kv-api + * @defgroup lcb-durability Durability + * @brief Ensure persistence and mutation of documents + * @addtogroup lcb-durability + * @{ + */ + +/** + * @name Wait for a mutation to be persisted/replicated + * @{ + */ + +/** + * Type of durability polling to use. + */ +typedef enum { + /** + * Use the preferred durability. If ::LCB_CNTL_FETCH_MUTATION_TOKENS is + * enabled and the server version is 4.0 or greater then ::LCB_DURABILITY_MODE_SEQNO + * is used. Otherwise ::LCB_DURABILITY_MODE_CAS is used. + */ + LCB_DURABILITY_MODE_DEFAULT = 0, + + /** + * Explicitly request CAS-based durability. This is done by checking the + * CAS of the item on each server with the item specified in the input. + * The durability operation is considered complete when all items' CAS + * values match. If the CAS value on the master node changes then the + * durability operation will fail with ::LCB_KEY_EEXISTS. + * + * @note + * CAS may change either because of a failover or because of another + * subsequent mutation. These scenarios are possible (though unlikely). + * The ::LCB_DURABILITY_MODE_SEQNO mode is not subject to these constraints. + */ + LCB_DURABILITY_MODE_CAS, + + /** + * Use sequence-number based polling. This is done by checking the current + * "mutation sequence number" for the given mutation. To use this mode + * either an explicit @ref lcb_MUTATION_TOKEN needs to be passed, or + * the ::LCB_CNTL_DURABILITY_MUTATION_TOKENS should be set, allowing + * the caching of the latest mutation token for each vBucket. + */ + LCB_DURABILITY_MODE_SEQNO +} lcb_DURMODE; + +/** @brief Options for lcb_endure3_ctxnew() */ +typedef struct { + /** + * Upper limit in microseconds from the scheduling of the command. When + * this timeout occurs, all remaining non-verified keys will have their + * callbacks invoked with @ref LCB_ETIMEDOUT. + * + * If this field is not set, the value of @ref LCB_CNTL_DURABILITY_TIMEOUT + * will be used. + */ + lcb_U32 timeout; + + /** + * The durability check may involve more than a single call to observe - or + * more than a single packet sent to a server to check the key status. This + * value determines the time to wait (in microseconds) + * between multiple probes for the same server. + * If not set, the @ref LCB_CNTL_DURABILITY_INTERVAL will be used + * instead. + */ + lcb_U32 interval; + + /** + * how many nodes the key should be persisted to (including master). + * If set to 0 then persistence will not be checked. If set to a large + * number (i.e. UINT16_MAX) and #cap_max is also set, will be set to the + * maximum number of nodes to which persistence is possible (which will + * always contain at least the master node). + * + * The maximum valid value for this field is + * 1 + the total number of configured replicas for the bucket which are part + * of the cluster. If this number is higher then it will either be + * automatically capped to the maximum available if (#cap_max is set) or + * will result in an ::LCB_DURABILITY_ETOOMANY error. + */ + lcb_U16 persist_to; + + /** + * how many nodes the key should be persisted to (excluding master). + * If set to 0 then replication will not be checked. If set to a large + * number (i.e. UINT16_MAX) and #cap_max is also set, will be set to the + * maximum number of nodes to which replication is possible (which may + * be 0 if the bucket is not configured for replicas). + * + * The maximum valid value for this field is the total number of configured + * replicas which are part of the cluster. If this number is higher then + * it will either be automatically capped to the maximum available + * if (#cap_max is set) or will result in an ::LCB_DURABILITY_ETOOMANY + * error. + * */ + lcb_U16 replicate_to; + + /** + * this flag inverts the sense of the durability check and ensures that + * the key does *not* exist. This should be used if checking durability + * after an lcb_remove3() operation. + */ + lcb_U8 check_delete; + + /** + * If replication/persistence requirements are excessive, cap to + * the maximum available + */ + lcb_U8 cap_max; + + /** + * Set the polling method to use. + * The value for this field should be one of the @ref lcb_DURMODE constants. + */ + lcb_U8 pollopts; +} lcb_DURABILITYOPTSv0; + +/**@brief Options for lcb_endure3_ctxnew() (wrapper) + * @see lcb_DURABILITYOPTSv0 */ +typedef struct lcb_durability_opts_st { + int version; + union { + lcb_DURABILITYOPTSv0 v0; + } v; +} lcb_durability_opts_t; + +/**Must specify this flag if using the 'mutation_token' field, as it was added in + * a later version */ +#define LCB_CMDENDURE_F_MUTATION_TOKEN 1<<16 + +/**@brief Command structure for endure. + * If the lcb_CMDENDURE::cas field is specified, the operation will check and + * verify that the CAS found on each of the nodes matches the CAS specified + * and only then consider the item to be replicated and/or persisted to the + * nodes. If the item exists on the master's cache with a different CAS then + * the operation will fail + */ +typedef struct { + LCB_CMD_BASE; + const lcb_MUTATION_TOKEN *mutation_token; +} lcb_CMDENDURE; + +/**@brief Response structure for endure */ +typedef struct { + LCB_RESP_BASE + /** + * Total number of polls (i.e. how many packets per server) did this + * operation require + */ + lcb_U16 nresponses; + + /** + * Whether this item exists in the master in its current form. This can be + * true even if #rc is not successful + */ + lcb_U8 exists_master; + + /** + * True if item was persisted on the master node. This may be true even if + * #rc is not successful. + */ + lcb_U8 persisted_master; + + /** + * Total number of nodes (including master) on which this mutation has + * been persisted. Valid even if #rc is not successful. + */ + lcb_U8 npersisted; + + /** + * Total number of replica nodes to which this mutation has been replicated. + * Valid even if #rc is not successful. + */ + lcb_U8 nreplicated; +} lcb_RESPENDURE; + +/** + * @committed + * + * @details + * Ensure a key is replicated to a set of nodes + * + * The lcb_endure3_ctxnew() API is used to wait asynchronously until the item + * have been persisted and/or replicated to at least the number of nodes + * specified in the durability options. + * + * The command is implemented by sending a series of `OBSERVE` broadcasts + * (see lcb_observe3_ctxnew()) to all the nodes in the cluster which are either + * master or replica for a specific key. It polls repeatedly + * (see lcb_DURABILITYOPTSv0::interval) until all the items have been persisted and/or + * replicated to the number of nodes specified in the criteria, or the timeout + * (aee lcb_DURABILITYOPTsv0::timeout) has been reached. + * + * The lcb_DURABILITYOPTSv0::persist_to and lcb_DURABILITYOPTS::replicate_to + * control the number of nodes the item must be persisted/replicated to + * in order for the durability polling to succeed. + * + * @brief Return a new command context for scheduling endure operations + * @param instance the instance + * @param options a structure containing the various criteria needed for + * durability requirements + * @param[out] err Error code if a new context could not be created + * @return the new context, or NULL on error. Note that in addition to memory + * allocation failure, this function might also return NULL because the `options` + * structure contained bad values. Always check the `err` result. + * + * @par Scheduling Errors + * The following errors may be encountered when scheduling: + * + * @cb_err ::LCB_DURABILITY_ETOOMANY if either lcb_DURABILITYOPTS::persist_to or + * lcb_DURABILITYOPTS::replicate_to is too big. `err` may indicate this. + * @cb_err ::LCB_DURABILITY_NO_MUTATION_TOKENS if no relevant mutation token + * could be found for a given command (this is returned from the relevant + * lcb_MULTICMD_CTX::addcmd call). + * @cb_err ::LCB_DUPLICATE_COMMANDS if using CAS-based durability and the + * same key was submitted twice to lcb_MULTICMD_CTX::addcmd(). This error is + * returned from lcb_MULTICMD_CTX::done() + * + * @par Callback Errors + * The following errors may be returned in the callback: + * @cb_err ::LCB_ETIMEDOUT if the criteria could not be verified within the + * accepted timeframe (see lcb_DURABILITYOPTSv0::timeout) + * @cb_err ::LCB_KEY_EEXISTS if using CAS-based durability and the item's + * CAS has been changed on the master node + * @cb_err ::LCB_MUTATION_LOST if using sequence-based durability and the + * server has detected that data loss has occurred due to a failover. + * + * @par Creating request context + * @code{.c} + * lcb_durability_opts_t dopts; + * lcb_error_t rc; + * memset(&dopts, 0, sizeof dopts); + * dopts.v.v0.persist_to = -1; + * dopts.v.v0.replicate_to = -1; + * dopts.v.v0.cap_max = 1; + * mctx = lcb_endure3_ctxnew(instance, &dopts, &rc); + * // Check mctx != NULL and rc == LCB_SUCCESS + * @endcode + * + * @par Adding keys - CAS + * This can be used to add keys using CAS-based durability. This shows an + * example within a store callback. + * @code{.c} + * lcb_RESPSTORE *resp = get_store_response(); + * lcb_CMDENDURE cmd = { 0 }; + * LCB_CMD_SET_KEY(&cmd, resp->key, resp->nkey); + * cmd.cas = resp->cas; + * rc = mctx->addcmd(mctx, (const lcb_CMDBASE*)&cmd); + * rc = mctx->done(mctx, cookie); + * @endcode + * + * @par Adding keys - explicit sequence number + * Shows how to use an explicit sequence number as a basis for polling + * @code{.c} + * // during instance creation: + * lcb_cntl_string(instance, "fetch_mutation_tokens", "true"); + * lcb_connect(instance); + * // ... + * lcb_RESPSTORE *resp = get_store_response(); + * lcb_CMDENDURE cmd = { 0 }; + * LCB_CMD_SET_KEY(&cmd, resp->key, resp->nkey); + * cmd.mutation_token = &resp->mutation_token; + * cmd.cmdflags |= LCB_CMDENDURE_F_MUTATION_TOKEN; + * rc = mctx->addcmd(mctx, (const lcb_CMDBASE*)&cmd); + * rc = mctx->done(mctx, cookie); + * @endcode + * + * @par Adding keys - implicit sequence number + * Shows how to use an implicit mutation token (internally tracked by the + * library) for durability: + * @code{.c} + * // during instance creation + * lcb_cntl_string(instance, "fetch_mutation_tokens", "true"); + * lcb_cntl_string(instance, "dur_mutation_tokens", "true"); + * lcb_connect(instance); + * // ... + * lcb_CMDENDURE cmd = { 0 }; + * LCB_CMD_SET_KEY(&cmd, "key", strlen("key")); + * mctx->addcmd(mctx, (const lcb_CMDBASE*)&cmd); + * mctx->done(mctx, cookie); + * @endcode + * + * @par Response + * @code{.c} + * lcb_install_callback3(instance, LCB_CALLBACK_ENDURE, dur_callback); + * void dur_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb) + * { + * const lcb_RESPENDURE *resp = (const lcb_RESPENDURE*)rb; + * printf("Durability polling result for %.*s.. ", (int)resp->nkey, resp->key); + * if (resp->rc != LCB_SUCCESS) { + * printf("Failed: %s\n", lcb_strerror(NULL, resp->rc); + * return; + * } + * } + * @endcode + */ +LIBCOUCHBASE_API +lcb_MULTICMD_CTX * +lcb_endure3_ctxnew(lcb_t instance, + const lcb_durability_opts_t *options, lcb_error_t *err); + +/** + * Command structure for lcb_storedur3() + * This is much like @ref lcb_CMDSTORE, but also includes durability options. + */ +typedef struct { + LCB_CMD_BASE; + lcb_VALBUF value; /**< @see lcb_CMDSTORE::value */ + lcb_U32 flags; /**< @see lcb_CMDSTORE::flags */ + lcb_datatype_t datatype; /**< @private */ + lcb_storage_t operation; /**< @see lcb_CMDSTORE::operation */ + + /** + * Number of nodes to persist to. If negative, will be capped at the maximum + * allowable for the current cluster. + * @see lcb_DURABILITYOPTSv0::persist_to + */ + char persist_to; + + /** + * Number of nodes to replicate to. If negative, will be capped at the maximum + * allowable for the current cluster. + * @see lcb_DURABILITYOPTSv0::replicate_to + */ + char replicate_to; +} lcb_CMDSTOREDUR; + +/** + * Response structure for `LCB_CALLBACK_STOREDUR. + */ +typedef struct { + LCB_RESP_BASE + /** Internal durability response structure. This should never be NULL */ + const lcb_RESPENDURE *dur_resp; + + /**If the #rc field is not @ref LCB_SUCCESS, this field indicates + * what failed. If this field is nonzero, then the store operation failed, + * but the durability checking failed. If this field is zero then the + * actual storage operation failed. */ + int store_ok; +} lcb_RESPSTOREDUR; + +/** + * @volatile + * + * Schedule a storage operation with subsequent durability checking. + * + * This is a compound of a logical lcb_store3() followed by an + * lcb_endure3_ctxnew() upon success. + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_storedur3(lcb_t instance, const void *cookie, const lcb_CMDSTOREDUR *cmd); + +#define LCB_DURABILITY_VALIDATE_CAPMAX 1<<1 + +/** + * @committed + * + * Validate durability settings. + * + * This function will validate (and optionally modify) the settings. This is + * helpful to ensure the durability options are valid _before_ issuing a command + * + * @param instance the instance + * + * @param[in,out] persist_to The desired number of servers to persist to. + * If the `CAPMAX` flag is set, on output this will contain the effective + * number of servers the item can be persisted to + * + * @param[in,out] replicate_to The desired number of servers to replicate to. + * If the `CAPMAX` flag is set, on output this will contain the effective + * number of servers the item can be replicated to + * + * @param options Options to use for validating. The only recognized option + * is currently `LCB_DURABILITY_VALIDATE_CAPMAX` which has the same semantics + * as lcb_DURABILITYOPTSv0::cap_max. + * + * @return LCB_SUCCESS on success + * @return LCB_DURABILITY_ETOOMANY if the requirements exceed the number of + * servers currently available, and `CAPMAX` was not specifie + * @return LCB_EINVAL if both `persist_to` and `replicate_to` are 0. + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_durability_validate(lcb_t instance, + lcb_U16 *persist_to, lcb_U16 *replicate_to, int options); +/**@} (NAME) */ + + +/** + * @name Retrieve current persistence/replication status + * @{ + */ + +/**Set this bit in the cmdflags field to indicate that only the master node + * should be contacted*/ +#define LCB_CMDOBSERVE_F_MASTER_ONLY 1<<16 + +/**@brief Structure for an observe request. + * To request the status from _only_ the master node of the key, set the + * LCB_CMDOBSERVE_F_MASTERONLY bit inside the lcb_CMDOBSERVE::cmdflags field + */ +typedef struct { + LCB_CMD_BASE; + /**For internal use: This determines the servers the command should be + * routed to. Each entry is an index within the server. */ + const lcb_U16* servers_; + size_t nservers_; +} lcb_CMDOBSERVE; + +/** + * @brief Possible statuses for keys in OBSERVE response + */ +typedef enum { + /** The item found in the memory, but not yet on the disk */ + LCB_OBSERVE_FOUND = 0x00, + /** The item hit the disk */ + LCB_OBSERVE_PERSISTED = 0x01, + /** The item missing on the disk and the memory */ + LCB_OBSERVE_NOT_FOUND = 0x80, + /** No knowledge of the key :) */ + LCB_OBSERVE_LOGICALLY_DELETED = 0x81, + + LCB_OBSERVE_MAX = 0x82 +} lcb_observe_t; + +/**@brief Response structure for an observe command. + * Note that the lcb_RESPOBSERVE::cas contains the CAS of the item as it is + * stored within that specific server. The CAS may be incorrect or stale + * unless lcb_RESPOBSERVE::ismaster is true. + */ +typedef struct { + LCB_RESP_BASE + lcb_U8 status; /**rflags & LCB_RESP_F_FINAL) { + * return; + * } + * + * printf("Got status for key %.*s\n", (int)resp->nkey, resp->key); + * printf(" Node Type: %s\n", resp->ismaster ? "MASTER" : "REPLICA"); + * printf(" Status: 0x%x\n", resp->status); + * printf(" Current CAS: 0x%"PRIx64"\n", resp->cas); + * } + * + * lcb_MULTICMD_CTX *mctx = lcb_observe3_ctxnew(instance); + * lcb_CMDOBSERVE cmd = { 0 }; + * LCB_CMD_SET_KEY(&cmd, "key", 3); + * mctx->addcmd(mctx, (lcb_CMDBASE *)&cmd); + * mctx->done(mctx, cookie); + * lcb_install_callback3(instance, LCB_CALLBACK_OBSERVE, (lcb_RESP_cb)callback); + * @endcode + * + * @warning + * Operations created by observe cannot be undone using lcb_sched_fail(). + */ +LIBCOUCHBASE_API +lcb_MULTICMD_CTX * +lcb_observe3_ctxnew(lcb_t instance); + +/** + * @brief Command structure for lcb_observe_seqno3(). + * Note #key, #nkey, and #cas are not used in this command. + */ +typedef struct { + LCB_CMD_BASE; + /** + * Server index to target. The server index must be valid and must also + * be either a master or a replica for the vBucket indicated in #vbid + */ + lcb_U16 server_index; + lcb_U16 vbid; /**< vBucket ID to query */ + lcb_U64 uuid; /**< UUID known to client which should be queried */ +} lcb_CMDOBSEQNO; + +/** + * @brief Response structure for lcb_observe_seqno3() + * + * Note that #key, #nkey and #cas are empty because the operand is the relevant + * mutation token fields in @ref lcb_CMDOBSEQNO + */ +typedef struct { + LCB_RESP_BASE + lcb_U16 vbid; /**< vBucket ID (for potential mapping) */ + lcb_U16 server_index; /**< Input server index */ + lcb_U64 cur_uuid; /**< UUID for this vBucket as known to the server */ + lcb_U64 persisted_seqno; /**< Highest persisted sequence */ + lcb_U64 mem_seqno; /**< Highest known sequence */ + + /** + * In the case where the command's uuid is not the most current, this + * contains the last known UUID + */ + lcb_U64 old_uuid; + + /** + * If #old_uuid is nonzero, contains the highest sequence number persisted + * in the #old_uuid snapshot. + */ + lcb_U64 old_seqno; +} lcb_RESPOBSEQNO; + +/** + * @volatile + * @brief Get the persistence/replication status for a given mutation token + * @param instance the handle + * @param cookie callback cookie + * @param cmd the command + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_observe_seqno3(lcb_t instance, const void *cookie, const lcb_CMDOBSEQNO *cmd); + +/**@} (Name: OBSERVE) */ +/**@} (Group: Durability) */ + +/** + * @ingroup lcb-public-api + * @defgroup lcb-mutation-tokens Mutation Tokens + * + * @details Mutation Tokens are returned with mutations if + * ::LCB_CNTL_FETCH_MUTATION_TOKENS is enabled (off by default). Mutation tokens + * are largely of internal use, but can be used by N1QL queries and durability + * requirement polling + * + * @addtogroup lcb-mutation-tokens + * @{ + */ + +/** + * @brief + * Structure representing a synchronization token. This token may be used + * for durability operations and N1QL queries. The contents of this structure + * should be considered opaque, and accessed via the various macros + * @struct lcb_MUTATION_TOKEN + */ + +/** Get the vBucket UUID */ +#define LCB_MUTATION_TOKEN_ID(p) (p)->uuid_ +/** Get the sequence number */ +#define LCB_MUTATION_TOKEN_SEQ(p) (p)->seqno_ +/** Get the vBucket number itself */ +#define LCB_MUTATION_TOKEN_VB(p) (p)->vbid_ +/** Whether this mutation token has valid contents */ +#define LCB_MUTATION_TOKEN_ISVALID(p) \ + (p && !((p)->uuid_ == 0 && (p)->seqno_ == 0 && (p)->vbid_ == 0)) + +/** + * Retrieves the mutation token from the response structure + * @param cbtype the type of callback invoked + * @param rb the pointer to the response + * @return The embedded mutation token, or NULL if the response does not have a + * mutation token. This may be either because the command does not support + * mutation tokens, or because they have been disabled at the connection + * level. + */ +LIBCOUCHBASE_API +const lcb_MUTATION_TOKEN * +lcb_resp_get_mutation_token(int cbtype, const lcb_RESPBASE *rb); + +/** + * @volatile + * + * Retrieves the last mutation token for a given key. + * This relies on the @ref LCB_CNTL_DURABILITY_MUTATION_TOKENS option, and will + * check the instance-level log to determine the latest MUTATION_TOKEN for + * the given vBucket ID which the key maps to. + * + * @param instance the instance + * @param kb The buffer representing the key. The type of the buffer (see + * lcb_KEYBUF::type) may either be ::LCB_KV_COPY or ::LCB_KV_VBID + * @param[out] errp Set to an error if this function returns NULL + * @return The mutation token if successful, otherwise NULL. + * + * Getting the latest mutation token for a key: + * + * @code{.c} + * lcb_KEYBUF kb; + * kb.type = LCB_KV_COPY; + * kb.contig.bytes = "Hello"; + * kv.config.nbytes = 5; + * mt = lcb_get_mutation_token(instance, &kb, &rc); + * @endcode + * + * Getting the latest mutation token for a vbucket: + * @code{.c} + * lcb_KEYBUF kb; + * kv.type = LCB_KV_VBID; + * kv.contig.nbytes = 543; + * kv.config.bytes = NULL; + * mt = lcb_get_mutation_token(instance, &kb, &rc); + * @endcode + * + * Getting the mutation token for each vbucket + * @code{.c} + * size_t ii, nvb; + * lcbvb_CONFIG *vbc; + * lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_VBCONFIG, &vbc); + * nvb = vbucket_get_num_vbuckets(vbc); + * for (ii = 0; ii < nvb; ii++) { + * lcb_KEYBUF kb; + * const lcb_MUTATION_TOKEN *mt; + * kb.type = LCB_KV_VBID; + * kb.contig.nbytes = ii; + * kb.config.bytes = NULL; + * mt = lcb_get_mutation_token(instance, &kb, &rc); + * } + * @endcode + */ +LIBCOUCHBASE_API +const lcb_MUTATION_TOKEN * +lcb_get_mutation_token(lcb_t instance, const lcb_KEYBUF *kb, lcb_error_t *errp); + +/**@} (Group: Mutation Tokens) */ + +/**@ingroup lcb-kv-api + * @defgroup lcb-counter Counters + * @brief Manipulate the numeric content of a document + * @details Counter operations treat the document being accessed as a numeric + * value (the document should contain a parseable integer as its content). This + * value may then be incremented or decremented. + * + * @addtogroup lcb-counter + * @{ + */ + +/**@brief Command for counter operations. + * @see lcb_counter3(), lcb_RESPCOUNTER. + * + * @warning You may only set the #exptime member if the #create member is set + * to a true value. Setting `exptime` otherwise will cause the operation to + * fail with @ref LCB_OPTIONS_CONFLICT + * + * @warning The #cas member should be set to 0 for this operation. As this + * operation itself is atomic, specifying a CAS is not necessary. + */ +typedef struct { + LCB_CMD_BASE; + /**Delta value. If this number is negative the item on the server is + * decremented. If this number is positive then the item on the server + * is incremented */ + lcb_int64_t delta; + /**If the item does not exist on the server (and `create` is true) then + * this will be the initial value for the item. */ + lcb_U64 initial; + /**Boolean value. Create the item and set it to `initial` if it does not + * already exist */ + int create; +} lcb_CMDCOUNTER; + +/**@brief Response structure for counter operations + * @see lcb_counter3() + */ +typedef struct { + LCB_RESP_BASE + /** Contains the _current_ value after the operation was performed */ + lcb_U64 value; +} lcb_RESPCOUNTER; + +/**@committed + * @brief Schedule single counter operation + * @param instance the instance + * @param cookie the pointer to associate with the request + * @param cmd the command to use + * @return LCB_SUCCESS on success, other error on failure + * + * @par Request + * @code{.c} + * lcb_CMDCOUNTER cmd = { 0 }; + * LCB_CMD_SET_KEY(&cmd, "counter", strlen("counter")); + * cmd.delta = 1; // Increment by one + * cmd.initial = 42; // Default value is 42 if it does not exist + * cmd.exptime = 300; // Expire in 5 minutes + * lcb_counter3(instance, NULL, &cmd); + * lcb_wait3(instance, LCB_WAIT_NOCHECK); + * @endcode + * + * @par Response + * @code{.c} + * lcb_install_callback3(instance, LCB_CALLBACKTYPE_COUNTER, counter_cb); + * void counter_cb(lcb_t instance, int cbtype, const lcb_RESPBASE *rb) + * { + * const lcb_RESPCOUNTER *resp = (const lcb_RESPCOUNTER *)rb; + * if (resp->rc == LCB_SUCCESS) { + * printf("Incremented counter for %.*s. Current value %llu\n", + * (int)resp->nkey, resp->key, resp->value); + * } + * } + * @endcode + * + * @par Callback Errors + * In addition to generic errors, the following errors may be returned in the + * callback (via lcb_RESPBASE::rc): + * + * @cb_err ::LCB_KEY_ENOENT if the counter doesn't exist + * (and lcb_CMDCOUNTER::create was not set) + * @cb_err ::LCB_DELTA_BADVAL if the existing document's content could not + * be parsed as a number by the server. + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_counter3(lcb_t instance, const void *cookie, const lcb_CMDCOUNTER *cmd); +/**@} (Group: Counter) */ + +/**@ingroup lcb-kv-api + * @defgroup lcb-lock Lock/Unlock + * @details Documents may be locked and unlocked on the server. While a document + * is locked, any attempt to modify it (or lock it again) will fail. + * + * @note Locks are not persistent across nodes (if a node fails over, the lock + * is not transferred to a replica). + * @note The recommended way to manage access and concurrency control for + * documents in Couchbase is through the CAS (See lcb_CMDBASE::cas and + * lcb_RESPBASE::cas), which can also be considered a form of opportunistic + * locking. + * + * @par Locking an item + * There is no exclusive function to lock an item. Locking an item is done + * using @ref lcb_get3(), by setting the lcb_CMDGET::lock option to true. + * + * @addtogroup lcb-lock + * @{ + */ + +/**@brief Command for lcb_unlock3() + * @attention lcb_CMDBASE::cas must be specified, or the operation will fail on + * the server*/ +typedef lcb_CMDBASE lcb_CMDUNLOCK; + +/**@brief Response structure for an unlock command. + * @note the lcb_RESPBASE::cas field does not contain the CAS of the item*/ +typedef lcb_RESPBASE lcb_RESPUNLOCK; + +/** + * @committed + * @brief + * Unlock a previously locked item using lcb_CMDGET::lock + * + * @param instance the instance + * @param cookie the context pointer to associate with the command + * @param cmd the command containing the information about the locked key + * @return LCB_SUCCESS if successful, an error code otherwise + * @see lcb_get3() + * + * @par Request + * + * @code{.c} + * void locked_callback(lcb_t, lcb_CALLBACKTYPE, const lcb_RESPBASE *resp) { + * lcb_CMDUNLOCK cmd = { 0 }; + * LCB_CMD_SET_KEY(&cmd, resp->key, resp->nkey); + * cmd.cas = resp->cas; + * lcb_unlock3(instance, cookie, &cmd); + * } + * + * @endcode + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_unlock3(lcb_t instance, const void *cookie, const lcb_CMDUNLOCK *cmd); +/**@} (Group: Unlock) */ + +/**@ingroup lcb-kv-api + * @defgroup lcb-touch Touch/Expiry + * @brief Modify or clear a document's expiration time + * @details Couchbase allows documents to contain expiration times + * (see lcb_CMDBASE::exptime). Most operations allow the expiry time to be + * updated, however lcb_touch3() allows the exclusive update of the expiration + * time without additional network overhead. + * + * @addtogroup lcb-touch + * @{ + */ + +/** + * @brief Command structure for a touch request + * @note The lcb_CMDTOUCH::cas field is ignored. The item's modification time + * is always updated regardless if the CAS on the server differs. + * The #exptime field is always used. If 0 then the expiry on the server is + * cleared. + */ +typedef lcb_CMDBASE lcb_CMDTOUCH; + +/**@brief Response structure for a touch request + * @note the lcb_RESPTOUCH::cas field contains the current CAS of the item*/ +typedef lcb_RESPBASE lcb_RESPTOUCH; + +/**@committed + * @brief Spool a touch request + * @param instance the handle + * @param cookie the pointer to associate with the request + * @param cmd the command + * @return LCB_SUCCESS on success, other error code on failure + * + * @par Request + * @code{.c} + * lcb_CMDTOUCH cmd = { 0 }; + * LCB_CMD_SET_KEY(&cmd, "keep_me", strlen("keep_me")); + * cmd.exptime = 0; // Clear the expiration + * lcb_touch3(instance, cookie, &cmd); + * @endcode + * + * @par Response + * @code{.c} + * lcb_install_callback3(instance, LCB_CALLBACK_TOUCH, touch_callback); + * void touch_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb) + * { + * if (rb->rc == LCB_SUCCESS) { + * printf("Touch succeeded\n"); + * } + * } + * @endcode + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_touch3(lcb_t instance, const void *cookie, const lcb_CMDTOUCH *cmd); +/**@} (Group: Touch) */ +/**@} (Group: KV API) */ + +/**@ingroup lcb-public-api + * @defgroup lcb-misc-cmds Miscellaneous Commands + * @brief Additional miscellaneous commands which can be executed on the server. + * + * @addtogroup lcb-misc-cmds + * @{ + */ + +/** + * @name Server Statistics + * @{ + */ + +/** + * @brief Command structure for stats request + * The lcb_CMDSTATS::key field should contain the statistics key, or be empty + * if the default statistics are desired. + * The #cmdflags field may contain the @ref LCB_CMDSTATS_F_KV flag. + */ +typedef lcb_CMDBASE lcb_CMDSTATS; + +/** + * The key is a stored item for which statistics should be retrieved. This + * invokes the 'keystats' semantics. Note that when using _keystats_, a key + * must be present, and must not have any spaces in it. + */ +#define LCB_CMDSTATS_F_KV (1 << 16) + +/**@brief Response structure for cluster statistics. + * The lcb_RESPSTATS::key field contains the statistic name (_not_ the same + * as was passed in lcb_CMDSTATS::key which is the name of the statistical + * _group_).*/ +typedef struct { + LCB_RESP_BASE + LCB_RESP_SERVER_FIELDS + const char *value; /**< The value, if any, for the given statistic */ + lcb_SIZE nvalue; /**< Length of value */ +} lcb_RESPSTATS; + +/**@committed + * @brief Schedule a request for statistics from the cluster. + * @param instance the instance + * @param cookie pointer to associate with the request + * @param cmd the command + * @return LCB_SUCCESS on success, other error code on failure. + * + * Note that the callback for this command is invoked an indeterminate amount + * of times. The callback is invoked once for each statistic for each server. + * When all the servers have responded with their statistics, a final callback + * is delivered to the application with the LCB_RESP_F_FINAL flag set in the + * lcb_RESPSTATS::rflags field. When this response is received no more callbacks + * for this command shall be invoked. + * + * @par Request + * @code{.c} + * lcb_CMDSTATS cmd = { 0 }; + * // Using default stats, no further initialization + * lcb_stats3(instance, fp, &cmd); + * lcb_wait(instance); + * @endcode + * + * @par Response + * @code{.c} + * lcb_install_callback3(instance, LCB_CALLBACK_STATS, stats_callback); + * void stats_callback(lcb_t, int, const lcb_RESPBASE *rb) + * { + * const lcb_RESPSTATS *resp = (const lcb_RESPSTATS*)rb; + * if (resp->key) { + * printf("Server %s: %.*s = %.*s\n", resp->server, + * (int)resp->nkey, resp->key, + * (int)resp->nvalue, resp->value); + * } + * if (resp->rflags & LCB_RESP_F_FINAL) { + * printf("No more replies remaining!\n"); + * } + * } + * @endcode + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_stats3(lcb_t instance, const void *cookie, const lcb_CMDSTATS * cmd); +/**@} (Name: Stats) */ + +/**@name Server Versions + * @warning This does not return the actual _Couchbase_ version but rather + * the internal version of the memcached server. + * @{ + */ + +/**@brief Response structure for the version command */ +typedef struct { + LCB_RESP_BASE + LCB_RESP_SERVER_FIELDS + const char *mcversion; /**< The version string */ + lcb_SIZE nversion; /**< Length of the version string */ +} lcb_RESPMCVERSION; + +/**@volatile*/ +LIBCOUCHBASE_API +lcb_error_t +lcb_server_versions3(lcb_t instance, const void *cookie, const lcb_CMDBASE * cmd); + +/**@} (Name: MCversion) */ + +/**@name Server Log Verbosity + * @{ + */ + +/** @brief `level` field for lcb_server_verbosity3 () */ +typedef enum { + LCB_VERBOSITY_DETAIL = 0x00, + LCB_VERBOSITY_DEBUG = 0x01, + LCB_VERBOSITY_INFO = 0x02, + LCB_VERBOSITY_WARNING = 0x03 +} lcb_verbosity_level_t; + +typedef struct { + /* unused */ + LCB_CMD_BASE; + const char *server; + lcb_verbosity_level_t level; +} lcb_CMDVERBOSITY; +typedef lcb_RESPSERVERBASE lcb_RESPVERBOSITY; +/**@volatile*/ +LIBCOUCHBASE_API +lcb_error_t +lcb_server_verbosity3(lcb_t instance, const void *cookie, const lcb_CMDVERBOSITY *cmd); +/**@} (Name: Verbosity) */ +/**@} (Group: Misc) */ + +/**@ingroup lcb-public-api + * @defgroup lcb-flush Flush + * @brief Clear the contents of a bucket + * + * Flush is useful for development environments (for example clearing a bucket + * before running tests). + * + * @addtogroup lcb-flush + * @{ + */ +typedef lcb_CMDBASE lcb_CMDCBFLUSH; +typedef lcb_RESPBASE lcb_RESPCBFLUSH; + +/** + * @uncomitted + * + * Flush a bucket + * This function will properly flush any type of bucket using the REST API + * via HTTP. This may be used in a manner similar to the older lcb_flush3(). + * + * The callback invoked under ::LCB_CALLBACK_CBFLUSH will be invoked with either + * a success or failure status depending on the outcome of the operation. Note + * that in order for lcb_cbflush3() to succeed, flush must already be enabled + * on the bucket via the administrative interface. + * + * @param instance the library handle + * @param cookie the cookie passed in the callback + * @param cmd empty command structure. Currently there are no options for this + * command. + * @return status code for scheduling. + * + * @attention + * Because this command is built using HTTP, this is not subject to operation + * pipeline calls such as lcb_sched_enter()/lcb_sched_leave() + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_cbflush3(lcb_t instance, const void *cookie, const lcb_CMDCBFLUSH *cmd); + + +typedef lcb_CMDBASE lcb_CMDFLUSH; +typedef lcb_RESPSERVERBASE lcb_RESPFLUSH; +/** + * @volatile + * @deprecated + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_flush3(lcb_t instance, const void *cookie, const lcb_CMDFLUSH *cmd); +/**@} (Group: Flush) */ + +/**@ingroup lcb-public-api + * @defgroup lcb-http HTTP Client + * @brief Access Couchbase HTTP APIs + * @details The low-level HTTP client may be used to access various HTTP-based + * Couchbase APIs. + * + * Note that existing higher level APIs can be used for N1QL queries (see + * @ref lcb-n1ql-api) and MapReduce view queries (see @ref lcb-view-api) + * + * @addtogroup lcb-http + * @{ + */ + +/** + * @brief The type of HTTP request to execute + */ +typedef enum { + /** + * Execute a request against the bucket. The handle must be of + * @ref LCB_TYPE_BUCKET and must be connected. + */ + LCB_HTTP_TYPE_VIEW = 0, + + /** + * Execute a management API request. The credentials used will match + * those passed during the instance creation time. Thus is the instance + * type is @ref LCB_TYPE_BUCKET then only bucket-level credentials will + * be used. + */ + LCB_HTTP_TYPE_MANAGEMENT = 1, + + /** + * Execute an arbitrary request against a host and port + */ + LCB_HTTP_TYPE_RAW = 2, + + /** Execute an N1QL Query */ + LCB_HTTP_TYPE_N1QL = 3, + + /** Search a fulltext index */ + LCB_HTTP_TYPE_FTS = 4, + + LCB_HTTP_TYPE_MAX +} lcb_http_type_t; + +/** + * @brief HTTP Request method enumeration + * These just enumerate the various types of HTTP request methods supported. + * Refer to the specific cluster or view API to see which method is appropriate + * for your request + */ +typedef enum { + LCB_HTTP_METHOD_GET = 0, + LCB_HTTP_METHOD_POST = 1, + LCB_HTTP_METHOD_PUT = 2, + LCB_HTTP_METHOD_DELETE = 3, + LCB_HTTP_METHOD_MAX = 4 +} lcb_http_method_t; + +/** + * Command flag for HTTP to indicate that the callback is to be invoked + * multiple times for each new chunk of incoming data. Once the entire body + * have been received, the callback will be invoked once more with the + * LCB_RESP_F_FINAL flag (in lcb_RESPHTTP::rflags) and an empty content. + * + * To use streaming requests, this flag should be set in the + * lcb_CMDHTTP::cmdflags field + */ +#define LCB_CMDHTTP_F_STREAM 1<<16 + +/** + * @private + * If specified, the lcb_CMDHTTP::cas field becomes the timeout for this + * specific request. + */ +#define LCB_CMDHTTP_F_CASTMO 1<<17 + +/** + * @private + * Do not inject authentication header into the request. + */ +#define LCB_CMDHTTP_F_NOUPASS 1<<18 + +/** + * Structure for performing an HTTP request. + * Note that the key and nkey fields indicate the _path_ for the API + */ +typedef struct { + LCB_CMD_BASE; + /**Type of request to issue. LCB_HTTP_TYPE_VIEW will issue a request + * against a random node's view API. LCB_HTTP_TYPE_MANAGEMENT will issue + * a request against a random node's administrative API, and + * LCB_HTTP_TYPE_RAW will issue a request against an arbitrary host. */ + lcb_http_type_t type; + lcb_http_method_t method; /**< HTTP Method to use */ + + /** If the request requires a body (e.g. `PUT` or `POST`) then it will + * go here. Be sure to indicate the length of the body too. */ + const char *body; + + /** Length of the body for the request */ + lcb_SIZE nbody; + + /** If non-NULL, will be assigned a handle which may be used to + * subsequently cancel the request */ + lcb_http_request_t *reqhandle; + + /** For views, set this to `application/json` */ + const char *content_type; + + /** Username to authenticate with, if left empty, will use the credentials + * passed to lcb_create() */ + const char *username; + + /** Password to authenticate with, if left empty, will use the credentials + * passed to lcb_create() */ + const char *password; + + /** If set, this must be a string in the form of `http://host:port`. Should + * only be used for raw requests. */ + const char *host; +} lcb_CMDHTTP; + +/** + * Structure for HTTP responses. + * Note that #rc being `LCB_SUCCESS` does not always indicate that the HTTP + * request itself was successful. It only indicates that the outgoing request + * was submitted to the server and the client received a well-formed HTTP + * response. Check the #hstatus field to see the actual HTTP-level status + * code received. + */ +typedef struct { + LCB_RESP_BASE + /**HTTP status code. The value is only valid if #rc is ::LCB_SUCCESS + * (if #rc is not LCB_SUCCESS then this field may be 0 as the response may + * have not been read/sent) */ + short htstatus; + + /**List of key-value headers. This field itself may be `NULL`. The list + * is terminated by a `NULL` pointer to indicate no more headers. */ + const char * const * headers; + + /**If @ref LCB_CMDHTTP_F_STREAM is true, contains the current chunk + * of response content. Otherwise, contains the entire response body.*/ + const void *body; + /** Length of buffer in #body */ + lcb_SIZE nbody; + /**@private*/ + lcb_http_request_t _htreq; +} lcb_RESPHTTP; + +/** + * @committed + * Issue an HTTP API request. + * @param instance the library handle + * @param cookie cookie to be associated with the request + * @param cmd the command + * @return LCB_SUCCESS if the request was scheduled successfully. + * + * + * @par Simple Response + * @code{.c} + * void http_callback(lcb_t, int, const lcb_RESPBASE *rb) + * { + * const lcb_RESPHTTP *resp = (const lcb_RESPHTTP *)rb; + * if (resp->rc != LCB_SUCCESS) { + * printf("I/O Error for HTTP: %s\n", lcb_strerror(NULL, resp->rc)); + * return; + * } + * printf("Got HTTP Status: %d\n", resp->htstatus); + * printf("Got paylod: %.*s\n", (int)resp->nbody, resp->body); + * const char **hdrp = resp->headers; + * while (*hdrp != NULL) { + * printf("%s: %s\n", hdrp[0], hdrp[1]); + * hdrp += 2; + * } + * } + * @endcode + * + * @par Streaming Response + * If the @ref LCB_CMDHTTP_F_STREAM flag is set in lcb_CMDHTTP::cmdflags then the + * response callback is invoked multiple times as data arrives off the socket. + * @code{.c} + * void http_strm_callback(lcb_t, int, const lcb_RESPBASE *rb) + * { + * const lcb_RESPHTTP *resp = (const lcb_RESPHTTP *)resp; + * if (resp->rflags & LCB_RESP_F_FINAL) { + * if (resp->rc != LCB_SUCCESS) { + * // .... + * } + * const char **hdrp = resp->headers; + * // ... + * } else { + * handle_body(resp->body, resp->nbody); + * } + * } + * @endcode + * + * @par Connection Reuse + * The library will attempt to reuse connections for frequently contacted hosts. + * By default the library will keep one idle connection to each host for a maximum + * of 10 seconds. The number of open idle HTTP connections can be controlled with + * @ref LCB_CNTL_HTTP_POOLSIZE. + * + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_http3(lcb_t instance, const void *cookie, const lcb_CMDHTTP *cmd); + +/** + * @brief Cancel ongoing HTTP request + * + * This API will stop the current request. Any pending callbacks will not be + * invoked any any pending data will not be delivered. Useful for a long running + * request which is no longer needed + * + * @param instance The handle to lcb + * @param request The request handle + * + * @committed + * + * @par Example + * @code{.c} + * lcb_CMDHTTP htcmd = { 0 }; + * populate_htcmd(&htcmd); // dummy function + * lcb_http_request_t reqhandle; + * htcmd.reqhandle = &reqhandle; + * lcb_http3(instance, cookie, &htcmd); + * do_stuff(); + * lcb_cancel_http_request(instance, reqhandle); + * @endcode + */ +LIBCOUCHBASE_API +void +lcb_cancel_http_request(lcb_t instance, lcb_http_request_t request); +/**@} (Group: HTTP) */ + +/**@ingroup lcb-public-api + * @defgroup lcb-cookie User Cookies + * @brief Associate user-defined data with operations + * @details + * + * User-defined pointers may be passed to all operations in the form of a + * `cookie` parameter. This cookie parameter allows any kind of application + * context to be accessible via the callback (in lcb_RESPBASE::cookie). + * + * The library will not inspect or manage the address or contents of the + * cookie; it may live on the stack (especially if using the library + * synchronously), on the heap, or may be NULL altogether. + * + * In addition to per-operation cookies, the library allows the instance itself + * (i.e. the `lcb_t` object) to contain its own cookie. This is helpful when + * there is a wrapper object which needs to be accessed from within the callback + * + * @addtogroup lcb-cookie + * @{ + */ + +/** + * Associate a cookie with an instance of lcb. The _cookie_ is a user defined + * pointer which will remain attached to the specified `lcb_t` for its duration. + * This is the way to associate user data with the `lcb_t`. + * + * @param instance the instance to associate the cookie to + * @param cookie the cookie to associate with this instance. + * + * @attention + * There is no destructor for the specified `cookie` stored with the instance; + * thus you must ensure to manually free resources to the pointer (if it was + * dynamically allocated) when it is no longer required. + * @committed + * + * @code{.c} + * typedef struct { + * const char *status; + * // .... + * } instance_info; + * + * static void bootstrap_callback(lcb_t instance, lcb_error_t err) { + * instance_info *info = (instance_info *)lcb_get_cookie(instance); + * if (err == LCB_SUCCESS) { + * info->status = "Connected"; + * } else { + * info->status = "Error"; + * } + * } + * + * static void do_create(void) { + * instance_info *info = calloc(1, sizeof(*info)); + * // info->status is currently NULL + * // .. create the instance here + * lcb_set_cookie(instance, info); + * lcb_set_bootstrap_callback(instance, bootstrap_callback); + * lcb_connect(instance); + * lcb_wait(instance); + * printf("Status of instance is %s\n", info->status); + * } + * @endcode + */ +LIBCOUCHBASE_API +void lcb_set_cookie(lcb_t instance, const void *cookie); + +/** + * Retrieve the cookie associated with this instance + * @param instance the instance of lcb + * @return The cookie associated with this instance or NULL + * @see lcb_set_cookie() + * @committed + */ +LIBCOUCHBASE_API +const void *lcb_get_cookie(lcb_t instance); +/**@} (Group: Cookies) */ + +/** + * @defgroup lcb-wait Waiting + * @brief Functions for synchronous I/O execution + * @details The lcb_wait() family of functions allow to control when the + * library sends the operations to the cluster and waits for their execution. + * + * It is also possible to use non-blocking I/O with the library + * + * @addtogroup lcb-wait + * @{ + */ + +/** + * @brief Wait for the execution of all batched requests + * + * A batched request is any request which requires network I/O. + * This includes most of the APIs. You should _not_ use this API if you are + * integrating with an asynchronous event loop (i.e. one where your application + * code is invoked asynchronously via event loops). + * + * This function will block the calling thread until either + * + * * All operations have been completed + * * lcb_breakout() is explicitly called + * + * @param instance the instance containing the requests + * @return whether the wait operation failed, or LCB_SUCCESS + * @committed + */ +LIBCOUCHBASE_API +lcb_error_t lcb_wait(lcb_t instance); + +/** + * @volatile + * This function will cause a single "tick" in the underlying event loop, + * causing operations whose I/O can be executed immediately to be sent to + * the server. + * + * Like lcb_wait(), callbacks for operations may be delivered here, however + * some operations may be left incomplete if their I/O could not be processed + * immediately. This function is intended as an optimization for large batches + * of operations - so that some I/O can be completed during the batching process + * itself, and only the remainder of those operations (which would have blocked) + * will be completed with lcb_wait() (which should be invoked after the batch). + * + * This function is mainly useful if there is a significant delay in time + * between each scheduling function, in which I/O may be completed during these + * gaps (thereby emptying the socket's kernel write buffer, and allowing for + * new operations to be added after the interval). Calling this function for + * batches where all data is available up-front may actually make things slower. + * + * @warning + * You must call lcb_wait() at least one after any batch of operations to ensure + * they have been completed. This function is provided as an optimization only. + * + * @return LCB_CLIENT_FEATURE_UNAVAILABLE if the event loop does not support + * the "tick" mode. + */ +LIBCOUCHBASE_API +lcb_error_t lcb_tick_nowait(lcb_t instance); + +/**@brief Flags for lcb_wait3()*/ +typedef enum { + /**Behave like the old lcb_wait()*/ + LCB_WAIT_DEFAULT = 0x00, + + /**Do not check pending operations before running the event loop. By default + * lcb_wait() will traverse the server list to check if any operations are + * pending, and if nothing is pending the function will return without + * running the event loop. This is usually not necessary for applications + * which already _only_ call lcb_wait() when they know they have scheduled + * at least one command. + */ + LCB_WAIT_NOCHECK = 0x01 +} lcb_WAITFLAGS; + +/** + * @committed + * @brief Wait for completion of scheduled operations. + * @param instance the instance + * @param flags flags to modify the behavior of lcb_wait(). Pass 0 to obtain + * behavior identical to lcb_wait(). + */ +LIBCOUCHBASE_API +void lcb_wait3(lcb_t instance, lcb_WAITFLAGS flags); + +/** + * @brief Forcefully break from the event loop. + * + * You may call this function from within any callback to signal to the library + * that it return control to the function calling lcb_wait() as soon as possible. + * Note that if there are pending functions which have not been processed, you + * are responsible for calling lcb_wait() a second time. + * + * @param instance the instance to run the event loop for. + * @committed + */ +LIBCOUCHBASE_API +void lcb_breakout(lcb_t instance); + +/** + * @brief Check if instance is blocked in the event loop + * @param instance the instance to run the event loop for. + * @return non-zero if nobody is waiting for IO interaction + * @uncomitted + */ +LIBCOUCHBASE_API +int lcb_is_waiting(lcb_t instance); +/**@} (Group: Wait) */ + +/** + * @uncommitted + * + * @brief Force the library to refetch the cluster configuration + * + * The library by default employs various heuristics to determine if a new + * configuration is needed from the cluster. However there are some situations + * in which an application may wish to force a refresh of the configuration: + * + * * If a specific node has been failed + * over and the library has received a configuration in which there is no + * master node for a given key, the library will immediately return the error + * `LCB_NO_MATCHING_SERVER` for the given item and will not request a new + * configuration. In this state, the client will not perform any network I/O + * until a request has been made to it using a key that is mapped to a known + * active node. + * + * * The library's heuristics may have failed to detect an error warranting + * a configuration change, but the application either through its own + * heuristics, or through an out-of-band channel knows that the configuration + * has changed. + * + * + * This function is provided as an aid to assist in such situations + * + * If you wish for your application to block until a new configuration is + * received, you _must_ call lcb_wait3() with the LCB_WAIT_NO_CHECK flag as + * this function call is not bound to a specific operation. Additionally there + * is no status notification as to whether this operation succeeded or failed + * (the configuration callback via lcb_set_configuration_callback() may + * provide hints as to whether a configuration was received or not, but by no + * means should be considered to be part of this function's control flow). + * + * In general the use pattern of this function is like so: + * + * @code{.c} + * unsigned retries = 5; + * lcb_error_t err; + * do { + * retries--; + * err = lcb_get(instance, cookie, ncmds, cmds); + * if (err == LCB_NO_MATCHING_SERVER) { + * lcb_refresh_config(instance); + * usleep(100000); + * lcb_wait3(instance, LCB_WAIT_NO_CHECK); + * } else { + * break; + * } + * } while (retries); + * if (err == LCB_SUCCESS) { + * lcb_wait3(instance, 0); // equivalent to lcb_wait(instance); + * } else { + * printf("Tried multiple times to fetch the key, but its node is down\n"); + * } + * @endcode + */ +LIBCOUCHBASE_API +void +lcb_refresh_config(lcb_t instance); + +/** + * @ingroup lcb-public-api + * @defgroup lcb-sched Advanced Scheduling + * @brief Additional functions for scheduling operations + * + * @details + * + * An application may spool multiple operations into the library with the + * option of unspooling previously-spooled operations in case one of + * the operations cannot be spooled. These semantics exist primarily to + * support "all-or-nothing" scheduling found in the V2 API as well as in + * some other wrapping SDKs. + * + * From version 2.4.0 to version 2.5.5, use of the explicit scheduling + * API was mandatory to schedule operations. This API is optional since 2.5.6. + * + * The following operation APIs are low level entry points which create a + * single operation. To use these operation APIs you should call the + * lcb_sched_enter() which creates a virtual scope in which to create operations. + * + * For each of these operation APIs, the actual API call will insert the + * created packet into a "Scheduling Queue" (this is done through + * mcreq_sched_add() which is in mcreq.h). You may add as many items to this + * scheduling queue as you would like. + * + * Note that an operation is only added to the queue if it was able to be + * scheduled properly. If a scheduling failure occurred (for example, if a + * configuration is missing, the command had invalid input, or memory allocation + * failed) then the command will not be placed into the queue. + * + * Once all operations have been scheduled you can call + * lcb_sched_leave() which will place all commands scheduled into the I/O + * queue. + * + * If you wish to _discard_ all scheduled operations (for example, if one of + * them errored, and your application cannot handle partial scheduling failures) + * then you may call lcb_sched_fail() which will release all the resources + * of the packets placed into the temporary queue. + * + * @par Behavior from version 2.5.6 + * + * Starting from version 2.5.6, use of this API is optional. Scheduling functions + * will now check if an empty call to lcb_sched_enter() is present. If no call + * to lcb_sched_enter() is found then the library will implicitly call + * lcb_sched_leave(). + * + * @addtogroup lcb-sched + * @{ + */ + +/** + * @brief Enter a scheduling context. + * + * @uncommitted + * + * A scheduling context is an ephemeral list of + * commands issued to various servers. Operations (like lcb_get3(), lcb_store3()) + * place packets into the current context. + * + * The context mechanism allows you to efficiently pipeline and schedule multiple + * operations of different types and quantities. The network is not touched + * and nothing is scheduled until the context is exited. + * + * @param instance the instance + * + * @code{.c} + * lcb_sched_enter(instance); + * lcb_get3(...); + * lcb_store3(...); + * lcb_counter3(...); + * lcb_sched_leave(instance); + * lcb_wait3(instance, LCB_WAIT_NOCHECK); + * @endcode + */ +LIBCOUCHBASE_API +void lcb_sched_enter(lcb_t instance); + +/** + * @uncommitted + * + * @brief Leave the current scheduling context, scheduling the commands within the + * context to be flushed to the network. + * + * @details This will initiate a network-level flush (depending on the I/O system) + * to the network. For completion-based I/O systems this typically means + * allocating a temporary write context to contain the buffer. If using a + * completion-based I/O module (for example, Windows or libuv) then it is + * recommended to limit the number of calls to one per loop iteration. If + * limiting the number of calls to this function is not possible (for example, + * if the legacy API is being used, or you wish to use implicit scheduling) then + * the flushing may be decoupled from this function - see the documentation for + * lcb_sched_flush(). + * + * @param instance the instance + */ +LIBCOUCHBASE_API +void lcb_sched_leave(lcb_t instance); + + +/** + * @uncommitted + * @brief Fail all commands in the current scheduling context. + * + * The commands placed within the current + * scheduling context are released and are never flushed to the network. + * @param instance + * + * @warning + * This function only affects commands which have a direct correspondence + * to memcached packets. Currently these are commands scheduled by: + * + * * lcb_get3() + * * lcb_rget3() + * * lcb_unlock3() + * * lcb_touch3() + * * lcb_store3() + * * lcb_counter3() + * * lcb_remove3() + * * lcb_stats3() + * * lcb_observe3_ctxnew() + * * lcb_observe_seqno3() + * + * Other commands are _compound_ commands and thus should be in their own + * scheduling context. + */ +LIBCOUCHBASE_API +void lcb_sched_fail(lcb_t instance); + +/** + * @committed + * @brief Request commands to be flushed to the network + * + * By default, the library will implicitly request a flush to the network upon + * every call to lcb_sched_leave(). + * + * [ Note, this does not mean the items are flushed + * and I/O is performed, but it means the relevant event loop watchers are + * activated to perform the operations on the next iteration ]. If + * @ref LCB_CNTL_SCHED_IMPLICIT_FLUSH + * is disabled then this behavior is disabled and the + * application must explicitly call lcb_sched_flush(). This may be considered + * more performant in the cases where multiple discreet operations are scheduled + * in an lcb_sched_enter()/lcb_sched_leave() pair. With implicit flush enabled, + * each call to lcb_sched_leave() will possibly invoke system repeatedly. + */ +LIBCOUCHBASE_API +void lcb_sched_flush(lcb_t instance); + +/**@} (Group: Adanced Scheduling) */ + +/**@ingroup lcb-public-api + * @defgroup lcb-destroy Destroying + * @brief Library destruction routines + * @addtogroup lcb-destroy + * @{ + */ +/** + * Destroy (and release all allocated resources) an instance of lcb. + * Using instance after calling destroy will most likely cause your + * application to crash. + * + * Note that any pending operations will not have their callbacks invoked. + * + * @param instance the instance to destroy. + * @committed + */ +LIBCOUCHBASE_API +void lcb_destroy(lcb_t instance); + +/** + * @brief Callback received when instance is about to be destroyed + * @param cookie cookie passed to lcb_destroy_async() + */ +typedef void (*lcb_destroy_callback)(const void *cookie); + +/** + * @brief Set the callback to be invoked when the instance is destroyed + * asynchronously. + * @return the previous callback. + */ +LIBCOUCHBASE_API +lcb_destroy_callback +lcb_set_destroy_callback(lcb_t, lcb_destroy_callback); +/** + * @brief Asynchronously schedule the destruction of an instance. + * + * This function provides a safe way for asynchronous environments to destroy + * the lcb_t handle without worrying about reentrancy issues. + * + * @param instance + * @param arg a pointer passed to the callback. + * + * While the callback and cookie are optional, they are very much recommended + * for testing scenarios where you wish to ensure that all resources allocated + * by the instance have been closed. Specifically when the callback is invoked, + * all timers (save for the one actually triggering the destruction) and sockets + * will have been closed. + * + * As with lcb_destroy() you may call this function only once. You may not + * call this function together with lcb_destroy as the two are mutually + * exclusive. + * + * If for whatever reason this function is being called in a synchronous + * flow, lcb_wait() must be invoked in order for the destruction to take effect + * + * @see lcb_set_destroy_callback + * + * @committed + */ +LIBCOUCHBASE_API +void lcb_destroy_async(lcb_t instance, const void *arg); +/**@} (Group: Destroy) */ + +/**@}*/ + +/** @private */ +#define LCB_DATATYPE_JSON 0x01 + +/** @private */ +typedef enum { LCB_VALUE_RAW = 0x00, LCB_VALUE_F_JSON = 0x01, LCB_VALUE_F_SNAPPYCOMP } lcb_VALUEFLAGS; + + +/** + * @ingroup lcb-public-api + * @defgroup lcb-cluster-status Cluster Information + * @brief These functions return status information about the handle, the current + * connection, and the number of nodes found within the cluster. + * + * @see lcb_cntl() for more functions to retrieve status info + * + * @addtogroup lcb-cluster-status + * @{ + */ + +/**@brief + * Type of node to retrieve for the lcb_get_node() function + */ +typedef enum { + /** Get an HTTP configuration (Rest API) node */ + LCB_NODE_HTCONFIG = 0x01, + /** Get a data (memcached) node */ + LCB_NODE_DATA = 0x02, + /** Get a view (CAPI) node */ + LCB_NODE_VIEWS = 0x04, + /** Only return a node which is connected, or a node which is known to be up */ + LCB_NODE_CONNECTED = 0x08, + + /** Specifying this flag adds additional semantics which instruct the library + * to search additional resources to return a host, and finally, + * if no host can be found, return the string + * constant @ref LCB_GETNODE_UNAVAILABLE. */ + LCB_NODE_NEVERNULL = 0x10, + + /** Equivalent to `LCB_NODE_HTCONFIG|LCB_NODE_CONNECTED` */ + LCB_NODE_HTCONFIG_CONNECTED = 0x09, + + /**Equivalent to `LCB_NODE_HTCONFIG|LCB_NODE_NEVERNULL`. + * When this is passed, some additional attempts may be made by the library + * to return any kind of host, including searching the initial list of hosts + * passed to the lcb_create() function. */ + LCB_NODE_HTCONFIG_ANY = 0x11 +} lcb_GETNODETYPE; + +/** String constant returned by lcb_get_node() when the @ref LCB_NODE_NEVERNULL + * flag is specified, and no node can be returned */ +#define LCB_GETNODE_UNAVAILABLE "invalid_host:0" + +/** + * @brief Return a string of `host:port` for a node of the given type. + * + * @param instance the instance from which to retrieve the node + * @param type the type of node to return + * @param index the node number if index is out of bounds it will be wrapped + * around, thus there is never an invalid value for this parameter + * + * @return a string in the form of `host:port`. If LCB_NODE_NEVERNULL was specified + * as an option in `type` then the string constant LCB_GETNODE_UNAVAILABLE is + * returned. Otherwise `NULL` is returned if the type is unrecognized or the + * LCB_NODE_CONNECTED option was specified and no connected node could be found + * or a memory allocation failed. + * + * @note The index parameter is _ignored_ if `type` is + * LCB_NODE_HTCONFIG|LCB_NODE_CONNECTED as there will always be only a single + * HTTP bootstrap node. + * + * @code{.c} + * const char *viewnode = lcb_get_node(instance, LCB_NODE_VIEWS, 0); + * // Get the connected REST endpoint: + * const char *restnode = lcb_get_node(instance, LCB_NODE_HTCONFIG|LCB_NODE_CONNECTED, 0); + * if (!restnode) { + * printf("Instance not connected via HTTP!\n"); + * } + * @endcode + * + * Iterate over all the data nodes: + * @code{.c} + * unsigned ii; + * for (ii = 0; ii < lcb_get_num_servers(instance); ii++) { + * const char *kvnode = lcb_get_node(instance, LCB_NODE_DATA, ii); + * if (kvnode) { + * printf("KV node %s exists at index %u\n", kvnode, ii); + * } else { + * printf("No node for index %u\n", ii); + * } + * } + * @endcode + * + * @committed + */ +LIBCOUCHBASE_API +const char * +lcb_get_node(lcb_t instance, lcb_GETNODETYPE type, unsigned index); + +/** + * @committed + * + * @brief Get the target server for a given key. + * + * This is a convenience function wrapping around the vBucket API which allows + * you to retrieve the target node (the node which will be contacted) when + * performing KV operations involving the key. + * + * @param instance the instance + * @param key the key to use + * @param nkey the length of the key + * @return a string containing the hostname, or NULL on error. + * + * Since this is a convenience function, error details are not contained here + * in favor of brevity. Use the full vBucket API for more powerful functions. + */ +LIBCOUCHBASE_API +const char * +lcb_get_keynode(lcb_t instance, const void *key, size_t nkey); + +/** + * @brief Get the number of the replicas in the cluster + * + * @param instance The handle to lcb + * @return -1 if the cluster wasn't configured yet, and number of replicas + * otherwise. This may be `0` if there are no replicas. + * @committed + */ +LIBCOUCHBASE_API +lcb_S32 lcb_get_num_replicas(lcb_t instance); + +/** + * @brief Get the number of the nodes in the cluster + * @param instance The handle to lcb + * @return -1 if the cluster wasn't configured yet, and number of nodes otherwise. + * @committed + */ +LIBCOUCHBASE_API +lcb_S32 lcb_get_num_nodes(lcb_t instance); + + +/** + * @brief Get a list of nodes in the cluster + * + * @return a NULL-terminated list of 0-terminated strings consisting of + * node hostnames:admin_ports for the entire cluster. + * The storage duration of this list is only valid until the + * next call to a libcouchbase function and/or when returning control to + * libcouchbase' event loop. + * + * @code{.c} + * const char * const * curp = lcb_get_server_list(instance); + * for (; *curp; curp++) { + * printf("Have node %s\n", *curp); + * } + * @endcode + * @committed + */ +LIBCOUCHBASE_API +const char *const *lcb_get_server_list(lcb_t instance); + +/** + * @volatile + * @brief Write a textual dump to a file. + * + * This function will inspect the various internal structures of the current + * client handle (indicated by `instance`) and write the state information + * to the file indicated by `fp`. + * @param instance the handle to dump + * @param fp the file to which the dump should be written + * @param flags a set of modifiers (of @ref lcb_DUMPFLAGS) indicating what + * information to dump. Note that a standard set of information is always + * dumped, but by default more verbose information is hidden, and may be + * enabled with these flags. + */ +LIBCOUCHBASE_API +void +lcb_dump(lcb_t instance, FILE *fp, lcb_U32 flags); +/**@} (Group: Cluster Info) */ + +/** + * @ingroup lcb-public-api + * @defgroup lcb-cntl Settings + * @brief Get/Set Library Options + * + * @details + * + * The lcb_cntl() function and its various helpers are the means by which to + * modify settings within the library + * + * @addtogroup lcb-cntl + * @see + * @{ + */ + +/** + * This function exposes an ioctl/fcntl-like interface to read and write + * various configuration properties to and from an lcb_t handle. + * + * @param instance The instance to modify + * + * @param mode One of LCB_CNTL_GET (to retrieve a setting) or LCB_CNTL_SET + * (to modify a setting). Note that not all configuration properties + * support SET. + * + * @param cmd The specific command/property to modify. This is one of the + * LCB_CNTL_* constants defined in this file. Note that it is safe + * (and even recommanded) to use the raw numeric value (i.e. + * to be backwards and forwards compatible with libcouchbase + * versions), as they are not subject to change. + * + * Using the actual value may be useful in ensuring your application + * will still compile with an older libcouchbase version (though + * you may get a runtime error (see return) if the command is not + * supported + * + * @param arg The argument passed to the configuration handler. + * The actual type of this pointer is dependent on the + * command in question. Typically for GET operations, the + * value of 'arg' is set to the current configuration value; + * and for SET operations, the current configuration is + * updated with the contents of *arg. + * + * @return ::LCB_NOT_SUPPORTED if the code is unrecognized + * @return ::LCB_EINVAL if there was a problem with the argument + * (typically for LCB_CNTL_SET) other error codes depending on the command. + * + * The following error codes are returned if the ::LCB_CNTL_DETAILED_ERRCODES + * are enabled. + * + * @return ::LCB_ECTL_UNKNOWN if the code is unrecognized + * @return ::LCB_ECTL_UNSUPPMODE An invalid _mode_ was passed + * @return ::LCB_ECTL_BADARG if the value was invalid + * + * @committed + * + * @see lcb_cntl_setu32() + * @see lcb_cntl_string() + */ +LIBCOUCHBASE_API +lcb_error_t lcb_cntl(lcb_t instance, int mode, int cmd, void *arg); + +/** + * Alternate way to set configuration settings by passing a string key + * and value. This may be used to provide a simple interface from a command + * line or higher level language to allow the setting of specific key-value + * pairs. + * + * The format for the value is dependent on the option passed, the following + * value types exist: + * + * - **Timeout**. A _timeout_ value can either be specified as fractional + * seconds (`"1.5"` for 1.5 seconds), or in microseconds (`"1500000"`). + * - **Number**. This is any valid numerical value. This may be signed or + * unsigned depending on the setting. + * - **Boolean**. This specifies a boolean. A true value is either a positive + * numeric value (i.e. `"1"`) or the string `"true"`. A false value + * is a zero (i.e. `"0"`) or the string `"false"`. + * - **Float**. This is like a _Number_, but also allows fractional specification, + * e.g. `"2.4"`. + * + * | Code | Name | Type + * |------|------|----- + * |@ref LCB_CNTL_OP_TIMEOUT | `"operation_timeout"` | Timeout | + * |@ref LCB_CNTL_VIEW_TIMEOUT | `"view_timeout"` | Timeout | + * |@ref LCB_CNTL_DURABILITY_TIMEOUT | `"durability_timeout"` | Timeout | + * |@ref LCB_CNTL_DURABILITY_INTERVAL | `"durability_interval"`| Timeout | + * |@ref LCB_CNTL_HTTP_TIMEOUT | `"http_timeout"` | Timeout | + * |@ref LCB_CNTL_RANDOMIZE_BOOTSTRAP_HOSTS | `"randomize_nodes"` | Boolean| + * |@ref LCB_CNTL_CONFERRTHRESH | `"error_thresh_count"`| Number (Positive)| + * |@ref LCB_CNTL_CONFDELAY_THRESH |`"error_thresh_delay"` | Timeout | + * |@ref LCB_CNTL_CONFIGURATION_TIMEOUT | `"config_total_timeout"`|Timeout| + * |@ref LCB_CNTL_CONFIG_NODE_TIMEOUT | `"config_node_timeout"` | Timeout | + * |@ref LCB_CNTL_CONFIGCACHE | `"config_cache"` | Path | + * |@ref LCB_CNTL_DETAILED_ERRCODES | `"detailed_errcodes"` | Boolean | + * |@ref LCB_CNTL_HTCONFIG_URLTYPE | `"http_urlmode"` | Number (values are the constant values) | + * |@ref LCB_CNTL_RETRY_BACKOFF | `"retry_backoff"` | Float | + * |@ref LCB_CNTL_HTTP_POOLSIZE | `"http_poolsize"` | Number | + * |@ref LCB_CNTL_VBGUESS_PERSIST | `"vbguess_persist"` | Boolean | + * + * + * @committed - Note, the actual API call is considered committed and will + * not disappear, however the existence of the various string settings are + * dependendent on the actual settings they map to. It is recommended that + * applications use the numerical lcb_cntl() as the string names are + * subject to change. + * + * @see lcb_cntl() + * @see lcb-cntl-settings + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_cntl_string(lcb_t instance, const char *key, const char *value); + +/** +* @brief Convenience function to set a value as an lcb_U32 +* @param instance +* @param cmd setting to modify +* @param arg the new value +* @return see lcb_cntl() for details +* @committed +*/ +LIBCOUCHBASE_API +lcb_error_t lcb_cntl_setu32(lcb_t instance, int cmd, lcb_U32 arg); + +/** +* @brief Retrieve an lcb_U32 setting +* @param instance +* @param cmd setting to retrieve +* @return the value. +* @warning This function does not return an error code. Ensure that the cntl is +* correct for this version, or use lcb_cntl() directly. +* @committed +*/ +LIBCOUCHBASE_API +lcb_U32 lcb_cntl_getu32(lcb_t instance, int cmd); + +/** + * Determine if a specific control code exists + * @param ctl the code to check for + * @return 0 if it does not exist, nonzero if it exists. + */ +LIBCOUCHBASE_API +int +lcb_cntl_exists(int ctl); +/**@}*/ /* settings */ + +/** + * @ingroup lcb-public-api + * @defgroup lcb-timings Timings + * @brief Determine how long operations are taking to be completed + * + * libcouchbase provides a simple form of per-command timings you may use + * to figure out the current lantency for the request-response cycle as + * generated by your application. Please note that these numbers are not + * necessarily accurate as you may affect the timing recorded by doing + * work in the event loop. + * + * The time recorded with this library is the time elapsed from the + * command being called, and the response packet being received from the + * server. Everything the application does before driving the event loop + * will affect the timers. + * + * The function lcb_enable_timings() is used to enable the timings for + * the given instance, and lcb_disable_timings is used to disable the + * timings. The overhead of using the timers should be negligible. + * + * The function lcb_get_timings is used to retrieve the current timing. + * values from the given instance. The cookie is passed transparently to + * the callback function. + * + * Here is an example of the usage of this module: + * + * @code{.c} + * #include + * + * static void callback( + * lcb_t instance, const void *cookie, lcb_timeunit_t timeunit, lcb_U32 min, + * lcb_U32 max, lcb_U32 total, lcb_U32 maxtotal) + * { + * FILE* out = (void*)cookie; + * int num = (float)10.0 * (float)total / ((float)maxtotal); + * fprintf(out, "[%3u - %3u]", min, max); + * switch (timeunit) { + * case LCB_TIMEUNIT_NSEC: + * fprintf(out, "ns"); + * break; + * case LCB_TIMEUNIT_USEC: + * fprintf(out, "us"); + * break; + * case LCB_TIMEUNIT_MSEC: + * fsprintf(out, "ms"); + * break; + * case LCB_TIMEUNIT_SEC: + * fprintf(out, "s "); + * break; + * default: + * ; + * } + * + * fprintf(out, " |"); + * for (int ii = 0; ii < num; ++ii) { + * fprintf(out, "#"); + * } + * fprintf(out, " - %u\n", total); + * } + * + * + * lcb_enable_timings(instance); + * ... do a lot of operations ... + * fprintf(stderr, " +---------+\n" + * lcb_get_timings(instance, stderr, callback); + * fprintf(stderr, " +---------+\n" + * lcb_disable_timings(instance); + * @endcode + * + * @addtogroup lcb-timings + * @{ + */ + +/** + * @brief Time units reported by lcb_get_timings() + */ +enum lcb_timeunit_t { + LCB_TIMEUNIT_NSEC = 0, /**< @brief Time is in nanoseconds */ + LCB_TIMEUNIT_USEC = 1, /**< @brief Time is in microseconds */ + LCB_TIMEUNIT_MSEC = 2, /**< @brief Time is in milliseconds */ + LCB_TIMEUNIT_SEC = 3 /**< @brief Time is in seconds */ +}; +typedef enum lcb_timeunit_t lcb_timeunit_t; + +/** + * Start recording timing metrics for the different operations. + * The timer is started when the command is called (and the data + * spooled to the server), and the execution time is the time until + * we parse the response packets. This means that you can affect + * the timers by doing a lot of other stuff before checking if + * there is any results available.. + * + * @param instance the handle to lcb + * @return Status of the operation. + * @committed + */ +LIBCOUCHBASE_API +lcb_error_t lcb_enable_timings(lcb_t instance); + + +/** + * Stop recording (and release all resources from previous measurements) + * timing metrics. + * + * @param instance the handle to lcb + * @return Status of the operation. + * @committed + */ +LIBCOUCHBASE_API +lcb_error_t lcb_disable_timings(lcb_t instance); + +/** + * The following function is called for each bucket in the timings + * histogram when you call lcb_get_timings. + * You are guaranteed that the callback will be called with the + * lowest [min,max] range first. + * + * @param instance the handle to lcb + * @param cookie the cookie you provided that allows you to pass + * arbitrary user data to the callback + * @param timeunit the "scale" for the values + * @param min The lower bound for this histogram bucket + * @param max The upper bound for this histogram bucket + * @param total The number of hits in this histogram bucket + * @param maxtotal The highest value in all of the buckets + */ +typedef void (*lcb_timings_callback)(lcb_t instance, + const void *cookie, + lcb_timeunit_t timeunit, + lcb_U32 min, + lcb_U32 max, + lcb_U32 total, + lcb_U32 maxtotal); + +/** + * Get the timings histogram + * + * @param instance the handle to lcb + * @param cookie a cookie that will be present in all of the callbacks + * @param callback Callback to invoke which will handle the timings + * @return Status of the operation. + * @committed + */ +LIBCOUCHBASE_API +lcb_error_t lcb_get_timings(lcb_t instance, + const void *cookie, + lcb_timings_callback callback); +/**@} (Group: Timings) */ + +/** +* @ingroup lcb-public-api +* @defgroup lcb-build-info Build Information +* @brief Get library version and supported features +* @details +* These functions and macros may be used to conditionally compile features +* depending on the version of the library being used. They may also be used +* to employ various features at runtime and to retrieve the version for +* informational purposes. +* @addtogroup lcb-build-info +* @{ +*/ + +#if !defined(LCB_VERSION_STRING) || defined(__LCB_DOXYGEN__) +/** @brief libcouchbase version string */ +#define LCB_VERSION_STRING "unknown" +#endif + +#if !defined(LCB_VERSION) || defined(__LCB_DOXYGEN__) +/**@brief libcouchbase hex version + * + * This number contains the hexadecimal representation of the library version. + * It is in a format of `0xXXYYZZ` where `XX` is the two digit major version + * (e.g. `02`), `YY` is the minor version (e.g. `05`) and `ZZ` is the patch + * version (e.g. `24`). + * + * For example: + * + * String |Hex + * ---------|--------- + * 2.0.0 | 0x020000 + * 2.1.3 | 0x020103 + * 3.0.15 | 0x030015 + */ +#define LCB_VERSION 0x000000 +#endif + +#if !defined(LCB_VERSION_CHANGESET) || defined(__LCB_DOXYGEN__) +/**@brief The SCM revision ID. @see LCB_CNTL_CHANGESET */ +#define LCB_VERSION_CHANGESET "0xdeadbeef" +#endif + +/** + * Get the version of the library. + * + * @param version where to store the numeric representation of the + * version (or NULL if you don't care) + * + * @return the textual description of the version ('\0' + * terminated). Do not try to release this string. + * + */ +LIBCOUCHBASE_API +const char *lcb_get_version(lcb_U32 *version); + +/** Global/extern variable containing the version of the library */ +LIBCOUCHBASE_API LCB_EXTERN_VAR +const lcb_U32 lcb_version_g; + +/**@brief Whether the library has SSL support*/ +#define LCB_SUPPORTS_SSL 1 +/**@brief Whether the library has experimental compression support */ +#define LCB_SUPPORTS_SNAPPY 2 + +/** + * @committed + * Determine if this version has support for a particularl feature + * @param n the feature ID to check for + * @return 0 if not supported, nonzero if supported. + */ +LIBCOUCHBASE_API +int +lcb_supports_feature(int n); + +/**@} (Group: Build Info) */ + + +/** + * Functions to allocate and free memory related to libcouchbase. This is + * mainly for use on Windows where it is possible that the DLL and EXE + * are using two different CRTs + */ +LIBCOUCHBASE_API +void *lcb_mem_alloc(lcb_SIZE size); + +/** Use this to free memory allocated with lcb_mem_alloc */ +LIBCOUCHBASE_API +void lcb_mem_free(void *ptr); + +/** + * @private + * + * These two functions unconditionally start and stop the event loop. These + * should be used _only_ when necessary. Use lcb_wait and lcb_breakout + * for safer variants. + * + * Internally these proxy to the run_event_loop/stop_event_loop calls + */ +LCB_INTERNAL_API +void lcb_run_loop(lcb_t instance); + +/** @private */ +LCB_INTERNAL_API +void lcb_stop_loop(lcb_t instance); + +/** @private */ +/* This returns the library's idea of time */ +LCB_INTERNAL_API +lcb_U64 lcb_nstime(void); + +typedef enum { + /** Dump the raw vbucket configuration */ + LCB_DUMP_VBCONFIG = 0x01, + /** Dump information about each packet */ + LCB_DUMP_PKTINFO = 0x02, + /** Dump memory usage/reservation information about buffers */ + LCB_DUMP_BUFINFO = 0x04, + /** Dump everything */ + LCB_DUMP_ALL = 0xff +} lcb_DUMPFLAGS; + +/** Internal histogram APIs, used by pillowfight and others */ +struct lcb_histogram_st; +typedef struct lcb_histogram_st lcb_HISTOGRAM; + +/** + * @private + * Create a histogram structure + * @return a new histogram structure + */ +LCB_INTERNAL_API +lcb_HISTOGRAM * +lcb_histogram_create(void); + +/** + * @private free a histogram structure + * @param hg the histogram + */ +LCB_INTERNAL_API +void +lcb_histogram_destroy(lcb_HISTOGRAM *hg); + +/** + * @private + * Add an entry to a histogram structure + * @param hg the histogram + * @param duration the duration in nanoseconds + */ +LCB_INTERNAL_API +void +lcb_histogram_record(lcb_HISTOGRAM *hg, lcb_U64 duration); + +typedef void (*lcb_HISTOGRAM_CALLBACK) + (const void *cookie, lcb_timeunit_t timeunit, lcb_U32 min, lcb_U32 max, + lcb_U32 total, lcb_U32 maxtotal); + +/** + * @private + * Repeatedly invoke a callback for all entries in the histogram + * @param hg the histogram + * @param cookie pointer passed to callback + * @param cb callback to invoke + */ +LCB_INTERNAL_API +void +lcb_histogram_read(const lcb_HISTOGRAM *hg, const void *cookie, + lcb_HISTOGRAM_CALLBACK cb); + +/** + * Print the histogram to the specified FILE. + * + * This essentially outputs the same raw information as lcb_histogram_read(), + * except it prints in implementation-defined format. It's simpler to use + * than lcb_histogram_read, but less flexible. + * + * @param hg the histogram + * @param stream File to print the histogram to. + */ +LCB_INTERNAL_API +void lcb_histogram_print(lcb_HISTOGRAM* hg, FILE* stream); + +/* Post-include some other headers */ +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#include +#include +#include +#include +#endif /* LIBCOUCHBASE_COUCHBASE_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/deprecated.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/deprecated.h new file mode 100644 index 00000000..72abba87 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/deprecated.h @@ -0,0 +1,300 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_DEPRECATED_H +#define LCB_DEPRECATED_H +#ifndef LIBCOUCHBASE_COUCHBASE_H +#error "include first" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/**@file + * Deprecated APIs + */ + +#define LCB_DEPR_API(X) LIBCOUCHBASE_API LCB_DEPRECATED(X) +#define LCB_DEPR_API2(X, reason) LIBCOUCHBASE_API LCB_DEPRECATED2(X, reason) + +/** @deprecated Use @ref LCB_CNTL_IP6POLICY via lcb_cntl() */ +LCB_DEPR_API2(void lcb_behavior_set_ipv6(lcb_t instance, lcb_ipv6_t mode), "Use LCB_CNTL_IP6POLICY"); +/** @deprecated Use @ref LCB_CNTL_IP6POLICY via lcb_cntl() */ +LCB_DEPR_API2(lcb_ipv6_t lcb_behavior_get_ipv6(lcb_t instance), "Use LCB_CNTL_IP6POLICY"); +/** @deprecated Use @ref LCB_CNTL_CONFERRTHRESH via lcb_cntl() */ +LCB_DEPR_API2(void lcb_behavior_set_config_errors_threshold(lcb_t instance, lcb_size_t num_events), + "Use LCB_CNTL_CONFERRTHRESH"); +/** @deprecated Use @ref LCB_CNTL_CONFERRTHRESH via lcb_cntl() */ +LCB_DEPR_API2(lcb_size_t lcb_behavior_get_config_errors_threshold(lcb_t instance), + "Use LCB_CNTL_CONFERRTHRESH"); +/** @deprecated Use @ref LCB_CNTL_OP_TIMEOUT via lcb_cntl() */ +LCB_DEPR_API2(void lcb_set_timeout(lcb_t instance, lcb_uint32_t usec), + "Use LCB_CNTL_OP_TIMEOUT"); +/** @deprecated Use @ref LCB_CNTL_OP_TIMEOUT via lcb_cntl() */ +LCB_DEPR_API2(lcb_uint32_t lcb_get_timeout(lcb_t instance), + "Use LCB_CNTL_OP_TIMEOUT"); +/** @deprecated Use @ref LCB_CNTL_VIEW_TIMEOUT via lcb_cntl() */ +LCB_DEPR_API2(void lcb_set_view_timeout(lcb_t instance, lcb_uint32_t usec), + "Use LCB_CNTL_VIEW_TIMEOUT"); +/** @deprecated Use @ref LCB_CNTL_VIEW_TIMEOUT via lcb_cntl() */ +LCB_DEPR_API2(lcb_uint32_t lcb_get_view_timeout(lcb_t instance), + "Use LCB_CNTL_VIEW_TIMEOUT"); + +/** + * @deprecated Do not use this function. Check the error code of the specific operation + * to determine if something succeeded or not. Because the library has many + * asynchronous "flows" of control, determining the "last error" is not very + * fruitful. Since most API calls are themselves only schedule-related, they cannot + * possibly derive a "Real" error either + */ +LCB_DEPR_API2(lcb_error_t lcb_get_last_error(lcb_t instance), + "This function does not returning meaningful information. Use the operation callbacks " + "and/or bootstrap callbacks"); + +/** @deprecated This function does nothing */ +LCB_DEPR_API2(void lcb_flush_buffers(lcb_t instance, const void *cookie), + "This function does nothing"); + +/** I'm not sure what uses this anymore */ +typedef enum { + LCB_VBUCKET_STATE_ACTIVE = 1, /* Actively servicing a vbucket. */ + LCB_VBUCKET_STATE_REPLICA = 2, /* Servicing a vbucket as a replica only. */ + LCB_VBUCKET_STATE_PENDING = 3, /* Pending active. */ + LCB_VBUCKET_STATE_DEAD = 4 /* Not in use, pending deletion. */ +} lcb_vbucket_state_t; + +typedef void (*lcb_error_callback)(lcb_t instance, lcb_error_t error, const char *errinfo); + +/** + * @deprecated Use the logging API (@ref LCB_CNTL_LOGGER) instead to receive error + * information. For programmatic errors, use the operations interface. For bootstrap + * status, use lcb_get_bootstrap_status() and lcb_set_bootstrap_callback() + */ +LCB_DEPR_API2(lcb_error_callback lcb_set_error_callback(lcb_t, lcb_error_callback), + "This function only reports bootstrap errors. Use lcb_set_bootstrap_callback instead"); + + + +/** + * Timer stuff is deprecated. It should not be used externally, and internal + * code should use the new lcbio_timer_ functions + */ +struct lcb_timer_st; +typedef struct lcb_timer_st *lcb_timer_t; +typedef void (*lcb_timer_callback)(lcb_timer_t timer, lcb_t instance, const void *cookie); +LCB_DEPR_API(lcb_timer_t lcb_timer_create(lcb_t instance, const void *command_cookie, lcb_uint32_t usec, int periodic, lcb_timer_callback callback, lcb_error_t *error)); +LCB_DEPR_API(lcb_error_t lcb_timer_destroy(lcb_t instance, lcb_timer_t timer)); + +typedef enum lcb_compat_t { LCB_MEMCACHED_CLUSTER = 0x00, LCB_CACHED_CONFIG = 0x01 } lcb_compat_t; +typedef lcb_compat_t lcb_cluster_t; +struct lcb_memcached_st { const char *serverlist; const char *username; const char *password; }; +struct lcb_cached_config_st { + const char *cachefile; + struct lcb_create_st createopt; +}; + +/** + * @deprecated + * Use @ref LCB_CNTL_CONFIGCACHE for configuration cache options + */ +#define lcb_create_compat lcb__create_compat_230 +LCB_DEPR_API2(lcb_error_t lcb_create_compat(lcb_compat_t type, const void *specific, lcb_t *instance, struct lcb_io_opt_st *io), + "Use memcached:// for legacy memcached. For config cache, use LCB_CNTL_CONFIGCACHE"); + +typedef enum { + LCB_ASYNCHRONOUS = 0x00, + LCB_SYNCHRONOUS = 0xff +} lcb_syncmode_t; +LCB_DEPR_API2(void lcb_behavior_set_syncmode(lcb_t, lcb_syncmode_t), + "Syncmode will be removed in future versions. Use lcb_wait() instead"); +LCB_DEPR_API2(lcb_syncmode_t lcb_behavior_get_syncmode(lcb_t), + "Syncmode will be removed in future versions. Use lcb_wait() instead"); + +LCB_DEPR_API2(const char *lcb_get_host(lcb_t), + "Use lcb_get_node(instance, LCB_NODE_HTCONFIG, 0)"); +LCB_DEPR_API2(const char *lcb_get_port(lcb_t), + "Use lcb_get_node(instance, LCB_NODE_HTCONFIG, 0)"); + +/* STRUCTURE ABBREV ID MAXVER */ +#define LCB_XSSIZES(X) \ + X(struct lcb_create_st, C_ST, 0, 3) \ + X(struct lcb_create_io_ops_st, C_I_O_ST, 1, 1) \ + \ + X(struct lcb_get_cmd_st, G_C_ST, 2, 0) \ + X(struct lcb_get_replica_cmd_st, G_R_C_ST, 3, 1) \ + X(struct lcb_unlock_cmd_st, U_C_ST, 4, 0) \ + X(lcb_touch_cmd_t, T_C_ST, 5, 0) \ + X(struct lcb_store_cmd_st, S_C_ST, 6, 0) \ + X(struct lcb_arithmetic_cmd_st, A_C_ST, 7, 0) \ + X(struct lcb_observe_cmd_st, O_C_ST, 8, 0) \ + X(struct lcb_remove_cmd_st, R_C_ST, 9, 0) \ + X(struct lcb_http_cmd_st, H_C_ST, 10, 1) \ + X(struct lcb_server_stats_cmd_st, S_S_C_ST, 11, 0) \ + X(struct lcb_server_version_cmd_st, S_V_C_ST, 12, 0) \ + X(struct lcb_verbosity_cmd_st, V_C_ST, 13, 0) \ + X(struct lcb_flush_cmd_st, F_C_ST, 14, 0) \ + \ + X(lcb_get_resp_t, G_R_ST, 15, 0) \ + X(lcb_store_resp_t, S_R_ST, 16, 0) \ + X(lcb_remove_resp_t, R_R_ST, 17, 0) \ + X(lcb_touch_resp_t, T_R_ST, 18, 0) \ + X(lcb_unlock_resp_t, U_R_ST, 19, 0) \ + X(lcb_arithmetic_resp_t, A_R_ST, 20, 0) \ + X(lcb_observe_resp_t, O_R_ST, 21, 0) \ + X(lcb_http_resp_t, H_R_ST, 22, 0) \ + X(lcb_server_stat_resp_t, S_S_R_ST, 23, 0) \ + X(lcb_server_version_resp_t, S_V_R_ST, 24, 0) \ + X(lcb_verbosity_resp_t, V_R_ST, 25, 0) \ + X(lcb_flush_resp_t, F_R_ST, 26, 0) + +typedef enum { +#define X(sname, sabbrev, idval, vernum) \ + LCB_##sabbrev##_ID = idval, LCB_##sabbrev##_V = vernum, + LCB_XSSIZES(X) + LCB_ST_M = 26 +#undef X +} lcb__STRUCTSIZES; + +#define lcb_verify_compiler_setup() ( \ + lcb_verify_struct_size(LCB_C_ST_ID, LCB_C_ST_V, sizeof(struct lcb_create_st)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_C_I_O_ST_ID, LCB_C_I_O_ST_V, sizeof(struct lcb_create_io_ops_st)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_G_C_ST_ID, LCB_G_C_ST_V, sizeof(struct lcb_get_cmd_st)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_G_R_C_ST_ID, LCB_G_R_C_ST_V, sizeof(struct lcb_get_replica_cmd_st)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_U_C_ST_ID, LCB_U_C_ST_V, sizeof(struct lcb_unlock_cmd_st)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_T_C_ST_ID, LCB_T_C_ST_V, sizeof(lcb_touch_cmd_t)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_S_C_ST_ID, LCB_S_C_ST_V, sizeof(struct lcb_store_cmd_st)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_A_C_ST_ID, LCB_A_C_ST_V, sizeof(struct lcb_arithmetic_cmd_st)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_O_C_ST_ID, LCB_O_C_ST_V, sizeof(struct lcb_observe_cmd_st)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_R_C_ST_ID, LCB_R_C_ST_V, sizeof(struct lcb_remove_cmd_st)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_H_C_ST_ID, LCB_H_C_ST_V, sizeof(struct lcb_http_cmd_st)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_S_S_C_ST_ID, LCB_S_S_C_ST_V, sizeof(struct lcb_server_stats_cmd_st)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_S_V_C_ST_ID, LCB_S_V_C_ST_V, sizeof(struct lcb_server_version_cmd_st)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_V_C_ST_ID, LCB_V_C_ST_V, sizeof(struct lcb_verbosity_cmd_st)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_F_C_ST_ID, LCB_F_C_ST_V, sizeof(struct lcb_flush_cmd_st)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_G_R_ST_ID, LCB_G_R_ST_V, sizeof(lcb_get_resp_t)) == LCB_SUCCESS &&\ + lcb_verify_struct_size(LCB_S_R_ST_ID, LCB_S_R_ST_V, sizeof(lcb_store_resp_t)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_R_R_ST_ID, LCB_R_R_ST_V, sizeof(lcb_remove_resp_t)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_T_R_ST_ID, LCB_T_R_ST_V, sizeof(lcb_touch_resp_t)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_U_R_ST_ID, LCB_U_R_ST_V, sizeof(lcb_unlock_resp_t)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_A_R_ST_ID, LCB_A_R_ST_V, sizeof(lcb_arithmetic_resp_t)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_O_R_ST_ID, LCB_O_R_ST_V, sizeof(lcb_observe_resp_t)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_H_R_ST_ID, LCB_H_R_ST_V, sizeof(lcb_http_resp_t)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_S_S_R_ST_ID, LCB_S_S_R_ST_V, sizeof(lcb_server_stat_resp_t)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_S_V_R_ST_ID, LCB_S_V_R_ST_V, sizeof(lcb_server_version_resp_t)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_V_R_ST_ID, LCB_V_R_ST_V, sizeof(lcb_verbosity_resp_t)) == LCB_SUCCESS && \ + lcb_verify_struct_size(LCB_F_R_ST_ID, LCB_F_R_ST_V, sizeof(lcb_flush_resp_t)) == LCB_SUCCESS \ +) + +/** + * Verify that libcouchbase and yourself have the same size for a + * certain version of a struct. Using different alignment / struct + * packing will give you strange results.. + * + * @param id the id of the structure to check + * @param version the version of the structure to check + * @param size the expected size of the structure + * @return LCB_SUCCESS on success + */ +LIBCOUCHBASE_API +lcb_error_t lcb_verify_struct_size(lcb_uint32_t id, lcb_uint32_t version, + lcb_size_t size); + +/** Deprecated cntls */ + +/**@deprecated It is currently not possible to adjust buffer sizes */ +#define LCB_CNTL_RBUFSIZE 0x02 +/**@deprecated It is currently not possible to adjust buffer sizes */ +#define LCB_CNTL_WBUFSIZE 0x03 +/** @deprecated */ +#define LCB_CNTL_SYNCMODE 0x0a +/**@deprecated Initial connections are always attempted */ +#define LCB_CNTL_SKIP_CONFIGURATION_ERRORS_ON_CONNECT 0x13 + +/**@deprecated - Use error classifiers */ +#define lcb_is_error_enomem(a) ((a == LCB_CLIENT_ENOMEM) || (a == LCB_ENOMEM)) +/**@deprecated - Use error classifiers */ +#define lcb_is_error_etmpfail(a) ((a == LCB_CLIENT_ETMPFAIL) || (a == LCB_ETMPFAIL)) + +typedef enum { + LCB_CONFIGURATION_NEW = 0x00, + LCB_CONFIGURATION_CHANGED = 0x01, + LCB_CONFIGURATION_UNCHANGED = 0x02 +} lcb_configuration_t; + +typedef void (*lcb_configuration_callback)(lcb_t instance, lcb_configuration_t config); + +/**@deprecated - Use lcb_set_bootstrap_callback() */ +LCB_DEPR_API2( + lcb_configuration_callback lcb_set_configuration_callback(lcb_t, lcb_configuration_callback), + "use lcb_set_bootstrap_callback() to determine when client is ready"); + +/* Deprecated HTTP "Status Aliases" */ +typedef enum { + LCB_HTTP_STATUS_CONTINUE = 100, + LCB_HTTP_STATUS_SWITCHING_PROTOCOLS = 101, + LCB_HTTP_STATUS_PROCESSING = 102, + LCB_HTTP_STATUS_OK = 200, + LCB_HTTP_STATUS_CREATED = 201, + LCB_HTTP_STATUS_ACCEPTED = 202, + LCB_HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203, + LCB_HTTP_STATUS_NO_CONTENT = 204, + LCB_HTTP_STATUS_RESET_CONTENT = 205, + LCB_HTTP_STATUS_PARTIAL_CONTENT = 206, + LCB_HTTP_STATUS_MULTI_STATUS = 207, + LCB_HTTP_STATUS_MULTIPLE_CHOICES = 300, + LCB_HTTP_STATUS_MOVED_PERMANENTLY = 301, + LCB_HTTP_STATUS_FOUND = 302, + LCB_HTTP_STATUS_SEE_OTHER = 303, + LCB_HTTP_STATUS_NOT_MODIFIED = 304, + LCB_HTTP_STATUS_USE_PROXY = 305, + LCB_HTTP_STATUS_UNUSED = 306, + LCB_HTTP_STATUS_TEMPORARY_REDIRECT = 307, + LCB_HTTP_STATUS_BAD_REQUEST = 400, + LCB_HTTP_STATUS_UNAUTHORIZED = 401, + LCB_HTTP_STATUS_PAYMENT_REQUIRED = 402, + LCB_HTTP_STATUS_FORBIDDEN = 403, + LCB_HTTP_STATUS_NOT_FOUND = 404, + LCB_HTTP_STATUS_METHOD_NOT_ALLOWED = 405, + LCB_HTTP_STATUS_NOT_ACCEPTABLE = 406, + LCB_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407, + LCB_HTTP_STATUS_REQUEST_TIMEOUT = 408, + LCB_HTTP_STATUS_CONFLICT = 409, + LCB_HTTP_STATUS_GONE = 410, + LCB_HTTP_STATUS_LENGTH_REQUIRED = 411, + LCB_HTTP_STATUS_PRECONDITION_FAILED = 412, + LCB_HTTP_STATUS_REQUEST_ENTITY_TOO_LARGE = 413, + LCB_HTTP_STATUS_REQUEST_URI_TOO_LONG = 414, + LCB_HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415, + LCB_HTTP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE = 416, + LCB_HTTP_STATUS_EXPECTATION_FAILED = 417, + LCB_HTTP_STATUS_UNPROCESSABLE_ENTITY = 422, + LCB_HTTP_STATUS_LOCKED = 423, + LCB_HTTP_STATUS_FAILED_DEPENDENCY = 424, + LCB_HTTP_STATUS_INTERNAL_SERVER_ERROR = 500, + LCB_HTTP_STATUS_NOT_IMPLEMENTED = 501, + LCB_HTTP_STATUS_BAD_GATEWAY = 502, + LCB_HTTP_STATUS_SERVICE_UNAVAILABLE = 503, + LCB_HTTP_STATUS_GATEWAY_TIMEOUT = 504, + LCB_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505, + LCB_HTTP_STATUS_INSUFFICIENT_STORAGE = 507 +} lcb_http_status_t; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/error.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/error.h new file mode 100644 index 00000000..23d49754 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/error.h @@ -0,0 +1,595 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * @brief + * Definition of all of the error codes used by libcouchbase + */ +#ifndef LIBCOUCHBASE_ERROR_H +#define LIBCOUCHBASE_ERROR_H 1 + +#ifndef LIBCOUCHBASE_COUCHBASE_H +#error "Include libcouchbase/couchbase.h instead" +#endif + + +/** + * @ingroup lcb-public-api + * @defgroup lcb-error-codes Error Codes + * @brief Status codes returned by the library + * + * @addtogroup lcb-error-codes + * @{ + */ +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Error Categories + * + * These error categories are assigned as a series of OR'd bits to each + * of the error codes in lcb_error_t. + * + * @see lcb_get_errtype + */ +typedef enum { + /** Error type indicating a likely issue in user input */ + LCB_ERRTYPE_INPUT = 1 << 0, + + /** Error type indicating a likely network failure */ + LCB_ERRTYPE_NETWORK = 1 << 1, + + /** Error type indicating a fatal condition within the server or library */ + LCB_ERRTYPE_FATAL = 1 << 2, + + /** Error type indicating a transient condition within the server */ + LCB_ERRTYPE_TRANSIENT = 1 << 3, + + /** Error type indicating a negative server reply for the data */ + LCB_ERRTYPE_DATAOP = 1 << 4, + + /** Error codes which should never be visible to the user */ + LCB_ERRTYPE_INTERNAL = 1 << 5, + + /** Error code indicating a plugin failure */ + LCB_ERRTYPE_PLUGIN = 1 << 6, + + /** Error code indicating the server is under load */ + LCB_ERRTYPE_SRVLOAD = 1 << 7, + + /** Error code indicating the server generated this message */ + LCB_ERRTYPE_SRVGEN = 1 << 8, + + /** + * Error code indicates document (fulldoc) access ok, but + * error in performing subdocument operation. Note that this only + * covers errors which relate to a specific operation, rather than + * operations which prevent _any_ subdoc operation from executing. + */ + LCB_ERRTYPE_SUBDOC = 1 << 9 +} lcb_errflags_t; + +/* PRIVATE. This is just here to instruct/inform users to use the more detailed codes */ +#define LCB__ERR_USEDETAILS \ + "Enable detailed error codes (via LCB_CNTL_DETAILED_ERRCODES, or via " \ + "`detailed_errcodes` in the connection string) and/or enable logging to " \ + "get more information" + +/** + * @brief XMacro for all error types + * @param X macro to be invoked for each function. This will accept the following + * arguments: + * - Raw unquoted literal error identifier (e.g. `LCB_EINVAL`) + * - Code for the error (e.g. `0x23`) + * - Set of categories for the specific error (e.g. `LCB_ERRTYPE_FOO|LCB_ERRTYPE_BAR`) + * - Quoted string literal describing the error (e.g. `"This is sad"`) + */ +#define LCB_XERR(X) \ + /** Success */ \ + X(LCB_SUCCESS, 0x00, 0, "Success (Not an error)") \ + \ + X(LCB_AUTH_CONTINUE, 0x01, LCB_ERRTYPE_INTERNAL|LCB_ERRTYPE_FATAL|LCB_ERRTYPE_SRVGEN, \ + "Error code used internally within libcouchbase for SASL auth. Should " \ + "not be visible from the API") \ + \ + /**This error code is received in callbacks when connecting or reconnecting + to the cluster. If received during initial bootstrap + (i.e. lcb_get_bootstrap_status()) then it should be considered a fatal + errror. This error should not be visible after initial bootstrap. + + This error may also be received if CCCP bootstrap is used and the bucket does + not exist.*/ \ + X(LCB_AUTH_ERROR, 0x02, LCB_ERRTYPE_FATAL|LCB_ERRTYPE_INPUT, \ + "Authentication failed. You may have provided an invalid " \ + "username/password combination") \ + \ + /**This error is received in callbacks. It is a result of trying to perform + an lcb_arithmetic() operation on an item which has an existing value that + cannot be parsed as a number. */ \ + X(LCB_DELTA_BADVAL, 0x03, LCB_ERRTYPE_INPUT|LCB_ERRTYPE_DATAOP|LCB_ERRTYPE_SRVGEN, \ + "The value requested to be incremented is not stored as a number") \ + \ + /**This error is received in callbacks. It indicates that the key and value + exceeded the constraints within the server. The current constraints are + 150 bytes for a key and 20MB for a value */ \ + X(LCB_E2BIG, 0x04, LCB_ERRTYPE_INPUT|LCB_ERRTYPE_DATAOP|LCB_ERRTYPE_SRVGEN, \ + "The object requested is too big to store in the server") \ + \ + X(LCB_EBUSY, 0x05, LCB_ERRTYPE_TRANSIENT, "The server is busy. Try again later") \ + /**Internal error within the library. This may be a result of a bug */ \ + X(LCB_EINTERNAL, 0x06, LCB_ERRTYPE_INTERNAL, "Internal libcouchbase error") \ + \ + /**If returned from an API call, it indicates invalid values were passed + to the function. If received within a callback, it indicates that a + malformed packet was sent to the server. */ \ + X(LCB_EINVAL, 0x07, LCB_ERRTYPE_INPUT, "Invalid input/arguments") \ + \ + /**This code is received in callbacks. It means the server has no more memory + left to store or modify the item. */ \ + X(LCB_ENOMEM, 0x08, LCB_ERRTYPE_TRANSIENT, \ + "The server is out of memory. Try again later") \ + \ + X(LCB_ERANGE, 0x09, LCB_ERRTYPE_INPUT|LCB_ERRTYPE_DATAOP|LCB_ERRTYPE_SRVGEN, \ + "Invalid range") \ + \ + /**Generic error */ \ + X(LCB_ERROR, 0x0A, 0, "Generic error") \ + \ + /**This error is received in callbacks from the server itself to indicate + that it could not perform the requested operation. This is usually due to memory and/or + resource constraints on the server. This error may also be returned if a + key has been locked (see lcb_get()) and an operation has been performed on it + without unlocking the item (see lcb_unlock(), or pass the correct CAS value + to a mutation function). */ \ + X(LCB_ETMPFAIL, 0x0B, LCB_ERRTYPE_TRANSIENT|LCB_ERRTYPE_SRVLOAD|LCB_ERRTYPE_SRVGEN, \ + "Temporary failure received from server. Try again later") \ + \ + /**The key already exists in the cluster. This error code is received within + callbacks as a result of an _add_ operation in which the key already exists. + It is also received for other operations in which a CAS was specified but has + changed on the server. */ \ + X(LCB_KEY_EEXISTS, 0x0C, LCB_ERRTYPE_DATAOP|LCB_ERRTYPE_SRVGEN, \ + "The key already exists in the server. If you have supplied a CAS then " \ + "the key exists with a CAS value different than specified") \ + \ + /**Received in callbacks to indicate that the server does not contain the item */ \ + X(LCB_KEY_ENOENT, 0x0D, LCB_ERRTYPE_DATAOP|LCB_ERRTYPE_SRVGEN, \ + "The key does not exist on the server") \ + \ + /**Error code thrown if an I/O plugin could not be located */ \ + X(LCB_DLOPEN_FAILED, 0x0E, LCB_ERRTYPE_INPUT|LCB_ERRTYPE_FATAL|LCB_ERRTYPE_PLUGIN, \ + "Could not locate plugin library") \ + \ + /**Error code thrown of an I/O plugin did not contain a proper initialization routine */ \ + X(LCB_DLSYM_FAILED, 0x0F, LCB_ERRTYPE_INPUT|LCB_ERRTYPE_FATAL|LCB_ERRTYPE_PLUGIN, \ + "Required plugin initializer not found") \ + \ + /** This is a generic error code returned for various forms of socket + operation failures. Newer applications are recommended to enable the + @ref LCB_CNTL_DETAILED_ERRCODES setting via lcb_cntl() and receive more + detailed information about a socket error. + + @see lcb_cntl(), @ref LCB_CNTL_DETAILED_ERRCODES */ \ + X(LCB_NETWORK_ERROR, 0x10, LCB_ERRTYPE_NETWORK, \ + "Generic network failure. " LCB__ERR_USEDETAILS) \ + \ + /**Error code received in callbacks if the command was forwarded to the wrong + server (for example, during a rebalance) and the library settings are configured + that the command should not be remapped to a new server */ \ + X(LCB_NOT_MY_VBUCKET, 0x11, LCB_ERRTYPE_NETWORK|LCB_ERRTYPE_TRANSIENT|LCB_ERRTYPE_SRVGEN, \ + "The server which received this command claims it is not hosting this key") \ + \ + /**Received in callbacks as a response to an LCB_APPEND or LCB_PREPEND on an + item that did not exist in the cluster. Equivalent to LCB_KEY_ENOENT */ \ + X(LCB_NOT_STORED, 0x12, LCB_ERRTYPE_DATAOP|LCB_ERRTYPE_SRVGEN, \ + "Item not stored (did you try to append/prepend to a missing key?)") \ + \ + /**Returned from API calls if a specific operation is valid but is unsupported + in the current version or state of the library. May also be received in a + callback if the cluster does not support the operation. + + This will be returned for unknown settings passed to lcb_cntl() unless + @ref LCB_CNTL_DETAILED_ERRCODES is set + */ \ + X(LCB_NOT_SUPPORTED, 0x13, 0, "Operation not supported") \ + \ + /**Received in callbacks if the cluster does not know about the command. + Similar to LCB_NOT_SUPPORTED */ \ + X(LCB_UNKNOWN_COMMAND, 0x14, LCB_ERRTYPE_SRVGEN, "Unknown command") \ + \ + /**Error code received if the hostname specified could not be found. It may + also be received if a socket could not be created to the host supplied. + + A more detailed error code may be returned instead if + @ref LCB_CNTL_DETAILED_ERRCODES is set.*/ \ + X(LCB_UNKNOWN_HOST, 0x15, LCB_ERRTYPE_NETWORK|LCB_ERRTYPE_INPUT, \ + "DNS/Hostname lookup failed") \ + \ + /**Error code received if the server replied with an unexpected response */ \ + X(LCB_PROTOCOL_ERROR, 0x16, LCB_ERRTYPE_NETWORK, \ + "Data received on socket was not in the expected format") \ + \ + /**Error code received in callbacks for operations which did not receive a + reply from the server within the timeout limit. + @see LCB_CNTL_OP_TIMEOUT */ \ + X(LCB_ETIMEDOUT, 0x17, LCB_ERRTYPE_NETWORK|LCB_ERRTYPE_TRANSIENT, \ + "Client-Side timeout exceeded for operation. Inspect network conditions " \ + "or increase the timeout") \ + \ + /** @see LCB_NETWORK_ERROR, LCB_UNKNOWN_HOST, @ref LCB_CNTL_DETAILED_ERRCODES */ \ + X(LCB_CONNECT_ERROR, 0x18, LCB_ERRTYPE_NETWORK, \ + "Error while establishing TCP connection. " LCB__ERR_USEDETAILS) \ + \ + /** Received on initial bootstrap if the bucket does not exist. Note that + for CCCP bootstrap, @ref LCB_AUTH_ERROR will be received instead */ \ + X(LCB_BUCKET_ENOENT, 0x19, LCB_ERRTYPE_INPUT|LCB_ERRTYPE_FATAL, \ + "The bucket requested does not exist") \ + \ + /** Client could not allocate memory for internal structures */ \ + X(LCB_CLIENT_ENOMEM, 0x1A, LCB_ERRTYPE_FATAL, \ + "Memory allocation for libcouchbase failed. Severe problems ahead") \ + \ + /** Client could not schedule the request. This is typically received when + an operation is requested before the initial bootstrap has completed */ \ + X(LCB_CLIENT_ENOCONF, 0x1B, LCB_ERRTYPE_TRANSIENT, \ + "Client not bootstrapped. Ensure bootstrap/connect was attempted and was successful") \ + \ + X(LCB_EBADHANDLE, 0x1C, LCB_ERRTYPE_INPUT, \ + "Bad handle type for operation. " \ + "You cannot perform administrative operations on a data handle, or data "\ + "operations on a cluster handle") \ + \ + X(LCB_SERVER_BUG, 0x1D, 0, "Encountered a server bug") \ + \ + X(LCB_PLUGIN_VERSION_MISMATCH, 0x1E, LCB_ERRTYPE_INPUT|LCB_ERRTYPE_FATAL|LCB_ERRTYPE_PLUGIN, \ + "This version of libcouchbase cannot load the specified plugin") \ + \ + X(LCB_INVALID_HOST_FORMAT, 0x1F, LCB_ERRTYPE_INPUT, \ + "Hostname specified for URI is in an invalid format") \ + \ + X(LCB_INVALID_CHAR, 0x20, LCB_ERRTYPE_INPUT, "Illegal characted") \ + \ + /** Received in response to the durability API call, if the amount of nodes + or replicas to persist/replicate to exceed the total number of replicas the + bucket was configured with. */ \ + X(LCB_DURABILITY_ETOOMANY, 0x21, LCB_ERRTYPE_INPUT, \ + "Durability constraints requires more nodes/replicas than the cluster "\ + "configuration allows. Durability constraints will never be satisfied") \ + \ + /** Received in scheduling if a command with the same key was specified more + than once. Some commands will accept this, but others (notably `observe`) + will not */ \ + X(LCB_DUPLICATE_COMMANDS, 0x22, LCB_ERRTYPE_INPUT, \ + "The same key was specified more than once in the command list") \ + \ + /** This error is received from API calls if the master node for the vBucket + the key has been hashed to is not present. This will happen in the result + of a node failover where no replica exists to replace it. */ \ + X(LCB_NO_MATCHING_SERVER, 0x23, LCB_ERRTYPE_TRANSIENT, \ + "The node the request was mapped to does not exist in the current cluster " \ + "map. This may be the result of a failover.") \ + \ + /** Received during initial creation (lcb_create()) if an environment variable + was specified with an incorrect or invalid value. + + @see @ref lcb-env-vars-page */ \ + X(LCB_BAD_ENVIRONMENT, 0x24, LCB_ERRTYPE_FATAL|LCB_ERRTYPE_INPUT, \ + "The value for an environment variable recognized by libcouchbase was " \ + "specified in an incorrect format. Check your environment for entries " \ + "starting with 'LCB_' or 'LIBCOUCHBASE_'") \ + \ + X(LCB_BUSY, 0x25, LCB_ERRTYPE_INTERNAL, "Busy. This is an internal error") \ + \ + /** Received from lcb_create() if the username does not match the bucket */ \ + X(LCB_INVALID_USERNAME, 0x26, LCB_ERRTYPE_INPUT|LCB_ERRTYPE_FATAL, \ + "The username must match the bucket name (or be NULL) for data access") \ + \ + X(LCB_CONFIG_CACHE_INVALID, 0x27, LCB_ERRTYPE_INPUT, \ + "The contents of the configuration cache file were invalid. Configuration " \ + "will be fetched from the network") \ + \ + /** Received during initial bootstrap if the library was configured to force + the usage of a specific SASL mechanism and the server did not support this + mechanism. @see LCB_CNTL_FORCE_SASL_MECH */ \ + X(LCB_SASLMECH_UNAVAILABLE, 0x28, LCB_ERRTYPE_INPUT|LCB_ERRTYPE_FATAL, \ + "The requested SASL mechanism was not supported by the server. Either " \ + "upgrade the server or change the mechanism requirements") \ + \ + /** Received in the HTTP callback if the response was redirected too many + times. @see LCB_CNTL_MAX_REDIRECTS */ \ + X(LCB_TOO_MANY_REDIRECTS, 0x29, LCB_ERRTYPE_NETWORK, \ + "Maximum allowed number of redirects reached. See lcb_cntl and the "\ + "LCB_CNTL_MAX_REDIRECTS option to modify this limit") \ + \ + /** May be received in operation callbacks if the cluster toplogy changed + and the library could not remap the command to a new node. This may be + because the internal structure lacked sufficient information to recreate + the packet, or because the configuration settings indicated that the command + should not be retried. @see LCB_CNTL_RETRYMODE */ \ + X(LCB_MAP_CHANGED, 0x2A, LCB_ERRTYPE_NETWORK|LCB_ERRTYPE_TRANSIENT, \ + "The cluster map has changed and this operation could not be completed " \ + "or retried internally. Try this operation again") \ + \ + /** Returned from the lcb_pktfwd3() function if an incomplete packet was + passed */ \ + X(LCB_INCOMPLETE_PACKET, 0x2B, LCB_ERRTYPE_TRANSIENT|LCB_ERRTYPE_INPUT, \ + "Incomplete packet was passed to forward function") \ + \ + /** Mapped directly to the system `ECONNREFUSED` errno. This is received + in callbacks if an initial connection to the node could not be established. + Check your firewall settings and ensure the specified service is online. */ \ + X(LCB_ECONNREFUSED, 0x2C, LCB_ERRTYPE_NETWORK|LCB_ERRTYPE_TRANSIENT, \ + "The remote host refused the connection. Is the service up?") \ + \ + /** Returned in a callback if the socket connection was gracefully closed, + but the library wasn't expecting it. This may happen if the system is + being shut down. + @lcb_see_detailed_neterr */ \ + X(LCB_ESOCKSHUTDOWN, 0x2D, LCB_ERRTYPE_NETWORK|LCB_ERRTYPE_TRANSIENT, \ + "The remote host closed the connection") \ + \ + /** Returned in a callback if the socket connection was forcefully reset, + Equivalent to the system `ECONNRESET`. + @lcb_see_detailed_neterr + */ \ + X(LCB_ECONNRESET, 0x2E, LCB_ERRTYPE_NETWORK|LCB_ERRTYPE_TRANSIENT, \ + "The connection was forcibly reset by the remote host") \ + \ + /** Returned in a callback if the library could not allocated a local socket + due to TCP local port exhaustion. This means you have either found a bug + in the library or are creating too many TCP connections. Keep in mind that + a TCP connection will still occupy a slot in your system socket table even + after it has been closed (and will thus appear in a `TIME_WAIT` state). + + @lcb_see_detailed_neterr + */ \ + X(LCB_ECANTGETPORT, 0x2F, LCB_ERRTYPE_NETWORK|LCB_ERRTYPE_FATAL, \ + "Could not assign a local port for this socket. For client sockets this means " \ + "there are too many TCP sockets open") \ + \ + /** Returned if the library could not allocate a new file descriptor for a + socket or other resource. This may be more common on systems (such as + Mac OS X) which have relatively low limits for file descriptors. To raise + the file descriptor limit, refer to the `ulimit -n` command + + @lcb_see_detailed_neterr + */ \ + X(LCB_EFDLIMITREACHED, 0x30, LCB_ERRTYPE_NETWORK|LCB_ERRTYPE_FATAL, \ + "The system or process has reached its maximum number of file descriptors") \ + \ + /** Returned in callback if the host or subnet containing a node could + not be contacted. This may be a result of a bad routing table or being + physically disconnected from the network. + @lcb_see_detailed_neterr. */ \ + X(LCB_ENETUNREACH, 0x31, LCB_ERRTYPE_NETWORK|LCB_ERRTYPE_TRANSIENT, \ + "The remote host was unreachable - is your network OK?") \ + \ + /** An unrecognized setting was passed to the lcb_cntl() function + @lcb_see_detailed_neterr */ \ + X(LCB_ECTL_UNKNOWN, 0x32, LCB_ERRTYPE_INPUT, \ + "Control code passed was unrecognized") \ + \ + /** An invalid operation was supplied for a setting to lcb_cntl(). This will + happen if you try to write to a read-only setting, or retrieve a value + which may only be set. Refer to the documentation for an individual setting + to see what modes it supports. + @lcb_see_detailed_neterr + */ \ + X(LCB_ECTL_UNSUPPMODE, 0x33, LCB_ERRTYPE_INPUT, \ + "Invalid modifier for cntl operation (e.g. tried to read a write-only value") \ + \ + /** A malformed argument was passed to lcb_cntl() for the given setting. See + the documentation for the setting to see what arguments it supports and + how they are to be supplied. + + @lcb_see_detailed_neterr */ \ + X(LCB_ECTL_BADARG, 0x34, LCB_ERRTYPE_INPUT, \ + "Argument passed to cntl was badly formatted") \ + \ + /**An empty key was passed to an operation. Most commands do not accept + empty keys. */ \ + X(LCB_EMPTY_KEY, 0x35, LCB_ERRTYPE_INPUT, \ + "An empty key was passed to an operation") \ + \ + /** A problem with the SSL system was encountered. Use logging to discover + what happened. This error will only be thrown if something internal to the + SSL library failed (for example, a bad certificate or bad user input); + otherwise a network error will be thrown if an SSL connection was terminated */ \ + X(LCB_SSL_ERROR, 0x36, LCB_ERRTYPE_FATAL, \ + "A generic error related to the SSL subsystem was encountered. Enable logging " \ + "to see more details") \ + \ + /** The certificate the server sent cannot be verified. This is a possible + case of a man-in-the-middle attack, but also of forgetting to supply + the path to the CA authority to the library. */ \ + X(LCB_SSL_CANTVERIFY, 0x37, LCB_ERRTYPE_FATAL, \ + "Client could not verify server's certificate") \ + \ + X(LCB_SCHEDFAIL_INTERNAL, 0x38, 0, \ + "Internal error used for destroying unscheduled command data") \ + \ + /** An optional client feature was requested, but the current configuration + does not allow it to be used. This might be because it is not available + on a particular platform/architecture/operating system/configuration, or + it has been disabled at the time the library was built. + */ \ + X(LCB_CLIENT_FEATURE_UNAVAILABLE, 0x39, LCB_ERRTYPE_INPUT, \ + "The requested feature is not supported by the client, either because of " \ + "settings in the configured instance, or because of options disabled at " \ + "the time the library was compiled") \ + \ + /**An option was passed to a command which is incompatible with other + options. This may happen if two fields are mutually exclusive */ \ + X(LCB_OPTIONS_CONFLICT, 0x3A, LCB_ERRTYPE_INPUT, \ + "The operation structure contains conflicting options") \ + \ + /**Received in callbacks if an operation failed because of a negative HTTP + status code */ \ + X(LCB_HTTP_ERROR, 0x3B, 0, \ + "HTTP Operation failed. Inspect status code for details") \ + \ + /**Scheduling error received if @ref LCB_CNTL_DURABILITY_MUTATION_TOKENS was + enabled, but there is no available mutation token for the key. */ \ + X(LCB_DURABILITY_NO_MUTATION_TOKENS, 0x3C, LCB_ERRTYPE_INPUT, \ + "The given item does not have a mutation token associated with it. " \ + "this is either because fetching mutation tokens was not enabled, or " \ + "you are trying to check on something not stored by this instance") \ + \ + /** The server replied with an unrecognized status code */ \ + X(LCB_UNKNOWN_MEMCACHED_ERROR, 0x3D, LCB_ERRTYPE_SRVGEN, \ + "The server replied with an unrecognized status code. A newer version " \ + "of this library may be able to decode it") \ + \ + /** The server replied that the given mutation has been lost */ \ + X(LCB_MUTATION_LOST, 0x3E, LCB_ERRTYPE_SRVGEN,\ + "The given mutation has been permanently lost due to the node failing " \ + "before replication") \ + X(LCB_SUBDOC_PATH_ENOENT, 0x3F, \ + LCB_ERRTYPE_DATAOP|LCB_ERRTYPE_SRVGEN|LCB_ERRTYPE_SUBDOC, \ + "Sub-document path does not exist") \ + X(LCB_SUBDOC_PATH_MISMATCH, 0x40,\ + LCB_ERRTYPE_DATAOP|LCB_ERRTYPE_SRVGEN|LCB_ERRTYPE_SUBDOC, \ + "Type of element in sub-document path conflicts with type in document") \ + X(LCB_SUBDOC_PATH_EINVAL, 0x41, \ + LCB_ERRTYPE_INPUT|LCB_ERRTYPE_SRVGEN|LCB_ERRTYPE_SUBDOC, \ + "Malformed sub-document path") \ + X(LCB_SUBDOC_PATH_E2BIG, 0x42, \ + LCB_ERRTYPE_INPUT|LCB_ERRTYPE_SRVGEN|LCB_ERRTYPE_SUBDOC, \ + "Sub-document contains too many components") \ + X(LCB_SUBDOC_DOC_E2DEEP, 0x43,\ + LCB_ERRTYPE_DATAOP|LCB_ERRTYPE_SRVGEN|LCB_ERRTYPE_SUBDOC, \ + "Existing document contains too many levels of nesting") \ + X(LCB_SUBDOC_VALUE_CANTINSERT, 0x44, \ + LCB_ERRTYPE_INPUT|LCB_ERRTYPE_SRVGEN|LCB_ERRTYPE_SUBDOC, \ + "Subdocument operation would invalidate the JSON") \ + X(LCB_SUBDOC_DOC_NOTJSON, 0x45,\ + LCB_ERRTYPE_DATAOP|LCB_ERRTYPE_SRVGEN|LCB_ERRTYPE_SUBDOC, \ + "Existing document is not valid JSON") \ + X(LCB_SUBDOC_NUM_ERANGE, 0x46, \ + LCB_ERRTYPE_DATAOP|LCB_ERRTYPE_SRVGEN|LCB_ERRTYPE_SUBDOC, \ + "The existing numeric value is too large") \ + X(LCB_SUBDOC_BAD_DELTA, 0x47,\ + LCB_ERRTYPE_DATAOP|LCB_ERRTYPE_SRVGEN|LCB_ERRTYPE_SUBDOC, \ + "Delta must be numeric, within the 64 bit signed range, and non-zero") \ + X(LCB_SUBDOC_PATH_EEXISTS, 0x48,\ + LCB_ERRTYPE_DATAOP|LCB_ERRTYPE_SRVGEN|LCB_ERRTYPE_SUBDOC, \ + "The given path already exists in the document") \ + X(LCB_SUBDOC_MULTI_FAILURE, 0x49,\ + LCB_ERRTYPE_DATAOP|LCB_ERRTYPE_SRVGEN|LCB_ERRTYPE_SUBDOC, \ + "Could not execute one or more multi lookups or mutations") \ + X(LCB_SUBDOC_VALUE_E2DEEP, 0x4A,\ + LCB_ERRTYPE_INPUT|LCB_ERRTYPE_SRVGEN|LCB_ERRTYPE_SUBDOC, \ + "Value is too deep to insert") \ + X(LCB_EINVAL_MCD, 0x4B, LCB_ERRTYPE_SRVGEN|LCB_ERRTYPE_INTERNAL, \ + "A badly formatted packet was sent to the server. Please report this in a bug") \ + X(LCB_EMPTY_PATH, 0x4C, LCB_ERRTYPE_INPUT, "Missing subdocument path") \ + X(LCB_UNKNOWN_SDCMD, 0x4D, LCB_ERRTYPE_INPUT, "Unknown subdocument command") \ + X(LCB_ENO_COMMANDS, 0x4E, LCB_ERRTYPE_INPUT, "No commands specified") \ + X(LCB_QUERY_ERROR, 0x4F, LCB_ERRTYPE_SRVGEN, \ + "Query execution failed. Inspect raw response object for information") + +/** Error codes returned by the library. */ +typedef enum { + #define X(n, v, cls, s) n = v, + LCB_XERR(X) + #undef X + + #ifdef LIBCOUCHBASE_INTERNAL + /** + * This is a private value used by the tests in libcouchbase + */ + LCB_MAX_ERROR_VAL, + #endif + + /* The errors below this value reserver for libcouchbase usage. */ + LCB_MAX_ERROR = 0x1000 +} lcb_error_t; + +/** @deprecated. Use new, less ambiguous identifier (@ref LCB_CLIENT_ENOCONF) */ +#define LCB_CLIENT_ETMPFAIL LCB_CLIENT_ENOCONF + +/** @brief If the error is a result of bad input */ +#define LCB_EIFINPUT(e) (lcb_get_errtype(e) & LCB_ERRTYPE_INPUT) + +/** @brief if the error is a result of a network condition */ +#define LCB_EIFNET(e) (lcb_get_errtype(e) & LCB_ERRTYPE_NETWORK) + +/** @brief if the error is fatal */ +#define LCB_EIFFATAL(e) (lcb_get_errtype(e) & LCB_ERRTYPE_FATAL) + +/** @brief if the error is transient */ +#define LCB_EIFTMP(e) (lcb_get_errtype(e) & LCB_ERRTYPE_TRANSIENT) + +/** @brief if the error is a routine negative server reply */ +#define LCB_EIFDATA(e) (lcb_get_errtype(e) & LCB_ERRTYPE_DATAOP) + +/** @brief if the error is a result of a plugin implementation */ +#define LCB_EIFPLUGIN(e) (lcb_get_errtype(e) & LCB_ERRTYPE_PLUGIN) +#define LCB_EIFSRVLOAD(e) (lcb_get_errtype(e) & LCB_ERRTYPE_SRVLOAD) +#define LCB_EIFSRVGEN(e) (lcb_get_errtype(e) & LCB_ERRTYPE_SRVGEN) +#define LCB_EIFSUBDOC(e) (lcb_get_errtype(e) & LCB_ERRTYPE_SUBDOC) + +/** + * @brief Get error categories for a specific code + * @param err the error received + * @return a set of flags containing the categories for the given error + * @committed + */ +LIBCOUCHBASE_API +int lcb_get_errtype(lcb_error_t err); + +/** + * Get a textual descrtiption for the given error code + * @param instance the instance the error code belongs to (you might + * want different localizations for the different instances) + * @param error the error code + * @return A textual description of the error message. The caller should + * not release the memory returned from this function. + * @committed + */ +LIBCOUCHBASE_API +const char *lcb_strerror(lcb_t instance, lcb_error_t error); + +/** + * This may be used in conjunction with the errmap callback if it wishes + * to fallback for default behavior for the given code. + * @uncomitted + */ +LIBCOUCHBASE_API +lcb_error_t lcb_errmap_default(lcb_t instance, lcb_U16 code); + +/** + * Callback for error mappings. This will be invoked when requesting whether + * the user has a possible mapping for this error code. + * + * This will be called for response codes which may be ambiguous in most + * use cases, or in cases where detailed response codes may be mapped to + * more generic ones. + */ +typedef lcb_error_t (*lcb_errmap_callback)(lcb_t instance, lcb_U16 bincode); + +/**@uncommitted*/ +LIBCOUCHBASE_API +lcb_errmap_callback lcb_set_errmap_callback(lcb_t, lcb_errmap_callback); + +#ifdef __cplusplus +} +#endif +/**@}*/ +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/http.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/http.h new file mode 100644 index 00000000..3cae1be7 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/http.h @@ -0,0 +1 @@ +/* Nothing to see here */ diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/iops.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/iops.h new file mode 100644 index 00000000..260bbd62 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/iops.h @@ -0,0 +1,1050 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBCOUCHBASE_COUCHBASE_H +#error "include libcouchbase/couchbase.h first" +#endif + +#ifndef LCB_IOPS_H +#define LCB_IOPS_H + +/** + * @file + * @brief Public I/O integration interface + * @details + * + * This file provides the public I/O interface for integrating with external + * event loops. + */ + +/** + * @ingroup lcbio lcb-public-api + * @defgroup lcb-io-plugin-api Network I/O + * @details + * + * I/O Integration comes in two flavors: + * + * @par (E)vent/Poll Based Integration + * This system is based upon the interfaces exposed by the `poll(2)` and + * `select(2)` calls found in POSIX-based systems and are wrapped by systems + * such as _libevent_ and _libev_. At their core is the notion that a socket + * may be polled for readiness (either readiness for reading or readiness + * for writing). When a socket is deemed ready, a callback is invoked indicating + * which events took place. + * + * + * @par (C)ompletion/Operation/Buffer Based Integration + * This system is based upon the interfaces exposed in the Win32 API where + * I/O is done in terms of operations which are awaiting _completion_. As such + * buffers are passed into the core, and the application is notified when the + * operation on those buffers (either read into a buffer, or write from a buffer) + * has been completed. + * + * + * @addtogroup lcb-io-plugin-api + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Type representing the native socket type of the operating system */ +#ifdef _WIN32 +typedef SOCKET lcb_socket_t; +#else +typedef int lcb_socket_t; +#endif + +struct sockaddr; + +#ifndef _WIN32 +/** Defined if the lcb_IOV structure conforms to `struct iovec` */ +#define LCB_IOV_LAYOUT_UIO +typedef struct lcb_iovec_st { + void *iov_base; + size_t iov_len; +} lcb_IOV; +#else +/** Defined if the lcb_IOV structure conforms to `WSABUF` */ +#define LCB_IOV_LAYOUT_WSABUF +typedef struct lcb_iovec_st { + ULONG iov_len; + void *iov_base; +} lcb_IOV; +#endif + +#if defined(LIBCOUCHBASE_INTERNAL) && !defined(LCB_IOPS_V12_NO_DEPRECATE) +#define LCB__IOPS_CONCAT2(X, Y) X ## Y +#define LCB__IOPS_CONCAT(X, Y) LCB__IOPS_CONCAT2(X, Y) +#define LCB_IOPS_DEPRECATED(X) void (*LCB__IOPS_CONCAT(lcb__iops__dummy, __LINE__))(void) +#else +#define LCB_IOPS_DEPRECATED(X) X +#endif + +/** @brief structure describing a connected socket's endpoints */ +struct lcb_nameinfo_st { + struct { + struct sockaddr *name; + int *len; + } local; + + struct { + struct sockaddr *name; + int *len; + } remote; +}; + +/** + * @struct lcb_IOV + * @brief structure indicating a buffer and its size + * + * @details + * This is compatible with a `struct iovec` on Unix and a `WSABUF` structure + * on Windows. It has an `iov_base` field which is the base pointer and an + * `iov_len` field which is the length of the buffer. + */ + +typedef struct lcb_io_opt_st* lcb_io_opt_t; + +/** + * @brief Callback invoked for all poll-like events + * + * @param sock the socket associated with the event + * @param events the events which activated this callback. This is set of bits + * comprising of LCB_READ_EVENT, LCB_WRITE_EVENT, and LCB_ERROR_EVENT + * @param uarg a user-defined pointer passed to the + * lcb_ioE_event_watch_fn routine. + */ +typedef void (*lcb_ioE_callback) + (lcb_socket_t sock, short events, void *uarg); + +/**@name Timer Callbacks + *@{*/ + +/** + * @brief Create a new timer object. + * + * @param iops the io structure + * @return an opaque timer handle. The timer shall remain inactive and shall + * be destroyed via the lcb_io_timer_destroy_fn routine. + */ +typedef void *(*lcb_io_timer_create_fn) + (lcb_io_opt_t iops); + +/** + * @brief Destroy a timer handler + * + * Destroy a timer previously created with lcb_io_timer_create_fn + * @param iops the io structure + * @param timer the opaque handle + * The timer must have already been cancelled via lcb_io_timer_cancel_fn + */ +typedef void (*lcb_io_timer_destroy_fn) + (lcb_io_opt_t iops, void *timer); + +/** + * @brief Cancel a pending timer callback + * + * Cancel and unregister a pending timer. If the timer has already + * fired, this does nothing. If the timer has not yet fired, the callback + * shall not be delivered. + * + * @param iops the I/O structure + * @param timer the timer to cancel. + */ +typedef void (*lcb_io_timer_cancel_fn) + (lcb_io_opt_t iops, void *timer); + +/** + * @brief Schedule a callback to be invoked within a given interval. + * + * Schedule a timer to be fired within usec microseconds from now + * @param iops the I/O structure + * @param timer a timer previously created with timer_create + * @param usecs the timer interval + * @param uarg the user-defined pointer to be passed in the callback + * @param callback the callback to invoke + */ +typedef int (*lcb_io_timer_schedule_fn) + (lcb_io_opt_t iops, void *timer, + lcb_U32 usecs, + void *uarg, + lcb_ioE_callback callback); + +/**@}*/ + + +/**@name Event Handle Callbacks + * @{*/ + +/** + * @brief Create a new event handle. + * + * An event object may be used to monitor a socket for given I/O readiness events + * @param iops the I/O structure. + * @return a new event handle. + * The handle may then be associated with a + * socket and watched (via lcb_ioE_event_watch_fn) for I/O readiness. + */ +typedef void *(*lcb_ioE_event_create_fn) + (lcb_io_opt_t iops); + +/** + * @brief Destroy an event handle + * + * Destroy an event object. The object must not be active. + * @param iops the I/O structure + * @param event the event to free + */ +typedef void (*lcb_ioE_event_destroy_fn) + (lcb_io_opt_t iops, void *event); + +/** + * @deprecated lcb_ioE_event_watch_fn() should be used with `0` for events + * @brief Cancel pending callbacks and unwatch a handle. + * + * @param iops the I/O structure + * @param sock the socket associated with the event + * @param event the opaque event object + * + * This function may be called multiple times and shall not fail even if the + * event is already inactive. + */ +typedef void (*lcb_ioE_event_cancel_fn) + (lcb_io_opt_t iops, lcb_socket_t sock, void *event); + + +/** Data is available for reading */ +#define LCB_READ_EVENT 0x02 +/** Data can be written */ +#define LCB_WRITE_EVENT 0x04 +/** Exceptional condition ocurred on socket */ +#define LCB_ERROR_EVENT 0x08 +#define LCB_RW_EVENT (LCB_READ_EVENT|LCB_WRITE_EVENT) + +/** + * Associate an event with a socket, requesting notification when one of + * the events specified in 'flags' becomes available on the socket. + * + * @param iops the IO context + * @param socket the socket to watch + * @param event the event to associate with the socket. If this parameter is + * @param evflags a bitflag of events to watch. This is one of LCB_READ_EVENT, + * LCB_WRITE_EVENT, or LCB_RW_EVENT. + * If this value is `0` then existing events shall be cancelled on the + * socket. + * + * Note that the callback may _also_ receive LCB_ERROR_EVENT but this cannot + * be requested as an event to watch for. + * + * @param uarg a user defined pointer to be passed to the callback + * @param callback the callback to invoke when one of the events becomes + * ready. + * + * @attention + * It shall be legal to call this routine multiple times without having to call + * the lcb_ioE_event_cancel_fn(). The cancel function should in fact be implemented + * via passing a `0` to the `evflags` parameter, effectively clearing the + * event. + */ +typedef int (*lcb_ioE_event_watch_fn) + (lcb_io_opt_t iops, + lcb_socket_t socket, + void *event, + short evflags, + void *uarg, + lcb_ioE_callback callback); + +/**@}*/ + +/**@name BSD-API I/O Routines + * @{*/ + +/** + * @brief Receive data into a single buffer + * @see `recv(2)` socket API call. + */ +typedef lcb_SSIZE (*lcb_ioE_recv_fn) + (lcb_io_opt_t iops, lcb_socket_t sock, void *target_buf, + lcb_SIZE buflen, int _unused_flags); + +/** @brief Send data from a single buffer. + * @see `send(2)` on POSIX + */ +typedef lcb_SSIZE (*lcb_ioE_send_fn) + (lcb_io_opt_t iops, lcb_socket_t sock, const void *srcbuf, + lcb_SIZE buflen, int _ignored); + +/**@brief Read data into a series of buffers. + * @see the `recvmsg(2)` function on POSIX */ +typedef lcb_SSIZE (*lcb_ioE_recvv_fn) + (lcb_io_opt_t iops, lcb_socket_t sock, lcb_IOV *iov, lcb_SIZE niov); + +/**@brief Write data from multiple buffers. + * @see the `sendmsg(2)` function on POSIX */ +typedef lcb_SSIZE (*lcb_ioE_sendv_fn) + (lcb_io_opt_t iops, lcb_socket_t sock, lcb_IOV *iov, lcb_SIZE niov); + +/**@brief Create a new socket. + * @see `socket(2)` on POSIX */ +typedef lcb_socket_t (*lcb_ioE_socket_fn) + (lcb_io_opt_t iops, int domain, int type, int protocol); + +/**@brief Connect a created socket + * @see `connect(2)` on POSIX */ +typedef int (*lcb_ioE_connect_fn) + (lcb_io_opt_t iops, + lcb_socket_t sock, + const struct sockaddr *dst, + unsigned int addrlen); + +/** @private */ +typedef int (*lcb_ioE_bind_fn) + (lcb_io_opt_t iops, + lcb_socket_t sock, + const struct sockaddr *srcaddr, + unsigned int addrlen); + +/** @private */ +typedef int (*lcb_ioE_listen_fn) + (lcb_io_opt_t iops, + lcb_socket_t bound_sock, + unsigned int queuelen); + +/** @private */ +typedef lcb_socket_t (*lcb_ioE_accept_fn) + (lcb_io_opt_t iops, + lcb_socket_t lsnsock); + +/** @brief Close a socket + * @see `close(2)` and `shutdown(2)` */ +typedef void (*lcb_ioE_close_fn) + (lcb_io_opt_t iops, lcb_socket_t sock); + + +/** + * While checking the socket, treat pending data as an _erorr_. + * This flag will be _missing_ if the socket participates in a protocol + * where unsolicited data is possible. + * + * Currently Couchbase does not provide such a protocol (at least not one where + * sockets are placed in a pool), but it may in the future. + * + * This may be passed as a `flags` option to lcb_ioE_chkclosed_fn + */ +#define LCB_IO_SOCKCHECK_PEND_IS_ERROR 1 + +#define LCB_IO_SOCKCHECK_STATUS_CLOSED 1 +#define LCB_IO_SOCKCHECK_STATUS_OK 0 +#define LCB_IO_SOCKCHECK_STATUS_UNKNOWN -1 + +/**@brief Check if a socket has been closed or not. This is used to check + * a socket's state after a period of inactivity. + * + * + * @param iops The iops + * @param sock The socket to check + * @param flags A bit set of options. + * @return A value greater than 0 if the socket _is_ closed, 0 if the socket + * has not been closed, or a negative number, if the status could not be + * determined within the given constraints (for example, if `flags` did not + * specify `LCB_IO_SOCKCHECK_PEND_IS_ERROR`, and the implementation does not + * have a way to check status otherwise. + * + * @since 2.4.4 + */ +typedef int (*lcb_ioE_chkclosed_fn) + (lcb_io_opt_t iops, lcb_socket_t sock, int flags); + + +/** For use with `io{E,C}_cntl_fn`, indicates the setting should be retrieved */ +#define LCB_IO_CNTL_GET 0 +/** For use with lcb_io{E,C}_cntl_fn`, indicates the setting should be modified */ +#define LCB_IO_CNTL_SET 1 + +/** Disable Nagle's algorithm (use an int) */ +#define LCB_IO_CNTL_TCP_NODELAY 1 + +/** + * @brief Execute a specificied operation on a socket. + * @param iops The iops + * @param sock The socket + * @param mode The mode, can be @ref LCB_IO_CNTL_GET or @ref LCB_IO_CNTL_SET + * @param option The option to access + * @param[in,out] arg the argument for the option + * @return zero on success, nonzero on failure. + */ +typedef int (*lcb_ioE_cntl_fn) + (lcb_io_opt_t iops, lcb_socket_t sock, int mode, int option, void *arg); +/**@}*/ + + +struct ringbuffer_st; +struct lcb_connection_st; +struct lcbio_SOCKET; + +/** @deprecated Ringbuffers are no longer used this way by the library for I/O */ +struct lcb_buf_info { + char *root; + lcb_SIZE size; + struct ringbuffer_st *ringbuffer; + struct lcb_iovec_st iov[2]; +}; + +/** + * @brief Socket handle for completion-based I/O + * + * The sockdata structure is analoguous to an `lcb_socket_t` returned by + * the E-model I/O. + */ +typedef struct lcb_sockdata_st { + lcb_socket_t socket; /**< System socket, for informational purposes */ + lcb_io_opt_t parent; /**< Parent I/O context */ + struct lcbio_SOCKET *lcbconn; /**< Internal socket equivalent */ + int closed; /**< @deprecated No longer used by the library */ + int is_reading; /**< Internally used by lcbio */ + struct lcb_buf_info read_buffer; /**< @deprecated No longer used by the library */ +} lcb_sockdata_t; + +/** @deprecated */ +typedef struct lcb_io_writebuf_st { + struct lcb_io_opt_st *parent; + struct lcb_buf_info buffer; +} lcb_io_writebuf_t; + +/**@name Completion Routines + * @{*/ + +/** + * @brief Create a completion socket handle + * + * Create an opaque socket handle + * @param iops the IO context + * @param domain socket address family, e.g. AF_INET + * @param type the transport type, e.g. SOCK_STREAM + * @param protocol the IP protocol, e.g. IPPROTO_TCP + * @return a socket pointer or NULL on failure. + */ +typedef lcb_sockdata_t* (*lcb_ioC_socket_fn) + (lcb_io_opt_t iops, int domain, int type, int protocol); + +/** + * @brief Callback to be invoked upon a connection result. + * Callback invoked for a connection result. + * @param socket the socket which is being connected + * @param status the status. 0 for success, nonzero on failure + */ +typedef void (*lcb_io_connect_cb)(lcb_sockdata_t *socket, int status); + +/** + * @brief Request a connection for a socket + * @param iops the IO context + * @param sd the socket pointer + * @param dst the address to connect to + * @param naddr the size of the address len, e.g. sizeof(struct sockaddr_in) + * @param callback the callback to invoke when the connection status is determined + * @return 0 on success, nonzero if a connection could not be scheduled. + */ +typedef int (*lcb_ioC_connect_fn) + (lcb_io_opt_t iops, lcb_sockdata_t *sd, + const struct sockaddr *dst, + unsigned int naddr, + lcb_io_connect_cb callback); + +/** + * @brief Callback invoked when a new client connection has been established + * @param sd_server the server listen socket + * @param sd_client the new client socket + * @param status if there was an error accepting (in this case, sd_client is NULL + */ +typedef void (lcb_ioC_serve_callback) + (lcb_sockdata_t *sd_server, + lcb_sockdata_t *sd_client, + int status); + +/** + * Specify that the socket start accepting connections. This should be called + * on a newly created non-connected socket + * @param iops the I/O context + * @param server_socket the socket used to listen with + * @param sockaddr the local address for listening + * @param callback the callback to invoke for each new connection + */ +typedef int (*lcb_ioC_serve_fn) + (lcb_io_opt_t iops, + lcb_sockdata_t *server_socket, + const struct sockaddr *listen_addr, + lcb_ioC_serve_callback callback); + +/** + * @brief Request address information on a connected socket + * @param iops the I/O context + * @param sock the socket from which to retrieve information + * @param ni a nameinfo structure to populate with the relevant details + */ +typedef int (*lcb_ioC_nameinfo_fn) + (lcb_io_opt_t iops, + lcb_sockdata_t *sock, + struct lcb_nameinfo_st *ni); + +/**@deprecated*/ +typedef void (*lcb_ioC_read_callback)(lcb_sockdata_t *sd, lcb_SSIZE nread); +#define lcb_io_read_cb lcb_ioC_read_callback +/**@deprecated See lcb_ioC_read2_fn(). Wrapped if not implemented*/ +typedef int (*lcb_ioC_read_fn)(lcb_io_opt_t,lcb_sockdata_t*,lcb_ioC_read_callback); +/**@deprecated See lcb_ioC_write2_fn(). Wrapped if not implemented*/ +typedef lcb_io_writebuf_t* (*lcb_ioC_wballoc_fn)(lcb_io_opt_t,lcb_sockdata_t *); +/**@deprecated See lcb_ioC_write2_fn(). Wrapped if not implemented */ +typedef void (*lcb_ioC_wbfree_fn)(lcb_io_opt_t,lcb_sockdata_t*,lcb_io_writebuf_t*); +/**@deprecated See lcb_ioC_write2_fn(). This will be wrapped if not implemented */ +typedef void (*lcb_ioC_write_callback)(lcb_sockdata_t*,lcb_io_writebuf_t*,int); +#define lcb_io_write_cb lcb_ioC_write_callback + +/**@deprecated*/ +typedef int (*lcb_ioC_write_fn) + (lcb_io_opt_t,lcb_sockdata_t*,lcb_io_writebuf_t*,lcb_ioC_write_callback); + + +/** + * @brief Callback received when a buffer has been flushed + * @param sd the socket + * @param status nonzero on error + * @param arg the opaque handle passed in the write2 call + */ +typedef void (*lcb_ioC_write2_callback) + (lcb_sockdata_t *sd, + int status, + void *arg); + +/** + * @brief Schedule a flush of a series of buffers to the network + * + * @param iops the I/O context + * @param sd the socket on which to send + * @param iov an array of IOV structures. + * The buffers pointed to by the IOVs themselves (i.e. `iov->iov_len`) + * **must** not be freed or modified until the callback has been invoked. + * The storage for the IOVs themselves (i.e. the array passed in `iov`) + * is copied internally to the implementation. + * + * @param niov the number of IOV structures within the array + * @param uarg an opaque pointer to be passed in the callback + * @param callback the callback to invoke. This will be called when the buffers + * passed have either been completely flushed (and are no longer required) + * or when an error has taken place. + */ +typedef int (*lcb_ioC_write2_fn) + (lcb_io_opt_t iops, + lcb_sockdata_t *sd, + lcb_IOV *iov, + lcb_SIZE niov, + void *uarg, + lcb_ioC_write2_callback callback); + + +/** + * @brief Callback invoked when a read has been completed + * @param sd the socket + * @param nread number of bytes read, or -1 on error + * @param arg user provided argument for callback. + */ +typedef void (*lcb_ioC_read2_callback) + (lcb_sockdata_t *sd, lcb_SSIZE nread, void *arg); +/** + * @brief Schedule a read from the network + * @param iops the I/O context + * @param sd the socket on which to read + * @param iov an array of IOV structures + * @param niov the number of IOV structures within the array + * @param uarg a pointer passed to the callback + * @param callback the callback to invoke + * @return 0 on success, nonzero on error + * + * The IOV array itself shall copied (if needed) into the I/O implementation + * and thus does not need to be kept in memory after the function has been + * called. Note that the underlying buffers _do_ need to remain valid until + * the callback is received. + */ +typedef int (*lcb_ioC_read2_fn) + (lcb_io_opt_t iops, + lcb_sockdata_t *sd, + lcb_IOV *iov, + lcb_SIZE niov, + void *uarg, + lcb_ioC_read2_callback callback); + +/** + * @brief Asynchronously shutdown the socket. + * + * Request an asynchronous close for the specified socket. This merely releases + * control from the library over to the plugin for the specified socket and + * does _not_ actually imply that the resources have been closed. + * + * Notable, callbacks for read and write operations will _still_ be invoked + * in order to maintain proper resource deallocation. However the socket's + * closed field will be set to true. + * + * @param iops the I/O context + * @param sd the socket structure + */ +typedef unsigned int (*lcb_ioC_close_fn) + (lcb_io_opt_t iops, + lcb_sockdata_t *sd); + +/** + * This is the completion variant of @ref lcb_ioE_chkclosed_fn. See that + * function for details + * + * @param iops + * @param sd + * @param flags + * @return + */ +typedef int (*lcb_ioC_chkclosed_fn) + (lcb_io_opt_t iops, lcb_sockdata_t *sd, int flags); + +/** + * @see lcb_ioE_cntl_fn. + * + * @param iops + * @param sd + * @param mode + * @param option + * @param arg + * @return + */ +typedef int (*lcb_ioC_cntl_fn) + (lcb_io_opt_t iops, lcb_sockdata_t *sd, int mode, int option, void *arg); + +/**@}*/ + +/** + * @brief Start the event loop + * @param iops The I/O context + * + * This should start polling for socket events on all registered watchers + * and scheduled events. This function should return either when there are + * no more timers or events pending, or when lcb_io_stop_fn() has been invoked. + */ +typedef void (*lcb_io_start_fn)(lcb_io_opt_t iops); + +/** + * @brief Run a single iteration of the event loop without blocking. This + * is intended to be an optimization to allow scheduled I/O operations to + * complete without blocking the main thread + */ +typedef void (*lcb_io_tick_fn)(lcb_io_opt_t iops); + +/** + * @brief Pause the event loop + * @param iops The I/O Context + * + * This function shall suspend the event loop, causing a current invocation + * to lcb_io_start_fn() to return as soon as possible + */ +typedef void (*lcb_io_stop_fn)(lcb_io_opt_t iops); + +LCB_DEPRECATED(typedef void (*lcb_io_error_cb)(lcb_sockdata_t *socket)); + +#define LCB_IOPS_BASE_FIELDS \ + void *cookie; \ + int error; \ + int need_cleanup; + +struct lcb_iops_evented_st { + LCB_IOPS_BASE_FIELDS + LCB_IOPS_DEPRECATED(lcb_ioE_socket_fn socket); + LCB_IOPS_DEPRECATED(lcb_ioE_connect_fn connect); + LCB_IOPS_DEPRECATED(lcb_ioE_recv_fn recv); + LCB_IOPS_DEPRECATED(lcb_ioE_send_fn send); + LCB_IOPS_DEPRECATED(lcb_ioE_recvv_fn recvv); + LCB_IOPS_DEPRECATED(lcb_ioE_sendv_fn sendv); + LCB_IOPS_DEPRECATED(lcb_ioE_close_fn close); + LCB_IOPS_DEPRECATED(lcb_io_timer_create_fn create_timer); + LCB_IOPS_DEPRECATED(lcb_io_timer_destroy_fn destroy_timer); + LCB_IOPS_DEPRECATED(lcb_io_timer_cancel_fn delete_timer); + LCB_IOPS_DEPRECATED(lcb_io_timer_schedule_fn update_timer); + LCB_IOPS_DEPRECATED(lcb_ioE_event_create_fn create_event); + LCB_IOPS_DEPRECATED(lcb_ioE_event_destroy_fn destroy_event); + LCB_IOPS_DEPRECATED(lcb_ioE_event_watch_fn update_event); + LCB_IOPS_DEPRECATED(lcb_ioE_event_cancel_fn delete_event); + LCB_IOPS_DEPRECATED(lcb_io_stop_fn stop_event_loop); + LCB_IOPS_DEPRECATED(lcb_io_start_fn run_event_loop); +}; + +struct lcb_iops_completion_st { + LCB_IOPS_BASE_FIELDS + LCB_IOPS_DEPRECATED(lcb_ioC_socket_fn create_socket); + LCB_IOPS_DEPRECATED(lcb_ioC_connect_fn start_connect); + LCB_IOPS_DEPRECATED(lcb_ioC_wballoc_fn create_writebuf); + LCB_IOPS_DEPRECATED(lcb_ioC_wbfree_fn release_writebuf); + LCB_IOPS_DEPRECATED(lcb_ioC_write_fn start_write); + LCB_IOPS_DEPRECATED(lcb_ioC_read_fn start_read); + LCB_IOPS_DEPRECATED(lcb_ioC_close_fn close_socket); + LCB_IOPS_DEPRECATED(lcb_io_timer_create_fn create_timer); + LCB_IOPS_DEPRECATED(lcb_io_timer_destroy_fn destroy_timer); + LCB_IOPS_DEPRECATED(lcb_io_timer_cancel_fn delete_timer); + LCB_IOPS_DEPRECATED(lcb_io_timer_schedule_fn update_timer); + LCB_IOPS_DEPRECATED(lcb_ioC_nameinfo_fn get_nameinfo); + void (*pad1)(void); + void (*pad2)(void); + LCB_IOPS_DEPRECATED(void (*send_error)(struct lcb_io_opt_st*, lcb_sockdata_t*,void(*)(lcb_sockdata_t*))); + LCB_IOPS_DEPRECATED(lcb_io_stop_fn stop_event_loop); + LCB_IOPS_DEPRECATED(lcb_io_start_fn run_event_loop); +}; + +/** @brief Common functions for starting and stopping timers */ +typedef struct lcb_timerprocs_st { + lcb_io_timer_create_fn create; + lcb_io_timer_destroy_fn destroy; + lcb_io_timer_cancel_fn cancel; + lcb_io_timer_schedule_fn schedule; +} lcb_timer_procs; + +/** @brief Common functions for starting and stopping the event loop */ +typedef struct lcb_loopprocs_st { + lcb_io_start_fn start; + lcb_io_stop_fn stop; + lcb_io_tick_fn tick; +} lcb_loop_procs; + +/** @brief Functions wrapping the Berkeley Socket API */ +typedef struct lcb_bsdprocs_st { + lcb_ioE_socket_fn socket0; + lcb_ioE_connect_fn connect0; + lcb_ioE_recv_fn recv; + lcb_ioE_recvv_fn recvv; + lcb_ioE_send_fn send; + lcb_ioE_sendv_fn sendv; + lcb_ioE_close_fn close; + lcb_ioE_bind_fn bind; + lcb_ioE_listen_fn listen; + lcb_ioE_accept_fn accept; + lcb_ioE_chkclosed_fn is_closed; + lcb_ioE_cntl_fn cntl; +} lcb_bsd_procs; + +/** @brief Functions handling socket watcher events */ +typedef struct lcb_evprocs_st { + lcb_ioE_event_create_fn create; + lcb_ioE_event_destroy_fn destroy; + lcb_ioE_event_cancel_fn cancel; + lcb_ioE_event_watch_fn watch; +} lcb_ev_procs; + +/** @brief Functions for completion-based I/O */ +typedef struct { + lcb_ioC_socket_fn socket; + lcb_ioC_close_fn close; + lcb_ioC_read_fn read; + lcb_ioC_connect_fn connect; + lcb_ioC_wballoc_fn wballoc; + lcb_ioC_wbfree_fn wbfree; + lcb_ioC_write_fn write; + lcb_ioC_write2_fn write2; + lcb_ioC_read2_fn read2; + lcb_ioC_serve_fn serve; + lcb_ioC_nameinfo_fn nameinfo; + lcb_ioC_chkclosed_fn is_closed; + lcb_ioC_cntl_fn cntl; +} lcb_completion_procs; + +/** + * Enumeration defining the I/O model + */ +typedef enum { + LCB_IOMODEL_EVENT, /**< Event/Poll style */ + LCB_IOMODEL_COMPLETION /**< IOCP/Completion style */ +} lcb_iomodel_t; + +/** + * @param version the ABI/API version for the proc structures. Note that + * ABI is forward compatible for all proc structures, meaning that newer + * versions will always extend new fields and never replace existing ones. + * However in order to avoid a situation where a newer version of a plugin + * is loaded against an older version of the library (in which case the plugin + * will assume the proc table size is actually bigger than it is) the version + * serves as an indicator for this. The version actually passed is defined + * in `LCB_IOPROCS_VERSION` + * + * @param loop_procs a table to be set to basic loop control routines + * @param timer_procs a table to be set to the timer routines + * @param bsd_procs a table to be set to BSD socket API routines + * @param ev_procs a table to be set to event watcher routines + * @param completion_procs a table to be set to completion routines + * @param iomodel the I/O model to be used. If this is `LCB_IOMODEL_COMPLETION` + * then the contents of `bsd_procs` will be ignored and `completion_procs` must + * be populated. If the mode is `LCB_IOMODEL_EVENT` then the `bsd_procs` must be + * populated and `completion_procs` is ignored. + * + * Important to note that internally the `ev`, `bsd`, and `completion` field are + * defined as a union, thus + * @code{.c} + * union { + * struct { + * lcb_bsd_procs; + * lcb_ev_procs; + * } event; + * struct lcb_completion_procs completion; + * } + * @endcode + * thus setting both fields will actually clobber. + * + * @attention + * Note that the library takes ownership of the passed tables and it should + * not be controlled or accessed by the plugin. + * + * @attention + * This function may not have any side effects as it may be called + * multiple times. + * + * As opposed to the v0 and v1 IOPS structures that require a table to be + * populated and returned, the v2 IOPS works differently. Specifically, the + * IOPS population happens at multiple stages: + * + * 1. The base structure is returned, i.e. `lcb_create_NAME_iops` where _NAME_ + * is the name of the plugin + * + * 2. Once the structure is returned, LCB shall invoke the `v.v2.get_procs()` + * function. The callback is responsible for populating the relevant fields. + * + * Note that the old `v0` and `v1` fields are now proxied via this mechanism. + * It _is_ possible to still monkey-patch the IO routines, but ensure the + * monkey patching takes place _before_ the instance is created (as the + * instance will initialize its own IO Table); thus, e.g. + * @code{.c} + * static void monkey_proc_fn(...) { + * // + * } + * + * static void monkey_patch_io(lcb_io_opt_t io) { + * io->v.v0.get_procs = monkey_proc_fn; + * } + * + * int main(void) { + * lcb_create_st options; + * lcb_t instance; + * lcb_io_opt_t io; + * lcb_create_iops(&io, NULL); + * monkey_patch_io(io); + * options.v.v0.io = io; + * lcb_create(&instance, &options); + * // ... + * } + * @endcode + * + * Typically the `get_procs` function will only be called once, and this will + * happen from within lcb_create(). Thus in order to monkey patch you must + * ensure that initially the `get_procs` function itself is first supplanted + * and then return your customized I/O routines from your own `get_procs` (in + * this example, `monkey_proc_fn()`) + * + */ +typedef void (*lcb_io_procs_fn) + (int version, + lcb_loop_procs *loop_procs, + lcb_timer_procs *timer_procs, + lcb_bsd_procs *bsd_procs, + lcb_ev_procs *ev_procs, + lcb_completion_procs *completion_procs, + lcb_iomodel_t *iomodel); + +struct lcbio_TABLE; +struct lcb_iops2_st { + LCB_IOPS_BASE_FIELDS + lcb_io_procs_fn get_procs; + struct lcbio_TABLE *iot; +}; + +/* This is here to provide backwards compatibility with older (broken) clients + * which attempt to 'subclass' the select plugin, or similar. In this case we + * provide 17 callback fields (unused here) which the plugin implementation + * may set, so that the older code can continue to function without upgrading + * the client to a newer version. This should not be used except by internal + * plugins; specifically the ABI layout of this field is subject to change + * (for example, additional fields may be added or existing fields may be + * renamed/removed) without notice. + */ +typedef void (*lcb__iops3fndummy)(void); +struct lcb_iops3_st { + LCB_IOPS_BASE_FIELDS + lcb__iops3fndummy pads[17]; + lcb_io_procs_fn get_procs; + struct lcbio_TABLE *iot; +}; + +/** + * This number is bumped up each time a new field is added to any of the + * function tables. This number is backwards compatible (i.e. version 3 contains + * all the fields of version 2, and some additional ones) + */ +#define LCB_IOPROCS_VERSION 4 + +#define LCB_IOPS_BASEFLD(iops, fld) ((iops)->v.base).fld +#define LCB_IOPS_ERRNO(iops) LCB_IOPS_BASEFLD(iops, error) + +struct lcb_io_opt_st { + int version; + void *dlhandle; + void (*destructor)(struct lcb_io_opt_st *iops); + union { + struct { + LCB_IOPS_BASE_FIELDS + } base; + + /** These two names are deprecated internally */ + struct lcb_iops_evented_st v0; + struct lcb_iops_completion_st v1; + struct lcb_iops2_st v2; + struct lcb_iops3_st v3; + } v; +}; + +/** + * @brief Signature for a loadable plugin's IOPS initializer + * + * @param version the plugin init API version. This will be 0 for this function + * @param io a pointer to be set to the I/O table + * @param cookie a user-defined argument passed to the I/O initializer + * @return LCB_SUCCESS on success, an error on failure + */ +typedef lcb_error_t (*lcb_io_create_fn) + (int version, lcb_io_opt_t *io, void *cookie); + + +/** + * @volatile + * + * This is an alternative to copying the 'bsdio-inl.c' file around. It is + * designed specifically for the @ref lcb_io_procs_fn function and will do the + * job of applying the current _runtime_ version of the default event-based I/O + * implementation. + * + * e.g. + * @code{.c} + * static void getprocs_impl(int version, lcb_loop_procs *loop_procs, + * lcb_timer_procs *timer_procs, lcb_bsd_procs *bsd_procs, + * lcb_ev_procs *ev_procs, lcb_completion_procs *completion_procs, + * lcb_iomodel_t *iomodel) { + * + * // do stuff normally + * // .. + * // install the default I/O handlers: + * lcb_iops_wire_bsd_impl2(bsd_procs, version); + * @endcode + * + * Use this function with care, and understand the implications between using + * this API call and embedding the `bsdio-inl.c` source file. Specifically: + * + * - If your application is using an _older_ version of the library, this + * implementation may contain bugs not present in the version you compiled + * against (and an embedded version may be newer) + * - If your application is using a _newer_ version, there may be some additional + * I/O functions which you may wish to wrap or rather not implement at all, + * but will be implemented if you call this function. + */ +LIBCOUCHBASE_API +void +lcb_iops_wire_bsd_impl2(lcb_bsd_procs *procs, int version); + +/****************************************************************************** + ****************************************************************************** + ** IO CREATION ** + ****************************************************************************** + ******************************************************************************/ + +/** + * @brief Built-in I/O plugins + * @committed + */ +typedef enum { + LCB_IO_OPS_INVALID = 0x00, /**< @private */ + LCB_IO_OPS_DEFAULT = 0x01, /**< @private */ + + /** Integrate with the libevent loop. See lcb_create_libevent_io_opts() */ + LCB_IO_OPS_LIBEVENT = 0x02, + LCB_IO_OPS_WINSOCK = 0x03, /**< @private */ + LCB_IO_OPS_LIBEV = 0x04, + LCB_IO_OPS_SELECT = 0x05, + LCB_IO_OPS_WINIOCP = 0x06, + LCB_IO_OPS_LIBUV = 0x07 +} lcb_io_ops_type_t; + +/** @brief IO Creation for builtin plugins */ +typedef struct { + lcb_io_ops_type_t type; /**< The predefined type you want to create */ + void *cookie; /**< Plugin-specific argument */ +} lcb_IOCREATEOPTS_BUILTIN; + +#ifndef __LCB_DOXYGEN__ +/* These are mostly internal structures which may be in use by older applications.*/ +typedef struct { const char *sofile; const char *symbol; void *cookie; } lcb_IOCREATEOPTS_DSO; +typedef struct { lcb_io_create_fn create; void *cookie; } lcb_IOCREATEOPS_FUNCTIONPOINTER; +#endif + +/** @uncommited */ +struct lcb_create_io_ops_st { + int version; + union { + lcb_IOCREATEOPTS_BUILTIN v0; + lcb_IOCREATEOPTS_DSO v1; + lcb_IOCREATEOPS_FUNCTIONPOINTER v2; + } v; +}; + +/** + * Create a new instance of one of the library-supplied io ops types. + * + * This function should only be used if you wish to override/customize the + * default I/O plugin behavior; for example to select a specific implementation + * (e.g. always for the _select_ plugin) and/or to integrate + * a builtin plugin with your own application (e.g. pass an existing `event_base` + * structure to the _libevent_ plugin). + * + * If you _do_ use this function, then you must call lcb_destroy_io_ops() on + * the plugin handle once it is no longer required (and no instance is using + * it). + * + * Whether a single `lcb_io_opt_t` may be used by multiple instances at once + * is dependent on the specific implementation, but as a general rule it should + * be assumed to be unsafe. + * + * @param[out] op The newly created io ops structure + * @param options How to create the io ops structure + * @return @ref LCB_SUCCESS on success + * @uncommitted + */ +LIBCOUCHBASE_API +lcb_error_t lcb_create_io_ops(lcb_io_opt_t *op, const struct lcb_create_io_ops_st *options); + +/** + * Destroy the plugin handle created by lcb_create_io_ops() + * @param op ops structure + * @return LCB_SUCCESS on success + * @uncommitted + */ +LIBCOUCHBASE_API +lcb_error_t lcb_destroy_io_ops(lcb_io_opt_t op); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/ixmgmt.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/ixmgmt.h new file mode 100644 index 00000000..34b75d2e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/ixmgmt.h @@ -0,0 +1,263 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2016 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_IXMGMT_H +#define LCB_IXMGMT_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @volatile + * + * Structure representing a single index definition + */ +typedef struct { + /** + * Raw JSON returned from server. + * Can be used to decode fields in future versions not present within the + * library. + * + * This field can also be used as an input field to populate the other + * fields in this structure. This means that if you have a raw JSON + * representation of an index, you need only set this field (and + * `nrawjson`). The library will internally parse the raw JSON and + * populate the internal equivalents of the fields in this structure. + * + * Note that when using this field as an input for creating indexes, you + * should still set the ::flags field if you wish to set flags (e.g. in + * order to create a deferred-build index). + */ + const char *rawjson; + size_t nrawjson; + + /** Name of the index. For raw JSON, use the `name` propery */ + const char *name; + size_t nname; + + /** + * Keyspace or "bucket" of the index. For raw JSON, use the + * `keyspace_id` property + */ + const char *keyspace; + size_t nkeyspace; + + /** 'namespace'. Currently unused */ + const char *nspace; + size_t nnspace; + + /** Output parameter only. State of index */ + const char *state; + size_t nstate; + + /** Actual index text. For raw JSON use the `index_key` property. + * The value for this field is a properly-encoded JSON array of fields + * to index. e.g. + * + * @code{c} + * spec.fields = "[\"`name`\", \"`email`\", \"`ctime`\"]" + * @endcode + */ + const char *fields; + size_t nfields; + + /**Indexing condition. If set, only field values matching this condition + * will be indexed */ + const char *cond; + size_t ncond; + + /** + * Modifiers for the index itself. This might be + * LCB_N1XSPEC_F_PRIMARY if the index is primary. For raw JSON, + * use `"is_primary":true` + * + * For creation the LCB_N1XSPEC_F_DEFER option is also accepted to + * indicate that the building of this index should be deferred. + */ + unsigned flags; + + /** + * Type of this index, Can be LCB_N1XSPEC_T_DEFAULT for the default + * server type, or an explicit LCB_N1XSPEC_T_GSI or LCB_N1XSPEC_T_VIEW. + * When using JSON, specify `"using":"gsi"` + */ + unsigned ixtype; +} lcb_N1XSPEC; + +/** Input/Output flag. The index is the primary index for the bucket */ +#define LCB_N1XSPEC_F_PRIMARY 1<<16 + +/** + * Input flag for creation. Defer the index building until later. This may + * be used to accelerate the building of multiple indexes, so that the index + * builder can construct all indexes by scanning items only once + */ +#define LCB_N1XSPEC_F_DEFER 1<<17 + +/** + * Input for index type. It's best to just leave this value to 0 (DEFAULT) + * unless you know what you're doing. + */ +#define LCB_N1XSPEC_T_DEFAULT 0 +#define LCB_N1XSPEC_T_GSI 1 +#define LCB_N1XSPEC_T_VIEW 2 + +struct lcb_RESPN1XMGMT_st; + +/** + * Callback for index management operations + * @param instance + * @param cbtype - set to LCB_CALLBACK_N1XMGMT + * @param resp the response structure + */ +typedef void (*lcb_N1XMGMTCALLBACK)(lcb_t instance, + int cbtype, const struct lcb_RESPN1XMGMT_st *resp); + +/** + * @volatile + * Command for index management operations + */ +typedef struct { + /** + * The index to operate on. This can either be a full definition + * (when creating an index) + * or a partial definition (when listing or building + * indexes) + */ + lcb_N1XSPEC spec; + + /** + * Callback to be invoked when operation is complete. + */ + lcb_N1XMGMTCALLBACK callback; +} lcb_CMDN1XMGMT; + +/** + * @volatile + * + * Response structure for index management operations + */ +typedef struct lcb_RESPN1XMGMT_st { + LCB_RESP_BASE + + /** + * A list of pointers to specs. This isn't a simple array of specs because + * the spec structure internally is backed by additional internal data. + */ + const lcb_N1XSPEC *const *specs; + /** Number of specs */ + size_t nspecs; + + /** Inner N1QL response. Examine on error */ + const lcb_RESPN1QL *inner; +} lcb_RESPN1XMGMT; + +/** + * @volatile + * + * Retrieve a list of all indexes in the cluster. If lcb_CMDN1XMGMT::spec + * contains entries then the search will be limited to the appropriate criteria. + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_n1x_list(lcb_t instance, const void *cookie, const lcb_CMDN1XMGMT *cmd); + +/** + * @volatile + * + * Create an index. The index can either be a primary or secondary index, and + * it may be created immediately or it may be deferred. + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_n1x_create(lcb_t instance, const void *cookie, const lcb_CMDN1XMGMT *cmd); + +/** + * @volatile + * Remove an index. + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_n1x_drop(lcb_t instance, const void *cookie, const lcb_CMDN1XMGMT *cmd); + +/** + * @volatile + * + * Build defered indexes. This may be used with the @ref LCB_N1XSPEC_F_DEFER + * option (see lcb_n1x_create()) to initiate the background creation of + * indexes. + * + * lcb_n1x_watchbuild may be used to wait on the status of those indexes. + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_n1x_startbuild(lcb_t instance, const void *cookie, const lcb_CMDN1XMGMT *cmd); + +/** + * @volatile + * + * Structure used for polling index building statuses + */ +typedef struct { + /** + * Input specs. This should be the specs received from lcb_n1x_startbuild()'s + * callback. If you are building from scratch, only the lcb_N1XSPEC::rawjson + * and lcb_INDEXSPEC::nrawjson need to be populated + */ + const lcb_N1XSPEC * const *specs; + /** Number of specs */ + size_t nspec; + + /** + * Maximum amount of time to wait (microseconds). + * If not specified, the default is 30 seconds (30 * 100000) + */ + lcb_U32 timeout; + + /** + * How often to check status (microseconds). + * Default is 500 milliseconds (500000) + */ + lcb_U32 interval; + + /** + * Callback to invoke once the indexes have been built or the timeout + * has been reached. + * + * The callback is only invoked once. + */ + lcb_N1XMGMTCALLBACK callback; +} lcb_CMDN1XWATCH; + +/** + * @volatile + * Poll indexes being built. This allows you to wait until the specified indexes + * which are being built (using lcb_n1x_startbuild()) have been fully + * created. + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_n1x_watchbuild(lcb_t instance, const void *cookie, const lcb_CMDN1XWATCH *cmd); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* LCB_IXMGMT_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/kvbuf.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/kvbuf.h new file mode 100644 index 00000000..5daeb5d5 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/kvbuf.h @@ -0,0 +1,132 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_KVBUF_H +#define LCB_KVBUF_H + +/** + * @file + * + * Low level structures used by commands for buffers. + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** @brief Flags indicating the storage policy for a buffer */ +typedef enum { + LCB_KV_COPY = 0, /**< The buffer should be copied */ + LCB_KV_CONTIG, /**< The buffer is contiguous and should not be copied */ + LCB_KV_IOV, /**< The buffer is not contiguous and should not be copied */ + + /**For use within the hashkey field, indicates that the _length_ + * of the hashkey is the vBucket ID, rather than an actual hashkey */ + LCB_KV_VBID, + + /** + * The buffers are not contiguous (multi-part buffers) but should be + * copied. This avoids having to make the buffers contiguous before + * passing it into the library (only to have the library copy it again) */ + LCB_KV_IOVCOPY +} lcb_KVBUFTYPE; + +#define LCB_KV_HEADER_AND_KEY LCB_KV_CONTIG + +/** + * @brief simple buf/length structure for a contiguous series of bytes + */ +typedef struct lcb_CONTIGBUF { + const void *bytes; + /** Number of total bytes */ + lcb_size_t nbytes; +} lcb_CONTIGBUF; + +/** @brief Common request header for all keys */ +typedef struct lcb_KEYBUF { + /** + * The type of key to provide. This can currently be LCB_KV_COPY (Default) + * to copy the key into the pipeline buffers, or LCB_KV_HEADER_AND_KEY + * to provide a buffer with the header storage and the key. + * + * TODO: + * Currently only LCB_KV_COPY should be used. LCB_KV_HEADER_AND_KEY is used + * internally but may be exposed later on + */ + lcb_KVBUFTYPE type; + lcb_CONTIGBUF contig; +} lcb_KEYBUF; + +/**@private*/ +#define LCB_KEYBUF_IS_EMPTY(k) (k)->contig.nbytes == 0 + +/** + * @brief Initialize a contiguous request backed by a buffer which should be + * copied + * @param req the key request to initialize + * @param k the key to copy + * @param nk the size of the key + */ +#define LCB_KREQ_SIMPLE(req, k, nk) do { \ + (req)->type = LCB_KV_COPY; \ + (req)->contig.bytes = k; \ + (req)->contig.nbytes = nk; \ +} while (0); + +/** + * Structure for an IOV buffer to be supplied as a buffer. This is currently + * only used for value buffers + */ +typedef struct lcb_FRAGBUF { + /** An IOV array */ + lcb_IOV *iov; + + /** Number of elements in iov array */ + unsigned int niov; + + /** + * Total length of the items. This should be set, if known, to prevent the + * library from manually traversing the iov array to calculate the length. + */ + unsigned int total_length; +} lcb_FRAGBUF; + +/** @brief Structure representing a value to be stored */ +typedef struct lcb_VALBUF { + /** + * Value request type. This may be one of: + * - LCB_KV_COPY: Copy over the value into LCB's own buffers + * Use the 'contig' field to supply the information. + * + * - LCB_KV_CONTIG: The buffer is a contiguous chunk of value data. + * Use the 'contig' field to supply the information. + * + * - LCB_KV_IOV: The buffer is a series of IOV elements. Use the 'multi' + * field to supply the information. + */ + lcb_KVBUFTYPE vtype; + union { + lcb_CONTIGBUF contig; + lcb_FRAGBUF multi; + } u_buf; +} lcb_VALBUF; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/n1ql.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/n1ql.h new file mode 100644 index 00000000..ce26da45 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/n1ql.h @@ -0,0 +1,364 @@ +/* + * Copyright 2015 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +#ifndef LCB_N1QL_API_H +#define LCB_N1QL_API_H +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @ingroup lcb-public-api + * @defgroup lcb-n1ql-api N1QL + * @brief Execute N1QL queries and receive resultant rows + */ + +/** + * @addtogroup lcb-n1ql-api + * @{ + */ +typedef struct lcb_RESPN1QL lcb_RESPN1QL; +typedef struct lcb_CMDN1QL lcb_CMDN1QL; +typedef struct lcb_N1QLREQ* lcb_N1QLHANDLE; + +/** + * Callback to be invoked for each row + * @param The instance + * @param Callback type. This is set to @ref LCB_CALLBACK_N1QL + * @param The response. + */ +typedef void (*lcb_N1QLCALLBACK)(lcb_t, int, const lcb_RESPN1QL*); + +/** + * @name N1QL Parameters + * + * The following APIs simply provide wrappers for creating the proper HTTP + * form parameters for N1QL requests. The general flow is to create a + * parameters (@ref lcb_N1QLPARAMS) object, set various options and properties + * on it, and populate an @ref lcb_CMDN1QL object using the lcb_n1p_mkcmd() + * function. + * + * @{ + */ + +/** + * Opaque object representing N1QL parameters. + * This object is created via lcb_n1p_new(), may be cleared + * (for use with another query) via lcb_n1p_reset(), and may be freed via + * lcb_n1p_free(). + */ +typedef struct lcb_N1QLPARAMS_st lcb_N1QLPARAMS; + +/** + * Create a new N1QL Parameters object. The returned object is an opaque + * pointer which may be used to set various properties on a N1QL query. This + * may then be used to populate relevant fields of an @ref lcb_CMDN1QL + * structure. + */ +LIBCOUCHBASE_API +lcb_N1QLPARAMS * +lcb_n1p_new(void); + +/** + * Reset the parameters structure so that it may be reused for a subsequent + * query. Internally this resets the buffer positions to 0, but does not free + * them, making this function optimal for issusing subsequent queries. + * @param params the object to reset + */ +LIBCOUCHBASE_API +void +lcb_n1p_reset(lcb_N1QLPARAMS *params); + +/** + * Free the parameters structure. This should be done when it is no longer + * needed + * @param params the object to reset + */ +LIBCOUCHBASE_API +void +lcb_n1p_free(lcb_N1QLPARAMS *params); + +/** Query is a statement string */ +#define LCB_N1P_QUERY_STATEMENT 1 + +/** @private */ +#define LCB_N1P_QUERY_PREPARED 2 + +/** + * Sets the actual statement to be executed + * @param params the params object + * @param qstr the query string (either N1QL statement or prepared JSON) + * @param nqstr the length of the string. Set to -1 if NUL-terminated + * @param type the type of statement. Should be ::LCB_N1P_QUERY_STATEMENT, + * currently. + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_n1p_setquery(lcb_N1QLPARAMS *params, const char *qstr, size_t nqstr, int type); + +#define lcb_n1p_setstmtz(params, qstr) \ + lcb_n1p_setquery(params, qstr, -1, LCB_N1P_QUERY_STATEMENT) + +/** + * Sets a named argument for the query. + * @param params the object + * @param name The argument name (e.g. `$age`) + * @param n_name + * @param value The argument value (e.g. `42`) + * @param n_value + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_n1p_namedparam(lcb_N1QLPARAMS *params, const char *name, size_t n_name, + const char *value, size_t n_value); + +#define lcb_n1p_namedparamz(params, name, value) \ + lcb_n1p_namedparam(params, name, -1, value, -1) + +/** + * Adds a _positional_ argument for the query + * @param params the params object + * @param value the argument + * @param n_value the length of the argument. + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_n1p_posparam(lcb_N1QLPARAMS *params, const char *value, size_t n_value); + +/** + * Set a query option + * @param params the params object + * @param name the name of the option + * @param n_name + * @param value the value of the option + * @param n_value + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_n1p_setopt(lcb_N1QLPARAMS *params, const char *name, size_t n_name, + const char *value, size_t n_value); + +/** + * Convenience function to set a string parameter with a string value + * @param params the parameter object + * @param key the NUL-terminated option name + * @param value the NUL-terminated option value + */ +#define lcb_n1p_setoptz(params, key, value) \ + lcb_n1p_setopt(params, key, -1, value, -1) + + +/** No consistency constraints */ +#define LCB_N1P_CONSISTENCY_NONE 0 + +/** + * This is implicitly set by the lcb_n1p_synctok() family of functions. This + * will ensure that mutations up to the vector indicated by the mutation token + * passed to lcb_n1p_synctok() are used. + */ +#define LCB_N1P_CONSISTENCY_RYOW 1 + +/** Refresh the snapshot for each request */ +#define LCB_N1P_CONSISTENCY_REQUEST 2 + +/** Refresh the snapshot for each statement */ +#define LCB_N1P_CONSISTENCY_STATEMENT 3 + +/** + * Sets the consistency mode for the request. + * By default results are read from a potentially stale snapshot of the data. + * This may be good for most cases; however at times you want the absolutely + * most recent data. + * @param params the parameters object + * @param mode one of the `LCB_N1P_CONSISTENT_*` constants. + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_n1p_setconsistency(lcb_N1QLPARAMS *params, int mode); + +/** + * Indicate that the query should synchronize its internal snapshot to reflect + * the changes indicated by the given mutation token (`ss`). + * @param params the parameters object + * @param keyspace the keyspace (or bucket name) which this mutation token + * pertains to + * @param st the mutation token + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_n1p_setconsistent_token(lcb_N1QLPARAMS *params, + const char *keyspace, const lcb_MUTATION_TOKEN *st); + +/** + * Indicate that the query should synchronize its internal snapshot to reflect + * any past changes made by the given instance `instance`. + * + * This iterates over all the vbuckets for the given instance and inserts + * the relevant mutation token, using @ref lcb_get_mutation_token + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_n1p_setconsistent_handle(lcb_N1QLPARAMS *params, lcb_t instance); + +/** + * Encodes the request and returns it as a string. The string is valid + * until the next call to the params function. + * @param params the parameter object + * @param[out] rc an error code if there was an issue in encoding + * @return the NUL-terminated query string. + * + * @note Calling this function regenerates the query string internally, + * and is implicitly called by lcb_n1p_mkcmd(). + */ +LIBCOUCHBASE_API +const char * +lcb_n1p_encode(lcb_N1QLPARAMS *params, lcb_error_t *rc); + +/** + * Populates the given low-level lcb_CMDN1QL structure with the relevant fields + * from the params structure. If this function returns successfuly, you must + * ensure that the params object is not modified until the command is + * submitted. + * + * @note + * This may also set some lcb_CMDN1QL::cmdflags fields. If setting your own + * flags, ensure that those flags do not replace the existing ones set by + * this function. + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_n1p_mkcmd(lcb_N1QLPARAMS *params, lcb_CMDN1QL *cmd); + +/**@}*/ + +/** + * @name Low-level N1QL interface + * @{ + */ + +/** + * Prepare and cache the query if required. This may be used on frequently + * issued queries, so they perform better. + */ +#define LCB_CMDN1QL_F_PREPCACHE 1<<16 + +/** @private The lcb_CMDN1QL::query member is an internal JSON structure */ +#define LCB_CMDN1QL_F_JSONQUERY 1<<17 + +/** + * Command structure for N1QL queries. Typically an application will use the + * lcb_N1QLPARAMS structure to populate the #query and #content_type fields. + * + * The #callback field must be specified, and indicates the function the + * library should call when more response data has arrived. + */ +struct lcb_CMDN1QL { + lcb_U32 cmdflags; + /**Query to be placed in the POST request. The library will not perform + * any conversions or validation on this string, so it is up to the user + * (or wrapping library) to ensure that the string is well formed. + * + * If using the @ref lcb_N1QLPARAMS structure, the lcb_n1p_mkcmd() function + * will properly populate this field. + * + * In general the string should either be JSON (in which case, the + * #content_type field should be `application/json`) or url-encoded + * (in which case the #content_type field should be + * `application/x-www-form-urlencoded`) + */ + const char *query; + + /** Length of the query data */ + size_t nquery; + + /** Ignored since version 2.5.3 */ + const char *host; + + /** Ignored since version 2.5.3 */ + const char *content_type; + + /** Callback to be invoked for each row */ + lcb_N1QLCALLBACK callback; + + /**Request handle. Will be set to the handle which may be passed to + * lcb_n1ql_cancel() */ + lcb_N1QLHANDLE *handle; +}; + +/** + * Response for a N1QL query. This is delivered in the @ref lcb_N1QLCALLBACK + * callback function for each result row received. The callback is also called + * one last time when all + */ +struct lcb_RESPN1QL { + #ifndef __LCB_DOXYGEN__ + LCB_RESP_BASE + #else + lcb_U16 rflags; /**< Flags for response structure */ + #endif + + /**Current result row. If #rflags has the ::LCB_RESP_F_FINAL bit set, then + * this field does not contain the actual row, but the remainder of the + * data not included with the resultset; e.g. the JSON surrounding + * the "results" field with any errors or metadata for the response. + */ + const char *row; + /** Length of the row */ + size_t nrow; + /** Raw HTTP response, if applicable */ + const lcb_RESPHTTP *htresp; +}; + +/** + * @volatile + * + * Execute a N1QL query. + * + * This function will send the query to a query server in the cluster + * and will invoke the callback (lcb_CMDN1QL::callback) for each result returned. + * + * @param instance The instance + * @param cookie Pointer to application data + * @param cmd the command + * @return Scheduling success or failure. + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_n1ql_query(lcb_t instance, const void *cookie, const lcb_CMDN1QL *cmd); + + +/** + * Cancels an in-progress request. This will ensure that further callbacks + * for the given request are not delivered. + * + * @param instance the instance + * @param handle the handle for the request. This is obtained during the + * request as an 'out' parameter (see lcb_CMDN1QL::handle) + */ +LIBCOUCHBASE_API +void +lcb_n1ql_cancel(lcb_t instance, lcb_N1QLHANDLE handle); +/**@}*/ + +/**@}*/ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/pktfwd.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/pktfwd.h new file mode 100644 index 00000000..fc725373 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/pktfwd.h @@ -0,0 +1,270 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_PKTFWD_H +#define LCB_PKTFWD_H +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @ingroup lcb-public-api + * @defgroup lcb-pktfwd Raw packet forwarding and dispatch routines + * @brief These functions perform packet forwarding functions to send and + * receive raw packets + * + * @addtogroup lcb-pktfwd + * @{ + */ + +typedef struct rdb_ROPESEG *lcb_BACKBUF; + +/**@brief Request for forwarding a packet + * This structure is passed to the lcb_pktfwd3() function. + */ +typedef struct { + int version; + /**This structure should be initialized to a packet. The packet may be + * in the form of a contiguous buffer to be copied (lcb_VALBUF::vtype should + * be LCB_KV_COPY), a contiguous buffer to be maintained by the user + * (lcb_VALBUF::vtype should be LCB_KV_CONTIG) or an array of lcb_IOV + * structures (which should not be copied; lcb_VALBUF::vtype should be + * LCB_KV_IOV). + * + * This field must contain a complete packet including any extras and body + * associated with it. + * + * If the buffer(s) passed are not copied, you must wait for the + * lcb_pktflushed_callback to be invoked to signal that the buffer is no + * longer needed and may be released back to the application. + * + * @warning + * The first 24 bytes of the buffer (i.e. the memcached header) + * **will be modified**. Currently this is used to modify the `opaque` field + * within the header. + */ + lcb_VALBUF vb; + + /** + * Whether to direct this command to a specific server. This should be + * set if the packet itself doesn't contain any mapping information; and + * should _not_ be used on normal key access commands, since key access + * commands should be mapped to the appropriate server via the vbucket + * mappet. + * + * The server should be specified in the #server_index field */ + char nomap; + + /** + * @brief Specify server index for the command. + * Only valid if #nomap is specified. */ + lcb_U16 server_index; +} lcb_CMDPKTFWD; + +/**@brief Response structure containing the response for a packet */ +typedef struct { + /** Version of the response structure */ + int version; + + /**Pointer to the memcached header. This pointer is guaranteed to be + * properly aligned as a protocol_binary_response_header structure and will + * typically be quicker to access than analyzing the header as found + * inside the #iovs field. + * + * This field may be NULL if the callback is invoked with an error.*/ + const lcb_U8 *header; + + /**Array of lcb_IOV structures containing the response packet. The number + * of items in this array is contained in the #nitems field. + * + * Note that you may modify the contents of the buffers themselves (i.e. + * the memory pointed to by lcb_IOV::iov_base. + * + * When a buffer is no longer needed, lcb_backbuf_unref() should be called + * on its associated lcb_BACKBUF structure - which is located at the same + * array index within the #bufs field (for example, `iovs[n]` will have + * its associated lcb_BACKBUF structure at `bufs[n]`. + */ + lcb_IOV *iovs; + + /**Contains the backing lcb_BACKBUF objects which control the allocation + * lifespan of their associated elements in the #iovs field. */ + lcb_BACKBUF *bufs; + + /** The number of items in the #iovs and #bufs array. Currently this is + * always `1` but may change in the future.*/ + unsigned nitems; +} lcb_PKTFWDRESP; + +/** + * @uncommitted + * + * @brief Forward a raw memcached packet to the cluster. + * + * @details + * This function will process a buffer containing a raw complete memcached packet + * to the cluster. Once the reply for the packet has been received, the + * lcb_pktfwd_callback will be invoked with the `cookie` argument and the + * response data. + * + * If using user-allocated buffers, an additional lcb_pktflushed_callback will + * be invoked once the library no longer needs the buffers for the packet. Note + * that no assumption should be made on the order of invocation for these two + * callbacks - thus it is recommended to implement a reference counting scheme + * on the buffer and decrement the count for each invocation of the callback. + * + * Note that not all memcached commands may be forwarded to this function. + * Specifically, any packet passed to this function: + * + * 1. Must contain a single key after the `header` and `extras` fields. This + * means that commands like _OBSERVE_ and _GET_CLUSTER_CONFIG_ are not + * currently supported. + * 2. The key must be mappable via the vBucket mapping algorithm. This means + * that commands such as _STATS_ are not currently supported. + * 3. Must receive exactly one reply from the server. This means that "quiet" + * versions of commands, such as _GETQ_ and _SETQ_ are not currently + * supported. + * + * If you wish to forward one of the unsupported commands you may use the + * higher level entry points (i.e. lcb_stats3(), lcb_observe3_ctxnew(), etc) + * and manually reconstruct the output packet based on the callbacks received. + * + * Note additionally that the _opaque_ field within the packet will be modified + * by the library. You should store the current opaque value in the structure + * pointed to by the `cookie` parameter and then re-assign it once the packet + * callback has been delivered. + * + * Forwarded packets are subject to the same lifecycle as normal commands. This + * means they may be retried and remapped to other nodes upon topology changes, + * and that they are subject to the same operation timeout semantics. + * + * @param instance the handle + * @param cookie a pointer to be passed to the callbacks for this packet + * @param cmd the command structure containing the buffer mappings for this + * packet. + * + * @return LCB_SUCCESS on success, LCB_INCOMPLETE_PACKET if the packet passed + * does not contain the full body. Other error codes may be returned as well + * + * @see lcb_set_pktfwd_callback lcb_set_pktflushed_callback, mc_forward_packet + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_pktfwd3(lcb_t instance, const void *cookie, const lcb_CMDPKTFWD *cmd); + +/** + * Callback invoked when a response packet has arrived for a request + * @param instance + * @param cookie Opaque pointer associated with the request + * @param err If a response packet could not be obtained, this contains the reason + * @param resp Response structure. This is always present if there is a reply + * from the server. + * + * The lcb_PKTFWDRESP::bufs structures are considered to be invalid after the + * callback has exited because lcb_backbuf_unref() will be called on each of + * them. To ensure they remain valid in your application outside the callback, + * invoke lcb_backbuf_ref() on the required lcb_BACKBUF structures and then + * once they are no longer needed use lcb_backbuf_unref() + */ +typedef void (*lcb_pktfwd_callback) + (lcb_t instance, const void *cookie, lcb_error_t err, lcb_PKTFWDRESP *resp); + +/** + * Callback invoked when the request buffer for a packet is no longer required. + * @param instance + * @param cookie The cookie associated with the request data + */ +typedef void (*lcb_pktflushed_callback) (lcb_t instance, const void *cookie); + +/** + * @uncommitted + * @brief Set the callback to be invoked when a response to a + * forwarded packet has been received. + * + * @param instance the handle + * @param callback the callback to install + * @return the old callback. If `callback` is NULL, this function just returns + * the existing callback + */ +LIBCOUCHBASE_API +lcb_pktfwd_callback +lcb_set_pktfwd_callback(lcb_t instance, lcb_pktfwd_callback callback); + +/** + * @uncommitted + * + * @brief Set the callback to be invoked when the buffer data supplied to the + * packet forwarding function is no longer needed. + * + * @param instance the handle + * @param callback the callback to install + * @return the old callback. If `callback` is NULL then this function just + * returns the existing callback. + */ +LIBCOUCHBASE_API +lcb_pktflushed_callback +lcb_set_pktflushed_callback(lcb_t instance, lcb_pktflushed_callback callback); + + +/** + * @name Response Buffer Handling + * + * @details + * + * The data received as part of a response buffer is _mapped_ by an lcb_IOV + * structure, however the actual allocated data is held together by an + * opaque lcb_BACKBUF structure. This structure allows multiple IOVs to exist + * concurrently within the same block of allocated memory (with different + * offsets and sizes). The lcb_BACKBUF structure functions as an opaque + * reference counted object which controls the duration of the memmory to which + * the IOV is mapped. + * + * From an API perspective, there is a one-to-one correlation between an IOV + * and an lcb_BACKBUF + * + * @{ + */ + +/** + * Indicate that the lcb_BACKBUF object which provides storage for an IOV's + * data pointer will need to remain valid until lcb_backbuf_unref() is called. + * + * This function may be called from an lcb_pktfwd_callback handler to allow + * the contents of the buffer to persist outside the specific callback + * invocation. + */ +LIBCOUCHBASE_API +void +lcb_backbuf_ref(lcb_BACKBUF buf); + +/** + * Indicate that the IOV backed by the specified `buf` is no longer required + * @param buf the buffer which backs the IOV + * After the buffer has been unreferenced, the relating IOV may no longer be + * accessed + */ +LIBCOUCHBASE_API +void +lcb_backbuf_unref(lcb_BACKBUF buf); +/**@}*/ + + +/**@}*/ +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/plugins/io/bsdio-inl.c b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/plugins/io/bsdio-inl.c new file mode 100644 index 00000000..87b38910 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/plugins/io/bsdio-inl.c @@ -0,0 +1,367 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Inline routines for common 'BSD'-style I/O for plugins. + * + * Include this file in your plugin and then call wire_lcb_bsd_impl2 on the + * plugin instance. + */ + +#ifndef _WIN32 +#include +#include +#endif + +static void wire_lcb_bsd_impl2(lcb_bsd_procs*,int); + +#ifdef _WIN32 +#include "wsaerr-inl.c" +static int +get_wserr(lcb_socket_t sock) +{ + DWORD error = WSAGetLastError(); + int ext = 0; + int len = sizeof(ext); + + /* Retrieves extended error status and clear */ + getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&ext, &len); + return wsaerr_map_impl(error); +} + +static lcb_ssize_t +recvv_impl(lcb_io_opt_t iops, lcb_socket_t sock, + struct lcb_iovec_st *iov, lcb_size_t niov) +{ + DWORD flags = 0, nr; + WSABUF *bufptr = (WSABUF *)iov; + + if (WSARecv(sock, bufptr, niov, &nr, &flags, NULL, NULL) == SOCKET_ERROR) { + LCB_IOPS_ERRNO(iops) = get_wserr(sock); + if (LCB_IOPS_ERRNO(iops) == ECONNRESET) { + return 0; + } + return -1; + } + + (void)iops; + return (lcb_ssize_t)nr; +} + +static lcb_ssize_t +recv_impl(lcb_io_opt_t iops, lcb_socket_t sock, void *buf, lcb_size_t nbuf, + int fl_unused) +{ + WSABUF iov; + iov.len = nbuf; + iov.buf = buf; + (void)fl_unused; + return recvv_impl(iops, sock, (struct lcb_iovec_st *)&iov, 1); +} + +static lcb_ssize_t +sendv_impl(lcb_io_opt_t iops, lcb_socket_t sock, struct lcb_iovec_st *iov, + lcb_size_t niov) +{ + DWORD nw, fl = 0; + WSABUF *bufptr = (WSABUF *)iov; + if (WSASend(sock, bufptr, niov, &nw, fl, NULL, NULL) == SOCKET_ERROR) { + LCB_IOPS_ERRNO(iops) = get_wserr(sock); + return -1; + } + return (lcb_ssize_t)nw; +} + +static lcb_ssize_t +send_impl(lcb_io_opt_t iops, lcb_socket_t sock, const void *buf, lcb_size_t nbuf, + int flags) +{ + WSABUF iov; + iov.buf = (void *)buf; + iov.len = nbuf; + (void)flags; + return sendv_impl(iops, sock, (struct lcb_iovec_st *)&iov, 1); +} + +#else +static lcb_ssize_t +recvv_impl(lcb_io_opt_t iops, lcb_socket_t sock, struct lcb_iovec_st *iov, + lcb_size_t niov) +{ + struct msghdr mh; + lcb_ssize_t ret; + + memset(&mh, 0, sizeof(mh)); + mh.msg_iov = (struct iovec *)iov; + mh.msg_iovlen = niov; + ret = recvmsg(sock, &mh, 0); + if (ret < 0) { + LCB_IOPS_ERRNO(iops) = errno; + } + return ret; +} + +static lcb_ssize_t +recv_impl(lcb_io_opt_t iops, lcb_socket_t sock, void *buf, lcb_size_t nbuf, + int flags) +{ + lcb_ssize_t ret = recv(sock, buf, nbuf, flags); + if (ret < 0) { + LCB_IOPS_ERRNO(iops) = errno; + } + return ret; +} + +static lcb_ssize_t +sendv_impl(lcb_io_opt_t iops, lcb_socket_t sock, struct lcb_iovec_st *iov, + lcb_size_t niov) +{ + struct msghdr mh; + lcb_ssize_t ret; + + memset(&mh, 0, sizeof(mh)); + mh.msg_iov = (struct iovec *)iov; + mh.msg_iovlen = niov; + ret = sendmsg(sock, &mh, 0); + if (ret < 0) { + LCB_IOPS_ERRNO(iops) = errno; + } + return ret; +} + +static lcb_ssize_t +send_impl(lcb_io_opt_t iops, lcb_socket_t sock, const void *buf, lcb_size_t nbuf, + int flags) +{ + lcb_ssize_t ret = send(sock, buf, nbuf, flags); + if (ret < 0) { + LCB_IOPS_ERRNO(iops) = errno; + } + return ret; +} + +#endif + +static int make_socket_nonblocking(lcb_socket_t sock) +{ +#ifdef _WIN32 + u_long nonblocking = 1; + if (ioctlsocket(sock, FIONBIO, &nonblocking) == SOCKET_ERROR) { + return -1; + } +#else + int flags; + if ((flags = fcntl(sock, F_GETFL, NULL)) < 0) { + return -1; + } + if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) { + return -1; + } +#endif + return 0; +} + +/* Declare */ +static void close_impl(lcb_io_opt_t,lcb_socket_t); + +static lcb_socket_t +socket_impl(lcb_io_opt_t iops, int domain, int type, int protocol) +{ + lcb_socket_t sock; +#ifdef _WIN32 + sock = (lcb_socket_t)WSASocket(domain, type, protocol, NULL, 0, 0); +#else + sock = socket(domain, type, protocol); +#endif + if (sock == INVALID_SOCKET) { + LCB_IOPS_ERRNO(iops) = errno; + } else { + if (make_socket_nonblocking(sock) != 0) { +#ifdef _WIN32 + LCB_IOPS_ERRNO(iops) = get_wserr(sock); +#else + LCB_IOPS_ERRNO(iops) = errno; +#endif + close_impl(iops, sock); + sock = INVALID_SOCKET; + } + } + return sock; +} + +static void +close_impl(lcb_io_opt_t iops, lcb_socket_t sock) +{ + (void)iops; +#ifdef _WIN32 + closesocket(sock); +#else + close(sock); +#endif +} + +static int +connect_impl(lcb_io_opt_t iops, lcb_socket_t sock, const struct sockaddr *name, + unsigned int namelen) +{ + int ret; + +#ifdef _WIN32 + ret = WSAConnect(sock, name, (int)namelen, NULL, NULL, NULL, NULL); + if (ret == SOCKET_ERROR) { + LCB_IOPS_ERRNO(iops) = get_wserr(sock); + } +#else + ret = connect(sock, name, (socklen_t)namelen); + if (ret < 0) { + LCB_IOPS_ERRNO(iops) = errno; + } +#endif + return ret; +} + +#if LCB_IOPROCS_VERSION >= 3 + +static int +chkclosed_impl(lcb_io_opt_t iops, lcb_socket_t sock, int flags) +{ + char buf = 0; + int rv = 0; + + (void)iops; + + GT_RETRY: + /* We can ignore flags for now, since both Windows and POSIX support MSG_PEEK */ + rv = recv(sock, &buf, 1, MSG_PEEK); + if (rv == 1) { + if (flags & LCB_IO_SOCKCHECK_PEND_IS_ERROR) { + return LCB_IO_SOCKCHECK_STATUS_CLOSED; + } else { + return LCB_IO_SOCKCHECK_STATUS_OK; + } + } else if (rv == 0) { + /* Really closed! */ + return LCB_IO_SOCKCHECK_STATUS_CLOSED; + } else { + int last_err; + #ifdef _WIN32 + last_err = get_wserr(sock); + #else + last_err = errno; + #endif + + if (last_err == EINTR) { + goto GT_RETRY; + } else if (last_err == EWOULDBLOCK || last_err == EAGAIN) { + return LCB_IO_SOCKCHECK_STATUS_OK; /* Nothing to report. So we're good */ + } else { + return LCB_IO_SOCKCHECK_STATUS_CLOSED; + } + } +} +#endif /* LCB_IOPROCS_VERSION >= 3 */ + +#if LCB_IOPROCS_VERSION >= 4 +static int +cntl_getset_impl(lcb_io_opt_t io, lcb_socket_t sock, int mode, int oslevel, + int osopt, int optsize, void *optval) +{ + int rv; + #ifndef _WIN32 + socklen_t dummy = optsize; + #else + int dummy = optsize; + #endif + + if (mode == LCB_IO_CNTL_GET) { + rv = getsockopt(sock, oslevel, osopt, &dummy, optval); + } else { + rv = setsockopt(sock, oslevel, osopt, optval, optsize); + } + if (rv == 0) { + return 0; + } else { + int lasterr; + #ifdef _WIN32 + lasterr = get_wserr(sock); + #else + lasterr = errno; + #endif + LCB_IOPS_ERRNO(io) = lasterr; + return -1; + } +} + +static int +cntl_impl(lcb_io_opt_t io, lcb_socket_t sock, int mode, int option, void *arg) +{ + switch (option) { + case LCB_IO_CNTL_TCP_NODELAY: + return cntl_getset_impl(io, + sock, mode, IPPROTO_TCP, TCP_NODELAY, sizeof(int), arg); + default: + LCB_IOPS_ERRNO(io) = ENOTSUP; + return -1; + } +} +#endif + +#if !defined(LIBCOUCHBASE_INTERNAL) || defined(LCB_IOPS_V12_NO_DEPRECATE) +static void +wire_lcb_bsd_impl(lcb_io_opt_t io) +{ + io->v.v0.recv = recv_impl; + io->v.v0.recvv = recvv_impl; + io->v.v0.send = send_impl; + io->v.v0.sendv = sendv_impl; + io->v.v0.socket = socket_impl; + io->v.v0.connect = connect_impl; + io->v.v0.close = close_impl; + + /* Avoid annoying 'unused' warnings */ + if (0) { wire_lcb_bsd_impl2(NULL,0); } +} +#define lcb__wire0_nowarn() if (0) { wire_lcb_bsd_impl(NULL); } +#else +#define lcb__wire0_nowarn() +#endif + +/** For plugins which use v2 or higher */ +static void +wire_lcb_bsd_impl2(lcb_bsd_procs *procs, int version) +{ + procs->recv = recv_impl; + procs->recvv = recvv_impl; + procs->send = send_impl; + procs->sendv = sendv_impl; + procs->socket0 = socket_impl; + procs->connect0 = connect_impl; + procs->close = close_impl; + + /* Check that this field exists at compile-time */ + #if LCB_IOPROCS_VERSION >= 3 + if (version >= 3) { + procs->is_closed = chkclosed_impl; + } + #endif + #if LCB_IOPROCS_VERSION >= 4 + if (version >= 4) { + procs->cntl = cntl_impl; + } + #endif + lcb__wire0_nowarn(); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/plugins/io/wsaerr-inl.c b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/plugins/io/wsaerr-inl.c new file mode 100644 index 00000000..8b83e5e6 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/plugins/io/wsaerr-inl.c @@ -0,0 +1,76 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wsaerr.h" + +static int +wsaerr_map_impl(DWORD in) +{ + switch (in) { + case WSAECONNRESET: + return ECONNRESET; + + case WSAECONNABORTED: + case WSA_OPERATION_ABORTED: + return ECONNABORTED; + + case WSA_NOT_ENOUGH_MEMORY: + return ENOMEM; + + case WSAEWOULDBLOCK: + case WSA_IO_PENDING: + return EWOULDBLOCK; + + case WSAEINVAL: + return EINVAL; + + case WSAEINPROGRESS: + return EINPROGRESS; + + case WSAEALREADY: + return EALREADY; + + case WSAEISCONN: + return EISCONN; + + case WSAENOTCONN: + case WSAESHUTDOWN: + return ENOTCONN; + + case WSAECONNREFUSED: + return ECONNREFUSED; + + case WSAEINTR: + return EINTR; + + + case WSAENETDOWN: + case WSAENETUNREACH: + case WSAEHOSTUNREACH: + case WSAEHOSTDOWN: + return ENETUNREACH; + + case WSAETIMEDOUT: + return ETIMEDOUT; + + case WSAENOTSOCK: + return ENOTSOCK; + + default: + return EINVAL; + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/plugins/io/wsaerr.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/plugins/io/wsaerr.h new file mode 100644 index 00000000..3810e4f8 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/plugins/io/wsaerr.h @@ -0,0 +1,199 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LIBCOUCHBASE_WIN_ERRNO_SOCK_H +#define LIBCOUCHBASE_WIN_ERRNO_SOCK_H 1 + +#include + +#ifndef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif + +#ifndef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#endif + +#ifndef EALREADY +#define EALREADY WSAEALREADY +#endif + +#ifndef ENOTSOCK +#define ENOTSOCK WSAENOTSOCK +#endif + +#ifndef EDESTADDRREQ +#define EDESTADDRREQ WSAEDESTADDRREQ +#endif + +#ifndef EMSGSIZE +#define EMSGSIZE WSAEMSGSIZE +#endif + +#ifndef EPROTOTYPE +#define EPROTOTYPE WSAEPROTOTYPE +#endif + +#ifndef ENOPROTOOPT +#define ENOPROTOOPT WSAENOPROTOOPT +#endif + +#ifndef EPROTONOSUPPORT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#endif + +#ifndef ESOCKTNOSUPPORT +#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#endif + +#ifndef EOPNOTSUPP +#define EOPNOTSUPP WSAEOPNOTSUPP +#endif + +#ifndef ENOPROTOOPT +#define ENOPROTOOPT WSAENOPROTOOPT +#endif + +#ifndef EPROTONOSUPPORT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#endif + +#ifndef ESOCKTNOSUPPORT +#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#endif + +#ifndef EPFNOSUPPORT +#define EPFNOSUPPORT WSAEPFNOSUPPORT +#endif + +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#endif + +#ifndef EADDRINUSE +#define EADDRINUSE WSAEADDRINUSE +#endif + +#ifndef EADDRNOTAVAIL +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#endif + +#ifndef ENETDOWN +#define ENETDOWN WSAENETDOWN +#endif + +#ifndef ENETUNREACH +#define ENETUNREACH WSAENETUNREACH +#endif + +#ifndef ENETRESET +#define ENETRESET WSAENETRESET +#endif + +#ifndef ECONNABORTED +#define ECONNABORTED WSAECONNABORTED +#endif + +#ifndef ECONNRESET +#define ECONNRESET WSAECONNRESET +#endif + +#ifndef ENOBUFS +#define ENOBUFS WSAENOBUFS +#endif + +#ifndef EISCONN +#define EISCONN WSAEISCONN +#endif + +#ifndef ENOTCONN +#define ENOTCONN WSAENOTCONN +#endif + +#ifndef ESHUTDOWN +#define ESHUTDOWN WSAESHUTDOWN +#endif + +#ifndef ETOOMANYREFS +#define ETOOMANYREFS WSAETOOMANYREFS +#endif + +#ifndef ETIMEDOUT +#define ETIMEDOUT WSAETIMEDOUT +#endif + +#ifndef ECONNREFUSED +#define ECONNREFUSED WSAECONNREFUSED +#endif + +#ifndef ELOOP +#define ELOOP WSAELOOP +#endif + +/* +#ifndef ENAMETOOLONG +#define ENAMETOOLONG WSAENAMETOOLONG +#endif +*/ + +#ifndef EHOSTDOWN +#define EHOSTDOWN WSAEHOSTDOWN +#endif + +#ifndef EHOSTUNREACH +#define EHOSTUNREACH WSAEHOSTUNREACH +#endif + +/* +#ifndef ENOTEMPTY +#define ENOTEMPTY WSAENOTEMPTY +#endif +*/ + +#ifndef EPROCLIM +#define EPROCLIM WSAEPROCLIM +#endif + +#ifndef EUSERS +#define EUSERS WSAEUSERS +#endif + +#ifndef EDQUOT +#define EDQUOT WSAEDQUOT +#endif + +#ifndef ESTALE +#define ESTALE WSAESTALE +#endif + +#ifndef EREMOTE +#define EREMOTE WSAEREMOTE +#endif + +#ifndef EPROTO +#define EPROTO WSAEPROTONOSUPPORT +#endif + +#ifndef ECANCELED +#define ECANCELED WSAECANCELLED +#endif + +/** Some versions have this; some don't */ +#ifndef ENOTSUP +#define ENOTSUP -1 +#endif + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/subdoc.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/subdoc.h new file mode 100644 index 00000000..40e5d517 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/subdoc.h @@ -0,0 +1,312 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2016 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LCB_SUBDOC_H +#define LCB_SUBDOC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/**@ingroup lcb-public-api + * @defgroup lcb-subdoc Sub-Document API + * @brief Experimental in-document API access + * @details The sub-document API uses features from the upcoming Couchbase + * 4.5 release which allows access to parts of the document. These parts are + * called _sub-documents_ and can be accessed using the sub-document API + * + * @warning + * The sub-document API is experimental and subject to change and is here for + * demonstration purposes only. + * + * @addtogroup lcb-subdoc + * @{ + */ + +/** + * @brief Sub-Document command codes + * + * These command codes should be applied as values to lcb_SDSPEC::sdcmd and + * indicate which type of subdoc command the server should perform. + */ +typedef enum { + /** + * Retrieve the value for a path + */ + LCB_SDCMD_GET = 1, + + /** + * Check if the value for a path exists. If the path exists then the error + * code will be @ref LCB_SUCCESS + */ + LCB_SDCMD_EXISTS, + + /** + * Replace the value at the specified path. This operation can work + * on any existing and valid path. + */ + LCB_SDCMD_REPLACE, + + /** + * Add the value at the given path, if the given path does not exist. + * The penultimate path component must point to an array. The operation + * may be used in conjunction with @ref LCB_SDSPEC_F_MKINTERMEDIATES to + * create the parent dictionary (and its parents as well) if it does not + * yet exist. + */ + LCB_SDCMD_DICT_ADD, + + /** + * Unconditionally set the value at the path. This logically + * attempts to perform a @ref LCB_SDCMD_REPLACE, and if it fails, performs + * an @ref LCB_SDCMD_DICT_ADD. + */ + LCB_SDCMD_DICT_UPSERT, + + /** + * Prepend the value(s) to the array indicated by the path. The path should + * reference an array. When the @ref LCB_SDSPEC_F_MKINTERMEDIATES flag + * is specified then the array may be created if it does not exist. + * + * Note that it is possible to add more than a single value to an array + * in an operation (this is valid for this commnand as well as + * @ref LCB_SDCMD_ARRAY_ADD_LAST and @ref LCB_SDCMD_ARRAY_INSERT). Multiple + * items can be specified by placing a comma between then (the values should + * otherwise be valid JSON). + */ + LCB_SDCMD_ARRAY_ADD_FIRST, + + /** + * Identical to @ref LCB_SDCMD_ARRAY_ADD_FIRST but places the item(s) + * at the end of the array rather than at the beginning. + */ + LCB_SDCMD_ARRAY_ADD_LAST, + + /** + * Add the value to the array indicated by the path, if the value is not + * already in the array. The @ref LCB_SDSPEC_F_MKINTERMEDIATES flag can + * be specified to create the array if it does not already exist. + * + * Currently the value for this operation must be a JSON primitive (i.e. + * no arrays or dictionaries) and the existing array itself must also + * contain only primitives (otherwise a @ref LCB_SUBDOC_PATH_MISMATCH + * error will be received). + */ + LCB_SDCMD_ARRAY_ADD_UNIQUE, + + /** + * Add the value at the given array index. Unlike other array operations, + * the path specified should include the actual index at which the item(s) + * should be placed, for example `array[2]` will cause the value(s) to be + * the 3rd item(s) in the array. + * + * The array must already exist and the @ref LCB_SDCMD_F_MKINTERMEDIATES + * flag is not honored. + */ + LCB_SDCMD_ARRAY_INSERT, + + /** + * Increment or decrement an existing numeric path. If the number does + * not exist, it will be created (though its parents will not, unless + * @ref LCB_SDSPEC_F_MKINTERMEDIATES is specified). + * + * The value for this operation should be a valid JSON-encoded integer and + * must be between `INT64_MIN` and `INT64_MAX`, inclusive. + */ + LCB_SDCMD_COUNTER, + + /** + * Remove an existing path in the document. + */ + LCB_SDCMD_REMOVE, + + /** + * Count the number of elements in an array or dictionary + */ + LCB_SDCMD_GET_COUNT, + + LCB_SDCMD_MAX +} lcb_SUBDOCOP; + +/** + * @brief Subdoc command specification. + * This structure describes an operation and its path, and possibly its value. + * This structure is provided in an array to the lcb_CMDSUBDOC::specs field. + */ +typedef struct { + /** + * The command code, @ref lcb_SUBDOCOP. There is no default for this + * value, and it therefore must be set. + */ + lcb_U32 sdcmd; + + /** + * Set of option flags for the command. Currently the only option known + * is @ref LCB_SDSPEC_F_MKINTERMEDIATES + */ + lcb_U32 options; + + /** + * Path for the operation. This should be assigned using + * @ref LCB_SDSPEC_SET_PATH. The contents of the path should be valid + * until the operation is scheduled (lcb_subdoc3()) + */ + lcb_KEYBUF path; + + /** + * @value for the operation. This should be assigned using + * @ref LCB_SDSPEC_SET_VALUE. The contents of the value should be valid + * until the operation is scheduled (i.e. lcb_subdoc3()) + */ + lcb_VALBUF value; +} lcb_SDSPEC; + +/** Create intermediate paths */ +#define LCB_SDSPEC_F_MKINTERMEDIATES (1<<16) + +/** Create document if it does not exist */ +#define LCB_SDSPEC_F_MKDOCUMENT (1<<17) + +/** + * Set the path for an @ref lcb_SDSPEC structure + * @param s pointer to spec + * @param p the path buffer + * @param n the length of the path buffer + */ +#define LCB_SDSPEC_SET_PATH(s, p, n) do { \ + (s)->path.contig.bytes = p; \ + (s)->path.contig.nbytes = n; \ + (s)->path.type = LCB_KV_COPY; \ +} while (0); + +/** + * Set the value for the @ref lcb_SDSPEC structure + * @param s pointer to spec + * @param v the value buffer + * @param n the length of the value buffer + */ +#define LCB_SDSPEC_SET_VALUE(s, v, n) \ + LCB_CMD_SET_VALUE(s, v, n) + +#define LCB_SDSPEC_INIT(spec, cmd_, path_, npath_, val_, nval_) do { \ + (spec)->sdcmd = cmd_; \ + LCB_SDSPEC_SET_PATH(spec, path_, npath_); \ + LCB_CMD_SET_VALUE(spec, val_, nval_); \ +} while (0); + +#define LCB_SDMULTI_MODE_INVALID 0 +#define LCB_SDMULTI_MODE_LOOKUP 1 +#define LCB_SDMULTI_MODE_MUTATE 2 +typedef struct { + LCB_CMD_BASE; + + /** + * An array of one or more command specifications. The storage + * for the array need only persist for the duration of the + * lcb_subdoc3() call. + * + * The specs array must be valid only through the invocation + * of lcb_subdoc3(). As such, they can reside on the stack and + * be re-used for scheduling multiple commands. See subdoc-simple.cc + */ + const lcb_SDSPEC *specs; + /** + * Number of entries in #specs + */ + size_t nspecs; + /** + * If the scheduling of the command failed, the index of the entry which + * caused the failure will be written to this pointer. + * + * If the value is -1 then the failure took place at the command level + * and not at the spec level. + */ + int *error_index; + /** + * Operation mode to use. This can either be @ref LCB_SDMULTI_MODE_LOOKUP + * or @ref LCB_SDMULTI_MODE_MUTATE. + * + * This field may be left empty, in which case the mode is implicitly + * derived from the _first_ command issued. + */ + lcb_U32 multimode; +} lcb_CMDSUBDOC; + +/** + * Perform one or more subdocument operations. + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_subdoc3(lcb_t instance, const void *cookie, const lcb_CMDSUBDOC *cmd); + +/** + * Response structure for multi lookups. If the top level response is successful + * then the individual results may be retrieved using lcb_sdmlookup_next() + */ +typedef struct { + LCB_RESP_BASE + const void *responses; + /** Use with lcb_backbuf_ref/unref */ + void *bufh; +} lcb_RESPSUBDOC; + +/** + * Structure for a single sub-document mutation or lookup result. + * Note that #value and #nvalue are only valid if #status is ::LCB_SUCCESS + */ +typedef struct { + /** Value for the mutation (only applicable for ::LCB_SUBDOC_COUNTER, currently) */ + const void *value; + /** Length of the value */ + size_t nvalue; + /** Status code */ + lcb_error_t status; + + /** + * Request index which this result pertains to. This field only + * makes sense for multi mutations where not all request specs are returned + * in the result + */ + lcb_U8 index; +} lcb_SDENTRY; + +/** + * Iterate over the results for a subdocument response. + * + * @warning + * This function _must_ be called from within the callback. The response itself + * may contain a pointer to internal stack data which is no longer valid + * once the callback exits. + * + * @param resp the response received from within the callback. + * @param[out] out structure to store the current result + * @param[in,out] iter internal iterator. First call should initialize this to 0 + * Note that this value may be 0, in which case only the first response is + * returned. + * + * @return If this function returns nonzero then `out` will contain a valid + * entry. If this function returns 0 then `ent` is invalid and no more results + * remain for the response. + */ +LIBCOUCHBASE_API +int +lcb_sdresult_next(const lcb_RESPSUBDOC *resp, lcb_SDENTRY *out, size_t *iter); + +/**@}*/ +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/sysdefs.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/sysdefs.h new file mode 100644 index 00000000..a015e463 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/sysdefs.h @@ -0,0 +1,98 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Basic platform includes. + */ +#ifndef LCB_SYSDEFS_H +#define LCB_SYSDEFS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 + #include + #include + #include + + typedef __int64 lcb_int64_t; + typedef __int32 lcb_int32_t; + typedef SIZE_T lcb_size_t; + typedef SSIZE_T lcb_ssize_t; + typedef unsigned __int8 lcb_uint8_t; + typedef unsigned __int16 lcb_vbucket_t; + typedef unsigned __int16 lcb_uint16_t; + typedef unsigned __int32 lcb_uint32_t; + typedef unsigned __int64 lcb_cas_t; + typedef unsigned __int64 lcb_uint64_t; + + /** FIXME: This should be a native type, but it's already defined here.. */ + typedef unsigned __int32 lcb_time_t; +#else + #include + #include + #include + + #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + #include + #endif + + typedef int64_t lcb_int64_t; + typedef int32_t lcb_int32_t; + typedef size_t lcb_size_t; + typedef ssize_t lcb_ssize_t; + typedef uint16_t lcb_vbucket_t; + typedef uint8_t lcb_uint8_t; + typedef uint16_t lcb_uint16_t; + typedef uint32_t lcb_uint32_t; + typedef uint64_t lcb_cas_t; + typedef uint64_t lcb_uint64_t; + typedef time_t lcb_time_t; +#endif + +typedef lcb_int64_t lcb_S64; /**< @brief Signed 64 bit type */ +typedef lcb_uint64_t lcb_U64; /**< @brief Unsigned 64 bit type */ +typedef lcb_uint32_t lcb_U32; /**< @brief Unsigned 32 bit type */ +typedef lcb_int32_t lcb_S32; /**< @brief Signed 32 bit type */ +typedef lcb_uint16_t lcb_U16; /**< @brief Unsigned 16 bit type */ +typedef lcb_uint8_t lcb_U8; /**< @brief unsigned 8 bit type */ +typedef lcb_size_t lcb_SIZE; /**< @brief Unsigned size type */ +typedef lcb_ssize_t lcb_SSIZE; /**<@brief Signed size type */ +typedef lcb_time_t lcb_SECS; /**< @brief Unsigned 'seconds time' type */ +typedef lcb_cas_t lcb_CAS; + +#ifdef __GNUC__ +#define LCB_DEPRECATED(X) X __attribute__((deprecated)) + #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) + #define LCB_DEPRECATED2(X, reason) X __attribute__((deprecated(reason))) + #else + #define LCB_DEPRECATED2(X, reason) LCB_DEPRECATED(X) + #endif +#elif defined(_MSC_VER) +#define LCB_DEPRECATED(X) __declspec(deprecated) X +#define LCB_DEPRECATED2(X, reason) __declspec(deprecated(reason)) X +#else +#define LCB_DEPRECATED(X) X +#define LCB_DEPRECATED2(X, reason) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LCB_SYSDEFS_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/vbucket.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/vbucket.h new file mode 100644 index 00000000..1422333a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/vbucket.h @@ -0,0 +1,643 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_VBUCKET_H +#define LCB_VBUCKET_H +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * @brief vBucket Mapping API + */ + +/** + * @ingroup lcb-public-api + * @defgroup lcb-vbucket-api vBucket + * @details + * These routines contain functionality for parsing a cluster topology configuration + * and mapping keys to cluster nodes appropriately. + */ + +/** + * @addtogroup lcb-vbucket-api + * @{ + */ + +/**@brief This enum functions as a 'key' to indicate a specific service offered + * by a node */ +typedef enum { + LCBVB_SVCTYPE_DATA = 0, /**< memcached/Data port */ + LCBVB_SVCTYPE_VIEWS, /**< Views/CAPI port */ + LCBVB_SVCTYPE_MGMT, /**< Administrative/'REST' UI */ + LCBVB_SVCTYPE_IXQUERY, /**< Index query */ + LCBVB_SVCTYPE_IXADMIN, /**< Index administration */ + LCBVB_SVCTYPE_N1QL, /**< N1QL Query */ + LCBVB_SVCTYPE_FTS, /**< Fulltext */ + LCBVB_SVCTYPE__MAX +} lcbvb_SVCTYPE; + +/**@brief This enum functions to indicate the 'mode' of the service. Currently + * this is to distinguish between SSL and plain transports */ +typedef enum { + LCBVB_SVCMODE_PLAIN = 0, /**< Plain transport */ + LCBVB_SVCMODE_SSL, /**< SSL Transport */ + LCBVB_SVCMODE__MAX +} lcbvb_SVCMODE; + +/** + * @volatile. ABI/API compatibility not guaranteed between versions + * @brief Services which may be provided by a node + */ +typedef struct { + lcb_U16 data; /**< Data port for key-value operations (memcached protocol) */ + lcb_U16 mgmt; /**< Port for adminsitrative operations (HTTP) */ + lcb_U16 views; /**< Port for view queries (HTTP) */ + lcb_U16 ixquery; /**< Indexing query port */ + lcb_U16 ixadmin; /**< Indexing admin port (HTTP) */ + lcb_U16 n1ql; /**< Query port */ + lcb_U16 fts; /**< CBFT */ + char *views_base_; /**< Views base URL */ + char *query_base_; /**< N1QL base URL */ + char *fts_base_; + char *hoststrs[LCBVB_SVCTYPE__MAX]; +} lcbvb_SERVICES; + +/** + * @volatile. ABI/API compatibility not guaranteed between versions. + * + * @brief Node in the cluster + * This structure represents a node in the cluster. The node has a hostname + * (@ref #hostname), and various services. + */ +typedef struct { + lcbvb_SERVICES svc; /**< Plain services */ + lcbvb_SERVICES svc_ssl; /**< SSL Services */ + char *authority; /**< host:dataport for comparison */ + char *hostname; /**< Hostname for the node */ + char *viewpath; /**< Path prefix for view queries */ + char *querypath; /**< Path prefix for n1ql queries */ + char *ftspath; /**< Path prefix for fulltext queries */ + unsigned nvbs; /**< Total number of vbuckets the server has assigned */ +} lcbvb_SERVER; + +/**@volatile. ABI/API compatibility not guaranteed between versions */ +typedef struct { + int servers[4]; +} lcbvb_VBUCKET; + +/**@volatile*/ +typedef struct { + lcb_U32 index; + lcb_U32 point; +} lcbvb_CONTINUUM; + +/** @brief Type of algorithm used to distribute keys. + * This also indicates the type of bucket */ +typedef enum { + LCBVB_DIST_VBUCKET = 0, /**< vBucket hashing ("couchbase") bucket */ + LCBVB_DIST_KETAMA = 1 /**< Ketama hashing ("memcached") bucket */ +} lcbvb_DISTMODE; + +/**@volatile. ABI/API compatibility not guaranteed between versions. + * @brief Structure containing the configuration.*/ +typedef struct lcbvb_CONFIG_st { + lcbvb_DISTMODE dtype; /**< Type of bucket/distribution */ + unsigned nvb; /**< Number of vbuckets */ + unsigned ndatasrv; /**< Number of data (memcached) servers */ + unsigned nsrv; /** Number of servers */ + unsigned nrepl; /**< Number of replicas */ + unsigned ncontinuum; /* number of continuum points */ + unsigned is3x; /* whether server 3.0 config semantics are in place */ + int revid; /* revision ID from the config (-1 if not present) */ + char *buuid; /* bucket UUID */ + char *bname; /* bucket name */ + const char *errstr; /* last error */ + lcbvb_SERVER *servers; /* nodes */ + lcbvb_VBUCKET *vbuckets; /* vbucket map */ + lcbvb_VBUCKET *ffvbuckets; /* fast-forward map */ + lcbvb_CONTINUUM *continuum; /* ketama continuums */ + int *randbuf; /* Used for random server selection */ +} lcbvb_CONFIG; + + +#define LCBVB_NSERVERS(cfg) (cfg)->nsrv +#define LCBVB_NDATASERVERS(cfg) (cfg)->ndatasrv +#define LCBVB_NREPLICAS(cfg) (cfg)->nrepl +#define LCBVB_DISTTYPE(cfg) (cfg)->dtype +#define LCBVB_GET_SERVER(conf, ix) ((conf)->servers + ix) + +/** + * @uncommitted + * @brief Allocate a new config + * This can be used to create new config object and load it with a JSON config, + * optionally retrieving the error code + * @code{.c} + * lcbvb_CONFIG *cfg = lcbvb_create(); + * if (0 != lcbvb_load_json(cfg, json)) { + * printf("Got error!", lcbvb_get_error(cfg)); + * lcbvb_destroy(cfg); + * } + * @endcode + */ +LIBCOUCHBASE_API +lcbvb_CONFIG * +lcbvb_create(void); + +/** + * @uncommitted + * Parse the configuration string in `data` and return a new config object + * @param data + * @return A new config object, or NULL on error. + */ +LIBCOUCHBASE_API +lcbvb_CONFIG * +lcbvb_parse_json(const char *data); + +/** + * @committed + * Load a JSON-based configuration string into a configuration object + * @param vbc Object to populate + * @param data NUL-terminated string to parse + * @return 0 on success, nonzero on failure + * @note it is recommended to use this function rather than lcbvb_parse_json() + * as this will contain the error string in the configuration in case of parse + * failures. + */ +LIBCOUCHBASE_API +int +lcbvb_load_json(lcbvb_CONFIG *vbc, const char *data); + +/**@brief Serialize the current config as a JSON string. + * @volatile + * Serialize the current configuration as a JSON string. The string returned is + * NUL-terminated and should be freed using the free() function. + */ +LIBCOUCHBASE_API +char * +lcbvb_save_json(lcbvb_CONFIG *vbc); + +/** + * @committed + * @brief Return a string indicating why parsing the configuration failed + * @return An error string. Do not free this string + */ +LIBCOUCHBASE_API +const char * +lcbvb_get_error(const lcbvb_CONFIG *vbc); + +/** + * @volatile + * @brief Replace hostname placeholders with specific host string + * This function shall replace hostname placeholists with the actual host string + * specified in `hoststr`. + * @param cfg the configuration + * @param hostname the actual hostname to use. + * + * Use this immediately after a successful parsing of the configuration file. + */ +LIBCOUCHBASE_API +void +lcbvb_replace_host(lcbvb_CONFIG *cfg, const char *hostname); + +/** + * @committed + * Destroy the configuration object + * @param conf + */ +LIBCOUCHBASE_API +void +lcbvb_destroy(lcbvb_CONFIG *conf); + +/** + * @committed + * + * Gets the master node index for the given vbucket + * @param cfg The configuration + * @param vbid The vbucket to query + * @return The master index. -1 if offline + * @warning This function does no bounds checking for `vbid`. Ensure it is + * within range of `0 < vbid < cfg->nvbs` + */ +LIBCOUCHBASE_API +int +lcbvb_vbmaster(lcbvb_CONFIG *cfg, int vbid); + +/** + * @committed + * + * Return the 0-based replica index for the given vbucket. + * @param cfg The configuration object + * @param vbid The vbucket to query + * @param ix the replica index to retrieve. This is a number ranging from + * 0 to vbc->nrepl exclusive. + * @return The replica index, or -1 if offline + * + * @warning This function does no bounds checking for `vbix` or `ix`. Ensure + * that `0 < vbid < cfg->nvbs` and `-1 < ix < cfg->nrepl` + */ +LIBCOUCHBASE_API +int +lcbvb_vbreplica(lcbvb_CONFIG *cfg, int vbid, unsigned ix); + +/** + * @volatile + * This allows to get the given index for a vbucket server. If the index is + * 0 then this returns the master index, if the index is greater then it + * returns the replica index + */ +#define lcbvb_vbserver(cfg, vbid, ix) ( (ix == 0) ? \ + lcbvb_vbmaster(cfg, vbid) : lcbvb_vbreplica(cfg, vbid, ix-1) ) + + +/** + * uncommitted + * Equivalent to + * @code{.c} + * lcbvb_nmv_remap_ex(cfg, vbid, bad, 0); + * @endcode + */ +#define lcbvb_nmv_remap(cfg, vbid, bad) lcbvb_nmv_remap_ex(cfg, vbid, bad, 0) + +/** + * @uncommitted + * + * Using various guesswork and heuristics, attempt to locate an alternate node + * for the master of a given vbucket. This should be used if the master index + * is -1 or if the master index is deemed incorrect by some other means. + * + * @param cfg the configuration object + * @param vbid the vbucket index to loop up + * @param bad the index known to be bad. Passing this parameter allows the + * handler to safely call this function and be sure that a previous call's + * applied heuristics will not affect the modified map. + * @param use_heuristics whether additional heuristics should be used. If + * heuristics is off, only the fast-forward map is employed. + */ +int +lcbvb_nmv_remap_ex(lcbvb_CONFIG *cfg, int vbid, int bad, int use_heuristics); + +/** + * @committed + * + * Map a given string to a vbucket and server + * @param cfg The configuration object + * @param key Key to map + * @param n Length of key + * @param[out] vbid Will contain the vBucket + * @param[out] srvix Will contain the server index + * @return 0 for now + */ +LIBCOUCHBASE_API +int +lcbvb_map_key(lcbvb_CONFIG *cfg, const void *key, lcb_SIZE n, + int *vbid, int *srvix); + +/** + * @committed + * + * Maps a key to a vBucket ID + * @param cfg The configuration + * @param key The key to retrieve + * @param n The size of the key + * @return the vBucket for the key + */ +LIBCOUCHBASE_API +int +lcbvb_k2vb(lcbvb_CONFIG *cfg, const void *key, lcb_SIZE n); + +/** + * @uncomitted + * Determines if a given server index is either a master or a replica for a + * vbucket + * @param vbc the configuration + * @param vbid the vbucket number + * @param ix the server index to check against + * @returns nonzero if the server `ix` is either a master or a replica for the + * vbucket `vbid`. Returns 0 otherwise. + */ +LIBCOUCHBASE_API +int +lcbvb_has_vbucket(lcbvb_CONFIG *vbc, int vbid, int ix); + +/**@committed + * @brief Get the number of servers in the bucket. Note that not all servers + * may actually be available. + * @param cfg The configuration + * @return The number of servers + **/ +LIBCOUCHBASE_API +unsigned +lcbvb_get_nservers(const lcbvb_CONFIG *cfg); + +/**@committed + * @brief Get the number of replicas the bucket is configured with + * Note that not all replicas may necessarily be online or available. + * @param cfg the configuration + * @return the number of configured replicas + */ +LIBCOUCHBASE_API +unsigned +lcbvb_get_nreplicas(const lcbvb_CONFIG *cfg); + +/**@committed + * @brief Get the distribution mode (AKA bucket type) of the bucket + * @param cfg the configuration + * @return the distribution mode + */ +LIBCOUCHBASE_API +lcbvb_DISTMODE +lcbvb_get_distmode(const lcbvb_CONFIG *cfg); + +/** + * @committed + * + * @brief Get the revision for this configuration. + * + * The revision is an + * integer which is increased each time the cluster generates a new + * configuration. This feature is available only on configurations generated + * by nodes of Couchbase Server v2.5 or later. + * + * @param cfg the configuration + * @return The revision ID, or `-1` if the config does not have a revision + */ +LIBCOUCHBASE_API +int +lcbvb_get_revision(const lcbvb_CONFIG *cfg); + +/** + * @committed + * @brief Gets the port associated with a given service of a given mode on a given + * server + * @param cfg the config object + * @param ix the index of the server to query + * @param type the type of service being provided + * @param mode the mode of transport being used (e.g. plain, ssl) + * @return a number greater than zero if the port exists, 0 otherwise + */ +LIBCOUCHBASE_API +unsigned +lcbvb_get_port(lcbvb_CONFIG *cfg, unsigned ix, + lcbvb_SVCTYPE type, lcbvb_SVCMODE mode); + + +/** + * @committed + * + * @brief Return a string for the given service + * This is like lcbvb_get_port but returns a string in the form of `host:port` + * rather than the numeric port + * + * @param cfg + * @param ix + * @param type + * @param mode + * @return A string if the service is found, NULL otherwise. The storage + * duration of the string is valid until the configuration object is + * destroyed. + */ +LIBCOUCHBASE_API +const char * +lcbvb_get_hostport(lcbvb_CONFIG *cfg, unsigned ix, + lcbvb_SVCTYPE type, lcbvb_SVCMODE mode); + +/** + * @committed + * + * @brief Get the hostname of a given server index. This may be used if all + * nodes reside on different hostnames, and can be used to answer the question + * of "which node does this index belong to" without having to perform + * additional string processing on the port of the string. + * + * @param cfg the configuration + * @param ix the index of the server to look up + * @return a hostname without a port, or NULL if the index is out of bounds + */ +LIBCOUCHBASE_API +const char * +lcbvb_get_hostname(const lcbvb_CONFIG *cfg, unsigned ix); + +/** + * Function to return the URL prefix for a REST service. + * + * Returns a string suitable for being passed as a URL. This is only valid + * for ::LCBVB_SVCTYPE_VIEWS and ::LCBVB_SVCTYPE_N1QL. + * + * This function is different from lcbvb_get_hostport() -- it is mainly a + * convenience, but does cache the string. Also, theoretically the cluster + * is free to choose a _different_ URL prefix for a given service. Using this + * function will guarantee the URL prefix is correct. + */ +LIBCOUCHBASE_API +const char * +lcbvb_get_resturl(lcbvb_CONFIG *cfg, unsigned ix, + lcbvb_SVCTYPE type, lcbvb_SVCMODE mode); + +/** + * Convenience function to select a random node for a service. + * @return 0 or greater if a node was found; a negative number if no node + * contains a service with the given criteria. + */ +LIBCOUCHBASE_API +int +lcbvb_get_randhost(const lcbvb_CONFIG *cfg, + lcbvb_SVCTYPE type, lcbvb_SVCMODE mode); + +/** + * Get random node, excluding nodes already tried + * @param cfg the config + * @param type type of service + * @param mode transport mode + * @param used an array of integers representing server indexes (should be of + * size LCBVB_NSERVERS). Servers whose indexes in the `used` array are nonzero + * will be *skipped*. + * + * @return a server index, or -1 if no server remains (either because no + * server has the service, or because all available servers are in the + * exclude list) + */ +LIBCOUCHBASE_API +int +lcbvb_get_randhost_ex(const lcbvb_CONFIG *cfg, + lcbvb_SVCTYPE type, lcbvb_SVCMODE mode, int *used); + +/** @brief Structure representing changes between two configurations */ +typedef struct { + /** List of strings of servers added (via `host:data_port`) */ + char **servers_added; + /** List of strings of servers removed (via `host:data_port`) */ + char **servers_removed; + /** How many vBuckets have had an ownership change */ + int n_vb_changes; + /** Whether the ordering of the nodes has changed as well */ + int sequence_changed; +} lcbvb_CONFIGDIFF, VBUCKET_CONFIG_DIFF; + +/** @brief Convenience enum to determine the mode of change */ +typedef enum { + LCBVB_NO_CHANGES = 0, /**< No changes between configs */ + LCBVB_SERVERS_MODIFIED = 1 << 0, /**< Servers have been added or removed */ + LCBVB_MAP_MODIFIED = 1 << 1 /**< vBuckets have been transferred */ +} lcbvb_CHANGETYPE, VBUCKET_CHANGE_STATUS; + +/** + * @volatile + * + * @brief Compare two configurations and return information on the changes + * @param from the original configuration to use as the base + * @param to the new configuration + * @return an object which may be inspected, or NULL on allocation failure. The + * returned object should be freed with lcbvb_free_diff() + * @see lcbvb_get_changetype() + */ +LIBCOUCHBASE_API +lcbvb_CONFIGDIFF * +lcbvb_compare(lcbvb_CONFIG *from, lcbvb_CONFIG *to); + +/** @brief Free the structure returned by lcbvb_compare() */ +LIBCOUCHBASE_API +void +lcbvb_free_diff(lcbvb_CONFIGDIFF *diff); + +/**@brief Get a quick summary of the changes in the passed object + * @param diff the diff returned from lcbvb_compare() + */ +LIBCOUCHBASE_API +lcbvb_CHANGETYPE +lcbvb_get_changetype(lcbvb_CONFIGDIFF *diff); + +/** + * @volatile + * + * @brief Generate a sample configuration. + * @param vb a new configuration object returned via lcbvb_create() + * @param name the name of the bucket + * @param uuid UUID for the bucket + * @param servers an array of server objects which will serve as the basis + * for the server list within the configuration. The memory pointed to by + * servers may be released after this function has completed + * @param nservers number of servers in the array + * @param nreplica how many replicas for the bucket + * @param nvbuckets how many vbuckets for the bucket + */ +LIBCOUCHBASE_API +int +lcbvb_genconfig_ex(lcbvb_CONFIG *vb, + const char *name, const char *uuid, + const lcbvb_SERVER *servers, + unsigned nservers, unsigned nreplica, unsigned nvbuckets); + +/** + * @volatile + * + * @brief Generate a sample configuration used for testing. + * @param vb a new configuration object returned via lcbvb_create() + * @param nservers how many nodes to place into the configuration + * @param nreplica how many replicas should be assigned to the bucket + * @param nvbuckets how many vbuckets to create + * @return 0 on success, nonzero on error + * + * @note The base port for the lcbvb_SERVICES::data starts at 1000; the base + * port for lcbvb_SERVICES::views starts at 2000 and the base port for + * lcbvb_SERVICES::mgmt starts at 3000. The port number is incremented for + * each additional node. + */ +LIBCOUCHBASE_API +int +lcbvb_genconfig(lcbvb_CONFIG *vb, + unsigned nservers, unsigned nreplica, unsigned nvbuckets); + +/** + * @volatile + * Generate a fast-forward vBucket map for the configuration. This simply + * provides alternate indices. + */ +LIBCOUCHBASE_API +void +lcbvb_genffmap(lcbvb_CONFIG *vb); + + +/** + * @volatile + * Convert the configuration to a ketama one. + * @param vb The configuration object. + */ +LIBCOUCHBASE_API +void +lcbvb_make_ketama(lcbvb_CONFIG *vb); + +/** + * @committed + * + * Get the views URL base. + * @param cfg The configuration + * @param ix The index of the server to fetch + * @param mode The mode, either plain or ssl + * @return A string reprenting the URL, or NULL if not available. + */ +LIBCOUCHBASE_API +const char * +lcbvb_get_capibase(lcbvb_CONFIG *cfg, unsigned ix, lcbvb_SVCMODE mode); +/**@}*/ + +/*the rest of these symbols are deprecated and should not be touched by + * doxygen */ + +typedef enum { + LIBVBUCKET_SOURCE_FILE, + LIBVBUCKET_SOURCE_MEMORY +} vbucket_source_t; + +typedef lcbvb_CONFIG *VBUCKET_CONFIG_HANDLE; +typedef lcbvb_DISTMODE VBUCKET_DISTRIBUTION_TYPE; +#define VBUCKET_DISTRIBUTION_VBUCKET LCBVB_DIST_VBUCKET +#define VBUCKET_DISTRIBUTION_KETAMA LCBVB_DIST_KETAMA +#define VBUCKET_NO_CHANGES LCBVB_NO_CHANGES +#define VBUCKET_SERVERS_MODIFIED LCBVB_SERVERS_MODIFIED +#define VBUCKET_MAP_MODIFIED LCVBVB_MAP_MODIFIED + +LIBCOUCHBASE_API int vbucket_config_parse(lcbvb_CONFIG*,vbucket_source_t,const char*); +LIBCOUCHBASE_API const char *vbucket_get_error_message(lcbvb_CONFIG*); +LIBCOUCHBASE_API lcbvb_CONFIG* vbucket_config_create(void); +LIBCOUCHBASE_API void vbucket_config_destroy(lcbvb_CONFIG*); +LIBCOUCHBASE_API int vbucket_config_get_num_replicas(lcbvb_CONFIG*); +LIBCOUCHBASE_API int vbucket_config_get_num_vbuckets(lcbvb_CONFIG*); +LIBCOUCHBASE_API int vbucket_config_get_num_servers(lcbvb_CONFIG*); +LIBCOUCHBASE_API const char *vbucket_config_get_server(lcbvb_CONFIG*,int); +LIBCOUCHBASE_API const char *vbucket_config_get_couch_api_base(lcbvb_CONFIG*,int); +LIBCOUCHBASE_API const char *vbucket_config_get_rest_api_server(lcbvb_CONFIG*,int); +LIBCOUCHBASE_API lcbvb_DISTMODE vbucket_config_get_distribution_type(lcbvb_CONFIG*); +LIBCOUCHBASE_API int vbucket_map(lcbvb_CONFIG*,const void*,lcb_SIZE,int*,int*); +LIBCOUCHBASE_API int vbucket_get_vbucket_by_key(lcbvb_CONFIG*,const void*,lcb_SIZE); +LIBCOUCHBASE_API int vbucket_get_master(lcbvb_CONFIG*,int); +LIBCOUCHBASE_API int vbucket_get_replica(lcbvb_CONFIG*,int,int); +LIBCOUCHBASE_API lcbvb_CONFIGDIFF* vbucket_compare(lcbvb_CONFIG*,lcbvb_CONFIG*); +LIBCOUCHBASE_API void vbucket_free_diff(lcbvb_CONFIGDIFF*); +LIBCOUCHBASE_API int vbucket_config_get_revision(lcbvb_CONFIG*); +LIBCOUCHBASE_API lcbvb_CHANGETYPE vbucket_what_changed(lcbvb_CONFIGDIFF *diff); +LIBCOUCHBASE_API int vbucket_config_generate(lcbvb_CONFIG *vb, unsigned, unsigned, unsigned); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/views.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/views.h new file mode 100644 index 00000000..fa53bd05 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/views.h @@ -0,0 +1,298 @@ +/* + * Copyright 2015 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +#ifndef LCB_VIEWS_API_H +#define LCB_VIEWS_API_H +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @ingroup lcb-public-api + * @defgroup lcb-view-api Views (Map-Reduce) + * @brief Higher level API which splits view results into rows + */ + +/** + * @addtogroup lcb-view-api + * @{ + */ +typedef struct lcb_RESPVIEW_st lcb_RESPVIEWQUERY; +typedef struct lcbview_REQUEST_st *lcb_VIEWHANDLE; + +/** + * Callback function invoked for each row returned from the view + * @param instance the library handle + * @param cbtype the callback type. This is set to @ref LCB_CALLBACK_VIEWQUERY + * @param row Information about the current row + * + * Note that this callback's `row->rflags` will contain the @ref LCB_RESP_F_FINAL + * flag set after all rows have been returned. Applications should check for + * the presence of this flag. If this flag is present, the row itself will + * contain the raw response metadata in its lcb_RESPVIEWQUERY::value field. + */ +typedef void (*lcb_VIEWQUERYCALLBACK)(lcb_t instance, + int cbtype, const lcb_RESPVIEWQUERY *row); + +/** Set this flag to execute an actual get with each response */ +#define LCB_CMDVIEWQUERY_F_INCLUDE_DOCS 1 << 16 + +/**Set this flag to only parse the top level row, and not its constituent + * parts. Note this is incompatible with `F_INCLUDE_DOCS`*/ +#define LCB_CMDVIEWQUERY_F_NOROWPARSE 1 << 17 + +/**This view is spatial. Modifies how the final view path will be constructed */ +#define LCB_CMDVIEWQUERY_F_SPATIAL 1 << 18 + +/** Command structure for querying a view */ +typedef struct { + /** Common command flags; e.g. @ref LCB_CMDVIEWQUERY_F_INCLUDE_DOCS */ + lcb_U32 cmdflags; + + /** The design document as a string; e.g. `"beer"` */ + const char *ddoc; + /** Length of design document name */ + size_t nddoc; + + /** The name of the view as a string; e.g. `"brewery_beers"` */ + const char *view; + /** Length of the view name */ + size_t nview; + + /**Any URL parameters to be passed to the view should be specified here. + * The library will internally insert a `?` character before the options + * (if specified), so do not place one yourself. + * + * The format of the options follows the standard for passing parameters + * via HTTP requests; thus e.g. `key1=value1&key2=value2`. This string + * is itself not parsed by the library but simply appended to the URL. */ + const char *optstr; + + /** Length of the option string */ + size_t noptstr; + + /**Some query parameters (in particular; 'keys') may be send via a POST + * request within the request body, since it might be too long for the + * URI itself. If you have such data, place it here. */ + const char *postdata; + size_t npostdata; + + /** + * The maximum number of internal get requests to issue concurrently for + * @c F_INCLUDE_DOCS. This is useful for large view responses where + * there is a potential for a large number of responses resulting in a large + * number of get requests; increasing memory usage. + * + * Setting this value will attempt to throttle the number of get requests, + * so that no more than this number of requests will be in progress at any + * given time. + */ + unsigned docs_concurrent_max; + + /**Callback to invoke for each row. If not provided, @ref LCB_EINVAL will + * be returned from lcb_view_query() */ + lcb_VIEWQUERYCALLBACK callback; + + /**If not NULL, this will be set to a handle which may be passed to + * lcb_view_cancel(). See that function for more details */ + lcb_VIEWHANDLE *handle; +} lcb_CMDVIEWQUERY; + +/**@brief Response structure representing a row. + * + * This is provided for each invocation of the + * lcb_CMDVIEWQUERY::callback invocation. The `key` and `nkey` fields here + * refer to the first argument passed to the `emit` function by the + * `map` function. + * + * This response structure may be used as-is, in case the values are simple, + * or may be relayed over to a more advanced JSON parser to decode the + * individual key and value properties. + * + * @note + * The #key and #value fields are JSON encoded. This means that if they are + * bare strings, they will be surrounded by quotes. On the other hand, the + * #docid is _not_ JSON encoded and is provided with any surrounding quotes + * stripped out (this is because the document ID is always a string). Please + * take note of this if doing any form of string comparison/processing. + * + * @note + * If the @ref LCB_CMDVIEWQUERY_F_NOROWPARSE flag has been set, the #value + * field will contain the raw row contents, rather than the constituent + * elements. + * + */ +struct lcb_RESPVIEW_st { + #ifndef __LCB_DOXYGEN__ /* Doxygen fails to substitute the base fields for some reason */ + LCB_RESP_BASE + #else + void *cookie; /**< User data associated with request */ + const void *key; /**< Emitted key */ + lcb_SIZE nkey; /**< Length of emitted key */ + lcb_cas_t cas; /**< unused */ + lcb_error_t rc; /**< Status code */ + lcb_U16 version; /**< unused */ + lcb_U16 rflags; /**< Response specific flags. see lcb_RESPFLAGS */ + #endif + + const char *docid; /**< Document ID (i.e. memcached key) associated with this row */ + size_t ndocid; /**< Length of document ID */ + + /**Emitted value. If `rflags & LCB_RESP_F_FINAL` is true then this will + * contain the _metadata_ of the view response itself. This includes the + * `total_rows` field among other things, and should be parsed as JSON */ + const char *value; + + size_t nvalue; /**< Length of emitted value */ + + /**If this is a spatial view, the GeoJSON geometry fields will be here */ + const char *geometry; + size_t ngeometry; + + /**If the request failed, this will contain the raw underlying request. + * You may inspect this request and perform some other processing on + * the underlying HTTP data. Note that this may not necessarily contain + * the entire response body; just the chunk at which processing failed.*/ + const lcb_RESPHTTP *htresp; + + /**If @ref LCB_CMDVIEWQUERY_F_INCLUDE_DOCS was specified in the request, + * this will contain the response for the _GET_ command. This is the same + * response as would be received in the `LCB_CALLBACK_GET` for + * lcb_get3(). + * + * Note that this field should be checked for various errors as well, as it + * is remotely possible the get request did not succeed. + * + * If the @ref LCB_CMDVIEWQUERY_F_INCLUDE_DOCS flag was not specified, this + * field will be `NULL`. + */ + const lcb_RESPGET *docresp; +}; + +/** + * @volatile + * + * Initiate a view query. This will execute a view (MapReduce) request against + * a view endpoint within the cluster. For each row emitted by the view + * functions (i.e. the `map`, and possibly `reduce` functions) the + * callback specified in the lcb_CMDVIEWQUERY::callback field will be invoked. + * + * Here is a functional example: + * + * @code{.c} + * static void rowCallback(lcb_t instance, int ignoreme, const lcb_RESPVIEWQUERY *response) { + * if (response->rflags & LCB_RESP_F_FINAL) { + * printf("View is done!\n"); + * if (response->nvalue) { + * printf("Raw JSON metadata: %.*s\n", (int)response->nvalue, response->value); + * } + * if (response->rc != LCB_SUCCESS) { + * printf("Query failed: %s\n", lcb_sterror(instance, response->rc)); + * } + * return; + * } + * + * // Key and value always exist: + * printf("Emitted key: %.*s\n", (int)resp->nkey, resp-key); + * printf("Emitted value: %s.*s\n", (int)resp->nvalue, resp->value); + * + * // Document IDs are only present for non-reduce queries + * if (resp->docid) { + * printf("Document ID: %.*s\n", (int)resp->docid, resp->ndocid); + * // If LCB_CMDVIEWQUERY_F_INCLUDE_DOCS was specified, this might + * // contain the actual document value + * if (resp->docresp) { + * const lcb_RESPGET *rg = resp->docresp; + * if (rg->rc == LCB_SUCCESS) { + * printf("Document contents: %.*s\n", (int)rg->value, rg->nvalue); + * printf("CAS: 0x%lx\n", rg->cas); + * } else { + * printf("Couldn't fetch document: %s\n", lcb_strerror(instance, rg->rc)); + * } + * } + * } + * } + * + * static void doViewRequest(lcb_t instance) { + * lcb_CMDVIEWQUERY cmd = { 0 }; + * vq.ddoc = "beer"; + * vq.nddoc = strlen(vq.ddoc); + * vq.view = "brewery_beers"; + * vq.nview = strlen(vq.view); + * vq.optstr = "limit=10&descending=true"; + * vq.noptstr = strlen(vq.optstr); + * vq.callback = rowCallback; + * + * // If you want to perform an implicit lcb_get3() on each row's `docid` + * if (includeDocs) { + * vq.cmdflags |= LCB_CMDVIEWQUERY_F_INCLUDE_DOCS; + * } + * + * lcb_error_t rc = lcb_view_query(instance, NULL, &cmd); + * // handle error + * lcb_wait(instance); + * } + * @endcode + * + * @param instance The library handle + * @param cookie user pointer included in every response + * @param cmd the command detailing the type of query to perform + * @return LCB_SUCCESS on scheduling success, or an error code on scheduling + * failure. + */ +LIBCOUCHBASE_API +lcb_error_t +lcb_view_query(lcb_t instance, const void *cookie, const lcb_CMDVIEWQUERY *cmd); + +/** + * @volatile + * + * Initialize a command object. This is a convenience function. + * @param vq the command to initialize + * @param design the name of the design. Required and should be NUL-terminated + * @param view the name of the view. Required and should be NUL-terminated + * @param options a string of options. Optional. If provided, should be + * NUL-terminated + * @param callback the callback to invoke. Required + */ +LIBCOUCHBASE_API +void +lcb_view_query_initcmd(lcb_CMDVIEWQUERY *vq, + const char *design, const char *view, const char *options, + lcb_VIEWQUERYCALLBACK callback); + +/** + * @volatile + * + * Cancels the ongoing request. This ensures the callback will never be invoked. + * This should be used only in situations where the lcb_t itself may be + * destroyed to avoid leaking any application allocated memory. This does not + * guarantee the view internals will not leak, however. + */ +LIBCOUCHBASE_API +void +lcb_view_cancel(lcb_t instance, lcb_VIEWHANDLE handle); + +/**@}*/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/visibility.h b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/visibility.h new file mode 100644 index 00000000..0261aafa --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/libcouchbase/visibility.h @@ -0,0 +1,65 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright (C) 2011 Couchbase, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LIBCOUCHBASE_VISIBILITY_H +#define LIBCOUCHBASE_VISIBILITY_H + +#ifdef LIBCOUCHBASE_STATIC +#define LIBCOUCHBASE_API +#define LCB_EXTERN_VAR extern +#else + +#ifdef LIBCOUCHBASE_INTERNAL + #ifdef __SUNPRO_C + #define LIBCOUCHBASE_API __global + #define LCB_CLASS_EXPORT LIBCOUCHBASE_API + #elif defined(HAVE_VISIBILITY) && HAVE_VISIBILITY + #define LIBCOUCHBASE_API __attribute__ ((visibility("default"))) + #define LCB_CLASS_EXPORT LIBCOUCHBASE_API + #elif defined(_MSC_VER) + #define LIBCOUCHBASE_API extern __declspec(dllexport) + #define LCB_CLASS_EXPORT __declspec(dllexport) + #else + #define LIBCOUCHBASE_API + #define LCB_CLASS_EXPORT + #endif /* compiler version */ + +#else /* !LIBCOUCHBASE_INTERNAL */ + #ifdef _MSC_VER + #define LIBCOUCHBASE_API extern __declspec(dllimport) + #define LCB_CLASS_EXPORT __declspec(dllimport) + #else + #define LIBCOUCHBASE_API + #define LCB_CLASS_EXPORT + #endif +#endif /* LIBCOUCHBASE_INTERNAL */ + +/* Define LCB_EXTERN_VAR only if !LIBCOUCHBASE_STATIC */ +#ifdef _MSC_VER +/* Already includes 'extern' in LIBCOUCHBASE_API def, don't use it twice! */ + #define LCB_EXTERN_VAR +#else + #define LCB_EXTERN_VAR extern +#endif /* _MSC_VER */ +#endif /* !LIBCOUCHBASE_STATIC */ + +/** + * This symbol declares internal APIs as accessible from other modules. + * It should still not be used. + */ +#define LCB_INTERNAL_API LIBCOUCHBASE_API + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/include/memcached/COPYING b/couchbase-sys/libcouchbase-2.7.0/include/memcached/COPYING new file mode 100644 index 00000000..4746b00c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/memcached/COPYING @@ -0,0 +1,30 @@ +Copyright (c) 2003, Danga Interactive, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + + * Neither the name of the Danga Interactive nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/couchbase-sys/libcouchbase-2.7.0/include/memcached/README b/couchbase-sys/libcouchbase-2.7.0/include/memcached/README new file mode 100644 index 00000000..e43d5c4c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/memcached/README @@ -0,0 +1,10 @@ +This directory contains headerfiles copied from +git://github.com/memcached/memcached.git. They are only included to +make it easier to build the software without having to download these +headers separately. Please do not change the files in this directory, but +rather copy a new set from the repository if you need to upgrade the +content. + +Cheers, + +Trond Norbye \ No newline at end of file diff --git a/couchbase-sys/libcouchbase-2.7.0/include/memcached/protocol_binary.h b/couchbase-sys/libcouchbase-2.7.0/include/memcached/protocol_binary.h new file mode 100644 index 00000000..cc368132 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/memcached/protocol_binary.h @@ -0,0 +1,1916 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright (c) <2008>, Sun Microsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY SUN MICROSYSTEMS, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Summary: Constants used by to implement the binary protocol. + * + * Copy: See Copyright for the status of this software. + * + * Author: Trond Norbye + */ + +#ifndef PROTOCOL_BINARY_H +#define PROTOCOL_BINARY_H + +#if !defined HAVE_STDINT_H && defined _WIN32 && defined(_MSC_VER) +# include "win_stdint.h" +#else +# include +#endif +#include + +/** + * \addtogroup Protocol + * @{ + */ + +/** + * This file contains definitions of the constants and packet formats + * defined in the binary specification. Please note that you _MUST_ remember + * to convert each multibyte field to / from network byte order to / from + * host order. + */ +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * Definition of the legal "magic" values used in a packet. + * See section 3.1 Magic byte + */ + typedef enum { + PROTOCOL_BINARY_REQ = 0x80, + PROTOCOL_BINARY_RES = 0x81 + } protocol_binary_magic; + + /** + * Definition of the valid response status numbers. + * + * A well written client should be "future proof" by handling new + * error codes to be defined. Note that new error codes means that + * the requested operation wasn't performed. + */ + typedef enum { + /** The operation completed successfully */ + PROTOCOL_BINARY_RESPONSE_SUCCESS = 0x00, + /** The key does not exists */ + PROTOCOL_BINARY_RESPONSE_KEY_ENOENT = 0x01, + /** The key exists in the cluster (with another CAS value) */ + PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS = 0x02, + /** The document exceeds the maximum size */ + PROTOCOL_BINARY_RESPONSE_E2BIG = 0x03, + /** Invalid request */ + PROTOCOL_BINARY_RESPONSE_EINVAL = 0x04, + /** The document was not stored for some reason. This is + * currently a "catch all" for number or error situations, and + * should be split into multiple error codes. */ + PROTOCOL_BINARY_RESPONSE_NOT_STORED = 0x05, + /** Non-numeric server-side value for incr or decr */ + PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL = 0x06, + /** The server is not responsible for the requested vbucket */ + PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET = 0x07, + /** Not connected to a bucket */ + PROTOCOL_BINARY_RESPONSE_NO_BUCKET = 0x08, + /** The authentication context is stale. You should reauthenticate*/ + PROTOCOL_BINARY_RESPONSE_AUTH_STALE = 0x1f, + /** Authentication failure (invalid user/password combination, + * OR an internal error in the authentication library. Could + * be a misconfigured SASL configuration. See server logs for + * more information.) */ + PROTOCOL_BINARY_RESPONSE_AUTH_ERROR = 0x20, + /** Authentication OK so far, please continue */ + PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE = 0x21, + /** The requested value is outside the legal range + * (similar to EINVAL, but more specific) */ + PROTOCOL_BINARY_RESPONSE_ERANGE = 0x22, + /** Roll back to an earlier version of the vbucket UUID + * (_currently_ only used by DCP for agreeing on selecting a + * starting point) */ + PROTOCOL_BINARY_RESPONSE_ROLLBACK = 0x23, + /** No access (could be opcode, value, bucket etc) */ + PROTOCOL_BINARY_RESPONSE_EACCESS = 0x24, + /** The Couchbase cluster is currently initializing this + * node, and the Cluster manager has not yet granted all + * users access to the cluster. */ + PROTOCOL_BINARY_RESPONSE_NOT_INITIALIZED = 0x25, + /** The server have no idea what this command is for */ + PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND = 0x81, + /** Not enough memory */ + PROTOCOL_BINARY_RESPONSE_ENOMEM = 0x82, + /** The server does not support this command */ + PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED = 0x83, + /** An internal error in the server */ + PROTOCOL_BINARY_RESPONSE_EINTERNAL = 0x84, + /** The system is currently too busy to handle the request. + * it is _currently_ only being used by the scrubber in + * default_engine to run a task there may only be one of + * (subsequent requests to start it would return ebusy until + * it's done). */ + PROTOCOL_BINARY_RESPONSE_EBUSY = 0x85, + /** A temporary error condition occurred. Retrying the + * operation may resolve the problem. This could be that the + * server is in a degraded situation (like running warmup on + * the node), the vbucket could be in an "incorrect" state, a + * temporary failure from the underlying persistence layer, + * etc). + */ + PROTOCOL_BINARY_RESPONSE_ETMPFAIL = 0x86, + + /* + * Sub-document specific responses. + */ + + /** The provided path does not exist in the document. */ + PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_ENOENT = 0xc0, + + /** One of path components treats a non-dictionary as a dictionary, or + * a non-array as an array. + * [Arithmetic operations only] The value the path points to is not + * a number. */ + PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_MISMATCH = 0xc1, + + /** The path’s syntax was incorrect. */ + PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_EINVAL = 0xc2, + + /** The path provided is too large; either the string is too long, + * or it contains too many components. */ + PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_E2BIG = 0xc3, + + /** The document has too many levels to parse. */ + PROTOCOL_BINARY_RESPONSE_SUBDOC_DOC_E2DEEP = 0xc4, + + /** [For mutations only] The value provided will invalidate the JSON if + * inserted. */ + PROTOCOL_BINARY_RESPONSE_SUBDOC_VALUE_CANTINSERT = 0xc5, + + /** The existing document is not valid JSON. */ + PROTOCOL_BINARY_RESPONSE_SUBDOC_DOC_NOTJSON = 0xc6, + + /** [For arithmetic ops] The existing number is out of the valid range + * for arithmetic ops (cannot be represented as an int64_t). */ + PROTOCOL_BINARY_RESPONSE_SUBDOC_NUM_ERANGE = 0xc7, + + /** [For arithmetic ops] The operation would result in a number + * outside the valid range (cannot be represented as an int64_t). */ + PROTOCOL_BINARY_RESPONSE_SUBDOC_DELTA_ERANGE = 0xc8, + + /** [For mutations only] The requested operation requires the path to + * not already exist, but it exists. */ + PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_EEXISTS = 0xc9, + + /** [For mutations only] Inserting the value would cause the document + * to be too deep. */ + PROTOCOL_BINARY_RESPONSE_SUBDOC_VALUE_ETOODEEP = 0xca, + + /** [For multi-path commands only] An invalid combination of commands + * was specified. */ + PROTOCOL_BINARY_RESPONSE_SUBDOC_INVALID_COMBO = 0xcb, + + /** [For multi-path commands only] Specified key was successfully + * found, but one or more path operations failed. Examine the individual + * lookup_result (MULTI_LOOKUP) / mutation_result (MULTI_MUTATION) + * structures for details. */ + PROTOCOL_BINARY_RESPONSE_SUBDOC_MULTI_PATH_FAILURE = 0xcc + + } protocol_binary_response_status; + + /** + * Defintion of the different command opcodes. + * See section 3.3 Command Opcodes + */ + typedef enum { + PROTOCOL_BINARY_CMD_GET = 0x00, + PROTOCOL_BINARY_CMD_SET = 0x01, + PROTOCOL_BINARY_CMD_ADD = 0x02, + PROTOCOL_BINARY_CMD_REPLACE = 0x03, + PROTOCOL_BINARY_CMD_DELETE = 0x04, + PROTOCOL_BINARY_CMD_INCREMENT = 0x05, + PROTOCOL_BINARY_CMD_DECREMENT = 0x06, + PROTOCOL_BINARY_CMD_QUIT = 0x07, + PROTOCOL_BINARY_CMD_FLUSH = 0x08, + PROTOCOL_BINARY_CMD_GETQ = 0x09, + PROTOCOL_BINARY_CMD_NOOP = 0x0a, + PROTOCOL_BINARY_CMD_VERSION = 0x0b, + PROTOCOL_BINARY_CMD_GETK = 0x0c, + PROTOCOL_BINARY_CMD_GETKQ = 0x0d, + PROTOCOL_BINARY_CMD_APPEND = 0x0e, + PROTOCOL_BINARY_CMD_PREPEND = 0x0f, + PROTOCOL_BINARY_CMD_STAT = 0x10, + PROTOCOL_BINARY_CMD_SETQ = 0x11, + PROTOCOL_BINARY_CMD_ADDQ = 0x12, + PROTOCOL_BINARY_CMD_REPLACEQ = 0x13, + PROTOCOL_BINARY_CMD_DELETEQ = 0x14, + PROTOCOL_BINARY_CMD_INCREMENTQ = 0x15, + PROTOCOL_BINARY_CMD_DECREMENTQ = 0x16, + PROTOCOL_BINARY_CMD_QUITQ = 0x17, + PROTOCOL_BINARY_CMD_FLUSHQ = 0x18, + PROTOCOL_BINARY_CMD_APPENDQ = 0x19, + PROTOCOL_BINARY_CMD_PREPENDQ = 0x1a, + PROTOCOL_BINARY_CMD_VERBOSITY = 0x1b, + PROTOCOL_BINARY_CMD_TOUCH = 0x1c, + PROTOCOL_BINARY_CMD_GAT = 0x1d, + PROTOCOL_BINARY_CMD_GATQ = 0x1e, + PROTOCOL_BINARY_CMD_HELLO = 0x1f, + + PROTOCOL_BINARY_CMD_SASL_LIST_MECHS = 0x20, + PROTOCOL_BINARY_CMD_SASL_AUTH = 0x21, + PROTOCOL_BINARY_CMD_SASL_STEP = 0x22, + + /* Control */ + PROTOCOL_BINARY_CMD_IOCTL_GET = 0x23, + PROTOCOL_BINARY_CMD_IOCTL_SET = 0x24, + + /* Config */ + PROTOCOL_BINARY_CMD_CONFIG_VALIDATE = 0x25, + PROTOCOL_BINARY_CMD_CONFIG_RELOAD = 0x26, + + /* Audit */ + PROTOCOL_BINARY_CMD_AUDIT_PUT = 0x27, + PROTOCOL_BINARY_CMD_AUDIT_CONFIG_RELOAD = 0x28, + + /* Shutdown the server */ + PROTOCOL_BINARY_CMD_SHUTDOWN = 0x29, + + /* These commands are used for range operations and exist within + * this header for use in other projects. Range operations are + * not expected to be implemented in the memcached server itself. + */ + PROTOCOL_BINARY_CMD_RGET = 0x30, + PROTOCOL_BINARY_CMD_RSET = 0x31, + PROTOCOL_BINARY_CMD_RSETQ = 0x32, + PROTOCOL_BINARY_CMD_RAPPEND = 0x33, + PROTOCOL_BINARY_CMD_RAPPENDQ = 0x34, + PROTOCOL_BINARY_CMD_RPREPEND = 0x35, + PROTOCOL_BINARY_CMD_RPREPENDQ = 0x36, + PROTOCOL_BINARY_CMD_RDELETE = 0x37, + PROTOCOL_BINARY_CMD_RDELETEQ = 0x38, + PROTOCOL_BINARY_CMD_RINCR = 0x39, + PROTOCOL_BINARY_CMD_RINCRQ = 0x3a, + PROTOCOL_BINARY_CMD_RDECR = 0x3b, + PROTOCOL_BINARY_CMD_RDECRQ = 0x3c, + /* End Range operations */ + + /* VBucket commands */ + PROTOCOL_BINARY_CMD_SET_VBUCKET = 0x3d, + PROTOCOL_BINARY_CMD_GET_VBUCKET = 0x3e, + PROTOCOL_BINARY_CMD_DEL_VBUCKET = 0x3f, + /* End VBucket commands */ + + /* TAP commands */ + PROTOCOL_BINARY_CMD_TAP_CONNECT = 0x40, + PROTOCOL_BINARY_CMD_TAP_MUTATION = 0x41, + PROTOCOL_BINARY_CMD_TAP_DELETE = 0x42, + PROTOCOL_BINARY_CMD_TAP_FLUSH = 0x43, + PROTOCOL_BINARY_CMD_TAP_OPAQUE = 0x44, + PROTOCOL_BINARY_CMD_TAP_VBUCKET_SET = 0x45, + PROTOCOL_BINARY_CMD_TAP_CHECKPOINT_START = 0x46, + PROTOCOL_BINARY_CMD_TAP_CHECKPOINT_END = 0x47, + /* End TAP */ + + /* Vbucket command to get the VBUCKET sequence numbers for all + * vbuckets on the node */ + PROTOCOL_BINARY_CMD_GET_ALL_VB_SEQNOS = 0x48, + + /* DCP */ + PROTOCOL_BINARY_CMD_DCP_OPEN = 0x50, + PROTOCOL_BINARY_CMD_DCP_ADD_STREAM = 0x51, + PROTOCOL_BINARY_CMD_DCP_CLOSE_STREAM = 0x52, + PROTOCOL_BINARY_CMD_DCP_STREAM_REQ = 0x53, + PROTOCOL_BINARY_CMD_DCP_GET_FAILOVER_LOG = 0x54, + PROTOCOL_BINARY_CMD_DCP_STREAM_END = 0x55, + PROTOCOL_BINARY_CMD_DCP_SNAPSHOT_MARKER = 0x56, + PROTOCOL_BINARY_CMD_DCP_MUTATION = 0x57, + PROTOCOL_BINARY_CMD_DCP_DELETION = 0x58, + PROTOCOL_BINARY_CMD_DCP_EXPIRATION = 0x59, + PROTOCOL_BINARY_CMD_DCP_FLUSH = 0x5a, + PROTOCOL_BINARY_CMD_DCP_SET_VBUCKET_STATE = 0x5b, + PROTOCOL_BINARY_CMD_DCP_NOOP = 0x5c, + PROTOCOL_BINARY_CMD_DCP_BUFFER_ACKNOWLEDGEMENT = 0x5d, + PROTOCOL_BINARY_CMD_DCP_CONTROL = 0x5e, + PROTOCOL_BINARY_CMD_DCP_RESERVED4 = 0x5f, + /* End DCP */ + + PROTOCOL_BINARY_CMD_STOP_PERSISTENCE = 0x80, + PROTOCOL_BINARY_CMD_START_PERSISTENCE = 0x81, + PROTOCOL_BINARY_CMD_SET_PARAM = 0x82, + PROTOCOL_BINARY_CMD_GET_REPLICA = 0x83, + + /* Bucket engine */ + PROTOCOL_BINARY_CMD_CREATE_BUCKET = 0x85, + PROTOCOL_BINARY_CMD_DELETE_BUCKET = 0x86, + PROTOCOL_BINARY_CMD_LIST_BUCKETS = 0x87, + PROTOCOL_BINARY_CMD_SELECT_BUCKET= 0x89, + + PROTOCOL_BINARY_CMD_ASSUME_ROLE = 0x8a, + + PROTOCOL_BINARY_CMD_OBSERVE_SEQNO = 0x91, + PROTOCOL_BINARY_CMD_OBSERVE = 0x92, + + PROTOCOL_BINARY_CMD_EVICT_KEY = 0x93, + PROTOCOL_BINARY_CMD_GET_LOCKED = 0x94, + PROTOCOL_BINARY_CMD_UNLOCK_KEY = 0x95, + + /** + * Return the last closed checkpoint Id for a given VBucket. + */ + PROTOCOL_BINARY_CMD_LAST_CLOSED_CHECKPOINT = 0x97, + /** + * Close the TAP connection for the registered TAP client and + * remove the checkpoint cursors from its registered vbuckets. + */ + PROTOCOL_BINARY_CMD_DEREGISTER_TAP_CLIENT = 0x9e, + + /** + * Reset the replication chain from the node that receives + * this command. For example, given the replication chain, + * A->B->C, if A receives this command, it will reset all the + * replica vbuckets on B and C, which are replicated from A. + */ + PROTOCOL_BINARY_CMD_RESET_REPLICATION_CHAIN = 0x9f, + + /** + * CMD_GET_META is used to retrieve the meta section for an item. + */ + PROTOCOL_BINARY_CMD_GET_META = 0xa0, + PROTOCOL_BINARY_CMD_GETQ_META = 0xa1, + PROTOCOL_BINARY_CMD_SET_WITH_META = 0xa2, + PROTOCOL_BINARY_CMD_SETQ_WITH_META = 0xa3, + PROTOCOL_BINARY_CMD_ADD_WITH_META = 0xa4, + PROTOCOL_BINARY_CMD_ADDQ_WITH_META = 0xa5, + PROTOCOL_BINARY_CMD_SNAPSHOT_VB_STATES = 0xa6, + PROTOCOL_BINARY_CMD_VBUCKET_BATCH_COUNT = 0xa7, + PROTOCOL_BINARY_CMD_DEL_WITH_META = 0xa8, + PROTOCOL_BINARY_CMD_DELQ_WITH_META = 0xa9, + + /** + * Command to create a new checkpoint on a given vbucket by force + */ + PROTOCOL_BINARY_CMD_CREATE_CHECKPOINT = 0xaa, + PROTOCOL_BINARY_CMD_NOTIFY_VBUCKET_UPDATE = 0xac, + /** + * Command to enable data traffic after completion of warm + */ + PROTOCOL_BINARY_CMD_ENABLE_TRAFFIC = 0xad, + /** + * Command to disable data traffic temporarily + */ + PROTOCOL_BINARY_CMD_DISABLE_TRAFFIC = 0xae, + /** + * Command to change the vbucket filter for a given TAP producer. + */ + PROTOCOL_BINARY_CMD_CHANGE_VB_FILTER = 0xb0, + /** + * Command to wait for the checkpoint persistence + */ + PROTOCOL_BINARY_CMD_CHECKPOINT_PERSISTENCE = 0xb1, + /** + * Command that returns meta data for typical memcached ops + */ + PROTOCOL_BINARY_CMD_RETURN_META = 0xb2, + /** + * Command to trigger compaction of a vbucket + */ + PROTOCOL_BINARY_CMD_COMPACT_DB = 0xb3, + /** + * Command to set cluster configuration + */ + PROTOCOL_BINARY_CMD_SET_CLUSTER_CONFIG = 0xb4, + /** + * Command that returns cluster configuration + */ + PROTOCOL_BINARY_CMD_GET_CLUSTER_CONFIG = 0xb5, + PROTOCOL_BINARY_CMD_GET_RANDOM_KEY = 0xb6, + /** + * Command to wait for the dcp sequence number persistence + */ + PROTOCOL_BINARY_CMD_SEQNO_PERSISTENCE = 0xb7, + + /** + * Commands for GO-XDCR + */ + PROTOCOL_BINARY_CMD_SET_DRIFT_COUNTER_STATE = 0xc1, + PROTOCOL_BINARY_CMD_GET_ADJUSTED_TIME = 0xc2, + + /** + * Commands for the Sub-document API. + */ + + /* Retrieval commands */ + PROTOCOL_BINARY_CMD_SUBDOC_GET = 0xc5, + PROTOCOL_BINARY_CMD_SUBDOC_EXISTS = 0xc6, + + /* Dictionary commands */ + PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD = 0xc7, + PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT = 0xc8, + + /* Generic modification commands */ + PROTOCOL_BINARY_CMD_SUBDOC_DELETE = 0xc9, + PROTOCOL_BINARY_CMD_SUBDOC_REPLACE = 0xca, + + /* Array commands */ + PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST = 0xcb, + PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST = 0xcc, + PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_INSERT = 0xcd, + PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_ADD_UNIQUE = 0xce, + + /* Arithmetic commands */ + PROTOCOL_BINARY_CMD_SUBDOC_COUNTER = 0xcf, + + /* Multi-Path commands */ + PROTOCOL_BINARY_CMD_SUBDOC_MULTI_LOOKUP = 0xd0, + PROTOCOL_BINARY_CMD_SUBDOC_MULTI_MUTATION = 0xd1, + + /* Subdoc additions for Spock: */ + PROTOCOL_BINARY_CMD_SUBDOC_GET_COUNT = 0xd2, + + /* Scrub the data */ + PROTOCOL_BINARY_CMD_SCRUB = 0xf0, + /* Refresh the ISASL data */ + PROTOCOL_BINARY_CMD_ISASL_REFRESH = 0xf1, + /* Refresh the SSL certificates */ + PROTOCOL_BINARY_CMD_SSL_CERTS_REFRESH = 0xf2, + /* Internal timer ioctl */ + PROTOCOL_BINARY_CMD_GET_CMD_TIMER = 0xf3, + /* ns_server - memcached session validation */ + PROTOCOL_BINARY_CMD_SET_CTRL_TOKEN = 0xf4, + PROTOCOL_BINARY_CMD_GET_CTRL_TOKEN = 0xf5, + + /* ns_server - memcached internal communication */ + PROTOCOL_BINARY_CMD_INIT_COMPLETE = 0xf6, + + /* Reserved for being able to signal invalid opcode */ + PROTOCOL_BINARY_CMD_INVALID = 0xff + } protocol_binary_command; + + /** + * Definition of the data types in the packet + * See section 3.4 Data Types + */ + typedef enum { + PROTOCOL_BINARY_RAW_BYTES = 0x00, + PROTOCOL_BINARY_DATATYPE_JSON = 0x01, + /* Compressed == snappy compression */ + PROTOCOL_BINARY_DATATYPE_COMPRESSED = 0x02, + /* Compressed == snappy compression */ + PROTOCOL_BINARY_DATATYPE_COMPRESSED_JSON = 0x03 + } protocol_binary_datatypes; + + /** + * Definitions for extended (flexible) metadata + * + * @1: Flex Code to identify the number of extended metadata fields + * @2: Size of the Flex Code, set to 1 byte + * @3: Current size of extended metadata + */ + typedef enum { + FLEX_META_CODE = 0x01, + FLEX_DATA_OFFSET = 1, + EXT_META_LEN = 1 + } protocol_binary_flexmeta; + + /** + * Definitions of sub-document flags. + */ + typedef enum { + /* No flags set */ + SUBDOC_FLAG_NONE = 0x0, + + /* (Mutation) Should non-existent intermediate paths be created? */ + SUBDOC_FLAG_MKDIR_P = 0x01, + + /* (Mutation) Create the document if it does not exist. Implies + * SUBDOC_FLAG_MKDIR_P. + */ + SUBDOC_FLAG_MKDOC = 0x02 + } protocol_binary_subdoc_flag; + + /** + * Definition of the header structure for a request packet. + * See section 2 + */ + typedef union { + struct { + uint8_t magic; + uint8_t opcode; + uint16_t keylen; + uint8_t extlen; + uint8_t datatype; + uint16_t vbucket; + uint32_t bodylen; + uint32_t opaque; + uint64_t cas; + } request; + uint8_t bytes[24]; + } protocol_binary_request_header; + + /** + * Definition of the header structure for a response packet. + * See section 2 + */ + typedef union { + struct { + uint8_t magic; + uint8_t opcode; + uint16_t keylen; + uint8_t extlen; + uint8_t datatype; + uint16_t status; + uint32_t bodylen; + uint32_t opaque; + uint64_t cas; + } response; + uint8_t bytes[24]; + } protocol_binary_response_header; + + /** + * Definition of a request-packet containing no extras + */ + typedef union { + struct { + protocol_binary_request_header header; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header)]; + } protocol_binary_request_no_extras; + + /** + * Definition of a response-packet containing no extras + */ + typedef union { + struct { + protocol_binary_response_header header; + } message; + uint8_t bytes[sizeof(protocol_binary_response_header)]; + } protocol_binary_response_no_extras; + + /** + * Definition of the packet used by the get, getq, getk and getkq command. + * See section 4 + */ + typedef protocol_binary_request_no_extras protocol_binary_request_get; + typedef protocol_binary_request_no_extras protocol_binary_request_getq; + typedef protocol_binary_request_no_extras protocol_binary_request_getk; + typedef protocol_binary_request_no_extras protocol_binary_request_getkq; + + /** + * Definition of the packet returned from a successful get, getq, getk and + * getkq. + * See section 4 + */ + typedef union { + struct { + protocol_binary_response_header header; + struct { + uint32_t flags; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_response_header) + 4]; + } protocol_binary_response_get; + + typedef protocol_binary_response_get protocol_binary_response_getq; + typedef protocol_binary_response_get protocol_binary_response_getk; + typedef protocol_binary_response_get protocol_binary_response_getkq; + + /** + * Definition of the packet used by the delete command + * See section 4 + */ + typedef protocol_binary_request_no_extras protocol_binary_request_delete; + + /** + * Definition of the packet returned by the delete command + * See section 4 + * + * extlen should be either zero, or 16 if the client has enabled the + * MUTATION_SEQNO feature, with the following format: + * + * Header: (0-23): + * Extras: + * Vbucket UUID (24-31): 0x0000000000003039 + * Seqno (32-39): 0x000000000000002D + */ + typedef protocol_binary_response_no_extras protocol_binary_response_delete; + + /** + * Definition of the packet used by the flush command + * See section 4 + * Please note that the expiration field is optional, so remember to see + * check the header.bodysize to see if it is present. + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + /* + * Specifying a non-null expiration time is no longer + * supported + */ + uint32_t expiration; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; + } protocol_binary_request_flush; + + /** + * Definition of the packet returned by the flush command + * See section 4 + */ + typedef protocol_binary_response_no_extras protocol_binary_response_flush; + + /** + * Definition of the packet used by set, add and replace + * See section 4 + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint32_t flags; + uint32_t expiration; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 8]; + } protocol_binary_request_set; + typedef protocol_binary_request_set protocol_binary_request_add; + typedef protocol_binary_request_set protocol_binary_request_replace; + + /** + * Definition of the packet returned by set, add and replace + * See section 4 + */ + typedef protocol_binary_response_no_extras protocol_binary_response_set; + typedef protocol_binary_response_no_extras protocol_binary_response_add; + typedef protocol_binary_response_no_extras protocol_binary_response_replace; + + /** + * Definition of the noop packet + * See section 4 + */ + typedef protocol_binary_request_no_extras protocol_binary_request_noop; + + /** + * Definition of the packet returned by the noop command + * See section 4 + */ + typedef protocol_binary_response_no_extras protocol_binary_response_noop; + + /** + * Definition of the structure used by the increment and decrement + * command. + * See section 4 + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint64_t delta; + uint64_t initial; + uint32_t expiration; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 20]; + } protocol_binary_request_incr; + typedef protocol_binary_request_incr protocol_binary_request_decr; + + /** + * Definition of the response from an incr or decr command + * command. + * + * The result of the incr/decr is a uint64_t placed at header + extlen. + * + * extlen should be either zero, or 16 if the client has enabled the + * MUTATION_SEQNO feature, with the following format: + * + * Header: (0-23): + * Extras: + * Vbucket UUID (24-31): 0x0000000000003039 + * Seqno (32-39): 0x000000000000002D + * Value: (40-47): .... + * + */ + typedef protocol_binary_response_no_extras protocol_binary_response_incr; + typedef protocol_binary_response_no_extras protocol_binary_response_decr; + + /** + * Definition of the quit + * See section 4 + */ + typedef protocol_binary_request_no_extras protocol_binary_request_quit; + + /** + * Definition of the packet returned by the quit command + * See section 4 + */ + typedef protocol_binary_response_no_extras protocol_binary_response_quit; + + /** + * Definition of the packet used by append and prepend command + * See section 4 + */ + typedef protocol_binary_request_no_extras protocol_binary_request_append; + typedef protocol_binary_request_no_extras protocol_binary_request_prepend; + + /** + * Definition of the packet returned from a successful append or prepend + * See section 4 + */ + typedef protocol_binary_response_no_extras protocol_binary_response_append; + typedef protocol_binary_response_no_extras protocol_binary_response_prepend; + + /** + * Definition of the packet used by the version command + * See section 4 + */ + typedef protocol_binary_request_no_extras protocol_binary_request_version; + + /** + * Definition of the packet returned from a successful version command + * See section 4 + */ + typedef protocol_binary_response_no_extras protocol_binary_response_version; + + + /** + * Definition of the packet used by the stats command. + * See section 4 + */ + typedef protocol_binary_request_no_extras protocol_binary_request_stats; + + /** + * Definition of the packet returned from a successful stats command + * See section 4 + */ + typedef protocol_binary_response_no_extras protocol_binary_response_stats; + + /** + * Definition of the packet used by the verbosity command + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint32_t level; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; + } protocol_binary_request_verbosity; + + /** + * Definition of the packet returned from the verbosity command + */ + typedef protocol_binary_response_no_extras protocol_binary_response_verbosity; + + /** + * Definition of the packet used by the touch command. + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint32_t expiration; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; + } protocol_binary_request_touch; + + /** + * Definition of the packet returned from the touch command + */ + typedef protocol_binary_response_no_extras protocol_binary_response_touch; + + /** + * Definition of the packet used by the GAT(Q) command. + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint32_t expiration; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; + } protocol_binary_request_gat; + + typedef protocol_binary_request_gat protocol_binary_request_gatq; + + /** + * Definition of the packet returned from the GAT(Q) + */ + typedef protocol_binary_response_get protocol_binary_response_gat; + typedef protocol_binary_response_get protocol_binary_response_gatq; + + /** + * Definition of the packet used by SUBDOCUMENT single-path commands. + * + * The path, which is always required, is in the Body, after the Key. + * + * Header: 24 @0: + * Extras: + * Sub-document flags 1 @24: + * Sub-document pathlen 2 @25: + * Body: + * Key keylen @27: + * Path pathlen @27+keylen: + * Value to insert/replace + * vallen-keylen-pathlen @27+keylen+pathlen: [variable] + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint16_t pathlen; // Length in bytes of the sub-doc path. + uint8_t subdoc_flags; // See protocol_binary_subdoc_flag + } extras; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 3]; + } protocol_binary_request_subdocument; + + + /** Definition of the packet used by SUBDOCUMENT responses. + */ + typedef union { + struct { + protocol_binary_response_header header; + } message; + uint8_t bytes[sizeof(protocol_binary_response_header)]; + } protocol_binary_response_subdocument; + + /** + * Definition of the request packets used by SUBDOCUMENT multi-path commands. + * + * Multi-path sub-document commands differ from single-path in that they + * encode a series of multiple paths to operate on (from a single key). + * There are two multi-path commands - MULTI_LOOKUP and MULTI_MUTATION. + * - MULTI_LOOKUP consists of variable number of subdoc lookup commands + * (SUBDOC_GET or SUBDOC_EXISTS). + * - MULTI_MUTATION consists of a variable number of subdoc mutation + * commands (i.e. all subdoc commands apart from + * SUBDOC_{GET,EXISTS}). + * + * Each path to be operated on is specified by an Operation Spec, which are + * contained in the body. This defines the opcode, path, and value + * (for mutations). + * + * A maximum of MULTI_MAX_PATHS paths (operations) can be encoded in a + * single multi-path command. + * + * SUBDOC_MULTI_LOOKUP: + * Header: 24 @0: + * Extras: 0 @24: no extras + * Body: @24: + * Key keylen @24: + * 1..MULTI_MAX_PATHS [Lookup Operation Spec] + * + * Lookup Operation Spec: + * 1 @0 : Opcode + * 1 @1 : Flags + * 2 @2 : Path Length + * pathlen @4 : Path + */ + static const int PROTOCOL_BINARY_SUBDOC_MULTI_MAX_PATHS = 16; + + typedef struct { + uint8_t opcode; + uint8_t flags; + uint16_t pathlen; + /* uint8_t path[pathlen] */ + } protocol_binary_subdoc_multi_lookup_spec; + + typedef protocol_binary_request_no_extras protocol_binary_request_subdocument_multi_lookup; + + /* + * + * SUBDOC_MULTI_MUTATION + * Header: 24 @0: + * Extras: 0 @24: + * Body: variable @24: + * Key keylen @24: + * 1..MULTI_MAX_PATHS [Mutation Operation Spec] + * + * Mutation Operation Spec: + * 1 @0 : Opcode + * 1 @1 : Flags + * 2 @2 : Path Length + * 4 @4 : Value Length + * pathlen @8 : Path + * vallen @8+pathlen : Value + */ + typedef struct { + uint8_t opcode; + uint8_t flags; + uint16_t pathlen; + uint32_t valuelen; + /* uint8_t path[pathlen] */ + /* uint8_t value[valuelen] */ + } protocol_binary_subdoc_multi_mutation_spec; + + typedef protocol_binary_request_no_extras protocol_binary_request_subdocument_multi_mutation; + + /** + * Definition of the response packets used by SUBDOCUMENT multi-path + * commands. + * + * SUBDOC_MULTI_LOOKUP - Body consists of a series of lookup_result structs, + * one per lookup_spec in the request. + * + * Lookup Result: + * 2 @0 : status + * 4 @2 : resultlen + * resultlen @6 : result + */ + typedef struct { + protocol_binary_request_header header; + /* Variable-length 1..PROTOCOL_BINARY_SUBDOC_MULTI_MAX_PATHS */ + protocol_binary_subdoc_multi_lookup_spec body[1]; + } protocol_binary_response_subdoc_multi_lookup; + + /** + * SUBDOC_MULTI_MUTATION - Body is either empty (if all mutations + * successful), or contains the sub-code and + * index of the first failed mutation spec.. + * Mutation Result (failure): + * 2 @0 : Status code of first spec which failed. + * 1 @2 : 0-based index of the first spec which failed. + */ + typedef union { + struct { + protocol_binary_response_header header; + } message; + uint8_t bytes[sizeof(protocol_binary_response_header)]; + } protocol_binary_response_subdoc_multi_mutation; + + + /** + * Definition of a request for a range operation. + * See http://code.google.com/p/memcached/wiki/RangeOps + * + * These types are used for range operations and exist within + * this header for use in other projects. Range operations are + * not expected to be implemented in the memcached server itself. + */ + typedef union { + struct { + protocol_binary_response_header header; + struct { + uint16_t size; + uint8_t reserved; + uint8_t flags; + uint32_t max_results; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; + } protocol_binary_request_rangeop; + + typedef protocol_binary_request_rangeop protocol_binary_request_rget; + typedef protocol_binary_request_rangeop protocol_binary_request_rset; + typedef protocol_binary_request_rangeop protocol_binary_request_rsetq; + typedef protocol_binary_request_rangeop protocol_binary_request_rappend; + typedef protocol_binary_request_rangeop protocol_binary_request_rappendq; + typedef protocol_binary_request_rangeop protocol_binary_request_rprepend; + typedef protocol_binary_request_rangeop protocol_binary_request_rprependq; + typedef protocol_binary_request_rangeop protocol_binary_request_rdelete; + typedef protocol_binary_request_rangeop protocol_binary_request_rdeleteq; + typedef protocol_binary_request_rangeop protocol_binary_request_rincr; + typedef protocol_binary_request_rangeop protocol_binary_request_rincrq; + typedef protocol_binary_request_rangeop protocol_binary_request_rdecr; + typedef protocol_binary_request_rangeop protocol_binary_request_rdecrq; + + + /** + * Definition of tap commands + * See To be written + * + */ + + typedef union { + struct { + protocol_binary_request_header header; + struct { + /** + * flags is a bitmask used to set properties for the + * the connection. Please In order to be forward compatible + * you should set all undefined bits to 0. + * + * If the bit require extra userdata, it will be stored + * in the user-data field of the body (passed to the engine + * as enginespeciffic). That means that when you parse the + * flags and the engine-specific data, you have to work your + * way from bit 0 and upwards to find the correct offset for + * the data. + * + */ + uint32_t flags; + + /** + * Backfill age + * + * By using this flag you can limit the amount of data being + * transmitted. If you don't specify a backfill age, the + * server will transmit everything it contains. + * + * The first 8 bytes in the engine specific data contains + * the oldest entry (from epoc) you're interested in. + * Specifying a time in the future (for the server you are + * connecting to), will cause it to start streaming current + * changes. + */ +#define TAP_CONNECT_FLAG_BACKFILL 0x01 + /** + * Dump will cause the server to send the data stored on the + * server, but disconnect when the keys stored in the server + * are transmitted. + */ +#define TAP_CONNECT_FLAG_DUMP 0x02 + /** + * The body contains a list of 16 bits words in network byte + * order specifying the vbucket ids to monitor. The first 16 + * bit word contains the number of buckets. The number of 0 + * means "all buckets" + */ +#define TAP_CONNECT_FLAG_LIST_VBUCKETS 0x04 + /** + * The responsibility of the vbuckets is to be transferred + * over to the caller when all items are transferred. + */ +#define TAP_CONNECT_FLAG_TAKEOVER_VBUCKETS 0x08 + /** + * The tap consumer supports ack'ing of tap messages + */ +#define TAP_CONNECT_SUPPORT_ACK 0x10 + /** + * The tap consumer would prefer to just get the keys + * back. If the engine supports this it will set + * the TAP_FLAG_NO_VALUE flag in each of the + * tap packets returned. + */ +#define TAP_CONNECT_REQUEST_KEYS_ONLY 0x20 + /** + * The body contains a list of (vbucket_id, last_checkpoint_id) + * pairs. This provides the checkpoint support in TAP streams. + * The last checkpoint id represents the last checkpoint that + * was successfully persisted. + */ +#define TAP_CONNECT_CHECKPOINT 0x40 + /** + * The tap consumer is a registered tap client, which means that + * the tap server will maintain its checkpoint cursor permanently. + */ +#define TAP_CONNECT_REGISTERED_CLIENT 0x80 + + /** + * The initial TAP implementation convert flags to/from network + * byte order, but the values isn't stored in host local order + * causing them to change if you mix platforms.. + */ +#define TAP_CONNECT_TAP_FIX_FLAG_BYTEORDER 0x100 + + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; + } protocol_binary_request_tap_connect; + + typedef union { + struct { + protocol_binary_request_header header; + struct { + struct { + uint16_t enginespecific_length; + /* + * The flag section support the following flags + */ + /** + * Request that the consumer send a response packet + * for this packet. The opaque field must be preserved + * in the response. + */ +#define TAP_FLAG_ACK 0x01 + /** + * The value for the key is not included in the packet + */ +#define TAP_FLAG_NO_VALUE 0x02 + /** + * The flags are in network byte order + */ +#define TAP_FLAG_NETWORK_BYTE_ORDER 0x04 + + uint16_t flags; + uint8_t ttl; + uint8_t res1; + uint8_t res2; + uint8_t res3; + } tap; + struct { + uint32_t flags; + uint32_t expiration; + } item; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 16]; + } protocol_binary_request_tap_mutation; + + typedef union { + struct { + protocol_binary_request_header header; + struct { + struct { + uint16_t enginespecific_length; + /** + * See the definition of the flags for + * protocol_binary_request_tap_mutation for a description + * of the available flags. + */ + uint16_t flags; + uint8_t ttl; + uint8_t res1; + uint8_t res2; + uint8_t res3; + } tap; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 8]; + } protocol_binary_request_tap_no_extras; + + typedef protocol_binary_request_tap_no_extras protocol_binary_request_tap_delete; + typedef protocol_binary_request_tap_no_extras protocol_binary_request_tap_flush; + +/** + * TAP OPAQUE command list + */ +#define TAP_OPAQUE_ENABLE_AUTO_NACK 0 +#define TAP_OPAQUE_INITIAL_VBUCKET_STREAM 1 +#define TAP_OPAQUE_ENABLE_CHECKPOINT_SYNC 2 +#define TAP_OPAQUE_OPEN_CHECKPOINT 3 +#define TAP_OPAQUE_COMPLETE_VB_FILTER_CHANGE 4 +#define TAP_OPAQUE_CLOSE_TAP_STREAM 7 +#define TAP_OPAQUE_CLOSE_BACKFILL 8 + + typedef protocol_binary_request_tap_no_extras protocol_binary_request_tap_opaque; + typedef protocol_binary_request_tap_no_extras protocol_binary_request_tap_vbucket_set; + + + /** + * Definition of the packet used by the scrub. + */ + typedef protocol_binary_request_no_extras protocol_binary_request_scrub; + + /** + * Definition of the packet returned from scrub. + */ + typedef protocol_binary_response_no_extras protocol_binary_response_scrub; + + + /** + * Definition of the packet used by set vbucket + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + vbucket_state_t state; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + sizeof(vbucket_state_t)]; + } protocol_binary_request_set_vbucket; + /** + * Definition of the packet returned from set vbucket + */ + typedef protocol_binary_response_no_extras protocol_binary_response_set_vbucket; + /** + * Definition of the packet used by del vbucket + */ + typedef protocol_binary_request_no_extras protocol_binary_request_del_vbucket; + /** + * Definition of the packet returned from del vbucket + */ + typedef protocol_binary_response_no_extras protocol_binary_response_del_vbucket; + + /** + * Definition of the packet used by get vbucket + */ + typedef protocol_binary_request_no_extras protocol_binary_request_get_vbucket; + + /** + * Definition of the packet returned from get vbucket + */ + typedef union { + struct { + protocol_binary_response_header header; + struct { + vbucket_state_t state; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_response_header) + sizeof(vbucket_state_t)]; + } protocol_binary_response_get_vbucket; + + /** + * Definition of hello's features. + */ + typedef enum { + PROTOCOL_BINARY_FEATURE_DATATYPE = 0x01, + PROTOCOL_BINARY_FEATURE_TLS = 0x2, + PROTOCOL_BINARY_FEATURE_TCPNODELAY = 0x03, + PROTOCOL_BINARY_FEATURE_MUTATION_SEQNO = 0x04, + PROTOCOL_BINARY_FEATURE_TCPDELAY = 0x05 + } protocol_binary_hello_features; + + #define MEMCACHED_FIRST_HELLO_FEATURE 0x01 + #define MEMCACHED_TOTAL_HELLO_FEATURES 0x05 + +#define protocol_feature_2_text(a) \ + (a == PROTOCOL_BINARY_FEATURE_DATATYPE) ? "Datatype" : \ + (a == PROTOCOL_BINARY_FEATURE_TLS) ? "TLS" : \ + (a == PROTOCOL_BINARY_FEATURE_TCPNODELAY) ? "TCP NODELAY" : \ + (a == PROTOCOL_BINARY_FEATURE_MUTATION_SEQNO) ? "Mutation seqno" : \ + (a == PROTOCOL_BINARY_FEATURE_TCPDELAY) ? "TCP DELAY" : "Unknown" + + /** + * The HELLO command is used by the client and the server to agree + * upon the set of features the other end supports. It is initiated + * by the client by sending its agent string and the list of features + * it would like to use. The server will then reply with the list + * of the requested features it supports. + * + * ex: + * Client -> HELLO [myclient 2.0] datatype, tls + * Server -> HELLO SUCCESS datatype + * + * In this example the server responds that it allows the client to + * use the datatype extension, but not the tls extension. + */ + + + /** + * Definition of the packet requested by hello cmd. + * Key: This is a client-specific identifier (not really used by + * the server, except for logging the HELLO and may therefore + * be used to identify the client at a later time) + * Body: Contains all features supported by client. Each feature is + * specified as an uint16_t in network byte order. + */ + typedef protocol_binary_request_no_extras protocol_binary_request_hello; + + + /** + * Definition of the packet returned by hello cmd. + * Body: Contains all features requested by the client that the + * server agrees to ssupport. Each feature is + * specified as an uint16_t in network byte order. + */ + typedef protocol_binary_response_no_extras protocol_binary_response_hello; + + /** + * The SET_CTRL_TOKEN command will be used by ns_server and ns_server alone + * to set the session cas token in memcached which will be used to + * recognize the particular instance on ns_server. The previous token will + * be passed in the cas section of the request header for the CAS operation, + * and the new token will be part of ext (8B). + * + * The response to this request will include the cas as it were set, + * and a SUCCESS as status, or a KEY_EEXISTS with the existing token in + * memcached if the CAS operation were to fail. + */ + + /** + * Definition of the request packet for SET_CTRL_TOKEN. + * Body: new session_cas_token of uint64_t type. + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint64_t new_cas; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 8]; + } protocol_binary_request_set_ctrl_token; + + /** + * Definition of the response packet for SET_CTRL_TOKEN + */ + typedef protocol_binary_response_no_extras protocol_binary_response_set_ctrl_token; + + /** + * The GET_CTRL_TOKEN command will be used by ns_server to fetch the current + * session cas token held in memcached. + * + * The response to this request will include the token currently held in + * memcached in the cas field of the header. + */ + + /** + * Definition of the request packet for GET_CTRL_TOKEN. + */ + typedef protocol_binary_request_no_extras protocol_binary_request_get_ctrl_token; + + + /** + * Definition of the response packet for GET_CTRL_TOKEN + */ + typedef protocol_binary_response_no_extras protocol_binary_response_get_ctrl_token; + + /* DCP related stuff */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint32_t seqno; + /* + * The following flags are defined + */ +#define DCP_OPEN_PRODUCER 1 +#define DCP_OPEN_NOTIFIER 2 + uint32_t flags; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 8]; + } protocol_binary_request_dcp_open; + + typedef protocol_binary_response_no_extras protocol_binary_response_dcp_open; + + typedef union { + struct { + protocol_binary_request_header header; + struct { + /* + * The following flags are defined + */ +#define DCP_ADD_STREAM_FLAG_TAKEOVER 1 +#define DCP_ADD_STREAM_FLAG_DISKONLY 2 +#define DCP_ADD_STREAM_FLAG_LATEST 4 + uint32_t flags; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; + } protocol_binary_request_dcp_add_stream; + + typedef union { + struct { + protocol_binary_response_header header; + struct { + uint32_t opaque; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_response_header) + 4]; + } protocol_binary_response_dcp_add_stream; + + typedef protocol_binary_request_no_extras protocol_binary_request_dcp_close_stream; + typedef protocol_binary_response_no_extras protocol_binary_response_dcp_close_stream; + + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint32_t flags; + uint32_t reserved; + uint64_t start_seqno; + uint64_t end_seqno; + uint64_t vbucket_uuid; + uint64_t snap_start_seqno; + uint64_t snap_end_seqno; + } body; + /* Group ID is specified in the key */ + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 48]; + } protocol_binary_request_dcp_stream_req; + + typedef union { + struct { + protocol_binary_response_header header; + } message; + /* + ** In case of PROTOCOL_BINARY_RESPONSE_ROLLBACK the body contains + ** the rollback sequence number (uint64_t) + */ + uint8_t bytes[sizeof(protocol_binary_request_header)]; + } protocol_binary_response_dcp_stream_req; + + typedef protocol_binary_request_no_extras protocol_binary_request_dcp_get_failover_log; + + /* The body of the message contains UUID/SEQNO pairs */ + typedef protocol_binary_response_no_extras protocol_binary_response_dcp_get_failover_log; + + typedef union { + struct { + protocol_binary_request_header header; + struct { + /** + * All flags set to 0 == OK, + * 1: state changed + */ + uint32_t flags; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; + } protocol_binary_request_dcp_stream_end; + typedef protocol_binary_response_no_extras protocol_binary_response_dcp_stream_end; + + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint64_t start_seqno; + uint64_t end_seqno; + uint32_t flags; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 20]; + } protocol_binary_request_dcp_snapshot_marker; + + typedef protocol_binary_response_no_extras protocol_binary_response_dcp_snapshot_marker; + + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint64_t by_seqno; + uint64_t rev_seqno; + uint32_t flags; + uint32_t expiration; + uint32_t lock_time; + uint16_t nmeta; + uint8_t nru; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 31]; + } protocol_binary_request_dcp_mutation; + + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint64_t by_seqno; + uint64_t rev_seqno; + uint16_t nmeta; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 18]; + } protocol_binary_request_dcp_deletion; + + typedef protocol_binary_request_dcp_deletion protocol_binary_request_dcp_expiration; + typedef protocol_binary_request_no_extras protocol_binary_request_dcp_flush; + + typedef union { + struct { + protocol_binary_request_header header; + struct { + /** + * 0x01 - Active + * 0x02 - Replica + * 0x03 - Pending + * 0x04 - Dead + */ + uint8_t state; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 1]; + } protocol_binary_request_dcp_set_vbucket_state; + typedef protocol_binary_response_no_extras protocol_binary_response_dcp_set_vbucket_state; + + typedef protocol_binary_request_no_extras protocol_binary_request_dcp_noop; + typedef protocol_binary_response_no_extras protocol_binary_response_dcp_noop; + + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint32_t buffer_bytes; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; + } protocol_binary_request_dcp_buffer_acknowledgement; + typedef protocol_binary_response_no_extras protocol_binary_response_dcp_buffer_acknowledgement; + + typedef protocol_binary_request_no_extras protocol_binary_request_dcp_control; + typedef protocol_binary_response_no_extras protocol_binary_response_dcp_control; + + + /** + * IOCTL_GET command message to get/set control parameters. + */ + typedef protocol_binary_request_no_extras protocol_binary_request_ioctl_get; + typedef protocol_binary_request_no_extras protocol_binary_request_ioctl_set; + + typedef protocol_binary_request_no_extras protocol_binary_request_config_validate; + typedef protocol_binary_request_no_extras protocol_binary_request_config_reload; + + typedef protocol_binary_request_no_extras protocol_binary_request_ssl_refresh; + typedef protocol_binary_response_no_extras protocol_binary_response_ssl_refresh; + + /** + * Request command timings for a bucket from memcached. Privileged + * connections may specify the name of the bucket in the "key" field, + * or the aggregated timings for the entire server by using the + * special name /all/. + * + * The returned payload is a json document of the following format: + * { "us" : [ x, x, x, x, ... ], + * "ms" : [ y, y, y, ...], + * "500ms" : [ z, z, z, ...], + * "wayout" : nnn + * } + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint8_t opcode; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 1]; + } protocol_binary_request_get_cmd_timer; + + typedef protocol_binary_response_no_extras protocol_binary_response_get_cmd_timer; + + typedef protocol_binary_request_no_extras protocol_binary_request_create_bucket; + typedef protocol_binary_request_no_extras protocol_binary_request_delete_bucket; + typedef protocol_binary_request_no_extras protocol_binary_request_list_buckets; + typedef protocol_binary_request_no_extras protocol_binary_request_select_bucket; + typedef protocol_binary_request_no_extras protocol_binary_request_assume_role; + + /* + * Parameter types of CMD_SET_PARAM command. + */ + typedef enum { + protocol_binary_engine_param_flush = 1, /* flusher-related param type */ + protocol_binary_engine_param_tap, /* tap-related param type */ + protocol_binary_engine_param_checkpoint /* checkpoint-related param type */ + } protocol_binary_engine_param_t; + + /** + * CMD_SET_PARAM command message to set engine parameters. + * flush, tap, and checkpoint parameter types are currently supported. + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + protocol_binary_engine_param_t param_type; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + sizeof(protocol_binary_engine_param_t)]; + } protocol_binary_request_set_param; + + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint32_t size; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; + } protocol_binary_request_set_batch_count; + + + /** + * This flag is used by the setWithMeta/addWithMeta/deleteWithMeta packets + * to specify that the conflict resolution mechanism should be skipped for + * this operation. + */ +#define SKIP_CONFLICT_RESOLUTION_FLAG 0x01 + +#define SET_RET_META 1 +#define ADD_RET_META 2 +#define DEL_RET_META 3 + +/** + * This flag is used with the get meta response packet. If set it + * specifies that the item recieved has been deleted, but that the + * items meta data is still contained in ep-engine. Eg. the item + * has been soft deleted. + */ +#define GET_META_ITEM_DELETED_FLAG 0x01 + + + /** + * The physical layout for the CMD_SET_WITH_META looks like the the normal + * set request with the addition of a bulk of extra meta data stored + * at the end of the package. + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint32_t flags; + uint32_t expiration; + uint64_t seqno; + uint64_t cas; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 24]; + } protocol_binary_request_set_with_meta; + + /** + * The message format for delete with meta + */ + typedef protocol_binary_request_set_with_meta protocol_binary_request_delete_with_meta; + + /** + * The message format for getLocked engine API + */ + typedef protocol_binary_request_gat protocol_binary_request_getl; + + /** + * The physical layout for a CMD_GET_META command returns the meta-data + * section for an item: + */ + typedef protocol_binary_request_no_extras protocol_binary_request_get_meta; + + /** + * The response for CMD_SET_WITH_META does not carry any user-data and the + * status of the operation is signalled in the status bits. + */ + typedef protocol_binary_response_no_extras protocol_binary_response_set_with_meta; + + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint64_t file_version; + uint64_t header_offset; + uint32_t vbucket_state_updated; + uint32_t state; + uint64_t checkpoint; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 32]; + } protocol_binary_request_notify_vbucket_update; + typedef protocol_binary_response_no_extras protocol_binary_response_notify_vbucket_update; + + /** + * The physical layout for the CMD_RETURN_META + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint32_t mutation_type; + uint32_t flags; + uint32_t expiration; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 12]; + } protocol_binary_request_return_meta; + + + /** + * Message format for CMD_INIT_COMPLETE + */ + typedef protocol_binary_request_no_extras protocol_binary_request_init_complete; + typedef protocol_binary_response_no_extras protocol_binary_response_init_complete; + + /** + * Message format for CMD_SET_CONFIG + */ + typedef protocol_binary_request_no_extras protocol_binary_request_set_cluster_config; + + /** + * Message format for CMD_GET_CONFIG + */ + typedef protocol_binary_request_no_extras protocol_binary_request_get_cluster_config; + + /** + * Message format for CMD_GET_ADJUSTED_TIME + * + * The PROTOCOL_BINARY_CMD_GET_ADJUSTED_TIME command will be + * used by XDCR to retrieve the vbucket's latest adjusted_time + * which is calculated based on the driftCounter if timeSync + * has been enabled. + * + * Request:- + * + * Header: Contains a vbucket id. + * + * Response:- + * + * The response will contain the adjusted_time (type: int64_t) + * as part of the body if in case of a SUCCESS, or else a NOTSUP + * in case of timeSync not being enabled. + * + * The request packet's header will contain the vbucket_id. + */ + typedef protocol_binary_request_no_extras protocol_binary_request_get_adjusted_time; + + /** + * Message format for CMD_SET_DRIFT_COUNTER_STATE + * + * The PROTOCOL_BINARY_CMD_SET_DRIFT_COUNTER_STATE command will be + * used by GO-XDCR to set the initial drift counter and enable/disable + * the time synchronization for the vbucket. + * + * Request:- + * + * Header: Contains a vbucket id. + * Extras: Contains the initial drift value which is of type int64_t and + * the time sync state (0x00 for disable, 0x01 for enable), + * + * Response:- + * + * The response will return a SUCCESS after saving the settings and + * a NOT_MY_VBUCKET (along with cluster config) if the vbucket isn't + * found. + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + int64_t initial_drift; + uint8_t time_sync; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 9]; + } protocol_binary_request_set_drift_counter_state; + + /** + * The physical layout for the CMD_COMPACT_DB + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint64_t purge_before_ts; + uint64_t purge_before_seq; + uint8_t drop_deletes; + uint8_t align_pad1; + uint16_t align_pad2; + uint32_t align_pad3; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 24]; + } protocol_binary_request_compact_db; + + typedef protocol_binary_request_get protocol_binary_request_get_random; + +#define OBS_STATE_NOT_PERSISTED 0x00 +#define OBS_STATE_PERSISTED 0x01 +#define OBS_STATE_NOT_FOUND 0x80 +#define OBS_STATE_LOGICAL_DEL 0x81 + + /** + * The physical layout for the PROTOCOL_BINARY_CMD_AUDIT_PUT + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint32_t id; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; + } protocol_binary_request_audit_put; + + typedef protocol_binary_response_no_extras protocol_binary_response_audit_put; + + /** + * The shutdown message is sent from ns_server to memcached to tell + * memcached to initiate a clean shutdown. This is a privileged + * command and carries no payload, but the CAS field needs to be + * set to the current session token (see GET/SET_CTRL_TOKEN) + */ + typedef protocol_binary_request_no_extras protocol_binary_request_shutdown; + typedef protocol_binary_response_no_extras protocol_binary_response_shutdown; + + + /** + * The PROTOCOL_BINARY_CMD_OBSERVE_SEQNO command is used by the + * client to retrieve information about the vbucket in order to + * find out if a particular mutation has been persisted or + * replicated at the server side. In order to do so, the client + * would pass the vbucket uuid of the vbucket that it wishes to + * observe to the serve. The response would contain the last + * persisted sequence number and the latest sequence number in the + * vbucket. For example, if a client sends a request to observe + * the vbucket 0 with uuid 12345 and if the response contains the + * values <58, 65> and then the client can infer that sequence + * number 56 has been persisted, 60 has only been replicated and + * not been persisted yet and 68 has not been replicated yet. + */ + + /** + * Definition of the request packet for the observe_seqno command. + * + * Header: Contains the vbucket id of the vbucket that the client + * wants to observe. + * + * Body: Contains the vbucket uuid of the vbucket that the client + * wants to observe. The vbucket uuid is of type uint64_t. + * + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + uint64_t uuid; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + 8]; + } protocol_binary_request_observe_seqno; + + /** + * Definition of the response packet for the observe_seqno command. + * Body: Contains a tuple of the form + * + * + * - format_type is of type uint8_t and it describes whether + * the vbucket has failed over or not. 1 indicates a hard + * failover, 0 indicates otherwise. + * - vbucket id is of type uint16_t and it is the identifier for + * the vbucket. + * - vbucket uuid is of type uint64_t and it represents a UUID for + * the vbucket. + * - last_persisted_seqno is of type uint64_t and it is the + * last sequence number that was persisted for this + * vbucket. + * - current_seqno is of the type uint64_t and it is the + * sequence number of the latest mutation in the vbucket. + * + * In the case of a hard failover, the tuple is of the form + * + * + * - old vbucket uuid is of type uint64_t and it is the + * vbucket UUID of the vbucket prior to the hard failover. + * + * - last_received_seqno is of type uint64_t and it is the + * last received sequence number in the old vbucket uuid. + * + * The other fields are the same as that mentioned in the normal case. + */ + typedef protocol_binary_response_no_extras protocol_binary_response_observe_seqno; + + /** + * Definition of the request packet for the command + * PROTOCOL_BINARY_CMD_GET_ALL_VB_SEQNOS + * + * Header: Only opcode field is used. + * + * Body: Contains the vbucket state for which the vb sequence numbers are + * requested. + * Please note that this field is optional, header.request.extlen is + * checked to see if it is present. If not present, it implies request + * is for all vbucket states. + */ + typedef union { + struct { + protocol_binary_request_header header; + struct { + vbucket_state_t state; + } body; + } message; + uint8_t bytes[sizeof(protocol_binary_request_header) + + sizeof(vbucket_state_t)]; + } protocol_binary_request_get_all_vb_seqnos; + + /** + * Definition of the payload in the PROTOCOL_BINARY_CMD_GET_ALL_VB_SEQNOS + * response. + * + * The body contains a "list" of "vbucket id - seqno pairs" for all + * active and replica buckets on the node in network byte order. + * + * + * Byte/ 0 | 1 | 2 | 3 | + * / | | | | + * |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + * +---------------+---------------+---------------+---------------+ + * 0| VBID | VBID | SEQNO | SEQNO | + * +---------------+---------------+---------------+---------------+ + * 4| SEQNO | SEQNO | SEQNO | SEQNO | + * +---------------+---------------+---------------+---------------+ + * 4| SEQNO | SEQNO | + * +---------------+---------------+ + */ + typedef protocol_binary_response_no_extras protocol_binary_response_get_all_vb_seqnos; + + + + /** + * @} + */ +#ifdef __cplusplus +} +#endif +#endif /* PROTOCOL_BINARY_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/include/memcached/vbucket.h b/couchbase-sys/libcouchbase-2.7.0/include/memcached/vbucket.h new file mode 100644 index 00000000..051a8bae --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/include/memcached/vbucket.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MEMCACHED_VBUCKET_H +#define MEMCACHED_VBUCKET_H 1 + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef enum { + vbucket_state_active = 1, /**< Actively servicing a vbucket. */ + vbucket_state_replica, /**< Servicing a vbucket as a replica only. */ + vbucket_state_pending, /**< Pending active. */ + vbucket_state_dead /**< Not in use, pending deletion. */ +} vbucket_state_t; + +#define is_valid_vbucket_state_t(state) \ + (state == vbucket_state_active || \ + state == vbucket_state_replica || \ + state == vbucket_state_pending || \ + state == vbucket_state_dead) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/README b/couchbase-sys/libcouchbase-2.7.0/packaging/README new file mode 100644 index 00000000..fd931836 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/README @@ -0,0 +1,7 @@ +These files contain the underlying metadata templates for building deb and +rpm packages. To build a local package, do `make dist-deb` or `make dist-rpm` +from within the top level directory. + +To build public signed packages, inspect the +[lcbpackage](https://github.com/couchbaselabs/lcbpackage) for the necessary +tooling diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/abicheck/.gitignore b/couchbase-sys/libcouchbase-2.7.0/packaging/abicheck/.gitignore new file mode 100644 index 00000000..ccc01202 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/abicheck/.gitignore @@ -0,0 +1,4 @@ +NEW.xml +OLD.xml +compat_reports/* +logs/* diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/abicheck/Makefile b/couchbase-sys/libcouchbase-2.7.0/packaging/abicheck/Makefile new file mode 100644 index 00000000..593a76ad --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/abicheck/Makefile @@ -0,0 +1,17 @@ +all: report + +OLDVER := 2.3.2 +NEWVER ?= $(shell git describe) +CURDIR := $(shell pwd) +NEWROOT ?= $(CURDIR)/../../inst +OLDROOT ?= $(CURDIR)/../../../libcouchbase-$(OLDVER)/inst + +report: + # Generate the oldone + sed 's#@VERSION@#$(OLDVER)#g;s#@INST@#$(OLDROOT)#g' < template.xml > OLD.xml + sed 's#@VERSION@#$(NEWVER)#g;s#@INST@#$(NEWROOT)#g' < template.xml > NEW.xml + abi-compliance-checker -lib couchbase -old OLD.xml -new NEW.xml + +clean: + rm -f NEW.xml OLD.xml + rm -fr logs compat_reports diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/abicheck/README.md b/couchbase-sys/libcouchbase-2.7.0/packaging/abicheck/README.md new file mode 100644 index 00000000..f5795fa9 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/abicheck/README.md @@ -0,0 +1,27 @@ +# Checking ABI compliance + +This contains some utilities for running `abi-compliance-checker` (which +you need to install). It will generate an HTML file in the `compat_reports` +directory which compares the ABI between different libcouchbase versions + +## Running + +Ensure you have the `abi-compliance-checker` package installed + +Run `make`, defining these `make` variables: + +* `OLDVER` - Name of the old version (for display purposes) +* `NEWVER` - Name of the new version (for display purposes) +* `OLDROOT` - Installation root of the old version, e.g. `/usr/local/libcouchbase-2.4.0` +* `NEWROOT` - Installation root of the new version, e.g. `/usr/local/libcouchbase-master` + +e.g. + +``` +make \ + OLDVER=2.3.0 NEWVER=2.4.3 \ + OLDROOT=/sources/libcouchbase-2.3.0/inst NEWROOT=/sources/lcb-master/inst +``` + +Then open the generated HTML file in the `compat_reports` directory with your favorite +browser. diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/abicheck/template.xml b/couchbase-sys/libcouchbase-2.7.0/packaging/abicheck/template.xml new file mode 100644 index 00000000..65980d7b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/abicheck/template.xml @@ -0,0 +1,3 @@ +@VERSION@ +@INST@/include/libcouchbase/couchbase.h +@INST@/lib/libcouchbase.so diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/deb/compat b/couchbase-sys/libcouchbase-2.7.0/packaging/deb/compat new file mode 100644 index 00000000..7f8f011e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/deb/compat @@ -0,0 +1 @@ +7 diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/deb/control b/couchbase-sys/libcouchbase-2.7.0/packaging/deb/control new file mode 100644 index 00000000..148fe3da --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/deb/control @@ -0,0 +1,73 @@ +Source: libcouchbase +Priority: optional +Maintainer: Couchbase SDK Team +Build-Depends: debhelper (>= 7.0.50~), cmake (>= 2.8.9), libevent-dev (>= 1.4), libev-dev (>= 3) +Standards-Version: 3.9.2 +Section: libs +Homepage: http://www.couchbase.com/develop/c/current +Vcs-Browser: https://github.com/couchbase/libcouchbase +Vcs-Git: git://github.com/couchbase/libcouchbase.git + +Package: libcouchbase2-libevent +Architecture: any +Depends: libevent-1.4-2 (>= 1.4) | libevent-2.0-5 (>= 2.0), libcouchbase2-core (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends} +Multi-Arch: same +Description: library for the Couchbase protocol (libevent backend) + libcouchbase is a library implementing Couchbase protocol. + . + This package provides libevent backend for libcouchbase + +Package: libcouchbase2-libev +Architecture: any +Depends: libev3 (>= 3) | libev4 (>= 4), libcouchbase2-core (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends} +Multi-Arch: same +Description: library for the Couchbase protocol (libev backend) + libcouchbase is a library implementing Couchbase protocol. + . + This package provides libev backend for libcouchbase + +Package: libcouchbase-dbg +Section: debug +Architecture: any +Priority: extra +Depends: libcouchbase2-core (= ${binary:Version}), ${misc:Depends} +Multi-Arch: same +Description: library for the Couchbase protocol, debug symbols + libcouchbase is a library implementing Couchbase protocol. + . + This package provides debugging symbols. + +Package: libcouchbase2-core +Architecture: any +Provides: libcouchbase2 +Depends: ${shlibs:Depends}, ${misc:Depends} +Multi-Arch: same +Description: library for the Couchbase protocol, core files + libcouchbase is a library implementing Couchbase protocol. + . + This package provides the core for libcouchbase. It contains an IO implementation + based on select(2). If preferred, you can install one of the available + backends (libcouchbase2-libevent or libcouchbase2-libev). libcouchbase will + automatically use the installed backend. It is also possible to integrate another + IO backend or write your own. + +Package: libcouchbase-dev +Section: libdevel +Architecture: any +Multi-Arch: same +Depends: libcouchbase2-core (= ${binary:Version}), ${misc:Depends} +Description: library for the Couchbase protocol, development files + libcouchbase is a library implementing Couchbase protocol. + . + This package provides the files needed for development. + +Package: libcouchbase2-bin +Section: utils +Architecture: any +Depends: libcouchbase2-core (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends} +Conflicts: libcouchbase1-bin +Description: library for the Couchbase protocol + libcouchbase is a library implementing Couchbase protocol. + . + This package provides the cbc tools built on libcouchbase library. + diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/deb/copyright b/couchbase-sys/libcouchbase-2.7.0/packaging/deb/copyright new file mode 100644 index 00000000..b137137a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/deb/copyright @@ -0,0 +1,10 @@ +Format-Specification: http://dep.debian.net/deps/dep5/ +Name: libcouchbase +Maintainer: Couchbase SDK Team +Source: https://github.com/couchbase/libcouchbase +Debianized-Date: Mon, 05 Dec 2011 13:34:18 +0300 + +Files: * +Copyright: 2011, Couchbase +License: Apache-2.0 + /usr/share/common-licenses/Apache-2.0 diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/deb/libcouchbase-dev.docs b/couchbase-sys/libcouchbase-2.7.0/packaging/deb/libcouchbase-dev.docs new file mode 100644 index 00000000..29db3138 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/deb/libcouchbase-dev.docs @@ -0,0 +1,3 @@ +LICENSE +README.markdown +RELEASE_NOTES.markdown diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/deb/package.mk b/couchbase-sys/libcouchbase-2.7.0/packaging/deb/package.mk new file mode 100644 index 00000000..efbe098e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/deb/package.mk @@ -0,0 +1,31 @@ +.PHONY: dist-deb + +# Defined by CMake. For autotools, these will just be the normal build tree +BUILDROOT ?= $(shell pwd) +SRCROOT ?= $(shell pwd) + +# Variables derived +GITPARSE := $(SRCROOT)/packaging/parse-git-describe.pl +DEB_WORKSPACE := $(BUILDROOT)/build-deb +DEB_DIR := $(DEB_WORKSPACE)/$(PACKAGE)-$(VERSION) +DEB_VERSION := $(shell $(GITPARSE) --deb --input $(REVDESCRIBE)) +TAR_VERSION := $(shell $(GITPARSE) --tar --input $(REVDESCRIBE)) + +dist-deb: dist + cd $(BUILDROOT) + rm -rf $(DEB_WORKSPACE) + mkdir -p $(DEB_DIR) + cp -r $(SRCROOT)/packaging/deb $(DEB_DIR)/debian + cp $(BUILDROOT)/$(PACKAGE)-$(TAR_VERSION).tar.gz $(DEB_WORKSPACE)/$(PACKAGE)_$(DEB_VERSION).orig.tar.gz + (cd $(DEB_WORKSPACE); tar zxvf $(PACKAGE)_$(DEB_VERSION).orig.tar.gz) + (\ + cd $(DEB_DIR); \ + dch \ + --package=libcouchbase \ + --create \ + --newversion="$(DEB_VERSION)" \ + "Release package for $(DEB_VERSION)" && \ + dpkg-buildpackage -rfakeroot ${DEB_FLAGS}\ + ) + mv $(DEB_WORKSPACE)/*.{changes,deb,dsc,tar.gz} `pwd` + rm -rf $(DEB_WORKSPACE) diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/deb/rules b/couchbase-sys/libcouchbase-2.7.0/packaging/deb/rules new file mode 100755 index 00000000..21fbdb3e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/deb/rules @@ -0,0 +1,46 @@ +#!/usr/bin/make -f + +DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) + +DH_AC_FLAGS=--buildsystem=cmake -- -DLCB_NO_MOCK=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo +DH_AT_CMD = dh_auto_build --verbose --parallel -- alltests +ifdef DEB_HOST_MULTIARCH + DH_AC_FLAGS += -DCMAKE_INSTALL_LIBDIR=lib/$(DEB_HOST_MULTIARCH) +endif + +%: + dh $@ --verbose --parallel + +override_dh_strip: + dh_strip -plibcouchbase2-bin -plibcouchbase2-core -plibcouchbase2-libevent -plibcouchbase2-libev --dbg-package=libcouchbase-dbg + dh_strip --remaining-packages + +override_dh_auto_configure: + # Generate the install files + echo "usr/lib/$(DEB_HOST_MULTIARCH)/libcouchbase.so.*" \ + > debian/libcouchbase2-core.install + echo "usr/lib/$(DEB_HOST_MULTIARCH)/libcouchbase_libevent.so" \ + > debian/libcouchbase2-libevent.install + echo "usr/lib/$(DEB_HOST_MULTIARCH)/libcouchbase_libev.so" \ + > debian/libcouchbase2-libev.install + echo "usr/lib/$(DEB_HOST_MULTIARCH)/libcouchbase.so" \ + > debian/libcouchbase-dev.install + echo "usr/include/*" \ + >> debian/libcouchbase-dev.install + echo "usr/lib/$(DEB_HOST_MULTIARCH)/pkgconfig/libcouchbase.pc" \ + >> debian/libcouchbase-dev.install + # Generate 'cbc' files + echo "usr/bin/cbc*" \ + > debian/libcouchbase2-bin.install + echo "usr/share/man/man1/cbc*.1*" \ + >> debian/libcouchbase2-bin.install + echo "usr/share/man/man4/cbcrc*.4*" \ + >> debian/libcouchbase2-bin.install + dh_auto_configure $(DH_AC_FLAGS) + +override_dh_auto_test: + $(DH_AT_CMD) + dh_auto_test $@ + +override_dh_installchangelogs: + dh_installchangelogs -plibcouchbase2-core --keep RELEASE_NOTES.markdown diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/deb/source/format b/couchbase-sys/libcouchbase-2.7.0/packaging/deb/source/format new file mode 100644 index 00000000..163aaf8d --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/deb/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/distinfo/README b/couchbase-sys/libcouchbase-2.7.0/packaging/distinfo/README new file mode 100644 index 00000000..8e4c39d0 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/distinfo/README @@ -0,0 +1 @@ +This contains generated version info for non-SCM builds. diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/distinfo/distinfo.cmake.in b/couchbase-sys/libcouchbase-2.7.0/packaging/distinfo/distinfo.cmake.in new file mode 100644 index 00000000..33063e1c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/distinfo/distinfo.cmake.in @@ -0,0 +1,4 @@ +SET(LCB_REVDESCRIBE @LCB_REVDESCRIBE@) +SET(LCB_VERSION_CHANGESET @LCB_VERSION_CHANGESET@) +SET(LCB_VERSION @LCB_VERSION@) +SET(LCB_VERSION_HEX @LCB_VERSION_HEX@) diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/dllversion.rc.in b/couchbase-sys/libcouchbase-2.7.0/packaging/dllversion.rc.in new file mode 100644 index 00000000..b49dbfcb --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/dllversion.rc.in @@ -0,0 +1,39 @@ +#include + +#define LCB_HEXVER @LCB_VERSION_HEX@ +#define LCB_MAJOR LCB_HEXVER & 0xFF0000 +#define LCB_MINOR LCB_HEXVER & 0x00FF00 +#define LCB_PATCH LCB_HEXVER & 0x0000FF + +#define VER_FILEVERSION LCB_MAJOR,LCB_MINOR,LCB_PATCH,0 +#define VER_PRODUCT VER_FILEVERSION +#define VER_FILEVERSION_STR "@LCB_VERSION@\0" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCT +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "Couchbase, Inc.\0" + VALUE "FileDescription", "Platform/Client driver for Couchbase\0" + + VALUE "LegalCopyright", "Copyright (C) 2014 Couchbase, Inc. " + "You may use this file in accordance with the " + "Apache License 2.0\0" + VALUE "ProductName", "libcouchbase\0" + VALUE "ProductVersion", "@LCB_VERSION@\0" + VALUE "InternalName", "LCB\0" + VALUE "Revision", "@LCB_VERSION_CHANGESET@\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/libcouchbase.pc.in b/couchbase-sys/libcouchbase-2.7.0/packaging/libcouchbase.pc.in new file mode 100644 index 00000000..94820a65 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/libcouchbase.pc.in @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix}/@CMAKE_INSTALL_BINDIR@ +includedir=${prefix}/include +libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ + +Name: libcouchbase @LCB_VERSION@ +Description: Couchbase client library +Version: @LCB_VERSION@ +Libs: -L${libdir} -lcouchbase +Cflags: -I${includedir} diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/nuget/libcouchbase.autopkg b/couchbase-sys/libcouchbase-2.7.0/packaging/nuget/libcouchbase.autopkg new file mode 100644 index 00000000..7ee8bbcb --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/nuget/libcouchbase.autopkg @@ -0,0 +1,76 @@ +configurations +{ + // This node contains custom pivot information. + Toolset + { + key : "PlatformToolset"; // this is CoApp pre-defined key + choices: { v140 }; + }; +}; +nuget { + nuspec { + id = libcouchbase.v140.windesktop.static.rt-dyn; + version : 2.5.8.1; + title : "Couchbase C SDK Library v140"; + authors : "Couchbase"; + owners : "Couchbase"; + licenseUrl : "https://github.com/couchbase/libcouchbase/blob/master/LICENSE"; + projectUrl: "https://github.com/couchbase/libcouchbase"; + iconUrl: "http://www.couchbase.com/images/couchbase_favicon_0.ico"; + requireLicenseAcceptance: false; + summary: @"Couchbase C SDK vc140 libraries"; + + description: @"The Couchbase C SDK provides a fast, callback-based API for interacting with your Couchbase cluster. +It operates as a single threaded asynchronous I/O library that can be optionally integrated with an existing asynchronous application. +It can also run standalone within a traditional synchronous or threaded application as well. +The SDK is cross-platform and can be used on Linux, Mac OS X, or Windows."; + + releaseNotes: "http://developer.couchbase.com/documentation/server/current/sdks/c-2.4/release-notes.html"; + + copyright: "Copyright 2016 COUCHBASE All rights reserved."; + + tags: { native, c, cpp, couchbase }; + }; + + files { + #defines { + SDK_ROOT = ..\..\; + }; + nestedInclude: { + #destination : ${d_include}libcouchbase; + "${SDK_ROOT}include\libcouchbase\**\*.h*"; + }; + nestedInclude += { + #destination : ${d_include}libcouchbase; + "${SDK_ROOT}build_v140_x64\generated\libcouchbase\*.h" + }; + + [x64,v140,release] { + lib: { ${SDK_ROOT}build_v140_x64\lib\RelWithDebInfo\libcouchbase.lib }; + bin: { ${SDK_ROOT}build_v140_x64\bin\RelWithDebInfo\libcouchbase.dll }; + symbols: { ${SDK_ROOT}build_v140_x64\bin\RelWithDebInfo\libcouchbase.pdb }; + }; + + [x64,v140,debug] { + lib: { ${SDK_ROOT}build_v140_x64\lib\Debug\libcouchbase_d.lib }; + bin: { ${SDK_ROOT}build_v140_x64\bin\Debug\libcouchbase_d.dll }; + symbols: { ${SDK_ROOT}build_v140_x64\bin\Debug\libcouchbase_d.pdb }; + }; + + [win32,v140,release] { + lib: { ${SDK_ROOT}build_v140_x86\lib\RelWithDebInfo\libcouchbase.lib }; + bin: { ${SDK_ROOT}build_v140_x86\bin\RelWithDebInfo\libcouchbase.dll }; + symbols: { ${SDK_ROOT}build_v140_x86\bin\RelWithDebInfo\libcouchbase.pdb }; + }; + + [win32,v140,debug] { + lib: { ${SDK_ROOT}build_v140_x86\lib\Debug\libcouchbase_d.lib }; + bin: { ${SDK_ROOT}build_v140_x86\bin\Debug\libcouchbase_d.dll }; + symbols: { ${SDK_ROOT}build_v140_x86\bin\Debug\libcouchbase_d.pdb }; + }; + }; + + targets { + Defines += HAS_LIBCOUCHBASE; + }; +} \ No newline at end of file diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/parse-git-describe.pl b/couchbase-sys/libcouchbase-2.7.0/packaging/parse-git-describe.pl new file mode 100755 index 00000000..8083f094 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/parse-git-describe.pl @@ -0,0 +1,166 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Getopt::Long; +use Time::gmtime; + +my $rv = GetOptions( + 'input|I=s' => \my $Input, + 'tag|basetag|t' => \my $PrintBase, + 'version|v' => \my $PrintVersion, + 'pre-release|P' => \my $PrintPTag, + 'ncommits|n' => \my $PrintCount, + 'sha|s' => \my $PrintSHA, + 'force|f' => \my $Force, + 'rpm-full' => \my $RPM_FULL, + 'rpm-ver' => \my $RPM_VER, + 'rpm-rel' => \my $RPM_REL, + 'deb' => \my $DEB, + 'tar' => \my $TAR, + 'help|h' => \my $WantHelp); + +my $HELPTEXT = +< +License: Apache-2 +Group: System Environment/Libraries +%if %{?fedora}0 >= 180 || %{?rhel}0 >= 60 +BuildRequires: cmake >= 2.8.9 +%else +BuildRequires: cmake28 +%endif +BuildRequires: libevent-devel >= 1.4, libev-devel >= 3, openssl-devel +URL: https://github.com/couchbase/libcouchbase +Source: @TARREDAS@.tar.gz +BuildRoot: %{_topdir}/build/@TARREDAS@ + +%description +This is the client and protocol library for Couchbase project. + +%package -n %{name}2-libevent +Group: System Environment/Libraries +Summary: Couchbase Client & Protocol Library (libevent backend) +Requires: %{name}2-core = %{version}-%{release}, libevent >= 1.4 +%description -n %{name}2-libevent +This package provides libevent backend for libcouchbase + +%package -n %{name}2-libev +Group: System Environment/Libraries +Summary: Couchbase Client & Protocol Library (libev backend) +Requires: %{name}2-core = %{version}-%{release}, libev >= 3 +%description -n %{name}2-libev +This package provides libev backend for libcouchbase + +%package -n %{name}2-core +Group: System Environment/Libraries +Summary: Couchbase Client & Protocol Library (core) +Provides: %{name}2 +%description -n %{name}2-core +This package provides the core for libcouchbase. It contains an IO +implementation based on select(2). If preferred, you can install one +of the available backends (libcouchbase2-libevent or +libcouchbase2-libev). libcouchbase will automatically use the +installed backend. It is also possible to integrate another IO backend +or write your own. + +%package -n %{name}2-bin +Group: Development/Tools +Summary: Couchbase Client Tools +Requires: %{name}2-core = %{version}-%{release} +%description -n %{name}2-bin +This is the CLI tools Couchbase project. + +%package devel +Group: Development/Libraries +Summary: Couchbase Client & Protocol Library - Header files +Requires: %{name}2-core = %{version}-%{release} +%description devel +Development files for the Couchbase Client & Protocol Library + +%prep +%if 0%{?rhel} > 5 || 0%{?fedora} > 7 +%define __lcb_cmake %cmake +%else +%define __lcb_cmake %cmake28 +%endif + +%setup -q -n @TARREDAS@ +%{__lcb_cmake} -DLCB_NO_TESTS=1 -DLCB_BUILD_LIBUV=OFF + +%build +%{__make} %{_smp_mflags} + +%install +%{__make} install DESTDIR="%{buildroot}" AM_INSTALL_PROGRAM_FLAGS="" + +%clean +%{__rm} -rf %{buildroot} + +%post -n %{name}2-core -p /sbin/ldconfig + +%postun -n %{name}2-core -p /sbin/ldconfig + +%files -n %{name}2-core +%defattr(-, root, root) +%{_libdir}/libcouchbase.so.* +%doc README.markdown LICENSE RELEASE_NOTES.markdown + +%files -n %{name}2-libevent +%defattr(-, root, root) +%{_libdir}/libcouchbase_libevent.so + +%files -n %{name}2-libev +%defattr(-, root, root) +%{_libdir}/libcouchbase_libev.so + +%files -n %{name}2-bin +%defattr(-, root, root) +%{_bindir}/cbc* +%{_mandir}/man1/cbc*.1* +%{_mandir}/man4/cbcrc*.4* + +%files devel +%defattr(-, root, root) +%{_includedir}/libcouchbase +%{_libdir}/libcouchbase.so +%{_libdir}/pkgconfig/libcouchbase.pc diff --git a/couchbase-sys/libcouchbase-2.7.0/packaging/rpm/package.mk b/couchbase-sys/libcouchbase-2.7.0/packaging/rpm/package.mk new file mode 100644 index 00000000..e5c4a5be --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/packaging/rpm/package.mk @@ -0,0 +1,40 @@ +.PHONY: dist-rpm + +# Defined by CMake. For autotools, these will just be the normal build tree +BUILDROOT ?= $(shell pwd) +SRCROOT ?= $(shell pwd) + +# Variables derived +GITPARSE := $(SRCROOT)/packaging/parse-git-describe.pl +RPM_WORKSPACE := $(BUILDROOT)/build-rpm +RPM_DIR := $(RPM_WORKSPACE)/ +RPM_VER := $(shell $(GITPARSE) --rpm-ver --input $(REVDESCRIBE)) +RPM_REL := $(shell $(GITPARSE) --rpm-rel --input $(REVDESCRIBE)) +TAR_VERSION := $(shell $(GITPARSE) --tar --input $(REVDESCRIBE)) + +EXTRA_RPMDEFS := + +dist-rpm: dist + rm -rf $(RPM_WORKSPACE) + mkdir -p $(RPM_DIR) + mkdir $(RPM_DIR)/SOURCES + mkdir $(RPM_DIR)/BUILD + mkdir $(RPM_DIR)/RPMS + mkdir $(RPM_DIR)/SRPMS + cp $(BUILDROOT)/$(PACKAGE)-$(TAR_VERSION).tar.gz $(RPM_DIR)/SOURCES + sed \ + 's/@VERSION@/$(RPM_VER)/g;s/@RELEASE@/$(RPM_REL)/g;s/@TARREDAS@/libcouchbase-$(TAR_VERSION)/g' \ + < packaging/rpm/$(PACKAGE).spec.in > $(RPM_WORKSPACE)/$(PACKAGE).spec + + (cd $(RPM_WORKSPACE) && \ + rpmbuild ${RPM_FLAGS} -ba \ + --define "_topdir $(RPM_DIR)" \ + --define "_source_filedigest_algorithm md5" \ + --define "_binary_filedigest_algorithm md5" \ + $(EXTRA_RPMDEFS) \ + $(PACKAGE).spec \ + ) + + mv $(RPM_DIR)/RPMS/*/*.rpm $(BUILDROOT) + mv $(RPM_DIR)/SRPMS/*.rpm $(BUILDROOT) + rm -rf $(RPM_WORKSPACE) diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/CMakeLists.txt b/couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/CMakeLists.txt new file mode 100644 index 00000000..cf447c9a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/CMakeLists.txt @@ -0,0 +1,9 @@ +IF(WIN32) + INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/contrib/win32-defs) + ADD_LIBRARY(couchbase_iocp OBJECT + iocp_iops.c + iocp_loop.c + iocp_timer.c + iocp_util.c) + ADD_DEFINITIONS(-DLIBCOUCHBASE_INTERNAL=1) +ENDIF(WIN32) diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_iops.c b/couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_iops.c new file mode 100644 index 00000000..3f4dfc17 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_iops.c @@ -0,0 +1,466 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* +* Copyright 2013 Couchbase, Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/** +* New-Style v1 plugin for Windows, Using IOCP +* @author Mark Nunberg +*/ + +#include "iocp_iops.h" +#include +#include +static int +start_write(lcb_io_opt_t iobase, lcb_sockdata_t *sockbase, +struct lcb_iovec_st *iov, lcb_size_t niov, void *uarg, + lcb_ioC_write2_callback callback) +{ + iocp_t *io = (iocp_t *)iobase; + iocp_write_t *w; + iocp_sockdata_t *sd = (iocp_sockdata_t *)sockbase; + int rv; + DWORD dwNbytes; + + /** Figure out which w we should use */ + if (sd->w_info.state == IOCP_WRITEBUF_AVAILABLE) { + w = &sd->w_info; + w->state = IOCP_WRITEBUF_INUSE; + memset(&w->ol_write.base, 0, sizeof(w->ol_write.base)); + } else { + w = calloc(1, sizeof(*w)); + assert(w); + if (!w) { iobase->v.v2.error = WSA_NOT_ENOUGH_MEMORY; return -1; } + + w->state = IOCP_WRITEBUF_ALLOCATED; + w->ol_write.action = LCBIOCP_ACTION_WRITE; + w->ol_write.sd = sd; + } + + w->cb = callback; + w->uarg = uarg; + + /* nbytes is ignored here, but mandatory for WSASend() */ + rv = WSASend(sd->sSocket, (WSABUF *)iov, niov, &dwNbytes, + 0 /* Flags */, (OVERLAPPED *)&w->ol_write, NULL /* IOCP Callback */); + rv = iocp_just_scheduled(io, &w->ol_write, rv); + /* TODO: if write completes immediately, maybe return a special code */ + return rv; +} + +static int +start_read(lcb_io_opt_t iobase, lcb_sockdata_t *sockbase, + lcb_IOV *iov, lcb_size_t niov, void *uarg, + lcb_ioC_read2_callback callback) +{ + int rv; + DWORD flags = 0, dwNbytes; + iocp_t *io = (iocp_t *)iobase; + iocp_sockdata_t *sd = (iocp_sockdata_t *)sockbase; + struct lcb_buf_info *bi = &sockbase->read_buffer; + + IOCP_LOG(IOCP_DEBUG, "Read Requested.."); + sd->ol_read.action = LCBIOCP_ACTION_READ; + sd->rdcb = callback; + sd->rdarg = uarg; + + /** Remove the leftover bits */ + ZeroMemory(&sd->ol_read.base, sizeof(OVERLAPPED)); + + /* nbytes and flags, required as an argument, but unused in our code */ + rv = WSARecv(sd->sSocket, (WSABUF *)iov, niov, &dwNbytes, + &flags, (OVERLAPPED *)&sd->ol_read, NULL); + + return iocp_just_scheduled(io, &sd->ol_read, rv); +} + +static int +start_connect(lcb_io_opt_t iobase, lcb_sockdata_t *sdbase, + const struct sockaddr *name, unsigned int namelen, + lcb_io_connect_cb callback) +{ + /* In order to use ConnectEx(), the socket must be bound. */ + union { struct sockaddr_in in4; struct sockaddr_in6 in6; } u_addr; + BOOL result; + LPFN_CONNECTEX pConnectEx; + iocp_t *io = (iocp_t *)iobase; + iocp_sockdata_t *sd = (iocp_sockdata_t *)sdbase; + iocp_connect_t *conn; + + conn = calloc(1, sizeof(*conn)); + + assert(conn); + if (conn == NULL) { iobase->v.v2.error = WSA_NOT_ENOUGH_MEMORY; return -1; } + + conn->cb = callback; + conn->ol_conn.sd = sd; + conn->ol_conn.action = LCBIOCP_ACTION_CONNECT; + IOCP_LOG(IOCP_INFO, "Connnection OL=%p", &conn->ol_conn); + + memset(&u_addr, 0, sizeof(u_addr)); + if (namelen == sizeof(u_addr.in4)) { + u_addr.in4.sin_family = AF_INET; + } else if (namelen == sizeof(u_addr.in6)) { + u_addr.in6.sin6_family = AF_INET6; + } else { + free(conn); + iobase->v.v2.error = WSAEINVAL; + return -1; + } + + if (bind(sd->sSocket, (const struct sockaddr *)&u_addr, namelen) != 0) { + iocp_set_last_error(iobase, sd->sSocket); + free(conn); + return -1; + } + + pConnectEx = iocp_initialize_connectex(sd->sSocket); + if (!pConnectEx) { + iocp_set_last_error(iobase, INVALID_SOCKET); + return -1; + } + + result = pConnectEx(sd->sSocket, name, namelen, + NULL, 0, NULL, /* Optional buffer and length to send when connected. (unused)*/ + (OVERLAPPED *)conn); + + /** Other functions return 0 to indicate success. Here it's the opposite */ + return iocp_just_scheduled(io, &conn->ol_conn, result == TRUE ? 0 : -1); +} + +static lcb_sockdata_t * +create_socket(lcb_io_opt_t iobase, int domain, int type, int protocol) +{ + iocp_t *io = (iocp_t *)iobase; + HANDLE hResult; + SOCKET s; + iocp_sockdata_t *sd; + + sd = calloc(1, sizeof(*sd)); + if (sd == NULL) { + return NULL; + } + + /* We need to use WSASocket to set the WSA_FLAG_OVERLAPPED option */ + s = WSASocket(domain, type, protocol, + NULL /* protocol info */, 0 /* "Group" */, + WSA_FLAG_OVERLAPPED); + + if (s == INVALID_SOCKET) { + iocp_set_last_error(iobase, s); + free(sd); + return NULL; + } + + /** + * Disable the send buffer. This ensures a callback is invoked. + * If this is not set, the send operation may complete immediately + * and our operations may never be queued. The KB article says we should + * ensure multiple write requests are batched into a single operation, which + * is something we already do :) + * + * See http://support.microsoft.com/kb/181611 for details. + * Disabled currently until we can get actual benchmark numbers + */ + if (0) { + int optval = 0, rv; + rv = setsockopt(s, SOL_SOCKET, SO_SNDBUF, (const char*)&optval, sizeof optval); + } + + hResult = CreateIoCompletionPort((HANDLE)s, + io->hCompletionPort, (ULONG_PTR)sd, 0 /* nthreads */); + + if (hResult == NULL) { + iocp_set_last_error(iobase, s); + closesocket(s); + free(sd); + return NULL; + } + + sd->ol_read.sd = sd; + sd->refcount = 1; + sd->sSocket = s; + sd->sd_base.socket = s; /* Informational, used in tests */ + + /** Initialize the write structure */ + sd->w_info.ol_write.sd = sd; + sd->w_info.state = IOCP_WRITEBUF_AVAILABLE; + sd->w_info.ol_write.action = LCBIOCP_ACTION_WRITE; + + lcb_list_append(&io->sockets, &sd->list); + + return &sd->sd_base; +} + +static unsigned int +close_socket(lcb_io_opt_t iobase, lcb_sockdata_t *sockbase) +{ + iocp_sockdata_t *sd = (iocp_sockdata_t *)sockbase; + + if (sd->sSocket != INVALID_SOCKET) { + closesocket(sd->sSocket); + sd->sSocket = INVALID_SOCKET; + } + iocp_socket_decref((iocp_t *)iobase, sd); + return 0; +} + +static int +sock_nameinfo(lcb_io_opt_t iobase, + lcb_sockdata_t *sockbase, struct lcb_nameinfo_st *ni) +{ + iocp_sockdata_t *sd = (iocp_sockdata_t *)sockbase; + getsockname(sd->sSocket, ni->local.name, ni->local.len); + getpeername(sd->sSocket, ni->remote.name, ni->remote.len); + return 0; +} + +static void * +create_timer(lcb_io_opt_t iobase) +{ + (void)iobase; return calloc(1, sizeof(iocp_timer_t)); +} + +static void +delete_timer(lcb_io_opt_t iobase, void *opaque) +{ + iocp_timer_t *tmr = (iocp_timer_t *)opaque; + iocp_t *io = (iocp_t *)iobase; + if (tmr->is_active) { + tmr->is_active = 0; + iocp_tmq_del(&io->timer_queue.list, tmr); + } +} + +static int +update_timer(lcb_io_opt_t iobase, void *opaque, lcb_U32 usec, void *arg, + lcb_ioE_callback cb) +{ + iocp_t *io = (iocp_t *)iobase; + iocp_timer_t *tmr = (iocp_timer_t *)opaque; + lcb_U64 now; + + if (tmr->is_active) { + iocp_tmq_del(&io->timer_queue.list, tmr); + } + + tmr->cb = cb; + tmr->arg = arg; + tmr->is_active = 1; + now = iocp_millis(); + tmr->ms = now + (usec / 1000); + iocp_tmq_add(&io->timer_queue.list, tmr); + return 0; +} + +static void +destroy_timer(lcb_io_opt_t iobase, void *opaque) +{ + free(opaque); + (void)iobase; +} + +static int +set_nbio(SOCKET s, u_long mode) +{ + int rv; + rv = ioctlsocket(s, FIONBIO, &mode); + if (rv == 0) { + return 0; + } else { + fprintf(stderr, "libcouchbase: ioctlsocket => %lu\n", WSAGetLastError()); + return -1; + } +} + +static int +check_closed(lcb_io_opt_t io, lcb_sockdata_t *sockbase, int flags) +{ + int rv, err; + char buf; + iocp_sockdata_t *sd = (iocp_sockdata_t *)sockbase; + WSABUF iov; + DWORD dwReceived, dwFlags = MSG_PEEK; + + /* Currently don't know if IOCP lets use use MSG_PEEK. + * On the one hand: "This flag is valid only for nonoverlapped sockets". + * On the other hand: + > If both lpOverlapped and lpCompletionRoutine are NULL, the socket + > in this function will be treated as a nonoverlapped socket. + * + * Source: http://msdn.microsoft.com/en-us/library/windows/desktop/ms741688(v=vs.85).aspx + + * As a workaround for now, let's just disable this check if we are + * expecting unsolicited data. It is apparently legal to mix overlapped + * and non-overlapped calls. + */ + + if ((flags & LCB_IO_SOCKCHECK_PEND_IS_ERROR) == 0) { + return LCB_IO_SOCKCHECK_STATUS_UNKNOWN; + } + + if (set_nbio(sd->sSocket, 1) != 0) { + return LCB_IO_SOCKCHECK_STATUS_UNKNOWN; + } + + iov.buf = &buf; + iov.len = 1; + rv = WSARecv(sd->sSocket, &iov, 1, &dwReceived, &dwFlags, NULL, NULL); + err = WSAGetLastError(); + + if (set_nbio(sd->sSocket, 0) != 0) { + return LCB_IO_SOCKCHECK_STATUS_CLOSED; + } + + if (rv == 0) { + return LCB_IO_SOCKCHECK_STATUS_CLOSED; + } else if (err == WSAEWOULDBLOCK) { + return LCB_IO_SOCKCHECK_STATUS_OK; + } else { + return LCB_IO_SOCKCHECK_STATUS_UNKNOWN; + } +} + +static void +iops_dtor(lcb_io_opt_t iobase) +{ + iocp_t *io = (iocp_t *)iobase; + lcb_list_t *cur; + + /** Close all sockets first so we can get events for them */ + LCB_LIST_FOR(cur, &io->sockets) { + iocp_sockdata_t *sd; + sd = LCB_LIST_ITEM(cur, iocp_sockdata_t, list); + if (sd->sSocket != INVALID_SOCKET) { + closesocket(sd->sSocket); + sd->sSocket = INVALID_SOCKET; + } + } + /** Drain the queue. This should not block */ + while (1) { + DWORD nbytes; + ULONG_PTR completionKey; + LPOVERLAPPED pOl; + iocp_sockdata_t *sd; + iocp_overlapped_t *ol; + + GetQueuedCompletionStatus(io->hCompletionPort, + &nbytes, &completionKey, &pOl, 0 /* Timeout */); + sd = (iocp_sockdata_t *)completionKey; + ol = (iocp_overlapped_t *)pOl; + + if (!ol) { + break; + } + + if (ol->action == LCBIOCP_ACTION_CONNECT) { + free(ol); + } else if (ol->action == LCBIOCP_ACTION_WRITE) { + iocp_write_done(io, IOCP_WRITEOBJ_FROM_OVERLAPPED(ol), -1); + } else if (ol->action == LCBIOCP_ACTION_READ) { + io->base.v.v2.error = WSAECONNRESET; + sd->rdcb(&sd->sd_base, -1, sd->rdarg); + } + iocp_socket_decref(io, sd); + } + + /* Destroy all remaining sockets */ + LCB_LIST_FOR(cur, &io->sockets) { + iocp_sockdata_t *sd = LCB_LIST_ITEM(cur, iocp_sockdata_t, list); + + IOCP_LOG(IOCP_WARN, "Leak detected in socket %p (%lu). Refcount=%d", sd, sd->sSocket, sd->refcount); + if (sd->sSocket != INVALID_SOCKET) { + closesocket(sd->sSocket); + sd->sSocket = INVALID_SOCKET; + } + } + + if (io->hCompletionPort && CloseHandle(io->hCompletionPort)) { + IOCP_LOG(IOCP_ERR, "Couldn't CloseHandle: %d", GetLastError()); + } + free(io); +} + +static void +get_procs(int version, lcb_loop_procs *loop, lcb_timer_procs *timer, + lcb_bsd_procs *bsd, lcb_ev_procs *ev, lcb_completion_procs *iocp, + lcb_iomodel_t *model) +{ + *model = LCB_IOMODEL_COMPLETION; + + loop->start = iocp_run; + loop->stop = iocp_stop; + + iocp->connect = start_connect; + iocp->read2 = start_read; + iocp->write2 = start_write; + iocp->socket = create_socket; + iocp->close = close_socket; + iocp->nameinfo = sock_nameinfo; + iocp->is_closed = check_closed; + + timer->create = create_timer; + timer->cancel = delete_timer; + timer->schedule = update_timer; + timer->destroy = destroy_timer; + (void)ev; + (void)bsd; +} + +LIBCOUCHBASE_API +lcb_error_t lcb_iocp_new_iops(int version, lcb_io_opt_t *ioret, void *arg) +{ + iocp_t *io; + lcb_io_opt_t tbl; + + io = calloc(1, sizeof(*io)); + if (!io) { + return LCB_CLIENT_ENOMEM; + } + + /** These functions check if they were called more than once using atomic ops */ + iocp_initialize_loop_globals(); + lcb_list_init(&io->timer_queue.list); + lcb_list_init(&io->sockets); + + tbl = &io->base; + *ioret = tbl; + + io->breakout = TRUE; + + /** Create IOCP */ + io->hCompletionPort = CreateIoCompletionPort( + INVALID_HANDLE_VALUE, NULL, 0, 0); + + if (!io->hCompletionPort) { + return LCB_EINTERNAL; + } + + tbl->destructor = iops_dtor; + tbl->version = 2; + tbl->v.v2.get_procs = get_procs; + + (void)version; + (void)arg; + + return LCB_SUCCESS; +} + +LIBCOUCHBASE_API +struct lcb_io_opt_st *lcb_create_iocp_io_opts(void) { + struct lcb_io_opt_st *ret; + lcb_iocp_new_iops(0, &ret, NULL); + return ret; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_iops.h b/couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_iops.h new file mode 100644 index 00000000..b1b06aa1 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_iops.h @@ -0,0 +1,217 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * New-Style v1 plugin for Windows, Using IOCP + * @author Mark Nunberg + */ + +#ifndef LCB_IOCP_H +#define LCB_IOCP_H + +#include + +#define WIN32_NO_STATUS +#include +#include "list.h" + +#undef WIN32_NO_STATUS + +#ifdef __MINGW32__ +#include "mingwdefs.h" +#else +#include +#endif + +#include "iocpdefs.h" + +/** + * These macros provide atomic operations for one-time initialization + * functions. They take as their parameter a 'LONG'. + * The initial value of the variable should be 0. If the initialization + * function has not run yet, it is set to 1. Subsequent functions will + * return 1. + */ +#define IOCP_SYNCTYPE volatile LONG +#define IOCP_INITONCE(syncvar) InterlockedCompareExchange(&syncvar, 1, 0) ? 0 : 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#define LCB_IOCP_VISTA_API +#if _WIN32_WINNT < 0x0600 +#undef LCB_IOCP_VISTA_API +#endif + +/** @see iocp_overlapped_t */ +enum { + LCBIOCP_ACTION_NONE = 100, + LCBIOCP_ACTION_READ, + LCBIOCP_ACTION_WRITE, + LCBIOCP_ACTION_CONNECT +}; + +struct iocp_sockdata_st; + +/** + * This structure is our 'overlapped' subclass. It in itself does not + * contain any data, but rather determines how to read the + * CompletionKey passed along with GetQueuedCompletionStatus + * This information is determined from the ::action field */ +typedef struct { + OVERLAPPED base; + struct iocp_sockdata_st *sd; + unsigned char action; +} iocp_overlapped_t; + +typedef enum { + IOCP_WRITEBUF_AVAILABLE = 0, + IOCP_WRITEBUF_INUSE, + IOCP_WRITEBUF_ALLOCATED +} iocp_wbuf_state_t; + +typedef struct { + iocp_overlapped_t ol_write; + lcb_ioC_write2_callback cb; + iocp_wbuf_state_t state; + void *uarg; +} iocp_write_t; + +#define IOCP_WRITEOBJ_FROM_OVERLAPPED(ol) \ + (iocp_write_t *)(((char *)ol) - offsetof(iocp_write_t, ol_write)) + +typedef struct iocp_sockdata_st { + lcb_sockdata_t sd_base; + /**OVERLAPPED subclass used for read operations. Since no more than one read + * per socket may be active at any given time, this is attached to the socket + * structure */ + iocp_overlapped_t ol_read; + /** Write structure allocated as a single chunk */ + iocp_write_t w_info; + /** Reference count. Mainly managed by iocp_just_scheduled() and + * iocp_on_dequeued(). This value is set to 1 for a new socket. */ + unsigned int refcount; + /** Actual socket descriptor */ + SOCKET sSocket; + /** Callback for read operations */ + lcb_ioC_read2_callback rdcb; + /** Argument for read callback */ + void *rdarg; + /** Node in linked list of sockets */ + lcb_list_t list; +} iocp_sockdata_t; + +typedef struct { + iocp_overlapped_t ol_conn; + lcb_io_connect_cb cb; +} iocp_connect_t; + +typedef struct iocp_timer_st { + lcb_list_t list; + char is_active; + lcb_U64 ms; + lcb_ioE_callback cb; + void *arg; +} iocp_timer_t; + +typedef struct iocp_st { + struct lcb_io_opt_st base; /**< Base table */ + HANDLE hCompletionPort; /**< Completion port */ + iocp_timer_t timer_queue; /**< Pending timers */ + lcb_list_t sockets; /**< List of all sockets */ + unsigned int n_iopending; /**< Count of outstanding I/O operations */ + BOOL breakout; /**< Flag unset during lcb_wait() and set during lcb_breakout() */ +} iocp_t; + +/** + * @brief Call this function when an I/O operation has been scheduled. + * @param io the io operations structure + * @param ol the overlapped upon which the operation was associated with + * @param status the return code of the scheduling API. + * @return an error code suitable for propagating up to the library. + * + * Note that this function does more than convert an error code. If an operation + * has been successfuly scheduled, the relevant socket's reference count will + * also be incremented. + */ +int iocp_just_scheduled(iocp_t *io, iocp_overlapped_t *ol, int status); + +/** + * @brief Call this function when an I/O operation has been completed. This + * is the logical end block of the iocp_just_scheduled() API + * @param io the I/O operations structure + * @param sd the socket associated with the operation + * @param action the type of operation performed + */ +void iocp_on_dequeued(iocp_t *io, iocp_sockdata_t *sd, int action); +void iocp_socket_decref(iocp_t *io, iocp_sockdata_t *sd); + +int iocp_w32err_2errno(DWORD error); +DWORD iocp_set_last_error(lcb_io_opt_t io, SOCKET sock); + +/** Get current timestamp in microseconds */ +lcb_U32 iocp_micros(void); + +/** Get current timestamp in milliseconds */ +#define iocp_millis() (iocp_micros() / 1000) + +/** Initialize any globals needed by the plugin. This may be called more than + * once, and will only initialize once. */ +void iocp_initialize_loop_globals(void); + +/** Call this on a new socket to retrieve its `ConnectEx` function pointer */ +LPFN_CONNECTEX iocp_initialize_connectex(SOCKET s); + +/** This safely invokes and restores the callback */ +void iocp_write_done(iocp_t *io, iocp_write_t *w, int status); + +/**Extract the actual error code from an OVERLAPPED after an operation + * has been received on it + * @param lpOverlapped the overlapped structure + * @return The actual Winsock error code + */ +int iocp_overlapped_status(OVERLAPPED *lpOverlapped); + +void iocp_run(lcb_io_opt_t iobase); +void iocp_stop(lcb_io_opt_t iobase); + +/** Timer Functions*/ +void iocp_tmq_add(lcb_list_t *list, iocp_timer_t *timer); +void iocp_tmq_del(lcb_list_t *list, iocp_timer_t *timer); +lcb_U64 iocp_tmq_next_timeout(lcb_list_t *list, lcb_U64 now); +iocp_timer_t *iocp_tmq_pop(lcb_list_t *list, lcb_U64 now); + +LIBCOUCHBASE_API +lcb_error_t lcb_iocp_new_iops(int version, lcb_io_opt_t *ioret, void *arg); + +enum { IOCP_TRACE, IOCP_DEBUG, IOCP_INFO, IOCP_WARN, IOCP_ERR, IOCP_FATAL }; +#ifdef IOCP_LOG_VERBOSE +#include +#define IOCP_LOG(facil, ...) \ + fprintf(stderr, "[%s] <%s:%d>: ", #facil, __FILE__, __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); +#else +#define IOCP_LOG(...) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LCB_IOCP_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_loop.c b/couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_loop.c new file mode 100644 index 00000000..ffc04b42 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_loop.c @@ -0,0 +1,295 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * New-Style v2 plugin for Windows, Using IOCP + * + * This file contains the core routines which actually make up the + * various "loops" of the event loop. + * + * @author Mark Nunberg + */ + +#include "iocp_iops.h" +#include + +static sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx = NULL; +static int Have_GQCS_Ex = 1; + +void iocp_initialize_loop_globals(void) +{ + HMODULE hKernel32; + static IOCP_SYNCTYPE initialized = 0; + + if (!IOCP_INITONCE(initialized)) { + return; + } + + hKernel32 = GetModuleHandleA("kernel32.dll"); + if (!hKernel32) { + fprintf(stderr, "Couldn't load Kernel32.dll: [%u]\n", GetLastError()); + return; + } + + pGetQueuedCompletionStatusEx = (sGetQueuedCompletionStatusEx)GetProcAddress(hKernel32, "GetQueuedCompletionStatusEx"); + if (pGetQueuedCompletionStatusEx == NULL) { + Have_GQCS_Ex = 0; + fprintf(stderr, "Couldn't load GetQueuedCompletionStatusEx. Using fallback [%u]\n", + GetLastError()); + } +} + +/** + * Make these macros prominent. + * It's important that they get called after each of our own calls to + * lcb. + */ +#define LOOP_CAN_CONTINUE(io) ((io)->breakout == FALSE) +#define DO_IF_BREAKOUT(io, e) if (!LOOP_CAN_CONTINUE(io)) { e; } +#define HAS_QUEUED_IO(io) (io)->n_iopending + +void iocp_write_done(iocp_t *io, iocp_write_t *w, int status) +{ + lcb_ioC_write2_callback callback = w->cb; + void *uarg = w->uarg; + iocp_sockdata_t *sd = w->ol_write.sd; + + if (w->state == IOCP_WRITEBUF_ALLOCATED) { + free(w); + } else { + w->state = IOCP_WRITEBUF_AVAILABLE; + } + callback(&sd->sd_base, status, uarg); +} + +/** + * Handles a single OVERLAPPED entry, and invokes + * the appropriate event + */ +static void +handle_single_overlapped(iocp_t *io, OVERLAPPED *lpOverlapped, + ULONG_PTR lpCompletionKey, DWORD dwNumberOfBytesTransferred) +{ + union { iocp_write_t *w; iocp_connect_t *conn; } u_ol; + void *pointer_to_free = NULL; + int opstatus = 0; + int ws_status; + int action; + iocp_overlapped_t *ol = (iocp_overlapped_t *)lpOverlapped; + iocp_sockdata_t *sd = (iocp_sockdata_t *)lpCompletionKey; + + IOCP_LOG(IOCP_TRACE, "OL=%p, NB=%lu", ol, dwNumberOfBytesTransferred); + + ws_status = iocp_overlapped_status(lpOverlapped); + + if (ws_status) { + IOCP_LOG(IOCP_WARN, "Got negative status for %p: %d", ol, ws_status); + io->base.v.v2.error = iocp_w32err_2errno(ws_status); + opstatus = -1; + } + + action = ol->action; + + switch (action) { + case LCBIOCP_ACTION_READ: + /** Nothing special in the OVERLAPPED. */ + if (sd->rdcb) { + sd->rdcb(&sd->sd_base, dwNumberOfBytesTransferred, sd->rdarg); + } + break; + + case LCBIOCP_ACTION_WRITE: + u_ol.w = IOCP_WRITEOBJ_FROM_OVERLAPPED(lpOverlapped); + iocp_write_done(io, u_ol.w, opstatus); + break; + + case LCBIOCP_ACTION_CONNECT: + u_ol.conn = (iocp_connect_t *)ol; + + if (opstatus == 0) { + /* This "Syncs" the connected state on the socket.. */ + int rv = setsockopt(ol->sd->sSocket, + SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0); + + if (rv == SOCKET_ERROR) { + iocp_set_last_error(&io->base, ol->sd->sSocket); + opstatus = -1; + } + } + u_ol.conn->cb(&sd->sd_base, opstatus); + pointer_to_free = u_ol.conn; + break; + + default: + fprintf(stderr, "COUCHBASE-IOCP: Unrecognized OVERLAPPED action %d\n", (int)action); + assert(0); + return; + } + + iocp_on_dequeued(io, sd, action); + free(pointer_to_free); +} + +static int dequeue_io_impl_ex(iocp_t *io, DWORD msTimeout) +{ + OVERLAPPED_ENTRY entries[64]; + BOOL status; + ULONG ulRemoved; + const unsigned max_entries = sizeof(entries) / sizeof(entries[0]); + unsigned int ii; + + status = pGetQueuedCompletionStatusEx(io->hCompletionPort, + entries, max_entries, &ulRemoved, msTimeout, FALSE); + + if (status == FALSE || ulRemoved == 0) { + return 0; + } + + for (ii = 0; ii < ulRemoved; ii++) { + OVERLAPPED_ENTRY *ent = entries + ii; + + io->n_iopending--; + handle_single_overlapped(io, ent->lpOverlapped, ent->lpCompletionKey, + ent->dwNumberOfBytesTransferred); + } + + return LOOP_CAN_CONTINUE(io); +} + +static int dequeue_io_impl_compat(iocp_t *io, DWORD msTimeout) +{ + BOOL result; + DWORD dwNbytes; + ULONG_PTR ulPtr; + OVERLAPPED *lpOverlapped; + + result = GetQueuedCompletionStatus(io->hCompletionPort, + &dwNbytes, &ulPtr, &lpOverlapped, msTimeout); + + if (lpOverlapped == NULL) { + IOCP_LOG(IOCP_TRACE, "No events left"); + /** Nothing to do here */ + return 0; + } + + io->n_iopending--; + handle_single_overlapped(io, lpOverlapped, ulPtr, dwNbytes); + return LOOP_CAN_CONTINUE(io); +} + +static void deque_expired_timers(iocp_t *io, lcb_U64 now) +{ + while (LOOP_CAN_CONTINUE(io)) { + iocp_timer_t *timer = iocp_tmq_pop(&io->timer_queue.list, now); + + if (!timer) { + return; + } + + timer->is_active = 0; + timer->cb(-1, 0, timer->arg); + } +} + +/** Maximum amount of time the I/O can hog the loop */ +#define IOCP_IOLOOP_MAXTIME 1000 + +static int should_yield(lcb_U32 start) +{ + lcb_U32 now = iocp_micros(); + return now - start > IOCP_IOLOOP_MAXTIME; +} + +/** + * I'd like to make several behavioral guidelines here: + * + * 1) LCB shall call breakout if it wishes to terminate the loop. + * 2) We shall not handle the case where the user accidentally calls lcb_wait() + * while not having anything pending. That's just too bad. + */ +void iocp_run(lcb_io_opt_t iobase) +{ + iocp_t *io = (iocp_t *)iobase; + lcb_U64 now = 0; + DWORD tmo; + int remaining; + + if (!io->breakout) { + return; + } + + io->breakout = FALSE; + IOCP_LOG(IOCP_INFO, "do-loop BEGIN"); + + do { + /** To ensure we don't starve pending timers, use an iteration */ + lcb_U32 usStartTime; + + if (!now) { + now = iocp_millis(); + } + + do { + tmo = (DWORD)iocp_tmq_next_timeout(&io->timer_queue.list, now); + IOCP_LOG(IOCP_TRACE, "Timeout=%lu msec", tmo); + + if (tmo) { + break; + } + + deque_expired_timers(io, now); + } while (tmo == 0 && LOOP_CAN_CONTINUE(io)); + + if (!LOOP_CAN_CONTINUE(io)) { + break; + } + + /** TODO: Use reference counting */ + if (tmo == INFINITE) { + if (HAS_QUEUED_IO(io)) { + assert(0 && "Found I/O without any timers"); + } + break; + } + + usStartTime = iocp_micros(); + do { + remaining = Have_GQCS_Ex ? + dequeue_io_impl_ex(io, tmo) : dequeue_io_impl_compat(io, tmo); + tmo = 0; + } while (LOOP_CAN_CONTINUE(io) && + remaining && should_yield(usStartTime) == 0); + + IOCP_LOG(IOCP_TRACE, "Stopped IO loop"); + + if (LOOP_CAN_CONTINUE(io)) { + now = iocp_millis(); + deque_expired_timers(io, now); + tmo = (DWORD)iocp_tmq_next_timeout(&io->timer_queue.list, now); + } + } while (LOOP_CAN_CONTINUE(io) && (HAS_QUEUED_IO(io) || tmo != INFINITE)); + + IOCP_LOG(IOCP_INFO, "do-loop END"); + io->breakout = TRUE; +} + +void iocp_stop(lcb_io_opt_t iobase) +{ + iocp_t *io = (iocp_t *)iobase; + IOCP_LOG(IOCP_INFO, "Breakout requested"); + io->breakout = TRUE; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_timer.c b/couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_timer.c new file mode 100644 index 00000000..6ab8d7f5 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_timer.c @@ -0,0 +1,79 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* +* Copyright 2013 Couchbase, Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/** +* New-Style v1 plugin for Windows, Using IOCP +* @author Mark Nunberg +* @author Sergey Avseyev +*/ + +#include "iocp_iops.h" + +static int iocp_timer_cmp_asc(lcb_list_t *a, lcb_list_t *b) +{ + iocp_timer_t *aa, *bb; + + aa = LCB_LIST_ITEM(a, iocp_timer_t, list); + bb = LCB_LIST_ITEM(b, iocp_timer_t, list); + if (aa->ms > bb->ms) { + return 1; + } else if (aa->ms < bb->ms) { + return -1; + } else { + return 0; + } +} + +lcb_uint64_t iocp_tmq_next_timeout(lcb_list_t *list, lcb_uint64_t now) +{ + if (LCB_LIST_IS_EMPTY(list)) { + return INFINITE; + + } else { + iocp_timer_t *tt; + tt = LCB_LIST_ITEM(list->next, iocp_timer_t, list); + return tt->ms > now ? tt->ms - now : 0; + } +} + +iocp_timer_t *iocp_tmq_pop(lcb_list_t *list, lcb_uint64_t now) +{ + iocp_timer_t *tt; + + if (LCB_LIST_IS_EMPTY(list)) { + return NULL; + } + tt = LCB_LIST_ITEM(list->next, iocp_timer_t, list); + if (tt->ms > now) { + return NULL; + } + lcb_list_delete(&tt->list); + return tt; +} + +void iocp_tmq_add(lcb_list_t *list, iocp_timer_t *timer) +{ + IOCP_LOG(IOCP_TRACE, "Adding timer %p with ms %lu", timer, timer->ms); + lcb_list_add_sorted(list, &timer->list, iocp_timer_cmp_asc); +} + +void iocp_tmq_del(lcb_list_t *list, iocp_timer_t *timer) +{ + lcb_list_delete(&timer->list); + IOCP_LOG(IOCP_TRACE, "Removing %p. Empty?=%d", timer, LCB_LIST_IS_EMPTY(list)); + (void)list; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_util.c b/couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_util.c new file mode 100644 index 00000000..2d3b560e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/iocp/iocp_util.c @@ -0,0 +1,229 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * New-Style v2 plugin for Windows, Using IOCP. + * This file contains various utility functions used by the plugin + * @author Mark Nunberg + */ + +#include "iocp_iops.h" +#include +#include +#include +#include "config.h" +#include + +#if defined(__MINGW32__) && !defined(_ftime_s) +#define _ftime_s _ftime /** Mingw doens't have the _s variant */ +#endif + +int iocp_w32err_2errno(DWORD error) +{ + return wsaerr_map_impl(error); +} + +DWORD iocp_set_last_error(lcb_io_opt_t io, SOCKET sock) +{ + int werr = GetLastError(); + io->v.v2.error = iocp_w32err_2errno(werr); + return werr; +} + +lcb_uint32_t iocp_micros(void) +{ + return (lcb_uint32_t)(gethrtime() / 1000); +} + +LPFN_CONNECTEX iocp_initialize_connectex(SOCKET sock) +{ + LPFN_CONNECTEX ret = NULL; + DWORD dwBytes; + GUID ce_guid = WSAID_CONNECTEX; + + WSAIoctl(sock, + SIO_GET_EXTENSION_FUNCTION_POINTER, + &ce_guid, + sizeof(ce_guid), + &ret, + sizeof(&ret), + &dwBytes, + NULL, + NULL); + + return ret; +} + +int iocp_just_scheduled(iocp_t *io, iocp_overlapped_t *ol, int status) +{ + DWORD err = GetLastError(); + IOCP_LOG(IOCP_TRACE, "Pending count: %d", io->n_iopending); + if ((status != 0 && err == WSA_IO_PENDING) || status == 0) { + io->n_iopending++; + ol->sd->refcount++; + return 0; + } + + /** + * Otherwise, there's something wrong + */ + IOCP_LOG(IOCP_ERR, "Got non-harmless error for %p: %d", ol, (int)err); + io->base.v.v2.error = iocp_w32err_2errno(err); + return -1; +} + +void iocp_socket_decref(iocp_t *io, iocp_sockdata_t *sd) +{ + if (--sd->refcount) { + return; + } + + if (sd->sSocket != INVALID_SOCKET) { + closesocket(sd->sSocket); + } + + lcb_list_delete(&sd->list); + + (void)io; + free(sd); +} + +void iocp_on_dequeued(iocp_t *io, iocp_sockdata_t *sd, int action) +{ + IOCP_LOG(IOCP_TRACE, "Dequeing. A=%d, Pending=%d", action, io->n_iopending); + iocp_socket_decref(io, sd); +} + +/**This following function was copied from libuv. + * See http://github.com/joyent/libuv for more details */ +int iocp_overlapped_status(OVERLAPPED *lpOverlapped) +{ + NTSTATUS status = (NTSTATUS)lpOverlapped->Internal; + switch (status) { + case 0: + return ERROR_SUCCESS; + + case STATUS_PENDING: + return ERROR_IO_PENDING; + + case STATUS_INVALID_HANDLE: + case STATUS_OBJECT_TYPE_MISMATCH: + return WSAENOTSOCK; + + case STATUS_INSUFFICIENT_RESOURCES: + case STATUS_PAGEFILE_QUOTA: + case STATUS_COMMITMENT_LIMIT: + case STATUS_WORKING_SET_QUOTA: + case STATUS_NO_MEMORY: + case STATUS_CONFLICTING_ADDRESSES: + case STATUS_QUOTA_EXCEEDED: + case STATUS_TOO_MANY_PAGING_FILES: + case STATUS_REMOTE_RESOURCES: + case STATUS_TOO_MANY_ADDRESSES: + return WSAENOBUFS; + + case STATUS_SHARING_VIOLATION: + case STATUS_ADDRESS_ALREADY_EXISTS: + return WSAEADDRINUSE; + + case STATUS_LINK_TIMEOUT: + case STATUS_IO_TIMEOUT: + case STATUS_TIMEOUT: + return WSAETIMEDOUT; + + case STATUS_GRACEFUL_DISCONNECT: + return WSAEDISCON; + + case STATUS_REMOTE_DISCONNECT: + case STATUS_CONNECTION_RESET: + case STATUS_LINK_FAILED: + case STATUS_CONNECTION_DISCONNECTED: + case STATUS_PORT_UNREACHABLE: + case STATUS_HOPLIMIT_EXCEEDED: + return WSAECONNRESET; + + case STATUS_LOCAL_DISCONNECT: + case STATUS_TRANSACTION_ABORTED: + case STATUS_CONNECTION_ABORTED: + return WSAECONNABORTED; + + case STATUS_BAD_NETWORK_PATH: + case STATUS_NETWORK_UNREACHABLE: + case STATUS_PROTOCOL_UNREACHABLE: + return WSAENETUNREACH; + + case STATUS_HOST_UNREACHABLE: + return WSAEHOSTUNREACH; + + case STATUS_CANCELLED: + case STATUS_REQUEST_ABORTED: + return WSAEINTR; + + case STATUS_BUFFER_OVERFLOW: + case STATUS_INVALID_BUFFER_SIZE: + return WSAEMSGSIZE; + + case STATUS_BUFFER_TOO_SMALL: + case STATUS_ACCESS_VIOLATION: + return WSAEFAULT; + + case STATUS_DEVICE_NOT_READY: + case STATUS_REQUEST_NOT_ACCEPTED: + return WSAEWOULDBLOCK; + + case STATUS_INVALID_NETWORK_RESPONSE: + case STATUS_NETWORK_BUSY: + case STATUS_NO_SUCH_DEVICE: + case STATUS_NO_SUCH_FILE: + case STATUS_OBJECT_PATH_NOT_FOUND: + case STATUS_OBJECT_NAME_NOT_FOUND: + case STATUS_UNEXPECTED_NETWORK_ERROR: + return WSAENETDOWN; + + case STATUS_INVALID_CONNECTION: + return WSAENOTCONN; + + case STATUS_REMOTE_NOT_LISTENING: + case STATUS_CONNECTION_REFUSED: + return WSAECONNREFUSED; + + case STATUS_PIPE_DISCONNECTED: + return WSAESHUTDOWN; + + case STATUS_INVALID_ADDRESS: + case STATUS_INVALID_ADDRESS_COMPONENT: + return WSAEADDRNOTAVAIL; + + case STATUS_NOT_SUPPORTED: + case STATUS_NOT_IMPLEMENTED: + return WSAEOPNOTSUPP; + + case STATUS_ACCESS_DENIED: + return WSAEACCES; + + default: + if ((status & (FACILITY_NTWIN32 << 16)) == (FACILITY_NTWIN32 << 16) && + (status & (ERROR_SEVERITY_ERROR | ERROR_SEVERITY_WARNING))) { + /* It's a windows error that has been previously mapped to an */ + /* ntstatus code. */ + return (DWORD)(status & 0xffff); + } else { + /* The default fallback for unmappable ntstatus codes. */ + return WSAEINVAL; + } + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/libev/CMakeLists.txt b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libev/CMakeLists.txt new file mode 100644 index 00000000..6eaa62f6 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libev/CMakeLists.txt @@ -0,0 +1,29 @@ +INSTALL( + FILES + libev_io_opts.h + DESTINATION + include/libcouchbase) + +IF(NOT (HAVE_LIBEV3 OR HAVE_LIBEV4)) + RETURN() +ENDIF() + + +ADD_LIBRARY(couchbase_libev SHARED plugin-libev.c) +SET_TARGET_PROPERTIES(couchbase_libev + PROPERTIES + PREFIX "lib" + IMPORT_PREFIX "lib" + COMPILE_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CORE_CFLAGS} -std=gnu99") +TARGET_LINK_LIBRARIES(couchbase_libev ${LIBEV_LIBRARIES}) +INCLUDE_DIRECTORIES(AFTER ${LIBEV_INCLUDE_DIR}) + +IF(HAVE_LIBEV4) + ADD_DEFINITIONS(-DHAVE_LIBEV4) +ELSE() + ADD_DEFINITIONS(-DHAVE_LIBEV3) +ENDIF() + +INSTALL(TARGETS + couchbase_libev + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/libev/libev_io_opts.h b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libev/libev_io_opts.h new file mode 100644 index 00000000..4ad25818 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libev/libev_io_opts.h @@ -0,0 +1,65 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2011-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * libev integration with libcouchbase + * @author Sergey Avseyev + */ + +/** + * @ingroup lcb-io-plugin-api + * @defgroup lcb-libev libev + * @brief libev integration + * + * @details + * libcouchbase_create_libev_io_opts() allows you to create an instance + * of the ioopts that will utilize libev. You may either supply an event + * loop (if you'd like to add your own events into the loop), or it will + * create it's own. + * + * @addtogroup lcb-libev + * @{ + */ +#ifndef LIBCOUCHBASE_LIBEV_IO_OPTS_H +#define LIBCOUCHBASE_LIBEV_IO_OPTS_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * Create an instance of an event handler that utilize libev for + * event notification. + * + * @param version the API version to use + * @param loop the event loop (struct ev_loop *) to hook use (please + * note that you shouldn't reference the event loop from + * multiple threads) + * @param io a pointer to a newly created and initialized event handler + * @return status of the operation + */ + LIBCOUCHBASE_API + lcb_error_t lcb_create_libev_io_opts(int version, lcb_io_opt_t *io, void *loop); +#ifdef __cplusplus +} +#endif + +/**@}*/ +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/libev/plugin-libev.c b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libev/plugin-libev.c new file mode 100644 index 00000000..187d4e24 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libev/plugin-libev.c @@ -0,0 +1,289 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This file contains IO operations that use libev + * + * @author Sergey Avseyev + */ +#define LCB_IOPS_V12_NO_DEPRECATE +#include "config.h" +#ifdef HAVE_LIBEV_EV_H +#include +#else +#include +#endif +#include "libev_io_opts.h" +#include +#include +#include +#include + +struct libev_cookie { + struct ev_loop *loop; + int allocated; + int suspended; +}; + + +struct libev_event { + union { + struct ev_io io; + struct ev_timer timer; + } ev; + void *data; + void (*handler)(lcb_socket_t sock, short which, void *cb_data); +}; + +static void handler_thunk(struct ev_loop *loop, ev_io *io, int events) +{ + struct libev_event *evt = (struct libev_event *)io; + int which = 0; + + if (events & EV_READ) { + which |= LCB_READ_EVENT; + } + if (events & EV_WRITE) { + which |= LCB_WRITE_EVENT; + } + evt->handler(io->fd, which, evt->data); + + (void)loop; +} + + +static void timer_thunk(struct ev_loop *loop, ev_timer *timer, int events) +{ + struct libev_event *evt = (struct libev_event *)timer; + evt->handler(0, 0, evt->data); + (void)events; + (void)loop; + +} + +static void *lcb_io_create_event(struct lcb_io_opt_st *iops) +{ + struct libev_event *event = calloc(1, sizeof(*event)); + (void)iops; + return event; +} + +static int lcb_io_update_event(struct lcb_io_opt_st *iops, + lcb_socket_t sock, + void *event, + short flags, + void *cb_data, + void (*handler)(lcb_socket_t sock, + short which, + void *cb_data)) +{ + struct libev_cookie *io_cookie = iops->v.v2.cookie; + struct libev_event *evt = event; + int events = EV_NONE; + + if (flags & LCB_READ_EVENT) { + events |= EV_READ; + } + if (flags & LCB_WRITE_EVENT) { + events |= EV_WRITE; + } + + if (events == evt->ev.io.events && handler == evt->handler) { + /* no change! */ + return 0; + } + + ev_io_stop(io_cookie->loop, &evt->ev.io); + evt->data = cb_data; + evt->handler = handler; + ev_init(&evt->ev.io, handler_thunk); + ev_io_set(&evt->ev.io, sock, events); + ev_io_stop(io_cookie->loop, &evt->ev.io); + ev_io_start(io_cookie->loop, &evt->ev.io); + + return 0; +} +static void lcb_io_delete_event(struct lcb_io_opt_st *iops, + lcb_socket_t sock, + void *event) +{ + struct libev_cookie *io_cookie = iops->v.v2.cookie; + struct libev_event *evt = event; + ev_io_stop(io_cookie->loop, &evt->ev.io); + ev_io_init(&evt->ev.io, NULL, 0, 0); + (void)sock; +} + +static void lcb_io_destroy_event(struct lcb_io_opt_st *iops, + void *event) +{ + lcb_io_delete_event(iops, -1, event); + free(event); +} + +static int lcb_io_update_timer(struct lcb_io_opt_st *iops, + void *timer, + lcb_uint32_t usec, + void *cb_data, + void (*handler)(lcb_socket_t sock, + short which, + void *cb_data)) +{ + struct libev_cookie *io_cookie = iops->v.v2.cookie; + struct libev_event *evt = timer; + ev_tstamp start; + evt->data = cb_data; + evt->handler = handler; + start = usec / (ev_tstamp)1000000; + ev_timer_stop(io_cookie->loop, &evt->ev.timer); + ev_timer_init(&evt->ev.timer, timer_thunk, start, 0); + ev_timer_start(io_cookie->loop, &evt->ev.timer); + return 0; +} + +static void lcb_io_delete_timer(struct lcb_io_opt_st *iops, + void *event) +{ + struct libev_cookie *io_cookie = iops->v.v2.cookie; + struct libev_event *evt = event; + ev_timer_stop(io_cookie->loop, &evt->ev.timer); +} + +static void lcb_io_destroy_timer(struct lcb_io_opt_st *iops, + void *event) +{ + lcb_io_delete_timer(iops, event); + free(event); +} + +static void lcb_io_stop_event_loop(struct lcb_io_opt_st *iops) +{ + struct libev_cookie *io_cookie = iops->v.v2.cookie; +#ifdef HAVE_LIBEV4 + ev_break(io_cookie->loop, EVBREAK_ONE); +#else + ev_unloop(io_cookie->loop, EVUNLOOP_ONE); +#endif +} + +static void run_common(struct lcb_io_opt_st *iops, int is_tick) +{ + struct libev_cookie *io_cookie = iops->v.v2.cookie; + int flags; + + io_cookie->suspended = 0; +#ifdef HAVE_LIBEV4 + flags = is_tick ? EVRUN_NOWAIT : 0; + ev_run(io_cookie->loop, flags); +#else + flags = is_tick ? EVLOOP_NOBLOCK : 0; + ev_loop(io_cookie->loop, flags); +#endif + io_cookie->suspended = 1; + +} + +static void lcb_io_run_event_loop(struct lcb_io_opt_st *iops) +{ + run_common(iops, 0); +} + +static void lcb_io_tick_event_loop(struct lcb_io_opt_st *iops) +{ + run_common(iops, 1); +} + +static void lcb_destroy_io_opts(struct lcb_io_opt_st *iops) +{ + struct libev_cookie *io_cookie = iops->v.v2.cookie; + if (io_cookie->allocated) { + ev_loop_destroy(io_cookie->loop); + } + free(io_cookie); + free(iops); +} + +static void +procs2_ev_callback(int version, lcb_loop_procs *loop_procs, + lcb_timer_procs *timer_procs, lcb_bsd_procs *bsd_procs, + lcb_ev_procs *ev_procs, lcb_completion_procs *completion_procs, + lcb_iomodel_t *iomodel) +{ + ev_procs->cancel = lcb_io_delete_event; + ev_procs->create = lcb_io_create_event; + ev_procs->watch = lcb_io_update_event; + ev_procs->destroy = lcb_io_destroy_event; + + timer_procs->create = lcb_io_create_event; + timer_procs->cancel = lcb_io_delete_timer; + timer_procs->schedule = lcb_io_update_timer; + timer_procs->destroy = lcb_io_destroy_timer; + + loop_procs->start = lcb_io_run_event_loop; + loop_procs->stop = lcb_io_stop_event_loop; + loop_procs->tick = lcb_io_tick_event_loop; + + *iomodel = LCB_IOMODEL_EVENT; + wire_lcb_bsd_impl2(bsd_procs, version); +} + +LIBCOUCHBASE_API +lcb_error_t lcb_create_libev_io_opts(int version, lcb_io_opt_t *io, void *arg) +{ + struct ev_loop *loop = arg; + struct lcb_io_opt_st *ret; + struct libev_cookie *cookie; + if (version != 0) { + return LCB_PLUGIN_VERSION_MISMATCH; + } + ret = calloc(1, sizeof(*ret)); + cookie = calloc(1, sizeof(*cookie)); + if (ret == NULL || cookie == NULL) { + free(ret); + free(cookie); + return LCB_CLIENT_ENOMEM; + } + + /* setup io iops! */ + ret->version = 3; + ret->dlhandle = NULL; + ret->destructor = lcb_destroy_io_opts; + ret->v.v3.get_procs = procs2_ev_callback; + + /* consider that struct isn't allocated by the library, + * `need_cleanup' flag might be set in lcb_create() */ + ret->v.v3.need_cleanup = 0; + + if (loop == NULL) { + if ((cookie->loop = ev_loop_new(EVFLAG_AUTO | EVFLAG_NOENV)) == NULL) { + free(ret); + free(cookie); + return LCB_CLIENT_ENOMEM; + } + cookie->allocated = 1; + } else { + cookie->loop = loop; + cookie->allocated = 0; + } + cookie->suspended = 1; + ret->v.v3.cookie = cookie; + + wire_lcb_bsd_impl(ret); + + *io = ret; + return LCB_SUCCESS; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/libevent/CMakeLists.txt b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libevent/CMakeLists.txt new file mode 100644 index 00000000..1b96a9e3 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libevent/CMakeLists.txt @@ -0,0 +1,29 @@ +INSTALL( + FILES + libevent_io_opts.h + DESTINATION + include/libcouchbase) + + +IF(NOT (HAVE_LIBEVENT OR HAVE_LIBEVENT2)) + RETURN() +ENDIF() + +IF(HAVE_LIBEVENT2) + ADD_DEFINITIONS(-DHAVE_LIBEVENT2) + INCLUDE_DIRECTORIES(AFTER ${LIBEVENT_INCLUDE_DIR}) +ELSE() + INCLUDE_DIRECTORIES(AFTER ${LIBEVENT_INCLUDE_DIR}) + ADD_DEFINITIONS(-DHAVE_LIBEVENT) +ENDIF() + +IF(LCB_EMBED_PLUGIN_LIBEVENT) + ADD_LIBRARY(couchbase_libevent OBJECT plugin-libevent.c) + SET(LCB_LINK_SPEC "${LCB_LINKS_SPEC} ${LIBEVENT_LIBRARIES}") +ELSE() + ADD_LIBRARY(couchbase_libevent SHARED plugin-libevent.c) + INSTALL(TARGETS couchbase_libevent LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + TARGET_LINK_LIBRARIES(couchbase_libevent ${LIBEVENT_LIBRARIES}) +ENDIF() + +LCB_UTIL(couchbase_libevent) diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/libevent/libevent_io_opts.h b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libevent/libevent_io_opts.h new file mode 100644 index 00000000..a116d46a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libevent/libevent_io_opts.h @@ -0,0 +1,67 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2011-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * libevent integration with libcouchbase + * @author Trond Norbye + */ + +/** + * @ingroup lcb-io-plugin-api + * @defgroup lcb-libevent libevent + * @brief libev integration + * + * lcb_create_libevent_io_opts() allows you to create an instance + * of the ioopts that will utilize libevent. You may either supply an event + * base (if you'd like to add your own events into the loop), or it will + * create its own. + */ + +/** + * @addtogroup lcb-libevent + * @{ + */ +#ifndef LIBCOUCHBASE_LIBEVENT_IO_OPTS_H +#define LIBCOUCHBASE_LIBEVENT_IO_OPTS_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * Create an instance of an event handler that utilize libevent for + * event notification. + * + * @param version the API version to use + * @param base the event base (struct event_base *) to hook use (please + * note that you shouldn't reference the event base from + * multiple threads) + * @param io a pointer to a newly created and initialized event handler + * @return status of the operation + */ + LIBCOUCHBASE_API + lcb_error_t lcb_create_libevent_io_opts(int version, lcb_io_opt_t *io, void *base); + +#ifdef __cplusplus +} +#endif + +/**@}*/ +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/libevent/plugin-libevent.c b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libevent/plugin-libevent.c new file mode 100644 index 00000000..90e9b013 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libevent/plugin-libevent.c @@ -0,0 +1,292 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2011-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This file contains IO operations that use libevent + * + * @author Trond Norbye + * @todo add more documentation + */ +#define LCB_IOPS_V12_NO_DEPRECATE +#include "config.h" +#include +#include "libevent_io_opts.h" +#include +#include +#include +#include + +struct libevent_cookie { + struct event_base *base; + int allocated; +}; + + +#ifndef HAVE_LIBEVENT2 +/* libevent 1.x compatibility layer */ +#ifndef evutil_socket_t +#define evutil_socket_t int +#endif + +typedef void (*event_callback_fn)(evutil_socket_t, short, void *); + +static int +event_assign(struct event *ev, + struct event_base *base, + evutil_socket_t fd, + short events, + event_callback_fn callback, + void *arg) +{ + ev->ev_callback = callback; + ev->ev_arg = arg; + ev->ev_fd = fd; + ev->ev_events = events; + ev->ev_res = 0; + ev->ev_flags = EVLIST_INIT; + ev->ev_ncalls = 0; + ev->ev_pncalls = NULL; + event_base_set(base, ev); + + return 0; +} + +static struct event * +event_new(struct event_base *base, + evutil_socket_t fd, + short events, + event_callback_fn cb, + void *arg) { + struct event *ev; + ev = malloc(sizeof(struct event)); + if (ev == NULL) { + return NULL; + } + if (event_assign(ev, base, fd, events, cb, arg) < 0) { + free(ev); + return NULL; + } + return ev; +} + +static void +event_free(struct event *ev) +{ + /* make sure that this event won't be coming back to haunt us. */ + free(ev); + +} +static short +event_get_events(const struct event *ev) +{ + return ev->ev_events; +} + +static event_callback_fn +event_get_callback(const struct event *ev) +{ + return ev->ev_callback; +} +#endif + +static void *lcb_io_create_event(struct lcb_io_opt_st *iops) +{ + return event_new(((struct libevent_cookie *)iops->v.v2.cookie)->base, + INVALID_SOCKET, 0, NULL, NULL); +} + +static int lcb_io_update_event(struct lcb_io_opt_st *iops, + lcb_socket_t sock, + void *event, + short flags, + void *cb_data, + void (*handler)(lcb_socket_t sock, + short which, + void *cb_data)) +{ + flags |= EV_PERSIST; + if (flags == event_get_events(event) && + handler == event_get_callback(event)) { + /* no change! */ + return 0; + } + + if (event_pending(event, EV_READ | EV_WRITE, 0)) { + event_del(event); + } + + event_assign(event, ((struct libevent_cookie *)iops->v.v2.cookie)->base, sock, flags, handler, cb_data); + return event_add(event, NULL); +} + + +static void lcb_io_delete_timer(struct lcb_io_opt_st *iops, + void *event) +{ + (void)iops; + if (event_pending(event, EV_TIMEOUT, 0) != 0 && event_del(event) == -1) { + iops->v.v2.error = EINVAL; + } + event_assign(event, ((struct libevent_cookie *)iops->v.v2.cookie)->base, -1, 0, NULL, NULL); +} + +static int lcb_io_update_timer(struct lcb_io_opt_st *iops, + void *timer, + lcb_uint32_t usec, + void *cb_data, + void (*handler)(lcb_socket_t sock, + short which, + void *cb_data)) +{ + short flags = EV_TIMEOUT | EV_PERSIST; + struct timeval tmo; + if (flags == event_get_events(timer) && + handler == event_get_callback(timer)) { + /* no change! */ + return 0; + } + + if (event_pending(timer, EV_TIMEOUT, 0)) { + event_del(timer); + } + + event_assign(timer, ((struct libevent_cookie *)iops->v.v2.cookie)->base, -1, flags, handler, cb_data); + tmo.tv_sec = usec / 1000000; + tmo.tv_usec = usec % 1000000; + return event_add(timer, &tmo); +} + +static void lcb_io_destroy_event(struct lcb_io_opt_st *iops, + void *event) +{ + (void)iops; + if (event_pending(event, EV_READ | EV_WRITE | EV_TIMEOUT, 0)) { + event_del(event); + } + event_free(event); +} + +static void lcb_io_delete_event(struct lcb_io_opt_st *iops, + lcb_socket_t sock, + void *event) +{ + (void)iops; + (void)sock; + if (event_del(event) == -1) { + iops->v.v2.error = EINVAL; + } + event_assign(event, ((struct libevent_cookie *)iops->v.v2.cookie)->base, -1, 0, NULL, NULL); +} + +static void lcb_io_stop_event_loop(struct lcb_io_opt_st *iops) +{ + event_base_loopbreak(((struct libevent_cookie *)iops->v.v2.cookie)->base); +} + +static void lcb_io_run_event_loop(struct lcb_io_opt_st *iops) +{ + event_base_loop(((struct libevent_cookie *)iops->v.v2.cookie)->base, 0); +} + +static void lcb_io_tick_event_loop(struct lcb_io_opt_st *iops) +{ + event_base_loop(((struct libevent_cookie *)iops->v.v2.cookie)->base, + EVLOOP_NONBLOCK); +} + +static void lcb_destroy_io_opts(struct lcb_io_opt_st *iops) +{ + if (((struct libevent_cookie *)iops->v.v2.cookie)->allocated) { + event_base_free(((struct libevent_cookie *)iops->v.v2.cookie)->base); + } + free(iops->v.v2.cookie); + free(iops); +} + +static void +procs2_lnt_callback(int version, lcb_loop_procs *loop_procs, + lcb_timer_procs *timer_procs, lcb_bsd_procs *bsd_procs, + lcb_ev_procs *ev_procs, lcb_completion_procs *completion_procs, + lcb_iomodel_t *iomodel) +{ + ev_procs->create = lcb_io_create_event; + ev_procs->destroy = lcb_io_destroy_event; + ev_procs->watch = lcb_io_update_event; + ev_procs->cancel = lcb_io_delete_event; + + timer_procs->create = lcb_io_create_event; + timer_procs->destroy = lcb_io_destroy_event; + timer_procs->schedule = lcb_io_update_timer; + timer_procs->cancel = lcb_io_delete_timer; + + loop_procs->start = lcb_io_run_event_loop; + loop_procs->stop = lcb_io_stop_event_loop; + loop_procs->tick = lcb_io_tick_event_loop; + + *iomodel = LCB_IOMODEL_EVENT; + + wire_lcb_bsd_impl2(bsd_procs, version); + (void)completion_procs; +} + +LIBCOUCHBASE_API +lcb_error_t lcb_create_libevent_io_opts(int version, lcb_io_opt_t *io, void *arg) +{ + struct event_base *base = arg; + struct lcb_io_opt_st *ret; + struct libevent_cookie *cookie; + if (version != 0) { + return LCB_PLUGIN_VERSION_MISMATCH; + } + + ret = calloc(1, sizeof(*ret)); + cookie = calloc(1, sizeof(*cookie)); + if (ret == NULL || cookie == NULL) { + free(ret); + free(cookie); + return LCB_CLIENT_ENOMEM; + } + + /* setup io iops! */ + ret->version = 3; + ret->dlhandle = NULL; + ret->destructor = lcb_destroy_io_opts; + /* consider that struct isn't allocated by the library, + * `need_cleanup' flag might be set in lcb_create() */ + ret->v.v3.need_cleanup = 0; + + if (base == NULL) { + if ((cookie->base = event_base_new()) == NULL) { + free(ret); + free(cookie); + return LCB_CLIENT_ENOMEM; + } + cookie->allocated = 1; + } else { + cookie->base = base; + cookie->allocated = 0; + } + + ret->v.v3.cookie = cookie; + ret->v.v3.get_procs = procs2_lnt_callback; + + /* For back-compat */ + wire_lcb_bsd_impl(ret); + + *io = ret; + return LCB_SUCCESS; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/CMakeLists.txt b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/CMakeLists.txt new file mode 100644 index 00000000..2492a835 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/CMakeLists.txt @@ -0,0 +1,42 @@ +INSTALL( + FILES + plugin-libuv.c + plugin-internal.h + libuv_compat.h + DESTINATION + include/libcouchbase/plugins/io/libuv) + +INSTALL( + FILES + libuv_io_opts.h + DESTINATION + include/libcouchbase) + +IF(NOT HAVE_LIBUV OR LCB_NO_PLUGINS OR NOT LCB_BUILD_LIBUV) + RETURN() +ENDIF() + +ADD_LIBRARY(couchbase_libuv SHARED plugin-libuv.c) + +# The macros abstracting UV versions dynamically inject special signatures. We +# can't have the compiler whine about that. + +# Unfortunately, Older GCC (EL5) barfs at this option. +#IF(CMAKE_COMPILER_IS_GNUCC) +# SET(_lcbuv_cflags "-Wno-unused-parameters") +#ELSE() +# SET(_lcbuv_cflags "") +#ENDIF() +SET_TARGET_PROPERTIES(couchbase_libuv + PROPERTIES + PREFIX "lib" + IMPORT_PREFIX "lib" + COMPILE_FLAGS "${CMAKE_C_FLAGS} ${LCB_CORE_FLAGS} ${_lcbuv_cflags}") + +TARGET_LINK_LIBRARIES(couchbase_libuv ${LIBUV_LIBRARIES} couchbase) +INCLUDE_DIRECTORIES(AFTER ${LIBUV_INCLUDE_DIR}) +ADD_DEFINITIONS(-DLIBCOUCHBASE_INTERNAL=1) +INSTALL(TARGETS + couchbase_libuv + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/libuv_compat.h b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/libuv_compat.h new file mode 100644 index 00000000..e54b204d --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/libuv_compat.h @@ -0,0 +1,212 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBUV_COMPAT_H +#define LIBUV_COMPAT_H + +#ifdef UV_VERSION_MAJOR +#ifndef UV_VERSION_PATCH +#define UV_VERSION_PATCH 0 +#endif +#define UV_VERSION ((UV_VERSION_MAJOR << 16) | \ + (UV_VERSION_MINOR << 8) | \ + (UV_VERSION_PATCH)) +#else +#define UV_VERSION 0x000b00 +#endif + +#if defined(_WIN32) && defined(LIBCOUCHBASE_INTERNAL) +#include +#endif + +#ifndef UNKNOWN +#define UNKNOWN -1 +#endif + +#ifndef EAIFAMNOSUPPORT +#define EAIFAMNOSUPPORT EAI_FAMILY +#endif + +#ifndef EAISERVICE +#define EAISERVICE EAI_SERVICE +#endif + +#ifndef EAI_SYSTEM +#define EAI_SYSTEM -11 +#endif +#ifndef EADDRINFO +#define EADDRINFO EAI_SYSTEM +#endif + +#ifndef EAISOCKTYPE +#define EAISOCKTYPE EAI_SOCKTYPE +#endif + +#ifndef ECHARSET +#define ECHARSET 0 +#endif + +#ifndef EOF +#define EOF -1 +#endif + +#ifndef ENONET +#define ENONET ENETDOWN +#endif + +#ifndef ESHUTDOWN +#define ESHUTDOWN WSAESHUTDOWN +#endif + +#ifndef EHOSTDOWN +/* missing only on Windows */ +#define EHOSTDOWN WSAEHOSTDOWN +#endif + +/* Not all systems have these error codes */ +#ifndef EAI_FAIL +#define EAI_FAIL (-1) +#endif +#ifndef EAI_CANCELED +#define EAI_CANCELED -101 +#endif +#ifndef EAI_ADDRFAMILY +#define EAI_ADDRFAMILY -9 +#endif +#ifndef EAI_BADHINTS +#define EAI_BADHINTS EAI_FAIL +#endif +#ifndef EAI_NODATA +#define EAI_NODATA EAI_FAIL +#endif +#ifndef EAI_PROTOCOL +#define EAI_PROTOCOL EAI_FAIL +#endif +#ifndef EAI_AGAIN +#define EAI_AGAIN EAI_FAIL +#endif +#ifndef EAI_BADFLAGS +#define EAI_BADFLAGS EAI_FAIL +#endif +#ifndef EAI_MEMORY +#define EAI_MEMORY EAI_FAIL +#endif +#ifndef EAI_OVERFLOW +#define EAI_OVERFLOW EAI_FAIL +#endif + +#define OK 0 + +#if UV_VERSION < 0x000900 + #define UVC_RUN_ONCE(l) uv_run_once(l) + #define UVC_RUN_DEFAULT(l) uv_run(l) +#else + #define UVC_RUN_ONCE(l) uv_run(l, UV_RUN_ONCE) + #define UVC_RUN_DEFAULT(l) uv_run(l, UV_RUN_DEFAULT) +#endif + +#if UV_VERSION < 0x000b00 + + #define UVC_TCP_CONNECT(req, handle, addr, cb) \ + uv_tcp_connect(req, handle, *(struct sockaddr_in *)addr, cb); + + #define UVC_TCP_CONNECT6(req, handle, addr, cb) \ + uv_tcp_connect6(req, handle, *(struct sockaddr_in6 *)addr, cb); + + #define UVC_ALLOC_CB(func) \ + uv_buf_t func(uv_handle_t *handle, size_t suggested_size) + + #define UVC_ALLOC_CB_VARS() \ + uv_buf_t _buf; uv_buf_t *buf = &_buf; + + #define UVC_ALLOC_CB_RETURN() \ + return _buf; + + #define UVC_READ_CB(func) \ + void func(uv_stream_t *stream, ssize_t nread, const uv_buf_t _buf) + + #define UVC_READ_CB_VARS() \ + const uv_buf_t *buf = &_buf; + + #define UVC_TIMER_CB(func) \ + void func(uv_timer_t *timer, int status) + + static int uvc_uv2syserr(int status) { + #define X(errnum,errname,errdesc) \ + if (status == UV_##errname) { return errname; } + UV_ERRNO_MAP(X); + #undef X + return 0; + } + + static int uvc_is_eof(uv_loop_t *loop, int error) { + error = uv_last_error(loop).code; + return error == UV_EOF; + } + + static int uvc_last_errno(uv_loop_t *loop, int error) { + int uverr = 0; + + if (!error) { + return 0; + } + + uverr = uv_last_error(loop).code; + return uvc_uv2syserr(uverr); + } + +#else + + #define UVC_TCP_CONNECT(req, handle, addr, cb) \ + uv_tcp_connect(req, handle, addr, cb); + + #define UVC_TCP_CONNECT6(req, handle, addr, cb) \ + uv_tcp_connect(req, handle, addr, cb); + + #define UVC_ALLOC_CB(func) \ + void func(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) + + #define UVC_ALLOC_CB_VARS() + + #define UVC_ALLOC_CB_RETURN() + + #define UVC_READ_CB(func) \ + void func(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) + + #define UVC_READ_CB_VARS() + + #define UVC_TIMER_CB(func) \ + void func(uv_timer_t *timer) + + static int uv_uv2syserr(int status) { + #define X(name, desc) if (status == UV_##name) { return name; } + UV_ERRNO_MAP(X) + #undef X + return 0; + } + + static int uvc_last_errno(uv_loop_t *loop, int error) { + return uv_uv2syserr(error); + } + + static int uvc_is_eof(uv_loop_t *loop, int error) { + (void) loop; + return error == UV_EOF; + } + +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/libuv_io_opts.h b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/libuv_io_opts.h new file mode 100644 index 00000000..cd09dd23 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/libuv_io_opts.h @@ -0,0 +1,118 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * libuv integration with libcouchbase + * @author Mark Nunberg + */ + +/** + * @ingroup lcb-io-plugin-api + * @defgroup lcb-libuv libuv + * @brief libuv integration + * + * @details + * libuv is a cross platform event framework using a completion-based programming + * model. Since most distributions do not offer up-to-date libuv binary packages + * and because libuv is constantly evolving itself, binary packages are not + * provided. The source code for this plugin is actually shipped with the + * headers of libcouchbase. + * + * If you built libuv together with libcouchbase (and thus there exists a + * library called `libcouchbase_libuv` then you may simply include this file + * and initialize the iops structure. + * + * Otherwise you should ensure that `` + * is compiled into an object, _and_ that the `LCBUV_EMBEDDED_SOURCE` macro + * is defined for both the compiled object and any code in your application + * that is using the module, thus: + * + * @code{.c} + * //uv-stub.c + * #include + * @endcode + * + * Then, in your application + * @code{.c} + * #include + * + * lcb_io_opt_t *io; + * lcbuv_options_t options; + * options.v.v0.loop = uv_default_loop(); + * options.v.v0.startsop_noop = 1; + * lcb_create_libuv_io_opts(0, &io, &options); + * @endcode + * + * And then compile as + * @code + * $ gcc -o myapp uv-stub.c main.c -DLCBUV_EMBEDDED_SOURCE + * @endcode + * + * @addtogroup lcb-libuv + * @{ + */ + +#ifndef LCB_PLUGIN_UV_H +#define LCB_PLUGIN_UV_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#ifdef LCBUV_EMBEDDED_SOURCE +#define LCBUV_API +#else +#define LCBUV_API LIBCOUCHBASE_API +#endif + + /**Options passed to the iops constructure. You will most likely want + * to set the 'startsop_noop' field to true if you are using an async + * application.*/ + typedef struct lcbuv_options_st { + int version; + union { + struct { + /** External loop to be used (if not default) */ + uv_loop_t *loop; + + /** Whether run_event_loop/stop_event_loop should do anything */ + int startsop_noop; + } v0; + } v; + } lcbuv_options_t; + + /** + * Use this if using an existing uv_loop_t + * @param version Set this to `0` + * @param [out] io a pointer to an io pointer. Will be populated on success + * @param options the options to be passed. From libcouchbase this is a + * `void*` parameter. + */ + LCBUV_API + lcb_error_t lcb_create_libuv_io_opts(int version, + lcb_io_opt_t *io, + lcbuv_options_t *options); + +#ifdef __cplusplus +} +#endif +#endif + +/**@}*/ diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/plugin-internal.h b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/plugin-internal.h new file mode 100644 index 00000000..4b65347f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/plugin-internal.h @@ -0,0 +1,148 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCBUV_PLUGIN_INTERNAL_H +#define LCBUV_PLUGIN_INTERNAL_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "libuv_compat.h" + +#ifdef LCBUV_EMBEDDED_SOURCE +#include +#else +/** Load definitions from inside */ +#include "libuv_io_opts.h" +#endif + +typedef void (*v0_callback_t)(lcb_socket_t, short, void *); +typedef void (*generic_callback_t)(void); + + +/** + * These structures come about the limitation that with -Werror -Wextra + * compilation fails because strictly speaking, a function pointer isn't + * convertible to a normal pointer. + */ + +/** + * Macro - sometimes we might want to use the ->data field? + */ +#define CbREQ(mr) (mr)->callback +typedef struct { + uv_tcp_t t; + lcb_ioC_read2_callback callback; +} my_tcp_t; + +/** + * Wrapper for lcb_sockdata_t + */ +typedef struct { + lcb_sockdata_t base; + + /** + * UV tcp handle. This is also a uv_stream_t. + * ->data field contains the read callback + */ + my_tcp_t tcp; + + /** Reference count */ + unsigned int refcount; + + /** Flag indicating whether uv_close has already been called on the handle */ + unsigned char uv_close_called; + + lcb_IOV iov; + void *rdarg; + + struct { + int read; + int write; + } pending; + +} my_sockdata_t; + + +typedef struct { + uv_write_t w; + lcb_ioC_write2_callback callback; + my_sockdata_t *sock; +} my_write_t; + + +typedef struct { + struct lcb_io_opt_st base; + uv_loop_t *loop; + + /** Refcount. When this hits zero we free this */ + unsigned int iops_refcount; + + /** Whether using a user-initiated loop */ + int external_loop; + + /** whether start/stop are noops */ + int startstop_noop; + + /** for 0.8 only, whether to stop */ + int do_stop; +} my_iops_t; + +typedef struct { + uv_timer_t uvt; + v0_callback_t callback; + void *cb_arg; + my_iops_t *parent; +} my_timer_t; + +typedef struct { + union { + uv_connect_t conn; + uv_idle_t idle; + } uvreq; + + union { + lcb_io_connect_cb conn; + generic_callback_t cb_; + } cb; + + my_sockdata_t *socket; +} my_uvreq_t; + +/****************************************************************************** + ****************************************************************************** + ** Common Macros ** + ****************************************************************************** + ******************************************************************************/ +#define PTR_FROM_FIELD(t, p, fld) ((t*)(void*)((char*)p-(offsetof(t, fld)))) + +#define incref_iops(io) (io)->iops_refcount++ + +#ifdef _WIN32 + typedef ULONG lcb_uvbuf_len_t; +#else + typedef size_t lcb_uvbuf_len_t; +#endif + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/plugin-libuv.c b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/plugin-libuv.c new file mode 100644 index 00000000..e43dda8b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/libuv/plugin-libuv.c @@ -0,0 +1,648 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "plugin-internal.h" + +static my_uvreq_t *alloc_uvreq(my_sockdata_t *sock, generic_callback_t callback); +static void set_last_error(my_iops_t *io, int error); +static void socket_closed_callback(uv_handle_t *handle); + +static void wire_iops2(int version, + lcb_loop_procs *loop, + lcb_timer_procs *timer, + lcb_bsd_procs *bsd, + lcb_ev_procs *ev, + lcb_completion_procs *iocp, + lcb_iomodel_t *model); + + +static void decref_iops(lcb_io_opt_t iobase) +{ + my_iops_t *io = (my_iops_t *)iobase; + lcb_assert(io->iops_refcount); + if (--io->iops_refcount) { + return; + } + + memset(io, 0xff, sizeof(*io)); + free(io); +} + +static void iops_lcb_dtor(lcb_io_opt_t iobase) +{ + my_iops_t *io = (my_iops_t *)iobase; + if (io->startstop_noop) { + decref_iops(iobase); + return; + } + + while (io->iops_refcount > 1) { + UVC_RUN_ONCE(io->loop); + } + + if (io->external_loop == 0) { + uv_loop_delete(io->loop); + } + + decref_iops(iobase); +} + +/****************************************************************************** + ****************************************************************************** + ** Event Loop Functions ** + ****************************************************************************** + ******************************************************************************/ + +#if UV_VERSION < 0x000900 +static void do_run_loop(my_iops_t *io) +{ + while (uv_run_once(io->loop) && io->do_stop == 0) { + /* nothing */ + } + io->do_stop = 0; +} +static void do_stop_loop(my_iops_t *io) +{ + io->do_stop = 1; +} +#else +static void do_run_loop(my_iops_t *io) { + uv_run(io->loop, UV_RUN_DEFAULT); +} +static void do_stop_loop(my_iops_t *io) +{ + uv_stop(io->loop); +} +#endif + + +static void run_event_loop(lcb_io_opt_t iobase) +{ + my_iops_t *io = (my_iops_t *)iobase; + + if (!io->startstop_noop) { + do_run_loop(io); + } +} + +static void tick_event_loop(lcb_io_opt_t iobase) +{ + my_iops_t *io = (my_iops_t *)iobase; + if (!io->startstop_noop) { +#if UV_VERSION < 0x000900 + uv_run_once(io->loop); + io->do_stop = 0; +#else + uv_run(io->loop, UV_RUN_NOWAIT); +#endif + } +} + +static void stop_event_loop(lcb_io_opt_t iobase) +{ + my_iops_t *io = (my_iops_t *)iobase; + if (!io->startstop_noop) { + do_stop_loop(io); + } +} + +LCBUV_API +lcb_error_t lcb_create_libuv_io_opts(int version, + lcb_io_opt_t *io, + lcbuv_options_t *options) +{ + lcb_io_opt_t iop; + uv_loop_t *loop = NULL; + my_iops_t *ret; + + if (version != 0) { + return LCB_PLUGIN_VERSION_MISMATCH; + } + +#ifdef _WIN32 + { + /** UV unloading on Windows doesn't work well */ + HMODULE module; + /* We need to provide a symbol */ + static int dummy; + BOOL result; + result = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_PIN, + (LPCSTR)&dummy, + &module); + if (!result) { + return LCB_EINTERNAL; + } + } +#endif + + ret = calloc(1, sizeof(*ret)); + + if (!ret) { + return LCB_CLIENT_ENOMEM; + } + + iop = &ret->base; + iop->version = 2; + iop->destructor = iops_lcb_dtor; + iop->v.v2.get_procs = wire_iops2; + + ret->iops_refcount = 1; + + *io = iop; + if (options) { + if (options->v.v0.loop) { + ret->external_loop = 1; + loop = options->v.v0.loop; + } + ret->startstop_noop = options->v.v0.startsop_noop; + } + + if (!loop) { + loop = uv_loop_new(); + } + + ret->loop = loop; + + return LCB_SUCCESS; +} + +#define SOCK_INCR_PENDING(s, fld) (s)->pending.fld++ +#define SOCK_DECR_PENDING(s, fld) (s)->pending.fld-- + +#ifdef DEBUG +static void sock_dump_pending(my_sockdata_t *sock) +{ + printf("Socket %p:\n", (void *)sock); + printf("\tRead: %d\n", sock->pending.read); + printf("\tWrite: %d\n", sock->pending.write); +} +#endif + +static void sock_do_uv_close(my_sockdata_t *sock) +{ + if (!sock->uv_close_called) { + sock->uv_close_called = 1; + uv_close((uv_handle_t *)&sock->tcp, socket_closed_callback); + } +} + +static void decref_sock(my_sockdata_t *sock) +{ + lcb_assert(sock->refcount); + + if (--sock->refcount) { + return; + } + sock_do_uv_close(sock); +} + +#define incref_sock(sd) (sd)->refcount++ + +/****************************************************************************** + ****************************************************************************** + ** Socket Functions ** + ****************************************************************************** + ******************************************************************************/ +static lcb_sockdata_t *create_socket(lcb_io_opt_t iobase, + int domain, + int type, + int protocol) +{ + my_sockdata_t *ret; + my_iops_t *io = (my_iops_t *)iobase; + + ret = calloc(1, sizeof(*ret)); + if (!ret) { + return NULL; + } + + uv_tcp_init(io->loop, &ret->tcp.t); + + incref_iops(io); + incref_sock(ret); + + set_last_error(io, 0); + + (void)domain; + (void)type; + (void)protocol; + + return (lcb_sockdata_t *)ret; +} + +/** + * This one is called from uv_close + */ +static void socket_closed_callback(uv_handle_t *handle) +{ + my_sockdata_t *sock = PTR_FROM_FIELD(my_sockdata_t, handle, tcp); + my_iops_t *io = (my_iops_t *)sock->base.parent; + + if (sock->pending.read) { + CbREQ(&sock->tcp)(&sock->base, -1, sock->rdarg); + } + + memset(sock, 0xEE, sizeof(*sock)); + free(sock); + + decref_iops(&io->base); +} + + +static unsigned int close_socket(lcb_io_opt_t iobase, lcb_sockdata_t *sockbase) +{ + my_sockdata_t *sock = (my_sockdata_t *)sockbase; + sock->uv_close_called = 1; + uv_close((uv_handle_t *)&sock->tcp, socket_closed_callback); + (void)iobase; + return 0; +} + +static int cntl_socket(lcb_io_opt_t iobase, lcb_sockdata_t *sockbase, + int mode, int option, void *arg) +{ + my_sockdata_t *sd = (my_sockdata_t *)sockbase; + int rv; + + switch (option) { + case LCB_IO_CNTL_TCP_NODELAY: + if (mode == LCB_IO_CNTL_SET) { + rv = uv_tcp_nodelay(&sd->tcp.t, *(int *)arg); + if (rv != 0) { + set_last_error((my_iops_t*)iobase, rv); + } + return rv; + } else { + LCB_IOPS_ERRNO(iobase) = ENOTSUP; + return -1; + } + default: + LCB_IOPS_ERRNO(iobase) = ENOTSUP; + return -1; + } +} + + +/****************************************************************************** + ****************************************************************************** + ** Connection Functions ** + ****************************************************************************** + ******************************************************************************/ +static void connect_callback(uv_connect_t *req, int status) +{ + my_uvreq_t *uvr = (my_uvreq_t *)req; + + set_last_error((my_iops_t *)uvr->socket->base.parent, status); + + if (uvr->cb.conn) { + uvr->cb.conn(&uvr->socket->base, status); + } + + decref_sock(uvr->socket); + free(uvr); +} + +static int start_connect(lcb_io_opt_t iobase, + lcb_sockdata_t *sockbase, + const struct sockaddr *name, + unsigned int namelen, + lcb_io_connect_cb callback) +{ + my_sockdata_t *sock = (my_sockdata_t *)sockbase; + my_iops_t *io = (my_iops_t *)iobase; + my_uvreq_t *uvr; + int ret; + int err_is_set = 0; + + uvr = alloc_uvreq(sock, (generic_callback_t)callback); + if (!uvr) { + return -1; + } + + if (namelen == sizeof(struct sockaddr_in)) { + ret = UVC_TCP_CONNECT(&uvr->uvreq.conn, + &sock->tcp.t, + name, + connect_callback); + + } else if (namelen == sizeof(struct sockaddr_in6)) { + ret = UVC_TCP_CONNECT6(&uvr->uvreq.conn, + &sock->tcp.t, + name, + connect_callback); + + } else { + io->base.v.v1.error = EINVAL; + ret = -1; + err_is_set = 1; + } + + if (ret) { + if (!err_is_set) { + set_last_error(io, ret); + } + + free(uvr); + + } else { + incref_sock(sock); + } + + return ret; +} + +/****************************************************************************** + ****************************************************************************** + ** Write Functions ** + ****************************************************************************** + ******************************************************************************/ +static void write2_callback(uv_write_t *req, int status) +{ + my_write_t *mw = (my_write_t *)req; + my_sockdata_t *sock = mw->sock; + + if (status != 0) { + set_last_error((my_iops_t *)sock->base.parent, status); + } + + mw->callback(&sock->base, status, mw->w.data); + free(mw); +} + +static int start_write2(lcb_io_opt_t iobase, + lcb_sockdata_t *sockbase, + struct lcb_iovec_st *iov, + lcb_size_t niov, + void *uarg, + lcb_ioC_write2_callback callback) +{ + my_write_t *w; + my_sockdata_t *sd = (my_sockdata_t *)sockbase; + int ret; + + w = calloc(1, sizeof(*w)); + w->w.data = uarg; + w->callback = callback; + w->sock = sd; + + ret = uv_write(&w->w, (uv_stream_t *)&sd->tcp, + (uv_buf_t *)iov, + niov, + write2_callback); + + if (ret != 0) { + free(w); + set_last_error((my_iops_t *)iobase, -1); + } + + return ret; +} + +/****************************************************************************** + ****************************************************************************** + ** Read Functions ** + ****************************************************************************** + ******************************************************************************/ + +/** + * Currently we support a single IOV. In theory while we could support + * multiple IOVs, two problems arise: + * + * (1) Because UV does not guarantee that it'll utilize the first IOV completely + * we may end up having a gap of unused space between IOVs. This may be + * resolved by keeping an offset into the last-returned IOV and then + * determining how much of this data was actually populated by UV itself. + * + * (2) In the event of an error, UV gives us "Undefined" behavior if we try + * to utilize the socket again. The IOPS policy dictates that we deliver + * any outstanding data to libcouchbase and _then_ deliver the pending + * error. If we are forced to do this all in a single go, we'd be forced + * to set up an 'async handle' to deliver the pending error, complicating + * our code paths. + */ + +static UVC_ALLOC_CB(alloc_cb) +{ + UVC_ALLOC_CB_VARS() + + my_sockdata_t *sock = PTR_FROM_FIELD(my_sockdata_t, handle, tcp); + buf->base = sock->iov.iov_base; + buf->len = sock->iov.iov_len; + + (void)suggested_size; + UVC_ALLOC_CB_RETURN(); +} + +static UVC_READ_CB(read_cb) +{ + UVC_READ_CB_VARS() + + my_tcp_t *mt = (my_tcp_t *)stream; + my_sockdata_t *sock = PTR_FROM_FIELD(my_sockdata_t, mt, tcp); + my_iops_t *io = (my_iops_t *)sock->base.parent; + lcb_ioC_read2_callback callback = CbREQ(mt); + + if (nread == 0) { + /* we have a fixed IOV between requests, so just retry again */ + return; + } + + /** + * XXX: + * For multi-IOV support, we would require a counter to determine if this + * EAGAIN is spurious (i.e. no previous data in buffer), or actual. In + * the case of the former, we'd retry -- but in the latter it is a signal + * that there is no more pending data within the socket buffer AND we have + * outstanding data to deliver back to the caller. + */ + SOCK_DECR_PENDING(sock, read); + uv_read_stop(stream); + CbREQ(mt) = NULL; + + if (nread < 0) { + set_last_error(io, uvc_last_errno(io->loop, nread)); + if (uvc_is_eof(io->loop, nread)) { + nread = 0; + } + } + callback(&sock->base, nread, sock->rdarg); + decref_sock(sock); + (void)buf; +} + +static int start_read(lcb_io_opt_t iobase, + lcb_sockdata_t *sockbase, + lcb_IOV *iov, + lcb_size_t niov, + void *uarg, + lcb_ioC_read2_callback callback) +{ + my_sockdata_t *sock = (my_sockdata_t *)sockbase; + my_iops_t *io = (my_iops_t *)iobase; + int ret; + + sock->iov = *iov; + sock->rdarg = uarg; + sock->tcp.callback = callback; + + ret = uv_read_start((uv_stream_t *)&sock->tcp.t, alloc_cb, read_cb); + set_last_error(io, ret); + + if (ret == 0) { + SOCK_INCR_PENDING(sock, read); + incref_sock(sock); + } + return ret; +} + +static int get_nameinfo(lcb_io_opt_t iobase, + lcb_sockdata_t *sockbase, + struct lcb_nameinfo_st *ni) +{ + my_sockdata_t *sock = (my_sockdata_t *)sockbase; + my_iops_t *io = (my_iops_t *)iobase; + uv_tcp_getpeername(&sock->tcp.t, ni->remote.name, ni->remote.len); + uv_tcp_getsockname(&sock->tcp.t, ni->local.name, ni->local.len); + + (void)io; + return 0; +} + +/****************************************************************************** + ****************************************************************************** + ** Timer Functions ** + ** There are just copied from the old couchnode I/O code ** + ****************************************************************************** + ******************************************************************************/ +static UVC_TIMER_CB(timer_cb) +{ + my_timer_t *mytimer = (my_timer_t *)timer; + if (mytimer->callback) { + mytimer->callback(-1, 0, mytimer->cb_arg); + } +} + +static void *create_timer(lcb_io_opt_t iobase) +{ + my_iops_t *io = (my_iops_t *)iobase; + my_timer_t *timer = calloc(1, sizeof(*timer)); + if (!timer) { + return NULL; + } + + timer->parent = io; + incref_iops(io); + uv_timer_init(io->loop, &timer->uvt); + + return timer; +} + +static int update_timer(lcb_io_opt_t iobase, + void *timer_opaque, + lcb_uint32_t usec, + void *cbdata, + v0_callback_t callback) +{ + my_timer_t *timer = (my_timer_t *)timer_opaque; + + timer->callback = callback; + timer->cb_arg = cbdata; + + (void)iobase; + + return uv_timer_start(&timer->uvt, timer_cb, usec / 1000, 0); +} + +static void delete_timer(lcb_io_opt_t iobase, void *timer_opaque) +{ + my_timer_t *timer = (my_timer_t *)timer_opaque; + + uv_timer_stop(&timer->uvt); + timer->callback = NULL; + + (void)iobase; +} + +static void timer_close_cb(uv_handle_t *handle) +{ + my_timer_t *timer = (my_timer_t *)handle; + decref_iops(&timer->parent->base); + memset(timer, 0xff, sizeof(*timer)); + free(timer); +} + +static void destroy_timer(lcb_io_opt_t io, void *timer_opaque) +{ + delete_timer(io, timer_opaque); + uv_close((uv_handle_t *)timer_opaque, timer_close_cb); +} + +static my_uvreq_t *alloc_uvreq(my_sockdata_t *sock, generic_callback_t callback) +{ + my_uvreq_t *ret = calloc(1, sizeof(*ret)); + if (!ret) { + sock->base.parent->v.v1.error = ENOMEM; + return NULL; + } + ret->socket = sock; + ret->cb.cb_ = callback; + return ret; +} + + +static void set_last_error(my_iops_t *io, int error) +{ + io->base.v.v1.error = uvc_last_errno(io->loop, error); +} + +static void wire_iops2(int version, + lcb_loop_procs *loop, + lcb_timer_procs *timer, + lcb_bsd_procs *bsd, + lcb_ev_procs *ev, + lcb_completion_procs *iocp, + lcb_iomodel_t *model) +{ + *model = LCB_IOMODEL_COMPLETION; + loop->start = run_event_loop; + loop->stop = stop_event_loop; + loop->tick = tick_event_loop; + + timer->create = create_timer; + timer->cancel = delete_timer; + timer->schedule = update_timer; + timer->destroy = destroy_timer; + + iocp->close = close_socket; + iocp->socket = create_socket; + iocp->connect = start_connect; + iocp->nameinfo = get_nameinfo; + iocp->read2 = start_read; + iocp->write2 = start_write2; + iocp->cntl = cntl_socket; + + /** Stuff we don't use */ + iocp->write = NULL; + iocp->wballoc = NULL; + iocp->wbfree = NULL; + iocp->serve = NULL; + + (void)bsd; + (void)version; + (void)ev; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/select/CMakeLists.txt b/couchbase-sys/libcouchbase-2.7.0/plugins/io/select/CMakeLists.txt new file mode 100644 index 00000000..e72d8c1c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/select/CMakeLists.txt @@ -0,0 +1,11 @@ +ADD_LIBRARY(couchbase_select OBJECT plugin-select.c) +ADD_DEFINITIONS(-DLIBCOUCHBASE_INTERNAL=1) +SET_TARGET_PROPERTIES(couchbase_select + PROPERTIES + COMPILE_FLAGS "${CMAKE_C_FLAGS} ${LCB_CORE_CFLAGS}" + POSITION_INDEPENDENT_CODE TRUE) +INSTALL( + FILES + select_io_opts.h + DESTINATION + include/libcouchbase/) diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/select/plugin-select.c b/couchbase-sys/libcouchbase-2.7.0/plugins/io/select/plugin-select.c new file mode 100644 index 00000000..9c203c24 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/select/plugin-select.c @@ -0,0 +1,448 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LCB_IOPS_V12_NO_DEPRECATE + +#include "internal.h" +#include "select_io_opts.h" +#include + +#if defined(_WIN32) && !defined(usleep) +#define usleep(n) Sleep((n) / 1000) +#endif + +typedef struct sel_EVENT sel_EVENT; +struct sel_EVENT { + lcb_list_t list; + lcb_socket_t sock; + short flags; + short eflags; /* effective flags */ + void *cb_data; + lcb_ioE_callback handler; + sel_EVENT *next; /* for chaining active events */ +}; + +typedef struct sel_TIMER sel_TIMER; +struct sel_TIMER { + lcb_list_t list; + int active; + hrtime_t exptime; + void *cb_data; + lcb_ioE_callback handler; +}; + +typedef struct { + sel_EVENT events; + lcb_list_t timers; + int event_loop; +} sel_LOOP; + +static int +timer_cmp_asc(lcb_list_t *a, lcb_list_t *b) +{ + sel_TIMER *ta = LCB_LIST_ITEM(a, sel_TIMER, list); + sel_TIMER *tb = LCB_LIST_ITEM(b, sel_TIMER, list); + if (ta->exptime > tb->exptime) { + return 1; + } else if (ta->exptime < tb->exptime) { + return -1; + } else { + return 0; + } +} + +static void * +sel_event_new(lcb_io_opt_t iops) +{ + sel_LOOP *io = iops->v.v2.cookie; + sel_EVENT *ret = calloc(1, sizeof(sel_EVENT)); + if (ret != NULL) { + lcb_list_append(&io->events.list, &ret->list); + } + return ret; +} + +static int +sel_event_update(lcb_io_opt_t iops, lcb_socket_t sock, void *event, short flags, + void *cb_data, lcb_ioE_callback handler) +{ + sel_EVENT *ev = event; + ev->sock = sock; + ev->handler = handler; + ev->cb_data = cb_data; + ev->flags = flags; + (void)iops; + return 0; +} + +static void +sel_event_free(lcb_io_opt_t iops, void *event) +{ + sel_EVENT *ev = event; + lcb_list_delete(&ev->list); + free(ev); + (void)iops; +} + +static void +sel_event_cancel(lcb_io_opt_t iops, lcb_socket_t sock, void *event) +{ + sel_EVENT *ev = event; + ev->flags = 0; + ev->cb_data = NULL; + ev->handler = NULL; + (void)iops; + (void)sock; +} + +static void * +sel_timer_new(lcb_io_opt_t iops) +{ + sel_TIMER *ret = calloc(1, sizeof(sel_TIMER)); + (void)iops; + return ret; +} + +static void +sel_timer_cancel(lcb_io_opt_t iops, void *timer) +{ + sel_TIMER *tm = timer; + if (tm->active) { + tm->active = 0; + lcb_list_delete(&tm->list); + } + (void)iops; +} + + +static void sel_timer_free(lcb_io_opt_t iops, void *timer) +{ + sel_timer_cancel(iops, timer); + free(timer); + (void)iops; +} + +static int +sel_timer_schedule(lcb_io_opt_t iops, void *timer, lcb_U32 usec, void *cb_data, + lcb_ioE_callback handler) +{ + sel_TIMER *tm = timer; + sel_LOOP *cookie = iops->v.v2.cookie; + lcb_assert(!tm->active); + tm->exptime = gethrtime() + (usec * (hrtime_t)1000); + tm->cb_data = cb_data; + tm->handler = handler; + tm->active = 1; + lcb_list_add_sorted(&cookie->timers, &tm->list, timer_cmp_asc); + + (void)iops; + return 0; +} + +static void +sel_stop_loop(struct lcb_io_opt_st *iops) +{ + sel_LOOP *io = iops->v.v2.cookie; + io->event_loop = 0; +} + +static sel_TIMER * +pop_next_timer(sel_LOOP *cookie, hrtime_t now) +{ + sel_TIMER *ret; + + if (LCB_LIST_IS_EMPTY(&cookie->timers)) { + return NULL; + } + + ret = LCB_LIST_ITEM(cookie->timers.next, sel_TIMER, list); + if (ret->exptime > now) { + return NULL; + } + lcb_list_shift(&cookie->timers); + ret->active = 0; + return ret; +} + +static int +get_next_timeout(sel_LOOP *cookie, struct timeval *tmo, hrtime_t now) +{ + sel_TIMER *first; + hrtime_t delta; + + if (LCB_LIST_IS_EMPTY(&cookie->timers)) { + tmo->tv_sec = 0; + tmo->tv_usec = 0; + return 0; + } + + first = LCB_LIST_ITEM(cookie->timers.next, sel_TIMER, list); + if (now < first->exptime) { + delta = first->exptime - now; + } else { + delta = 0; + } + + + if (delta) { + delta /= 1000; + tmo->tv_sec = (long)(delta / 1000000); + tmo->tv_usec = delta % 1000000; + } else { + tmo->tv_sec = 0; + tmo->tv_usec = 0; + } + return 1; +} + +static void +run_loop(sel_LOOP *io, int is_tick) +{ + sel_EVENT *ev; + lcb_list_t *ii; + + fd_set readfds, writefds, exceptfds; + + io->event_loop = !is_tick; + do { + struct timeval tmo, *t; + int ret; + int nevents = 0; + int has_timers; + lcb_socket_t fdmax = 0; + hrtime_t now; + + t = NULL; + now = gethrtime(); + + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + + LCB_LIST_FOR(ii, &io->events.list) { + ev = LCB_LIST_ITEM(ii, sel_EVENT, list); + if (ev->flags != 0) { + if (ev->flags & LCB_READ_EVENT) { + FD_SET(ev->sock, &readfds); + } + + if (ev->flags & LCB_WRITE_EVENT) { + FD_SET(ev->sock, &writefds); + } + + FD_SET(ev->sock, &exceptfds); + if (ev->sock > fdmax) { + fdmax = ev->sock; + } + ++nevents; + } + } + + has_timers = get_next_timeout(io, &tmo, now); + if (has_timers && !is_tick) { + t = &tmo; + } + + + if (nevents == 0 && has_timers == 0) { + io->event_loop = 0; + return; + } + + if (nevents) { + ret = select(fdmax + 1, &readfds, &writefds, &exceptfds, t); + if (ret == SOCKET_ERROR) { + return; + } + } else { + ret = 0; + if (!is_tick) { + usleep((t->tv_sec * 1000000) + t->tv_usec); + } + } + + + /** Always invoke the pending timers */ + if (has_timers) { + sel_TIMER *tm; + now = gethrtime(); + + while ((tm = pop_next_timer(io, now))) { + tm->handler(-1, 0, tm->cb_data); + } + } + + /* To be completely safe, we need to copy active events + * before handing them. Iterating over the list of + * registered events isn't safe, because one callback can + * cancel all registered events before iteration will end + */ + + if (ret && nevents) { + sel_EVENT *active = NULL; + LCB_LIST_FOR(ii, &io->events.list) { + ev = LCB_LIST_ITEM(ii, sel_EVENT, list); + if (ev->flags != 0) { + ev->eflags = 0; + if (FD_ISSET(ev->sock, &readfds)) { + ev->eflags |= LCB_READ_EVENT; + } + if (FD_ISSET(ev->sock, &writefds)) { + ev->eflags |= LCB_WRITE_EVENT; + } + if (FD_ISSET(ev->sock, &exceptfds)) { + ev->eflags = LCB_ERROR_EVENT | LCB_RW_EVENT; /** It should error */ + } + if (ev->eflags != 0) { + ev->next = active; + active = ev; + } + } + } + ev = active; + while (ev) { + sel_EVENT *p = ev->next; + ev->handler(ev->sock, ev->eflags, ev->cb_data); + ev = p; + } + } + } while (io->event_loop); +} + +static void +sel_run_loop(struct lcb_io_opt_st *iops) +{ + run_loop(iops->v.v2.cookie, 0); +} +static void +sel_tick_loop(struct lcb_io_opt_st *iops) +{ + run_loop(iops->v.v2.cookie, 1); +} + +static void +sel_destroy_iops(struct lcb_io_opt_st *iops) +{ + sel_LOOP *io = iops->v.v2.cookie; + lcb_list_t *nn, *ii; + sel_EVENT *ev; + sel_TIMER *tm; + + assert(io->event_loop == 0); + LCB_LIST_SAFE_FOR(ii, nn, &io->events.list) { + ev = LCB_LIST_ITEM(ii, sel_EVENT, list); + sel_event_free(iops, ev); + } + assert(LCB_LIST_IS_EMPTY(&io->events.list)); + LCB_LIST_SAFE_FOR(ii, nn, &io->timers) { + tm = LCB_LIST_ITEM(ii, sel_TIMER, list); + sel_timer_free(iops, tm); + } + assert(LCB_LIST_IS_EMPTY(&io->timers)); + free(io); + free(iops); +} + +static lcb_socket_t +sel_socket_wrap(lcb_io_opt_t io, int domain, int type, int protocol) +{ + lcb_socket_t res = socket_impl(io, domain, type, protocol); +#ifndef _WIN32 + + /* This only works on non-Windows where FD_SETSIZE is in effect wrt the + * actual FD number. On Windows, FD_SETSIZE is the cap on the _total_ + * number of sockets to be used in select; not necessarily what their + * FD values are. + * + * TODO: Just use poll() on POSIX in the future. + */ + if (res != INVALID_SOCKET && res > FD_SETSIZE) { + close_impl(io, res); + fprintf(stderr, "COUCHBASE: too many FDs. Cannot have socket > FD_SETSIZE. Use other I/O plugin\n"); + io->v.v3.error = EINVAL; + res = INVALID_SOCKET; + } +#endif + return res; +} + +static void +procs2_sel_callback(int version, lcb_loop_procs *loop_procs, + lcb_timer_procs *timer_procs, lcb_bsd_procs *bsd_procs, + lcb_ev_procs *ev_procs, lcb_completion_procs *completion_procs, + lcb_iomodel_t *iomodel) +{ + ev_procs->create = sel_event_new; + ev_procs->destroy = sel_event_free; + ev_procs->watch = sel_event_update; + ev_procs->cancel = sel_event_cancel; + + timer_procs->create = sel_timer_new; + timer_procs->destroy = sel_timer_free; + timer_procs->schedule = sel_timer_schedule; + timer_procs->cancel = sel_timer_cancel; + + loop_procs->start = sel_run_loop; + loop_procs->stop = sel_stop_loop; + loop_procs->tick = sel_tick_loop; + + *iomodel = LCB_IOMODEL_EVENT; + wire_lcb_bsd_impl2(bsd_procs, version); + + /* Override */ + bsd_procs->socket0 = sel_socket_wrap; + (void)completion_procs; +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_create_select_io_opts(int version, lcb_io_opt_t *io, void *arg) +{ + lcb_io_opt_t ret; + sel_LOOP *cookie; + + if (version != 0) { + return LCB_PLUGIN_VERSION_MISMATCH; + } + ret = calloc(1, sizeof(*ret)); + cookie = calloc(1, sizeof(*cookie)); + if (ret == NULL || cookie == NULL) { + free(ret); + free(cookie); + return LCB_CLIENT_ENOMEM; + } + lcb_list_init(&cookie->events.list); + lcb_list_init(&cookie->timers); + + /* setup io iops! */ + ret->version = 3; + ret->dlhandle = NULL; + ret->destructor = sel_destroy_iops; + + /* consider that struct isn't allocated by the library, + * `need_cleanup' flag might be set in lcb_create() */ + ret->v.v3.need_cleanup = 0; + ret->v.v3.get_procs = procs2_sel_callback; + ret->v.v3.cookie = cookie; + + /* For backwards compatibility */ + wire_lcb_bsd_impl(ret); + + *io = ret; + (void)arg; + return LCB_SUCCESS; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/plugins/io/select/select_io_opts.h b/couchbase-sys/libcouchbase-2.7.0/plugins/io/select/select_io_opts.h new file mode 100644 index 00000000..3502aa7f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/plugins/io/select/select_io_opts.h @@ -0,0 +1,39 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBCOUCHBASE_SELECT_IO_OPTS_H +#define LIBCOUCHBASE_SELECT_IO_OPTS_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * Create an instance of an event handler that utilize libev for + * event notification. + * + * @return status of the operation + */ + LIBCOUCHBASE_API + lcb_error_t lcb_create_select_io_opts(int version, lcb_io_opt_t *io, void *loop); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/README.md b/couchbase-sys/libcouchbase-2.7.0/src/README.md new file mode 100644 index 00000000..647b8d0d --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/README.md @@ -0,0 +1,103 @@ +# Internal source code structure + +This directory contains the source code for libcouchbase. Here is a brief +listing of the various subcomponents and what they do: + +* `internal.h` contains the top level internal declarations. + +* `config_static.h` contains statically inferred (via macros) platform information. + +* `instance.c` contains functions related to creating and destroying the actual + `lcb_t` handle. It also contains some convenience functions. + +* `bootstrap.{c,h}` contains the top-level logic for _retrieving_ the cluster + configuration + +* `newconfig.c` contains the logic for _applying_ a new configuration + +* `bucketconfig/*` is a directory which contains the low level transport logic used + to retrieve a new configuration + +* `callbacks.c` contains the functions used to implement the library's `set_callback` + accessor functions. + +* `operations/*` is a directory which contains the top level entry points for memcached + requests + +* `handler.c` contains the response handlers for memcached. + +* `getconfig.c` contains the implementation for requesting a config from an existing + server. + +* `connspec.{c,h}` contains the connection string parsing logic + +* `cntl.c` contains the handlers for the `lcb_cntl()` family of functions + +* `dump.c` contains the handlers for the `lcb_dump()` function + +* `gethrtime.c` contains platform-dependent implementations of a nanosecond timer + +* `hashset.{c,h}` contains the implementation for a set (unique collection of pointers) + +* `list.{c,h}` contains the implementation for a double-linked list + +* `sllist.h, sllist-inl.h` contain the implementation for a single-linked list + +* `simplestring.{c,h}` contains the implementation for a dynamically sized string + +* `logging.{c,h}` contains the implementation for the library's logging mechanism + +* `hostlist.{c,h}` defines a list of hosts, with features for de-duping and converting + to other structures + +* `nodeinfo.c` contains the implementation for the `lcb_get_node()` function. + +* `packetutils.{c,h}` contains utilities and macros for handling partial memcached + response packets + +* `wait.c` contains the implementation of `lcb_wait()` + +* `timings.c` contains the implementation of `lcb_get_timings()` + +* `trace.h` contains macros for DTrace functionality + +* `utilities.c` contains cross-platform utilities (such as temporary directory, + getting environment variables) + +* `iofactory.c` contains the plugin intialization/loading functionality for I/O + plugins + +* `retrychk.c` contains logic which determines if a rety is necessary under certain + conditions + +* `retryq.{c,h}` contains an internal retried operations, and are placed there if they + are eligible for retries. + +* `aspend.h` contains definitions for pending operations which are meant to block + calls to `lcb_wait()` (implementation in instance.c) + +* `lcbio/*` is a directory which contains the cross platform/cross model IO + implementation. Most I/O is done in this subdirectory + +* `http/*` contains the API implementation for user-level HTTP requests + +* `lcbht/*` contains an HTTP response parsing API/implementation + +* `mc/*` contains the memcached/Couchbase structure and packet/buffer allocation + and scheduling logic. + +* `mcserver/*` contains the operation/failure/IO logic for memcached connections + +* `vbucket/*` contains the raw vBucket config parsing and hashing/mapping implementation + (formerly known as "libvbucket") + +* `rdb/*` contains an extensible pooled read buffer implementation + +* `netbuf/*` contains an extensible high performance output buffer implementation + +* `rigbuffer.{c,h}` contains a circular buffer implementation. + +* `ssl/*` contains the OpenSSL interfacing routines + +* `strcodecs/*` contains utility functions to encode/decode strings to/from + various formats. diff --git a/couchbase-sys/libcouchbase-2.7.0/src/aspend.h b/couchbase-sys/libcouchbase-2.7.0/src/aspend.h new file mode 100644 index 00000000..4debdb38 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/aspend.h @@ -0,0 +1,105 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_ASPEND_H +#define LCB_ASPEND_H + +#ifdef __cplusplus +#include +typedef std::set lcb_ASPEND_SETTYPE; +#else +typedef void lcb_ASPEND_SETTYPE; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page Asynchronous Pending Queue + * + * This defines the API for asynchronous requests which should block calls to + * lcb_wait() or similar. This is a replacement for the explicit hashsets used + * in lcb_t. + * + * Items are added to the pending queue via lcb_aspend_add(). They may be + * removed either explicitly via lcb_aspend_del() or implicitly when the + * instance is destroyed. + * + * An exception to this rule is the special LCB_PENDTYPE_COUNTER which does + * not associate a specific pointer with it. + */ + +/** Pending item type */ +typedef enum { + LCB_PENDTYPE_TIMER = 0, /**< item is of type lcb_timer_t */ + LCB_PENDTYPE_HTTP, /**< item is of type lcb_http_request_t */ + LCB_PENDTYPE_DURABILITY, /**< item is of type lcb_durability_set_t */ + LCB_PENDTYPE_COUNTER, /**< just increment/decrement the counter */ + LCB_PENDTYPE_MAX +} lcb_ASPENDTYPE; + + +/** Items for pending operations */ +typedef struct { + lcb_ASPEND_SETTYPE* items[LCB_PENDTYPE_MAX]; + unsigned count; +} lcb_ASPEND; + +/** + * Initialize the pending queues + * @param ops + */ +void lcb_aspend_init(lcb_ASPEND *ops); + +/** + * Clean up any resources used by the pending queues + * @param ops + */ +void lcb_aspend_cleanup(lcb_ASPEND *ops); + +/** + * Add an opaque pointer of a given type to a pending queue + * @param ops + * @param type The type of pointer to add + * @param item The item to add + */ +void lcb_aspend_add(lcb_ASPEND *ops, lcb_ASPENDTYPE type, const void *item); + +/** + * Remove an item from the queue and decrement the pending count + * @param ops + * @param type The type of item to remove + * @param item The item to remove + * + * @attention If the item is not found inside the queue then the count is + * _not_ decremented. An exception to this rule is the LCB_PENDTYPE_COUNTER + * type which does not have a pointer associated with it. In this case the + * counter is always decremented. + */ +void lcb_aspend_del(lcb_ASPEND *ops, lcb_ASPENDTYPE type, const void *item); + +/** + * Determine whether there are pending items in any of the queues + * @param ops + */ +#define lcb_aspend_pending(ops) ((ops)->count > 0) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/auth-priv.h b/couchbase-sys/libcouchbase-2.7.0/src/auth-priv.h new file mode 100644 index 00000000..d5051da8 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/auth-priv.h @@ -0,0 +1,34 @@ +#ifndef LCB_AUTH_PRIV_H +#define LCB_AUTH_PRIV_H +#include + +#ifdef __cplusplus +#include +#include + +namespace lcb { +class Authenticator { +public: + typedef std::map Map; + const std::string& username() const { return m_username; } + const std::string& password() const { return m_password; } + const Map& buckets() const { return m_buckets; } + Authenticator() : m_refcount(1) {} + + size_t refcount() const { return m_refcount; } + void incref() { ++m_refcount; } + void decref() { if (!--m_refcount) { delete this; } } + lcb_error_t add(const char *user, const char *pass, int flags); + lcb_error_t init(const std::string& username_, const std::string& bucket, + const std::string& password, lcb_type_t conntype); + +private: + // todo: refactor these out + Map m_buckets; + std::string m_username; + std::string m_password; + size_t m_refcount; +}; +} +#endif +#endif /* LCB_AUTH_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/auth.cc b/couchbase-sys/libcouchbase-2.7.0/src/auth.cc new file mode 100644 index 00000000..65265b3a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/auth.cc @@ -0,0 +1,99 @@ +#include +#include "auth-priv.h" + +using namespace lcb; + +lcb_AUTHENTICATOR * +lcbauth_new() +{ + return new Authenticator(); +} + +const char * +lcbauth_get_bpass(const lcb_AUTHENTICATOR *auth, const char *u) +{ + Authenticator::Map::const_iterator ii = auth->buckets().find(u); + if (ii == auth->buckets().end()) { + return NULL; + } + return ii->second.c_str(); +} + +lcb_error_t +lcbauth_add_pass(lcb_AUTHENTICATOR *auth, const char *u, const char *p, int flags) +{ + return auth->add(u, p, flags); +} + +lcb_error_t +Authenticator::add(const char *u, const char *p, int flags) +{ + if (!flags) { + return LCB_EINVAL; + } + + if (flags & LCBAUTH_F_CLUSTER) { + if (!p) { + m_username.clear(); + m_password.clear(); + } else { + m_username = u; + m_password = p; + } + } + if (flags & LCBAUTH_F_BUCKET) { + if (!p) { + m_buckets.erase(u); + } else { + m_buckets[u] = p; + } + } + return LCB_SUCCESS; +} + +void +lcbauth_get_upass(const lcb_AUTHENTICATOR *auth, const char **u, const char **p) +{ + if (!auth->username().empty()) { + *u = auth->username().c_str(); + *p = auth->password().empty() ? NULL : auth->password().c_str(); + + } else if (!auth->buckets().empty()) { + Authenticator::Map::const_iterator it = auth->buckets().begin(); + *u = it->first.c_str(); + if (!it->second.empty()) { + *p = it->second.c_str(); + } else { + *p = NULL; + } + } else { + *u = *p = NULL; + } +} + +void +lcbauth_ref(lcb_AUTHENTICATOR *auth) +{ + auth->incref(); +} + +void +lcbauth_unref(lcb_AUTHENTICATOR *auth) +{ + auth->decref(); +} + +lcb_error_t +Authenticator::init(const std::string& username_, const std::string& bucket, + const std::string& passwd, lcb_type_t conntype) +{ + m_username = (!username_.empty()) ? username_ : bucket; + m_password = passwd; + + if (conntype == LCB_TYPE_BUCKET && m_username != bucket) { + return LCB_INVALID_USERNAME; + } + + m_buckets[bucket] = m_password; + return LCB_SUCCESS; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/bootstrap.c b/couchbase-sys/libcouchbase-2.7.0/src/bootstrap.c new file mode 100644 index 00000000..fd00bf2c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/bootstrap.c @@ -0,0 +1,269 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LCB_BOOTSTRAP_DEFINE_STRUCT 1 +#include "internal.h" + + +#define LOGARGS(instance, lvl) instance->settings, "bootstrap", LCB_LOG_##lvl, __FILE__, __LINE__ + +static void async_step_callback(clconfig_listener*,clconfig_event_t,clconfig_info*); +static void initial_bootstrap_error(lcb_t, lcb_error_t,const char*); + +/** + * This function is where the configuration actually takes place. We ensure + * in other functions that this is only ever called directly from an event + * loop stack frame (or one of the small mini functions here) so that we + * don't accidentally end up destroying resources underneath us. + */ +static void +config_callback(clconfig_listener *listener, clconfig_event_t event, + clconfig_info *info) +{ + struct lcb_BOOTSTRAP *bs = (struct lcb_BOOTSTRAP *)listener; + lcb_t instance = bs->parent; + + if (event != CLCONFIG_EVENT_GOT_NEW_CONFIG) { + if (event == CLCONFIG_EVENT_PROVIDERS_CYCLED) { + if (!LCBT_VBCONFIG(instance)) { + initial_bootstrap_error( + instance, LCB_ERROR, "No more bootstrap providers remain"); + } + } + return; + } + + instance->last_error = LCB_SUCCESS; + /** Ensure we're not called directly twice again */ + listener->callback = async_step_callback; + lcbio_timer_disarm(bs->tm); + + lcb_log(LOGARGS(instance, DEBUG), "Instance configured!"); + + if (info->origin != LCB_CLCONFIG_FILE) { + /* Set the timestamp for the current config to control throttling, + * but only if it's not an initial file-based config. See CCBC-482 */ + bs->last_refresh = gethrtime(); + bs->errcounter = 0; + } + + if (info->origin == LCB_CLCONFIG_CCCP) { + /* Disable HTTP provider if we've received something via CCCP */ + + if (instance->cur_configinfo == NULL || + instance->cur_configinfo->origin != LCB_CLCONFIG_HTTP) { + /* Never disable HTTP if it's still being used */ + lcb_confmon_set_provider_active( + instance->confmon, LCB_CLCONFIG_HTTP, 0); + } + } + + if (instance->type != LCB_TYPE_CLUSTER) { + lcb_update_vbconfig(instance, info); + } + + if (!bs->bootstrapped) { + bs->bootstrapped = 1; + lcb_aspend_del(&instance->pendops, LCB_PENDTYPE_COUNTER, NULL); + + if (instance->type == LCB_TYPE_BUCKET && + LCBVB_DISTTYPE(LCBT_VBCONFIG(instance)) == LCBVB_DIST_KETAMA && + instance->cur_configinfo->origin != LCB_CLCONFIG_MCRAW) { + + lcb_log(LOGARGS(instance, INFO), "Reverting to HTTP Config for memcached buckets"); + instance->settings->bc_http_stream_time = -1; + lcb_confmon_set_provider_active( + instance->confmon, LCB_CLCONFIG_HTTP, 1); + lcb_confmon_set_provider_active( + instance->confmon, LCB_CLCONFIG_CCCP, 0); + } + instance->callbacks.bootstrap(instance, LCB_SUCCESS); + } + + lcb_maybe_breakout(instance); +} + + +static void +initial_bootstrap_error(lcb_t instance, lcb_error_t err, const char *errinfo) +{ + struct lcb_BOOTSTRAP *bs = instance->bootstrap; + + instance->last_error = lcb_confmon_last_error(instance->confmon); + if (instance->last_error == LCB_SUCCESS) { + instance->last_error = err; + } + instance->callbacks.error(instance, instance->last_error, errinfo); + lcb_log(LOGARGS(instance, ERR), "Failed to bootstrap client=%p. Code=0x%x, Message=%s", (void *)instance, err, errinfo); + lcbio_timer_disarm(bs->tm); + + instance->callbacks.bootstrap(instance, instance->last_error); + + lcb_aspend_del(&instance->pendops, LCB_PENDTYPE_COUNTER, NULL); + lcb_maybe_breakout(instance); +} + +/** + * This it the initial bootstrap timeout handler. This timeout pins down the + * instance. It is only scheduled during the initial bootstrap and is only + * triggered if the initial bootstrap fails to configure in time. + */ +static void initial_timeout(void *arg) +{ + struct lcb_BOOTSTRAP *bs = arg; + initial_bootstrap_error(bs->parent, LCB_ETIMEDOUT, "Failed to bootstrap in time"); +} + +/** + * Proxy async call to config_callback + */ +static void async_refresh(void *arg) +{ + /** Get the best configuration and run stuff.. */ + struct lcb_BOOTSTRAP *bs = arg; + clconfig_info *info; + + info = lcb_confmon_get_config(bs->parent->confmon); + config_callback(&bs->listener, CLCONFIG_EVENT_GOT_NEW_CONFIG, info); +} + +/** + * set_next listener callback which schedules an async call to our config + * callback. + */ +static void +async_step_callback(clconfig_listener *listener, clconfig_event_t event, + clconfig_info *info) +{ + struct lcb_BOOTSTRAP *bs = (struct lcb_BOOTSTRAP *)listener; + + if (event != CLCONFIG_EVENT_GOT_NEW_CONFIG) { + return; + } + + if (lcbio_timer_armed(bs->tm) && lcbio_timer_get_target(bs->tm) == async_refresh) { + lcb_log(LOGARGS(bs->parent, DEBUG), "Timer already present.."); + return; + } + + lcb_log(LOGARGS(bs->parent, INFO), "Got async step callback.."); + lcbio_timer_set_target(bs->tm, async_refresh); + lcbio_async_signal(bs->tm); + (void)info; +} + +lcb_error_t +lcb_bootstrap_common(lcb_t instance, int options) +{ + struct lcb_BOOTSTRAP *bs = instance->bootstrap; + hrtime_t now = gethrtime(); + + if (!bs) { + bs = calloc(1, sizeof(*instance->bootstrap)); + if (!bs) { + return LCB_CLIENT_ENOMEM; + } + + bs->tm = lcbio_timer_new(instance->iotable, bs, initial_timeout); + instance->bootstrap = bs; + bs->parent = instance; + lcb_confmon_add_listener(instance->confmon, &bs->listener); + } + + if (lcb_confmon_is_refreshing(instance->confmon)) { + return LCB_SUCCESS; + } + + if (options & LCB_BS_REFRESH_THROTTLE) { + /* Refresh throttle requested. This is not true if options == ALWAYS */ + hrtime_t next_ts; + unsigned errthresh = LCBT_SETTING(instance, weird_things_threshold); + + if (options & LCB_BS_REFRESH_INCRERR) { + bs->errcounter++; + } + next_ts = bs->last_refresh; + next_ts += LCB_US2NS(LCBT_SETTING(instance, weird_things_delay)); + if (now < next_ts && bs->errcounter < errthresh) { + lcb_log(LOGARGS(instance, INFO), + "Not requesting a config refresh because of throttling parameters. Next refresh possible in %ums or %u errors. " + "See LCB_CNTL_CONFDELAY_THRESH and LCB_CNTL_CONFERRTHRESH to modify the throttling settings", + LCB_NS2US(next_ts-now)/1000, (unsigned)errthresh-bs->errcounter); + return LCB_SUCCESS; + } + } + + if (options == LCB_BS_REFRESH_INITIAL) { + lcb_confmon_prepare(instance->confmon); + + bs->listener.callback = config_callback; + lcbio_timer_set_target(bs->tm, initial_timeout); + lcbio_timer_rearm(bs->tm, LCBT_SETTING(instance, config_timeout)); + lcb_aspend_add(&instance->pendops, LCB_PENDTYPE_COUNTER, NULL); + } else { + /** No initial timer */ + bs->listener.callback = async_step_callback; + } + + /* Reset the counters */ + bs->errcounter = 0; + if (options != LCB_BS_REFRESH_INITIAL) { + bs->last_refresh = now; + } + return lcb_confmon_start(instance->confmon); +} + +void lcb_bootstrap_destroy(lcb_t instance) +{ + struct lcb_BOOTSTRAP *bs = instance->bootstrap; + if (!bs) { + return; + } + if (bs->tm) { + lcbio_timer_destroy(bs->tm); + } + + lcb_confmon_remove_listener(instance->confmon, &bs->listener); + free(bs); + instance->bootstrap = NULL; +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_get_bootstrap_status(lcb_t instance) +{ + if (instance->cur_configinfo) { + return LCB_SUCCESS; + } + if (instance->last_error != LCB_SUCCESS) { + return instance->last_error; + } + if (instance->type == LCB_TYPE_CLUSTER) { + lcbio_SOCKET *restconn = lcb_confmon_get_rest_connection(instance->confmon); + if (restconn) { + return LCB_SUCCESS; + } + } + return LCB_ERROR; +} + +LIBCOUCHBASE_API +void +lcb_refresh_config(lcb_t instance) +{ + lcb_bootstrap_common(instance, LCB_BS_REFRESH_ALWAYS); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/bootstrap.h b/couchbase-sys/libcouchbase-2.7.0/src/bootstrap.h new file mode 100644 index 00000000..3cc10c2f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/bootstrap.h @@ -0,0 +1,129 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_BOOTSTRAP_H +#define LCB_BOOTSTRAP_H +#ifdef __cplusplus +extern "C" { +#endif + +/**@file + * Core bootstrap/cluster configuration routines */ + +/**@defgroup lcb_bootstrap Bootstrap Routines + * @addtogroup lcb_bootstrap + * @{ + */ + +#if defined(__LCB_DOXYGEN__) || defined(LCB_BOOTSTRAP_DEFINE_STRUCT) +#include "bucketconfig/clconfig.h" + +/** + * Structure containing the bootstrap state for the instance. + */ +struct lcb_BOOTSTRAP { + /**Listener object used to react when a new configuration is received. This + * is used for both requested configurations (i.e. an explicit call to + * lcb_bootstrap_common()) as well as unsolicited updates such as + * HTTP streaming configurations or Not-My-Vbucket "Carrier" updates. + */ + clconfig_listener listener; + + lcb_t parent; + + /**Timer used for initial bootstrap as an interval timer, and for subsequent + * updates as an asynchronous event (to allow safe updates and avoid + * reentrancy issues) + */ + lcbio_pTIMER tm; + + /** + * Timestamp indicating the most recent configuration activity. This + * timestamp is used to control throttling, such that the @ref + * LCB_CNTL_CONFDELAY_THRESH setting is applied as an offset to this + * timestamp (accounting for ns-to-us conversion). This flag is set whenever + * + * * A new configuration is received (solicited or unsolicited) + * * A request for a new configuration is made, and the request has not + * been throttled + */ + hrtime_t last_refresh; + + /** + * Counter incremented each time a request is based to lcb_bootstrap_common() + * with the @ref LCB_BS_REFRESH_INCRERR flag, and where the request itself + * had been throttled. This increments the internal error counter and when + * the counter reaches a threshold higher than @ref LCB_CNTL_CONFERRTHRESH + * a new configuration is requested. + * This counter is cleared whenever a new configuration arrives. + */ + unsigned errcounter; + + /** Flag indicating whether the _initial_ configuration has been received */ + int bootstrapped; +}; +#endif + +/** + * These flags control the bootstrap refreshing mode that will take place + * when lcb_bootstrap_common() is invoked. These options may be OR'd with + * each other (with the exception of ::LCB_BS_REFRESH_ALWAYS). + */ +typedef enum { + /** Always fetch a new configuration. No throttling checks are performed */ + LCB_BS_REFRESH_ALWAYS = 0x00, + /** Special mode used to fetch the first configuration */ + LCB_BS_REFRESH_INITIAL = 0x02, + + /** Make the request for a new configuration subject to throttling + * limitations. Currently this will be subject to the interval specified + * in the @ref LCB_CNTL_CONFDELAY_THRESH setting and the @ref + * LCB_CNTL_CONFERRTHRESH setting. If the refresh has been throttled + * the lcb_confmon_is_refreshing() function will return false */ + LCB_BS_REFRESH_THROTTLE = 0x04, + + /** To be used in conjunction with ::LCB_BS_REFRESH_THROTTLE, this will + * increment the error counter in case the current refresh is throttled, + * such that when the error counter reaches the threshold, the throttle + * limitations will expire and a new refresh will take place */ + LCB_BS_REFRESH_INCRERR = 0x08 +} lcb_BSFLAGS; + +/** + * @brief Request that the handle update its configuration. + * + * This function acts as a gateway to the more abstract confmon interface. + * + * @param instance The instance + * @param options A set of options specified as flags, indicating under what + * conditions a new configuration should be refetched. + * + * @return + */ +LCB_INTERNAL_API +lcb_error_t +lcb_bootstrap_common(lcb_t instance, int options); + +void +lcb_bootstrap_destroy(lcb_t instance); + +/**@}*/ + +#ifdef __cplusplus +} +#endif +#endif /* LCB_BOOTSTRAP_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_cccp.cc b/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_cccp.cc new file mode 100644 index 00000000..9731247a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_cccp.cc @@ -0,0 +1,488 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This file contains the CCCP (Cluster Carrier Configuration Protocol) + * implementation of the confmon provider. It utilizes a memcached connection + * to retrieve configuration information. + */ + +#include "internal.h" +#include "clconfig.h" +#include "packetutils.h" +#include "simplestring.h" +#include +#include +#include +#include +#include "ctx-log-inl.h" +#define LOGARGS(cccp, lvl) cccp->parent->settings, "cccp", LCB_LOG_##lvl, __FILE__, __LINE__ +#define LOGFMT "<%s:%s> " +#define LOGID(cccp) get_ctx_host(cccp->ioctx), get_ctx_port(cccp->ioctx) + +#define PROVIDER_SETTING_T(fld) PROVIDER_SETTING(this, fld) + +struct CccpCookie; + +struct CccpProvider : public clconfig_provider { + CccpProvider(lcb_confmon *); + ~CccpProvider(); + + void release_socket(bool can_reuse); + lcb_error_t schedule_next_request(lcb_error_t why, bool can_rollover); + lcb_error_t mcio_error(lcb_error_t why); + lcb_error_t update(const char *host, const char* data); + void request_config(); + void on_config_updated(lcbvb_CONFIG *vbc); + void on_io_read(); + + lcb::Hostlist *nodes; + clconfig_info *config; + bool server_active; + lcbio_pTIMER timer; + lcb_t instance; + lcbio_CONNREQ creq; + lcbio_CTX *ioctx; + CccpCookie *cmdcookie; +}; + +struct CccpCookie { + CccpProvider *parent; + bool ignore_errors; + CccpCookie(CccpProvider *parent_) : parent(parent_), ignore_errors(false) { + } +}; + +static void io_error_handler(lcbio_CTX *, lcb_error_t); +static void io_read_handler(lcbio_CTX *, unsigned nr); +static void on_connected(lcbio_SOCKET *, void*, lcb_error_t, lcbio_OSERR); + +static void +pooled_close_cb(lcbio_SOCKET *sock, int reusable, void *arg) +{ + bool *ru_ex = reinterpret_cast(arg); + lcbio_ref(sock); + if (reusable && *ru_ex) { + lcbio_mgr_put(sock); + } else { + lcbio_mgr_discard(sock); + } +} + +void +CccpProvider::release_socket(bool can_reuse) +{ + if (cmdcookie) { + cmdcookie->ignore_errors = 1; + cmdcookie = NULL; + return; + } + + lcbio_connreq_cancel(&creq); + + if (ioctx) { + lcbio_ctx_close(ioctx, pooled_close_cb, &can_reuse); + ioctx = NULL; + } +} + +lcb_error_t +CccpProvider::schedule_next_request(lcb_error_t err, bool can_rollover) +{ + lcb::Server *server; + lcb_host_t *next_host = nodes->next(can_rollover); + if (!next_host) { + lcbio_timer_disarm(timer); + lcb_confmon_provider_failed(this, err); + server_active = false; + return err; + } + + server = lcb_find_server_by_host(instance, next_host); + if (server) { + CccpCookie *cookie = new CccpCookie(this); + cmdcookie = cookie; + lcb_log(LOGARGS(this, INFO), "Re-Issuing CCCP Command on server struct %p (%s:%s)", (void*)server, next_host->host, next_host->port); + lcbio_timer_rearm(timer, PROVIDER_SETTING_T(config_node_timeout)); + return lcb_getconfig(instance, cookie, server); + + } else { + lcbio_pMGRREQ preq; + + lcb_log(LOGARGS(this, INFO), "Requesting connection to node %s:%s for CCCP configuration", next_host->host, next_host->port); + preq = lcbio_mgr_get( + instance->memd_sockpool, next_host, + PROVIDER_SETTING_T(config_node_timeout), + on_connected, this); + LCBIO_CONNREQ_MKPOOLED(&creq, preq); + } + + server_active = true; + return LCB_SUCCESS; +} + +lcb_error_t +CccpProvider::mcio_error(lcb_error_t err) +{ + if (err != LCB_NOT_SUPPORTED && err != LCB_UNKNOWN_COMMAND) { + lcb_log(LOGARGS(this, ERR), LOGFMT "Got I/O Error=0x%x", LOGID(this), err); + } + + release_socket(err == LCB_NOT_SUPPORTED); + return schedule_next_request(err, false); +} + +static void socket_timeout(void *arg) { + reinterpret_cast(arg)->mcio_error(LCB_ETIMEDOUT); +} + +void lcb_clconfig_cccp_enable(clconfig_provider *pb, lcb_t instance) +{ + CccpProvider *cccp = static_cast(pb); + lcb_assert(pb->type == LCB_CLCONFIG_CCCP); + cccp->instance = instance; + pb->enabled = true; +} + +static hostlist_t get_nodes(const clconfig_provider *pb) { + return static_cast(pb)->nodes; +} + +/** Update the configuration from a server. */ +lcb_error_t +lcb_cccp_update(clconfig_provider *provider, const char *host, const char *data) { + return static_cast(provider)->update(host, data); +} + +lcb_error_t +CccpProvider::update(const char *host, const char *data) +{ + /** TODO: replace this with lcbvb_ names */ + lcbvb_CONFIG* vbc; + int rv; + clconfig_info *new_config; + vbc = lcbvb_create(); + + if (!vbc) { + return LCB_CLIENT_ENOMEM; + } + rv = lcbvb_load_json(vbc, data); + + if (rv) { + lcb_log(LOGARGS(this, ERROR), LOGFMT "Failed to parse config", LOGID(this)); + lcb_log_badconfig(LOGARGS(this, ERROR), vbc, data); + lcbvb_destroy(vbc); + return LCB_PROTOCOL_ERROR; + } + + lcbvb_replace_host(vbc, host); + new_config = lcb_clconfig_create(vbc, LCB_CLCONFIG_CCCP); + + if (!new_config) { + lcbvb_destroy(vbc); + return LCB_CLIENT_ENOMEM; + } + + if (config) { + lcb_clconfig_decref(config); + } + + /** TODO: Figure out the comparison vector */ + new_config->cmpclock = gethrtime(); + config = new_config; + lcb_confmon_provider_success(this, new_config); + return LCB_SUCCESS; +} + +void lcb_cccp_update2(const void *cookie, lcb_error_t err, + const void *bytes, lcb_size_t nbytes, + const lcb_host_t *origin) +{ + CccpCookie *ck = reinterpret_cast(const_cast(cookie)); + CccpProvider *cccp = ck->parent; + + if (ck == cccp->cmdcookie) { + lcbio_timer_disarm(cccp->timer); + cccp->cmdcookie = NULL; + } + + if (err == LCB_SUCCESS) { + std::string ss(reinterpret_cast(bytes), nbytes); + err = cccp->update(origin->host, ss.c_str()); + } + + if (err != LCB_SUCCESS && ck->ignore_errors == 0) { + cccp->mcio_error(err); + } + + free(ck); +} + +static void +on_connected(lcbio_SOCKET *sock, void *data, lcb_error_t err, lcbio_OSERR) +{ + lcbio_CTXPROCS ioprocs; + CccpProvider *cccp = reinterpret_cast(data); + lcb_settings *settings = cccp->parent->settings; + + LCBIO_CONNREQ_CLEAR(&cccp->creq); + if (err != LCB_SUCCESS) { + if (sock) { + lcbio_mgr_discard(sock); + } + cccp->mcio_error(err); + return; + } + + if (lcbio_protoctx_get(sock, LCBIO_PROTOCTX_SESSINFO) == NULL) { + lcb::SessionRequest *sreq = lcb::SessionRequest::start( + sock, settings, settings->config_node_timeout, on_connected, + cccp); + LCBIO_CONNREQ_MKGENERIC(&cccp->creq, sreq, lcb::sessreq_cancel); + return; + } + + ioprocs.cb_err = io_error_handler; + ioprocs.cb_read = io_read_handler; + cccp->ioctx = lcbio_ctx_new(sock, data, &ioprocs); + cccp->ioctx->subsys = "bc_cccp"; + cccp->request_config(); +} + +static lcb_error_t cccp_get(clconfig_provider *pb) +{ + CccpProvider *cccp = static_cast(pb); + if (cccp->creq.u.p_generic || cccp->server_active || cccp->cmdcookie) { + return LCB_BUSY; + } + + return cccp->schedule_next_request(LCB_SUCCESS, true); +} + +static clconfig_info *cccp_get_cached(clconfig_provider *pb) { + return static_cast(pb)->config; +} + +static lcb_error_t cccp_pause(clconfig_provider *pb) +{ + CccpProvider *cccp = static_cast(pb); + if (!cccp->server_active) { + return LCB_SUCCESS; + } + + cccp->server_active = 0; + cccp->release_socket(false); + lcbio_timer_disarm(cccp->timer); + return LCB_SUCCESS; +} + +static void cccp_cleanup(clconfig_provider *pb) { + delete static_cast(pb); +} + +CccpProvider::~CccpProvider() { + release_socket(false); + + if (config) { + lcb_clconfig_decref(config); + } + if (nodes) { + delete nodes; + } + if (timer) { + lcbio_timer_destroy(timer); + } + if (cmdcookie) { + cmdcookie->ignore_errors = 1; + } +} + +static void +configure_nodes(clconfig_provider *pb, const hostlist_t nodes) +{ + CccpProvider *cccp = static_cast(pb); + cccp->nodes->assign(*nodes); + if (PROVIDER_SETTING(pb, randomize_bootstrap_nodes)) { + cccp->nodes->randomize(); + } +} + +static void config_updated(clconfig_provider *provider, lcbvb_CONFIG* vbc) { + static_cast(provider)->on_config_updated(vbc); +} + +void +CccpProvider::on_config_updated(lcbvb_CONFIG *vbc) +{ + lcbvb_SVCMODE mode; + if (LCBVB_NSERVERS(vbc) < 1) { + return; + } + + nodes->clear(); + if (PROVIDER_SETTING_T(sslopts) & LCB_SSL_ENABLED) { + mode = LCBVB_SVCMODE_SSL; + } else { + mode = LCBVB_SVCMODE_PLAIN; + } + for (size_t ii = 0; ii < LCBVB_NSERVERS(vbc); ii++) { + const char *mcaddr = lcbvb_get_hostport(vbc, + ii, LCBVB_SVCTYPE_DATA, mode); + if (!mcaddr) { + lcb_log(LOGARGS(this, DEBUG), "Node %lu has no data service", ii); + continue; + } + nodes->add(mcaddr, LCB_CONFIG_MCD_PORT); + } + + if (PROVIDER_SETTING_T(randomize_bootstrap_nodes)) { + nodes->randomize(); + } +} + +static void +io_error_handler(lcbio_CTX *ctx, lcb_error_t err) +{ + CccpProvider *cccp = reinterpret_cast(lcbio_ctx_data(ctx)); + cccp->mcio_error(err); +} + +static void io_read_handler(lcbio_CTX *ioctx, unsigned) { + reinterpret_cast(lcbio_ctx_data(ioctx))->on_io_read(); +} + +void +CccpProvider::on_io_read() +{ + unsigned required; + +#define return_error(e) \ + resp.release(ioctx); \ + mcio_error(e); \ + return + + lcb::MemcachedResponse resp; + if (!resp.load(ioctx, &required)) { + lcbio_ctx_rwant(ioctx, required); + lcbio_ctx_schedule(ioctx); + return; + } + + if (resp.status() != PROTOCOL_BINARY_RESPONSE_SUCCESS) { + lcb_log(LOGARGS(this, WARN), LOGFMT "CCCP Packet responded with 0x%x; nkey=%d, nbytes=%lu, cmd=0x%x, seq=0x%x", LOGID(this), + resp.status(), resp.keylen(), (unsigned long)resp.bodylen(), + resp.opcode(), resp.opaque()); + + switch (resp.status()) { + case PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED: + case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND: + return_error(LCB_NOT_SUPPORTED); + default: + return_error(LCB_PROTOCOL_ERROR); + } + + return; + } + + if (!resp.bodylen()) { + return_error(LCB_PROTOCOL_ERROR); + } + + std::string jsonstr(resp.body(), resp.bodylen()); + std::string hoststr(lcbio_get_host(lcbio_ctx_sock(ioctx))->host); + + resp.release(ioctx); + release_socket(true); + + lcb_error_t err = update(hoststr.c_str(), jsonstr.c_str()); + + if (err == LCB_SUCCESS) { + lcbio_timer_disarm(timer); + server_active = false; + } else { + schedule_next_request(LCB_PROTOCOL_ERROR, 0); + } + +#undef return_error +} + +void CccpProvider::request_config() +{ + lcb::MemcachedRequest req(PROTOCOL_BINARY_CMD_GET_CLUSTER_CONFIG); + req.opaque(0xF00D); + lcbio_ctx_put(ioctx, req.data(), req.size()); + lcbio_ctx_rwant(ioctx, 24); + lcbio_ctx_schedule(ioctx); + lcbio_timer_rearm(timer, PROVIDER_SETTING(this, config_node_timeout)); +} + +static void do_dump(clconfig_provider *pb, FILE *fp) +{ + CccpProvider *cccp = (CccpProvider *)pb; + + if (!cccp->enabled) { + return; + } + + fprintf(fp, "## BEGIN CCCP PROVIDER DUMP ##\n"); + fprintf(fp, "TIMER ACTIVE: %s\n", lcbio_timer_armed(cccp->timer) ? "YES" : "NO"); + fprintf(fp, "PIPELINE RESPONSE COOKIE: %p\n", (void*)cccp->cmdcookie); + if (cccp->ioctx) { + fprintf(fp, "CCCP Owns connection:\n"); + lcbio_ctx_dump(cccp->ioctx, fp); + } else if (cccp->creq.u.p_generic) { + fprintf(fp, "CCCP Is connecting\n"); + } else { + fprintf(fp, "CCCP does not have a dedicated connection\n"); + } + + for (size_t ii = 0; ii < cccp->nodes->size(); ii++) { + const lcb_host_t &curhost = (*cccp->nodes)[ii]; + fprintf(fp, "CCCP NODE: %s:%s\n", curhost.host, curhost.port); + } + fprintf(fp, "## END CCCP PROVIDER DUMP ##\n"); +} + +clconfig_provider * lcb_clconfig_create_cccp(lcb_confmon *mon) +{ + return new CccpProvider(mon); +} + +CccpProvider::CccpProvider(lcb_confmon *mon) + : nodes(new lcb::Hostlist()), + config(NULL), + server_active(false), + timer(NULL), + instance(NULL), + ioctx(NULL), + cmdcookie(NULL) +{ + clconfig_provider::type = LCB_CLCONFIG_CCCP; + clconfig_provider::refresh = cccp_get; + clconfig_provider::get_cached = cccp_get_cached; + clconfig_provider::pause = cccp_pause; + clconfig_provider::shutdown = cccp_cleanup; + clconfig_provider::config_updated = ::config_updated; + clconfig_provider::configure_nodes = ::configure_nodes; + clconfig_provider::get_nodes = ::get_nodes; + clconfig_provider::dump = do_dump; + clconfig_provider::parent = mon; + clconfig_provider::enabled = 0; + + timer = lcbio_timer_new(mon->iot, this, socket_timeout); + std::memset(&creq, 0, sizeof creq); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_file.c b/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_file.c new file mode 100644 index 00000000..46a034bc --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_file.c @@ -0,0 +1,347 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" +#include "clconfig.h" +#include "simplestring.h" +#include +#include + +#define CONFIG_CACHE_MAGIC "{{{fb85b563d0a8f65fa8d3d58f1b3a0708}}}" + +#define LOGARGS(pb, lvl) pb->base.parent->settings, "bc_file", LCB_LOG_##lvl, __FILE__, __LINE__ +#define LOGFMT "(cache=%s) " +#define LOGID(fb) fb->filename + +typedef struct { + clconfig_provider base; + char *filename; + clconfig_info *config; + time_t last_mtime; + int last_errno; + int ro_mode; /* Whether the config cache should _not_ overwrite the file */ + lcbio_pTIMER timer; + clconfig_listener listener; +} file_provider; + +static int load_cache(file_provider *provider) +{ + lcb_string str; + char line[1024]; + lcb_ssize_t nr; + int fail; + FILE *fp = NULL; + lcbvb_CONFIG *config = NULL; + char *end; + struct stat st; + int status = -1; + + lcb_string_init(&str); + + if (provider->filename == NULL) { + return -1; + } + + fp = fopen(provider->filename, "r"); + if (fp == NULL) { + int save_errno = errno; + lcb_log(LOGARGS(provider, ERROR), LOGFMT "Couldn't open for reading: %s", LOGID(provider), strerror(save_errno)); + return -1; + } + + if (fstat(fileno(fp), &st)) { + provider->last_errno = errno; + goto GT_DONE; + } + + if (provider->last_mtime == st.st_mtime) { + lcb_log(LOGARGS(provider, WARN), LOGFMT "Modification time too old", LOGID(provider)); + goto GT_DONE; + } + + config = lcbvb_create(); + if (config == NULL) { + goto GT_DONE; + } + + lcb_string_init(&str); + + while ((nr = fread(line, 1, sizeof(line), fp)) > 0) { + if (lcb_string_append(&str, line, nr)) { + goto GT_DONE; + } + } + + if (ferror(fp)) { + goto GT_DONE; + } + + fclose(fp); + fp = NULL; + + if (!str.nused) { + status = -1; + goto GT_DONE; + } + + end = strstr(str.base, CONFIG_CACHE_MAGIC); + if (end == NULL) { + lcb_log(LOGARGS(provider, ERROR), LOGFMT "Couldn't find magic", LOGID(provider)); + if (!provider->ro_mode) { + remove(provider->filename); + } + status = -1; + goto GT_DONE; + } + + fail = lcbvb_load_json(config, str.base); + if (fail) { + status = -1; + lcb_log(LOGARGS(provider, ERROR), LOGFMT "Couldn't parse configuration", LOGID(provider)); + lcb_log_badconfig(LOGARGS(provider, ERROR), config, str.base); + if (!provider->ro_mode) { + remove(provider->filename); + } + goto GT_DONE; + } + + if (lcbvb_get_distmode(config) != LCBVB_DIST_VBUCKET) { + status = -1; + lcb_log(LOGARGS(provider, ERROR), LOGFMT "Not applying cached memcached config", LOGID(provider)); + goto GT_DONE; + } + + if (strcmp(config->bname, PROVIDER_SETTING(&provider->base, bucket)) != 0) { + status = -1; + lcb_log(LOGARGS(provider, ERROR), LOGFMT "Bucket name in file is different from the one requested", LOGID(provider)); + } + + if (provider->config) { + lcb_clconfig_decref(provider->config); + } + + provider->config = lcb_clconfig_create(config, LCB_CLCONFIG_FILE); + provider->config->cmpclock = gethrtime(); + provider->config->origin = provider->base.type; + provider->last_mtime = st.st_mtime; + status = 0; + config = NULL; + + GT_DONE: + if (fp != NULL) { + fclose(fp); + } + + if (config != NULL) { + lcbvb_destroy(config); + } + + lcb_string_release(&str); + return status; +} + +static void +write_to_file(file_provider *provider, lcbvb_CONFIG *cfg) +{ + FILE *fp; + + if (provider->filename == NULL || provider->ro_mode) { + return; + } + + fp = fopen(provider->filename, "w"); + if (fp) { + char *json = lcbvb_save_json(cfg); + lcb_log(LOGARGS(provider, INFO), LOGFMT "Writing configuration to file", LOGID(provider)); + fprintf(fp, "%s%s", json, CONFIG_CACHE_MAGIC); + fclose(fp); + free(json); + } else { + int save_errno = errno; + lcb_log(LOGARGS(provider, ERROR), LOGFMT "Couldn't open file for writing: %s", LOGID(provider), strerror(save_errno)); + } +} + +static clconfig_info * get_cached(clconfig_provider *pb) +{ + file_provider *provider = (file_provider *)pb; + if (!provider->filename) { + return NULL; + } + + return provider->config; +} + +static void async_callback(void *cookie) +{ + time_t last_mtime; + file_provider *provider = (file_provider *)cookie; + last_mtime = provider->last_mtime; + if (load_cache(provider) == 0) { + if (last_mtime != provider->last_mtime) { + lcb_confmon_provider_success(&provider->base, provider->config); + return; + } + } + + lcb_confmon_provider_failed(&provider->base, LCB_ERROR); +} + +static lcb_error_t refresh_file(clconfig_provider *pb) +{ + file_provider *provider = (file_provider *)pb; + if (lcbio_timer_armed(provider->timer)) { + return LCB_SUCCESS; + } + + lcbio_async_signal(provider->timer); + return LCB_SUCCESS; +} + +static lcb_error_t pause_file(clconfig_provider *pb) +{ + (void)pb; + return LCB_SUCCESS; +} + +static void shutdown_file(clconfig_provider *pb) +{ + file_provider *provider = (file_provider *)pb; + free(provider->filename); + if (provider->timer) { + lcbio_timer_destroy(provider->timer); + } + if (provider->config) { + lcb_clconfig_decref(provider->config); + } + free(provider); +} + +static void config_listener(clconfig_listener *lsn, clconfig_event_t event, + clconfig_info *info) +{ + file_provider *provider; + + if (event != CLCONFIG_EVENT_GOT_NEW_CONFIG) { + return; + } + + provider = (file_provider *) (void*)(((char *)lsn) - offsetof(file_provider, listener)); + if (!provider->base.enabled) { + return; + } + + if (info->origin == LCB_CLCONFIG_PHONY || info->origin == LCB_CLCONFIG_FILE) { + lcb_log(LOGARGS(provider, TRACE), "Not writing configuration originating from PHONY or FILE to cache"); + return; + } + + write_to_file(provider, info->vbc); +} + +static void +do_file_dump(clconfig_provider *pb, FILE *fp) +{ + file_provider *pr = (file_provider *)pb; + + fprintf(fp, "## BEGIN FILE PROVIEDER DUMP ##\n"); + if (pr->filename) { + fprintf(fp, "FILENAME: %s\n", pr->filename); + } + fprintf(fp, "LAST SYSTEM ERRNO: %d\n", pr->last_errno); + fprintf(fp, "LAST MTIME: %lu\n", (unsigned long)pr->last_mtime); + fprintf(fp, "## END FILE PROVIDER DUMP ##\n"); + +} + +clconfig_provider * lcb_clconfig_create_file(lcb_confmon *parent) +{ + file_provider *provider = calloc(1, sizeof(*provider)); + + if (!provider) { + return NULL; + } + + provider->base.get_cached = get_cached; + provider->base.refresh = refresh_file; + provider->base.pause = pause_file; + provider->base.shutdown = shutdown_file; + provider->base.dump = do_file_dump; + provider->base.type = LCB_CLCONFIG_FILE; + provider->listener.callback = config_listener; + provider->timer = lcbio_timer_new(parent->iot, provider, async_callback); + + lcb_confmon_add_listener(parent, &provider->listener); + + return &provider->base; +} + + +static char *mkcachefile(const char *name, const char *bucket) +{ + if (name != NULL) { + return strdup(name); + } else { + char buffer[1024]; + const char *tmpdir = lcb_get_tmpdir(); + + snprintf(buffer, sizeof(buffer), + "%s/%s", tmpdir ? tmpdir : ".", bucket); + return strdup(buffer); + } +} + +int lcb_clconfig_file_set_filename(clconfig_provider *p, const char *f, int ro) +{ + file_provider *provider = (file_provider *)p; + lcb_assert(provider->base.type == LCB_CLCONFIG_FILE); + provider->base.enabled = 1; + + if (provider->filename) { + free(provider->filename); + } + + provider->filename = mkcachefile(f, p->parent->settings->bucket); + + if (ro) { + FILE *fp_tmp; + provider->ro_mode = 1; + + fp_tmp = fopen(provider->filename, "r"); + if (!fp_tmp) { + lcb_log(LOGARGS(provider, ERROR), LOGFMT "Couldn't open for reading: %s", LOGID(provider), strerror(errno)); + return -1; + } else { + fclose(fp_tmp); + } + } + + return 0; +} + +const char * +lcb_clconfig_file_get_filename(clconfig_provider *p) +{ + file_provider *fp = (file_provider *)p; + return fp->filename; +} + +void +lcb_clconfig_file_set_readonly(clconfig_provider *p, int val) +{ + ((file_provider *)p)->ro_mode = val; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_http.c b/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_http.c new file mode 100644 index 00000000..319560ac --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_http.c @@ -0,0 +1,630 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" +#include "clconfig.h" +#include "bc_http.h" +#include +#include "ctx-log-inl.h" +#define LOGARGS(ht, lvlbase) ht->base.parent->settings, "htconfig", LCB_LOG_##lvlbase, __FILE__, __LINE__ +#define LOGFMT "<%s:%s> " +#define LOGID(h) get_ctx_host(h->ioctx), get_ctx_port(h->ioctx) + +static void io_error_handler(lcbio_CTX *, lcb_error_t); +static void on_connected(lcbio_SOCKET *, void *, lcb_error_t, lcbio_OSERR); +static lcb_error_t connect_next(http_provider *); +static void read_common(lcbio_CTX *, unsigned); +static lcb_error_t setup_request_header(http_provider *, const lcb_host_t *); + +/** + * Determine if we're in compatibility mode with the previous versions of the + * library - where the idle timeout is disabled and a perpetual streaming + * connection will always remain open (regardless of whether it was triggered + * by start_refresh/get_refresh). + */ +static int is_v220_compat(http_provider *http) +{ + lcb_uint32_t setting = PROVIDER_SETTING(&http->base, bc_http_stream_time); + if (setting == (lcb_uint32_t)-1) { + return 1; + } + return 0; +} + +/** + * Closes the current connection and removes the disconn timer along with it + */ +static void close_current(http_provider *http) +{ + lcbio_timer_disarm(http->disconn_timer); + if (http->ioctx) { + lcbio_ctx_close(http->ioctx, NULL, NULL); + } else if (http->creq){ + lcbio_connect_cancel(http->creq); + } + http->creq = NULL; + http->ioctx = NULL; +} + +/** + * Call when there is an error in I/O. This includes read, write, connect + * and timeouts. + */ +static lcb_error_t +io_error(http_provider *http, lcb_error_t origerr) +{ + lcb_confmon *mon = http->base.parent; + lcb_settings *settings = mon->settings; + + close_current(http); + + http->creq = lcbio_connect_hl( + mon->iot, settings, http->nodes, 0, settings->config_node_timeout, + on_connected, http); + if (http->creq) { + return LCB_SUCCESS; + } + + lcb_confmon_provider_failed(&http->base, origerr); + lcbio_timer_disarm(http->io_timer); + if (is_v220_compat(http) && http->base.parent->config != NULL) { + lcb_log(LOGARGS(http, INFO), "HTTP node list finished. Trying to obtain connection from first node in list"); + if (!lcbio_timer_armed(http->as_reconnect)) { + lcbio_timer_rearm(http->as_reconnect, + PROVIDER_SETTING(&http->base, grace_next_cycle)); + } + } + return origerr; +} + +/** + * Call this if the configuration generation has changed. + */ +static void set_new_config(http_provider *http) +{ + const lcb_host_t *curhost; + if (http->current_config) { + lcb_clconfig_decref(http->current_config); + } + + curhost = lcbio_get_host(lcbio_ctx_sock(http->ioctx)); + http->current_config = http->last_parsed; + lcb_clconfig_incref(http->current_config); + lcbvb_replace_host(http->current_config->vbc, curhost->host); + lcb_confmon_provider_success(&http->base, http->current_config); +} + +static lcb_error_t +process_chunk(http_provider *http, const void *buf, unsigned nbuf) +{ + lcb_error_t err = LCB_SUCCESS; + char *term; + int rv; + lcbvb_CONFIG *cfgh; + lcbht_RESPSTATE state, oldstate, diff; + lcbht_RESPONSE *resp = lcbht_get_response(http->htp); + + oldstate = resp->state; + state = lcbht_parse(http->htp, buf, nbuf); + diff = state ^ oldstate; + + if (state & LCBHT_S_ERROR) { + return LCB_PROTOCOL_ERROR; + } + + if (diff & LCBHT_S_HEADER) { + /* see that we got a success? */ + if (resp->status == 200) { + /* nothing */ + } else if (resp->status == 404) { + const int urlmode = PROVIDER_SETTING(&http->base, bc_http_urltype); + err = LCB_BUCKET_ENOENT; + + if (++http->uritype > LCB_HTCONFIG_URLTYPE_COMPAT) { + lcb_log(LOGARGS(http, ERR), LOGFMT "Got 404 on config stream. Assuming bucket does not exist as we've tried both URL types", LOGID(http)); + goto GT_HT_ERROR; + + } else if ((urlmode & LCB_HTCONFIG_URLTYPE_COMPAT) == 0) { + lcb_log(LOGARGS(http, ERR), LOGFMT "Got 404 on config stream for terse URI. Compat URI disabled, so not trying", LOGID(http)); + + } else { + /* reissue the request; but wait for it to drain */ + lcb_log(LOGARGS(http, WARN), LOGFMT "Got 404 on config stream. Assuming terse URI not supported on cluster", LOGID(http)); + http->try_nexturi = 1; + err = LCB_SUCCESS; + goto GT_CHECKDONE; + } + } else if (resp->status == 401) { + err = LCB_AUTH_ERROR; + } else { + err = LCB_ERROR; + } + + GT_HT_ERROR: + if (err != LCB_SUCCESS) { + lcb_log(LOGARGS(http, ERR), LOGFMT "Got non-success HTTP status code %d", LOGID(http), resp->status); + return err; + } + } + + GT_CHECKDONE: + if (http->try_nexturi) { + lcb_host_t *host; + if (!(state & LCBHT_S_DONE)) { + return LCB_SUCCESS; + } + host = lcbio_get_host(lcbio_ctx_sock(http->ioctx)); + http->try_nexturi = 0; + if ((err = setup_request_header(http, host)) != LCB_SUCCESS) { + return err; + } + + /* reset the state? */ + lcbht_reset(http->htp); + lcbio_ctx_put(http->ioctx, http->request_buf, strlen(http->request_buf)); + return LCB_SUCCESS; + } + + if (PROVIDER_SETTING(&http->base, conntype) == LCB_TYPE_CLUSTER) { + /* don't bother with parsing the actual config */ + resp->body.nused = 0; + return LCB_SUCCESS; + } + if (!(state & LCBHT_S_BODY)) { + /* nothing to parse yet */ + return LCB_SUCCESS; + } + + /* seek ahead for strstr */ + term = strstr(resp->body.base, CONFIG_DELIMITER); + if (!term) { + return LCB_SUCCESS; + } + + *term = '\0'; + cfgh = lcbvb_create(); + if (!cfgh) { + return LCB_CLIENT_ENOMEM; + } + rv = lcbvb_load_json(cfgh, resp->body.base); + if (rv != 0) { + lcb_log(LOGARGS(http, ERR), LOGFMT "Failed to parse a valid config from HTTP stream", LOGID(http)); + lcb_log_badconfig(LOGARGS(http, ERR), cfgh, resp->body.base); + lcbvb_destroy(cfgh); + return LCB_PROTOCOL_ERROR; + } + if (http->last_parsed) { + lcb_clconfig_decref(http->last_parsed); + } + http->last_parsed = lcb_clconfig_create(cfgh, LCB_CLCONFIG_HTTP); + http->last_parsed->cmpclock = gethrtime(); + http->generation++; + + /** Relocate the stream */ + lcb_string_erase_beginning(&resp->body, + (term+sizeof(CONFIG_DELIMITER)-1)-resp->body.base); + + return LCB_SUCCESS; +} + +/** + * Common function to handle parsing the HTTP stream for both v0 and v1 io + * implementations. + */ +static void +read_common(lcbio_CTX *ctx, unsigned nr) +{ + lcbio_CTXRDITER riter; + http_provider *http = lcbio_ctx_data(ctx); + int old_generation = http->generation; + + lcb_log(LOGARGS(http, TRACE), LOGFMT "Received %d bytes on HTTP stream", LOGID(http), nr); + + lcbio_timer_rearm(http->io_timer, + PROVIDER_SETTING(&http->base, config_node_timeout)); + + LCBIO_CTX_ITERFOR(ctx, &riter, nr) { + unsigned nbuf = lcbio_ctx_risize(&riter); + void *buf = lcbio_ctx_ribuf(&riter); + lcb_error_t err = process_chunk(http, buf, nbuf); + + if (err != LCB_SUCCESS) { + io_error(http, err); + return; + } + } + + if (http->generation != old_generation) { + lcb_log(LOGARGS(http, DEBUG), LOGFMT "Generation %d -> %d", LOGID(http), old_generation, http->generation); + lcbio_timer_disarm(http->io_timer); + set_new_config(http); + } + + lcbio_ctx_rwant(ctx, 1); + lcbio_ctx_schedule(ctx); +} + +static lcb_error_t +setup_request_header(http_provider *http, const lcb_host_t *host) +{ + lcb_settings *settings = http->base.parent->settings; + + char *buf = http->request_buf; + const char *username = NULL, *password = NULL; + lcb_size_t nbuf = sizeof(http->request_buf); + + lcb_size_t offset = 0; + http->request_buf[0] = '\0'; + + if (settings->conntype == LCB_TYPE_BUCKET) { + const char *fmt; + if (http->uritype == LCB_HTCONFIG_URLTYPE_25PLUS) { + fmt = REQBUCKET_TERSE_FMT; + } else { + fmt = REQBUCKET_COMPAT_FMT; + } + offset = snprintf(buf, nbuf, fmt, settings->bucket); + + } else if (settings->conntype == LCB_TYPE_CLUSTER) { + offset = snprintf(buf, nbuf, REQPOOLS_FMT); + + } else { + return LCB_EINVAL; + } + lcbauth_get_upass(settings->auth, &username, &password); + + if (password) { + char cred[256], b64[256]; + snprintf(cred, sizeof(cred), "%s:%s", username, password); + + if (lcb_base64_encode(cred, b64, sizeof(b64)) == -1) { + return LCB_EINTERNAL; + } + + offset += snprintf(buf + offset, nbuf - offset, AUTHDR_FMT, b64); + } + + offset += snprintf(buf + offset, nbuf - offset, HOSTHDR_FMT, + host->host, host->port); + + offset += snprintf(buf + offset, nbuf - offset, "%s\r\n", LAST_HTTP_HEADER); + + return LCB_SUCCESS; +} + +static void reset_stream_state(http_provider *http) +{ + const int urlmode = PROVIDER_SETTING(&http->base, bc_http_urltype); + if (http->last_parsed) { + lcb_clconfig_decref(http->last_parsed); + http->last_parsed = NULL; + } + if (urlmode & LCB_HTCONFIG_URLTYPE_25PLUS) { + http->uritype = LCB_HTCONFIG_URLTYPE_25PLUS; + } else { + http->uritype = LCB_HTCONFIG_URLTYPE_COMPAT; + } + http->try_nexturi = 0; + lcbht_reset(http->htp); +} + +static void +on_connected(lcbio_SOCKET *sock, void *arg, lcb_error_t err, lcbio_OSERR syserr) +{ + http_provider *http = arg; + lcb_host_t *host; + lcbio_CTXPROCS procs; + http->creq = NULL; + + if (err != LCB_SUCCESS) { + lcb_log(LOGARGS(http, ERR), "Connection to REST API failed with code=0x%x (%d)", err, syserr); + io_error(http, err); + return; + } + host = lcbio_get_host(sock); + lcb_log(LOGARGS(http, DEBUG), "Successfuly connected to REST API %s:%s", host->host, host->port); + + lcbio_sslify_if_needed(sock, http->base.parent->settings); + reset_stream_state(http); + + if ((err = setup_request_header(http, host)) != LCB_SUCCESS) { + lcb_log(LOGARGS(http, ERR), "Couldn't setup request header"); + io_error(http, err); + return; + } + + memset(&procs, 0, sizeof(procs)); + procs.cb_err = io_error_handler; + procs.cb_read = read_common; + http->ioctx = lcbio_ctx_new(sock, http, &procs); + http->ioctx->subsys = "bc_http"; + + lcbio_ctx_put(http->ioctx, http->request_buf, strlen(http->request_buf)); + lcbio_ctx_rwant(http->ioctx, 1); + lcbio_ctx_schedule(http->ioctx); + lcbio_timer_rearm(http->io_timer, + PROVIDER_SETTING(&http->base, config_node_timeout)); +} + +static void +timeout_handler(void *arg) +{ + http_provider *http = arg; + + lcb_log(LOGARGS(http, ERR), LOGFMT "HTTP Provider timed out waiting for I/O", LOGID(http)); + + /** + * If we're not the current provider then ignore the timeout until we're + * actively requested to do so + */ + if (&http->base != http->base.parent->cur_provider || + lcb_confmon_is_refreshing(http->base.parent) == 0) { + lcb_log(LOGARGS(http, DEBUG), LOGFMT "Ignoring timeout because we're either not in a refresh or not the current provider", LOGID(http)); + return; + } + + io_error(http, LCB_ETIMEDOUT); +} + + +static lcb_error_t +connect_next(http_provider *http) +{ + lcb_settings *settings = http->base.parent->settings; + lcb_log(LOGARGS(http, TRACE), "Starting HTTP Configuration Provider %p", (void*)http); + close_current(http); + lcbio_timer_disarm(http->as_reconnect); + + if (!hostlist_size(http->nodes)) { + lcb_log(LOGARGS(http, ERROR), "Not scheduling HTTP provider since no nodes have been configured for HTTP bootstrap"); + return LCB_CONNECT_ERROR; + } + + http->creq = lcbio_connect_hl(http->base.parent->iot, settings, http->nodes, 1, + settings->config_node_timeout, on_connected, http); + if (http->creq) { + return LCB_SUCCESS; + } + lcb_log(LOGARGS(http, ERROR), "%p: Couldn't schedule connection", (void*)http); + return LCB_CONNECT_ERROR; +} + +static void delayed_disconn(void *arg) +{ + http_provider *http = arg; + lcb_log(LOGARGS(http, DEBUG), "Stopping HTTP provider %p", (void*)http); + + /** closes the connection and cleans up the timer */ + close_current(http); + lcbio_timer_disarm(http->io_timer); +} + +static void delayed_reconnect(void *arg) +{ + http_provider *http = arg; + lcb_error_t err; + if (http->ioctx) { + /* have a context already */ + return; + } + err = connect_next(http); + if (err != LCB_SUCCESS) { + io_error(http, err); + } +} + +static lcb_error_t pause_http(clconfig_provider *pb) +{ + http_provider *http = (http_provider *)pb; + if (is_v220_compat(http)) { + return LCB_SUCCESS; + } + + if (!lcbio_timer_armed(http->disconn_timer)) { + lcbio_timer_rearm(http->disconn_timer, + PROVIDER_SETTING(pb, bc_http_stream_time)); + } + return LCB_SUCCESS; +} + +static lcb_error_t get_refresh(clconfig_provider *provider) +{ + http_provider *http = (http_provider *)provider; + + /** + * We want a grace interval here because we might already be fetching a + * connection. HOWEVER we don't want to indefinitely wait on a socket + * so we issue a timer indicating how long we expect to wait for a + * streaming update until we get something. + */ + + /** If we need a new socket, we do connect_next. */ + if (http->ioctx == NULL && http->creq == NULL) { + lcbio_async_signal(http->as_reconnect); + } + + lcbio_timer_disarm(http->disconn_timer); + if (http->ioctx) { + lcbio_timer_rearm(http->io_timer, + PROVIDER_SETTING(provider, config_node_timeout)); + } + return LCB_SUCCESS; +} + +static clconfig_info* http_get_cached(clconfig_provider *provider) +{ + http_provider *http = (http_provider *)provider; + return http->current_config; +} + +static void +config_updated(clconfig_provider *pb, lcbvb_CONFIG *newconfig) +{ + unsigned int ii; + http_provider *http = (http_provider *)pb; + lcb_SSLOPTS sopts; + lcbvb_SVCMODE mode; + + hostlist_clear(http->nodes); + + sopts = PROVIDER_SETTING(pb, sslopts); + if (sopts & LCB_SSL_ENABLED) { + mode = LCBVB_SVCMODE_SSL; + } else { + mode = LCBVB_SVCMODE_PLAIN; + } + + for (ii = 0; ii < newconfig->nsrv; ++ii) { + const char *ss; + lcb_error_t status; + ss = lcbvb_get_hostport(newconfig, ii, LCBVB_SVCTYPE_MGMT, mode); + if (!ss) { + /* not supported? */ + continue; + } + status = hostlist_add_stringz(http->nodes, ss, LCB_CONFIG_HTTP_PORT); + lcb_assert(status == LCB_SUCCESS); + } + if (!hostlist_size(http->nodes)) { + lcb_log(LOGARGS(http, FATAL), "New nodes do not contain management ports"); + } + + if (PROVIDER_SETTING(pb, randomize_bootstrap_nodes)) { + hostlist_randomize(http->nodes); + } +} + +static void +configure_nodes(clconfig_provider *pb, const hostlist_t newnodes) +{ + http_provider *http = (void *)pb; + hostlist_assign(http->nodes, newnodes); + if (PROVIDER_SETTING(pb, randomize_bootstrap_nodes)) { + hostlist_randomize(http->nodes); + } +} + +static hostlist_t +get_nodes(const clconfig_provider *pb) +{ + return ((http_provider *)pb)->nodes; +} + +static void shutdown_http(clconfig_provider *provider) +{ + http_provider *http = (http_provider *)provider; + reset_stream_state(http); + close_current(http); + lcbht_free(http->htp); + + if (http->current_config) { + lcb_clconfig_decref(http->current_config); + } + if (http->disconn_timer) { + lcbio_timer_destroy(http->disconn_timer); + } + if (http->io_timer) { + lcbio_timer_destroy(http->io_timer); + } + if (http->as_reconnect) { + lcbio_timer_destroy(http->as_reconnect); + } + if (http->nodes) { + hostlist_destroy(http->nodes); + } + free(http); +} + +static void +do_http_dump(clconfig_provider *pb, FILE *fp) +{ + http_provider *ht = (http_provider *)pb; + fprintf(fp, "## BEGIN HTTP PROVIDER DUMP\n"); + fprintf(fp, "NUMBER OF CONFIGS RECEIVED: %u\n", ht->generation); + fprintf(fp, "DUMPING I/O TIMER\n"); + lcbio_timer_dump(ht->io_timer, fp); + if (ht->ioctx) { + fprintf(fp, "DUMPING CURRENT CONNECTION:\n"); + lcbio_ctx_dump(ht->ioctx, fp); + } else if (ht->creq) { + fprintf(fp, "CURRENTLY CONNECTING..\n"); + } else { + fprintf(fp, "NO CONNECTION ACTIVE\n"); + } +} + +clconfig_provider * lcb_clconfig_create_http(lcb_confmon *parent) +{ + http_provider *http = calloc(1, sizeof(*http)); + + if (!http) { + return NULL; + } + + if (! (http->nodes = hostlist_create())) { + free(http); + return NULL; + } + + http->base.type = LCB_CLCONFIG_HTTP; + http->base.refresh = get_refresh; + http->base.pause = pause_http; + http->base.get_cached = http_get_cached; + http->base.shutdown = shutdown_http; + http->base.config_updated = config_updated; + http->base.configure_nodes = configure_nodes; + http->base.get_nodes = get_nodes; + http->base.dump = do_http_dump; + http->base.enabled = 0; + http->io_timer = lcbio_timer_new(parent->iot, http, timeout_handler); + http->disconn_timer = lcbio_timer_new(parent->iot, http, delayed_disconn); + http->as_reconnect = lcbio_timer_new(parent->iot, http, delayed_reconnect); + http->htp = lcbht_new(parent->settings); + return &http->base; +} + +static void +io_error_handler(lcbio_CTX *ctx, lcb_error_t err) +{ + io_error((http_provider *)lcbio_ctx_data(ctx), err); +} + +void lcb_clconfig_http_enable(clconfig_provider *http) +{ + http->enabled = 1; +} + +lcbio_SOCKET * +lcb_confmon_get_rest_connection(lcb_confmon *mon) +{ + http_provider *http = (http_provider *)mon->all_providers[LCB_CLCONFIG_HTTP]; + if (!http->ioctx) { + return NULL; + } + return lcbio_ctx_sock(http->ioctx); + +} + +lcb_host_t * +lcb_confmon_get_rest_host(lcb_confmon *mon) +{ + lcbio_SOCKET *sock = lcb_confmon_get_rest_connection(mon); + if (sock) { + return lcbio_get_host(sock); + } + return NULL; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_http.h b/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_http.h new file mode 100644 index 00000000..503406e4 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_http.h @@ -0,0 +1,82 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * HTTP-based 'REST' configuration. This module works by connecting to the + * REST API port (and trying various other nodes) until it receives a + * configuration. + */ +#ifndef LCB_CLPROVIDER_HTTP_H +#define LCB_CLPROVIDER_HTTP_H + +#include "config.h" +#include "hostlist.h" +#include "simplestring.h" +#include "clconfig.h" +#include + +#define REQBUCKET_COMPAT_FMT "GET /pools/default/bucketsStreaming/%s HTTP/1.1\r\n" +#define REQBUCKET_TERSE_FMT "GET /pools/default/bs/%s HTTP/1.1\r\n" +#define REQPOOLS_FMT "GET /pools/ HTTP/1.1\r\n" +#define HOSTHDR_FMT "Host: %s:%s\r\n" +#define AUTHDR_FMT "Authorization: Basic %s\r\n" +#define LAST_HTTP_HEADER "X-Libcouchbase: " LCB_VERSION_STRING "\r\n" +#define CONFIG_DELIMITER "\n\n\n\n" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct clprovider_http_st { + /** Base configuration structure */ + clconfig_provider base; + lcbio_pCONNSTART creq; + lcbio_CTX *ioctx; + lcbht_pPARSER htp; + + /** + * Buffer to use for writing our request header. Recreated for each + * connection because of the Host: header + */ + char request_buf[1024]; + + /** + * We only recreate the connection if our current stream 'times out'. This + * timer waits until the current stream times out and then proceeds to the + * next connection. + */ + lcbio_pTIMER disconn_timer; + lcbio_pTIMER io_timer; + lcbio_pTIMER as_reconnect; + + /** List of hosts to try */ + hostlist_t nodes; + + /** The cached configuration. */ + clconfig_info *current_config; + clconfig_info *last_parsed; + int generation; + int try_nexturi; + lcb_HTCONFIG_URLTYPE uritype; +} http_provider; + +#ifdef __cplusplus +} +#endif + +#endif /* LCB_CLPROVIDER_HTTP_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_mcraw.c b/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_mcraw.c new file mode 100644 index 00000000..77109617 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/bc_mcraw.c @@ -0,0 +1,150 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "clconfig.h" + +#define LOGARGS(mcr, lvlbase) mcr->base.parent->settings, "mcraw", LCB_LOG_##lvlbase, __FILE__, __LINE__ +#define LOGFMT "(MCRAW=%p)> " +#define LOGID(mcr) (void *)mcr + +/* Raw memcached provider */ + +typedef struct { + /* Base provider */ + clconfig_provider base; + /* Current (user defined) configuration */ + clconfig_info *config; + lcbio_pTIMER async; +} bc_MCRAW; + + +static void +async_update(void *arg) +{ + bc_MCRAW *mcr = arg; + if (!mcr->config) { + lcb_log(LOGARGS(mcr, WARN), "No current config set. Not setting configuration"); + return; + } + lcb_confmon_provider_success(&mcr->base, mcr->config); +} + +static clconfig_info* get_cached(clconfig_provider *pb) { + return ((bc_MCRAW *)pb)->config; +} +static lcb_error_t get_refresh(clconfig_provider *pb) { + lcbio_async_signal( ((bc_MCRAW*)pb)->async ); + return LCB_SUCCESS; +} +static lcb_error_t pause_mcr(clconfig_provider *pb) { + (void)pb; + return LCB_SUCCESS; +} + +static void configure_nodes(clconfig_provider *pb, const hostlist_t hl) +{ + bc_MCRAW *mcr = (bc_MCRAW *)pb; + lcbvb_SERVER *servers; + lcbvb_CONFIG *newconfig; + unsigned ii, nsrv; + + nsrv = hostlist_size(hl); + + if (!nsrv) { + lcb_log(LOGARGS(mcr, FATAL), "No nodes provided"); + return; + } + + servers = calloc(nsrv, sizeof(*servers)); + for (ii = 0; ii < nsrv; ii++) { + int itmp; + const lcb_host_t *curhost = hostlist_get(hl, ii); + lcbvb_SERVER *srv = servers + ii; + + /* just set the memcached port and hostname */ + srv->hostname = (char *)curhost->host; + sscanf(curhost->port, "%d", &itmp); + srv->svc.data = itmp; + if (pb->parent->settings->sslopts) { + srv->svc_ssl.data = itmp; + } + } + + newconfig = lcbvb_create(); + lcbvb_genconfig_ex(newconfig, "NOBUCKET", "deadbeef", servers, nsrv, 0, 2); + lcbvb_make_ketama(newconfig); + newconfig->revid = -1; + + if (mcr->config) { + lcb_clconfig_decref(mcr->config); + mcr->config = NULL; + } + mcr->config = lcb_clconfig_create(newconfig, LCB_CLCONFIG_MCRAW); + mcr->config->cmpclock = gethrtime(); +} + +lcb_error_t +lcb_clconfig_mcraw_update(clconfig_provider *pb, const char *nodes) +{ + lcb_error_t err; + bc_MCRAW *mcr = (bc_MCRAW *)pb; + hostlist_t hl = hostlist_create(); + err = hostlist_add_stringz(hl, nodes, LCB_CONFIG_MCCOMPAT_PORT); + if (err != LCB_SUCCESS) { + hostlist_destroy(hl); + return err; + } + + configure_nodes(pb, hl); + hostlist_destroy(hl); + lcbio_async_signal(mcr->async); + return LCB_SUCCESS; +} + +static void +mcraw_shutdown(clconfig_provider *pb) +{ + bc_MCRAW *mcr = (bc_MCRAW *)pb; + if (mcr->config) { + lcb_clconfig_decref(mcr->config); + } + if (mcr->async) { + lcbio_timer_destroy(mcr->async); + } + free(mcr); +} + +clconfig_provider * +lcb_clconfig_create_mcraw(lcb_confmon *parent) +{ + bc_MCRAW *mcr = calloc(1, sizeof(*mcr)); + if (!mcr) { + return NULL; + } + mcr->async = lcbio_timer_new(parent->iot, mcr, async_update); + mcr->base.parent = parent; + mcr->base.type = LCB_CLCONFIG_MCRAW; + mcr->base.get_cached = get_cached; + mcr->base.refresh = get_refresh; + mcr->base.pause = pause_mcr; + mcr->base.configure_nodes = configure_nodes; + mcr->base.shutdown = mcraw_shutdown; + return &mcr->base; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/clconfig.h b/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/clconfig.h new file mode 100644 index 00000000..5194fbe4 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/clconfig.h @@ -0,0 +1,681 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_CLCONFIG_H +#define LCB_CLCONFIG_H + +#include "hostlist.h" +#include "list.h" +#include "simplestring.h" +#include +#ifdef __cplusplus +extern "C" { +#endif + +/** @file */ + +/** + * @defgroup lcb-confmon Cluster Configuration Management + * + * @brief Monitors the retrieval and application of new cluster topology maps + * (vBucket Configurations) + * + * @details + * This module attempts to implement the 'Configuration Provider' interface + * described at https://docs.google.com/document/d/1bSMt0Sj1uQtm0OYolQaJDJg4sASfoCEwU6_gjm1he8s/edit + * + * The model is fairly complex though significantly more maintainable and + * testable than the previous model. The basic idea is as follows: + * + * + *
      + * + *
    1. + * There is a _Configuration Monitor_ object (lcb_confmon) which acts + * as the configuration supervisor. It is responsible for returning + * configuration objects to those entities which request it. + *
    2. + * + *
    3. + * There are multiple _Configuration Provider_ (clconfig_provider) objects. + * These providers aggregate configurations from multiple sources and + * implement a common interface to: + * + *
        + *
      1. Return a _quick_ configuration without fetching from network or disk + * (see clconfig_provider::get_cached()) + + *
      2. Schedule a refresh to retrieve the latest configuration from the + * network (see clconfig_provider::refresh())
      3. + * + *
      4. Notify the monitor that it has received a new configuration. The + * monitor itself will determine whether or not to accept the new + * configuration by examining the configuration and determining if it is more + * recent than the one currently in used. See lcb_confmon_set_next()
      5. + *
    4. + * + *
    5. + * _Configuration Info_ objects. These objects are refcounted wrappers + * around vbucket configuration handles. They have a refcount and also an + * integer which can be used to compare with other objects for 'freshness'. + * See clconfig_info + *
    6. + * + *
    7. + * _Configuration Listeners_. These are registered with the global supervisor + * and are invoked whenever a new valid configuration is detected. This is + * really only ever used during bootstrap or testing where we are explicitly + * waiting for a configuration without having any actual commands to schedule. + * See clconfig_listener + *
    8. + *
    + */ + +/** + *@addtogroup lcb-confmon + *@{ + */ + +/** + * @brief Enumeration of the various config providers available. + * The type of methods available. These are enumerated in order of preference + */ +typedef enum { + /** Currently unused. The intent here is to allow a user to provide means + * by which the application may give a configuration file to the library */ + LCB_CLCONFIG_USER, + /** File-based "configcache" provider. Implemented in bc_file.c */ + LCB_CLCONFIG_FILE, + /** New-style config-over-memcached provider. Implemented in bc_cccp.c */ + LCB_CLCONFIG_CCCP, + /** Old-style streaming HTTP provider. Implemented in bc_http.c */ + LCB_CLCONFIG_HTTP, + /** Raw memcached provided */ + LCB_CLCONFIG_MCRAW, + + LCB_CLCONFIG_MAX, + + /** Ephemeral source, used for tests */ + LCB_CLCONFIG_PHONY +} clconfig_method_t; + + +struct clconfig_info_st; +struct clconfig_provider_st; +struct clconfig_listener_st; +struct lcb_confmon_st; + +/** + * This object contains the information needed for libcouchbase to deal with + * when retrieving new configs. + */ +typedef struct lcb_confmon_st { + /** Linked list of active/enabled providers */ + lcb_clist_t active_providers; + + /**Current provider. This provider may either fail or succeed. + * In either case unless the provider can provide us with a specific + * config which is newer than the one we have, it will roll over to the + * next provider. */ + struct clconfig_provider_st *cur_provider; + + /** All providers we know about. Currently this means the 'builtin' providers */ + struct clconfig_provider_st * all_providers[LCB_CLCONFIG_MAX]; + + /** The current configuration pointer. This contains the most recent accepted + * configuration */ + struct clconfig_info_st * config; + + /* CONFMON_S_* values. Used internally */ + int state; + + /** Last time the provider was stopped. As a microsecond timestamp */ + lcb_uint32_t last_stop_us; + + /** This is the async handle for a reentrant start */ + lcbio_pTIMER as_start; + + /** Async handle for a reentrant stop */ + lcbio_pTIMER as_stop; + + /** List of listeners for events */ + lcb_list_t listeners; + lcb_settings *settings; + lcb_error_t last_error; + lcbio_pTABLE iot; +} lcb_confmon; + +/** + * The base structure of a provider. This structure is intended to be + * 'subclassed' by implementors. + */ +typedef struct clconfig_provider_st { + lcb_list_t ll; /**< Node in linked list of active providers (if active) */ + + /** The type of provider */ + clconfig_method_t type; + + /** Whether this provider has been disabled/enabled explicitly by a user */ + int enabled; + + /** The parent manager object */ + struct lcb_confmon_st *parent; + + /** + * Get the current map known to this provider. This should not perform + * any blocking operations. Providers which use a push model may use + * this method as an asynchronous return value for a previously-received + * configuration. + * + * @param pb + */ + struct clconfig_info_st* (*get_cached)(struct clconfig_provider_st *pb); + + + /** + * Request a new configuration. This will be called by the manager when + * the cached configuration (i.e. 'get_cached') is deemed invalid. Thus + * this function should unconditionally try to schedule getting the + * newest configuration it can. When the configuration has been received, + * the provider may call provider_success or provider_failed. + * + * @note + * The PROVIDER is responsible for terminating its own + * process. In other words there is no safeguard within confmon itself + * against a provider taking an excessively long time; therefore a provider + * should implement a timeout mechanism of its choice to promptly deliver + * a success or failure. + * + * @param pb + */ + lcb_error_t (*refresh)(struct clconfig_provider_st *pb); + + /** + * Callback invoked to the provider to indicate that it should cease + * performing any "Active" configuration changes. Note that this is only + * a hint and a provider may perform its own hooking based on this. In any + * event receiving this callback is indicating that the provider will not + * be needed again in quite some time. How long this "time" is can range + * between 0 seconds and several minutes depending on how a user has + * configured the client. + * @param pb + */ + lcb_error_t (*pause)(struct clconfig_provider_st *pb); + + /** + * Called when a new configuration has been received. + * + * @param provider the provider instance + * @param config the current configuration. + * Note that this should only update the server list and do nothing + * else. + */ + void (*config_updated)(struct clconfig_provider_st *provider, + lcbvb_CONFIG* config); + + /** + * Retrieve the list of nodes from this provider, if applicable + * @param p the provider + * @return A list of nodes, or NULL if the provider does not have a list + */ + hostlist_t (*get_nodes)(const struct clconfig_provider_st *p); + + /** + * Call to change the configured nodes of this provider. + * @param p The provider + * @param l The list of nodes to apply + */ + void (*configure_nodes)(struct clconfig_provider_st *p, const hostlist_t l); + + /** Destroy the resources created by this provider. */ + void (*shutdown)(struct clconfig_provider_st *); + + /** + * Dump state information. This callback is optional + * @param p the provider + * @param f the file to write to + */ + void (*dump)(struct clconfig_provider_st *p, FILE *f); +} clconfig_provider; + + +/** @brief refcounted object encapsulating a vbucket config */ +typedef struct clconfig_info_st { + /** Actual configuration */ + lcbvb_CONFIG* vbc; + + /** Comparative clock with which to compare */ + lcb_uint64_t cmpclock; + + /** Reference counter */ + unsigned int refcount; + + /** Origin provider type which produced this config */ + clconfig_method_t origin; +} clconfig_info; + +/** Event types propagated to listeners */ +typedef enum { + /** Called when a new configuration is being set in confmon */ + CLCONFIG_EVENT_GOT_NEW_CONFIG, + + /** Called when _any_ configuration is received via set_enxt */ + CLCONFIG_EVENT_GOT_ANY_CONFIG, + + /** Called when all providers have been tried */ + CLCONFIG_EVENT_PROVIDERS_CYCLED, + + /** The monitor has stopped */ + CLCONFIG_EVENT_MONITOR_STOPPED +} clconfig_event_t; + +/** + * @brief Listener for events + * One or more listeners may be installed into the confmon which will have + * a callback invoked on significant vbucket events. See clconfig_event_t + * for a variety of events the listener can know. + */ +typedef struct clconfig_listener_st { + /** Linked list node */ + lcb_list_t ll; + + /** Monitor object */ + lcb_confmon *parent; + + /** + * Callback invoked for significant events + * + * @param lsn the listener structure itself + * @param event the event which took place + * @param config the configuration associated with the event. Note that + * `config` may also be NULL + */ + void (*callback)(struct clconfig_listener_st *lsn, clconfig_event_t event, + struct clconfig_info_st *config); + +} clconfig_listener; + +/* Method-specific setup methods.. */ + +clconfig_provider * lcb_clconfig_create_http(lcb_confmon *mon); +clconfig_provider * lcb_clconfig_create_cccp(lcb_confmon *mon); +clconfig_provider * lcb_clconfig_create_file(lcb_confmon *mon); +clconfig_provider * lcb_clconfig_create_user(lcb_confmon *mon); +clconfig_provider * lcb_clconfig_create_mcraw(lcb_confmon *mon); + +/**@brief Get a provider by its type + * @param mon the monitor + * @param ix a clconfig_method_t indicating the type of provider to fetch + * @return a pointer to the provider of the given type + */ +#define lcb_confmon_get_provider(mon, ix) (mon)->all_providers[ix] + +/** + * @brief Macro used to retrieve a setting from a provider + * @param p the provider pointer + * @param n the name of the setting field to retrieve as token + */ +#define PROVIDER_SETTING(p, n) ((p)->parent->settings->n) + +/** + * @brief Macro used by a provider to set the failure code in the parent + * lcb_confmon object + * @param p the provider pointer + * @param e the error code + */ +#define PROVIDER_SET_ERROR(p, e) (p)->parent->last_error = e + +/** + * @brief Create a new configuration monitor. + * This function creates a new `confmon` object which can be used to manage + * configurations and their providers. + * + * @param settings + * @param iot + * + * Once the confmon object has been created you may enable or disable various + * providers (see lcb_confmon_set_provider_active()). Once no more providers + * remain to be activated you should call lcb_confmon_prepare() once. Then + * call the rest of the functions. + */ +LIBCOUCHBASE_API +lcb_confmon * +lcb_confmon_create(lcb_settings *settings, lcbio_pTABLE iot); + +/**Destroy the confmon object. + * @param mon */ +LIBCOUCHBASE_API +void +lcb_confmon_destroy(lcb_confmon *mon); + +/** + * Prepares the configuration monitor object for operations. This will insert + * all the enabled providers into a list. Call this function each time a + * provider has been enabled. + * @param mon + */ +LIBCOUCHBASE_API +void +lcb_confmon_prepare(lcb_confmon *mon); + +LCB_INTERNAL_API +void +lcb_confmon_set_provider_active(lcb_confmon *mon, + clconfig_method_t type, int enabled); + + +/** + * @brief Request a configuration refresh + * + * Start traversing the list of current providers, requesting a new + * configuration for each. This function will asynchronously loop through all + * providers until one provides a new configuration. + * + * You may call lcb_confmon_stop() to asynchronously break out of the loop. + * If the confmon is already in a refreshing state + * (i.e. lcb_confmon_is_refreshing()) returns true then this function does + * nothing. + * + * This function is reentrant safe and may be called at any time. + * + * @param mon + * @see lcb_confmon_add_listener() + * @see lcb_confmon_stop() + * @see lcb_confmon_is_refreshing() + */ +LIBCOUCHBASE_API +lcb_error_t lcb_confmon_start(lcb_confmon *mon); + +/** + * @brief Cancel a pending configuration refresh. + * + * Stops the monitor. This will call clconfig_provider::pause() for each active + * provider. Typically called before destruction or when a new configuration + * has been found. + * + * This function is safe to call anywhere. If the monitor is already stopped + * then this function does nothing. + * + * @param mon + * @see lcb_confmon_start() + * @see lcb_confmon_is_refreshing() + */ +LIBCOUCHBASE_API +lcb_error_t lcb_confmon_stop(lcb_confmon *mon); + +/** + * @brief Check if the monitor is waiting for a new config from a provider + * @param mon + * @return true if refreshing, false if idle + */ +LCB_INTERNAL_API +int lcb_confmon_is_refreshing(lcb_confmon *mon); + + +/**@brief Get the current configuration object + * @return The current configuration */ +#define lcb_confmon_get_config(mon) (mon)->config + +/**@brief Get the last error code set by a provider + * @return the last error code (if failure) */ +#define lcb_confmon_last_error(mon) (mon)->last_error + +/** + * @brief Indicate that a provider has failed and advance the monitor + * + * Indicate that the current provider has failed to obtain a new configuration. + * This is always called by a provider and should be invoked when the provider + * has encountered an internal error which caused it to be unable to fetch + * the configuration. + * + * Note that this function is safe to call from any provider at any time. If + * the provider is not the current provider then it is treated as an async + * push notification failure and ignored. This function is _not_ safe to call + * from consumers of providers + * + * Once this is called, the confmon instance will either roll over to the next + * provider or enter the inactive state depending on the configuration and + * whether the current provider is the last provider in the list. + * + * @param provider + * @param err + */ +LIBCOUCHBASE_API +void +lcb_confmon_provider_failed(clconfig_provider *provider, lcb_error_t err); + + +/** + * @brief Indicate that a provider has successfuly retrieved a configuration. + * + * Indicates that the provider has fetched a new configuration from the network + * and that confmon should attempt to propagate it. It has similar semantics + * to lcb_confmon_provider_failed() except that the second argument is a config + * object rather than an error code. The second argument must not be `NULL` + * + * The monitor will compare the new config against the current config. + * If the new config does not feature any changes from the current config then + * it is ignored and the confmon instance will proceed to the next provider. + * This is done through a direct call to provider_failed(provider, LCB_SUCCESS). + * + * This function should _not_ be called outside of an asynchronous provider's + * handler. + * + * @param provider the provider which yielded the new configuration + * @param info the new configuration + */ +LIBCOUCHBASE_API +void +lcb_confmon_provider_success(clconfig_provider *provider, clconfig_info *info); + +/** + * @brief Register a listener to be invoked on state changes and events + * + * Adds a 'listener' object to be called at each configuration update. The + * listener may co-exist with other listeners (though it should never be added + * twice). When a new configuration is received and accept, the listener's + * clconfig_listener::callback field will be invoked with it. + * + * The callback will continue to be invoked for each new configuration received + * until remove_listener is called. Note that the listener is not allocated + * by the confmon and its responsibility is the user's + * + * @param mon the monitor + * @param listener the listener. The listener's contents are not copied into + * confmon and should thus remain valid until it is removed + */ +LIBCOUCHBASE_API +void lcb_confmon_add_listener(lcb_confmon *mon, clconfig_listener *listener); + +/** + * @brief Unregister (and remove) a listener added via lcb_confmon_add_listener() + * @param mon the monitor + * @param listener the listener + */ +LIBCOUCHBASE_API +void lcb_confmon_remove_listener(lcb_confmon *mon, clconfig_listener *listener); + +/** @brief Possible confmon states */ +typedef enum { + /** The monitor is idle and not requesting a new configuration */ + CONFMON_S_INACTIVE = 0, + + /** The monitor is actively requesting a configuration */ + CONFMON_S_ACTIVE = 1 << 0, + + /** The monitor is fetching a configuration, but is in a throttle state */ + CONFMON_S_ITERGRACE = 1 << 1 +} confmon_state_t; + +/** + * @brief Get the current monitor state + * @param mon the monitor + * @return a set of flags consisting of confmon_state_t values. + */ +#define lcb_confmon_get_state(mon) (mon)->state + +/** + * Creates a new configuration wrapper object containing the vbucket config + * pointed to by 'config'. Its initial refcount will be set to 1. + * + * @param config a newly parsed configuration + * @param origin the type of provider from which the config originated. + */ +clconfig_info * +lcb_clconfig_create(lcbvb_CONFIG* config, clconfig_method_t origin); + +/** + * @brief Compares two info structures and determine which one is newer + * + * This function returns an integer less than + * zero, zero or greater than zero if the first argument is considered older + * than, equal to, or later than the second argument. + * @param a + * @param b + * @see lcbvb_get_revision + * @see clconfig_info::cmpclock + */ +LIBCOUCHBASE_API +int lcb_clconfig_compare(const clconfig_info *a, const clconfig_info *b); + +/** + * @brief Decrement the refcount on a config object. + * Decrement the refcount. If the internal refcount reaches 0 then the internal + * members (including the vbucket config handle itself) will be freed. + * @param info the configuration + */ +void lcb_clconfig_decref(clconfig_info *info); + +/** + * @brief Increment the refcount on a config object + * @param info the config object + */ +#define lcb_clconfig_incref(info) (info)->refcount++ + +/** Dump information about the monitor + * @param mon the monitor object + * @param fp the file to which information should be written + */ +void lcb_confmon_dump(lcb_confmon *mon, FILE *fp); + +/** + * @name File Provider-specific APIs + * @{ + */ + +/** + * Sets the input/output filename for the file provider. This also enables + * the file provider. + * @param p the provider + * @param f the filename (if NULL, a temporary filename will be created) + * @param ro whether the client will never modify the file + * @return zero on success, nonzero on failure. + */ +int lcb_clconfig_file_set_filename(clconfig_provider *p, const char *f, int ro); + +/** + * Retrieve the filename for the provider + * @param p The provider of type LCB_CLCONFIG_FILE + * @return the current filename being used. + */ +const char * lcb_clconfig_file_get_filename(clconfig_provider *p); + +void lcb_clconfig_file_set_readonly(clconfig_provider *p, int val); +/**@}*/ + +/** + * @name HTTP Provider-specific APIs + * @{ + */ + +/** + * Get the socket representing the current REST connection to the cluster + * (if applicable) + * @param mon + * @return + */ +lcbio_SOCKET* lcb_confmon_get_rest_connection(lcb_confmon *mon); + +/** + * Get the hostname for the current REST connection to the cluster + * @param mon + * @return + */ +lcb_host_t * lcb_confmon_get_rest_host(lcb_confmon *mon); + +/** + * Enables the HTTP provider + * @param pb a provider of type LCB_CLCONFIG_HTTP + */ +LCB_INTERNAL_API +void lcb_clconfig_http_enable(clconfig_provider *pb); +#define lcb_clconfig_http_set_nodes lcb_clconfig_cccp_set_nodes +/**@}*/ + +/** + * @name CCCP Provider-specific APIs + * @{ + */ +LCB_INTERNAL_API +void lcb_clconfig_cccp_enable(clconfig_provider *pb, lcb_t instance); + +/** + * @brief Notify the CCCP provider about a new configuration from a + * `NOT_MY_VBUCKET` response + * + * This should be called by the packet handler when a configuration has been + * received as a payload to a response with the error of `NOT_MY_VBUCKET`. + * + * @param provider The CCCP provider + * @param host The hostname (without the port) on which the packet was received + * @param data The configuration JSON blob + * @return LCB_SUCCESS, or an error code if the configuration could not be + * set + */ +lcb_error_t +lcb_cccp_update(clconfig_provider *provider, const char *host, const char *data); + +/** + * @brief Notify the CCCP provider about a configuration received from a + * `CMD_GET_CLUSTER_CONFIG` response. + * + * @param cookie The cookie object attached to the packet + * @param err The error code for the reply + * @param bytes The payload pointer + * @param nbytes Size of payload + * @param origin Host object from which the packet was received + */ +void +lcb_cccp_update2(const void *cookie, lcb_error_t err, + const void *bytes, lcb_size_t nbytes, const lcb_host_t *origin); + +#define lcb_clconfig_cccp_set_nodes(pb, nodes) (pb)->configure_nodes(pb, nodes) +/**@}*/ + +/**@name Raw Memcached (MCRAW) Provider-specific APIs + * @{*/ +LCB_INTERNAL_API +lcb_error_t +lcb_clconfig_mcraw_update(clconfig_provider *pb, const char *nodes); +/**@}*/ + +/**@}*/ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* LCB_CLCONFIG_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/confmon.c b/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/confmon.c new file mode 100644 index 00000000..fcab197d --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/bucketconfig/confmon.c @@ -0,0 +1,474 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" +#include "clconfig.h" +#define LOGARGS(mon, lvlbase) mon->settings, "confmon", LCB_LOG_##lvlbase, __FILE__, __LINE__ +#define LOG(mon, lvlbase, msg) lcb_log(mon->settings, "confmon", LCB_LOG_##lvlbase, __FILE__, __LINE__, msg) + +static void async_stop(void *); +static void async_start(void *); +static int do_next_provider(lcb_confmon *mon); +static void invoke_listeners(lcb_confmon *mon, + clconfig_event_t event, + clconfig_info *info); + +#define IS_REFRESHING(mon) ((mon)->state & CONFMON_S_ACTIVE) + +static clconfig_provider *next_active(lcb_confmon *mon, clconfig_provider *cur) +{ + if (!LCB_LIST_HAS_NEXT((lcb_list_t *)&mon->active_providers, &cur->ll)) { + return NULL; + } + return LCB_LIST_ITEM(cur->ll.next, clconfig_provider, ll); +} +static clconfig_provider *first_active(lcb_confmon *mon) +{ + if (LCB_LIST_IS_EMPTY((lcb_list_t *)&mon->active_providers)) { + return NULL; + } + return LCB_LIST_ITEM(mon->active_providers.next, clconfig_provider, ll); +} + +static const char * +provider_string(clconfig_method_t type) { + if (type == LCB_CLCONFIG_HTTP) { return "HTTP"; } + if (type == LCB_CLCONFIG_CCCP) { return "CCCP"; } + if (type == LCB_CLCONFIG_FILE) { return "FILE"; } + if (type == LCB_CLCONFIG_MCRAW) { return "MCRAW"; } + if (type == LCB_CLCONFIG_USER) { return "USER"; } + return ""; +} + +lcb_confmon* lcb_confmon_create(lcb_settings *settings, lcbio_pTABLE iot) +{ + int ii; + lcb_confmon * mon = calloc(1, sizeof(*mon)); + mon->settings = settings; + mon->iot = iot; + lcb_list_init(&mon->listeners); + lcb_clist_init(&mon->active_providers); + lcbio_table_ref(mon->iot); + lcb_settings_ref(mon->settings); + + mon->all_providers[LCB_CLCONFIG_FILE] = lcb_clconfig_create_file(mon); + mon->all_providers[LCB_CLCONFIG_CCCP] = lcb_clconfig_create_cccp(mon); + mon->all_providers[LCB_CLCONFIG_HTTP] = lcb_clconfig_create_http(mon); + mon->all_providers[LCB_CLCONFIG_USER] = lcb_clconfig_create_user(mon); + mon->all_providers[LCB_CLCONFIG_MCRAW] = lcb_clconfig_create_mcraw(mon); + + for (ii = 0; ii < LCB_CLCONFIG_MAX; ii++) { + mon->all_providers[ii]->parent = mon; + } + mon->as_stop = lcbio_timer_new(iot, mon, async_stop); + mon->as_start = lcbio_timer_new(iot, mon, async_start); + return mon; +} + +void lcb_confmon_prepare(lcb_confmon *mon) +{ + int ii; + + memset(&mon->active_providers, 0, sizeof(mon->active_providers)); + lcb_clist_init(&mon->active_providers); + + lcb_log(LOGARGS(mon, DEBUG), "Preparing providers (this may be called multiple times)"); + + for (ii = 0; ii < LCB_CLCONFIG_MAX; ii++) { + clconfig_provider *cur = mon->all_providers[ii]; + if (cur) { + if (cur->enabled) { + lcb_clist_append(&mon->active_providers, &cur->ll); + lcb_log(LOGARGS(mon, DEBUG), "Provider %s is ENABLED", provider_string(cur->type)); + } else if (cur->pause){ + cur->pause(cur); + lcb_log(LOGARGS(mon, DEBUG), "Provider %s is DISABLED", provider_string(cur->type)); + } + } + } + + lcb_assert(LCB_CLIST_SIZE(&mon->active_providers)); + mon->cur_provider = first_active(mon); +} + +void lcb_confmon_destroy(lcb_confmon *mon) +{ + unsigned int ii; + + if (mon->as_start) { + lcbio_timer_destroy(mon->as_start); + } + + if (mon->as_stop) { + lcbio_timer_destroy(mon->as_stop); + } + + mon->as_start = NULL; + mon->as_stop = NULL; + + if (mon->config) { + lcb_clconfig_decref(mon->config); + mon->config = NULL; + } + + for (ii = 0; ii < LCB_CLCONFIG_MAX; ii++) { + clconfig_provider *provider = mon->all_providers[ii]; + if (provider == NULL) { + continue; + } + + provider->shutdown(provider); + mon->all_providers[ii] = NULL; + } + + lcbio_table_unref(mon->iot); + lcb_settings_unref(mon->settings); + + free(mon); +} + +static int do_set_next(lcb_confmon *mon, clconfig_info *info, int notify_miss) +{ + unsigned ii; + + if (mon->config) { + lcbvb_CHANGETYPE chstatus = LCBVB_NO_CHANGES; + lcbvb_CONFIGDIFF *diff = lcbvb_compare(mon->config->vbc, info->vbc); + + if (!diff) { + lcb_log(LOGARGS(mon, DEBUG), "Couldn't create vbucket diff"); + return 0; + } + + chstatus = lcbvb_get_changetype(diff); + lcbvb_free_diff(diff); + + if (chstatus == 0 || lcb_clconfig_compare(mon->config, info) >= 0) { + const lcbvb_CONFIG *ca, *cb; + + ca = mon->config->vbc; + cb = info->vbc; + + lcb_log(LOGARGS(mon, INFO), "Not applying configuration received via %s. No changes detected. A.rev=%d, B.rev=%d", provider_string(info->origin), ca->revid, cb->revid); + if (notify_miss) { + invoke_listeners(mon, CLCONFIG_EVENT_GOT_ANY_CONFIG, info); + } + return 0; + } + } + + lcb_log(LOGARGS(mon, INFO), "Setting new configuration. Received via %s", provider_string(info->origin)); + + if (mon->config) { + /** DECREF the old one */ + lcb_clconfig_decref(mon->config); + } + + for (ii = 0; ii < LCB_CLCONFIG_MAX; ii++) { + clconfig_provider *cur = mon->all_providers[ii]; + if (cur && cur->enabled && cur->config_updated) { + cur->config_updated(cur, info->vbc); + } + } + + lcb_clconfig_incref(info); + mon->config = info; + lcb_confmon_stop(mon); + + invoke_listeners(mon, CLCONFIG_EVENT_GOT_NEW_CONFIG, info); + + return 1; +} + +void lcb_confmon_provider_failed(clconfig_provider *provider, + lcb_error_t reason) +{ + lcb_confmon *mon = provider->parent; + + lcb_log(LOGARGS(mon, INFO), "Provider '%s' failed", provider_string(provider->type)); + + if (provider != mon->cur_provider) { + lcb_log(LOGARGS(mon, TRACE), "Ignoring failure. Current=%p (%s)", (void*)mon->cur_provider, provider_string(mon->cur_provider->type)); + return; + } + if (!lcb_confmon_is_refreshing(mon)) { + lcb_log(LOGARGS(mon, DEBUG), "Ignoring failure. Refresh not active"); + } + + if (reason != LCB_SUCCESS) { + if (mon->settings->detailed_neterr && mon->last_error != LCB_SUCCESS) { + /* Filter out any artificial 'connect error' or 'network error' codes */ + if (reason != LCB_CONNECT_ERROR && reason != LCB_NETWORK_ERROR) { + mon->last_error = reason; + } + } else { + mon->last_error = reason; + } + } + + mon->cur_provider = next_active(mon, mon->cur_provider); + + if (!mon->cur_provider) { + LOG(mon, TRACE, "Maximum provider reached. Resetting index"); + invoke_listeners(mon, CLCONFIG_EVENT_PROVIDERS_CYCLED, NULL); + mon->cur_provider = first_active(mon); + lcb_confmon_stop(mon); + } else { + uint32_t interval = 0; + if (mon->config) { + /* Not first */ + interval = PROVIDER_SETTING(provider, grace_next_provider); + } + lcb_log(LOGARGS(mon, DEBUG), "Will try next provider in %uus", interval); + mon->state |= CONFMON_S_ITERGRACE; + lcbio_timer_rearm(mon->as_start, interval); + } +} + +void lcb_confmon_provider_success(clconfig_provider *provider, + clconfig_info *config) +{ + do_set_next(provider->parent, config, 1); + lcb_confmon_stop(provider->parent); +} + + +static int do_next_provider(lcb_confmon *mon) +{ + lcb_list_t *ii; + mon->state &= ~CONFMON_S_ITERGRACE; + + LCB_LIST_FOR(ii, (lcb_list_t *)&mon->active_providers) { + clconfig_info *info; + clconfig_provider *cached_provider; + + cached_provider = LCB_LIST_ITEM(ii, clconfig_provider, ll); + info = cached_provider->get_cached(cached_provider); + if (!info) { + continue; + } + + if (do_set_next(mon, info, 0)) { + LOG(mon, DEBUG, "Using cached configuration"); + return 1; + } + } + + lcb_log(LOGARGS(mon, TRACE), "Current provider is %s", provider_string(mon->cur_provider->type)); + + mon->cur_provider->refresh(mon->cur_provider); + return 0; +} + +static void async_start(void *arg) +{ + do_next_provider(arg); +} + +lcb_error_t lcb_confmon_start(lcb_confmon *mon) +{ + lcb_U32 tmonext = 0; + + lcbio_async_cancel(mon->as_stop); + if (IS_REFRESHING(mon)) { + LOG(mon, DEBUG, "Refresh already in progress..."); + return LCB_SUCCESS; + } + + LOG(mon, TRACE, "Start refresh requested"); + lcb_assert(mon->cur_provider); + mon->state = CONFMON_S_ACTIVE|CONFMON_S_ITERGRACE; + + if (mon->last_stop_us > 0) { + lcb_U32 diff = LCB_NS2US(gethrtime()) - mon->last_stop_us; + if (diff <= mon->settings->grace_next_cycle) { + tmonext = mon->settings->grace_next_cycle - diff; + } + } + + lcbio_timer_rearm(mon->as_start, tmonext); + return LCB_SUCCESS; +} + +static void async_stop(void *arg) +{ + lcb_confmon *mon = arg; + lcb_list_t *ii; + + LCB_LIST_FOR(ii, (lcb_list_t *)&mon->active_providers) { + clconfig_provider *provider = LCB_LIST_ITEM(ii, clconfig_provider, ll); + if (!provider->pause) { + continue; + } + provider->pause(provider); + } + + mon->last_stop_us = LCB_NS2US(gethrtime()); + invoke_listeners(mon, CLCONFIG_EVENT_MONITOR_STOPPED, NULL); +} + +lcb_error_t lcb_confmon_stop(lcb_confmon *mon) +{ + if (!IS_REFRESHING(mon)) { + return LCB_SUCCESS; + } + lcbio_timer_disarm(mon->as_start); + lcbio_async_signal(mon->as_stop); + mon->state = CONFMON_S_INACTIVE; + return LCB_SUCCESS; +} + +void lcb_clconfig_decref(clconfig_info *info) +{ + lcb_assert(info->refcount); + + if (--info->refcount) { + return; + } + + if (info->vbc) { + lcbvb_destroy(info->vbc); + } + + free(info); +} + +int lcb_clconfig_compare(const clconfig_info *a, const clconfig_info *b) +{ + /** First check if both have revisions */ + int rev_a, rev_b; + rev_a = lcbvb_get_revision(a->vbc); + rev_b = lcbvb_get_revision(b->vbc); + if (rev_a >= 0 && rev_b >= 0) { + return rev_a - rev_b; + } + + if (a->cmpclock == b->cmpclock) { + return 0; + + } else if (a->cmpclock < b->cmpclock) { + return -1; + } + + return 1; +} + +clconfig_info * +lcb_clconfig_create(lcbvb_CONFIG* config, clconfig_method_t origin) +{ + clconfig_info *info = calloc(1, sizeof(*info)); + if (!info) { + return NULL; + } + info->refcount = 1; + info->vbc = config; + info->origin = origin; + return info; +} + +void lcb_confmon_add_listener(lcb_confmon *mon, clconfig_listener *listener) +{ + listener->parent = mon; + lcb_list_append(&mon->listeners, &listener->ll); +} + +void lcb_confmon_remove_listener(lcb_confmon *mon, clconfig_listener *listener) +{ + lcb_list_delete(&listener->ll); + (void)mon; +} + +static void invoke_listeners(lcb_confmon *mon, + clconfig_event_t event, + clconfig_info *info) +{ + lcb_list_t *ll, *ll_next; + LCB_LIST_SAFE_FOR(ll, ll_next, &mon->listeners) { + clconfig_listener *lsn = LCB_LIST_ITEM(ll, clconfig_listener, ll); + lsn->callback(lsn, event, info); + } +} + +static void generic_shutdown(clconfig_provider *provider) +{ + free(provider); +} + +clconfig_provider * lcb_clconfig_create_user(lcb_confmon *mon) +{ + clconfig_provider *provider = calloc(1, sizeof(*provider)); + provider->type = LCB_CLCONFIG_USER; + provider->shutdown = generic_shutdown; + + (void)mon; + return provider; +} + +LCB_INTERNAL_API +int lcb_confmon_is_refreshing(lcb_confmon *mon) +{ + return IS_REFRESHING(mon); +} + +LCB_INTERNAL_API +void +lcb_confmon_set_provider_active(lcb_confmon *mon, + clconfig_method_t type, int enabled) +{ + clconfig_provider *provider = mon->all_providers[type]; + if (provider->enabled == enabled) { + return; + } else { + provider->enabled = enabled; + } + lcb_confmon_prepare(mon); +} + +void +lcb_confmon_dump(lcb_confmon *mon, FILE *fp) +{ + unsigned ii; + fprintf(fp, "CONFMON=%p\n", (void*)mon); + fprintf(fp, "STATE= (0x%x)", mon->state); + if (mon->state & CONFMON_S_ACTIVE) { + fprintf(fp, "ACTIVE|"); + } + if (mon->state == CONFMON_S_INACTIVE) { + fprintf(fp, "INACTIVE/IDLE"); + } + if (mon->state & CONFMON_S_ITERGRACE) { + fprintf(fp, "ITERGRACE"); + } + fprintf(fp, "\n"); + fprintf(fp, "LAST ERROR: 0x%x\n", mon->last_error); + + + for (ii = 0; ii < LCB_CLCONFIG_MAX; ii++) { + clconfig_provider *cur = mon->all_providers[ii]; + if (!cur) { + continue; + } + + fprintf(fp, "** PROVIDER: 0x%x (%s) %p\n", cur->type, provider_string(cur->type), (void*)cur); + fprintf(fp, "** ENABLED: %s\n", cur->enabled ? "YES" : "NO"); + fprintf(fp, "** CURRENT: %s\n", cur == mon->cur_provider ? "YES" : "NO"); + if (cur->dump) { + cur->dump(cur, fp); + } + fprintf(fp, "\n"); + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/callbacks.c b/couchbase-sys/libcouchbase-2.7.0/src/callbacks.c new file mode 100644 index 00000000..162e7b2c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/callbacks.c @@ -0,0 +1,378 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" + +#define DEFINE_DUMMY_CALLBACK(name, resptype) \ + static void name(lcb_t i, const void *c, lcb_error_t e, const resptype *r) \ + { (void)i;(void)e;(void)c;(void)r; } + +static void dummy_error_callback(lcb_t instance,lcb_error_t error, const char *errinfo) { + lcb_breakout(instance); + (void)error; + (void)errinfo; +} + +static void dummy_store_callback(lcb_t instance, const void *cookie, + lcb_storage_t operation, lcb_error_t error, const lcb_store_resp_t *resp) { + (void)instance; (void)cookie; (void)operation; (void)error; (void)resp; +} + + +static void dummy_http_callback(lcb_http_request_t request, lcb_t instance, + const void *cookie, lcb_error_t error, const lcb_http_resp_t *resp) { + (void)request;(void)instance;(void)cookie;(void)error; (void)resp; +} + +static void dummy_configuration_callback(lcb_t instance, lcb_configuration_t val) { + (void)instance; (void)val; +} + +static void dummy_bootstrap_callback(lcb_t instance, lcb_error_t err) { + (void)instance; (void)err; +} + +static void dummy_pktfwd_callback(lcb_t instance, const void *cookie, + lcb_error_t err, lcb_PKTFWDRESP *resp) { + (void)instance;(void)cookie;(void)err;(void)resp; +} +static void dummy_pktflushed_callback(lcb_t instance, const void *cookie) { + (void)instance;(void)cookie; +} + +DEFINE_DUMMY_CALLBACK(dummy_stat_callback, lcb_server_stat_resp_t) +DEFINE_DUMMY_CALLBACK(dummy_version_callback, lcb_server_version_resp_t) +DEFINE_DUMMY_CALLBACK(dummy_verbosity_callback, lcb_verbosity_resp_t) +DEFINE_DUMMY_CALLBACK(dummy_get_callback, lcb_get_resp_t) +DEFINE_DUMMY_CALLBACK(dummy_arithmetic_callback, lcb_arithmetic_resp_t) +DEFINE_DUMMY_CALLBACK(dummy_remove_callback, lcb_remove_resp_t) +DEFINE_DUMMY_CALLBACK(dummy_touch_callback, lcb_touch_resp_t) +DEFINE_DUMMY_CALLBACK(dummy_flush_callback, lcb_flush_resp_t) +DEFINE_DUMMY_CALLBACK(dummy_unlock_callback, lcb_unlock_resp_t) +DEFINE_DUMMY_CALLBACK(dummy_observe_callback, lcb_observe_resp_t) +DEFINE_DUMMY_CALLBACK(dummy_durability_callback, lcb_durability_resp_t) + +typedef union { + lcb_RESPBASE base; + lcb_RESPGET get; + lcb_RESPSTORE store; + lcb_RESPUNLOCK unlock; + lcb_RESPCOUNTER arith; + lcb_RESPREMOVE del; + lcb_RESPENDURE endure; + lcb_RESPUNLOCK unl; + lcb_RESPFLUSH flush; + lcb_RESPSTATS stats; + lcb_RESPMCVERSION mcversion; + lcb_RESPVERBOSITY verbosity; + lcb_RESPOBSERVE observe; + lcb_RESPHTTP http; +} uRESP; + +static void +compat_default_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *r3base) +{ + const uRESP *r3 = (uRESP *)r3base; + const void *cookie = r3base->cookie; + lcb_error_t err = r3base->rc; + + #define FILL_KVC(dst) \ + (dst)->v.v0.key = r3base->key; \ + (dst)->v.v0.nkey = r3base->nkey; \ + (dst)->v.v0.cas = r3base->cas; + + switch (cbtype) { + case LCB_CALLBACK_GET: + case LCB_CALLBACK_GETREPLICA: { + lcb_get_resp_t r2 = { 0 }; + FILL_KVC(&r2) + r2.v.v0.bytes = r3->get.value; + r2.v.v0.nbytes = r3->get.nvalue; + r2.v.v0.flags = r3->get.itmflags; + r2.v.v0.datatype = r3->get.datatype; + instance->callbacks.get(instance, cookie, err, &r2); + break; + } + case LCB_CALLBACK_STORE: { + lcb_store_resp_t r2 = { 0 }; + FILL_KVC(&r2) + r2.v.v0.mutation_token = lcb_resp_get_mutation_token(cbtype, r3base); + instance->callbacks.store(instance, cookie, r3->store.op, err, &r2); + break; + } + case LCB_CALLBACK_COUNTER: { + lcb_arithmetic_resp_t r2 = { 0 }; + FILL_KVC(&r2); + r2.v.v0.value = r3->arith.value; + r2.v.v0.mutation_token = lcb_resp_get_mutation_token(cbtype, r3base); + instance->callbacks.arithmetic(instance, cookie, err, &r2); + break; + } + case LCB_CALLBACK_REMOVE: { + lcb_remove_resp_t r2 = { 0 }; + FILL_KVC(&r2) + r2.v.v0.mutation_token = lcb_resp_get_mutation_token(cbtype, r3base); + instance->callbacks.remove(instance, cookie, err, &r2); + break; + } + case LCB_CALLBACK_TOUCH: { + lcb_touch_resp_t r2 = { 0 }; + FILL_KVC(&r2) + instance->callbacks.touch(instance, cookie, err, &r2); + break; + } + case LCB_CALLBACK_UNLOCK: { + lcb_unlock_resp_t r2 = { 0 }; + r2.v.v0.key = r3->unl.key; + r2.v.v0.nkey = r3->unl.nkey; + instance->callbacks.unlock(instance, cookie, err, &r2); + break; + } + case LCB_CALLBACK_FLUSH: { + lcb_flush_resp_t r2 = { 0 }; + r2.v.v0.server_endpoint = r3->flush.server; + instance->callbacks.flush(instance, cookie, err, &r2); + break; + } + case LCB_CALLBACK_VERSIONS: { + lcb_server_version_resp_t r2 = { 0 }; + r2.v.v0.server_endpoint = r3->mcversion.server; + r2.v.v0.vstring = r3->mcversion.mcversion; + r2.v.v0.nvstring = r3->mcversion.nversion; + instance->callbacks.version(instance, cookie, err, &r2); + break; + } + case LCB_CALLBACK_VERBOSITY: { + lcb_verbosity_resp_t r2 = { 0 }; + r2.v.v0.server_endpoint = r3->verbosity.server; + instance->callbacks.verbosity(instance, cookie, err, &r2); + break; + } + case LCB_CALLBACK_STATS: { + lcb_server_stat_resp_t r2 = { 0 }; + r2.v.v0.key = r3->stats.key; + r2.v.v0.nkey = r3->stats.nkey; + r2.v.v0.bytes = r3->stats.value; + r2.v.v0.nbytes = r3->stats.nvalue; + r2.v.v0.server_endpoint = r3->stats.server; + instance->callbacks.stat(instance, cookie, err, &r2); + break; + } + case LCB_CALLBACK_OBSERVE: { + lcb_observe_resp_t r2 = { 0 }; + FILL_KVC(&r2); + r2.v.v0.status = r3->observe.status; + r2.v.v0.from_master = r3->observe.ismaster; + r2.v.v0.ttp = r3->observe.ttp; + r2.v.v0.ttr = r3->observe.ttr; + instance->callbacks.observe(instance, cookie, err, &r2); + break; + } + case LCB_CALLBACK_ENDURE: { + lcb_durability_resp_t r2 = { 0 }; + FILL_KVC(&r2); + r2.v.v0.err = r3->endure.rc; + r2.v.v0.exists_master = r3->endure.exists_master; + r2.v.v0.persisted_master = r3->endure.persisted_master; + r2.v.v0.npersisted = r3->endure.npersisted; + r2.v.v0.nreplicated = r3->endure.nreplicated; + r2.v.v0.nresponses = r3->endure.nresponses; + if (err == LCB_SUCCESS) { + err = r3->endure.rc; + } + instance->callbacks.durability(instance, cookie, err, &r2); + break; + } + case LCB_CALLBACK_HTTP: { + lcb_http_res_callback target; + lcb_http_resp_t r2 = { 0 }; + r2.v.v0.path = r3->http.key; + r2.v.v0.npath = r3->http.nkey; + r2.v.v0.bytes = r3->http.body; + r2.v.v0.nbytes = r3->http.nbody; + r2.v.v0.status = r3->http.htstatus; + r2.v.v0.headers = r3->http.headers; + if (!(r3base->rflags & LCB_RESP_F_FINAL)) { + target = instance->callbacks.http_data; + } else { + target = instance->callbacks.http_complete; + } + target(r3->http._htreq, instance, cookie, err, &r2); + break; + } + + default: + break; + } +} + +void lcb_initialize_packet_handlers(lcb_t instance) +{ + instance->callbacks.get = dummy_get_callback; + instance->callbacks.store = dummy_store_callback; + instance->callbacks.arithmetic = dummy_arithmetic_callback; + instance->callbacks.remove = dummy_remove_callback; + instance->callbacks.touch = dummy_touch_callback; + instance->callbacks.error = dummy_error_callback; + instance->callbacks.stat = dummy_stat_callback; + instance->callbacks.version = dummy_version_callback; + instance->callbacks.http_complete = dummy_http_callback; + instance->callbacks.http_data = dummy_http_callback; + instance->callbacks.flush = dummy_flush_callback; + instance->callbacks.unlock = dummy_unlock_callback; + instance->callbacks.configuration = dummy_configuration_callback; + instance->callbacks.observe = dummy_observe_callback; + instance->callbacks.verbosity = dummy_verbosity_callback; + instance->callbacks.durability = dummy_durability_callback; + instance->callbacks.errmap = lcb_errmap_default; + instance->callbacks.bootstrap = dummy_bootstrap_callback; + instance->callbacks.pktflushed = dummy_pktflushed_callback; + instance->callbacks.pktfwd = dummy_pktfwd_callback; + instance->callbacks.v3callbacks[LCB_CALLBACK_DEFAULT] = compat_default_callback; +} + +#define CALLBACK_ACCESSOR(name, cbtype, field) \ +LIBCOUCHBASE_API \ +cbtype name(lcb_t instance, cbtype cb) { \ + cbtype ret = instance->callbacks.field; \ + if (cb != NULL) { \ + instance->callbacks.field = cb; \ + } \ + return ret; \ +} + +LIBCOUCHBASE_API +lcb_destroy_callback +lcb_set_destroy_callback(lcb_t instance, lcb_destroy_callback cb) +{ + lcb_destroy_callback ret = LCBT_SETTING(instance, dtorcb); + if (cb) { + LCBT_SETTING(instance, dtorcb) = cb; + } + return ret; +} + +CALLBACK_ACCESSOR(lcb_set_get_callback, lcb_get_callback, get) +CALLBACK_ACCESSOR(lcb_set_store_callback, lcb_store_callback, store) +CALLBACK_ACCESSOR(lcb_set_arithmetic_callback, lcb_arithmetic_callback, arithmetic) +CALLBACK_ACCESSOR(lcb_set_observe_callback, lcb_observe_callback, observe) +CALLBACK_ACCESSOR(lcb_set_remove_callback, lcb_remove_callback, remove) +CALLBACK_ACCESSOR(lcb_set_touch_callback, lcb_touch_callback, touch) +CALLBACK_ACCESSOR(lcb_set_stat_callback, lcb_stat_callback, stat) +CALLBACK_ACCESSOR(lcb_set_version_callback, lcb_version_callback, version) +CALLBACK_ACCESSOR(lcb_set_error_callback, lcb_error_callback, error) +CALLBACK_ACCESSOR(lcb_set_flush_callback, lcb_flush_callback, flush) +CALLBACK_ACCESSOR(lcb_set_http_complete_callback, lcb_http_complete_callback, http_complete) +CALLBACK_ACCESSOR(lcb_set_http_data_callback, lcb_http_data_callback, http_data) +CALLBACK_ACCESSOR(lcb_set_unlock_callback, lcb_unlock_callback, unlock) +CALLBACK_ACCESSOR(lcb_set_configuration_callback, lcb_configuration_callback, configuration) +CALLBACK_ACCESSOR(lcb_set_verbosity_callback, lcb_verbosity_callback, verbosity) +CALLBACK_ACCESSOR(lcb_set_durability_callback, lcb_durability_callback, durability) +CALLBACK_ACCESSOR(lcb_set_errmap_callback, lcb_errmap_callback, errmap) +CALLBACK_ACCESSOR(lcb_set_bootstrap_callback, lcb_bootstrap_callback, bootstrap) +CALLBACK_ACCESSOR(lcb_set_pktfwd_callback, lcb_pktfwd_callback, pktfwd) +CALLBACK_ACCESSOR(lcb_set_pktflushed_callback, lcb_pktflushed_callback, pktflushed) + +LIBCOUCHBASE_API +lcb_RESPCALLBACK +lcb_install_callback3(lcb_t instance, int cbtype, lcb_RESPCALLBACK cb) +{ + lcb_RESPCALLBACK ret; + if (cbtype >= LCB_CALLBACK__MAX) { + return NULL; + } + + ret = instance->callbacks.v3callbacks[cbtype]; + instance->callbacks.v3callbacks[cbtype] = cb; + return ret; +} + +LIBCOUCHBASE_API +lcb_RESPCALLBACK +lcb_get_callback3(lcb_t instance, int cbtype) +{ + if (cbtype >= LCB_CALLBACK__MAX) { + return NULL; + } + return instance->callbacks.v3callbacks[cbtype]; +} + +LIBCOUCHBASE_API +const char * +lcb_strcbtype(int cbtype) +{ + switch (cbtype) { + case LCB_CALLBACK_GET: + return "GET"; + case LCB_CALLBACK_STORE: + return "STORE"; + case LCB_CALLBACK_COUNTER: + return "COUNTER"; + case LCB_CALLBACK_TOUCH: + return "TOUCH"; + case LCB_CALLBACK_REMOVE: + return "REMOVE"; + case LCB_CALLBACK_UNLOCK: + return "UNLOCK"; + case LCB_CALLBACK_STATS: + return "STATS"; + case LCB_CALLBACK_VERSIONS: + return "VERSIONS"; + case LCB_CALLBACK_VERBOSITY: + return "VERBOSITY"; + case LCB_CALLBACK_FLUSH: + return "FLUSH"; + case LCB_CALLBACK_OBSERVE: + return "OBSERVE"; + case LCB_CALLBACK_GETREPLICA: + return "GETREPLICA"; + case LCB_CALLBACK_ENDURE: + return "ENDURE"; + case LCB_CALLBACK_HTTP: + return "HTTP"; + case LCB_CALLBACK_CBFLUSH: + return "CBFLUSH"; + case LCB_CALLBACK_OBSEQNO: + return "OBSEQNO"; + case LCB_CALLBACK_STOREDUR: + return "STOREDUR"; + case LCB_CALLBACK_SDMUTATE: + return "SDMUTATE"; + case LCB_CALLBACK_SDLOOKUP: + return "SDLOOKUP"; + default: + return "UNKNOWN"; + } +} + +static void +nocb_fallback(lcb_t instance, int type, const lcb_RESPBASE *response) +{ + (void)instance; (void)type; (void)response; +} +lcb_RESPCALLBACK +lcb_find_callback(lcb_t instance, lcb_CALLBACKTYPE cbtype) +{ + lcb_RESPCALLBACK ret = instance->callbacks.v3callbacks[cbtype]; + if (!ret) { + ret = instance->callbacks.v3callbacks[LCB_CALLBACK_DEFAULT]; + if (!ret) { + ret = nocb_fallback; + } + } + return ret; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/cbft.cc b/couchbase-sys/libcouchbase-2.7.0/src/cbft.cc new file mode 100644 index 00000000..21ac3e89 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/cbft.cc @@ -0,0 +1,210 @@ +/* + * Copyright 2016 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +#include +#include +#include +#include "internal.h" +#include "http/http.h" +#include "logging.h" +#include "contrib/lcb-jsoncpp/lcb-jsoncpp.h" +#include + +#define LOGFMT "(FTR=%p) " +#define LOGID(req) static_cast(req) +#define LOGARGS(req, lvl) req->instance->settings, "n1ql", LCB_LOG_##lvl, __FILE__, __LINE__ + +struct lcb_FTSREQ { + const lcb_RESPHTTP *cur_htresp; + lcb_http_request_t htreq; + lcbjsp_PARSER *parser; + const void *cookie; + lcb_FTSCALLBACK callback; + lcb_t instance; + size_t nrows; + lcb_error_t lasterr; + void invoke_row(lcb_RESPFTS *resp); + void invoke_last(); + + lcb_FTSREQ(lcb_t, const void *, const lcb_CMDFTS *); + ~lcb_FTSREQ(); +}; + +static void +row_callback(lcbjsp_PARSER *parser, const lcbjsp_ROW *datum) +{ + lcb_FTSREQ *req = static_cast(parser->data); + lcb_RESPFTS resp = { 0 }; + + if (datum->type == LCBJSP_TYPE_ROW) { + resp.row = static_cast(datum->row.iov_base); + resp.nrow = datum->row.iov_len; + req->nrows++; + req->invoke_row(&resp); + } else if (datum->type == LCBJSP_TYPE_ERROR) { + req->lasterr = resp.rc = LCB_PROTOCOL_ERROR; + } else if (datum->type == LCBJSP_TYPE_COMPLETE) { + /* Nothing */ + } +} + +static void +chunk_callback(lcb_t, int, const lcb_RESPBASE *rb) +{ + const lcb_RESPHTTP *rh = (const lcb_RESPHTTP *)rb; + lcb_FTSREQ *req = static_cast(rh->cookie); + + req->cur_htresp = rh; + if (rh->rc != LCB_SUCCESS || rh->htstatus != 200) { + if (req->lasterr == LCB_SUCCESS || rh->htstatus != 200) { + req->lasterr = rh->rc ? rh->rc : LCB_HTTP_ERROR; + } + } + + if (rh->rflags & LCB_RESP_F_FINAL) { + req->invoke_last(); + delete req; + + } else if (req->callback == NULL) { + /* Cancelled. Similar to the block above, except the http request + * should remain alive (so we can cancel it later on) */ + delete req; + } else { + lcbjsp_feed(req->parser, static_cast(rh->body), rh->nbody); + } +} + +void +lcb_FTSREQ::invoke_row(lcb_RESPFTS *resp) +{ + resp->cookie = const_cast(cookie); + resp->htresp = cur_htresp; + + if (callback) { + callback(instance, -4, resp); + } +} + +void +lcb_FTSREQ::invoke_last() +{ + lcb_RESPFTS resp = { 0 }; + resp.rflags |= LCB_RESP_F_FINAL; + resp.rc = lasterr; + + if (parser) { + lcb_IOV meta; + lcbjsp_get_postmortem(parser, &meta); + resp.row = static_cast(meta.iov_base); + resp.nrow = meta.iov_len; + } + invoke_row(&resp); + callback = NULL; +} + +lcb_FTSREQ::lcb_FTSREQ(lcb_t instance_, const void *cookie_, const lcb_CMDFTS *cmd) +: cur_htresp(NULL), htreq(NULL), parser(lcbjsp_create(LCBJSP_MODE_FTS)), + cookie(cookie_), callback(cmd->callback), instance(instance_), nrows(0), + lasterr(LCB_SUCCESS) +{ + lcb_CMDHTTP htcmd = { 0 }; + htcmd.type = LCB_HTTP_TYPE_FTS; + htcmd.method = LCB_HTTP_METHOD_POST; + htcmd.reqhandle = &htreq; + htcmd.content_type = "application/json"; + htcmd.cmdflags |= LCB_CMDHTTP_F_STREAM; + if (!callback) { + lasterr = LCB_EINVAL; + return; + } + + Json::Value root; + Json::Reader rr; + if (!rr.parse(cmd->query, cmd->query + cmd->nquery, root)) { + lasterr = LCB_EINVAL; + return; + } + + const Json::Value& constRoot = root; + const Json::Value& j_ixname = constRoot["indexName"]; + if (!j_ixname.isString()) { + lasterr = LCB_EINVAL; + return; + } + + std::string url; + url.append("api/index/").append(j_ixname.asCString()).append("/query"); + LCB_CMD_SET_KEY(&htcmd, url.c_str(), url.size()); + + // Making a copy here to ensure that we don't accidentally create a new + // 'ctl' field. + const Json::Value& ctl = constRoot["value"]; + if (ctl.isObject()) { + const Json::Value& tmo = ctl["timeout"]; + if (tmo.isNumeric()) { + htcmd.cmdflags |= LCB_CMDHTTP_F_CASTMO; + htcmd.cas = tmo.asLargestUInt(); + } + } else { + root["ctl"]["timeout"] = LCBT_SETTING(instance, n1ql_timeout) / 1000; + } + + std::string qbody(Json::FastWriter().write(root)); + htcmd.body = qbody.c_str(); + htcmd.nbody = qbody.size(); + parser->data = this; + parser->callback = row_callback; + + lasterr = lcb_http3(instance, this, &htcmd); + if (lasterr == LCB_SUCCESS) { + lcb_htreq_setcb(htreq, chunk_callback); + if (cmd->handle) { + *cmd->handle = reinterpret_cast(this); + } + } +} + +lcb_FTSREQ::~lcb_FTSREQ() +{ + if (htreq != NULL) { + lcb_cancel_http_request(instance, htreq); + htreq = NULL; + } + if (parser) { + lcbjsp_free(parser); + parser = NULL; + } +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_fts_query(lcb_t instance, const void *cookie, const lcb_CMDFTS *cmd) +{ + lcb_FTSREQ *req = new lcb_FTSREQ(instance, cookie, cmd); + if (req->lasterr) { + lcb_error_t rc = req->lasterr; + delete req; + return rc; + } + return LCB_SUCCESS; +} + +LIBCOUCHBASE_API +void +lcb_fts_cancel(lcb_t, lcb_FTSHANDLE handle) +{ + handle->callback = NULL; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/cntl.cc b/couchbase-sys/libcouchbase-2.7.0/src/cntl.cc new file mode 100644 index 00000000..43f730cd --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/cntl.cc @@ -0,0 +1,846 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "internal.h" +#include "bucketconfig/clconfig.h" +#include "contrib/lcb-jsoncpp/lcb-jsoncpp.h" +#include +#include +#include + +#define CNTL__MODE_SETSTRING 0x1000 + +/* Basic definition/declaration for handlers */ +#define HANDLER(name) static lcb_error_t name(int mode, lcb_t instance, int cmd, void *arg) + +/* For handlers which only retrieve values */ +#define RETURN_GET_ONLY(T, acc) \ + if (mode != LCB_CNTL_GET) { return LCB_ECTL_UNSUPPMODE; } \ + *reinterpret_cast(arg) = (T)acc; \ + return LCB_SUCCESS; \ + (void)cmd; + +#define RETURN_SET_ONLY(T, acc) \ + if (mode != LCB_CNTL_SET) { return LCB_ECTL_UNSUPPMODE; } \ + acc = *reinterpret_cast(arg); \ + return LCB_SUCCESS; + +#define RETURN_GET_SET(T, acc) \ + if (mode == LCB_CNTL_GET) { \ + RETURN_GET_ONLY(T, acc); \ + } \ + else if (mode == LCB_CNTL_SET) { \ + RETURN_SET_ONLY(T, acc); \ + } \ + else { \ + return LCB_ECTL_UNSUPPMODE; \ + } + +typedef lcb_error_t (*ctl_handler)(int, lcb_t, int, void *); +typedef struct { const char *s; lcb_U32 u32; } STR_u32MAP; +static const STR_u32MAP* u32_from_map(const char *s, const STR_u32MAP *lookup) { + const STR_u32MAP *ret; + for (ret = lookup; ret->s; ret++) { + lcb_SIZE maxlen = strlen(ret->s); + if (!strncmp(ret->s, s, maxlen)) { return ret; } + } + return NULL; +} +#define DO_CONVERT_STR2NUM(s, lookup, v) { \ + const STR_u32MAP *str__rv = u32_from_map(s, lookup); \ + if (str__rv) { v = str__rv->u32; } else { return LCB_ECTL_BADARG; } } + +static lcb_uint32_t *get_timeout_field(lcb_t instance, int cmd) +{ + lcb_settings *settings = instance->settings; + switch (cmd) { + case LCB_CNTL_OP_TIMEOUT: return &settings->operation_timeout; + case LCB_CNTL_VIEW_TIMEOUT: return &settings->views_timeout; + case LCB_CNTL_N1QL_TIMEOUT: return &settings->n1ql_timeout; + case LCB_CNTL_DURABILITY_INTERVAL: return &settings->durability_interval; + case LCB_CNTL_DURABILITY_TIMEOUT: return &settings->durability_timeout; + case LCB_CNTL_HTTP_TIMEOUT: return &settings->http_timeout; + case LCB_CNTL_CONFIGURATION_TIMEOUT: return &settings->config_timeout; + case LCB_CNTL_CONFDELAY_THRESH: return &settings->weird_things_delay; + case LCB_CNTL_CONFIG_NODE_TIMEOUT: return &settings->config_node_timeout; + case LCB_CNTL_HTCONFIG_IDLE_TIMEOUT: return &settings->bc_http_stream_time; + case LCB_CNTL_RETRY_INTERVAL: return &settings->retry_interval; + case LCB_CNTL_RETRY_NMV_INTERVAL: return &settings->retry_nmv_interval; + default: return NULL; + } +} + +HANDLER(timeout_common) { + lcb_U32 *ptr; + lcb_U32 *user = reinterpret_cast(arg); + + ptr = get_timeout_field(instance, cmd); + if (!ptr) { + return LCB_ECTL_BADARG; + } + if (mode == LCB_CNTL_GET) { + *user = *ptr; + } else { + *ptr = *user; + } + return LCB_SUCCESS; +} + +HANDLER(noop_handler) { + (void)mode;(void)instance;(void)cmd;(void)arg; return LCB_SUCCESS; +} +HANDLER(get_vbconfig) { + RETURN_GET_ONLY(lcbvb_CONFIG*, LCBT_VBCONFIG(instance)) +} +HANDLER(get_htype) { + RETURN_GET_ONLY(lcb_type_t, static_cast(instance->type)) +} +HANDLER(get_iops) { + RETURN_GET_ONLY(lcb_io_opt_t, instance->iotable->p) +} +HANDLER(syncmode) { + (void)mode; (void)instance; (void)cmd; (void)arg; return LCB_ECTL_UNKNOWN; +} +HANDLER(ippolicy) { + RETURN_GET_SET(lcb_ipv6_t, instance->settings->ipv6) +} +HANDLER(confthresh) { + RETURN_GET_SET(lcb_SIZE, instance->settings->weird_things_threshold) +} +HANDLER(randomize_bootstrap_hosts_handler) { + RETURN_GET_SET(int, LCBT_SETTING(instance, randomize_bootstrap_nodes)) +} +HANDLER(get_changeset) { + (void)instance; RETURN_GET_ONLY(char*, LCB_VERSION_CHANGESET) +} +HANDLER(ssl_mode_handler) { + RETURN_GET_ONLY(int, LCBT_SETTING(instance, sslopts)) +} +HANDLER(ssl_certpath_handler) { + RETURN_GET_ONLY(char*, LCBT_SETTING(instance, certpath)) +} +HANDLER(htconfig_urltype_handler) { + RETURN_GET_SET(int, LCBT_SETTING(instance, bc_http_urltype)); +} +HANDLER(syncdtor_handler) { + RETURN_GET_SET(int, LCBT_SETTING(instance, syncdtor)); +} +HANDLER(detailed_errcode_handler) { + RETURN_GET_SET(int, LCBT_SETTING(instance, detailed_neterr)) +} +HANDLER(retry_backoff_handler) { + RETURN_GET_SET(float, LCBT_SETTING(instance, retry_backoff)) +} +HANDLER(http_poolsz_handler) { + RETURN_GET_SET(lcb_SIZE, instance->http_sockpool->maxidle) +} +HANDLER(http_refresh_config_handler) { + RETURN_GET_SET(int, LCBT_SETTING(instance, refresh_on_hterr)) +} +HANDLER(compmode_handler) { + RETURN_GET_SET(int, LCBT_SETTING(instance, compressopts)) +} +HANDLER(bucketname_handler) { + RETURN_GET_ONLY(const char*, LCBT_SETTING(instance, bucket)) +} +HANDLER(schedflush_handler) { + RETURN_GET_SET(int, LCBT_SETTING(instance, sched_implicit_flush)) +} +HANDLER(vbguess_handler) { + RETURN_GET_SET(int, LCBT_SETTING(instance, keep_guess_vbs)) +} +HANDLER(fetch_mutation_tokens_handler) { + RETURN_GET_SET(int, LCBT_SETTING(instance, fetch_mutation_tokens)) +} +HANDLER(dur_mutation_tokens_handler) { + RETURN_GET_SET(int, LCBT_SETTING(instance, dur_mutation_tokens)) +} +HANDLER(nmv_imm_retry_handler) { + RETURN_GET_SET(int, LCBT_SETTING(instance, nmv_retry_imm)); +} +HANDLER(tcp_nodelay_handler) { + RETURN_GET_SET(int, LCBT_SETTING(instance, tcp_nodelay)); +} +HANDLER(readj_ts_wait_handler) { + RETURN_GET_SET(int, LCBT_SETTING(instance, readj_ts_wait)); +} +HANDLER(kv_hg_handler) { + RETURN_GET_ONLY(lcb_HISTOGRAM*, instance->kv_timings); +} +HANDLER(read_chunk_size_handler) { + RETURN_GET_SET(lcb_U32, LCBT_SETTING(instance, read_chunk_size)); +} + +HANDLER(get_kvb) { + lcb_cntl_vbinfo_st *vbi = reinterpret_cast(arg); + + if (mode != LCB_CNTL_GET) { return LCB_ECTL_UNSUPPMODE; } + if (!LCBT_VBCONFIG(instance)) { return LCB_CLIENT_ETMPFAIL; } + if (vbi->version != 0) { return LCB_ECTL_BADARG; } + + lcbvb_map_key(LCBT_VBCONFIG(instance), vbi->v.v0.key, vbi->v.v0.nkey, + &vbi->v.v0.vbucket, &vbi->v.v0.server_index); + (void)cmd; return LCB_SUCCESS; +} + + +HANDLER(conninfo) { + lcbio_SOCKET *sock; + lcb_cntl_server_st *si = reinterpret_cast(arg); + const lcb_host_t *host; + + if (mode != LCB_CNTL_GET) { return LCB_ECTL_UNSUPPMODE; } + if (si->version < 0 || si->version > 1) { return LCB_ECTL_BADARG; } + + if (cmd == LCB_CNTL_MEMDNODE_INFO) { + lcb::Server *server; + int ix = si->v.v0.index; + + if (ix < 0 || ix > (int)LCBT_NSERVERS(instance)) { + return LCB_ECTL_BADARG; + } + server = LCBT_GET_SERVER(instance, ix); + if (!server) { + return LCB_NETWORK_ERROR; + } + sock = server->connctx->sock; + if (si->version == 1 && sock) { + lcb::SessionInfo *info = lcb::SessionInfo::get(server->connctx->sock); + if (info) { + si->v.v1.sasl_mech = info->get_mech().c_str(); + } + } + } else if (cmd == LCB_CNTL_CONFIGNODE_INFO) { + sock = lcb_confmon_get_rest_connection(instance->confmon); + } else { + return LCB_ECTL_BADARG; + } + + if (!sock) { + return LCB_SUCCESS; + } + host = lcbio_get_host(sock); + si->v.v0.connected = 1; + si->v.v0.host = host->host; + si->v.v0.port = host->port; + if (instance->iotable->model == LCB_IOMODEL_EVENT) { + si->v.v0.sock.sockfd = sock->u.fd; + } else { + si->v.v0.sock.sockptr = sock->u.sd; + } + return LCB_SUCCESS; +} + +HANDLER(config_cache_loaded_handler) { + if (mode != LCB_CNTL_GET) { return LCB_ECTL_UNSUPPMODE; } + *(int *)arg = instance->cur_configinfo && instance->cur_configinfo->origin == LCB_CLCONFIG_FILE; + (void)cmd; return LCB_SUCCESS; +} + +HANDLER(force_sasl_mech_handler) { + if (mode == LCB_CNTL_SET) { + free(instance->settings->sasl_mech_force); + if (arg) { + const char *s = reinterpret_cast(arg); + instance->settings->sasl_mech_force = strdup(s); + } + } else { + *(char**)arg = instance->settings->sasl_mech_force; + } + (void)cmd; return LCB_SUCCESS; +} + +HANDLER(max_redirects) { + if (mode == LCB_CNTL_SET && *(int*)arg < -1) { return LCB_ECTL_BADARG; } + RETURN_GET_SET(int, LCBT_SETTING(instance, max_redir)) +} + +HANDLER(logprocs_handler) { + if (mode == LCB_CNTL_GET) { + *(lcb_logprocs**)arg = LCBT_SETTING(instance, logger); + } else if (mode == LCB_CNTL_SET) { + LCBT_SETTING(instance, logger) = (lcb_logprocs *)arg; + } + (void)cmd; return LCB_SUCCESS; +} + +HANDLER(config_transport) { + lcb_config_transport_t *val = reinterpret_cast(arg); + if (mode == LCB_CNTL_SET) { return LCB_ECTL_UNSUPPMODE; } + if (!instance->cur_configinfo) { return LCB_CLIENT_ETMPFAIL; } + + switch (instance->cur_configinfo->origin) { + case LCB_CLCONFIG_HTTP: *val = LCB_CONFIG_TRANSPORT_HTTP; break; + case LCB_CLCONFIG_CCCP: *val = LCB_CONFIG_TRANSPORT_CCCP; break; + default: return LCB_CLIENT_ETMPFAIL; + } + (void)cmd; return LCB_SUCCESS; +} + +HANDLER(config_nodes) { + const char *node_strs = reinterpret_cast(arg); + clconfig_provider *target; + lcb::Hostlist hostlist; + lcb_error_t err; + + if (mode != LCB_CNTL_SET) { + return LCB_ECTL_UNSUPPMODE; + } + + err = hostlist.add(node_strs, -1, + cmd == LCB_CNTL_CONFIG_HTTP_NODES + ? LCB_CONFIG_HTTP_PORT : LCB_CONFIG_MCD_PORT); + + if (err != LCB_SUCCESS) { + return err; + } + + if (cmd == LCB_CNTL_CONFIG_HTTP_NODES) { + target = lcb_confmon_get_provider(instance->confmon, LCB_CLCONFIG_HTTP); + lcb_clconfig_http_set_nodes(target, &hostlist); + } else { + target = lcb_confmon_get_provider(instance->confmon, LCB_CLCONFIG_CCCP); + lcb_clconfig_cccp_set_nodes(target, &hostlist); + } + + return LCB_SUCCESS; +} + + +HANDLER(init_providers) { + lcb_create_st2 *opts = reinterpret_cast(arg); + if (mode != LCB_CNTL_SET) { return LCB_ECTL_UNSUPPMODE; } + (void)cmd; return lcb_init_providers2(instance, opts); +} + +HANDLER(config_cache_handler) { + clconfig_provider *provider; + + provider = lcb_confmon_get_provider(instance->confmon, LCB_CLCONFIG_FILE); + if (mode == LCB_CNTL_SET) { + int rv; + rv = lcb_clconfig_file_set_filename(provider, + reinterpret_cast(arg), + cmd == LCB_CNTL_CONFIGCACHE_RO); + + if (rv == 0) { + instance->settings->bc_http_stream_time = LCB_MS2US(10000); + return LCB_SUCCESS; + } + return LCB_ERROR; + } else { + *(const char **)arg = lcb_clconfig_file_get_filename(provider); + return LCB_SUCCESS; + } +} + +HANDLER(retrymode_handler) { + lcb_U32 *val = reinterpret_cast(arg); + lcb_U32 rmode = LCB_RETRYOPT_GETMODE(*val); + uint8_t *p = NULL; + + if (rmode >= LCB_RETRY_ON_MAX) { return LCB_ECTL_BADARG; } + p = &(LCBT_SETTING(instance, retry)[rmode]); + if (mode == LCB_CNTL_SET) { + *p = LCB_RETRYOPT_GETPOLICY(*val); + } else { + *val = LCB_RETRYOPT_CREATE(rmode, *p); + } + (void)cmd; + return LCB_SUCCESS; +} + +HANDLER(allocfactory_handler) { + lcb_cntl_rdballocfactory *cbw = reinterpret_cast(arg); + if (mode == LCB_CNTL_SET) { + LCBT_SETTING(instance, allocator_factory) = cbw->factory; + } else { + cbw->factory = LCBT_SETTING(instance, allocator_factory); + } + (void)cmd; return LCB_SUCCESS; +} + +HANDLER(console_log_handler) { + lcb_U32 level; + struct lcb_CONSOLELOGGER *logger; + lcb_logprocs *procs; + + level = *(lcb_U32*)arg; + if (mode != LCB_CNTL_SET) { + return LCB_ECTL_UNSUPPMODE; + } + + procs = LCBT_SETTING(instance, logger); + if (!procs) { + procs = lcb_init_console_logger(); + } + if (procs) { + /* don't override previous config */ + return LCB_SUCCESS; + } + + logger = (struct lcb_CONSOLELOGGER* ) lcb_console_logprocs; + level = LCB_LOG_ERROR - level; + logger->minlevel = level; + LCBT_SETTING(instance, logger) = &logger->base; + (void)cmd; return LCB_SUCCESS; +} + +HANDLER(console_fp_handler) { + struct lcb_CONSOLELOGGER *logger = + (struct lcb_CONSOLELOGGER*)lcb_console_logprocs; + if (mode == LCB_CNTL_GET) { + *(FILE **)arg = logger->fp; + } else if (mode == LCB_CNTL_SET) { + logger->fp = *(FILE**)arg; + } else if (mode == CNTL__MODE_SETSTRING) { + FILE *fp = fopen(reinterpret_cast(arg), "w"); + if (!fp) { + return LCB_ERROR; + } else { + logger->fp = fp; + } + } + (void)cmd; (void)instance; + return LCB_SUCCESS; +} + +HANDLER(reinit_spec_handler) { + if (mode == LCB_CNTL_GET) { return LCB_ECTL_UNSUPPMODE; } + (void)cmd; return lcb_reinit3(instance, reinterpret_cast(arg)); +} + +HANDLER(client_string_handler) { + if (mode == LCB_CNTL_SET) { + const char *val = reinterpret_cast(arg); + free(LCBT_SETTING(instance, client_string)); + if (val) { + LCBT_SETTING(instance, client_string) = strdup(val); + } + } else { + *(const char **)arg = LCBT_SETTING(instance, client_string); + } + (void)cmd; + return LCB_SUCCESS; +} + +HANDLER(unsafe_optimize) { + lcb_error_t rc; + int val = *(int *)arg; + if (mode != LCB_CNTL_SET) { + return LCB_ECTL_UNSUPPMODE; + } + if (!val) { + return LCB_ECTL_BADARG; + } + + /* Simpler to just input strings here. */ + #define APPLY_UNSAFE(k, v) \ + if ((rc = lcb_cntl_string(instance, k, v)) != LCB_SUCCESS) { return rc; } + + APPLY_UNSAFE("vbguess_persist", "1"); + APPLY_UNSAFE("retry_policy", "topochange:none") + APPLY_UNSAFE("retry_policy", "sockerr:none"); + APPLY_UNSAFE("retry_policy", "maperr:none"); + APPLY_UNSAFE("retry_policy", "missingnode:none"); + APPLY_UNSAFE("retry_backoff", "0.0"); + (void)cmd; + return LCB_SUCCESS; +} + +HANDLER(mutation_tokens_supported_handler) { + size_t ii; + if (mode != LCB_CNTL_GET) { + return LCB_ECTL_UNSUPPMODE; + } + + *(int *)arg = 0; + + for (ii = 0; ii < LCBT_NSERVERS(instance); ii++) { + if (instance->get_server(ii)->supports_mutation_tokens()) { + *(int *)arg = 1; + break; + } + } + (void)cmd; + return LCB_SUCCESS; +} + +HANDLER(n1ql_cache_clear_handler) { + if (mode != LCB_CNTL_SET) { + return LCB_ECTL_UNSUPPMODE; + } + + lcb_n1qlcache_clear(instance->n1ql_cache); + + (void)cmd; + (void)arg; + return LCB_SUCCESS; +} + +HANDLER(bucket_auth_handler) { + const lcb_BUCKETCRED *cred; + if (mode == LCB_CNTL_SET) { + /* Parse the bucket string... */ + cred = (const lcb_BUCKETCRED *)arg; + lcbauth_add_pass(instance->settings->auth, (*cred)[0], (*cred)[1], LCBAUTH_F_BUCKET); + (void)cmd; (void)arg; + } else if (mode == CNTL__MODE_SETSTRING) { + const char *ss = reinterpret_cast(arg); + size_t sslen = strlen(ss); + Json::Value root; + if (!Json::Reader().parse(ss, ss + sslen, root)) { + return LCB_ECTL_BADARG; + } + if (!root.isArray() || root.size() != 2) { + return LCB_ECTL_BADARG; + } + lcbauth_add_pass(instance->settings->auth, + root[0].asString().c_str(), + root[1].asString().c_str(), LCBAUTH_F_BUCKET); + } else { + return LCB_ECTL_UNSUPPMODE; + } + return LCB_SUCCESS; +} + + +static ctl_handler handlers[] = { + timeout_common, /* LCB_CNTL_OP_TIMEOUT */ + timeout_common, /* LCB_CNTL_VIEW_TIMEOUT */ + noop_handler, /* LCB_CNTL_RBUFSIZE */ + noop_handler, /* LCB_CNTL_WBUFSIZE */ + get_htype, /* LCB_CNTL_HANDLETYPE */ + get_vbconfig, /* LCB_CNTL_VBCONFIG */ + get_iops, /* LCB_CNTL_IOPS */ + get_kvb, /* LCB_CNTL_VBMAP */ + conninfo, /* LCB_CNTL_MEMDNODE_INFO */ + conninfo, /* LCB_CNTL_CONFIGNODE_INFO */ + syncmode, /* LCB_CNTL_SYNCMODE */ + ippolicy, /* LCB_CNTL_IP6POLICY */ + confthresh /* LCB_CNTL_CONFERRTHRESH */, + timeout_common, /* LCB_CNTL_DURABILITY_INTERVAL */ + timeout_common, /* LCB_CNTL_DURABILITY_TIMEOUT */ + timeout_common, /* LCB_CNTL_HTTP_TIMEOUT */ + lcb_iops_cntl_handler, /* LCB_CNTL_IOPS_DEFAULT_TYPES */ + lcb_iops_cntl_handler, /* LCB_CNTL_IOPS_DLOPEN_DEBUG */ + timeout_common, /* LCB_CNTL_CONFIGURATION_TIMEOUT */ + noop_handler, /* LCB_CNTL_SKIP_CONFIGURATION_ERRORS_ON_CONNECT */ + randomize_bootstrap_hosts_handler /* LCB_CNTL_RANDOMIZE_BOOTSTRAP_HOSTS */, + config_cache_loaded_handler /* LCB_CNTL_CONFIG_CACHE_LOADED */, + force_sasl_mech_handler, /* LCB_CNTL_FORCE_SASL_MECH */ + max_redirects, /* LCB_CNTL_MAX_REDIRECTS */ + logprocs_handler /* LCB_CNTL_LOGGER */, + timeout_common, /* LCB_CNTL_CONFDELAY_THRESH */ + config_transport, /* LCB_CNTL_CONFIG_TRANSPORT */ + timeout_common, /* LCB_CNTL_CONFIG_NODE_TIMEOUT */ + timeout_common, /* LCB_CNTL_HTCONFIG_IDLE_TIMEOUT */ + config_nodes, /* LCB_CNTL_CONFIG_HTTP_NODES */ + config_nodes, /* LCB_CNTL_CONFIG_CCCP_NODES */ + get_changeset, /* LCB_CNTL_CHANGESET */ + init_providers, /* LCB_CNTL_CONFIG_ALL_NODES */ + config_cache_handler, /* LCB_CNTL_CONFIGCACHE */ + ssl_mode_handler, /* LCB_CNTL_SSL_MODE */ + ssl_certpath_handler, /* LCB_CNTL_SSL_CAPATH */ + retrymode_handler, /* LCB_CNTL_RETRYMODE */ + htconfig_urltype_handler, /* LCB_CNTL_HTCONFIG_URLTYPE */ + compmode_handler, /* LCB_CNTL_COMPRESSION_OPTS */ + allocfactory_handler, /* LCB_CNTL_RDBALLOCFACTORY */ + syncdtor_handler, /* LCB_CNTL_SYNCDESTROY */ + console_log_handler, /* LCB_CNTL_CONLOGGER_LEVEL */ + detailed_errcode_handler, /* LCB_CNTL_DETAILED_ERRCODES */ + reinit_spec_handler, /* LCB_CNTL_REINIT_CONNSTR */ + timeout_common, /* LCB_CNTL_RETRY_INTERVAL */ + retry_backoff_handler, /* LCB_CNTL_RETRY_BACKOFF */ + http_poolsz_handler, /* LCB_CNTL_HTTP_POOLSIZE */ + http_refresh_config_handler, /* LCB_CNTL_HTTP_REFRESH_CONFIG_ON_ERROR */ + bucketname_handler, /* LCB_CNTL_BUCKETNAME */ + schedflush_handler, /* LCB_CNTL_SCHED_IMPLICIT_FLUSH */ + vbguess_handler, /* LCB_CNTL_VBGUESS_PERSIST */ + unsafe_optimize, /* LCB_CNTL_UNSAFE_OPTIMIZE */ + fetch_mutation_tokens_handler, /* LCB_CNTL_FETCH_MUTATION_TOKENS */ + dur_mutation_tokens_handler, /* LCB_CNTL_DURABILITY_MUTATION_TOKENS */ + config_cache_handler, /* LCB_CNTL_CONFIGCACHE_READONLY */ + nmv_imm_retry_handler, /* LCB_CNTL_RETRY_NMV_IMM */ + mutation_tokens_supported_handler, /* LCB_CNTL_MUTATION_TOKENS_SUPPORTED */ + tcp_nodelay_handler, /* LCB_CNTL_TCP_NODELAY */ + readj_ts_wait_handler, /* LCB_CNTL_RESET_TIMEOUT_ON_WAIT */ + console_fp_handler, /* LCB_CNTL_CONLOGGER_FP */ + kv_hg_handler, /* LCB_CNTL_KVTIMINGS */ + timeout_common, /* LCB_CNTL_N1QL_TIMEOUT */ + n1ql_cache_clear_handler, /* LCB_CNTL_N1QL_CLEARCACHE */ + client_string_handler, /* LCB_CNTL_CLIENT_STRING */ + bucket_auth_handler, /* LCB_CNTL_BUCKET_CRED */ + timeout_common, /* LCB_CNTL_RETRY_NMV_DELAY */ + read_chunk_size_handler /*LCB_CNTL_READ_CHUNKSIZE */ +}; + +/* Union used for conversion to/from string functions */ +typedef union { + lcb_U32 u32; + lcb_SIZE sz; + int i; + float f; + void *p; +} u_STRCONVERT; + +/* This handler should convert the string argument to the appropriate + * type needed for the actual control handler. It should return an error if the + * argument is invalid. + */ +typedef lcb_error_t (*ctl_str_cb)(const char *value, u_STRCONVERT *u); + +typedef struct { + const char *key; + int opcode; + ctl_str_cb converter; +} cntl_OPCODESTRS; + +static lcb_error_t convert_timeout(const char *arg, u_STRCONVERT *u) { + int rv; + unsigned long tmp; + + /* Parse as a float */ + double dtmp; + rv = sscanf(arg, "%lf", &dtmp); + if (rv != 1) { return LCB_ECTL_BADARG; } + tmp = dtmp * (double) 1000000; + u->u32 = tmp; + return LCB_SUCCESS; +} + +static lcb_error_t convert_intbool(const char *arg, u_STRCONVERT *u) { + if (!strcmp(arg, "true")) { + u->i = 1; + } else if (!strcmp(arg, "false")) { + u->i = 0; + } else { + u->i = atoi(arg); + } + return LCB_SUCCESS; +} + +static lcb_error_t convert_passthru(const char *arg, u_STRCONVERT *u) { + u->p = (void*)arg; + return LCB_SUCCESS; +} + +static lcb_error_t convert_int(const char *arg, u_STRCONVERT *u) { + int rv = sscanf(arg, "%d", &u->i); + return rv == 1 ? LCB_SUCCESS : LCB_ECTL_BADARG; +} + +static lcb_error_t convert_u32(const char *arg, u_STRCONVERT *u) { + return convert_timeout(arg, u); +} +static lcb_error_t convert_float(const char *arg, u_STRCONVERT *u) { + double d; + int rv = sscanf(arg, "%lf", &d); + if (rv != 1) { return LCB_ECTL_BADARG; } + u->f = d; + return LCB_SUCCESS; +} + +static lcb_error_t convert_SIZE(const char *arg, u_STRCONVERT *u) { + unsigned long lu; + int rv; + rv = sscanf(arg, "%lu", &lu); + if (rv != 1) { return LCB_ECTL_BADARG; } + u->sz = lu; + return LCB_SUCCESS; +} + +static lcb_error_t convert_compression(const char *arg, u_STRCONVERT *u) { + static const STR_u32MAP optmap[] = { + { "on", LCB_COMPRESS_INOUT }, + { "off", LCB_COMPRESS_NONE }, + { "inflate_only", LCB_COMPRESS_IN }, + { "force", LCB_COMPRESS_INOUT|LCB_COMPRESS_FORCE }, + { NULL } + }; + DO_CONVERT_STR2NUM(arg, optmap, u->i); + return LCB_SUCCESS; +} + +static lcb_error_t convert_retrymode(const char *arg, u_STRCONVERT *u) { + static const STR_u32MAP modemap[] = { + { "topochange", LCB_RETRY_ON_TOPOCHANGE }, + { "sockerr", LCB_RETRY_ON_SOCKERR }, + { "maperr", LCB_RETRY_ON_VBMAPERR }, + { "missingnode", LCB_RETRY_ON_MISSINGNODE }, { NULL } + }; + static const STR_u32MAP polmap[] = { + { "all", LCB_RETRY_CMDS_ALL }, + { "get", LCB_RETRY_CMDS_GET }, + { "safe", LCB_RETRY_CMDS_SAFE }, + { "none", LCB_RETRY_CMDS_NONE }, { NULL } + }; + + lcb_U32 polval, modeval; + const char *polstr = strchr(arg, ':'); + if (!polstr) { return LCB_ECTL_BADARG; } + polstr++; + DO_CONVERT_STR2NUM(arg, modemap, modeval); + DO_CONVERT_STR2NUM(polstr, polmap, polval); + u->u32 = LCB_RETRYOPT_CREATE(modeval, polval); + return LCB_SUCCESS; +} + +static cntl_OPCODESTRS stropcode_map[] = { + {"operation_timeout", LCB_CNTL_OP_TIMEOUT, convert_timeout}, + {"timeout", LCB_CNTL_OP_TIMEOUT, convert_timeout}, + {"views_timeout", LCB_CNTL_VIEW_TIMEOUT, convert_timeout}, + {"n1ql_timeout", LCB_CNTL_N1QL_TIMEOUT, convert_timeout}, + {"durability_timeout", LCB_CNTL_DURABILITY_TIMEOUT, convert_timeout}, + {"durability_interval", LCB_CNTL_DURABILITY_INTERVAL, convert_timeout}, + {"http_timeout", LCB_CNTL_HTTP_TIMEOUT, convert_timeout}, + {"randomize_nodes", LCB_CNTL_RANDOMIZE_BOOTSTRAP_HOSTS, convert_intbool}, + {"sasl_mech_force", LCB_CNTL_FORCE_SASL_MECH, convert_passthru}, + {"error_thresh_count", LCB_CNTL_CONFERRTHRESH, convert_SIZE}, + {"error_thresh_delay", LCB_CNTL_CONFDELAY_THRESH, convert_timeout}, + {"config_total_timeout", LCB_CNTL_CONFIGURATION_TIMEOUT, convert_timeout}, + {"config_node_timeout", LCB_CNTL_CONFIG_NODE_TIMEOUT, convert_timeout}, + {"compression", LCB_CNTL_COMPRESSION_OPTS, convert_compression}, + {"console_log_level", LCB_CNTL_CONLOGGER_LEVEL, convert_u32}, + {"config_cache", LCB_CNTL_CONFIGCACHE, convert_passthru }, + {"config_cache_ro", LCB_CNTL_CONFIGCACHE_RO, convert_passthru }, + {"detailed_errcodes", LCB_CNTL_DETAILED_ERRCODES, convert_intbool}, + {"retry_policy", LCB_CNTL_RETRYMODE, convert_retrymode}, + {"http_urlmode", LCB_CNTL_HTCONFIG_URLTYPE, convert_int }, + {"sync_dtor", LCB_CNTL_SYNCDESTROY, convert_intbool }, + {"_reinit_connstr", LCB_CNTL_REINIT_CONNSTR }, + {"retry_backoff", LCB_CNTL_RETRY_BACKOFF, convert_float }, + {"retry_interval", LCB_CNTL_RETRY_INTERVAL, convert_timeout}, + {"http_poolsize", LCB_CNTL_HTTP_POOLSIZE, convert_SIZE }, + {"vbguess_persist", LCB_CNTL_VBGUESS_PERSIST, convert_intbool }, + {"unsafe_optimize", LCB_CNTL_UNSAFE_OPTIMIZE, convert_intbool }, + {"fetch_mutation_tokens", LCB_CNTL_FETCH_MUTATION_TOKENS, convert_intbool }, + {"dur_mutation_tokens", LCB_CNTL_DURABILITY_MUTATION_TOKENS, convert_intbool }, + {"retry_nmv_imm", LCB_CNTL_RETRY_NMV_IMM, convert_intbool }, + {"tcp_nodelay", LCB_CNTL_TCP_NODELAY, convert_intbool }, + {"readj_ts_wait", LCB_CNTL_RESET_TIMEOUT_ON_WAIT, convert_intbool }, + {"console_log_file", LCB_CNTL_CONLOGGER_FP, NULL }, + {"client_string", LCB_CNTL_CLIENT_STRING, convert_passthru}, + {"retry_nmv_delay", LCB_CNTL_RETRY_NMV_INTERVAL, convert_timeout}, + {"bucket_cred", LCB_CNTL_BUCKET_CRED, NULL}, + {"read_chunk_size", LCB_CNTL_READ_CHUNKSIZE, convert_u32}, + {NULL, -1} +}; + +#define CNTL_NUM_HANDLERS (sizeof(handlers) / sizeof(handlers[0])) + +static lcb_error_t +wrap_return(lcb_t instance, lcb_error_t retval) +{ + if (retval == LCB_SUCCESS) { + return retval; + } + if (instance && LCBT_SETTING(instance, detailed_neterr) == 0) { + switch (retval) { + case LCB_ECTL_UNKNOWN: + return LCB_NOT_SUPPORTED; + case LCB_ECTL_UNSUPPMODE: + return LCB_NOT_SUPPORTED; + case LCB_ECTL_BADARG: + return LCB_EINVAL; + default: + return retval; + } + } else { + return retval; + } +} + +LIBCOUCHBASE_API +lcb_error_t lcb_cntl(lcb_t instance, int mode, int cmd, void *arg) +{ + ctl_handler handler; + if (cmd >= (int)CNTL_NUM_HANDLERS || cmd < 0) { + return wrap_return(instance, LCB_ECTL_UNKNOWN); + } + + handler = handlers[cmd]; + + if (!handler) { + return wrap_return(instance, LCB_ECTL_UNKNOWN); + } + + return wrap_return(instance, handler(mode, instance, cmd, arg)); +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_cntl_string(lcb_t instance, const char *key, const char *value) +{ + cntl_OPCODESTRS *cur; + u_STRCONVERT u; + lcb_error_t err; + + for (cur = stropcode_map; cur->key; cur++) { + if (!strcmp(cur->key, key)) { + if (cur->converter) { + err = cur->converter(value, &u); + if (err != LCB_SUCCESS) { + return err; + } + if (cur->converter == convert_passthru) { + return lcb_cntl(instance, LCB_CNTL_SET, cur->opcode, u.p); + } else { + return lcb_cntl(instance, LCB_CNTL_SET, cur->opcode, &u); + } + } + + return lcb_cntl(instance, CNTL__MODE_SETSTRING, cur->opcode, + (void *)value); + } + } + return wrap_return(instance, LCB_NOT_SUPPORTED); +} + +LIBCOUCHBASE_API +int +lcb_cntl_exists(int ctl) +{ + if (ctl >= (int)CNTL_NUM_HANDLERS || ctl < 0) { + return 0; + } + return handlers[ctl] != NULL; +} + +LIBCOUCHBASE_API +lcb_error_t lcb_cntl_setu32(lcb_t instance, int cmd, lcb_uint32_t arg) +{ + return lcb_cntl(instance, LCB_CNTL_SET, cmd, &arg); +} + +LIBCOUCHBASE_API +lcb_uint32_t lcb_cntl_getu32(lcb_t instance, int cmd) +{ + lcb_uint32_t ret = 0; + lcb_cntl(instance, LCB_CNTL_GET, cmd, &ret); + return ret; +} + +#define DECL_DEPR_FUNC(T, name_set, name_get, ctl) \ +LIBCOUCHBASE_API void name_set(lcb_t instance, T input) { \ + lcb_cntl(instance, LCB_CNTL_SET, ctl, &input); } \ +LIBCOUCHBASE_API T name_get(lcb_t instance) { T output = (T)0; \ + lcb_cntl(instance, LCB_CNTL_GET, ctl, &output); return (T)output; } + +DECL_DEPR_FUNC(lcb_ipv6_t, lcb_behavior_set_ipv6, lcb_behavior_get_ipv6, LCB_CNTL_IP6POLICY) +DECL_DEPR_FUNC(lcb_size_t, lcb_behavior_set_config_errors_threshold, lcb_behavior_get_config_errors_threshold, LCB_CNTL_CONFERRTHRESH) +DECL_DEPR_FUNC(lcb_U32, lcb_set_timeout, lcb_get_timeout, LCB_CNTL_OP_TIMEOUT) +DECL_DEPR_FUNC(lcb_U32, lcb_set_view_timeout, lcb_get_view_timeout, LCB_CNTL_VIEW_TIMEOUT) diff --git a/couchbase-sys/libcouchbase-2.7.0/src/config_static.h b/couchbase-sys/libcouchbase-2.7.0/src/config_static.h new file mode 100644 index 00000000..680e521c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/config_static.h @@ -0,0 +1,159 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This file contains the static part of the configure script. Please add + * all platform specific conditional code to this file. + * + * @author Trond Norbye + */ +#ifndef LIBCOUCHBASE_CONFIG_STATIC_H +#define LIBCOUCHBASE_CONFIG_STATIC_H 1 + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#if !defined HAVE_STDINT_H && defined _WIN32 && defined(_MSC_VER) +# include "win_stdint.h" +#else +# include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_INTTYPES_H +#include +#endif + +#ifdef HAVE_NETDB_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef _WIN32 +#include +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_UIO_H +#include +#endif + +#ifdef HAVE_STRINGS_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_DLFCN_H +#include +#endif + + +#ifdef HAVE_ARPA_INET_H +#include +#endif + +/* Standard C includes */ +#include +#include +#include +#include + +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +#ifdef _WIN32 +#include + +#ifndef __MINGW32__ + +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf _snprintf +#endif + +#define strcasecmp(a,b) _stricmp(a,b) +#define strncasecmp(a,b,c) _strnicmp(a,b,c) +#undef strdup +#define strdup _strdup +#endif + +#else +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#endif /* _WIN32 */ + + +#if defined(HAVE_HTONLL) + #define lcb_htonll htonll + #define lcb_ntohll ntohll +#elif defined(WORDS_BIGENDIAN) + #define lcb_ntohll(a) a + #define lcb_htonll(a) a +#else + #define lcb_ntohll(a) lcb_byteswap64(a) + #define lcb_htonll(a) lcb_byteswap64(a) +#endif /* HAVE_HTONLL */ + +#ifdef __cplusplus +extern "C" { +#endif + extern uint64_t lcb_byteswap64(uint64_t val); +#ifdef __cplusplus +} +#endif + +#ifdef linux +#undef ntohs +#undef ntohl +#undef htons +#undef htonl +#endif + +#ifndef HAVE_GETHRTIME +#ifdef __cplusplus +extern "C" { +#endif + typedef uint64_t hrtime_t; + extern hrtime_t gethrtime(void); +#ifdef __cplusplus +} +#endif +#endif + +#if defined(EWOULDBLOCK) && defined(EAGAIN) && EWOULDBLOCK != EAGAIN +#define USE_EAGAIN 1 +#endif + +#endif /* LIBCOUCHBASE_CONFIG_STATIC_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/connspec.cc b/couchbase-sys/libcouchbase-2.7.0/src/connspec.cc new file mode 100644 index 00000000..5c108a27 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/connspec.cc @@ -0,0 +1,462 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "connspec.h" +#include "hostlist.h" +#include "strcodecs/strcodecs.h" +#include +#include +#include + +#define SET_ERROR(msg) \ + *errmsg = msg; \ + return LCB_EINVAL; + +#define F_HASBUCKET (1<<0) +#define F_HASPASSWD (1<<1) +#define F_HASUSER (1<<2) +#define F_SSLSCHEME (1<<3) +#define F_FILEONLY (1<<4) + +using namespace lcb; + +static int string_to_porttype(const char *s) { + if (!strcmp(s, "HTTP")) { + return LCB_CONFIG_HTTP_PORT; + } else if (!strcmp(s, "MCD")) { + return LCB_CONFIG_MCD_PORT; + } else if (!strcmp(s, "HTTPS")) { + return LCB_CONFIG_HTTP_SSL_PORT; + } else if (!strcmp(s, "MCDS")) { + return LCB_CONFIG_MCD_SSL_PORT; + } else if (!strcmp(s, "MCCOMPAT")) { + return LCB_CONFIG_MCCOMPAT_PORT; + } else { + return -1; + } +} + +lcb_error_t +Connspec::parse_hosts(const char *hostbegin, + const char *hostend, const char **errmsg) +{ + std::string decoded(hostbegin, hostend); + if (!strcodecs::urldecode(decoded)) { + SET_ERROR("Couldn't decode from URL encoding!"); + } + + const char *c = decoded.c_str(); + + while (*c) { + // get the current host + const char *curend; + unsigned curlen, hostlen; + int rv; + + /* Seek ahead, chopping off any ',' */ + while (*c == ',' || *c == ';') { + if (*(++c) == '\0') { + return LCB_SUCCESS; + } + } + + /* Find the end */ + curend = strpbrk(c, ",;"); + if (!curend) { + curend = c + strlen(c); + } + curlen = curend - c; + if (!curlen) { + continue; + } + + std::string scratch(c, curlen); + c = curend; + + /* weed out erroneous characters */ + if (scratch.find("://") != std::string::npos) { + SET_ERROR("Detected '://' inside hostname"); + } + + size_t colonpos = scratch.find(":"); + std::string port; + + if (colonpos == std::string::npos) { + hostlen = scratch.size(); + } else if (colonpos == 0 || colonpos == scratch.size()-1) { + SET_ERROR("First or last character in spec is colon!"); + } else { + hostlen = colonpos; + port = scratch.substr(colonpos + 1); + } + + m_hosts.resize(m_hosts.size() + 1); + Spechost *dh = &m_hosts.back(); + dh->hostname = scratch.substr(0, hostlen); + + if (port.empty()) { + continue; + } + + char hpdummy[256] = { 0 }; + int itmp; + if (port.size() > sizeof hpdummy) { + SET_ERROR("Port spec too big!"); + } + + /* we have a port. The format is port=proto */ + rv = sscanf(port.c_str(), "%d=%s", &itmp, hpdummy); + if (rv == 2) { + for (char *tmp = hpdummy; *tmp; tmp++) { + *tmp = toupper(*tmp); + } + // Both host and port. Not ambiguous + if (-1 == (dh->type = string_to_porttype(hpdummy))) { + SET_ERROR("Unrecognized protocol specified. Recognized are " + "HTTP, HTTPS, MCD, MCDS"); + } + } else if (rv == 1 && m_implicit_port) { + // Port only, but we have a scheme. No need to set this implicitly + // in the host object + if (m_implicit_port == itmp) { + continue; + } + + // couchbase scheme with :8091 specification. Just ignore + if (itmp == LCB_CONFIG_HTTP_PORT && + m_implicit_port == LCB_CONFIG_MCD_PORT) { + /* Honest 'couchbase://host:8091' mistake */ + continue; + } + dh->type = m_implicit_port; + } else { + SET_ERROR("Port must be specified with protocol (host:port=proto)"); + } + dh->port = itmp; + } + return LCB_SUCCESS; +} + +lcb_error_t +Connspec::parse_options( + const char *options_, const char *specend, const char **errmsg) +{ + while (options_ != NULL && options_ < specend) { + unsigned curlen; + const char *curend; + + if (*options_ == '&') { + options_++; + continue; + } + + curend = strchr(options_, '&'); + if (!curend) { + curend = specend; + } + + curlen = curend - options_; + std::vector optpair(options_, options_ + curlen); + optpair.push_back('\0'); + + options_ = curend+1; + + char *key = &optpair[0]; + char *value = strchr(key, '='); + if (!value) { + SET_ERROR("Option must be specified as a key=value pair"); + } + + *(value++) = '\0'; + if (!*value) { + SET_ERROR("Value cannot be empty"); + } + if (! (strcodecs::urldecode(value) && strcodecs::urldecode(key))) { + SET_ERROR("Couldn't decode key or value!"); + } + if (!strcmp(key, "bootstrap_on")) { + m_transports.clear(); + if (!strcmp(value, "cccp")) { + m_transports.insert(LCB_CONFIG_TRANSPORT_CCCP); + } else if (!strcmp(value, "http")) { + m_transports.insert(LCB_CONFIG_TRANSPORT_HTTP); + } else if (!strcmp(value, "all")) { + m_transports.insert(LCB_CONFIG_TRANSPORT_CCCP); + m_transports.insert(LCB_CONFIG_TRANSPORT_HTTP); + } else if (!strcmp(value, "file_only")) { + m_flags |= LCB_CONNSPEC_F_FILEONLY; + } else { + SET_ERROR("Value for bootstrap_on must be 'cccp', 'http', or 'all'"); + } + } else if (!strcmp(key, "username") || !strcmp(key, "user")) { + if (! (m_flags & F_HASUSER)) { + m_username = value; + } + } else if (!strcmp(key, "password") || !strcmp(key, "pass")) { + if (! (m_flags & F_HASPASSWD)) { + m_password = value; + } + } else if (!strcmp(key, "ssl")) { + if (!strcmp(value, "off")) { + if (m_flags & F_SSLSCHEME) { + SET_ERROR("SSL scheme specified, but ssl=off found in options"); + } + m_sslopts &= (~LCB_SSL_ENABLED); + } else if (!strcmp(value, "on")) { + m_sslopts |= LCB_SSL_ENABLED; + } else if (!strcmp(value, "no_verify")) { + m_sslopts |= LCB_SSL_ENABLED|LCB_SSL_NOVERIFY; + } else if (!strcmp(value, "no_global_init")) { + m_sslopts |= LCB_SSL_NOGLOBALINIT; + } else { + SET_ERROR("Invalid value for 'ssl'. Choices are on, off, and no_verify"); + } + } else if (!strcmp(key, "certpath")) { + if (! (m_flags & F_SSLSCHEME)) { + SET_ERROR("Certificate path must be specified with SSL host or scheme"); + } + m_certpath = value; + } else if (!strcmp(key, "console_log_level")) { + if (sscanf(value, "%d", &m_loglevel) != 1) { + SET_ERROR("console_log_level must be a numeric value"); + } + } else { + m_ctlopts.push_back(std::make_pair(key, value)); + } + } + + return LCB_SUCCESS; +} + +lcb_error_t +Connspec::parse(const char *connstr_, const char **errmsg) +{ + lcb_error_t err = LCB_SUCCESS; + const char *errmsg_s; /* stack based error message pointer */ + const char *hlend; /* end of hosts list */ + const char *bucket_s = NULL; /* beginning of bucket (path) string */ + const char *options_ = NULL; /* beginning of options (query) string */ + const char *specend = NULL; /* end of spec */ + unsigned speclen; /* length of spec string */ + const char *found_scheme = NULL; + + if (!errmsg) { + errmsg = &errmsg_s; + } + + if (!connstr_) { + connstr_ = "couchbase://"; + } + + m_connstr = connstr_; + +#define SCHEME_MATCHES(scheme_const) \ + strncmp(connstr_, scheme_const, sizeof(scheme_const)-1) == 0 && \ + (found_scheme = scheme_const) + + if (SCHEME_MATCHES(LCB_SPECSCHEME_MCD_SSL)) { + m_implicit_port = LCB_CONFIG_MCD_SSL_PORT; + m_sslopts |= LCB_SSL_ENABLED; + m_flags |= F_SSLSCHEME; + + } else if (SCHEME_MATCHES(LCB_SPECSCHEME_HTTP_SSL)) { + m_implicit_port = LCB_CONFIG_HTTP_SSL_PORT; + m_sslopts |= LCB_SSL_ENABLED; + m_flags |= F_SSLSCHEME; + + } else if (SCHEME_MATCHES(LCB_SPECSCHEME_HTTP)) { + m_implicit_port = LCB_CONFIG_HTTP_PORT; + + } else if (SCHEME_MATCHES(LCB_SPECSCHEME_MCD)) { + m_implicit_port = LCB_CONFIG_MCD_PORT; + + } else if (SCHEME_MATCHES(LCB_SPECSCHEME_RAW)) { + m_implicit_port = 0; + } else if (SCHEME_MATCHES(LCB_SPECSCHEME_MCCOMPAT)) { + m_implicit_port = LCB_CONFIG_MCCOMPAT_PORT; + } else { + /* If we don't have a scheme at all: */ + if (strstr(connstr_, "://")) { + SET_ERROR("String must begin with ''couchbase://, 'couchbases://', or 'http://'"); + } else { + found_scheme = ""; + m_implicit_port = LCB_CONFIG_HTTP_PORT; + } + } + + connstr_ += strlen(found_scheme); + speclen = strlen(connstr_); + specend = connstr_ + speclen; + + /* Hosts end where either options or the bucket itself begin */ + if ((hlend = strpbrk(connstr_, "?/"))) { + if (*hlend == '?') { + /* Options first */ + options_ = hlend + 1; + + } else if (*hlend == '/') { + /* Bucket first. Options follow bucket */ + bucket_s = hlend + 1; + if ((options_ = strchr(bucket_s, '?'))) { + options_++; + } + } + } else { + hlend = specend; + } + + if (bucket_s != NULL) { + unsigned blen; + const char *b_end = options_ ? options_-1 : specend; + /* scan each of the options */ + blen = b_end - bucket_s; + + m_bucket.assign(bucket_s, bucket_s + blen); + if (!(m_flags & F_HASBUCKET)) { + if (!strcodecs::urldecode(m_bucket)) { + SET_ERROR("Couldn't decode bucket string"); + } + } + if (m_bucket.empty()) { + SET_ERROR("Bucket name is set to empty"); + } + } else if (bucket_s == NULL) { + m_bucket = "default"; + } + + if ((err = parse_hosts(connstr_, hlend, errmsg)) != LCB_SUCCESS) { + goto GT_DONE; + } + + if (m_hosts.empty()) { + m_hosts.resize(m_hosts.size()+1); + m_hosts.back().hostname = "localhost"; + } + + if (options_ != NULL) { + if ((err = parse_options(options_, specend, errmsg)) != LCB_SUCCESS) { + goto GT_DONE; + } + } + + if (m_username.empty()) { + m_username = m_bucket; + } + + GT_DONE: + return err; +} + +#define MAYBEDUP(s) ((s) && (*s)) ? strdup(s) : NULL + +static lcb_error_t +convert_hosts(std::string& outstr, const char *instr, int deflport) +{ + Hostlist hl; + lcb_error_t rc = hl.add(instr, -1, deflport); + if (rc != LCB_SUCCESS) { + return rc; + } + + for (size_t ii = 0; ii < hl.size(); ii++) { + const lcb_host_t& src = hl[ii]; + int port, rv; + outstr.append(src.host); + rv = sscanf(src.port, "%d", &port); + if (rv && port != deflport) { + const char *proto; + char tmpbuf[256]; + if (deflport == LCB_CONFIG_MCD_PORT) { + proto = "mcd"; + } else { + proto = "http"; + } + sprintf(tmpbuf, ":%d=%s", port, proto); + outstr.append(tmpbuf); + } + outstr.append(","); + } + return LCB_SUCCESS; +} + +#define TRYDUP(s) (s) ? strdup(s) : NULL +lcb_error_t +Connspec::load(const lcb_create_st& cropts) +{ + const char *errmsg; + const struct lcb_create_st2 *cr2 = &cropts.v.v2; + lcb_error_t err = LCB_SUCCESS; + + /* handle overrides */ + if (cr2->bucket && *cr2->bucket) { + m_flags |= F_HASBUCKET; + m_bucket = cr2->bucket; + } + + if (cr2->user && *cr2->user) { + m_flags |= F_HASUSER; + m_username = cr2->user; + } + + if (cr2->passwd && *cr2->passwd) { + m_flags |= F_HASPASSWD; + m_password = cr2->passwd; + } + + if (cropts.version == 3) { + return parse(cropts.v.v3.connstr, &errmsg); + } + + if (cropts.version > 2 || cropts.version < 0) { + return LCB_NOT_SUPPORTED; + } + + m_connstr = LCB_SPECSCHEME_RAW; + if (cr2->host) { + err = convert_hosts(m_connstr, cr2->host, LCB_CONFIG_HTTP_PORT); + if (err != LCB_SUCCESS) { + goto GT_DONE; + } + } + + if (cropts.version == 2 && cr2->mchosts) { + err = convert_hosts(m_connstr, cr2->mchosts, LCB_CONFIG_MCD_PORT); + if (err != LCB_SUCCESS) { + goto GT_DONE; + } + } + + if (cropts.version < 3 && cr2->bucket) { + m_connstr += "/"; + m_connstr += cr2->bucket; + } + + m_connstr += '?'; + err = parse(m_connstr.c_str(), &errmsg); + if (err == LCB_SUCCESS && cropts.version == 2 && cr2->transports) { + /* copy over bootstrap list */ + for (size_t ii = 0; ii < LCB_CONFIG_TRANSPORT_MAX; ii++) { + if (cr2->transports[ii] == LCB_CONFIG_TRANSPORT_LIST_END) { + break; + } + m_transports.insert(cr2->transports[ii]); + } + } + + GT_DONE: + return err; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/connspec.h b/couchbase-sys/libcouchbase-2.7.0/src/connspec.h new file mode 100644 index 00000000..eae3903a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/connspec.h @@ -0,0 +1,118 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_CONNSPEC_H +#define LCB_CONNSPEC_H + +#include +#include "config.h" +#include +#include +#include + +#ifdef _MSC_VER +/* + * Disable DLL interface warning. This isn't an issue since this API is + * private anyway + */ +#pragma warning(push) +#pragma warning(disable : 4251) +#endif + +namespace lcb { +struct Spechost { + Spechost() : port(0), type(0) {} + lcb_U16 port; + short type; + std::string hostname; + bool isSSL() const { return type == LCB_CONFIG_MCD_SSL_PORT || type == LCB_CONFIG_HTTP_SSL_PORT; } + bool isHTTPS() const { return type == LCB_CONFIG_HTTP_SSL_PORT; } + bool isHTTP() const { return type == LCB_CONFIG_HTTP_PORT; } + bool isMCD() const { return type == LCB_CONFIG_MCD_PORT; } + bool isMCDS() const { return type == LCB_CONFIG_MCD_SSL_PORT; } + bool isTypeless() const { return type == 0 ; } + + bool isAnyMcd() const { + return isMCD() || isMCDS() || type == LCB_CONFIG_MCCOMPAT_PORT; + } + bool isAnyHttp() const { + return isHTTP() || isHTTPS(); + } +}; + +#define LCB_CONNSPEC_F_FILEONLY (1<<4) + +class LCB_CLASS_EXPORT Connspec { +public: + typedef std::vector > Options; + Connspec() : m_sslopts(0), m_implicit_port(0), m_loglevel(0), m_flags(0) {} + + lcb_error_t parse(const char *connstr, const char **errmsg = NULL); + lcb_error_t load(const lcb_create_st&); + + bool has_bsmode(int mode) const { + return m_transports.find(mode) != m_transports.end(); + } + bool is_bs_udef() const { + return !m_transports.empty() || (m_flags & LCB_CONNSPEC_F_FILEONLY); + } + bool is_bs_http() const { return has_bsmode(LCB_CONFIG_TRANSPORT_HTTP); } + bool is_bs_cccp() const { return has_bsmode(LCB_CONFIG_TRANSPORT_CCCP); } + bool is_bs_file() const { return m_flags & LCB_CONNSPEC_F_FILEONLY; } + uint16_t default_port() const { return m_implicit_port; } + const std::vector& hosts() const { return m_hosts; } + const std::string& bucket() const { return m_bucket; } + const std::string& username() const { return m_username; } + const std::string& password() const { return m_password; } + const std::string& certpath() const { return m_certpath; } + unsigned sslopts() const { return m_sslopts; } + const Options& options() const { return m_ctlopts; } + unsigned loglevel() const { return m_loglevel; } + const std::string& connstr() const { return m_connstr; } +private: + Options m_ctlopts; + std::string m_bucket; + std::string m_username; + std::string m_password; + std::string m_certpath; + std::string m_connstr; + unsigned m_sslopts; /**< SSL Options */ + std::vector m_hosts; + lcb_U16 m_implicit_port; /**< Implicit port, based on scheme */ + int m_loglevel; /* cached loglevel */ + + inline lcb_error_t parse_options( + const char *options, const char *optend, const char **errmsg); + inline lcb_error_t parse_hosts( + const char *hostbegin, const char *hostend, const char **errmsg); + + std::set m_transports; + unsigned m_flags; /**< Internal flags */ +}; + +#define LCB_SPECSCHEME_RAW "couchbase+explicit://" +#define LCB_SPECSCHEME_MCD "couchbase://" +#define LCB_SPECSCHEME_MCD_SSL "couchbases://" +#define LCB_SPECSCHEME_HTTP "http://" +#define LCB_SPECSCHEME_HTTP_SSL "https-internal://" +#define LCB_SPECSCHEME_MCCOMPAT "memcached://" +} // namespace +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/ctx-log-inl.h b/couchbase-sys/libcouchbase-2.7.0/src/ctx-log-inl.h new file mode 100644 index 00000000..4e076614 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/ctx-log-inl.h @@ -0,0 +1,27 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* small utility for retrieving host/port information from the CTX */ +static const char* get__ctx_hostinfo(const lcbio_CTX *ctx, int is_host) +{ + if (ctx == NULL || ctx->sock == NULL || ctx->sock->info == NULL) { + return is_host ? "NOHOST" : "NOPORT"; + } + return is_host ? ctx->sock->info->ep.host : ctx->sock->info->ep.port; +} +static const char *get_ctx_host(const lcbio_CTX *ctx) { return get__ctx_hostinfo(ctx, 1); } +static const char *get_ctx_port(const lcbio_CTX *ctx) { return get__ctx_hostinfo(ctx, 0); } diff --git a/couchbase-sys/libcouchbase-2.7.0/src/dump.cc b/couchbase-sys/libcouchbase-2.7.0/src/dump.cc new file mode 100644 index 00000000..6788c32f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/dump.cc @@ -0,0 +1,95 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" +#include "bucketconfig/clconfig.h" +#include "list.h" +#include "mc/mcreq.h" +#include "retryq.h" + +LIBCOUCHBASE_API +void +lcb_dump(lcb_t instance, FILE *fp, lcb_U32 flags) +{ + unsigned ii; + + if (!fp) { + fp = stderr; + } + fprintf(fp, "Dumping state for lcb_t=%p\n", (void*)instance); + fprintf(fp, "Settings=%p\n", (void*)instance->settings); + + if (instance->cur_configinfo) { + clconfig_info *cfg = instance->cur_configinfo; + fprintf(fp, "Current VBC=%p\n", (void*)cfg->vbc); + fprintf(fp, "Config RevID=%d\n", cfg->vbc->revid); + if (flags & LCB_DUMP_VBCONFIG) { + char *cfgdump = lcbvb_save_json(cfg->vbc); + fprintf(fp, "=== CLUSTER CONFIG BEGIN ===\n"); + fprintf(fp, "%s", cfgdump); + free(cfgdump); + fprintf(fp, "\n"); + fprintf(fp, "=== CLUSTER CONFIG END ===\n"); + } else { + fprintf(fp, "=== NOT DUMPING CLUSTER CONFIG. LCB_DUMP_VBCONFIG not passed\n"); + } + } else { + fprintf(fp, "NO CLUSTER CONFIG\n"); + } + fprintf(fp,"Retry queue has items: %s\n", instance->retryq->empty() ? "No" : "Yes"); + if (flags & LCB_DUMP_PKTINFO) { + fprintf(fp, "=== BEGIN RETRY QUEUE DUMP ===\n"); + instance->retryq->dump(fp, NULL); + fprintf(fp, "=== END RETRY QUEUE DUMP ===\n"); + } else { + fprintf(fp, "=== NOT DUMPING PACKET INFO. LCB_DUMP_PKTINFO not passed\n"); + } + + fprintf(fp, "=== BEGIN PIPELINE DUMP ===\n"); + for (ii = 0; ii < instance->cmdq.npipelines; ii++) { + lcb::Server *server = static_cast(instance->cmdq.pipelines[ii]); + fprintf(fp, "** [%u] SERVER %s:%s\n", ii, server->curhost->host, server->curhost->port); + if (server->connctx) { + fprintf(fp, "** == BEGIN SOCKET INFO\n"); + lcbio_ctx_dump(server->connctx, fp); + fprintf(fp, "** == END SOCKET INFO\n"); + } else if (server->connreq.u.p_generic) { + fprintf(fp, "** == STILL CONNECTING\n"); + } else { + fprintf(fp, "** == NOT CONNECTED\n"); + } + if (flags & LCB_DUMP_BUFINFO) { + fprintf(fp, "** == DUMPING NETBUF INFO (For packet network data)\n"); + netbuf_dump_status(&server->nbmgr, fp); + fprintf(fp, "** == DUMPING NETBUF INFO (For packet structures)\n"); + netbuf_dump_status(&server->reqpool, fp); + } else { + fprintf(fp, "** == NOT DUMPING NETBUF INFO. LCB_DUMP_BUFINFO not passed\n"); + } + if (flags & LCB_DUMP_PKTINFO) { + mcreq_dump_chain(server, fp, NULL); + } else { + fprintf(fp, "** == NOT DUMPING PACKETS. LCB_DUMP_PKTINFO not passed\n"); + } + fprintf(fp, "\n\n"); + } + fprintf(fp, "=== END PIPELINE DUMP ===\n"); + + fprintf(fp, "=== BEGIN CONFMON DUMP ===\n"); + lcb_confmon_dump(instance->confmon, fp); + fprintf(fp, "=== END CONFMON DUMP ===\n"); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/getconfig.cc b/couchbase-sys/libcouchbase-2.7.0/src/getconfig.cc new file mode 100644 index 00000000..61e2b706 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/getconfig.cc @@ -0,0 +1,99 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" +#include "packetutils.h" +#include +LCB_INTERNAL_API +lcb::Server * +lcb_find_server_by_host(lcb_t instance, const lcb_host_t *host) +{ + mc_CMDQUEUE *cq = &instance->cmdq; + unsigned ii; + for (ii = 0; ii < cq->npipelines; ii++) { + lcb::Server *server = static_cast(cq->pipelines[ii]); + if (lcb_host_equals(&server->get_host(), host)) { + return server; + } + } + return NULL; +} + +LCB_INTERNAL_API +lcb::Server * +lcb_find_server_by_index(lcb_t instance, int ix) +{ + mc_CMDQUEUE *cq = &instance->cmdq; + if (ix < 0 || ix >= static_cast(cq->npipelines)) { + return NULL; + } + return static_cast(cq->pipelines[ix]); +} + +static void +ext_callback_proxy(mc_PIPELINE *pl, mc_PACKET *req, lcb_error_t rc, + const void *resdata) +{ + lcb::Server *server = static_cast(pl); + mc_REQDATAEX *rd = req->u_rdata.exdata; + const lcb::MemcachedResponse *res = + reinterpret_cast(resdata); + + lcb_cccp_update2(rd->cookie, rc, res->body(), res->bodylen(), + &server->get_host()); + free(rd); +} + +static mc_REQDATAPROCS procs = { ext_callback_proxy }; + +LCB_INTERNAL_API +lcb_error_t +lcb_getconfig(lcb_t instance, const void *cookie, lcb::Server *server) +{ + lcb_error_t err; + mc_CMDQUEUE *cq = &instance->cmdq; + mc_PACKET *packet; + mc_REQDATAEX *rd; + + packet = mcreq_allocate_packet(server); + if (!packet) { + return LCB_CLIENT_ENOMEM; + } + + err = mcreq_reserve_header(server, packet, 24); + if (err != LCB_SUCCESS) { + mcreq_release_packet(server, packet); + return err; + } + + rd = reinterpret_cast(calloc(1, sizeof(*rd))); + rd->procs = &procs; + rd->cookie = cookie; + rd->start = gethrtime(); + packet->u_rdata.exdata = rd; + packet->flags |= MCREQ_F_REQEXT; + + lcb::MemcachedRequest hdr( + PROTOCOL_BINARY_CMD_GET_CLUSTER_CONFIG, packet->opaque); + hdr.opaque(packet->opaque); + memcpy(SPAN_BUFFER(&packet->kh_span), hdr.data(), hdr.size()); + + mcreq_sched_enter(cq); + mcreq_sched_add(server, packet); + mcreq_sched_leave(cq, 1); + return LCB_SUCCESS; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/gethrtime.c b/couchbase-sys/libcouchbase-2.7.0/src/gethrtime.c new file mode 100644 index 00000000..153281cd --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/gethrtime.c @@ -0,0 +1,109 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2011-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include "settings.h" +#include + +#ifndef HAVE_GETHRTIME + +#include +#include +#if defined(__MACH__) && defined(__APPLE__) +#include +#endif + +#ifdef __linux__ +#undef HAVE_CLOCK_GETTIME +#define HAVE_CLOCK_GETTIME 1 +#endif + +#define CLOCK_START_OFFSET (LCB_S2NS(3600 * 24)) + +hrtime_t gethrtime(void) +{ +#ifdef __APPLE__ + + /* Most things expect a pretty large timestamp - even though a smaller one + * may be perfectly valid. Initialize the default with an offset of one day, + * in nanoseconds + */ + + /* Use the various mach stuff: + * https://developer.apple.com/library/mac/qa/qa1398/_index.html */ + + static uint64_t start = 0; + uint64_t now ; + static mach_timebase_info_data_t tmbi; + + if (start == 0) { + start = mach_absolute_time(); + mach_timebase_info(&tmbi); + } + + now = mach_absolute_time(); + return ((now - start) * tmbi.numer / tmbi.denom) + CLOCK_START_OFFSET; + +#elif defined(HAVE_CLOCK_GETTIME) + struct timespec tm; + lcb_assert(clock_gettime(CLOCK_MONOTONIC, &tm) != -1); + return (((hrtime_t)tm.tv_sec) * 1000000000) + (hrtime_t)tm.tv_nsec; +#elif defined(HAVE_GETTIMEOFDAY) + + hrtime_t ret; + struct timeval tv; + if (gettimeofday(&tv, NULL) == -1) { + return -1; + } + + ret = (hrtime_t)tv.tv_sec * 1000000000; + ret += (hrtime_t)tv.tv_usec * 1000; + return ret; +#elif defined(HAVE_QUERYPERFORMANCECOUNTER) + double ret; + // To fix the potential race condition for the local static variable, + // gethrtime should be called in a global static variable first. + // It will guarantee the local static variable will be initialized + // before any thread calls the function. + static LARGE_INTEGER pf = { 0 }; + static double freq; + LARGE_INTEGER currtime; + + if (pf.QuadPart == 0) { + lcb_assert(QueryPerformanceFrequency(&pf)); + lcb_assert(pf.QuadPart != 0); + freq = 1.0e9 / (double)pf.QuadPart; + } + + QueryPerformanceCounter(&currtime); + + ret = (double)currtime.QuadPart * freq ; + return (hrtime_t)ret + CLOCK_START_OFFSET; +#else +#error "I don't know how to build a highres clock..." +#endif +} +#endif /* HAVE_GETHRTIME */ + +/* Symbol usable so other subsystems can get the same idea of time the library + * has. This will also allow us to stop shipping the 'gethrtime' file around. + */ + +LCB_INTERNAL_API +lcb_U64 lcb_nstime(void) { + return gethrtime(); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/handler.cc b/couchbase-sys/libcouchbase-2.7.0/src/handler.cc new file mode 100644 index 00000000..32914998 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/handler.cc @@ -0,0 +1,936 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" +#include "packetutils.h" +#include "mc/mcreq.h" +#include "mc/compress.h" +#include "trace.h" + +using lcb::MemcachedResponse; + +template +class ResponsePack { +public: + T resp; + lcb_MUTATION_TOKEN mt; + + static const lcb_MUTATION_TOKEN* + get_mt(const lcb_RESPBASE *rb) { + const ResponsePack *rp = reinterpret_cast(rb); + return &rp->mt; + } +}; + +LIBCOUCHBASE_API +lcb_error_t +lcb_errmap_default(lcb_t instance, lcb_uint16_t in) +{ + switch (in) { + case PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET: + return LCB_ETIMEDOUT; + case PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE: + return LCB_AUTH_CONTINUE; + case PROTOCOL_BINARY_RESPONSE_EBUSY: + return LCB_EBUSY; + case PROTOCOL_BINARY_RESPONSE_ETMPFAIL: + return LCB_ETMPFAIL; + + case PROTOCOL_BINARY_RESPONSE_EINTERNAL: + default: + if (instance) { + lcb_log(instance->settings, "handler", LCB_LOG_ERROR, __FILE__, __LINE__, "Got unhandled memcached error 0x%X", in); + } else { + fprintf(stderr, "COUCHBASE: Unhandled memcached status=0x%x\n", in); + } + return LCB_UNKNOWN_MEMCACHED_ERROR; + } +} + +static lcb_error_t +map_error(lcb_t instance, int in) +{ + switch (in) { + case PROTOCOL_BINARY_RESPONSE_SUCCESS: + return LCB_SUCCESS; + case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT: + return LCB_KEY_ENOENT; + case PROTOCOL_BINARY_RESPONSE_E2BIG: + return LCB_E2BIG; + case PROTOCOL_BINARY_RESPONSE_ENOMEM: + return LCB_ENOMEM; + case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS: + return LCB_KEY_EEXISTS; + case PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_ENOENT: + return LCB_SUBDOC_PATH_ENOENT; + case PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_MISMATCH: + return LCB_SUBDOC_PATH_MISMATCH; + case PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_EINVAL: + return LCB_SUBDOC_PATH_EINVAL; + case PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_E2BIG: + return LCB_SUBDOC_PATH_E2BIG; + case PROTOCOL_BINARY_RESPONSE_SUBDOC_DOC_E2DEEP: + return LCB_SUBDOC_DOC_E2DEEP; + case PROTOCOL_BINARY_RESPONSE_SUBDOC_VALUE_ETOODEEP: + return LCB_SUBDOC_VALUE_E2DEEP; + case PROTOCOL_BINARY_RESPONSE_SUBDOC_VALUE_CANTINSERT: + return LCB_SUBDOC_VALUE_CANTINSERT; + case PROTOCOL_BINARY_RESPONSE_SUBDOC_DOC_NOTJSON: + return LCB_SUBDOC_DOC_NOTJSON; + case PROTOCOL_BINARY_RESPONSE_SUBDOC_NUM_ERANGE: + return LCB_SUBDOC_NUM_ERANGE; + case PROTOCOL_BINARY_RESPONSE_SUBDOC_DELTA_ERANGE: + return LCB_SUBDOC_BAD_DELTA; + case PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_EEXISTS: + return LCB_SUBDOC_PATH_EEXISTS; + case PROTOCOL_BINARY_RESPONSE_SUBDOC_MULTI_PATH_FAILURE: + return LCB_SUBDOC_MULTI_FAILURE; + case PROTOCOL_BINARY_RESPONSE_EINVAL: + return LCB_EINVAL_MCD; + case PROTOCOL_BINARY_RESPONSE_NOT_STORED: + return LCB_NOT_STORED; + case PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL: + return LCB_DELTA_BADVAL; + case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR: + return LCB_AUTH_ERROR; + case PROTOCOL_BINARY_RESPONSE_ERANGE: + return LCB_ERANGE; + case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND: + return LCB_UNKNOWN_COMMAND; + case PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED: + return LCB_NOT_SUPPORTED; + default: + if (instance != NULL) { + return instance->callbacks.errmap(instance, in); + } else { + return lcb_errmap_default(NULL, in); + } + } +} + +static lcb_RESPCALLBACK +find_callback(lcb_t instance, lcb_CALLBACKTYPE type) +{ + lcb_RESPCALLBACK cb = instance->callbacks.v3callbacks[type]; + if (!cb) { + cb = lcb_find_callback(instance, type); + } + return cb; +} + + +/** + * This file contains the mapping of various protocol response codes for + * a given command. Each handler receives the following parameters: + * + * @param pipeline the pipeline (or "Server") upon which the request was sent + * (and response was received) + * + * @param request the original request (including associated data). The request + * may be used to determine additional information about it, such as the + * user-defined "Cookie", number of related requests remaining, and more. + * + * @param response the response which was received. This is an opaque + * representation of a memcached response packet + * + * @param immerr in the case of an abnormal failure (i.e. network failure) the + * handler will be invoked with this callback set to a non-success value. The + * 'info' structure will still contain a valid (albeit empty and cryptic) + * header. If the user depends on special data being found in the payload then + * the handler must check that this variable is set to LCB_SUCCESS before + * continuing. Also note that a negative reply may also be present within + * the response itself; however this is not the purpose of this parameter. + * + * @return request status + * The return value should indicate whether outstanding responses remain + * to be received for this request, or if this request is deemed to be + * satisfied. + */ + +template +void make_error(lcb_t instance, T* resp, + const MemcachedResponse *response, lcb_error_t imm) { + if (imm) { + resp->rc = imm; + resp->rflags |= LCB_RESP_F_CLIENTGEN; + } else if (response->status() == PROTOCOL_BINARY_RESPONSE_SUCCESS) { + resp->rc = LCB_SUCCESS; + } else { + resp->rc = map_error(instance, response->status()); + } +} + +template +void init_resp(lcb_t instance, const MemcachedResponse* mc_resp, + const mc_PACKET *req, lcb_error_t immerr, T *resp) { + make_error(instance, resp, mc_resp, immerr); + resp->cas = mc_resp->cas(); + resp->cookie = const_cast(MCREQ_PKT_COOKIE(req)); + mcreq_get_key(req, &resp->key, &resp->nkey); +} + +/** + * Handles the propagation and population of the 'mutation token' information. + * @param mc_resp The response packet + * @param req The request packet (used to get the vBucket) + * @param tgt Pointer to mutation token which should be populated. + */ +static void +handle_mutation_token(lcb_t instance, const MemcachedResponse *mc_resp, + const mc_PACKET *req, lcb_MUTATION_TOKEN *stok) +{ + const char *sbuf; + uint16_t vbid; + if (mc_resp->extlen() == 0) { + return; /* No extras */ + } + + if (!instance->dcpinfo && LCBT_SETTING(instance, dur_mutation_tokens)) { + size_t nvb = LCBT_VBCONFIG(instance)->nvb; + if (nvb) { + instance->dcpinfo = new lcb_MUTATION_TOKEN[nvb]; + memset(instance->dcpinfo, 0, sizeof(*instance->dcpinfo) * nvb); + } + } + + sbuf = mc_resp->body(); + vbid = mcreq_get_vbucket(req); + stok->vbid_ = vbid; + memcpy(&stok->uuid_, sbuf, 8); + memcpy(&stok->seqno_, sbuf + 8, 8); + + stok->uuid_ = lcb_ntohll(stok->uuid_); + stok->seqno_ = lcb_ntohll(stok->seqno_); + + if (instance->dcpinfo) { + instance->dcpinfo[vbid] = *stok; + } +} + +static lcb_t get_instance(mc_PIPELINE *pipeline) { + return reinterpret_cast(pipeline->parent->cqdata); +} + +template +void invoke_callback(const mc_PACKET *pkt, + lcb_t instance, T* resp, lcb_CALLBACKTYPE cbtype) +{ + if (!(pkt->flags & MCREQ_F_INVOKED)) { + resp->cookie = const_cast(MCREQ_PKT_COOKIE(pkt)); + const lcb_RESPBASE *base = reinterpret_cast(resp); + if ((pkt->flags & MCREQ_F_PRIVCALLBACK) == 0) { + find_callback(instance, cbtype)(instance, cbtype, base); + } else { + (*(lcb_RESPCALLBACK*)resp->cookie)(instance, cbtype, base); + } + } +} + +template +void invoke_callback(const mc_PACKET *pkt, mc_PIPELINE *pipeline, T *resp, + lcb_CALLBACKTYPE cbtype) +{ + invoke_callback(pkt, get_instance(pipeline), cbtype, resp); +} + +/** + * Optionally decompress an incoming payload. + * @param o The instance + * @param resp The response received + * @param[out] bytes pointer to the final payload + * @param[out] nbytes pointer to the size of the final payload + * @param[out] freeptr pointer to free. This should be initialized to `NULL`. + * If temporary dynamic storage is required this will be set to the allocated + * pointer upon return. Otherwise it will be set to NULL. In any case it must + */ +static void +maybe_decompress(lcb_t o, + const MemcachedResponse* respkt, lcb_RESPGET *rescmd, void **freeptr) +{ + lcb_U8 dtype = 0; + if (!respkt->vallen()) { + return; + } + + if (respkt->datatype() & PROTOCOL_BINARY_DATATYPE_JSON) { + dtype = LCB_VALUE_F_JSON; + } + + if (respkt->datatype() & PROTOCOL_BINARY_DATATYPE_COMPRESSED) { + if (LCBT_SETTING(o, compressopts) & LCB_COMPRESS_IN) { + /* if we inflate, we don't set the flag */ + mcreq_inflate_value( + respkt->value(), respkt->vallen(), + &rescmd->value, &rescmd->nvalue, freeptr); + + } else { + /* user doesn't want inflation. signal it's compressed */ + dtype |= LCB_VALUE_F_SNAPPYCOMP; + } + } + rescmd->datatype = dtype; +} + +static void +H_get(mc_PIPELINE *pipeline, mc_PACKET *request, MemcachedResponse* response, + lcb_error_t immerr) +{ + lcb_RESPGET resp = { 0 }; + + lcb_t o = get_instance(pipeline); + init_resp(o, response, request, immerr, &resp); + resp.rflags |= LCB_RESP_F_FINAL; + + if (resp.rc == LCB_SUCCESS) { + const protocol_binary_response_get *get = + reinterpret_cast( + response->ephemeral_start()); + resp.datatype = response->datatype(); + resp.itmflags = ntohl(get->message.body.flags); + resp.value = response->value(); + resp.nvalue = response->vallen(); + resp.bufh = response->bufseg(); + } + + void *freeptr = NULL; + maybe_decompress(o, response, &resp, &freeptr); + TRACE_GET_END(response, &resp); + invoke_callback(request, o, &resp, LCB_CALLBACK_GET); + free(freeptr); +} + +static void +H_getreplica(mc_PIPELINE *pipeline, mc_PACKET *request, + MemcachedResponse *response, lcb_error_t immerr) +{ + lcb_RESPGET resp = { 0 }; + lcb_t instance = get_instance(pipeline); + void *freeptr = NULL; + mc_REQDATAEX *rd = request->u_rdata.exdata; + + init_resp(instance, response, request, immerr, &resp); + + if (resp.rc == LCB_SUCCESS) { + const protocol_binary_response_get *get = + reinterpret_cast( + response->ephemeral_start()); + resp.itmflags = ntohl(get->message.body.flags); + resp.datatype = response->datatype(); + resp.value = response->value(); + resp.nvalue = response->vallen(); + resp.bufh = response->bufseg(); + } + + maybe_decompress(instance, response, &resp, &freeptr); + rd->procs->handler(pipeline, request, resp.rc, &resp); + free(freeptr); +} + +static void +H_subdoc(mc_PIPELINE *pipeline, mc_PACKET *request, + MemcachedResponse *response, lcb_error_t immerr) +{ + lcb_t o = get_instance(pipeline); + ResponsePack w = {{ 0 }}; + lcb_CALLBACKTYPE cbtype; + init_resp(o, response, request, immerr, &w.resp); + w.resp.rflags |= LCB_RESP_F_FINAL; + + /* For mutations, add the mutation token */ + switch (response->opcode()) { + case PROTOCOL_BINARY_CMD_SUBDOC_GET: + case PROTOCOL_BINARY_CMD_SUBDOC_EXISTS: + case PROTOCOL_BINARY_CMD_SUBDOC_MULTI_LOOKUP: + cbtype = LCB_CALLBACK_SDLOOKUP; + break; + + default: + handle_mutation_token(o, response, request, &w.mt); + w.resp.rflags |= LCB_RESP_F_EXTDATA; + cbtype = LCB_CALLBACK_SDMUTATE; + break; + } + + if (response->opcode() == PROTOCOL_BINARY_CMD_SUBDOC_MULTI_LOOKUP || + response->opcode() == PROTOCOL_BINARY_CMD_SUBDOC_MULTI_MUTATION) { + if (w.resp.rc == LCB_SUCCESS || w.resp.rc == LCB_SUBDOC_MULTI_FAILURE) { + w.resp.responses = response; + } + } else { + /* Single response */ + w.resp.rflags |= LCB_RESP_F_SDSINGLE; + if (w.resp.rc == LCB_SUCCESS) { + w.resp.responses = response; + } else if (LCB_EIFSUBDOC(w.resp.rc)) { + w.resp.responses = response; + w.resp.rc = LCB_SUBDOC_MULTI_FAILURE; + } + } + invoke_callback(request, o, &w.resp, cbtype); +} + +static int +sdlookup_next(const MemcachedResponse *response, lcb_SDENTRY *ent, size_t *iter) +{ + const char *buf; + uint16_t rc; + uint32_t vlen; + + if (*iter == response->vallen()) { + return 0; + } + + buf = response->value(); + buf += *iter; + + memcpy(&rc, buf, 2); + memcpy(&vlen, buf + 2, 4); + + rc = ntohs(rc); + vlen = ntohl(vlen); + + ent->status = map_error(NULL, rc); + ent->nvalue = vlen; + + if (ent->status == LCB_SUCCESS) { + ent->value = buf + 6; + } else { + ent->value = NULL; + ent->nvalue = 0; + } + + *iter += (6 + vlen); + return 1; +} + +static int +sdmutate_next(const MemcachedResponse *response, lcb_SDENTRY *ent, size_t *iter) +{ + const char *buf, *buf_end; + uint16_t rc; + uint32_t vlen; + + if (*iter == response->vallen()) { + return 0; + } + + buf_end = (const char *)response->value() + response->vallen(); + buf = ((const char *)(response->value())) + *iter; + + #define ADVANCE_BUF(sz) \ + buf += sz; \ + *iter += sz; \ + assert(buf <= buf_end); \ + + /* Index */ + ent->index = *(lcb_U8*)buf; + ADVANCE_BUF(1); + + /* Status */ + memcpy(&rc, buf, 2); + ADVANCE_BUF(2); + + rc = ntohs(rc); + ent->status = map_error(NULL, rc); + + if (rc == PROTOCOL_BINARY_RESPONSE_SUCCESS) { + memcpy(&vlen, buf, 4); + ADVANCE_BUF(4); + + vlen = ntohl(vlen); + ent->nvalue = vlen; + ent->value = buf; + ADVANCE_BUF(vlen); + + } else { + ent->value = NULL; + ent->nvalue = 0; + } + + return 1; + #undef ADVANCE_BUF +} + +LIBCOUCHBASE_API +int +lcb_sdresult_next(const lcb_RESPSUBDOC *resp, lcb_SDENTRY *ent, size_t *iter) +{ + size_t iter_s = 0; + const MemcachedResponse *response = + reinterpret_cast(resp->responses); + if (!response) { + return 0; + } + if (!iter) { + /* Single response */ + iter = &iter_s; + } + + switch (response->opcode()) { + case PROTOCOL_BINARY_CMD_SUBDOC_MULTI_LOOKUP: + return sdlookup_next(response, ent, iter); + case PROTOCOL_BINARY_CMD_SUBDOC_MULTI_MUTATION: + return sdmutate_next(response, ent, iter); + default: + if (*iter) { + return 0; + } + *iter = 1; + + if (resp->rc == LCB_SUCCESS || resp->rc == LCB_SUBDOC_MULTI_FAILURE) { + ent->status = map_error(NULL, response->status()); + ent->value = response->value(); + ent->nvalue = response->vallen(); + ent->index = 0; + return 1; + } else { + return 0; + } + } +} + +static void +H_delete(mc_PIPELINE *pipeline, mc_PACKET *packet, MemcachedResponse *response, + lcb_error_t immerr) +{ + lcb_t root = get_instance(pipeline); + ResponsePack w = { { 0 } }; + w.resp.rflags |= LCB_RESP_F_EXTDATA | LCB_RESP_F_FINAL; + init_resp(root, response, packet, immerr, &w.resp); + handle_mutation_token(root, response, packet, &w.mt); + TRACE_REMOVE_END(response, &w.resp); + invoke_callback(packet, root, &w.resp, LCB_CALLBACK_REMOVE); +} + +static void +H_observe(mc_PIPELINE *pipeline, mc_PACKET *request, MemcachedResponse *response, + lcb_error_t immerr) +{ + lcb_t root = get_instance(pipeline); + uint32_t ttp; + uint32_t ttr; + size_t pos; + lcbvb_CONFIG* config; + const char *end, *ptr; + mc_REQDATAEX *rd = request->u_rdata.exdata; + lcb_RESPOBSERVE resp = { 0 }; + make_error(root, &resp, response, immerr); + + if (resp.rc != LCB_SUCCESS) { + if (! (request->flags & MCREQ_F_INVOKED)) { + rd->procs->handler(pipeline, request, resp.rc, NULL); + } + return; + } + + /** The CAS field is split into TTP/TTR values */ + uint64_t tmpcas = lcb_htonll(response->cas()); + ptr = reinterpret_cast(&tmpcas); + memcpy(&ttp, ptr, sizeof(ttp)); + memcpy(&ttr, ptr + sizeof(ttp), sizeof(ttp)); + + ttp = ntohl(ttp); + ttr = ntohl(ttr); + + /** Actual payload sequence of (vb, nkey, key). Repeats multiple times */ + ptr = response->body(); + end = ptr + response->bodylen(); + config = pipeline->parent->config; + + for (pos = 0; ptr < end; pos++) { + uint64_t cas; + uint8_t obs; + uint16_t nkey, vb; + const char *key; + + memcpy(&vb, ptr, sizeof(vb)); + vb = ntohs(vb); + ptr += sizeof(vb); + memcpy(&nkey, ptr, sizeof(nkey)); + nkey = ntohs(nkey); + ptr += sizeof(nkey); + key = (const char *)ptr; + ptr += nkey; + obs = *((lcb_uint8_t *)ptr); + ptr += sizeof(obs); + memcpy(&cas, ptr, sizeof(cas)); + ptr += sizeof(cas); + + resp.key = key; + resp.nkey = nkey; + resp.cas = lcb_ntohll(cas); + resp.status = obs; + resp.ismaster = pipeline->index == lcbvb_vbmaster(config, vb); + resp.ttp = 0; + resp.ttr = 0; + TRACE_OBSERVE_PROGRESS(response, &resp); + if (! (request->flags & MCREQ_F_INVOKED)) { + rd->procs->handler(pipeline, request, resp.rc, &resp); + } + } + TRACE_OBSERVE_END(response); +} + +static void +H_observe_seqno(mc_PIPELINE *pipeline, mc_PACKET *request, + MemcachedResponse *response, lcb_error_t immerr) { + lcb_t root = get_instance(pipeline); + lcb_RESPOBSEQNO resp = { 0 }; + init_resp(root, response, request, immerr, &resp); + + resp.server_index = pipeline->index; + + if (resp.rc == LCB_SUCCESS) { + const uint8_t *data = response->body(); + bool is_failover = *data != 0; + + data++; + #define COPY_ADV(dstfld, n, conv_fn) \ + memcpy(&resp.dstfld, data, n); \ + data += n; \ + resp.dstfld = conv_fn(resp.dstfld); + + COPY_ADV(vbid, 2, ntohs); + COPY_ADV(cur_uuid, 8, lcb_ntohll); + COPY_ADV(persisted_seqno, 8, lcb_ntohll); + COPY_ADV(mem_seqno, 8, lcb_ntohll); + if (is_failover) { + COPY_ADV(old_uuid, 8, lcb_ntohll); + COPY_ADV(old_seqno, 8, lcb_ntohll); + } + #undef COPY_ADV + + /* Get the server for this command. Note that since this is a successful + * operation, the server is never a dummy */ + } + invoke_callback(request, root, &resp, LCB_CALLBACK_OBSEQNO); +} + +static void +H_store(mc_PIPELINE *pipeline, mc_PACKET *request, MemcachedResponse *response, + lcb_error_t immerr) +{ + lcb_t root = get_instance(pipeline); + ResponsePack w = { { 0 } }; + uint8_t opcode; + init_resp(root, response, request, immerr, &w.resp); + if (!immerr) { + opcode = response->opcode(); + } else { + protocol_binary_request_header hdr; + mcreq_read_hdr(request, &hdr); + opcode = hdr.request.opcode; + } + if (opcode == PROTOCOL_BINARY_CMD_ADD) { + w.resp.op = LCB_ADD; + } else if (opcode == PROTOCOL_BINARY_CMD_REPLACE) { + w.resp.op = LCB_REPLACE; + } else if (opcode == PROTOCOL_BINARY_CMD_APPEND) { + w.resp.op = LCB_APPEND; + } else if (opcode == PROTOCOL_BINARY_CMD_PREPEND) { + w.resp.op = LCB_PREPEND; + } else if (opcode == PROTOCOL_BINARY_CMD_SET) { + w.resp.op = LCB_SET; + } + w.resp.rflags |= LCB_RESP_F_EXTDATA | LCB_RESP_F_FINAL; + handle_mutation_token(root, response, request, &w.mt); + TRACE_STORE_END(response, &w.resp); + if (request->flags & MCREQ_F_REQEXT) { + request->u_rdata.exdata->procs->handler(pipeline, request, immerr, &w.resp); + } else { + invoke_callback(request, root, &w.resp, LCB_CALLBACK_STORE); + } +} + +static void +H_arithmetic(mc_PIPELINE *pipeline, mc_PACKET *request, + MemcachedResponse *response, lcb_error_t immerr) +{ + lcb_t root = get_instance(pipeline); + ResponsePack w = { { 0 } }; + init_resp(root, response, request, immerr, &w.resp); + + if (w.resp.rc == LCB_SUCCESS) { + memcpy(&w.resp.value, response->value(), sizeof(w.resp.value)); + w.resp.value = lcb_ntohll(w.resp.value); + w.resp.rflags |= LCB_RESP_F_EXTDATA; + handle_mutation_token(root, response, request, &w.mt); + } + w.resp.rflags |= LCB_RESP_F_FINAL; + w.resp.cas = response->cas(); + TRACE_ARITHMETIC_END(response, &w.resp); + invoke_callback(request, root, &w.resp, LCB_CALLBACK_COUNTER); +} + +static void +H_stats(mc_PIPELINE *pipeline, mc_PACKET *request, + MemcachedResponse *response, lcb_error_t immerr) +{ + lcb_t root = get_instance(pipeline); + lcb_RESPSTATS resp = { 0 }; + mc_REQDATAEX *exdata; + + make_error(root, &resp, response, immerr); + resp.version = 0; + + exdata = request->u_rdata.exdata; + if (resp.rc != LCB_SUCCESS || response->keylen() == 0) { + /* Call the handler without a response, this indicates that this server + * has finished responding */ + exdata->procs->handler(pipeline, request, resp.rc, NULL); + return; + } + + if ((resp.nkey = response->keylen())) { + resp.key = response->key(); + if ((resp.value = response->value())) { + resp.nvalue = response->vallen(); + } + } + + exdata->procs->handler(pipeline, request, resp.rc, &resp); +} + +static void +H_verbosity(mc_PIPELINE *pipeline, mc_PACKET *request, + MemcachedResponse *response, lcb_error_t immerr) +{ + lcb_t root = get_instance(pipeline); + lcb_RESPBASE dummy = { 0 }; + mc_REQDATAEX *exdata = request->u_rdata.exdata; + make_error(root, &dummy, response, immerr); + + exdata->procs->handler(pipeline, request, dummy.rc, NULL); +} + +static void +H_version(mc_PIPELINE *pipeline, mc_PACKET *request, + MemcachedResponse *response, lcb_error_t immerr) +{ + lcb_t root = get_instance(pipeline); + lcb_RESPMCVERSION resp = { 0 }; + mc_REQDATAEX *exdata = request->u_rdata.exdata; + + make_error(root, &resp, response, immerr); + + if (response->bodylen()) { + resp.mcversion = response->body(); + resp.nversion = response->bodylen(); + } + + + exdata->procs->handler(pipeline, request, resp.rc, &resp); +} + +static void +H_touch(mc_PIPELINE *pipeline, mc_PACKET *request, MemcachedResponse *response, + lcb_error_t immerr) +{ + lcb_t root = get_instance(pipeline); + lcb_RESPTOUCH resp = { 0 }; + init_resp(root, response, request, immerr, &resp); + resp.rflags |= LCB_RESP_F_FINAL; + TRACE_TOUCH_END(response, &resp); + invoke_callback(request, root, &resp, LCB_CALLBACK_TOUCH); +} + +static void +H_flush(mc_PIPELINE *pipeline, mc_PACKET *request, MemcachedResponse *response, + lcb_error_t immerr) +{ + lcb_t root = get_instance(pipeline); + lcb_RESPFLUSH resp = { 0 }; + mc_REQDATAEX *exdata = request->u_rdata.exdata; + make_error(root, &resp, response, immerr); + exdata->procs->handler(pipeline, request, resp.rc, &resp); +} + +static void +H_unlock(mc_PIPELINE *pipeline, mc_PACKET *request, MemcachedResponse *response, + lcb_error_t immerr) +{ + lcb_t root = get_instance(pipeline); + lcb_RESPUNLOCK resp = { 0 }; + init_resp(root, response, request, immerr, &resp); + resp.rflags |= LCB_RESP_F_FINAL; + TRACE_UNLOCK_END(response, &resp); + invoke_callback(request, root, &resp, LCB_CALLBACK_UNLOCK); +} + +static void +H_config(mc_PIPELINE *pipeline, mc_PACKET *request, MemcachedResponse *response, + lcb_error_t immerr) +{ + /** We just jump to the normal config handler */ + lcb_RESPBASE dummy; + mc_REQDATAEX *exdata = request->u_rdata.exdata; + make_error(get_instance(pipeline), &dummy, response, immerr); + + exdata->procs->handler(pipeline, request, dummy.rc, response); +} + +static void +record_metrics(mc_PIPELINE *pipeline, mc_PACKET *req, MemcachedResponse *) +{ + lcb_t instance = get_instance(pipeline); + if (instance->kv_timings) { + lcb_histogram_record(instance->kv_timings, + gethrtime() - MCREQ_PKT_RDATA(req)->start); + } +} + +static void +dispatch_ufwd_error(mc_PIPELINE *pipeline, mc_PACKET *req, lcb_error_t immerr) +{ + lcb_PKTFWDRESP resp = { 0 }; + lcb_t instance = static_cast(pipeline)->get_instance(); + assert(immerr != LCB_SUCCESS); + resp.version = 0; + instance->callbacks.pktfwd(instance, MCREQ_PKT_COOKIE(req), immerr, &resp); +} + +int +mcreq_dispatch_response( + mc_PIPELINE *pipeline, mc_PACKET *req, MemcachedResponse *res, + lcb_error_t immerr) +{ + record_metrics(pipeline, req, res); + + if (req->flags & MCREQ_F_UFWD) { + dispatch_ufwd_error(pipeline, req, immerr); + return 0; + } + + +#define INVOKE_OP(handler) \ + handler(pipeline, req, res, immerr); \ + return 0; \ + break; + + switch (res->opcode()) { + case PROTOCOL_BINARY_CMD_GETQ: + case PROTOCOL_BINARY_CMD_GATQ: + case PROTOCOL_BINARY_CMD_GET: + case PROTOCOL_BINARY_CMD_GAT: + case PROTOCOL_BINARY_CMD_GET_LOCKED: + INVOKE_OP(H_get); + + case PROTOCOL_BINARY_CMD_ADD: + case PROTOCOL_BINARY_CMD_REPLACE: + case PROTOCOL_BINARY_CMD_SET: + case PROTOCOL_BINARY_CMD_APPEND: + case PROTOCOL_BINARY_CMD_PREPEND: + INVOKE_OP(H_store); + + case PROTOCOL_BINARY_CMD_INCREMENT: + case PROTOCOL_BINARY_CMD_DECREMENT: + INVOKE_OP(H_arithmetic); + + case PROTOCOL_BINARY_CMD_SUBDOC_GET: + case PROTOCOL_BINARY_CMD_SUBDOC_EXISTS: + case PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_ADD_UNIQUE: + case PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST: + case PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST: + case PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_INSERT: + case PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD: + case PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT: + case PROTOCOL_BINARY_CMD_SUBDOC_REPLACE: + case PROTOCOL_BINARY_CMD_SUBDOC_DELETE: + case PROTOCOL_BINARY_CMD_SUBDOC_COUNTER: + case PROTOCOL_BINARY_CMD_SUBDOC_GET_COUNT: + case PROTOCOL_BINARY_CMD_SUBDOC_MULTI_LOOKUP: + case PROTOCOL_BINARY_CMD_SUBDOC_MULTI_MUTATION: + INVOKE_OP(H_subdoc); + + case PROTOCOL_BINARY_CMD_OBSERVE: + INVOKE_OP(H_observe); + + case PROTOCOL_BINARY_CMD_GET_REPLICA: + INVOKE_OP(H_getreplica); + + case PROTOCOL_BINARY_CMD_UNLOCK_KEY: + INVOKE_OP(H_unlock); + + case PROTOCOL_BINARY_CMD_DELETE: + INVOKE_OP(H_delete); + + case PROTOCOL_BINARY_CMD_TOUCH: + INVOKE_OP(H_touch); + + case PROTOCOL_BINARY_CMD_OBSERVE_SEQNO: + INVOKE_OP(H_observe_seqno); + + case PROTOCOL_BINARY_CMD_STAT: + INVOKE_OP(H_stats); + + case PROTOCOL_BINARY_CMD_FLUSH: + INVOKE_OP(H_flush); + + case PROTOCOL_BINARY_CMD_VERSION: + INVOKE_OP(H_version); + + case PROTOCOL_BINARY_CMD_VERBOSITY: + INVOKE_OP(H_verbosity); + +#if 0 + case PROTOCOL_BINARY_CMD_NOOP: + INVOKE_OP(H_noop); +#endif + + case PROTOCOL_BINARY_CMD_GET_CLUSTER_CONFIG: + INVOKE_OP(H_config); + + default: + fprintf(stderr, "COUCHBASE: Received unknown opcode=0x%x\n", res->opcode()); + return -1; + } +} + +const lcb_MUTATION_TOKEN * +lcb_resp_get_mutation_token(int cbtype, const lcb_RESPBASE *rb) +{ + const lcb_MUTATION_TOKEN *ss = NULL; + if ((rb->rflags & LCB_RESP_F_EXTDATA) == 0) { + return NULL; + } + + switch (cbtype) { + case LCB_CALLBACK_STORE: + ss = ResponsePack::get_mt(rb); + break; + + case LCB_CALLBACK_COUNTER: + ss = ResponsePack::get_mt(rb); + break; + + case LCB_CALLBACK_REMOVE: + ss = ResponsePack::get_mt(rb); + break; + + case LCB_CALLBACK_SDMUTATE: + ss = ResponsePack::get_mt(rb); + break; + + default: + return NULL; + } + + if (ss->uuid_ == 0 && ss->seqno_ == 0) { + return NULL; + } + return ss; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/hashtable.c b/couchbase-sys/libcouchbase-2.7.0/src/hashtable.c new file mode 100644 index 00000000..81577fb8 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/hashtable.c @@ -0,0 +1,75 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" +#include "contrib/genhash/genhash.h" + +/** + * Structures for common hash table operations + */ + +static int hasheq(const void *a, lcb_size_t n_a, const void *b, lcb_size_t n_b) +{ + if (n_a != n_b) { + return 0; + } + + return memcmp(a, b, n_a) == 0; +} +/** + * Structure for a no-copy hash table + */ +static struct lcb_hash_ops hashops_nocopy = { + genhash_string_hash, /* hashfunc */ + hasheq, /* hasheq */ + NULL, /* dupKey */ + NULL, /* dupValue */ + NULL, /* freeKey */ + NULL, /* freeValue */ +}; + +genhash_t *lcb_hashtable_nc_new(lcb_size_t est) +{ + return genhash_init(est, hashops_nocopy); +} + + +static int u32_hash(const void *p, lcb_size_t n) +{ + (void)p; + return n; +} + +static int u32_eq(const void *a, lcb_size_t n_a, const void *b, lcb_size_t n_b) +{ + (void)a; (void)b; + return n_a == n_b; +} + +static struct lcb_hash_ops hashops_u32 = { + u32_hash, + u32_eq, + NULL, + NULL, + NULL, + NULL +}; + +genhash_t *lcb_hashtable_szt_new(lcb_size_t est) +{ + return genhash_init(est, hashops_u32); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/hdr_timings.c b/couchbase-sys/libcouchbase-2.7.0/src/hdr_timings.c new file mode 100644 index 00000000..dbe58275 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/hdr_timings.c @@ -0,0 +1,92 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2016 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* High dynamic range timings, using HdrHistogram + * (http://hdrhistogram.github.io/HdrHistogram/), specifically the + * C implementation (https://github.com/HdrHistogram/HdrHistogram_c) + * + * hdr_timings.c is a rrop-in replacment for timings.c - you only want one of + * the two files. + */ + +#include "internal.h" + +#include "hdr_histogram.h" + +struct lcb_histogram_st { + struct hdr_histogram* hdr_histogram; +}; + +LCB_INTERNAL_API +lcb_HISTOGRAM * +lcb_histogram_create(void) +{ + lcb_HISTOGRAM* histo = calloc(1, sizeof(struct lcb_histogram_st)); + + if (histo != NULL) { + hdr_init(/* minimum - 1 ns*/ 1, + /* maximum - 30 s*/ 30e9, + /* s ignificant figures */3, + &histo->hdr_histogram); + } + + return histo; +} + +LCB_INTERNAL_API +void +lcb_histogram_destroy(lcb_HISTOGRAM *hg) +{ + free(hg->hdr_histogram); + free(hg); +} + +LCB_INTERNAL_API +void +lcb_histogram_read(const lcb_HISTOGRAM *hg, + const void *cookie, lcb_HISTOGRAM_CALLBACK callback) +{ + struct hdr_iter iter; + hdr_iter_recorded_init(&iter, hg->hdr_histogram); + + while (hdr_iter_next(&iter)) { + callback(cookie, LCB_TIMEUNIT_NSEC, + iter.value_iterated_from, iter.value_iterated_to, + iter.count, + hdr_max(hg->hdr_histogram)); + + } +} + +LCB_INTERNAL_API +lcb_error_t lcb_histogram_print(lcb_HISTOGRAM* hg, FILE* stream) { + hdr_percentiles_print( + hg->hdr_histogram, + stream, + 5, // Granularity of printed values + 1.0, // Multiplier for results + CLASSIC); // Format CLASSIC/CSV supported. + + return LCB_SUCCESS; +} + +LCB_INTERNAL_API +void +lcb_histogram_record(lcb_HISTOGRAM *hg, lcb_U64 delta) +{ + hdr_record_value(hg->hdr_histogram, delta); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/hostlist.cc b/couchbase-sys/libcouchbase-2.7.0/src/hostlist.cc new file mode 100644 index 00000000..f3f3906c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/hostlist.cc @@ -0,0 +1,301 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hostlist.h" +#include "simplestring.h" +#include +#include +#include +#include +#include + +using namespace lcb; + + +Hostlist::~Hostlist() +{ + reset_strlist(); +} + +void +Hostlist::reset_strlist() +{ + for (size_t ii = 0; ii < hoststrs.size(); ++ii) { + if (hoststrs[ii] != NULL) { + delete[] hoststrs[ii]; + } + } + hoststrs.clear(); +} + +lcb_error_t +lcb_host_parse(lcb_host_t *host, const char *spec, int speclen, int deflport) +{ + std::vector zspec; + char *host_s; + char *port_s; + char *delim; + + /** Parse the host properly */ + if (speclen < 0) { + speclen = strlen(spec); + } else if (speclen == 0) { + return LCB_INVALID_HOST_FORMAT; + } + + if (deflport < 1) { + return LCB_INVALID_HOST_FORMAT; + } + + zspec.assign(spec, spec + speclen); + zspec.push_back('\0'); + host_s = &zspec[0]; + + if (*host_s == ':') { + return LCB_INVALID_HOST_FORMAT; + } + + if ( (delim = strstr(host_s, "://"))) { + host_s = delim + 3; + } + + if ((delim = strstr(host_s, "/"))) { + *delim = '\0'; + } + + port_s = strstr(host_s, ":"); + if (port_s != NULL) { + char *endp; + long ll; + + *port_s = '\0'; + port_s++; + + if (! *port_s) { + return LCB_INVALID_HOST_FORMAT; + } + + ll = strtol(port_s, &endp, 10); + if (ll == LONG_MIN || ll == LONG_MAX) { + return LCB_INVALID_HOST_FORMAT; + } + + if (*endp) { + return LCB_INVALID_HOST_FORMAT; + } + + } else { + port_s = const_cast(""); + } + + + if (strlen(host_s) > sizeof(host->host)-1 || + strlen(port_s) > sizeof(host->port)-1 || + *host_s == '\0') { + return LCB_INVALID_HOST_FORMAT; + } + + size_t ii, hostlen = strlen(host_s); + for (ii = 0; ii < hostlen; ii++) { + if (isalnum(host_s[ii])) { + continue; + } + switch (host_s[ii]) { + case '.': + case '-': + case '_': + break; + default: + return LCB_INVALID_HOST_FORMAT; + } + } + + strcpy(host->host, host_s); + if (*port_s) { + strcpy(host->port, port_s); + } else { + sprintf(host->port, "%d", deflport); + } + + return LCB_SUCCESS; +} + +int lcb_host_equals(const lcb_host_t *a, const lcb_host_t *b) +{ + return strcmp(a->host, b->host) == 0 && strcmp(a->port, b->port) == 0; +} + +bool +Hostlist::exists(const lcb_host_t& host) const +{ + for (size_t ii = 0; ii < hosts.size(); ++ii) { + if (lcb_host_equals(&host, &hosts[ii])) { + return true; + } + } + return false; +} + +bool +Hostlist::exists(const char *s) const +{ + lcb_host_t tmp; + if (lcb_host_parse(&tmp, s, -1, 1) != LCB_SUCCESS) { + return false; + } + return exists(tmp); +} + +void +Hostlist::add(const lcb_host_t& host) +{ + if (exists(host)) { + return; + } + hosts.push_back(host); + reset_strlist(); +} + +lcb_error_t +Hostlist::add(const char *hostport, long len, int deflport) +{ + lcb_error_t err = LCB_SUCCESS; + + if (len < 0) { + len = strlen(hostport); + } + + std::string ss(hostport, len); + if (ss.empty()) { + return LCB_SUCCESS; + } + if (ss[ss.length()-1] != ';') { + ss += ';'; + } + + const char *curstart = ss.c_str(); + const char *delim; + while ( (delim = strstr(curstart, ";"))) { + lcb_host_t curhost; + size_t curlen; + + if (delim == curstart) { + curstart++; + continue; + } + + /** { 'f', 'o', 'o', ';' } */ + curlen = delim - curstart; + + err = lcb_host_parse(&curhost, curstart, curlen, deflport); + if (err != LCB_SUCCESS) { + return err; + } + add(curhost); + curstart = delim + 1; + } + return LCB_SUCCESS; +} + +lcb_host_t * +Hostlist::next(bool wrap) +{ + lcb_host_t *ret; + if (empty()) { + return NULL; + } + if (ix == size()) { + if (wrap) { + ix = 0; + } else { + return NULL; + } + } + + ret = &hosts[ix]; + ix++; + return ret; +} + +void +Hostlist::randomize() +{ + std::random_shuffle(hosts.begin(), hosts.end()); + reset_strlist(); +} + +void Hostlist::ensure_strlist() { + if (hoststrs.size()) { + return; + } + for (size_t ii = 0; ii < hosts.size(); ii++) { + const lcb_host_t& host = hosts[ii]; + std::string ss(host.host); + ss.append(":").append(host.port); + char *newstr = new char[ss.size() + 1]; + newstr[ss.size()] = '\0'; + memcpy(newstr, ss.c_str(), ss.size()); + hoststrs.push_back(newstr); + } + hoststrs.push_back(NULL); +} + +Hostlist& +Hostlist::assign(const Hostlist& src) +{ + clear(); + reset_strlist(); + for (size_t ii = 0; ii < src.size(); ++ii) { + add(src.hosts[ii]); + } + return *this; +} + +extern "C" { +Hostlist* hostlist_create(void) { return new Hostlist(); } +void hostlist_destroy(hostlist_t l) { delete l; } +void hostlist_clear(hostlist_t l) { l->clear(); } +void hostlist_reset_strlist(hostlist_t l) { l->reset_strlist(); } +lcb_error_t hostlist_add_host(hostlist_t l, const lcb_host_t *h) { l->add(*h); return LCB_SUCCESS; } +lcb_host_t *hostlist_shift_next(hostlist_t hl, int wrap) { return hl->next(wrap); } +int hostlist_finished(hostlist_t l) { return l->ix == l->hosts.size(); } +size_t hostlist_size(hostlist_t l) { return l->size(); } +void hostlist_randomize(hostlist_t l) { l->randomize(); } + +lcb_error_t +hostlist_add_string(hostlist_t hl, const char *spec, int len, int deflport) { + return hl->add(spec, len, deflport); +} + +void +hostlist_assign(hostlist_t dst, const hostlist_t src) { + dst->assign(*src); +} + +const lcb_host_t* +hostlist_get(const hostlist_t h, size_t ix) { return &h->hosts[ix]; } + +const char * const * +hostlist_strents(const hostlist_t h) { + h->ensure_strlist(); + if (h->hoststrs.size()) { + return &h->hoststrs[0]; + } else { + return NULL; + } +} +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/hostlist.h b/couchbase-sys/libcouchbase-2.7.0/src/hostlist.h new file mode 100644 index 00000000..9048210d --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/hostlist.h @@ -0,0 +1,171 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_HOSTLIST_H +#define LCB_HOSTLIST_H + +#include "config.h" +#include + +/** + * Structure representing a host. This contains the string representation + * of a host and a port. + */ +typedef struct lcb_host_st { + char host[NI_MAXHOST + 1]; + char port[NI_MAXSERV + 1]; +} lcb_host_t; + +/** + * Structure representing a list of hosts. This has an internal iteration + * index which is used to cycle between 'good' and 'bad' hosts. + */ +#ifndef __cplusplus +struct hostlist_st; +typedef struct hostlist_st *hostlist_t; +#else +#include + +namespace lcb { +struct Hostlist { + Hostlist() : ix(0) {} + ~Hostlist(); + + void add(const lcb_host_t&); + lcb_error_t add(const char *s, long len, int deflport); + lcb_error_t add(const char *s, int deflport) { return add(s, -1, deflport); } + bool exists(const lcb_host_t&) const; + bool exists(const char *hostport) const; + lcb_host_t *next(bool wrap); + bool finished() const; + + size_t size() const { return hosts.size(); } + bool empty() const { return hosts.empty(); } + Hostlist& assign(const Hostlist& other); + void clear() { hosts.clear(); reset_strlist(); ix = 0; } + void randomize(); + void ensure_strlist(); + void reset_strlist(); + unsigned int ix; + const lcb_host_t& operator[](size_t ix_) const { return hosts[ix_]; } + + std::vector hosts; + std::vector hoststrs; +}; +} +typedef lcb::Hostlist* hostlist_t; +#define hostlist_st lcb::Hostlist +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Creates a new hostlist. Returns NULL on allocation failure + */ +hostlist_t hostlist_create(void); + +/** + * Frees resources associated with the hostlist + */ +void hostlist_destroy(hostlist_t hostlist); + +/** + * Clears the hostlist + */ +void hostlist_clear(hostlist_t hostlist); + +/** + * Parses a string into a hostlist + * @param host the target host to populate + * @param spec a string to parse. This may either be an IP/host or an + * IP:Port pair. + * @param speclen the length of the string. If this is -1 then it is assumed + * to be NUL-terminated and strlen will be used + * @param deflport If a port is not found in the spec, then this port will + * be used + * + * @return LCB_EINVAL if the host format is invalid + */ +lcb_error_t +lcb_host_parse(lcb_host_t *host, const char *spec, int speclen, int deflport); + +/** Wrapper around lcb_host_parse() which accepts a NUL-terminated string + * @param host the host to populate + * @param spec a NUL-terminated string to parse + * @param deflport the default port to use if the `spec` does not contain a port + * @see lcb_host_parse() + */ +#define lcb_host_parsez(host, spec, deflport) lcb_host_parse(host, spec, -1, deflport) + +/** + * Compares two hosts for equality. + * @param a host to compare + * @param b other host to compare + * @return true if equal, false if different. + */ +int lcb_host_equals(const lcb_host_t *a, const lcb_host_t *b); + +/** + * Adds a string to the hostlist. See lcb_host_parse for details. + * Note that if the host already exists (see 'lcb_host_equals') it will + * not be added + * @return LCB_EINVAL if the host string is not value, LCB_CLIENT_ENOMEM on + * allocation failure. + */ +lcb_error_t hostlist_add_string(hostlist_t hostlist, + const char *spec, + int speclen, + int deflport); + +#define hostlist_add_stringz(hostlist, spec, deflport) \ + hostlist_add_string(hostlist, spec, -1, deflport) + +lcb_error_t hostlist_add_host(hostlist_t hostlist, const lcb_host_t *host); + +/** + * Return the next host in the list. + * @param hostlist the hostlist to use + * @param rollover If the internal iterator has reached its limit, this + * indicates whether it should be reset, or if it should return NULL + * @return a new host if available, or NULL if the list is empty or the + * iterator is finished. + */ +lcb_host_t *hostlist_shift_next(hostlist_t hostlist, int rollover); + +/** + * Randomize the hostlist + */ +void hostlist_randomize(hostlist_t hostlist); + +/** + * String list handling functions. These are used to return the hostlist via + * the API where we return a char*[] terminated by a NULL pointer. + */ +void hostlist_reset_strlist(hostlist_t hostlist); + +/** Whether the internal iterator has finished. */ +int hostlist_finished(hostlist_t); +size_t hostlist_size(const hostlist_t hl); +void hostlist_assign(hostlist_t dst, const hostlist_t src); +const lcb_host_t *hostlist_get(const hostlist_t, size_t); +const char * const *hostlist_strents(const hostlist_t hl); +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/http/http-priv.h b/couchbase-sys/libcouchbase-2.7.0/src/http/http-priv.h new file mode 100644 index 00000000..2f65def2 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/http/http-priv.h @@ -0,0 +1,307 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2015 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_HTTPPRIV_H +#define LCB_HTTPPRIV_H + +#include +#include +#include +#include +#include "contrib/http_parser/http_parser.h" +#include "http.h" +#include +#include +#include + +namespace lcb { +namespace http { + +// Simple object for header key and value +struct Header { + std::string key; + std::string value; + Header(const std::string& key_, const std::string& value_) + : key(key_), value(value_) { + } +}; + +struct Request { + enum State { + /** + * The request is still ongoing. Callbacks are still active. + * Note that this essentially means the absence of any flags :) + */ + ONGOING = 0, + + /** + * This flag is set when the on_complete callback has been invoked. This + * is used as a marker to prevent us from calling that callback more than + * once per request + */ + CBINVOKED = 1 << 0, + + /** + * This flag is set by lcb_http_request_finish, and indicates that the + * request is no longer active per se. This means that while the request + * may still be valid in memory, it is simply waiting for any pending I/O + * operations to close, so the reference count can hit zero. + */ + FINISHED = 1 << 1, + + /** + * Internal flag used to indicate that finish() should not not attempt + * to modify any instance-level globals. This is currently used + * from within lcb_destroy() + */ + NOLCB = 1 << 2 + }; + + lcb_t instance; /**< Library handle */ + std::string url; /** body; /**< Input body (for POST/PUT) */ + + /** Request buffer (excluding body). Reassembled from inputs */ + std::vector preamble; + + struct http_parser_url url_info; /**< Parser info for the URL */ + const lcb_http_method_t method; /**< Request method constant */ + const bool chunked; /**< Whether to invoke callback for each data chunk */ + bool paused; /**< See pause() and resume() */ + const void * const command_cookie; /** User context for callback */ + size_t refcount; /** Initialized to 1. See incref() and decref() */ + int redircount; /** Times this request was redirected */ + + /** + * Whether this request has delivered data to the user. This is relevant + * in cases where a retry is requested. If any data has been passed at + * all, we cannot retry the request. + */ + bool passed_data; + + /** Sparse map indicating which nodes the request was already sent to */ + std::vector used_nodes; + + /** + * Last revision ID of vBucket config. If the current revision does not + * match this number, the ::used_nodes field is cleared + */ + int last_vbcrev; + + const lcb_http_type_t reqtype; /**< HTTP API type */ + int status; /**< OR'd flags of ::State */ + std::vector
    request_headers; /**< List of request headers */ + + /** + * Response headers for callback (array of char*). Buffers are mapped to + * ::response_headers + */ + std::vector response_headers_clist; + + /** Backing buffers for response headers */ + std::vector
    response_headers; + + /** Callback to invoke */ + lcb_RESPCALLBACK callback; + + // IO variables + lcbio_pTABLE io; + lcbio_pCTX ioctx; + lcbio_pTIMER timer; + lcbio_CONNREQ creq; + + /** HTTP Protocol parser */ + lcbht_pPARSER parser; + + /** overrides default timeout if nonzero */ + const uint32_t user_timeout; + + /** + * @return The effective timeout. This is either the user timeout or the + * default timeout for the API type + */ + uint32_t timeout() const; + bool is_data_request() const { + return reqtype == LCB_HTTP_TYPE_N1QL || + reqtype == LCB_HTTP_TYPE_VIEW || + reqtype == LCB_HTTP_TYPE_FTS; + } + + /** + * @return If this request is in an ONGOING state, meaning no I/O errors + * and is not finished. + */ + bool is_ongoing() const { return status == ONGOING; } + + /** + * Initializes the request. This simply copies the relevant fields from the + * body and initializes instance members to their default values. The static + * ::create() method should be used instead to construct a new object + */ + inline Request(lcb_t instance, const void *cookie, const lcb_CMDHTTP* cmd); + + /** + * Sets up inputs from the command. This should really be in the + * constructor, however we also need a return value. The ::create() + * static method creates a new object and calls this function to verify + * the inputs + */ + inline lcb_error_t setup_inputs(const lcb_CMDHTTP* cmd); + + /** + * Gets a target node for the destination API in the form of host:port. + * + * This function will also inspect previously used nodes and skip over ones + * which are problematic, in the case of retries. + * + * @param[out] rc error code + * @return string if there is a node, or NULL if there is an error. Check rc + * for error details + */ + const char *get_api_node(lcb_error_t &rc); + const char *get_api_node() { lcb_error_t dummy; return get_api_node(dummy); } + + /** Creates a new request object and verifies the input (setup_inputs()) */ + static Request * create(lcb_t instance, const void *cookie, + const lcb_CMDHTTP *cmd, lcb_error_t *rc); + + /** + * Sets the URL for the field + * @param base The URL base (i.e. http://foo.com) + * @param nbase Length of the base field + * @param rest User input (the path) + * @param nrest Length of the path + * @return LCB_SUCCESS on success, error if failure + * + * If all parameters are 0 then this function will use the already-contained + * ::url field and validate/parse the inputs + */ + inline lcb_error_t assign_url(const char *base, size_t nbase, + const char *rest, size_t nrest); + + /** + * Helper method to get a URL field + * @param field The field constant (UF_XXX) + * @param target The string to contain the result + */ + inline void assign_from_urlfield(http_parser_url_fields field, + std::string& target); + + /** + * Add a new header to the list of request headers + * @param key header key + * @param value header value + */ + void add_header(const std::string& key, const std::string& value) { + request_headers.push_back(Header(key, value)); + } + + // Helper methods to populate request buffer + inline void add_to_preamble(const char *); + inline void add_to_preamble(const std::string&); + inline void add_to_preamble(const Header&); + + /** + * Start all operations required to make this request. This should be + * called once all inputs have been completed. If successful, I/O operations + * will begin (this function calls start_io()) + */ + lcb_error_t submit(); + + /** + * Starts the IO on the current request. This really belongs in submit(), + * but submit() handles building the request while start_io() sets up + * the actual connection + */ + lcb_error_t start_io(lcb_host_t&); + + /** + * Release any I/O resources attached to this request. + */ + void close_io(); + + // Helper functions for parsing response data from network + inline int handle_parse_chunked(const char *buf, unsigned nbuf); + inline void assign_response_headers(const lcbht_RESPONSE*); + + /** + * Called when a redirect has happened. pending_redirect must not be empty. + * This will transfer control to the redirect call. If there is an error + * during redirect, the appropriate finish() API will be called by this + * function. + * + * Note that the new URL should be present in the ::pending_redirect + * field + */ + void redirect(); + + /** + * Called to initialize a response object. Sets up standard boilerplate + * variables for the response + */ + void init_resp(lcb_RESPHTTP *resp); + + /** Called by finish() to refresh the config, if necessary */ + void maybe_refresh_config(lcb_error_t rc); + + /** Finish this request, invoking the final callback if necessary */ + void finish(lcb_error_t rc); + + /** + * Finish or retry this request, depending on the state of the response + * If the request can be retried, IO will begin again, otherwise it will + * be finished via a call to finish() + **/ + void finish_or_retry(lcb_error_t rc); + + /** Pause IO on this request */ + void pause(); + + /** Resume previously paused IO */ + void resume(); + + /** Cancel and finish this request, suppressing callbacks */ + void cancel(); + + /** + * Let the request finish its normal course, suppressing any callbacks. + * Unlike cancel(), this does not dispatch to finish. Finish is called + * when any final I/O has been completed, which may happen after the + * instance is already destroyed + */ + void block_callback() { + status |= NOLCB|CBINVOKED; + } + + /** Decrement refcount. The object is destroyed when the refcount hits 0 */ + void decref(); + + /** Increment refcount */ + void incref() { refcount++; } +}; + +} // namespace: http +} // namespace: lcb + +struct lcb_http_request_st : public lcb::http::Request {}; + +#endif /* HEADER GUARD */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/http/http.cc b/couchbase-sys/libcouchbase-2.7.0/src/http/http.cc new file mode 100644 index 00000000..6c5685af --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/http/http.cc @@ -0,0 +1,638 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012-2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" +#include "bucketconfig/clconfig.h" +#include "http/http.h" +#include "http/http-priv.h" +using namespace lcb::http; + +#define LOGFMT "<%s:%s> " +#define LOGID(req) (req)->host.c_str(), (req)->port.c_str() +#define LOGARGS(req, lvl) req->instance->settings, "http-io", LCB_LOG_##lvl, __FILE__, __LINE__ + +static const char *method_strings[] = { + "GET ", /* LCB_HTTP_METHOD_GET */ + "POST ", /* LCB_HTTP_METHOD_POST */ + "PUT ", /* LCB_HTTP_METHOD_PUT */ + "DELETE " /* LCB_HTTP_METHOD_DELETE */ +}; + +void +Request::decref() +{ + assert(refcount > 0); + if (--refcount) { + return; + } + + close_io(); + + if (parser) { + lcbht_free(parser); + } + + if (timer) { + lcbio_timer_destroy(timer); + timer = NULL; + } + + delete this; +} + +void +Request::finish_or_retry(lcb_error_t rc) +{ + if (rc == LCB_ETIMEDOUT) { + // No point on trying (or even logging) a timeout + finish(rc); + return; + } + if (passed_data) { + lcb_log(LOGARGS(this, WARN), LOGFMT "Not retrying. Data passed to callback", LOGID(this)); + finish(rc); + return; + } + + // Not a 'data API'. Request may be node-specific + if (!is_data_request()) { + lcb_log(LOGARGS(this, WARN), LOGFMT "Not retrying non-data-api request", LOGID(this)); + finish(rc); + return; + } + + // See if we can find an API node. + const char *nextnode = get_api_node(); + if (!nextnode) { + lcb_log(LOGARGS(this, WARN), LOGFMT "Not retrying. No nodes available", LOGID(this)); + finish(rc); + return; + } + + // Reassemble URL: + + // get offset and length of host bits + size_t host_begin = url_info.field_data[UF_HOST].off; + size_t hplen = + url_info.field_data[UF_HOST].len + + url_info.field_data[UF_PORT].len + 1; // +1 for ":" + + url.replace(host_begin, hplen, nextnode); + lcb_error_t newrc = submit(); + if (newrc != LCB_SUCCESS) { + lcb_log(LOGARGS(this, WARN), LOGFMT "Retry failed!", LOGID(this)); + finish(rc); + } +} + +void +Request::maybe_refresh_config(lcb_error_t err) +{ + int htstatus_ok; + lcbht_RESPONSE *resp; + if (!parser) { + return; + } + + if (!LCBT_SETTING(instance, refresh_on_hterr)) { + return; + } + + resp = lcbht_get_response(parser); + htstatus_ok = resp->status >= 200 && resp->status < 299; + + if (err != LCB_SUCCESS && (err == LCB_ESOCKSHUTDOWN && htstatus_ok) == 0) { + /* ignore graceful close */ + lcb_bootstrap_common(instance, LCB_BS_REFRESH_ALWAYS); + return; + } + + if (htstatus_ok) { + return; + } + lcb_bootstrap_common(instance, LCB_BS_REFRESH_ALWAYS); +} + +void +Request::init_resp(lcb_RESPHTTP *res) +{ + const lcbht_RESPONSE *htres = lcbht_get_response(parser); + + res->cookie = const_cast(command_cookie); + res->key = url.c_str() + url_info.field_data[UF_PATH].off; + res->nkey = url_info.field_data[UF_PATH].len; + res->_htreq = static_cast(this); + if (!response_headers.empty()) { + res->headers = &response_headers_clist[0]; + } + if (htres) { + res->htstatus = htres->status; + } +} + +void +Request::finish(lcb_error_t error) +{ + /* This is always safe to execute */ + if (!(status & NOLCB)) { + maybe_refresh_config(error); + } + + /* And this one too */ + if ((status & CBINVOKED) == 0) { + lcb_RESPHTTP resp = { 0 }; + init_resp(&resp); + resp.rflags = LCB_RESP_F_FINAL; + resp.rc = error; + + status |= CBINVOKED; + callback(instance, LCB_CALLBACK_HTTP, (lcb_RESPBASE*)&resp); + } + + if (status & FINISHED) { + return; + } + + status |= FINISHED; + + if (!(status & NOLCB)) { + /* Remove from wait queue */ + lcb_aspend_del(&instance->pendops, LCB_PENDTYPE_HTTP, this); + /* Break out from the loop (must be called after aspend_del) */ + lcb_maybe_breakout(instance); + } + + /* Cancel the timeout */ + lcbio_timer_disarm(timer); + + /* Remove the initial refcount=1 (set from lcb_http3). Typically this will + * also free the request (though this is dependent on pending I/O operations) */ + decref(); +} + +void Request::add_to_preamble(const char *s) { + preamble.insert(preamble.end(), s, s + strlen(s)); +} +void Request::add_to_preamble(const std::string& s) { + preamble.insert(preamble.end(), s.c_str(), s.c_str() + s.size()); +} +void Request::add_to_preamble(const Header& header) { + add_to_preamble(header.key); + add_to_preamble(": "); + add_to_preamble(header.value); + add_to_preamble("\r\n"); +} + +lcb_error_t +Request::submit() +{ + lcb_error_t rc; + lcb_host_t reqhost; + + // Stop any pending socket/request + close_io(); + + if (host.size() > sizeof reqhost.host || port.size() > sizeof reqhost.port) { + decref(); + return LCB_E2BIG; + } + + preamble.clear(); + + strncpy(reqhost.host, host.c_str(), host.size()); + strncpy(reqhost.port, port.c_str(), port.size()); + reqhost.host[host.size()] = '\0'; + reqhost.port[port.size()] = '\0'; + + // Add the HTTP verb (e.g. "GET ") [note, the string contains a trailing space] + add_to_preamble(method_strings[method]); + + // Add the path + const char *url_s = url.c_str(); + size_t path_off = url_info.field_data[UF_PATH].off; + size_t path_len = url.size() - path_off; + preamble.insert(preamble.end(), + url_s + path_off, url_s + path_off + path_len); + lcb_log(LOGARGS(this, TRACE), LOGFMT "%s %s. Body=%lu bytes", LOGID(this), method_strings[method], url.c_str(), body.size()); + + add_to_preamble(" HTTP/1.1\r\n"); + + // Add the Host: header manually. If redirected to a different host then + // we need to recalculate this, so don't make this part of the + // global headers (which are typically not cleared) + add_to_preamble("Host: "); + add_to_preamble(host); + add_to_preamble(":"); + add_to_preamble(port); + add_to_preamble("\r\n"); + + // Add the rest of the headers + std::vector
    ::const_iterator ii = request_headers.begin(); + for (; ii != request_headers.end(); ++ii) { + add_to_preamble(*ii); + } + add_to_preamble("\r\n"); + // If there is a body, it is appended in the IO stage + + rc = start_io(reqhost); + + if (rc == LCB_SUCCESS) { + // Only wipe old parser/response information if current I/O request + // was a success + if (parser) { + lcbht_reset(parser); + } else { + parser = lcbht_new(instance->settings); + } + response_headers.clear(); + response_headers_clist.clear(); + } + + return rc; +} + +void +Request::assign_from_urlfield(http_parser_url_fields field, std::string &target) +{ + target = url.substr(url_info.field_data[field].off, + url_info.field_data[field].len); +} + +lcb_error_t +Request::assign_url(const char *base, size_t nbase, const char *path, size_t npath) +{ + const char *htscheme; + unsigned schemsize; + + if (LCBT_SETTING(instance, sslopts) & LCB_SSL_ENABLED) { + htscheme = "https://"; + schemsize = sizeof("https://"); + } else { + htscheme = "http://"; + schemsize = sizeof("http://"); + } + + schemsize--; + if (base) { + url.assign(htscheme, schemsize); + if (nbase > schemsize && memcmp(base, htscheme, schemsize) == 0) { + base += schemsize; + nbase -= schemsize; + } + url.append(base, nbase); + if (path) { + if (*path != '/' && url[url.size()-1] != '/') { + url.append("/"); + } + + if (!lcb::strcodecs::urlencode(path, path + npath, url)) { + return LCB_INVALID_CHAR; + } + } + } + + + bool redir_checked = false; + static const unsigned required_fields = + ((1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)); + + GT_REPARSE: + if (_lcb_http_parser_parse_url(url.c_str(), url.size(), 0, &url_info)) { + return LCB_EINVAL; + } + + if ((url_info.field_set & required_fields) != required_fields) { + if (base == NULL && path == NULL && !redir_checked) { + redir_checked = true; + std::string first_part(htscheme, schemsize); + first_part += host; + first_part += ':'; + first_part += port; + url = first_part + url; + goto GT_REPARSE; + } + return LCB_EINVAL; + } + + assign_from_urlfield(UF_HOST, host); + assign_from_urlfield(UF_PORT, port); + return LCB_SUCCESS; +} + +void +Request::redirect() +{ + lcb_error_t rc; + assert(!pending_redirect.empty()); + if (LCBT_SETTING(instance, max_redir) > -1) { + if (LCBT_SETTING(instance, max_redir) < ++redircount) { + finish(LCB_TOO_MANY_REDIRECTS); + return; + } + } + + memset(&url_info, 0, sizeof url_info); + url = pending_redirect; + pending_redirect.clear(); + + if ((rc = assign_url(NULL, 0, NULL, 0)) != LCB_SUCCESS) { + lcb_log(LOGARGS(this, ERR), LOGFMT "Failed to add redirect URL (%s)", LOGID(this), url.c_str()); + finish(rc); + return; + } + + if ((rc = submit()) != LCB_SUCCESS) { + finish(rc); + } +} + +static lcbvb_SVCTYPE +httype2svctype(unsigned httype) +{ + switch (httype) { + case LCB_HTTP_TYPE_VIEW: + return LCBVB_SVCTYPE_VIEWS; + case LCB_HTTP_TYPE_N1QL: + return LCBVB_SVCTYPE_N1QL; + case LCB_HTTP_TYPE_FTS: + return LCBVB_SVCTYPE_FTS; + default: + return LCBVB_SVCTYPE__MAX; + } +} + +const char * +Request::get_api_node(lcb_error_t &rc) +{ + if (!is_data_request()) { + return lcb_get_node(instance, LCB_NODE_HTCONFIG, 0); + } + + if (!LCBT_VBCONFIG(instance)) { + rc = LCB_CLIENT_ETMPFAIL; + return NULL; + } + + const lcbvb_SVCTYPE svc = httype2svctype(reqtype); + const lcbvb_SVCMODE mode = LCBT_SETTING(instance, sslopts) ? + LCBVB_SVCMODE_SSL : LCBVB_SVCMODE_PLAIN; + + lcbvb_CONFIG *vbc = LCBT_VBCONFIG(instance); + + if (last_vbcrev != vbc->revid) { + used_nodes.clear(); + last_vbcrev = vbc->revid; + } + used_nodes.resize(LCBVB_NSERVERS(vbc)); + + int ix = lcbvb_get_randhost_ex(vbc, svc, mode, &used_nodes[0]); + if (ix < 0) { + rc = LCB_NOT_SUPPORTED; + return NULL; + } + used_nodes[ix] = 1; + return lcbvb_get_resturl(vbc, ix, svc, mode); +} + +lcb_error_t +Request::setup_inputs(const lcb_CMDHTTP *cmd) +{ + const char *base = NULL, *username, *password; + size_t nbase = 0; + lcb_error_t rc; + + if (method > LCB_HTTP_METHOD_MAX) { + return LCB_EINVAL; + } + + username = cmd->username; + password = cmd->password; + + if (reqtype == LCB_HTTP_TYPE_RAW) { + if ((base = cmd->host) == NULL) { + return LCB_EINVAL; + } + } else { + if (cmd->host) { + return LCB_EINVAL; + } + if (cmd->cmdflags & LCB_CMDHTTP_F_NOUPASS) { + username = password = NULL; + } else if (username == NULL && password == NULL) { + if (reqtype == LCB_HTTP_TYPE_MANAGEMENT) { + lcbauth_get_upass(LCBT_SETTING(instance, auth), &username, &password); + } else { + username = LCBT_SETTING(instance, bucket); + password = lcbauth_get_bpass(LCBT_SETTING(instance, auth), username); + } + } + + base = get_api_node(rc); + if (base == NULL || *base == '\0') { + if (rc == LCB_SUCCESS) { + return LCB_EINTERNAL; + } else { + return rc; + } + } + } + + if (base) { + nbase = strlen(base); + } + + rc = assign_url(base, nbase, + reinterpret_cast(cmd->key.contig.bytes), + cmd->key.contig.nbytes); + if (rc != LCB_SUCCESS) { + return rc; + } + + add_header("User-Agent", "libcouchbase/" LCB_VERSION_STRING); + if (instance->http_sockpool->maxidle == 0 || !is_data_request()) { + add_header("Connection", "close"); + } + + add_header("Accept", "application/json"); + if (password && username) { + char auth[256]; + std::string upassbuf; + upassbuf.append(username).append(":").append(password); + if (lcb_base64_encode(upassbuf.c_str(), auth, sizeof(auth)) == -1) { + return LCB_EINVAL; + } + add_header("Authorization", std::string("Basic ") + auth); + } + + if (!body.empty()) { + char lenbuf[64]; + sprintf(lenbuf, "%ld", body.size()); + add_header("Content-Length", lenbuf); + if (cmd->content_type) { + add_header("Content-Type", cmd->content_type); + } + } + + return LCB_SUCCESS; +} + +Request::Request(lcb_t instance_, const void *cookie, const lcb_CMDHTTP* cmd) +: instance(instance_), + body(cmd->body, cmd->body + cmd->nbody), + method(cmd->method), + chunked(cmd->cmdflags & LCB_CMDHTTP_F_STREAM), + paused(false), + command_cookie(cookie), + refcount(1), + redircount(0), + passed_data(false), + last_vbcrev(-1), + reqtype(cmd->type), + status(ONGOING), + callback(lcb_find_callback(instance, LCB_CALLBACK_HTTP)), + io(instance->iotable), + ioctx(NULL), + timer(NULL), + parser(NULL), + user_timeout(cmd->cmdflags & LCB_CMDHTTP_F_CASTMO ? cmd->cas : 0) +{ + memset(&creq, 0, sizeof creq); +} + +uint32_t +Request::timeout() const +{ + if (user_timeout) { + return user_timeout; + } + switch (reqtype) { + case LCB_HTTP_TYPE_N1QL: + case LCB_HTTP_TYPE_FTS: + return LCBT_SETTING(instance, n1ql_timeout); + case LCB_HTTP_TYPE_VIEW: + return LCBT_SETTING(instance, views_timeout); + default: + return LCBT_SETTING(instance, http_timeout); + } +} + +Request * +Request::create(lcb_t instance, + const void *cookie, const lcb_CMDHTTP *cmd, lcb_error_t *rc) +{ + Request *req = new Request(instance, cookie, cmd); + if (!req) { + *rc = LCB_CLIENT_ENOMEM; + return NULL; + } + + *rc = req->setup_inputs(cmd); + if (*rc != LCB_SUCCESS) { + req->decref(); + return NULL; + } + + *rc = req->submit(); + if (*rc == LCB_SUCCESS) { + if (cmd->reqhandle) { + *cmd->reqhandle = static_cast(req); + } + lcb_aspend_add(&instance->pendops, LCB_PENDTYPE_HTTP, req); + return req; + } else { + // Do not call finish() as we don't want a callback + req->decref(); + return NULL; + } +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_http3(lcb_t instance, const void *cookie, const lcb_CMDHTTP *cmd) +{ + lcb_error_t rc; + Request::create(instance, cookie, cmd, &rc); + return rc; +} + +LIBCOUCHBASE_API +lcb_error_t lcb_make_http_request(lcb_t instance, + const void *cookie, lcb_http_type_t type, const lcb_http_cmd_t *cmd, + lcb_http_request_t *request) +{ + lcb_CMDHTTP htcmd = { 0 }; + lcb_error_t err; + const lcb_HTTPCMDv0 *cmdbase = &cmd->v.v0; + + LCB_CMD_SET_KEY(&htcmd, cmdbase->path, cmdbase->npath); + htcmd.type = type; + htcmd.body = reinterpret_cast(cmdbase->body); + htcmd.nbody = cmdbase->nbody; + htcmd.content_type = cmdbase->content_type; + htcmd.method = cmdbase->method; + htcmd.reqhandle = request; + + if (cmd->version == 1) { + htcmd.username = cmd->v.v1.username; + htcmd.password = cmd->v.v1.password; + htcmd.host = cmd->v.v1.host; + } + if (cmdbase->chunked) { + htcmd.cmdflags |= LCB_CMDHTTP_F_STREAM; + } + + err = lcb_http3(instance, cookie, &htcmd); + if (err == LCB_SUCCESS) { + SYNCMODE_INTERCEPT(instance); + } + return err; +} + +void +Request::cancel() +{ + if (status & (FINISHED|CBINVOKED)) { + /* Nothing to cancel */ + return; + } + status |= CBINVOKED; + finish(LCB_SUCCESS); +} + +// Wrappers +void lcb_htreq_setcb(lcb_http_request_t req, lcb_RESPCALLBACK callback) { + req->callback = callback; +} +void lcb_htreq_block_callback(lcb_http_request_t req) { + req->block_callback(); +} +void lcb_htreq_pause(lcb_http_request_t req) { + req->pause(); +} +void lcb_htreq_resume(lcb_http_request_t req) { + req->resume(); +} +void lcb_htreq_finish(lcb_t, lcb_http_request_t req, lcb_error_t rc) { + req->finish(rc); +} + +LIBCOUCHBASE_API +void +lcb_cancel_http_request(lcb_t, lcb_http_request_t req) +{ + req->cancel(); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/http/http.h b/couchbase-sys/libcouchbase-2.7.0/src/http/http.h new file mode 100644 index 00000000..c97192cb --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/http/http.h @@ -0,0 +1,34 @@ +#ifndef LCB_HTTPAPI_H +#define LCB_HTTPAPI_H + +/* This file contains the internal API for HTTP requests. This allows us to + * change the internals without exposing the object structure to the rest + * of the library + */ + +#ifdef __cplusplus +extern "C" { +#endif + +void +lcb_htreq_setcb(lcb_http_request_t, lcb_RESPCALLBACK); + +void +lcb_htreq_pause(lcb_http_request_t); + +void +lcb_htreq_resume(lcb_http_request_t); + +void +lcb_htreq_finish(lcb_t, lcb_http_request_t, lcb_error_t); + +/* Prevents the callback from being invoked. This is different than a full + * destruction of the object. This is only called in lcb_destroy() to + * prevent dereferencing the instance itself. + */ +void +lcb_htreq_block_callback(lcb_http_request_t); +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/http/http_io.cc b/couchbase-sys/libcouchbase-2.7.0/src/http/http_io.cc new file mode 100644 index 00000000..29af3249 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/http/http_io.cc @@ -0,0 +1,307 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "internal.h" +#include "logging.h" +#include "settings.h" +#include "http-priv.h" +#include "http.h" +#include "ctx-log-inl.h" +#include "sllist.h" +#include + +using namespace lcb::http; + +#define LOGFMT "<%s:%s>" +#define LOGID(req) get_ctx_host((req)->ioctx), get_ctx_port((req)->ioctx) +#define LOGARGS(req, lvl) req->instance->settings, "http-io", LCB_LOG_##lvl, __FILE__, __LINE__ + +void +Request::assign_response_headers(const lcbht_RESPONSE *resp) +{ + response_headers.clear(); + response_headers_clist.clear(); + + sllist_node *curnode; + SLLIST_ITERBASIC(&resp->headers, curnode) { + lcbht_MIMEHDR *hdr = SLLIST_ITEM(curnode, lcbht_MIMEHDR, slnode); + response_headers.push_back(Header(hdr->key, hdr->value)); + } + + std::vector
    ::const_iterator ii = response_headers.begin(); + for (; ii != response_headers.end(); ++ii) { + response_headers_clist.push_back(ii->key.c_str()); + response_headers_clist.push_back(ii->value.c_str()); + } + response_headers_clist.push_back(NULL); +} + +int +Request::handle_parse_chunked(const char *buf, unsigned nbuf) +{ + int parse_state, oldstate, diff; + lcbht_RESPONSE *res = lcbht_get_response(parser); + + do { + const char *rbody; + unsigned nused = -1, nbody = -1; + oldstate = res->state; + + parse_state = lcbht_parse_ex(parser, buf, nbuf, &nused, &nbody, &rbody); + diff = oldstate ^ parse_state; + + /* Got headers now for the first time */ + if (diff & LCBHT_S_HEADER) { + assign_response_headers(res); + if (res->status >= 300 && res->status <= 400) { + const char *redir = lcbht_get_resphdr(res, "Location"); + if (redir != NULL) { + pending_redirect.assign(redir); + return LCBHT_S_DONE; + } + } + } + + if (parse_state & LCBHT_S_ERROR) { + /* nothing to do here */ + return parse_state; + } + + if (nbody) { + if (chunked) { + lcb_RESPHTTP htresp = { 0 }; + init_resp(&htresp); + htresp.body = rbody; + htresp.nbody = nbody; + htresp.rc = LCB_SUCCESS; + passed_data = true; + callback(instance, LCB_CALLBACK_HTTP, (const lcb_RESPBASE *)&htresp); + + } else { + lcb_string_append(&res->body, rbody, nbody); + } + } + + buf += nused; + nbuf -= nused; + } while ((parse_state & LCBHT_S_DONE) == 0 && is_ongoing() && nbuf); + + if ( (parse_state & LCBHT_S_DONE) && is_ongoing()) { + lcb_RESPHTTP resp = { 0 }; + if (chunked) { + buf = NULL; + nbuf = 0; + } else { + buf = res->body.base; + nbuf = res->body.nused; + } + + init_resp(&resp); + resp.rflags = LCB_RESP_F_FINAL; + resp.rc = LCB_SUCCESS; + resp.body = buf; + resp.nbody = nbuf; + passed_data = true; + callback(instance, LCB_CALLBACK_HTTP, (const lcb_RESPBASE*)&resp); + status |= Request::CBINVOKED; + } + return parse_state; +} + +static void +io_read(lcbio_CTX *ctx, unsigned nr) +{ + Request *req = reinterpret_cast(lcbio_ctx_data(ctx)); + lcb_t instance = req->instance; + /** this variable set to 0 (in progress), -1 (error), 1 (done) */ + int rv = 0; + lcbio_CTXRDITER iter; + req->incref(); + + /** Delay the timer */ + lcbio_timer_rearm(req->timer, req->timeout()); + + LCBIO_CTX_ITERFOR(ctx, &iter, nr) { + char *buf; + unsigned nbuf; + int parse_state; + + buf = reinterpret_cast(lcbio_ctx_ribuf(&iter)); + nbuf = lcbio_ctx_risize(&iter); + parse_state = req->handle_parse_chunked(buf, nbuf); + + if ((parse_state & LCBHT_S_ERROR) || req->has_pending_redirect()) { + rv = -1; + break; + } else if (!req->is_ongoing()) { + rv = 1; + break; + } + } + + if (rv == -1) { + // parse error or redirect + lcb_error_t err; + if (req->has_pending_redirect()) { + lcb_bootstrap_common(instance, LCB_BS_REFRESH_THROTTLE); + // Transfer control to redirect function() + lcb_log(LOGARGS(req, DEBUG), LOGFMT "Attempting redirect to %s", LOGID(req), req->pending_redirect.c_str()); + req->redirect(); + } else { + err = LCB_PROTOCOL_ERROR; + lcb_log(LOGARGS(req, ERR), LOGFMT "Got parser error while parsing HTTP stream", LOGID(req)); + req->finish_or_retry(err); + } + } else if (rv == 1) { + // Complete + req->finish(LCB_SUCCESS); + } else { + // Pending + lcbio_ctx_rwant(ctx, req->paused ? 0 : 1); + lcbio_ctx_schedule(ctx); + } + + req->decref(); +} + +void +Request::pause() +{ + if (!paused) { + paused = true; + if (ioctx) { + lcbio_ctx_rwant(ioctx, 0); + lcbio_ctx_schedule(ioctx); + } + } +} + +void +Request::resume() +{ + if (!paused) { + return; + } + + if (ioctx == NULL) { + return; + } + paused = false; + lcbio_ctx_rwant(ioctx, 1); + lcbio_ctx_schedule(ioctx); +} + +static void +io_error(lcbio_CTX *ctx, lcb_error_t err) +{ + Request *req = reinterpret_cast(lcbio_ctx_data(ctx)); + lcb_log(LOGARGS(req, ERR), LOGFMT "Got error while performing I/O on HTTP stream. Err=0x%x", LOGID(req), err); + req->finish_or_retry(err); +} + +static void +request_timed_out(void *arg) +{ + (reinterpret_cast(arg))->finish(LCB_ETIMEDOUT); +} + +static void +on_connected(lcbio_SOCKET *sock, void *arg, lcb_error_t err, lcbio_OSERR syserr) +{ + Request *req = reinterpret_cast(arg); + lcbio_CTXPROCS procs; + lcb_settings *settings = req->instance->settings; + + LCBIO_CONNREQ_CLEAR(&req->creq); + + if (err != LCB_SUCCESS) { + lcb_log(LOGARGS(req, ERR), "Connection to failed with Err=0x%x", err); + req->finish_or_retry(err); + return; + } + + lcbio_sslify_if_needed(sock, settings); + + procs.cb_err = io_error; + procs.cb_read = io_read; + req->ioctx = lcbio_ctx_new(sock, arg, &procs); + req->ioctx->subsys = "mgmt/capi"; + lcbio_ctx_put(req->ioctx, &req->preamble[0], req->preamble.size()); + if (!req->body.empty()) { + lcbio_ctx_put(req->ioctx, &req->body[0], req->body.size()); + } + lcbio_ctx_rwant(req->ioctx, 1); + lcbio_ctx_schedule(req->ioctx); + (void)syserr; +} + +lcb_error_t +Request::start_io(lcb_host_t& dest) +{ + lcbio_MGR *pool = instance->http_sockpool; + lcbio_pMGRREQ poolreq; + + poolreq = lcbio_mgr_get(pool, &dest, timeout(), on_connected, this); + if (!poolreq) { + return LCB_CONNECT_ERROR; + } + + LCBIO_CONNREQ_MKPOOLED(&creq, poolreq); + + if (!timer) { + timer = lcbio_timer_new(io, this, request_timed_out); + } + + if (!lcbio_timer_armed(timer)) { + lcbio_timer_rearm(timer, timeout()); + } + + return LCB_SUCCESS; +} + +static void +pool_close_cb(lcbio_SOCKET *sock, int reusable, void *arg) +{ + int close_ok = *(int *)arg; + + lcbio_ref(sock); + if (reusable && close_ok) { + lcbio_mgr_put(sock); + } else { + lcbio_mgr_discard(sock); + } +} + +void +Request::close_io() +{ + lcbio_connreq_cancel(&creq); + + if (!ioctx) { + return; + } + + int can_ka; + + if (parser && is_data_request()) { + can_ka = lcbht_can_keepalive(parser); + } else { + can_ka = 0; + } + + lcbio_ctx_close(ioctx, pool_close_cb, &can_ka); + ioctx = NULL; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/instance.cc b/couchbase-sys/libcouchbase-2.7.0/src/instance.cc new file mode 100644 index 00000000..0833c71c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/instance.cc @@ -0,0 +1,728 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "internal.h" +#include "auth-priv.h" +#include "connspec.h" +#include "logging.h" +#include "hostlist.h" +#include "http/http.h" +#include "bucketconfig/clconfig.h" +#include +#include +#define LOGARGS(obj,lvl) (obj)->settings, "instance", LCB_LOG_##lvl, __FILE__, __LINE__ + +static volatile unsigned int lcb_instance_index = 0; +using namespace lcb; + +LIBCOUCHBASE_API +const char *lcb_get_version(lcb_uint32_t *version) +{ + if (version != NULL) { + *version = (lcb_uint32_t)LCB_VERSION; + } + + return LCB_VERSION_STRING; +} + +const lcb_U32 lcb_version_g = LCB_VERSION; + +LIBCOUCHBASE_API +void lcb_set_cookie(lcb_t instance, const void *cookie) +{ + instance->cookie = cookie; +} + +LIBCOUCHBASE_API +const void *lcb_get_cookie(lcb_t instance) +{ + return instance->cookie; +} + +LIBCOUCHBASE_API +void +lcb_set_auth(lcb_t instance, lcb_AUTHENTICATOR *auth) +{ + /* First increase refcount in case they are the same object(!) */ + lcbauth_ref(auth); + lcbauth_unref(instance->settings->auth); + instance->settings->auth = auth; +} + +void +lcb_st::add_bs_host(const char *host, int port, unsigned bstype) +{ + const char *tname = NULL; + lcb::Hostlist* target; + if (bstype == LCB_CONFIG_TRANSPORT_CCCP) { + tname = "CCCP"; + target = mc_nodes; + } else { + tname = "HTTP"; + target = ht_nodes; + } + lcb_log(LOGARGS(this, DEBUG), "Adding host %s:%d to initial %s bootstrap list", host, port, tname); + target->add(host, port); +} + +void +lcb_st::add_bs_host(const lcb::Spechost& host, int defl_http, int defl_cccp) +{ + if (host.isTypeless()) { + add_bs_host(host.hostname.c_str(), defl_http, LCB_CONFIG_TRANSPORT_HTTP); + add_bs_host(host.hostname.c_str(), defl_cccp, LCB_CONFIG_TRANSPORT_CCCP); + return; + } else { + add_bs_host(host.hostname.c_str(), host.port, + host.isAnyHttp() + ? LCB_CONFIG_TRANSPORT_HTTP : LCB_CONFIG_TRANSPORT_CCCP); + } +} + +void +lcb_st::populate_nodes(const Connspec& spec) +{ + int has_ssl = settings->sslopts & LCB_SSL_ENABLED; + int defl_http, defl_cccp; + + if (spec.default_port() == LCB_CONFIG_MCCOMPAT_PORT) { + defl_http = -1; + defl_cccp = LCB_CONFIG_MCCOMPAT_PORT; + + } else if (has_ssl) { + defl_http = LCB_CONFIG_HTTP_SSL_PORT; + defl_cccp = LCB_CONFIG_MCD_SSL_PORT; + } else { + defl_http = LCB_CONFIG_HTTP_PORT; + defl_cccp = LCB_CONFIG_MCD_PORT; + } + + for (size_t ii = 0; ii < spec.hosts().size(); ++ii) { + const Spechost &dh = spec.hosts()[ii]; + add_bs_host(dh, defl_http, defl_cccp); + } +} + +static lcb_error_t +init_providers(lcb_t obj, const Connspec &spec) +{ + clconfig_provider *http, *cccp, *mcraw; + http = lcb_confmon_get_provider(obj->confmon, LCB_CLCONFIG_HTTP); + cccp = lcb_confmon_get_provider(obj->confmon, LCB_CLCONFIG_CCCP); + mcraw = lcb_confmon_get_provider(obj->confmon, LCB_CLCONFIG_MCRAW); + + if (spec.default_port() == LCB_CONFIG_MCCOMPAT_PORT) { + lcb_confmon_set_provider_active(obj->confmon, LCB_CLCONFIG_MCRAW, 1); + mcraw->configure_nodes(mcraw, obj->mc_nodes); + return LCB_SUCCESS; + } + + bool cccp_found = spec.is_bs_cccp(); + bool http_found = spec.is_bs_http(); + bool cccp_enabled = true, http_enabled = true; + + if (cccp_found || http_found || spec.is_bs_file()) { + http_enabled = http_found; + cccp_enabled = cccp_found; + + } + + if (lcb_getenv_boolean("LCB_NO_CCCP")) { + cccp_enabled = false; + } + if (lcb_getenv_boolean("LCB_NO_HTTP")) { + http_enabled = false; + } + if (spec.is_bs_file()) { + cccp_found = false; + http_found = false; + } + + if (cccp_enabled == 0 && http_enabled == 0) { + if (spec.is_bs_file()) { + /* If the 'file_only' provider is set, just assume something else + * will provide us with the config, and forget about it. */ + clconfig_provider *prov = lcb_confmon_get_provider(obj->confmon, LCB_CLCONFIG_FILE); + if (prov && prov->enabled) { + return LCB_SUCCESS; + } + } + return LCB_BAD_ENVIRONMENT; + } + + if (http_enabled) { + lcb_clconfig_http_enable(http); + lcb_clconfig_http_set_nodes(http, obj->ht_nodes); + } else { + lcb_confmon_set_provider_active(obj->confmon, LCB_CLCONFIG_HTTP, 0); + } + + if (cccp_enabled && obj->type != LCB_TYPE_CLUSTER) { + lcb_clconfig_cccp_enable(cccp, obj); + lcb_clconfig_cccp_set_nodes(cccp, obj->mc_nodes); + } else { + lcb_confmon_set_provider_active(obj->confmon, LCB_CLCONFIG_CCCP, 0); + } + return LCB_SUCCESS; +} + +static lcb_error_t +setup_ssl(lcb_t obj, const Connspec& params) +{ + char optbuf[4096]; + int env_policy = -1; + lcb_settings *settings = obj->settings; + lcb_error_t err = LCB_SUCCESS; + + if (lcb_getenv_nonempty("LCB_SSL_CACERT", optbuf, sizeof optbuf)) { + lcb_log(LOGARGS(obj, INFO), "SSL CA certificate %s specified on environment", optbuf); + settings->certpath = strdup(optbuf); + } + + if (lcb_getenv_nonempty("LCB_SSL_MODE", optbuf, sizeof optbuf)) { + if (sscanf(optbuf, "%d", &env_policy) != 1) { + lcb_log(LOGARGS(obj, ERR), "Invalid value for environment LCB_SSL. (%s)", optbuf); + return LCB_BAD_ENVIRONMENT; + } else { + lcb_log(LOGARGS(obj, INFO), "SSL modified from environment. Policy is 0x%x", env_policy); + settings->sslopts = env_policy; + } + } + + if (settings->certpath == NULL && !params.certpath().empty()) { + settings->certpath = strdup(params.certpath().c_str()); + } + + if (env_policy == -1) { + settings->sslopts = params.sslopts(); + } + + if (settings->sslopts & LCB_SSL_ENABLED) { + if (! (settings->sslopts & LCB_SSL_NOGLOBALINIT)) { + lcbio_ssl_global_init(); + } else { + lcb_log(LOGARGS(obj, INFO), "ssl=no_global_init. Not initializing openssl globals"); + } + settings->ssl_ctx = lcbio_ssl_new(settings->certpath, + settings->sslopts & LCB_SSL_NOVERIFY, &err, settings); + if (!settings->ssl_ctx) { + return err; + } + } + return LCB_SUCCESS; +} + +static lcb_error_t +apply_spec_options(lcb_t obj, const Connspec& params) +{ + lcb_error_t err; + Connspec::Options::const_iterator ii = params.options().begin(); + for (; ii != params.options().end(); ++ii) { + lcb_log(LOGARGS(obj, DEBUG), "Applying initial cntl %s=%s", + ii->first.c_str(), ii->second.c_str()); + + err = lcb_cntl_string(obj, ii->first.c_str(), ii->second.c_str()); + if (err != LCB_SUCCESS) { + return err; + } + } + return LCB_SUCCESS; +} + +static lcb_error_t +apply_env_options(lcb_t obj) +{ + Connspec tmpspec; + const char *options = getenv("LCB_OPTIONS"); + + if (!options) { + return LCB_SUCCESS; + } + + std::string tmp("couchbase://?"); + tmp.append(options); + if (tmpspec.parse(tmp.c_str()) != LCB_SUCCESS) { + return LCB_BAD_ENVIRONMENT; + } + return apply_spec_options(obj, tmpspec); +} + +lcb_error_t +lcb_init_providers2(lcb_t obj, const struct lcb_create_st2 *options) +{ + Connspec params; + lcb_error_t err; + struct lcb_create_st cropts; + cropts.version = 2; + cropts.v.v2 = *options; + err = params.load(cropts); + if (err == LCB_SUCCESS) { + err = init_providers(obj, params); + } + return err; +} + +lcb_error_t +lcb_reinit3(lcb_t obj, const char *connstr) +{ + Connspec params; + lcb_error_t err; + const char *errmsg = NULL; + memset(¶ms, 0, sizeof params); + err = params.parse(connstr, &errmsg); + + if (err != LCB_SUCCESS) { + lcb_log(LOGARGS(obj, ERROR), "Couldn't reinit: %s", errmsg); + } + + if (params.sslopts() != LCBT_SETTING(obj, sslopts) || + !params.certpath().empty()) { + lcb_log(LOGARGS(obj, WARN), "Ignoring SSL reinit options"); + } + + /* apply the options */ + err = apply_spec_options(obj, params); + if (err != LCB_SUCCESS) { + goto GT_DONE; + } + obj->populate_nodes(params); + err = init_providers(obj, params); + if (err != LCB_SUCCESS) { + goto GT_DONE; + } + + GT_DONE: + return err; +} + +LIBCOUCHBASE_API +lcb_error_t lcb_create(lcb_t *instance, + const struct lcb_create_st *options) +{ + Connspec spec; + struct lcb_io_opt_st *io_priv = NULL; + lcb_type_t type = LCB_TYPE_BUCKET; + lcb_t obj = NULL; + lcb_error_t err; + lcb_settings *settings; + + if (options) { + io_priv = options->v.v0.io; + if (options->version > 0) { + type = options->v.v1.type; + } + err = spec.load(*options); + } else { + const char *errmsg; + err = spec.parse("couchbase://", &errmsg); + } + if (err != LCB_SUCCESS) { + goto GT_DONE; + } + + if ((obj = (lcb_t)calloc(1, sizeof(*obj))) == NULL) { + err = LCB_CLIENT_ENOMEM; + goto GT_DONE; + } + if (!(settings = lcb_settings_new())) { + err = LCB_CLIENT_ENOMEM; + goto GT_DONE; + } + + /* initialize the settings */ + obj->type = type; + obj->settings = settings; + + settings->bucket = strdup(spec.bucket().c_str()); + if ((err = settings->auth->init(spec.username(), spec.bucket(), + spec.password(), type)) != LCB_SUCCESS) { + goto GT_DONE; + } + + settings->logger = lcb_init_console_logger(); + settings->iid = lcb_instance_index++; + if (spec.loglevel()) { + lcb_U32 val = spec.loglevel(); + lcb_cntl(obj, LCB_CNTL_SET, LCB_CNTL_CONLOGGER_LEVEL, &val); + } + + lcb_log(LOGARGS(obj, INFO), "Version=%s, Changeset=%s", lcb_get_version(NULL), LCB_VERSION_CHANGESET); + lcb_log(LOGARGS(obj, INFO), "Effective connection string: %s. Bucket=%s", spec.connstr().c_str(), settings->bucket); + + if (io_priv == NULL) { + lcb_io_opt_t ops; + if ((err = lcb_create_io_ops(&ops, NULL)) != LCB_SUCCESS) { + goto GT_DONE; + } + io_priv = ops; + LCB_IOPS_BASEFLD(io_priv, need_cleanup) = 1; + } + + obj->cmdq.cqdata = obj; + obj->iotable = lcbio_table_new(io_priv); + obj->memd_sockpool = lcbio_mgr_create(settings, obj->iotable); + obj->http_sockpool = lcbio_mgr_create(settings, obj->iotable); + obj->memd_sockpool->maxidle = 1; + obj->memd_sockpool->tmoidle = 10000000; + obj->http_sockpool->maxidle = 1; + obj->http_sockpool->tmoidle = 10000000; + obj->confmon = lcb_confmon_create(settings, obj->iotable); + obj->ht_nodes = new Hostlist(); + obj->mc_nodes = new Hostlist(); + obj->retryq = new RetryQueue(&obj->cmdq, obj->iotable, obj->settings); + obj->n1ql_cache = lcb_n1qlcache_create(); + lcb_initialize_packet_handlers(obj); + lcb_aspend_init(&obj->pendops); + + if ((err = setup_ssl(obj, spec)) != LCB_SUCCESS) { + goto GT_DONE; + } + + if ((err = apply_spec_options(obj, spec)) != LCB_SUCCESS) { + goto GT_DONE; + } + if ((err = apply_env_options(obj)) != LCB_SUCCESS) { + goto GT_DONE; + } + + obj->populate_nodes(spec); + err = init_providers(obj, spec); + if (err != LCB_SUCCESS) { + lcb_destroy(obj); + return err; + } + + obj->last_error = err; + GT_DONE: + if (err != LCB_SUCCESS && obj) { + lcb_destroy(obj); + *instance = NULL; + } else { + *instance = obj; + } + return err; +} + +typedef struct { + lcbio_pTABLE table; + lcbio_pTIMER timer; + int stopped; +} SYNCDTOR; + +static void +sync_dtor_cb(void *arg) +{ + SYNCDTOR *sd = (SYNCDTOR*)arg; + if (sd->table->refcount == 2) { + lcbio_timer_destroy(sd->timer); + IOT_STOP(sd->table); + sd->stopped = 1; + } +} + +extern "C" { +void lcbdur_destroy(void*); +} + +LIBCOUCHBASE_API +void lcb_destroy(lcb_t instance) +{ + #define DESTROY(fn,fld) if(instance->fld){fn(instance->fld);instance->fld=NULL;} + + lcb_ASPEND *po = &instance->pendops; + lcb_ASPEND_SETTYPE::iterator it; + lcb_ASPEND_SETTYPE *pendq; + + DESTROY(lcb_clconfig_decref, cur_configinfo); + instance->cmdq.config = NULL; + + lcb_bootstrap_destroy(instance); + DESTROY(delete, ht_nodes); + DESTROY(delete, mc_nodes); + + if ((pendq = po->items[LCB_PENDTYPE_TIMER])) { + for (it = pendq->begin(); it != pendq->end(); ++it) { + lcb__timer_destroy_nowarn(instance, (lcb_timer_t)*it); + } + } + + if ((pendq = po->items[LCB_PENDTYPE_DURABILITY])) { + std::vector dsets(pendq->begin(), pendq->end()); + for (size_t ii = 0; ii < dsets.size(); ++ii) { + lcbdur_destroy(dsets[ii]); + } + pendq->clear(); + } + + for (size_t ii = 0; ii < LCBT_NSERVERS(instance); ++ii) { + instance->get_server(ii)->close(); + } + + if ((pendq = po->items[LCB_PENDTYPE_HTTP])) { + for (it = pendq->begin(); it != pendq->end(); ++it) { + lcb_http_request_t htreq = reinterpret_cast(*it); + lcb_htreq_block_callback(htreq); + lcb_htreq_finish(instance, htreq, LCB_ERROR); + } + } + + DESTROY(delete, retryq); + DESTROY(lcb_confmon_destroy, confmon); + DESTROY(lcbio_mgr_destroy, memd_sockpool); + DESTROY(lcbio_mgr_destroy, http_sockpool); + DESTROY(lcb_vbguess_destroy, vbguess); + DESTROY(lcb_n1qlcache_destroy, n1ql_cache); + + mcreq_queue_cleanup(&instance->cmdq); + lcb_aspend_cleanup(po); + + if (instance->iotable && instance->iotable->refcount > 1 && + instance->settings && instance->settings->syncdtor) { + /* create an async object */ + SYNCDTOR sd; + sd.table = instance->iotable; + sd.timer = lcbio_timer_new(instance->iotable, &sd, sync_dtor_cb); + sd.stopped = 0; + lcbio_async_signal(sd.timer); + lcb_log(LOGARGS(instance, WARN), "Running event loop to drain any pending I/O events"); + do { + IOT_START(instance->iotable); + } while (!sd.stopped); + } + + DESTROY(lcbio_table_unref, iotable); + DESTROY(lcb_settings_unref, settings); + DESTROY(lcb_histogram_destroy, kv_timings); + if (instance->scratch) { + delete instance->scratch; + instance->scratch = NULL; + } + + delete[] instance->dcpinfo; + memset(instance, 0xff, sizeof(*instance)); + free(instance); +#undef DESTROY +} + +static void +destroy_cb(void *arg) +{ + lcb_t instance = (lcb_t)arg; + lcbio_timer_destroy(instance->dtor_timer); + lcb_destroy(instance); +} + +LIBCOUCHBASE_API +void +lcb_destroy_async(lcb_t instance, const void *arg) +{ + instance->dtor_timer = lcbio_timer_new(instance->iotable, instance, destroy_cb); + instance->settings->dtorarg = (void *)arg; + lcbio_async_signal(instance->dtor_timer); +} + +LIBCOUCHBASE_API +lcb_error_t lcb_connect(lcb_t instance) +{ + lcb_error_t err = lcb_bootstrap_common(instance, LCB_BS_REFRESH_INITIAL); + if (err == LCB_SUCCESS) { + SYNCMODE_INTERCEPT(instance); + } else { + return err; + } +} + +LIBCOUCHBASE_API +void *lcb_mem_alloc(lcb_size_t size) +{ + return malloc(size); +} + +LIBCOUCHBASE_API +void lcb_mem_free(void *ptr) +{ + free(ptr); +} + +LCB_INTERNAL_API +void lcb_run_loop(lcb_t instance) +{ + IOT_START(instance->iotable); +} + +LCB_INTERNAL_API +void lcb_stop_loop(lcb_t instance) +{ + IOT_STOP(instance->iotable); +} + +void +lcb_aspend_init(lcb_ASPEND *ops) +{ + unsigned ii; + for (ii = 0; ii < LCB_PENDTYPE_MAX; ++ii) { + ops->items[ii] = new lcb_ASPEND_SETTYPE(); + } + ops->count = 0; +} + +void +lcb_aspend_add(lcb_ASPEND *ops, lcb_ASPENDTYPE type, const void *item) +{ + ops->count++; + if (type == LCB_PENDTYPE_COUNTER) { + return; + } + ops->items[type]->insert(const_cast(item)); +} + +void +lcb_aspend_del(lcb_ASPEND *ops, lcb_ASPENDTYPE type, const void *item) +{ + if (type == LCB_PENDTYPE_COUNTER) { + ops->count--; + return; + } + if (ops->items[type]->erase(const_cast(item)) != 0) { + ops->count--; + } +} + +void +lcb_aspend_cleanup(lcb_ASPEND *ops) +{ + unsigned ii; + for (ii = 0; ii < LCB_PENDTYPE_MAX; ii++) { + delete ops->items[ii]; + } +} + +LIBCOUCHBASE_API +void +lcb_sched_enter(lcb_t instance) +{ + mcreq_sched_enter(&instance->cmdq); +} +LIBCOUCHBASE_API +void +lcb_sched_leave(lcb_t instance) +{ + mcreq_sched_leave(&instance->cmdq, LCBT_SETTING(instance, sched_implicit_flush)); +} +LIBCOUCHBASE_API +void +lcb_sched_fail(lcb_t instance) +{ + mcreq_sched_fail(&instance->cmdq); +} + +LIBCOUCHBASE_API +int +lcb_supports_feature(int n) +{ + if (n == LCB_SUPPORTS_SNAPPY) { +#ifdef LCB_NO_SNAPPY + return 0; +#else + return 1; +#endif + } + if (n == LCB_SUPPORTS_SSL) { + return lcbio_ssl_supported(); + } else { + return 0; + } +} + + +LCB_INTERNAL_API void lcb_loop_ref(lcb_t instance) { + lcb_aspend_add(&instance->pendops, LCB_PENDTYPE_COUNTER, NULL); +} +LCB_INTERNAL_API void lcb_loop_unref(lcb_t instance) { + lcb_aspend_del(&instance->pendops, LCB_PENDTYPE_COUNTER, NULL); + lcb_maybe_breakout(instance); +} + +LIBCOUCHBASE_API +lcb_error_t lcb_enable_timings(lcb_t instance) +{ + if (instance->kv_timings != NULL) { + return LCB_KEY_EEXISTS; + } + instance->kv_timings = lcb_histogram_create(); + return instance->kv_timings == NULL ? LCB_CLIENT_ENOMEM : LCB_SUCCESS; +} + +LIBCOUCHBASE_API +lcb_error_t lcb_disable_timings(lcb_t instance) +{ + if (instance->kv_timings == NULL) { + return LCB_KEY_ENOENT; + } + lcb_histogram_destroy(instance->kv_timings); + instance->kv_timings = NULL; + return LCB_SUCCESS; +} + +typedef struct { + lcb_t instance; + const void *real_cookie; + lcb_timings_callback real_cb; +} timings_wrapper; + +static void +timings_wrapper_callback(const void *cookie, lcb_timeunit_t unit, lcb_U32 start, + lcb_U32 end, lcb_U32 val, lcb_U32 max) +{ + const timings_wrapper *wrap = (const timings_wrapper*)cookie; + wrap->real_cb(wrap->instance, wrap->real_cookie, unit, start, end, val, max); +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_get_timings(lcb_t instance, const void *cookie, lcb_timings_callback cb) +{ + timings_wrapper wrap; + wrap.instance = instance; + wrap.real_cookie = cookie; + wrap.real_cb = cb; + + if (!instance->kv_timings) { + return LCB_KEY_ENOENT; + } + lcb_histogram_read(instance->kv_timings, &wrap, timings_wrapper_callback); + return LCB_SUCCESS; +} + +LIBCOUCHBASE_API +const char *lcb_strerror(lcb_t instance, lcb_error_t error) +{ + #define X(c, v, t, s) if (error == c) { return s; } + LCB_XERR(X) + #undef X + + (void)instance; + return "Unknown error"; +} + +LIBCOUCHBASE_API +int lcb_get_errtype(lcb_error_t err) +{ + #define X(c, v, t, s) if (err == c) { return t; } + LCB_XERR(X) + #undef X + return -1; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/internal.h b/couchbase-sys/libcouchbase-2.7.0/src/internal.h new file mode 100644 index 00000000..b1c6f793 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/internal.h @@ -0,0 +1,246 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LIBCOUCHBASE_INTERNAL_H +#define LIBCOUCHBASE_INTERNAL_H 1 + +/* System/Standard includes */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global Project Dependencies/Includes */ +#include +#include +#include +#include +#include + +/* Internal dependencies */ +#include +#include +#include "mcserver/mcserver.h" +#include "mc/mcreq.h" +#include "settings.h" + +/* lcb_t-specific includes */ +#include "retryq.h" +#include "aspend.h" +#include "bootstrap.h" + +/* n1ql cache */ +#include "n1ql/n1ql-internal.h" + +#include "hostlist.h" + +#ifdef __cplusplus +namespace lcb { +class Connspec; +struct Spechost; +class RetryQueue; +} +extern "C" { +#endif + +struct lcb_string_st; + +struct lcb_callback_st { + lcb_RESPCALLBACK v3callbacks[LCB_CALLBACK__MAX]; + lcb_get_callback get; + lcb_store_callback store; + lcb_arithmetic_callback arithmetic; + lcb_observe_callback observe; + lcb_remove_callback remove; + lcb_stat_callback stat; + lcb_version_callback version; + lcb_touch_callback touch; + lcb_flush_callback flush; + lcb_error_callback error; + lcb_http_complete_callback http_complete; + lcb_http_data_callback http_data; + lcb_unlock_callback unlock; + lcb_configuration_callback configuration; + lcb_verbosity_callback verbosity; + lcb_durability_callback durability; + lcb_errmap_callback errmap; + lcb_bootstrap_callback bootstrap; + lcb_pktfwd_callback pktfwd; + lcb_pktflushed_callback pktflushed; +}; + +struct lcb_confmon_st; +struct lcb_BOOTSTRAP; +struct lcb_GUESSVB_st; + +#ifdef __cplusplus +#include +typedef std::string* lcb_pSCRATCHBUF; +typedef lcb::RetryQueue lcb_RETRYQ; +#else +typedef struct lcb_SCRATCHBUF* lcb_pSCRATCHBUF; +typedef struct lcb_RETRYQ_st lcb_RETRYQ; +#endif + +struct lcb_st { + mc_CMDQUEUE cmdq; /**< Base command queue object */ + const void *cookie; /**< User defined pointer */ + struct lcb_confmon_st *confmon; /**< Cluster config manager */ + hostlist_t mc_nodes; /**< List of current memcached endpoints */ + hostlist_t ht_nodes; /**< List of current management endpoints */ + struct clconfig_info_st *cur_configinfo; /**< Pointer to current config */ + struct lcb_BOOTSTRAP *bootstrap; /**< Bootstrapping state */ + struct lcb_callback_st callbacks; /**< Callback table */ + lcb_HISTOGRAM *kv_timings; /**< Histogram object (for timing) */ + lcb_ASPEND pendops; /**< Pending asynchronous requests */ + int wait; /**< Are we in lcb_wait() ?*/ + lcbio_MGR *memd_sockpool; /**< Connection pool for memcached connections */ + lcbio_MGR *http_sockpool; /**< Connection pool for capi connections */ + lcb_error_t last_error; /**< Seldom used. Mainly for bootstrap */ + lcb_settings *settings; /**< User settings */ + lcbio_pTABLE iotable; /**< IO Routine table */ + lcb_RETRYQ *retryq; /**< Retry queue for failed operations */ + lcb_pSCRATCHBUF scratch; /**< Generic buffer space */ + struct lcb_GUESSVB_st *vbguess; /**< Heuristic masters for vbuckets */ + lcb_N1QLCACHE *n1ql_cache; + lcb_MUTATION_TOKEN *dcpinfo; /**< Mapping of known vbucket to {uuid,seqno} info */ + lcbio_pTIMER dtor_timer; /**< Asynchronous destruction timer */ + int type; /**< Type of connection */ + + #ifdef __cplusplus + lcb_settings* getSettings() { return settings; } + lcbio_pTABLE getIOT() { return iotable; } + inline void add_bs_host(const char *host, int port, unsigned bstype); + inline void add_bs_host(const lcb::Spechost& host, int defl_http, int defl_cccp); + inline void populate_nodes(const lcb::Connspec&); + mc_SERVER *get_server(size_t index) const { + return static_cast(cmdq.pipelines[index]); + } + #endif +}; + +#define LCBT_VBCONFIG(instance) (instance)->cmdq.config +#define LCBT_NSERVERS(instance) (instance)->cmdq.npipelines +#define LCBT_NDATASERVERS(instance) LCBVB_NDATASERVERS(LCBT_VBCONFIG(instance)) +#define LCBT_NREPLICAS(instance) LCBVB_NREPLICAS(LCBT_VBCONFIG(instance)) +#define LCBT_GET_SERVER(instance, ix) ((mc_SERVER *)(instance)->cmdq.pipelines[ix]) +#define LCBT_SETTING(instance, name) (instance)->settings->name + +void lcb_initialize_packet_handlers(lcb_t instance); + +LCB_INTERNAL_API +void lcb_maybe_breakout(lcb_t instance); + +struct clconfig_info_st; +void lcb_update_vbconfig(lcb_t instance, struct clconfig_info_st *config); +/** + * Hashtable wrappers + */ +genhash_t *lcb_hashtable_nc_new(lcb_size_t est); +genhash_t *lcb_hashtable_szt_new(lcb_size_t est); + +lcb_error_t lcb_iops_cntl_handler(int mode, lcb_t instance, int cmd, void *arg); + +/** + * These two routines define portable ways to get environment variables + * on various platforms. + * + * They are mainly useful for Windows compatibility. + */ +LCB_INTERNAL_API +int lcb_getenv_nonempty(const char *key, char *buf, lcb_size_t len); +LCB_INTERNAL_API +int lcb_getenv_boolean(const char *key); +LCB_INTERNAL_API +int lcb_getenv_nonempty_multi(char *buf, lcb_size_t nbuf, ...); +int lcb_getenv_boolean_multi(const char *key, ...); +LCB_INTERNAL_API +const char *lcb_get_tmpdir(void); + +/** + * Initialize the socket subsystem. For windows, this initializes Winsock. + * On Unix, this does nothing + */ +LCB_INTERNAL_API +lcb_error_t lcb_initialize_socket_subsystem(void); + +lcb_error_t lcb_init_providers2(lcb_t obj, + const struct lcb_create_st2 *e_options); +lcb_error_t lcb_reinit3(lcb_t obj, const char *connstr); + + +LCB_INTERNAL_API +mc_SERVER * +lcb_find_server_by_host(lcb_t instance, const lcb_host_t *host); + + +LCB_INTERNAL_API +mc_SERVER * +lcb_find_server_by_index(lcb_t instance, int ix); + +LCB_INTERNAL_API +lcb_error_t +lcb_getconfig(lcb_t instance, const void *cookie, mc_SERVER *server); + +int +lcb_should_retry(const lcb_settings *settings, const mc_PACKET *pkt, lcb_error_t err); + +lcb_error_t +lcb__synchandler_return(lcb_t instance); + +lcb_RESPCALLBACK +lcb_find_callback(lcb_t instance, lcb_CALLBACKTYPE cbtype); + +/* These two functions exist to allow the tests to keep the loop alive while + * scheduling other operations asynchronously */ + +LCB_INTERNAL_API void lcb_loop_ref(lcb_t instance); +LCB_INTERNAL_API void lcb_loop_unref(lcb_t instance); + +/* To suppress compiler warnings */ +LCB_INTERNAL_API void lcb__timer_destroy_nowarn(lcb_t instance, lcb_timer_t timer); + +#define SYNCMODE_INTERCEPT(o) \ + if (LCBT_SETTING(o, syncmode) == LCB_ASYNCHRONOUS) { \ + return LCB_SUCCESS; \ + } else { \ + return lcb__synchandler_return(o); \ + } + + +#define MAYBE_SCHEDLEAVE(o) \ + if (!o->cmdq.ctxenter) { \ + lcb_sched_leave(o); \ + } + +#define LCB_SCHED_ADD(instance, pl, pkt) \ + mcreq_sched_add(pl, pkt); \ + MAYBE_SCHEDLEAVE(instance) + +void lcb_vbguess_newconfig(lcb_t instance, lcbvb_CONFIG *cfg, struct lcb_GUESSVB_st *guesses); +int lcb_vbguess_remap(lcb_t instance, int vbid, int bad); +#define lcb_vbguess_destroy(p) free(p) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/iofactory.c b/couchbase-sys/libcouchbase-2.7.0/src/iofactory.c new file mode 100644 index 00000000..74eae50c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/iofactory.c @@ -0,0 +1,575 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2011-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LCB_IOPS_V12_NO_DEPRECATE 1 /* For Ruby */ + +#include "internal.h" +#include "plugins/io/select/select_io_opts.h" +#include + +#ifdef LCB_EMBED_PLUGIN_LIBEVENT +LIBCOUCHBASE_API lcb_error_t lcb_create_libevent_io_opts(int, lcb_io_opt_t*,void*); +#endif + +typedef lcb_error_t (*create_func_t)(int version, lcb_io_opt_t *io, void *cookie); + + +#ifdef _WIN32 +LIBCOUCHBASE_API +lcb_error_t lcb_iocp_new_iops(int, lcb_io_opt_t *, void *); +#define DEFAULT_IOPS LCB_IO_OPS_WINIOCP +#else +#define DEFAULT_IOPS LCB_IO_OPS_LIBEVENT +#endif + + +typedef struct { + /** The "base" name of the plugin */ + const char *base; + + /** Corresponding type */ + lcb_io_ops_type_t iotype; + + /** Filename */ + const char *soname; + + /** Symbol used to initialize the plugin */ + const char *symbol; + + /** Function to create the iops (if builtin) */ + create_func_t create; + + /** Static buffers if reading from the environment */ + char s_soname[PATH_MAX]; + char s_symbol[256]; +} plugin_info; + + +#ifdef __APPLE__ +#define PLUGIN_SO(NAME) "libcouchbase_"NAME".dylib" +#elif defined(_WIN32) +/** Trailing period intentional. See docs for LoadLibrary */ +#if (_DEBUG && _MSC_VER) + #define PLUGIN_SO(NAME) "libcouchbase_"NAME"_d.dll." +#else + #define PLUGIN_SO(NAME) "libcouchbase_"NAME".dll." +#endif /* _DEBUG */ +#else +#define PLUGIN_SO(NAME) "libcouchbase_"NAME".so" +#endif + +#define PLUGIN_SYMBOL(NAME) "lcb_create_"NAME"_io_opts" + +#define BUILTIN_CORE(name, type, create) \ + { name, type, NULL, NULL, create, { 0 }, { 0 } } + +#define BUILTIN_DL(name, type) \ + { name, type, PLUGIN_SO(name), PLUGIN_SYMBOL(name), NULL, { 0 }, { 0 } } + +static plugin_info builtin_plugins[] = { + BUILTIN_CORE("select", LCB_IO_OPS_SELECT, lcb_create_select_io_opts), + BUILTIN_CORE("winsock", LCB_IO_OPS_WINSOCK, lcb_create_select_io_opts), + +#ifdef _WIN32 + BUILTIN_CORE("iocp", LCB_IO_OPS_WINIOCP, lcb_iocp_new_iops), +#endif + +#ifdef LCB_EMBED_PLUGIN_LIBEVENT + BUILTIN_CORE("libevent", LCB_IO_OPS_LIBEVENT, lcb_create_libevent_io_opts), +#else + BUILTIN_DL("libevent", LCB_IO_OPS_LIBEVENT), +#endif + + BUILTIN_DL("libev", LCB_IO_OPS_LIBEV), + BUILTIN_DL("libuv", LCB_IO_OPS_LIBUV), + + { NULL, LCB_IO_OPS_INVALID, NULL, NULL, NULL, { 0 }, { 0 } } +}; + +/** + * Checks the environment for plugin information. + * Returns: + * 1 information found and valid + * 0 not found + * -1 error + */ +static int get_env_plugin_info(plugin_info *info) +{ + + plugin_info *cur = NULL; + memset(info, 0, sizeof(*info)); + + if (!lcb_getenv_nonempty_multi(info->s_soname, sizeof(info->s_soname), + "LIBCOUCHBASE_EVENT_PLUGIN_NAME", "LCB_IOPS_NAME", NULL)) { + return 0; + } + + for (cur = builtin_plugins; cur->base; cur++) { + if (strlen(cur->base) != strlen(info->s_soname)) { + continue; + } + + if (strcmp(cur->base, info->s_soname) == 0) { + memcpy(info, cur, sizeof(*cur)); + return 1; + } + } + + if (!lcb_getenv_nonempty_multi(info->s_symbol, sizeof(info->s_symbol), + "LIBCOUCHBASE_EVENT_PLUGIN_SYMBOL", "LCB_IOPS_SYMBOL", NULL)) { + return -1; + } + + info->soname = info->s_soname; + info->symbol = info->s_symbol; + return 1; +} + +static plugin_info *find_plugin_info(lcb_io_ops_type_t iotype) +{ + plugin_info *cur; + + if (iotype == LCB_IO_OPS_DEFAULT) { + iotype = DEFAULT_IOPS; + } + + for (cur = builtin_plugins; cur->base; cur++) { + if (cur->iotype == iotype) { + return cur; + } + } + return NULL; +} + +static void options_from_info(struct lcb_create_io_ops_st *opts, + const plugin_info *info) +{ + void *cookie; + + switch (opts->version) { + case 0: + cookie = opts->v.v0.cookie; + break; + case 1: + cookie = opts->v.v1.cookie; + break; + case 2: + cookie = opts->v.v2.cookie; + break; + default: + lcb_assert("unknown options version" && 0); + cookie = NULL; + } + + if (info->create) { + opts->version = 2; + opts->v.v2.create = info->create; + opts->v.v2.cookie = cookie; + return; + } + + opts->version = 1; + opts->v.v1.sofile = info->soname; + opts->v.v1.symbol = info->symbol; + opts->v.v1.cookie = cookie; +} + +static lcb_error_t create_v2(lcb_io_opt_t *io, + const struct lcb_create_io_ops_st *options); + +struct plugin_st { + void *dlhandle; + union { + create_func_t create; + void *voidptr; + } func; +}; + +#ifndef _WIN32 +static lcb_error_t get_create_func(const char *image, + const char *symbol, + struct plugin_st *plugin, + int do_warn) +{ + void *dlhandle = dlopen(image, RTLD_NOW | RTLD_LOCAL); + if (dlhandle == NULL) { + if (do_warn) { + fprintf(stderr, + "[libcouchbase] dlopen of %s failed with '%s'\n", + image, dlerror()); + } + return LCB_DLOPEN_FAILED; + } + + memset(plugin, 0, sizeof(*plugin)); + plugin->func.create = NULL; + plugin->func.voidptr = dlsym(dlhandle, symbol); + + if (plugin->func.voidptr == NULL) { + if (do_warn) { + fprintf(stderr, + "[libcouchbase] dlsym (%s) -> (%s) failed: %s\n", + image, symbol, dlerror()); + } + dlclose(dlhandle); + dlhandle = NULL; + return LCB_DLSYM_FAILED; + + } else { + plugin->dlhandle = dlhandle; + } + return LCB_SUCCESS; +} + +static void close_dlhandle(void *handle) +{ + dlclose(handle); +} +#else +static lcb_error_t get_create_func(const char *image, + const char *symbol, + struct plugin_st *plugin, + int do_warn) +{ + HMODULE hLibrary = LoadLibrary(image); + FARPROC hFunction; + + memset(plugin, 0, sizeof(*plugin)); + + if (!hLibrary) { + if (do_warn) { + fprintf(stderr, "LoadLibrary of %s failed with code %d\n", + image, (int)GetLastError()); + } + return LCB_DLOPEN_FAILED; + } + + hFunction = GetProcAddress(hLibrary, symbol); + if (!hFunction) { + if (do_warn) { + fprintf(stderr, "GetProcAddress (%s) -> (%s) failed with code %d\n", + image, symbol, (int)GetLastError()); + } + FreeLibrary(hLibrary); + return LCB_DLSYM_FAILED; + } + + plugin->func.create = (create_func_t)hFunction; + plugin->dlhandle = hLibrary; + return LCB_SUCCESS; +} + +static void close_dlhandle(void *handle) +{ + FreeLibrary((HMODULE)handle); +} +#endif + +static int want_dl_debug = 0; /* global variable */ +static lcb_error_t create_v1(lcb_io_opt_t *io, + const struct lcb_create_io_ops_st *options); + +LIBCOUCHBASE_API +lcb_error_t lcb_destroy_io_ops(lcb_io_opt_t io) +{ + if (io) { + void *dlhandle = io->dlhandle; + if (io->destructor) { + io->destructor(io); + } + if (dlhandle) { + close_dlhandle(dlhandle); + } + } + + return LCB_SUCCESS; +} + +/** + * Note, the 'pi' is just a context variable to ensure the pointers copied + * to the options are valid. It is *not* meant to be inspected. + */ +static lcb_error_t generate_options(plugin_info *pi, + const struct lcb_create_io_ops_st *user, + struct lcb_create_io_ops_st *ours, + lcb_io_ops_type_t *type) +{ + if (user) { + memcpy(ours, user, sizeof(*user)); + + } else { + memset(ours, 0, sizeof(*ours)); + ours->version = 0; + ours->v.v0.type = LCB_IO_OPS_DEFAULT; + } + + if (ours->version > 0) { + if (type) { + *type = LCB_IO_OPS_INVALID; + } + /* we don't handle non-v0 options */ + return LCB_SUCCESS; + } + + if (ours->v.v0.type == LCB_IO_OPS_DEFAULT) { + int rv; + memset(pi, 0, sizeof(*pi)); + + rv = get_env_plugin_info(pi); + if (rv > 0) { + options_from_info(ours, pi); + + if (type) { + *type = pi->iotype; + } + + } else if (rv < 0) { + return LCB_BAD_ENVIRONMENT; + + } else { + plugin_info *pip = find_plugin_info(LCB_IO_OPS_DEFAULT); + lcb_assert(pip); + + if (type) { + *type = pip->iotype; + } + + options_from_info(ours, pip); + + /* if the plugin is dynamically loadable, we need to + * fallback to select(2) plugin in case we cannot find the + * create function */ + if (ours->version == 1) { + struct plugin_st plugin; + int want_debug; + lcb_error_t ret; + + if (lcb_getenv_boolean_multi("LIBCOUCHBASE_DLOPEN_DEBUG", + "LCB_DLOPEN_DEBUG", NULL)) { + want_debug = 1; + } else { + want_debug = want_dl_debug; + } + ret = get_create_func(ours->v.v1.sofile, ours->v.v1.symbol, &plugin, want_debug); + if (ret != LCB_SUCCESS) { + if (type) { + *type = LCB_IO_OPS_SELECT; + } + ours->version = 2; + ours->v.v2.create = lcb_create_select_io_opts; + ours->v.v2.cookie = NULL; + } + } + } + return LCB_SUCCESS; + + } else { + /** Not default, ignore environment */ + plugin_info *pip = find_plugin_info(ours->v.v0.type); + if (!pip) { + return LCB_NOT_SUPPORTED; + } + options_from_info(ours, pip); + if (type) { + *type = pip->iotype; + } + return LCB_SUCCESS; + } +} + +LIBCOUCHBASE_API +lcb_error_t lcb_create_io_ops(lcb_io_opt_t *io, + const struct lcb_create_io_ops_st *io_opts) +{ + + struct lcb_create_io_ops_st options; + lcb_error_t err; + plugin_info pi; + memset(&options, 0, sizeof(options)); + + err = lcb_initialize_socket_subsystem(); + if (err != LCB_SUCCESS) { + return err; + } + + err = generate_options(&pi, io_opts, &options, NULL); + if (err != LCB_SUCCESS) { + return err; + } + + if (options.version == 1) { + err = create_v1(io, &options); + } else if (options.version == 2) { + err = create_v2(io, &options); + } else { + return LCB_NOT_SUPPORTED; + } + + if (err != LCB_SUCCESS) { + return err; + } + /*XXX: + * This block of code here because the Ruby SDK relies on undocumented + * functionality of older versions of libcouchbase in which its send/recv + * functions assert that the number of IOV elements passed is always going + * to be 2. + * + * This works around the issue by patching the send/recv functions of + * the ruby implementation at load-time. + * + * This block of code will go away once the Ruby SDK is fixed and a released + * version has been out for enough time that it won't break common existing + * deployments. + */ + if (io_opts && io_opts->version == 1 && io_opts->v.v1.symbol != NULL) { + if (strstr(io_opts->v.v1.symbol, "cb_create_ruby")) { + wire_lcb_bsd_impl(*io); + } + } + return LCB_SUCCESS; +} + +static lcb_error_t create_v1(lcb_io_opt_t *io, + const struct lcb_create_io_ops_st *options) +{ + struct plugin_st plugin; + int want_debug; + lcb_error_t ret; + + if (lcb_getenv_boolean_multi("LIBCOUCHBASE_DLOPEN_DEBUG", + "LCB_DLOPEN_DEBUG", NULL)) { + want_debug = 1; + } else { + want_debug = want_dl_debug; + } + ret = get_create_func(options->v.v1.sofile, + options->v.v1.symbol, &plugin, want_debug); + if (ret != LCB_SUCCESS) { + /* try to look up the symbol in the current image */ + lcb_error_t ret2 = get_create_func(NULL, options->v.v1.symbol, &plugin, want_debug); + if (ret2 != LCB_SUCCESS) { +#ifndef _WIN32 + char path[PATH_MAX]; + /* try to look up the so-file in the libdir */ + snprintf(path, PATH_MAX, "%s/%s", LCB_LIBDIR, options->v.v1.sofile); + ret2 = get_create_func(path, options->v.v1.symbol, &plugin, want_debug); +#endif + if (ret2 != LCB_SUCCESS) { + /* return original error to allow caller to fix it */ + return ret; + } + } + } + + ret = plugin.func.create(0, io, options->v.v1.cookie); + if (ret != LCB_SUCCESS) { + if (options->v.v1.sofile != NULL) { + close_dlhandle(plugin.dlhandle); + } + return LCB_CLIENT_ENOMEM; + } else { + lcb_io_opt_t iop = *io; + iop->dlhandle = plugin.dlhandle; + /* check if plugin selected compatible version */ + if (iop->version < 0 || iop->version > 3) { + lcb_destroy_io_ops(iop); + return LCB_PLUGIN_VERSION_MISMATCH; + } + } + + return LCB_SUCCESS; +} + +static lcb_error_t create_v2(lcb_io_opt_t *io, + const struct lcb_create_io_ops_st *options) +{ + lcb_error_t ret; + + ret = options->v.v2.create(0, io, options->v.v2.cookie); + if (ret != LCB_SUCCESS) { + return ret; + } else { + lcb_io_opt_t iop = *io; + /* check if plugin selected compatible version */ + if (iop->version < 0 || iop->version > 3) { + lcb_destroy_io_ops(iop); + return LCB_PLUGIN_VERSION_MISMATCH; + } + } + + return LCB_SUCCESS; +} + +lcb_error_t lcb_iops_cntl_handler(int mode, + lcb_t instance, int cmd, void *arg) +{ + (void)instance; + + switch (cmd) { + case LCB_CNTL_IOPS_DEFAULT_TYPES: { + struct lcb_create_io_ops_st options; + struct lcb_cntl_iops_info_st *info = arg; + lcb_error_t err; + plugin_info pi; + + memset(&options, 0, sizeof(options)); + if (mode != LCB_CNTL_GET) { + return LCB_NOT_SUPPORTED; + } + + if (info->version != 0) { + return LCB_EINVAL; + } + + info->v.v0.os_default = DEFAULT_IOPS; + + err = generate_options(&pi, + info->v.v0.options, + &options, + &info->v.v0.effective); + + if (err != LCB_SUCCESS) { + return LCB_ERROR; + } + + return LCB_SUCCESS; + } + + case LCB_CNTL_IOPS_DLOPEN_DEBUG: { + int *usr = arg; + if (mode == LCB_CNTL_SET) { + want_dl_debug = *usr; + } else { + *usr = want_dl_debug; + } + return LCB_SUCCESS; + } + + default: + return LCB_EINVAL; + + } + +} + +/* In-library wrapper version */ +LIBCOUCHBASE_API +void +lcb_iops_wire_bsd_impl2(lcb_bsd_procs *procs, int version) +{ + wire_lcb_bsd_impl2(procs, version); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/jsparse/parser.cc b/couchbase-sys/libcouchbase-2.7.0/src/jsparse/parser.cc new file mode 100644 index 00000000..bf488620 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/jsparse/parser.cc @@ -0,0 +1,519 @@ +/* + * Copyright 2013-2015 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#if defined(__GNUC__) +#define JSONSL_API static __attribute__((unused)) +#elif defined(_MSC_VER) +#define JSONSL_API static __inline +#else +#define JSONSL_API static +#endif +#include "contrib/jsonsl/jsonsl.c" +#include "contrib/lcb-jsoncpp/lcb-jsoncpp.h" +#include "parser.h" +#include + +#define DECLARE_JSONSL_CALLBACK(name) \ +static void name(jsonsl_t,jsonsl_action_t,struct jsonsl_state_st*,const char*) + + +DECLARE_JSONSL_CALLBACK(row_pop_callback); +DECLARE_JSONSL_CALLBACK(initial_push_callback); +DECLARE_JSONSL_CALLBACK(initial_pop_callback); +DECLARE_JSONSL_CALLBACK(meta_header_complete_callback); +DECLARE_JSONSL_CALLBACK(trailer_pop_callback); + +/* conform to void */ +#define JOBJ_RESPONSE_ROOT (void*)1 +#define JOBJ_ROWSET (void*)2 + +#define NORMALIZE_OFFSETS(buf, len) buf++; len--; + +#define buffer_append lcb_string_append + +/** + * Gets a buffer, given an (absolute) position offset. + * It will try to get a buffer of size desired. The actual size is + * returned in 'actual' (and may be less than desired, maybe even 0) + */ +static const char * +get_buffer_region(lcbjsp_PARSER *ctx, size_t pos, size_t desired, size_t *actual) +{ + const char *ret = ctx->current_buf.base + pos - ctx->min_pos; + const char *end = ctx->current_buf.base + ctx->current_buf.nused; + *actual = end - ret; + + if (ctx->min_pos > pos) { + /* swallowed */ + *actual = 0; + return NULL; + } + + assert(ret < end); + if (desired < *actual) { + *actual = desired; + } + return ret; +} + +/** + * Consolidate the meta data into a single parsable string.. + */ +static void +combine_meta(lcbjsp_PARSER *ctx) +{ + const char *meta_trailer; + size_t ntrailer; + + if (ctx->meta_complete) { + return; + } + + assert(ctx->header_len <= ctx->meta_buf.nused); + + /* Adjust the length for the first portion */ + ctx->meta_buf.nused = ctx->header_len; + + /* Append any trailing data */ + meta_trailer = get_buffer_region(ctx, ctx->last_row_endpos, -1, &ntrailer); + + buffer_append(&ctx->meta_buf, meta_trailer, ntrailer); + ctx->meta_complete = 1; +} + +static lcbjsp_PARSER * +get_ctx(jsonsl_t jsn) +{ + return reinterpret_cast(jsn->data); +} + +static void +meta_header_complete_callback(jsonsl_t jsn, jsonsl_action_t action, + struct jsonsl_state_st *state, const jsonsl_char_t *at) +{ + + lcbjsp_PARSER *ctx = get_ctx(jsn); + buffer_append(&ctx->meta_buf, ctx->current_buf.base, state->pos_begin); + + ctx->header_len = state->pos_begin; + jsn->action_callback_PUSH = NULL; + + (void)action; (void)at; +} + + +static void +row_pop_callback(jsonsl_t jsn, jsonsl_action_t action, + struct jsonsl_state_st *state, const jsonsl_char_t *at) +{ + lcbjsp_ROW dt = { LCBJSP_TYPE_ROW }; + lcbjsp_PARSER *ctx = get_ctx(jsn); + const char *rowbuf; + size_t szdummy; + + if (ctx->have_error) { + return; + } + + ctx->keep_pos = jsn->pos; + ctx->last_row_endpos = jsn->pos; + + if (state->data == JOBJ_ROWSET) { + /** The closing ] of "rows" : [ ... ] */ + jsn->action_callback_POP = trailer_pop_callback; + jsn->action_callback_PUSH = NULL; + if (ctx->rowcount == 0) { + /* Emulate what meta_header_complete callback does. */ + + /* While the entire meta is available to us, the _closing_ part + * of the meta is handled in a different callback. */ + buffer_append(&ctx->meta_buf, ctx->current_buf.base, jsn->pos); + ctx->header_len = jsn->pos; + } + return; + } + + ctx->rowcount++; + + if (!ctx->callback) { + return; + } + + rowbuf = get_buffer_region(ctx, state->pos_begin, -1, &szdummy); + dt.type = LCBJSP_TYPE_ROW; + dt.row.iov_base = (void *)rowbuf; + dt.row.iov_len = jsn->pos - state->pos_begin + 1; + ctx->callback(ctx, &dt); + + (void)action; (void)at; +} + +static int +parse_error_callback(jsonsl_t jsn, jsonsl_error_t error, + struct jsonsl_state_st *state, jsonsl_char_t *at) +{ + lcbjsp_PARSER *ctx = get_ctx(jsn); + lcbjsp_ROW dt; + + ctx->have_error = 1; + + /* invoke the callback */ + dt.type = LCBJSP_TYPE_ERROR; + dt.row.iov_base = ctx->current_buf.base; + dt.row.iov_len = ctx->current_buf.nused; + ctx->callback(ctx, &dt); + + (void)error; (void)state; (void)at; + + return 0; +} + +static void +trailer_pop_callback(jsonsl_t jsn, jsonsl_action_t action, + struct jsonsl_state_st *state, const jsonsl_char_t *at) +{ + lcbjsp_PARSER *ctx = get_ctx(jsn); + lcbjsp_ROW dt; + if (state->data != JOBJ_RESPONSE_ROOT) { + return; + } + combine_meta(ctx); + dt.row.iov_base = ctx->meta_buf.base; + dt.row.iov_len = ctx->meta_buf.nused; + dt.type = LCBJSP_TYPE_COMPLETE; + ctx->callback(ctx, &dt); + + (void)action; (void)at; +} + +static void +initial_pop_callback(jsonsl_t jsn, jsonsl_action_t action, + struct jsonsl_state_st *state, const jsonsl_char_t *at) +{ + lcbjsp_PARSER *ctx = get_ctx(jsn); + char *key; + unsigned long len; + + if (ctx->have_error) { + return; + } + if (JSONSL_STATE_IS_CONTAINER(state)) { + return; + } + if (state->type != JSONSL_T_HKEY) { + return; + } + + key = ctx->current_buf.base + state->pos_begin; + len = jsn->pos - state->pos_begin; + NORMALIZE_OFFSETS(key, len); + + lcb_string_clear(&ctx->last_hk); + buffer_append(&ctx->last_hk, key, len); + + (void)action; (void)at; +} + +/** + * This is called for the first few tokens, where we are still searching + * for the row set. + */ +static void +initial_push_callback(jsonsl_t jsn, jsonsl_action_t action, + struct jsonsl_state_st *state, const jsonsl_char_t *at) +{ + lcbjsp_PARSER *ctx = (lcbjsp_PARSER*)jsn->data; + jsonsl_jpr_match_t match = JSONSL_MATCH_UNKNOWN; + + if (ctx->have_error) { + return; + } + + if (JSONSL_STATE_IS_CONTAINER(state)) { + jsonsl_jpr_match_state(jsn, state, ctx->last_hk.base, ctx->last_hk.nused, + &match); + } + + lcb_string_clear(&ctx->last_hk); + + if (ctx->initialized == 0) { + if (state->type != JSONSL_T_OBJECT) { + ctx->have_error = 1; + return; + } + + if (match != JSONSL_MATCH_POSSIBLE) { + ctx->have_error = 1; + return; + } + /* tag the state */ + state->data = JOBJ_RESPONSE_ROOT; + ctx->initialized = 1; + return; + } + + if (state->type == JSONSL_T_LIST && match == JSONSL_MATCH_POSSIBLE) { + /* we have a match, e.g. "rows:[]" */ + jsn->action_callback_POP = row_pop_callback; + jsn->action_callback_PUSH = meta_header_complete_callback; + state->data = JOBJ_ROWSET; + } + + (void)action; /* always PUSH */ + (void)at; +} + +static void +feed_data(lcbjsp_PARSER *ctx, const char *data, size_t ndata) +{ + size_t old_len = ctx->current_buf.nused; + + buffer_append(&ctx->current_buf, data, ndata); + jsonsl_feed(ctx->jsn, ctx->current_buf.base + old_len, ndata); + + /* Do we need to cut off some bytes? */ + if (ctx->keep_pos > ctx->min_pos) { + size_t lentmp, diff = ctx->keep_pos - ctx->min_pos; + const char *buf = get_buffer_region(ctx, ctx->keep_pos, -1, &lentmp); + memmove(ctx->current_buf.base, buf, ctx->current_buf.nused - diff); + ctx->current_buf.nused -= diff; + } + + ctx->min_pos = ctx->keep_pos; +} + +/* Non-static wrapper */ +void +lcbjsp_feed(lcbjsp_PARSER *ctx, const char *data, size_t ndata) +{ + feed_data(ctx, data, ndata); +} + +lcbjsp_PARSER* +lcbjsp_create(int mode) +{ + lcbjsp_PARSER *ctx; + jsonsl_error_t err; + + ctx = reinterpret_cast(calloc(1, sizeof(*ctx))); + ctx->jsn = jsonsl_new(512); + ctx->mode = mode; + ctx->cxx_data = new Json::Value(); + + if (ctx->mode == LCBJSP_MODE_VIEWS) { + ctx->jpr = jsonsl_jpr_new("/rows/^", &err); + } else if (ctx->mode == LCBJSP_MODE_N1QL) { + ctx->jpr = jsonsl_jpr_new("/results/^", &err); + } else { + ctx->jpr = jsonsl_jpr_new("/hits/^", &err); + } + ctx->jsn_rdetails = jsonsl_new(32); + + lcb_string_init(&ctx->meta_buf); + lcb_string_init(&ctx->current_buf); + lcb_string_init(&ctx->last_hk); + + if (!ctx->jpr) { abort(); } + if (!ctx->jsn_rdetails) { abort(); } + + jsonsl_jpr_match_state_init(ctx->jsn, &ctx->jpr, 1); + assert(ctx->jsn_rdetails); + + lcbjsp_reset(ctx); + assert(ctx->jsn_rdetails); + return ctx; +} + +void +lcbjsp_get_postmortem(const lcbjsp_PARSER *v, lcb_IOV *out) +{ + if (v->meta_complete) { + out->iov_base = v->meta_buf.base; + out->iov_len = v->meta_buf.nused; + } else { + out->iov_base = v->current_buf.base; + out->iov_len = v->current_buf.nused; + } +} + +void +lcbjsp_reset(lcbjsp_PARSER* ctx) +{ + /** + * We create a copy, and set its relevant fields. All other + * fields are zeroed implicitly. Then we copy the object back. + */ + jsonsl_reset(ctx->jsn); + jsonsl_reset(ctx->jsn_rdetails); + + lcb_string_clear(&ctx->current_buf); + lcb_string_clear(&ctx->meta_buf); + lcb_string_clear(&ctx->last_hk); + + /* Initially all callbacks are enabled so that we can search for the + * rows array. */ + ctx->jsn->action_callback_POP = initial_pop_callback; + ctx->jsn->action_callback_PUSH = initial_push_callback; + ctx->jsn->error_callback = parse_error_callback; + ctx->jsn->max_callback_level = 4; + ctx->jsn->data = ctx; + jsonsl_enable_all_callbacks(ctx->jsn); + + ctx->have_error = 0; + ctx->initialized = 0; + ctx->meta_complete = 0; + ctx->rowcount = 0; + ctx->min_pos = 0; + ctx->keep_pos = 0; + ctx->header_len = 0; + ctx->last_row_endpos = 0; + reinterpret_cast(ctx->cxx_data)->clear(); +} + +void +lcbjsp_free(lcbjsp_PARSER *ctx) +{ + jsonsl_jpr_match_state_cleanup(ctx->jsn); + jsonsl_destroy(ctx->jsn); + jsonsl_destroy(ctx->jsn_rdetails); + jsonsl_jpr_destroy(ctx->jpr); + + lcb_string_release(&ctx->current_buf); + lcb_string_release(&ctx->meta_buf); + lcb_string_release(&ctx->last_hk); + delete reinterpret_cast(ctx->cxx_data); + + free(ctx); +} + +typedef struct { + const char *root; + lcb_IOV *next_iov; + lcbjsp_ROW *datum; + lcbjsp_PARSER *parent; +} miniparse_ctx; + +static void +parse_json_docid(lcb_IOV* iov, lcbjsp_PARSER *parent) +{ + Json::Reader r; + const char *s = static_cast(iov->iov_base); + const char *s_end = s + iov->iov_len; + Json::Value *jvp = reinterpret_cast(parent->cxx_data); + bool rv = r.parse(s, s_end, *jvp); + if (!rv) { + // fprintf(stderr, "libcouchbase: Failed to parse document ID as JSON!\n"); + return; + } + + s = NULL; + s_end = NULL; + + assert(jvp->isString()); + + // Re-use s and s_end values for the string value itself + if (!jvp->getString(&s, &s_end)) { + // fprintf(stderr, "libcouchbase: couldn't get string value!\n"); + iov->iov_base = NULL; + iov->iov_len = 0; + } + iov->iov_base = const_cast(s); + iov->iov_len = s_end - s; +} + +static void +miniparse_callback(jsonsl_t jsn, jsonsl_action_t action, + struct jsonsl_state_st *state, const jsonsl_char_t *at) +{ + miniparse_ctx *ctx = reinterpret_cast(jsn->data); + lcb_IOV *iov; + + if (state->level == 1) { + return; + } + + /* Is a hashkey? */ + if (state->type == JSONSL_T_HKEY) { + size_t nhk = state->pos_cur - state->pos_begin; + + nhk--; + + #define IS_ROWFIELD(s) \ + (nhk == sizeof(s)-1 && !strncmp(s, at- (sizeof(s)-1) , sizeof(s)-1) ) + + if (IS_ROWFIELD("id")) { + /* "id" */ + ctx->next_iov = &ctx->datum->docid; + } else if (IS_ROWFIELD("key")) { + /* "key" */ + ctx->next_iov = &ctx->datum->key; + } else if (IS_ROWFIELD("value")) { + /* "value" */ + ctx->next_iov = &ctx->datum->value; + } else if (IS_ROWFIELD("geometry")) { + ctx->next_iov = &ctx->datum->geo; + } else { + ctx->next_iov = NULL; + } + #undef IS_ROWFIELD + return; + } + + if (ctx->next_iov == NULL) { + return; + } + + iov = ctx->next_iov; + + if (JSONSL_STATE_IS_CONTAINER(state)) { + iov->iov_base = (void *) (ctx->root + state->pos_begin); + iov->iov_len = (jsn->pos - state->pos_begin) + 1; + } else if (iov == &ctx->datum->docid) { + if (state->nescapes) { + iov->iov_base = (void *) (ctx->root + state->pos_begin); + iov->iov_len = (state->pos_cur - state->pos_begin) + 1; + parse_json_docid(iov, ctx->parent); + } else { + iov->iov_base = (void *) (ctx->root + state->pos_begin + 1); + iov->iov_len = (state->pos_cur - state->pos_begin) - 1; + } + } else { + iov->iov_base = (void *) (ctx->root + state->pos_begin); + iov->iov_len = state->pos_cur - state->pos_begin; + if (state->type == JSONSL_T_STRING) { + iov->iov_len++; + } + } + (void)at; (void)action; +} + +void +lcbjsp_parse_viewrow(lcbjsp_PARSER *vp, lcbjsp_ROW *vr) +{ + miniparse_ctx ctx = { NULL }; + ctx.datum = vr; + ctx.root = static_cast(vr->row.iov_base); + ctx.parent = vp; + + jsonsl_reset(vp->jsn_rdetails); + + jsonsl_enable_all_callbacks(vp->jsn_rdetails); + vp->jsn_rdetails->max_callback_level = 3; + vp->jsn_rdetails->action_callback_POP = miniparse_callback; + vp->jsn_rdetails->data = &ctx; + + jsonsl_feed(vp->jsn_rdetails, + static_cast(vr->row.iov_base), vr->row.iov_len); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/jsparse/parser.h b/couchbase-sys/libcouchbase-2.7.0/src/jsparse/parser.h new file mode 100644 index 00000000..bdaa536c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/jsparse/parser.h @@ -0,0 +1,173 @@ +/** + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +#ifndef LCB_VIEWROW_H_ +#define LCB_VIEWROW_H_ + +#include +#include +#include "contrib/jsonsl/jsonsl.h" +#include "simplestring.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct lcbvrow_PARSER_st lcbjsp_PARSER; + +typedef enum { + LCBJSP_MODE_VIEWS, + LCBJSP_MODE_N1QL, + LCBJSP_MODE_FTS +} lcbjsp_MODE; + +typedef enum { + /**This is a row of view data. You can parse this as JSON from your + * favorite decoder/converter */ + LCBJSP_TYPE_ROW, + + /** + * All the rows have been returned. In this case, the data is the 'meta'. + * This is a valid JSON payload which was returned from the server. + * The "rows" : [] array will be empty. + */ + LCBJSP_TYPE_COMPLETE, + + /** + * A JSON parse error occured. The payload will contain string data. This + * may be JSON (but this is not likely). + * The callback will be delivered twice. First when the error is noticed, + * and second at the end (instead of a COMPLETE callback) + */ + LCBJSP_TYPE_ERROR +} lcbjsp_ROWTYPE; + + +typedef struct { + lcbjsp_ROWTYPE type; /**< The type of data encapsulated */ + lcb_IOV docid; + lcb_IOV key; + lcb_IOV value; + lcb_IOV row; + lcb_IOV geo; +} lcbjsp_ROW; + +typedef void (*lcbjsp_CALLBACK)(lcbjsp_PARSER*,const lcbjsp_ROW*); + +struct lcbvrow_PARSER_st { + jsonsl_t jsn; /**< Parser for the row itself */ + jsonsl_t jsn_rdetails; /**< Parser for the row details */ + jsonsl_jpr_t jpr; /**< jsonpointer match object */ + lcb_string meta_buf; /**< String containing the skeleton (outer layer) */ + lcb_string current_buf; /**< Scratch/read buffer */ + lcb_string last_hk; /**< Last hashkey */ + + lcb_U8 mode; + + /* flags. This should be an int with a bunch of constant flags */ + lcb_U8 have_error; + lcb_U8 initialized; + lcb_U8 meta_complete; + unsigned rowcount; + + /* absolute position offset corresponding to the first byte in current_buf */ + size_t min_pos; + + /* minimum (absolute) position to keep */ + size_t keep_pos; + + /** + * size of the metadata header chunk (i.e. everything until the opening + * bracket of "rows" [ + */ + size_t header_len; + + /** + * Position of last row returned. If there are no subsequent rows, this + * signals the beginning of the metadata trailer + */ + size_t last_row_endpos; + + void *data; + + /** + * std::string to contain parsed document ID. + */ + void *cxx_data; + + /* callback to invoke */ + lcbjsp_CALLBACK callback; +}; + +/** + * Creates a new vrow context object. + * You must set callbacks on this object if you wish it to be useful. + * You must feed it data (calling vrow_feed) as well. The data may be fed + * in chunks and callbacks will be invoked as each row is read. + */ +lcbjsp_PARSER* +lcbjsp_create(int); + +/** + * Resets the context to a pristine state. Callbacks and cookies are kept. + * This may be more efficient than allocating/freeing a context each time + * (as this can be expensive with the jsonsl structures) + */ +void +lcbjsp_reset(lcbjsp_PARSER *ctx); + +/** + * Frees a vrow object created by vrow_create + */ +void +lcbjsp_free(lcbjsp_PARSER *ctx); + +/** + * Feeds data into the vrow. The callback may be invoked multiple times + * in this function. In the context of normal lcb usage, this will typically + * be invoked from within an http_data_callback. + */ +void +lcbjsp_feed(lcbjsp_PARSER *ctx, const char *data, size_t ndata); + +/** + * Parse the row buffer into its constituent parts. This should be called + * if you want to split the row into its basic 'docid', 'key' and 'value' + * fields + * @param vp The parser to use + * @param vr The row to parse. This assumes the row's "row" field is properly + * set. + */ +void +lcbjsp_parse_viewrow(lcbjsp_PARSER *vp, lcbjsp_ROW *vr); + +/** + * Get the raw contents of the current buffer. This can be used to debug errors. + * + * Note that the buffer may be partial or malformed or otherwise unsuitable + * for structured inspection, but may help human observers debug problems. + * + * @param v The parser + * @param out The iov structure to contain the buffer/offset + */ +void +lcbjsp_get_postmortem(const lcbjsp_PARSER *v, lcb_IOV *out); + +#ifdef __cplusplus +} +#endif + +#endif /* LCB_VIEWROW_H_ */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/lcbht/lcbht.c b/couchbase-sys/libcouchbase-2.7.0/src/lcbht/lcbht.c new file mode 100644 index 00000000..b42621d8 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/lcbht/lcbht.c @@ -0,0 +1,282 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "lcbht.h" +#include "sllist-inl.h" +#include "contrib/http_parser/http_parser.h" +#include "settings.h" +enum last_call_type { + CB_NONE, + CB_HDR_KEY, + CB_HDR_VALUE, + CB_HDR_DONE, + CB_BODY, + CB_MSG_DONE +}; + +struct lcbht_PARSER { + http_parser parser; + lcbht_RESPONSE resp; /**< Current response being processed */ + lcb_settings *settings; + enum last_call_type lastcall; + + /* this stuff for parse_ex() */ + const char *last_body; + unsigned last_bodylen; + char paused; + char is_ex; +}; + +static int +on_hdr_key(http_parser *pb, const char *s, size_t n) +{ + lcbht_pPARSER p = (void *)pb; + lcbht_MIMEHDR *hdr; + lcbht_RESPONSE *resp = &p->resp; + + if (p->lastcall != CB_HDR_KEY) { + /* new key */ + hdr = calloc(1, sizeof *hdr); + sllist_append(&resp->headers, &hdr->slnode); + lcb_string_init(&hdr->buf_); + } else { + hdr = SLLIST_ITEM(resp->headers.last, lcbht_MIMEHDR, slnode); + } + + /* append key data */ + lcb_string_append(&hdr->buf_, s, n); + p->lastcall = CB_HDR_KEY; + return 0; +} + +static int +on_hdr_value(http_parser *pb, const char *s, size_t n) +{ + lcbht_pPARSER p = (void *)pb; + lcbht_MIMEHDR *hdr; + lcbht_RESPONSE *resp = &p->resp; + + hdr = SLLIST_ITEM(resp->headers.last, lcbht_MIMEHDR, slnode); + if (p->lastcall == CB_HDR_KEY) { + lcb_string_appendz(&hdr->buf_, ":"); + } + lcb_string_append(&hdr->buf_, s, n); + p->lastcall = CB_HDR_VALUE; + return 0; +} + +static int +on_hdr_done(http_parser *pb) +{ + lcbht_pPARSER p = (void *)pb; + lcbht_RESPONSE *resp = &p->resp; + sllist_iterator iter; + resp->state |= LCBHT_S_HTSTATUS|LCBHT_S_HEADER; + + /* extract the status */ + resp->status = pb->status_code; + p->lastcall = CB_HDR_DONE; + + /** Iterate through all the headers and do proper formatting */ + SLLIST_ITERFOR(&resp->headers, &iter) { + char *delim; + lcbht_MIMEHDR *hdr = SLLIST_ITEM(iter.cur, lcbht_MIMEHDR, slnode); + + delim = strstr(hdr->buf_.base, ":"); + hdr->key = hdr->buf_.base; + + if (delim) { + *delim = '\0'; + hdr->value = delim + 1; + } else { + hdr->value = ""; + } + } + return 0; +} + +static int +on_body(http_parser *pb, const char *s, size_t n) +{ + lcbht_pPARSER p = (void *)pb; + lcbht_RESPONSE *resp = &p->resp; + if (p->is_ex) { + p->last_body = s; + p->last_bodylen = n; + p->paused = 1; + _lcb_http_parser_pause(pb, 1); + } else { + lcb_string_append(&resp->body, s, n); + } + + p->lastcall = CB_BODY; + resp->state |= LCBHT_S_BODY; + return 0; +} + +static int +on_msg_done(http_parser *pb) +{ + lcbht_pPARSER p = (void *)pb; + lcbht_RESPONSE *resp = &p->resp; + resp->state |= LCBHT_S_DONE; + return 0; +} + + +static struct http_parser_settings Parser_Settings = { + NULL, /* msg_begin */ + NULL, /* on_url */ + on_hdr_key, + on_hdr_value, + on_hdr_done, + on_body, + on_msg_done +}; + +lcbht_pPARSER +lcbht_new(lcb_settings *settings) +{ + lcbht_pPARSER ret = calloc(1, sizeof *ret); + lcbht_reset(ret); + ret->settings = settings; + lcb_settings_ref(settings); + return ret; +} + +void +lcbht_free(lcbht_pPARSER parser) +{ + lcbht_clear_response(&parser->resp); + lcb_settings_unref(parser->settings); + free(parser); +} + +lcbht_RESPSTATE +lcbht_parse(lcbht_pPARSER parser, const void *data, unsigned ndata) +{ + size_t nb; + parser->is_ex = 0; + nb = _lcb_http_parser_execute(&parser->parser, &Parser_Settings, data, ndata); + if (nb != ndata) { + parser->resp.state |= LCBHT_S_ERROR; + } + return parser->resp.state; +} + +lcbht_RESPSTATE +lcbht_parse_ex(lcbht_pPARSER parser, const void *data, unsigned ndata, + unsigned *nused, unsigned *nbody, const char **pbody) +{ + size_t nb; + parser->is_ex = 1; + nb = _lcb_http_parser_execute(&parser->parser, &Parser_Settings, data, ndata); + if (nb != ndata) { + if (parser->paused) { + _lcb_http_parser_pause(&parser->parser, 0); + parser->paused = 0; + } else { + parser->resp.state |= LCBHT_S_ERROR; + return parser->resp.state; + } + } + + *nused = nb; + *nbody = parser->last_bodylen; + *pbody = parser->last_body; + + parser->last_body = NULL; + parser->last_bodylen = 0; + return parser->resp.state; +} + +lcbht_RESPONSE * +lcbht_get_response(lcbht_pPARSER parser) +{ + return &parser->resp; +} + +int +lcbht_can_keepalive(lcbht_pPARSER parser) +{ + if (!(parser->resp.state & LCBHT_S_DONE)) { + return 0; + } + + if (parser->resp.state & LCBHT_S_ERROR) { + return 0; + } + + return _lcb_http_should_keep_alive(&parser->parser); +} + +void +lcbht_clear_response(lcbht_RESPONSE *resp) +{ + sllist_iterator iter; + SLLIST_ITERFOR(&resp->headers, &iter) { + lcbht_MIMEHDR *hdr = SLLIST_ITEM(iter.cur, lcbht_MIMEHDR, slnode); + lcb_string_release(&hdr->buf_); + sllist_iter_remove(&resp->headers, &iter); + free(hdr); + } + lcb_string_release(&resp->body); + resp->state = 0; + resp->status = 0; +} + +void +lcbht_reset(lcbht_pPARSER parser) +{ + lcbht_clear_response(&parser->resp); + _lcb_http_parser_init(&parser->parser, HTTP_RESPONSE); +} + +const char * +lcbht_get_resphdr(const lcbht_RESPONSE *resp, const char *key) +{ + sllist_node *curnode; + SLLIST_ITERBASIC(&resp->headers, curnode) { + lcbht_MIMEHDR *hdr = SLLIST_ITEM(curnode, lcbht_MIMEHDR, slnode); + if (!strcmp(hdr->key, key)) { + return hdr->value; + } + } + return NULL; +} + +char ** +lcbht_make_resphdrlist(lcbht_RESPONSE *response) +{ + char **ret; + unsigned nhdrs = 0; + unsigned curix = 0; + sllist_node *curnode; + SLLIST_ITERBASIC(&response->headers, curnode) { + nhdrs++; + } + ret = malloc(sizeof(*ret) * (nhdrs + 1) * 2); + + SLLIST_ITERBASIC(&response->headers, curnode) { + lcbht_MIMEHDR *hdr = SLLIST_ITEM(curnode, lcbht_MIMEHDR, slnode); + ret[curix++] = strdup(hdr->key); + ret[curix++] = strdup(hdr->value); + } + + ret[curix] = NULL; + return ret; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/lcbht/lcbht.h b/couchbase-sys/libcouchbase-2.7.0/src/lcbht/lcbht.h new file mode 100644 index 00000000..d38470d9 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/lcbht/lcbht.h @@ -0,0 +1,199 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_HTTP_H +#define LCB_HTTP_H + +#include +#include "simplestring.h" +#include "sllist.h" +struct lcb_settings_st; + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @file + * HTTP Response parsing. + * + * This file provides HTTP/1.0 compatible response parsing semantics, supporting + * the Content-Length header. + * + * Specifically this may be used to parse incoming HTTP streams into a single + * body. + */ + +/** Response state */ +typedef enum { + LCBHT_S_HTSTATUS = 1 << 0, /**< Have HTTP status */ + LCBHT_S_HEADER = 1 << 1, /**< Have HTTP header */ + LCBHT_S_BODY = 1 << 2, /**< Have HTTP body */ + LCBHT_S_DONE = 1 << 3, /**< Have a full message */ + + /**Have a parse error. Note this is not the same as a HTTP error */ + LCBHT_S_ERROR = 1 << 4 +} lcbht_RESPSTATE; + +typedef struct { + sllist_node slnode; /**< Next header in list */ + const char *key; + const char *value; + lcb_string buf_; /**< Storage for the key and value */ +} lcbht_MIMEHDR; + +typedef struct { + unsigned short status; /**< HTTP Status code */ + lcbht_RESPSTATE state; + sllist_root headers; /**< List of response headers */ + lcb_string body; /**< Body */ +} lcbht_RESPONSE; + +typedef struct lcbht_PARSER *lcbht_pPARSER; + +/** + * Initialize the parser object + * @param settings the settings structure used for logging + * @return a new parser object + */ +lcbht_pPARSER +lcbht_new(struct lcb_settings_st *settings); + +/** Free the parser object */ +void +lcbht_free(lcbht_pPARSER); + +void +lcbht_reset(lcbht_pPARSER); + +/** + * Parse incoming data into a message + * @param parser The parser + * @param data Pointer to new data + * @param ndata Size of the data + * + * @return The current state of the parser. If `state & LCBHT_S_DONE` then + * the current response should be handled before continuing. + * If `state & LCBHT_S_ERROR` then there was an error parsing the contents + * as it violated the HTTP protocol. + */ +lcbht_RESPSTATE +lcbht_parse(lcbht_pPARSER parser, const void *data, unsigned ndata); + +/** + * Parse incoming data without buffering + * @param parser The parser to use + * @param data The data to parse + * @param ndata Length of the data + * @param[out] nused How much of the data was actually consumed + * @param[out] nbody Size of the body pointer + * @param[out] pbody a pointer for the body + * + * @return See lcbht_set_bufmode for the meaning of this value + * + * @note It is not an error if `pbody` is NULL. It may mean that the parse state + * is still within the headers and there is no body to parse yet. + * + * This function is intended to be used in a loop, until there is no input + * remaining. The use of the `nused` pointer is to determine by how much the + * `data` pointer should be incremented (and the `ndata` decremented) for the + * next call. When this function returns with a non-error status, `pbody` + * will contain a pointer to a buffer of data (but see note above) which can + * then be processed by the application. + * + * @code{.c} + * char **body, *input; + * unsigned inlen = get_input_len(), nused, bodylen; + * lcbht_RESPSTATE res; + * do { + * res = lcbht_parse_ex(parser, input, inlen, &nused, &nbody, &body); + * if (res & LCBHT_S_ERROR) { + * // handle error + * break; + * } + * if (nbody) { + * // handle body + * } + * input += nused; + * inlen -= nused; + * } while (!(res & LCBHT_S_DONE)); + * @endcode + */ +lcbht_RESPSTATE +lcbht_parse_ex(lcbht_pPARSER parser, const void *data, unsigned ndata, + unsigned *nused, unsigned *nbody, const char **pbody); + + +/** + * Obtain the current response being processed. + * @param parser The parser + * @return a pointer to a response object. The response object is only valid + * until the next call into another parser API + */ +lcbht_RESPONSE * +lcbht_get_response(lcbht_pPARSER parser); + +/** + * Determine whether HTTP/1.1 keepalive is enabled on the connection + * @param parser The parser + * @return true if keepalive is enabled, false otherwise. + */ +int +lcbht_can_keepalive(lcbht_pPARSER parser); + +/** + * Clear the response object + * @param resp the response to clear + */ +void +lcbht_clear_response(lcbht_RESPONSE *resp); + +/** + * Get a header value for a key + * @param response The response + * @param key The key to look up + * @return A string containing the value. If the header has no value then the + * empty string will be returned. If the header does not exist NULL will be + * returned. + */ +const char * +lcbht_get_resphdr(const lcbht_RESPONSE *response, const char *key); + +/** + * Return a list of headers + * @param response The response + * @return A list of headers. Iterate over this value like so: + * @code{.c} + * char **hdrlist = lcbht_make_resphdrlist(response); + * for (char **cur = hdrlist; *cur; cur += 2) { + * char *key = cur[0]; + * char *value = cur[1]; + * // do something + * free(key); + * free(value); + * } + * free(hdrlist); + * @endcode + */ +char ** +lcbht_make_resphdrlist(lcbht_RESPONSE *response); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/lcbio/connect.c b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/connect.c new file mode 100644 index 00000000..ac9c3384 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/connect.c @@ -0,0 +1,557 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include "connect.h" +#include "ioutils.h" +#include "iotable.h" +#include "settings.h" +#include "timer-ng.h" +#include + +/* win32 lacks EAI_SYSTEM */ +#ifndef EAI_SYSTEM +#define EAI_SYSTEM 0 +#endif +#define LOGARGS(conn, lvl) conn->settings, "connection", LCB_LOG_##lvl, __FILE__, __LINE__ +static const lcb_host_t *get_loghost(lcbio_SOCKET *s) { + static lcb_host_t host = { "NOHOST", "NOPORT" }; + if (!s) { return &host; } + if (!s->info) { return &host; } + return &s->info->ep; +} + +/** Format string arguments for %p%s:%s */ +#define CSLOGID(sock) get_loghost(sock)->host, get_loghost(sock)->port, (void*)s +#define CSLOGFMT "<%s:%s> (SOCK=%p) " + +typedef enum { + CS_PENDING = 0, + CS_CANCELLED, + CS_TIMEDOUT, + CS_CONNECTED, + CS_ERROR +} connect_state; + +typedef struct lcbio_CONNSTART { + lcbio_CONNDONE_cb handler; + lcbio_SOCKET *sock; + lcbio_OSERR syserr; + void *arg; + void *event; + short ev_active; /* whether the event pointer is active (Event only) */ + short in_uhandler; /* Whether we're inside the user-defined handler */ + struct addrinfo *ai_root; + struct addrinfo *ai; + connect_state state; + lcb_error_t pending; + lcbio_ASYNC *async; + char *hoststr; +} lcbio_CONNSTART; + +static void +cs_unwatch(lcbio_CONNSTART *cs) +{ + lcbio_SOCKET *s = cs->sock; + if (s && cs->ev_active) { + lcb_assert(s->u.fd != INVALID_SOCKET); + IOT_V0EV(s->io).cancel(IOT_ARG(s->io), s->u.fd, cs->event); + cs->ev_active = 0; + } +} + +/** + * Handler invoked to deliver final status for a connection. This will invoke + * the user supplied callback with the relevant status (if it has not been + * cancelled) and then free the CONNSTART object. + */ +static void +cs_handler(void *cookie) +{ + lcbio_CONNSTART *cs = cookie; + lcb_error_t err; + lcbio_SOCKET *s = cs->sock; + + if (s && cs->event) { + cs_unwatch(cs); + IOT_V0EV(s->io).destroy(IOT_ARG(s->io), cs->event); + } + + if (cs->state == CS_PENDING) { + /* state was not changed since initial scheduling */ + err = LCB_ETIMEDOUT; + } else if (cs->state == CS_CONNECTED) { + /* clear pending error */ + err = LCB_SUCCESS; + } else { + if (s != NULL && cs->pending == LCB_CONNECT_ERROR) { + err = lcbio_mklcberr(cs->syserr, s->settings); + } else { + err = cs->pending; + } + } + + if (cs->state == CS_CANCELLED) { + /* ignore everything. Clean up resources */ + goto GT_DTOR; + } + + if (s) { + lcbio__load_socknames(s); + if (err == LCB_SUCCESS) { + lcb_log(LOGARGS(s, INFO), CSLOGFMT "Connected ", CSLOGID(s)); + + if (s->settings->tcp_nodelay) { + lcb_error_t ndrc = lcbio_disable_nagle(s); + if (ndrc != LCB_SUCCESS) { + lcb_log(LOGARGS(s, INFO), CSLOGFMT "Couldn't set TCP_NODELAY", CSLOGID(s)); + } else { + lcb_log(LOGARGS(s, DEBUG), CSLOGFMT "Successfuly set TCP_NODELAY", CSLOGID(s)); + } + } + } else { + lcb_log(LOGARGS(s, ERR), CSLOGFMT "Failed: lcb_err=0x%x, os_errno=%u", CSLOGID(s), err, cs->syserr); + } + } + + /** Handler section */ + cs->in_uhandler = 1; + cs->handler(err == LCB_SUCCESS ? s : NULL, cs->arg, err, cs->syserr); + + GT_DTOR: + if (cs->async) { + lcbio_timer_destroy(cs->async); + } + if (cs->sock) { + lcbio_unref(cs->sock); + } + if (cs->ai_root) { + freeaddrinfo(cs->ai_root); + } + free(cs); +} + +static void +cs_state_signal(lcbio_CONNSTART *cs, connect_state state, lcb_error_t err) +{ + if (cs->state != CS_PENDING) { + /** State already set */ + return; + } + + + if (state == CS_CONNECTED) { + /* clear last errors if we're successful */ + cs->pending = LCB_SUCCESS; + } else if (cs->pending == LCB_SUCCESS) { + /* set error code only if previous code was not a failure */ + cs->pending = err; + } + + cs->state = state; + lcbio_async_signal(cs->async); +} + +/** Cancels and mutes any pending event */ +void +lcbio_connect_cancel(lcbio_pCONNSTART cs) +{ + if (cs->in_uhandler) { + /* already inside user-defined handler */ + return; + } + cs->state = CS_CANCELLED; + cs_handler(cs); +} + + +static int +ensure_sock(lcbio_CONNSTART *cs) +{ + lcbio_SOCKET *s = cs->sock; + lcbio_TABLE *io = s->io; + int errtmp = 0; + + if (cs->ai == NULL) { + return -1; + } + + if (IOT_IS_EVENT(io)) { + if (s->u.fd != INVALID_SOCKET) { + /* already have one? */ + return 0; + } + + while (s->u.fd == INVALID_SOCKET && cs->ai != NULL) { + s->u.fd = lcbio_E_ai2sock(io, &cs->ai, &errtmp); + if (s->u.fd != INVALID_SOCKET) { + return 0; + } + } + } else { + if (s->u.sd) { + return 0; + } + + while (s->u.sd == NULL && cs->ai != NULL) { + s->u.sd = lcbio_C_ai2sock(io, &cs->ai, &errtmp); + if (s->u.sd) { + s->u.sd->lcbconn = (void *) cs->sock; + s->u.sd->parent = IOT_ARG(io); + return 0; + } + } + } + + if (cs->ai == NULL) { + lcbio_mksyserr(IOT_ERRNO(io), &cs->syserr); + return -1; + } + return 0; +} + +static void +destroy_cursock(lcbio_CONNSTART *cs) +{ + lcbio_SOCKET *s = cs->sock; + lcbio_TABLE *iot = s->io; + if (cs->ai) { + cs->ai = cs->ai->ai_next; + } + + if (!cs->ai) { + return; + } + + if (IOT_IS_EVENT(iot)) { + if (cs->ev_active) { + lcb_assert(s->u.fd != INVALID_SOCKET); + IOT_V0EV(iot).cancel(IOT_ARG(iot), s->u.fd, cs->event); + cs->ev_active = 0; + } + IOT_V0IO(iot).close(IOT_ARG(iot), s->u.fd); + s->u.fd = INVALID_SOCKET; + } else { + if (s->u.sd) { + IOT_V1(iot).close(IOT_ARG(iot), s->u.sd); + s->u.sd = NULL; + } + } +} + +static void +E_connect(lcb_socket_t sock, short events, void *arg) +{ + lcbio_CONNSTART *cs = arg; + lcbio_SOCKET *s = cs->sock; + lcbio_TABLE *io = s->io; + int retry_once = 0; + lcbio_CSERR connstatus; + + (void)sock; + + lcb_log(LOGARGS(s, TRACE), CSLOGFMT "Got event handler for new connection", CSLOGID(s)); + + GT_NEXTSOCK: + if (ensure_sock(cs) == -1) { + cs_state_signal(cs, CS_ERROR, LCB_CONNECT_ERROR); + return; + } + + if (events & LCB_ERROR_EVENT) { + socklen_t errlen = sizeof(int); + int sockerr = 0; + lcb_log(LOGARGS(s, TRACE), CSLOGFMT "Received ERROR_EVENT", CSLOGID(s)); + getsockopt(s->u.fd, SOL_SOCKET, SO_ERROR, (char *)&sockerr, &errlen); + lcbio_mksyserr(sockerr, &cs->syserr); + destroy_cursock(cs); + goto GT_NEXTSOCK; + + } else { + int rv = 0; + struct addrinfo *ai = cs->ai; + + GT_CONNECT: + rv = IOT_V0IO(io).connect0( + IOT_ARG(io), s->u.fd, ai->ai_addr, (unsigned)ai->ai_addrlen); + + if (rv == 0) { + cs_unwatch(cs); + cs_state_signal(cs, CS_CONNECTED, LCB_SUCCESS); + return; + } + } + + connstatus = lcbio_mkcserr(IOT_ERRNO(io)); + lcbio_mksyserr(IOT_ERRNO(io), &cs->syserr); + + + switch (connstatus) { + + case LCBIO_CSERR_INTR: + goto GT_CONNECT; + + case LCBIO_CSERR_CONNECTED: + cs_unwatch(cs); + cs_state_signal(cs, CS_CONNECTED, LCB_SUCCESS); + return; + + case LCBIO_CSERR_BUSY: + lcb_log(LOGARGS(s, TRACE), CSLOGFMT "Scheduling asynchronous watch for socket.", CSLOGID(s)); + IOT_V0EV(io).watch( + IOT_ARG(io), s->u.fd, cs->event, LCB_WRITE_EVENT, cs, E_connect); + cs->ev_active = 1; + return; + + case LCBIO_CSERR_EINVAL: + if (!retry_once) { + retry_once = 1; + goto GT_CONNECT; + } + /* fallthrough */ + + case LCBIO_CSERR_EFAIL: + default: + /* close the current socket and try again */ + lcb_log(LOGARGS(s, TRACE), CSLOGFMT "connect() failed. os_error=%d [%s]", CSLOGID(s), IOT_ERRNO(io), strerror(IOT_ERRNO(io))); + destroy_cursock(cs); + goto GT_NEXTSOCK; + } +} + +static void C_connect(lcbio_CONNSTART *cs); + +static void +C_conncb(lcb_sockdata_t *sock, int status) +{ + lcbio_SOCKET *s = (void *)sock->lcbconn; + lcbio_CONNSTART *cs = (void *)s->ctx; + + lcb_log(LOGARGS(s, TRACE), CSLOGFMT "Received completion handler. Status=%d. errno=%d", CSLOGID(s), status, IOT_ERRNO(s->io)); + + if (!--s->refcount) { + lcbio__destroy(s); + return; + } + + if (!status) { + if (cs->state == CS_PENDING) { + cs->state = CS_CONNECTED; + } + cs_handler(cs); + } else { + lcbio_mksyserr(IOT_ERRNO(s->io), &cs->syserr); + destroy_cursock(cs); + C_connect(cs); + } +} + +static void +C_connect(lcbio_CONNSTART *cs) +{ + int rv; + lcbio_SOCKET *s = cs->sock; + int retry_once = 0; + lcbio_CSERR status; + lcbio_TABLE *io = s->io; + + GT_NEXTSOCK: + if (ensure_sock(cs) != 0) { + lcbio_mksyserr(IOT_ERRNO(io), &cs->syserr); + cs_state_signal(cs, CS_ERROR, LCB_CONNECT_ERROR); + return; + } + + GT_CONNECT: + rv = IOT_V1(io).connect(IOT_ARG(io), s->u.sd, cs->ai->ai_addr, + (unsigned)cs->ai->ai_addrlen, C_conncb); + if (rv == 0) { + lcbio_ref(s); + return; + } + + lcbio_mksyserr(IOT_ERRNO(io), &cs->syserr); + status = lcbio_mkcserr(IOT_ERRNO(io)); + switch (status) { + + case LCBIO_CSERR_INTR: + goto GT_CONNECT; + + case LCBIO_CSERR_CONNECTED: + cs_state_signal(cs, CS_CONNECTED, LCB_SUCCESS); + return; + + case LCBIO_CSERR_BUSY: + return; + + case LCBIO_CSERR_EINVAL: + if (!retry_once) { + retry_once = 1; + goto GT_CONNECT; + } + /* fallthrough */ + + case LCBIO_CSERR_EFAIL: + default: + destroy_cursock(cs); + goto GT_NEXTSOCK; + } +} + +struct lcbio_CONNSTART * +lcbio_connect(lcbio_TABLE *iot, lcb_settings *settings, const lcb_host_t *dest, + uint32_t timeout, lcbio_CONNDONE_cb handler, void *arg) +{ + lcbio_SOCKET *s; + lcbio_CONNSTART *ret; + struct addrinfo hints; + int rv; + + s = calloc(1, sizeof(*s)); + ret = calloc(1, sizeof(*ret)); + + /** Initialize the socket first */ + s->io = iot; + s->settings = settings; + s->ctx = ret; + s->refcount = 1; + s->info = calloc(1, sizeof(*s->info)); + s->info->ep = *dest; + lcbio_table_ref(s->io); + lcb_settings_ref(s->settings); + lcb_list_init(&s->protos); + + if (IOT_IS_EVENT(iot)) { + s->u.fd = INVALID_SOCKET; + ret->event = IOT_V0EV(iot).create(IOT_ARG(iot)); + } + + /** Initialize the connstart structure */ + ret->handler = handler; + ret->arg = arg; + ret->sock = s; + ret->async = lcbio_timer_new(iot, ret, cs_handler); + + lcbio_timer_rearm(ret->async, timeout); + lcb_log(LOGARGS(s, INFO), CSLOGFMT "Starting. Timeout=%uus", CSLOGID(s), timeout); + + /** Hostname lookup: */ + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_socktype = SOCK_STREAM; + if (settings->ipv6 == LCB_IPV6_DISABLED) { + hints.ai_family = AF_INET; + } else if (settings->ipv6 == LCB_IPV6_ONLY) { + hints.ai_family = AF_INET6; + } else { + hints.ai_family = AF_UNSPEC; + } + + if ((rv = getaddrinfo(dest->host, dest->port, &hints, &ret->ai_root))) { + const char *errstr = rv != EAI_SYSTEM ? gai_strerror(rv) : ""; + lcb_log(LOGARGS(s, ERR), CSLOGFMT "Couldn't look up %s (%s) [EAI=%d]", CSLOGID(s), dest->host, errstr, rv); + cs_state_signal(ret, CS_ERROR, LCB_UNKNOWN_HOST); + } else { + ret->ai = ret->ai_root; + + /** Figure out how to connect */ + if (IOT_IS_EVENT(iot)) { + E_connect(-1, LCB_WRITE_EVENT, ret); + } else { + C_connect(ret); + } + } + return ret; +} + +lcbio_CONNSTART * +lcbio_connect_hl(lcbio_TABLE *iot, lcb_settings *settings, + hostlist_t hl, int rollover, uint32_t timeout, + lcbio_CONNDONE_cb handler, void *arg) +{ + const lcb_host_t *cur; + unsigned ii, hlmax; + ii = 0; + hlmax = hostlist_size(hl); + + while ( (cur = hostlist_shift_next(hl, rollover)) && ii++ < hlmax) { + lcbio_CONNSTART *ret = lcbio_connect( + iot, settings, cur, timeout, handler, arg); + if (ret) { + return ret; + } + } + + return NULL; +} + +lcbio_SOCKET * +lcbio_wrap_fd(lcbio_pTABLE iot, lcb_settings *settings, lcb_socket_t fd) +{ + lcbio_SOCKET *ret = calloc(1, sizeof(*ret)); + lcbio_CONNDONE_cb *ci = calloc(1, sizeof(*ci)); + + if (ret == NULL || ci == NULL) { + free(ret); + free(ci); + return NULL; + } + + assert(iot->model = LCB_IOMODEL_EVENT); + + lcb_list_init(&ret->protos); + ret->settings = settings; + ret->io = iot; + ret->refcount = 1; + ret->u.fd = fd; + + lcbio_table_ref(ret->io); + lcb_settings_ref(ret->settings); + lcbio__load_socknames(ret); + return ret; +} + +void +lcbio_shutdown(lcbio_SOCKET *s) +{ + lcbio_TABLE *io = s->io; + + lcbio__protoctx_delall(s); + if (IOT_IS_EVENT(io)) { + if (s->u.fd != INVALID_SOCKET) { + IOT_V0IO(io).close(IOT_ARG(io), s->u.fd); + s->u.fd = INVALID_SOCKET; + } + } else { + if (s->u.sd) { + IOT_V1(io).close(IOT_ARG(io), s->u.sd); + s->u.sd = NULL; + } + } +} + +void +lcbio__destroy(lcbio_SOCKET *s) +{ + lcbio_shutdown(s); + if (s->info) { + free(s->info); + } + lcbio_table_unref(s->io); + lcb_settings_unref(s->settings); + free(s); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/lcbio/connect.h b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/connect.h new file mode 100644 index 00000000..d9992fe9 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/connect.h @@ -0,0 +1,364 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCBIO_CONNECTION_H +#define LCBIO_CONNECTION_H +#include +#include "list.h" +#include "logging.h" +#include "settings.h" +#include "hostlist.h" +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * This provides the core socket routines + */ + +/** + * @ingroup lcbio + * @defgroup lcbio-core Socket Module + * @brief Socket routines + * @addtogroup lcbio-core + * @{ + */ + +struct lcbio_CONNSTART; +struct lcbio_MGRREQ; + +/** @brief Pending connection request */ +typedef struct lcbio_CONNSTART *lcbio_pCONNSTART; +typedef struct lcbio_MGRREQ *lcbio_pMGRREQ; +typedef struct lcbio_TABLE *lcbio_pTABLE; +typedef struct lcbio_TIMER *lcbio_pTIMER, *lcbio_pASYNC; + +/** + * A type representing the underlying operating system's error type. On Unix + * this is typically an `int`, while on Windows this is a `DWORD` + */ +#ifdef WIN32 +typedef DWORD lcbio_OSERR; +#else +typedef int lcbio_OSERR; +#endif + +/** @brief Information about a connected socket */ +typedef struct { + unsigned naddr; + struct sockaddr_storage sa_remote; + struct sockaddr_storage sa_local; + lcb_host_t ep; +} lcbio_CONNINFO; + + +/** @brief Core socket structure */ +typedef struct lcbio_SOCKET { + lcbio_pTABLE io; + lcb_settings *settings; + void *ctx; + lcbio_CONNINFO *info; + lcbio_OSERR last_error; /**< last OS error */ + unsigned refcount; /**< refcount on socket */ + union { + lcb_sockdata_t *sd; + lcb_socket_t fd; + } u; + lcb_list_t protos; +} lcbio_SOCKET; + + +/** + * @name Connecting and Destroying a Socket + * @{ + */ + +/** + * Invoked when the connection result is ready + * @param s the socket to use. You should call lcbio_ref() on it. May be NULL + * in the case of an error + * @param arg user provided argument to the lcbio_connect() function + * @param err an error code (if connection is NULL) + * @param syserr the raw errno variable received. + */ +typedef void (*lcbio_CONNDONE_cb) + (lcbio_SOCKET *s, void *arg, lcb_error_t err, lcbio_OSERR syserr); + + +/** + * Schedule a new connection to a remote endpoint. + * + * @param iot I/O table to use. The socket will increment the reference count of + * the table until the socket is destroyed. + * @param settings Settings structure. Used for logging + * @param dest the endpoint to connect to + * @param timeout number of time to wait for connection. The handler will be + * invoked with an error of `LCB_ETIMEDOUT` if a successful connection + * cannot be established in time. + * @param handler a handler to invoke with the result. The handler will always + * be invoked unless the request has been cancelled. You should inspect + * the socket and error code in the handler to see if the connection has + * been successful. + * @param arg the argument passed to the handler + * @return a request handle. The handle may be cancelled (to stop the pending + * connection attempt) before the handler is invoked. + * Once the handler is invoked, the returned handle is considered to + * be invalid; as such the following idiom should be employed: + * + * + * @code{.c} + * struct my_ctx { + * lcbio_SOCKET *sock; + * lcbio_pCONNSTART creq; + * } + * + * static void do_connect(void) { + * my_ctx *ctx; + * ctx->creq = lcbio_connect(iot, settings, dest, tmo, handler, ctx); + * // check errors.. + * } + * + * + * static void handler(lcbio_SOCKET *s, void *arg, lcb_error_t err) { + * my_ctx *ctx = arg; + * ctx->creq = NULL; + * if (!(ctx->sock = s)) { + * ... + * } + * } + * @endcode + */ +lcbio_pCONNSTART +lcbio_connect(lcbio_pTABLE iot, + lcb_settings *settings, + const lcb_host_t *dest, + uint32_t timeout, + lcbio_CONNDONE_cb handler, void *arg); + +/** + * Wraps an existing socket descriptor into an lcbio_SOCKET structure + * @param iot + * @param settings + * @param fd The socket descriptor to wrap. This must refer to a _connected_ + * socket (e.g. via `connect(2)` or `socketpair(2)`. + * @return A new socket object. + */ +lcbio_SOCKET * +lcbio_wrap_fd(lcbio_pTABLE iot, lcb_settings *settings, lcb_socket_t fd); + +/** + * Wraps `lcb_connect()` by traversing a list of hosts. This will cycle through + * each host in the list until a connection has been successful. Currently + * this will not intercept the handler but will catch any hostname lookup + * failures. + * + * @param iot + * @param settings + * @param hl The hostlist to traverse + * @param rollover If the hostlist position is at the end, this boolean parameter + * indicates whether the position should be reset + * @param timeout + * @param handler + * @param arg + * @see lcbio_connect() + */ +lcbio_pCONNSTART +lcbio_connect_hl(lcbio_pTABLE iot, lcb_settings *settings, + hostlist_t hl, int rollover, + uint32_t timeout, lcbio_CONNDONE_cb handler, void *arg); + + +/** + * Cancel a pending connection attempt. Once the attempt is cancelled the + * handler will not be invoked and the CONNSTART object will be invalid. + * @param cs the handle returned from lcbio_connect() + */ +void +lcbio_connect_cancel(lcbio_pCONNSTART cs); + +/** + * Cancel any pending I/O on this socket. Outstanding callbacks for I/O (i.e. + * for completion-based reads and writes) will still be delivered with an error + * code. Outstanding callbacks for event-based I/O will not be invoked. + * + * This function does not modify the reference count of the socket directly + * but will clear any lcbio_PROTOCTX objects attached to it. + */ +void +lcbio_shutdown(lcbio_SOCKET *); + +/** + * Increment the reference count on the socket. When the socket is no longer + * needed, call lcbio_unref(). + */ +#define lcbio_ref(s) (s)->refcount++ + +/** + * Decrement the reference count on the socket. When the reference count hits + * zero, lcbio_shutdown() will be called. + */ +#define lcbio_unref(s) if ( !--(s)->refcount ) { lcbio__destroy(s); } + +/** @} */ + + +/** + * @name Protocol Contexts + * @{ + */ + +typedef enum { + LCBIO_PROTOCTX_SESSINFO = 1, + LCBIO_PROTOCTX_POOL, + LCBIO_PROTOCTX_HOSTINFO, + LCBIO_PROTOCTX_SSL, + LCBIO_PROTOCTX_MAX +} lcbio_PROTOID; + + + +/** + * @brief Protocol-specific data attached to lcbio_SOCKET. + * + * A protocol context is an object which is bound to the actual low level + * socket connection rather than the logical socket owner. This is used for + * resources which operate on the TCP state (such as encryption or authentication) + * or which employ socket reuse (for things such as pooling). + */ +typedef struct lcbio_PROTOCTX { + lcb_list_t ll; + lcbio_PROTOID id; + /** Called when the context is to be removed from the socket */ + void (*dtor)(struct lcbio_PROTOCTX *); +} lcbio_PROTOCTX; + +/** + * Attach an lcbio_PROTOCTX object to the socket. This object will remain + * part of the socket until lcbio_shutdown() is invoked, or the context itself + * is removed explicitly. + * + * @param socket the socket the context should be added to + * @param proto the object to be added. The protocol object should have its + * `id` and `dtor` fields initialized. + */ +void +lcbio_protoctx_add(lcbio_SOCKET *socket, lcbio_PROTOCTX *proto); + +/** + * Retrieve an existing protocol context by its ID + * @param socket The socket to query + * @param id The ID of the context + * @return the context, or NULL if not found + */ +lcbio_PROTOCTX * +lcbio_protoctx_get(lcbio_SOCKET *socket, lcbio_PROTOID id); + +/** + * Remove a protocol context by its ID + * @param socket socket from which to remove + * @param id The id of the context to remove + * @param call_dtor whether the destructor should be invoked + * @return the returned context, or NULL if not found + */ +lcbio_PROTOCTX * +lcbio_protoctx_delid(lcbio_SOCKET *socket, lcbio_PROTOID id, int call_dtor); + +/** + * Delete a protocol context by its pointer. + * @param socket The socket from which the context should be removed + * @param ctx The pointer to remove + * @param call_dtor Whether to invoke the destructor for the lcbio_PROTOCTX + */ +void +lcbio_protoctx_delptr(lcbio_SOCKET *socket, lcbio_PROTOCTX *ctx, int call_dtor); + +/** @private */ +void +lcbio__protoctx_delall(lcbio_SOCKET *s); + +/** @} */ + +/** + * Get the lcb_host_t pointer indicating the endpoint the socket is connected to. + * @param sock The socket + * @return a pointer to the host. + */ +#define lcbio_get_host(sock) (&(sock)->info->ep) + +/** + * @private + * Internal destroy function for when the refcount hits 0 + */ +void +lcbio__destroy(lcbio_SOCKET *s); + +/** + * @name IO Table Functions + * @details + * These functions provide the user-facing API for dealing with the lcbio_TABLE + * structure. These functions only control its handling as an opaque object. + * The definition of the structure may be found in and contains + * more routines for actually using it. + * + * @{ + */ + +/** + * Create a new table based on the input iops structure. The table itself retains + * ownership over the structure and will destroy it once the table itself has + * been destroyed. + * @param io An IOPS structure. See lcb_create_io_ops() + * @return A table with a reference count initialized to 1 + */ +lcbio_pTABLE +lcbio_table_new(lcb_io_opt_t io); + +/** Increment the reference count on the lcbio_TABLE */ +void +lcbio_table_unref(lcbio_pTABLE iot); + +/** Decrement the reference count on the lcbio_TABLE */ +void +lcbio_table_ref(lcbio_pTABLE iot); + +/** @}*/ + +/** @name IO Status Codes + *@{ */ +typedef enum { + LCBIO_COMPLETED = 0, /**< Operation has been completed */ + LCBIO_PENDING, /**< Operation is partially completed */ + LCBIO__SUCCESS_MAX, /**< Status codes higher than this value are errors */ + LCBIO_IOERR, /**< An I/O error has been received */ + LCBIO_INTERR, /**< An internal non-I/O error has been received */ + LCBIO_SHUTDOWN /**< Socket was gracefully closed */ +} lcbio_IOSTATUS; + +#define LCBIO_WFLUSHED LCBIO_COMPLETED +#define LCBIO_CANREAD LCBIO_COMPLETED +#define LCBIO_IS_OK(s) ((s) < LCBIO__SUCCESS_MAX) +/** @} */ + +#ifdef __cplusplus +} +#endif +#endif + +/** + * @} + */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/lcbio/ctx.c b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/ctx.c new file mode 100644 index 00000000..52b9f9d8 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/ctx.c @@ -0,0 +1,611 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ctx.h" +#include "iotable.h" +#include "timer-ng.h" +#include "ioutils.h" +#include +#include + +#define CTX_FD(ctx) (ctx)->fd +#define CTX_SD(ctx) (ctx)->sd +#define CTX_IOT(ctx) (ctx)->io +#include "rw-inl.h" + +#define LOGARGS(c, lvl) (c)->sock->settings, "ioctx", LCB_LOG_##lvl, __FILE__, __LINE__ +static const lcb_host_t * get_ctx_host(const lcbio_CTX *ctx) { + static lcb_host_t host = { "NOHOST", "NOPORT" }; + if (!ctx) { return &host; } + if (!ctx->sock) { return &host; } + if (!ctx->sock->info) { return &host; } + return &ctx->sock->info->ep; +} +#define CTX_LOGFMT "<%s:%s> (CTX=%p,%s) " +#define CTX_LOGID(ctx) get_ctx_host(ctx)->host, get_ctx_host(ctx)->port, (void*)ctx, ctx?ctx->subsys : "" + +typedef enum { + ES_ACTIVE = 0, + ES_DETACHED +} easy_state; + +static void +err_handler(void *cookie) +{ + lcbio_CTX *ctx = (void *)cookie; + ctx->procs.cb_err(ctx, ctx->err); +} + +static lcb_error_t +convert_lcberr(const lcbio_CTX *ctx, lcbio_IOSTATUS status) +{ + const lcb_settings *settings = ctx->sock->settings; + lcbio_OSERR oserr = IOT_ERRNO(ctx->sock->io); + + if (lcbio_ssl_check(ctx->sock)) { + lcb_error_t err = lcbio_ssl_get_error(ctx->sock); + if (err) { + return err; + } + } + + if (status == LCBIO_SHUTDOWN) { + return lcbio_mklcberr(0, settings); + } else if (oserr != 0) { + return lcbio_mklcberr(oserr, settings); + } else { + return LCB_NETWORK_ERROR; + } +} + +lcbio_CTX * +lcbio_ctx_new(lcbio_SOCKET *sock, void *data, const lcbio_CTXPROCS *procs) +{ + lcbio_CTX *ctx = calloc(1, sizeof(*ctx)); + ctx->sock = sock; + sock->ctx = ctx; + ctx->io = sock->io; + ctx->data = data; + ctx->procs = *procs; + ctx->state = ES_ACTIVE; + ctx->as_err = lcbio_timer_new(ctx->io, ctx, err_handler); + ctx->subsys = "unknown"; + + rdb_init(&ctx->ior, sock->settings->allocator_factory()); + lcbio_ref(sock); + + if (IOT_IS_EVENT(ctx->io)) { + ctx->event = IOT_V0EV(ctx->io).create(IOT_ARG(ctx->io)); + ctx->fd = sock->u.fd; + } else { + ctx->sd = sock->u.sd; + } + + ctx->procs = *procs; + ctx->state = ES_ACTIVE; + + lcb_log(LOGARGS(ctx, DEBUG), CTX_LOGFMT "Pairing with SOCK=%p", CTX_LOGID(ctx), (void*)sock); + return ctx; +} + +static void +free_ctx(lcbio_CTX *ctx) +{ + rdb_cleanup(&ctx->ior); + lcbio_unref(ctx->sock); + if (ctx->output) { + ringbuffer_destruct(&ctx->output->rb); + free(ctx->output); + } + if (ctx->procs.cb_flush_ready) { + /* dtor */ + ctx->procs.cb_flush_ready(ctx); + } + free(ctx); +} + +static void +deactivate_watcher(lcbio_CTX *ctx) +{ + if (ctx->evactive && ctx->event) { + IOT_V0EV(CTX_IOT(ctx)).cancel( + IOT_ARG(CTX_IOT(ctx)), CTX_FD(ctx), ctx->event); + ctx->evactive = 0; + } +} + +void +lcbio_ctx_close_ex(lcbio_CTX *ctx, lcbio_CTXCLOSE_cb cb, void *arg, + lcbio_CTXDTOR_cb dtor, void *dtor_arg) +{ + unsigned oldrc; + ctx->state = ES_DETACHED; + assert(ctx->sock); + + if (ctx->event) { + deactivate_watcher(ctx); + IOT_V0EV(CTX_IOT(ctx)).destroy(IOT_ARG(CTX_IOT(ctx)), ctx->event); + ctx->event = NULL; + } + + if (ctx->as_err) { + lcbio_timer_destroy(ctx->as_err); + ctx->as_err = NULL; + } + + oldrc = ctx->sock->refcount; + lcb_log(LOGARGS(ctx, DEBUG), CTX_LOGFMT "Destroying. PND=%d,ENT=%d,SORC=%d", CTX_LOGID(ctx), (int)ctx->npending, (int)ctx->entered, oldrc); + + if (cb) { + int reusable = + ctx->npending == 0 && /* no pending events */ + ctx->err == LCB_SUCCESS && /* no socket errors */ + ctx->rdwant == 0 && /* no expected input */ + ctx->wwant == 0 && /* no expected output */ + (ctx->output == NULL || ctx->output->rb.nbytes == 0); + cb(ctx->sock, reusable, arg); + } + + if (oldrc == ctx->sock->refcount) { + lcbio_shutdown(ctx->sock); + } + + if (ctx->output) { + ringbuffer_destruct(&ctx->output->rb); + free(ctx->output); + ctx->output = NULL; + } + + ctx->fd = INVALID_SOCKET; + ctx->sd = NULL; + + if (dtor) { + ctx->data = dtor_arg; + ctx->procs.cb_flush_ready = dtor; + + } else { + ctx->procs.cb_flush_ready = NULL; + } + + if (ctx->npending == 0 && ctx->entered == 0) { + free_ctx(ctx); + } +} + +void +lcbio_ctx_close(lcbio_CTX *ctx, lcbio_CTXCLOSE_cb cb, void *arg) +{ + lcbio_ctx_close_ex(ctx, cb, arg, NULL, NULL); +} + +void +lcbio_ctx_put(lcbio_CTX *ctx, const void *buf, unsigned nbuf) +{ + lcbio__EASYRB *erb = ctx->output; + + if (!erb) { + ctx->output = erb = calloc(1, sizeof(*ctx->output)); + + if (!erb) { + lcbio_ctx_senderr(ctx, LCB_CLIENT_ENOMEM); + return; + } + + erb->parent = ctx; + + if (!ringbuffer_initialize(&erb->rb, nbuf)) { + lcbio_ctx_senderr(ctx, LCB_CLIENT_ENOMEM); + return; + } + } + + if (!ringbuffer_ensure_capacity(&erb->rb, nbuf)) { + lcbio_ctx_senderr(ctx, LCB_CLIENT_ENOMEM); + return; + } + + ringbuffer_write(&erb->rb, buf, nbuf); +} + +void +lcbio_ctx_rwant(lcbio_CTX *ctx, unsigned n) +{ + ctx->rdwant = n; +} + +static void +set_iterbuf(lcbio_CTX *ctx, lcbio_CTXRDITER *iter) +{ + if ((iter->nbuf = rdb_get_contigsize(&ctx->ior))) { + if (iter->nbuf > iter->remaining) { + iter->nbuf = iter->remaining; + } + iter->buf = rdb_get_consolidated(&ctx->ior, iter->nbuf); + } else { + iter->buf = NULL; + } +} + +void +lcbio_ctx_ristart(lcbio_CTX *ctx, lcbio_CTXRDITER *iter, unsigned nb) +{ + iter->remaining = nb; + set_iterbuf(ctx, iter); +} + +void +lcbio_ctx_rinext(lcbio_CTX *ctx, lcbio_CTXRDITER *iter) +{ + rdb_consumed(&ctx->ior, iter->nbuf); + iter->remaining -= iter->nbuf; + set_iterbuf(ctx, iter); +} + +static int +E_free_detached(lcbio_CTX *ctx) +{ + if (ctx->state == ES_DETACHED) { + free_ctx(ctx); + return 1; + } + return 0; +} + +static void +invoke_read_cb(lcbio_CTX *ctx, unsigned nb) +{ + ctx->rdwant = 0; + ctx->entered++; + ctx->procs.cb_read(ctx, nb); + ctx->entered--; +} + +static void +E_handler(lcb_socket_t sock, short which, void *arg) +{ + lcbio_CTX *ctx = arg; + lcbio_IOSTATUS status; + (void)sock; + + if (which & LCB_READ_EVENT) { + unsigned nb; + status = lcbio_E_rdb_slurp(ctx, &ctx->ior); + nb = rdb_get_nused(&ctx->ior); + + if (nb >= ctx->rdwant) { + invoke_read_cb(ctx, nb); + if (E_free_detached(ctx)) { + return; + } + } + if (!LCBIO_IS_OK(status)) { + lcb_error_t err = convert_lcberr(ctx, status); + lcbio_ctx_senderr(ctx, err); + return; + } + } + + if (which & LCB_WRITE_EVENT) { + if (ctx->wwant) { + ctx->wwant = 0; + ctx->procs.cb_flush_ready(ctx); + if (ctx->err) { + return; + } + } else if (ctx->output) { + status = lcbio_E_rb_write(ctx, &ctx->output->rb); + if (!LCBIO_IS_OK(status)) { + deactivate_watcher(ctx); + ctx->err = convert_lcberr(ctx, status); + err_handler(ctx); + return; + } + } + } + + lcbio_ctx_schedule(ctx); +} + +static void +invoke_entered_errcb(lcbio_CTX *ctx, lcb_error_t err) +{ + ctx->err = err; + ctx->entered++; + ctx->procs.cb_err(ctx, err); + ctx->entered--; +} + +static void +Cw_handler(lcb_sockdata_t *sd, int status, void *arg) +{ + lcbio__EASYRB *erb = arg; + lcbio_CTX *ctx = erb->parent; + (void)sd; + + ctx->npending--; + + if (!ctx->output) { + ctx->output = erb; + ringbuffer_reset(&erb->rb); + + } else { + ringbuffer_destruct(&erb->rb); + free(erb); + } + + if (ctx->state == ES_ACTIVE && status) { + invoke_entered_errcb(ctx, convert_lcberr(ctx, LCBIO_IOERR)); + } + + if (ctx->state != ES_ACTIVE && ctx->npending == 0) { + free_ctx(ctx); + } +} + +static void C_schedule(lcbio_CTX *ctx); + +static void +Cr_handler(lcb_sockdata_t *sd, lcb_ssize_t nr, void *arg) +{ + lcbio_CTX *ctx = arg; + sd->is_reading = 0; + ctx->npending--; + + if (ctx->state == ES_ACTIVE) { + if (nr > 0) { + unsigned total; + rdb_rdend(&ctx->ior, nr); + total = rdb_get_nused(&ctx->ior); + if (total >= ctx->rdwant) { + invoke_read_cb(ctx, total); + } + + lcbio_ctx_schedule(ctx); + } else { + lcb_error_t err = + convert_lcberr(ctx, nr ? LCBIO_SHUTDOWN : LCBIO_IOERR); + ctx->rdwant = 0; + invoke_entered_errcb(ctx, err); + } + } + + if (ctx->state != ES_ACTIVE && ctx->npending == 0) { + free_ctx(ctx); + } +} + +static void +C_schedule(lcbio_CTX *ctx) +{ + lcbio_TABLE *io = ctx->io; + lcb_sockdata_t *sd = CTX_SD(ctx); + int rv; + + if (ctx->output && ctx->output->rb.nbytes) { + /** Schedule a write */ + lcb_IOV iov[2]; + unsigned niov; + + ringbuffer_get_iov(&ctx->output->rb, RINGBUFFER_READ, iov); + niov = iov[1].iov_len ? 2 : 1; + rv = IOT_V1(io).write2(IOT_ARG(io), sd, iov, niov, ctx->output, Cw_handler); + if (rv) { + lcbio_ctx_senderr(ctx, convert_lcberr(ctx, LCBIO_IOERR)); + return; + } else { + ctx->output = NULL; + ctx->npending++; + } + } + + if (ctx->wwant) { + ctx->wwant = 0; + ctx->procs.cb_flush_ready(ctx); + } + + if (ctx->rdwant && sd->is_reading == 0) { + lcb_IOV iov[RWINL_IOVSIZE]; + unsigned ii; + unsigned niov = rdb_rdstart(&ctx->ior, (nb_IOV *)iov, RWINL_IOVSIZE); + + assert(niov); + for (ii = 0; ii < niov; ++ii) { + assert(iov[ii].iov_len); + } + + rv = IOT_V1(io).read2(IOT_ARG(io), sd, iov, niov, ctx, Cr_handler); + if (rv) { + lcbio_ctx_senderr(ctx, convert_lcberr(ctx, LCBIO_IOERR)); + + } else { + sd->is_reading = 1; + ctx->npending++; + } + } +} + +static void +E_schedule(lcbio_CTX *ctx) +{ + lcbio_TABLE *io = ctx->io; + short which = 0; + + if (ctx->rdwant) { + which |= LCB_READ_EVENT; + } + if (ctx->wwant || (ctx->output && ctx->output->rb.nbytes)) { + which |= LCB_WRITE_EVENT; + } + + if (!which) { + deactivate_watcher(ctx); + return; + } + + IOT_V0EV(io).watch(IOT_ARG(io), CTX_FD(ctx), ctx->event, which, ctx, E_handler); + ctx->evactive = 1; +} + +void +lcbio_ctx_schedule(lcbio_CTX *ctx) +{ + if (ctx->entered || ctx->err || ctx->state != ES_ACTIVE) { + /* don't schedule events on i/o errors or on entered state */ + return; + } + if (IOT_IS_EVENT(ctx->io)) { + E_schedule(ctx); + } else { + C_schedule(ctx); + } +} + +/** Extended function used for write-on-callback mode */ +static int +E_put_ex(lcbio_CTX *ctx, lcb_IOV *iov, unsigned niov, unsigned nb) +{ + lcb_ssize_t nw; + lcbio_TABLE *iot = ctx->io; + lcb_socket_t fd = CTX_FD(ctx); + + GT_WRITE_AGAIN: + nw = IOT_V0IO(iot).sendv(IOT_ARG(iot), fd, iov, + niov <= RWINL_IOVSIZE ? niov : RWINL_IOVSIZE); + if (nw > 0) { + ctx->procs.cb_flush_done(ctx, nb, nw); + return 1; + + } else if (nw == -1) { + switch (IOT_ERRNO(iot)) { + case EINTR: + /* jump back to retry */ + goto GT_WRITE_AGAIN; + + case C_EAGAIN: + case EWOULDBLOCK: + nw = 0; + /* indicate zero bytes were written, but don't send an error */ + goto GT_WRITE0; + default: + /* pretend all the bytes were written and deliver an error during + * the next event loop iteration. */ + nw = nb; + lcbio_ctx_senderr(ctx, convert_lcberr(ctx, LCBIO_IOERR)); + goto GT_WRITE0; + } + } else { + /* connection closed. pretend everything was written and send an error */ + nw = nb; + lcbio_ctx_senderr(ctx, convert_lcberr(ctx, LCBIO_SHUTDOWN)); + goto GT_WRITE0; + } + + GT_WRITE0: + ctx->procs.cb_flush_done(ctx, nb, nw); + return 0; +} + +static void +Cw_ex_handler(lcb_sockdata_t *sd, int status, void *wdata) +{ + lcbio_CTX *ctx = ((lcbio_SOCKET *)sd->lcbconn)->ctx; + unsigned nflushed = (uintptr_t)wdata; + ctx->npending--; + + ctx->entered = 1; + ctx->procs.cb_flush_done(ctx, nflushed, nflushed); + ctx->entered = 0; + + if (ctx->state == ES_ACTIVE && status) { + invoke_entered_errcb(ctx, convert_lcberr(ctx, LCBIO_IOERR)); + } + if (ctx->state != ES_ACTIVE && !ctx->npending) { + free_ctx(ctx); + } +} + +static int +C_put_ex(lcbio_CTX *ctx, lcb_IOV *iov, unsigned niov, unsigned nb) +{ + lcbio_TABLE *iot = ctx->io; + lcb_sockdata_t *sd = CTX_SD(ctx); + int status = IOT_V1(iot).write2(IOT_ARG(iot), + sd, iov, niov, (void *)(uintptr_t)nb, Cw_ex_handler); + if (status) { + /** error! */ + lcbio_OSERR saverr = IOT_ERRNO(iot); + ctx->procs.cb_flush_done(ctx, nb, nb); + lcbio_ctx_senderr(ctx, lcbio_mklcberr(saverr, ctx->sock->settings)); + return 0; + } else { + ctx->npending++; + return 1; + } +} + +int +lcbio_ctx_put_ex(lcbio_CTX *ctx, lcb_IOV *iov, unsigned niov, unsigned nb) +{ + lcbio_TABLE *iot = ctx->io; + if (IOT_IS_EVENT(iot)) { + return E_put_ex(ctx, iov, niov, nb); + } else { + return C_put_ex(ctx, iov, niov, nb); + } +} + +void +lcbio_ctx_wwant(lcbio_CTX *ctx) +{ + if ((IOT_IS_EVENT(ctx->io)) == 0 && ctx->entered == 0) { + ctx->procs.cb_flush_ready(ctx); + } else { + ctx->wwant = 1; + } +} + +void +lcbio_ctx_senderr(lcbio_CTX *ctx, lcb_error_t err) +{ + if (ctx->err == LCB_SUCCESS) { + ctx->err = err; + } + deactivate_watcher(ctx); + lcbio_async_signal(ctx->as_err); +} + +void +lcbio_ctx_dump(lcbio_CTX *ctx, FILE *fp) +{ + fprintf(fp, "IOCTX=%p. SUBSYS=%s\n", (void*)ctx, ctx->subsys); + fprintf(fp, " Pending=%d\n", ctx->npending); + fprintf(fp, " ReqRead=%d\n", ctx->rdwant); + fprintf(fp, " WantWrite=%d\n", ctx->wwant); + fprintf(fp, " Entered=%d\n", ctx->entered); + fprintf(fp, " Active=%d\n", ctx->state == ES_ACTIVE); + fprintf(fp, " SOCKET=%p\n", (void*)ctx->sock); + fprintf(fp, " Model=%s\n", ctx->io->model == LCB_IOMODEL_EVENT ? "Event" : "Completion"); + if (IOT_IS_EVENT(ctx->io)) { + fprintf(fp, " FD=%d\n", ctx->sock->u.fd); + fprintf(fp, " Watcher Active=%d\n", ctx->evactive); + } else { + fprintf(fp, " SD=%p\n", (void *)ctx->sock->u.sd); + fprintf(fp, " Reading=%d\n", ctx->sock->u.sd->is_reading); + } + fprintf(fp, " WILL DUMP IOR/READBUF INFO:\n"); + rdb_dump(&ctx->ior, fp); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/lcbio/ctx.h b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/ctx.h new file mode 100644 index 00000000..eba591af --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/ctx.h @@ -0,0 +1,405 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCBIO_CTXEASY_H +#define LCBIO_CTXEASY_H +#include "connect.h" +#include "rdb/rope.h" +#include "ringbuffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * This file contains routines for reading and writing from and to a socket + */ + +/** + * @ingroup lcbio + * @defgroup lcbio-ctx Reading/Writing Routines + * + * @details + * + * # Attaching + * + * A context is first _attached_ to a socket and a _user-defined_ data object. + * The idea is that the context is the broker which schedules I/O on behalf + * of the application to the socket, and then receives events from the socket, + * passing it along to user-defined data. + * + * To create a new context, invoke the lcbio_ctx_new() function. When you are done + * with it call the lcbio_ctx_close() function. + * + * # Reading + * + * Reading data is done through first requesting an amount of data to read + * and then reading the data from the buffers when the lcbio_CTXPROCS#cb_read + * handler is invoked. + * + * Requesting an amount of data to be read may be dependent on some current + * parsing context. In cases where the expected message size is known the pattern + * is to initially request the size of the header; once the header has been + * delivered to the application, the application should request the full + * header+body size, and so on. + * + * For streaming based protocols where the amount of data is not known ahead of + * time, requesting a single byte may be sufficient. Note that typically the + * read callback will be invoked with _more_ bytes than requested. + * + * Data is read from the network as one or more chunks to improve performance + * and increase flexibility. Because of this model, you must iterate over the + * data read _or_ employ the underlying API. For streaming data + * sockets where data may simply be copied to another buffer, use the iterator + * API presented here. + * + * @note The RDB interface requires you to explicitly advance the read + * cursor in addition to actually obtaining the data. This is handled within + * the iterator interface declared in this file (but of course must be done + * manually if employing RDB directly). + * + * + * # Writing + * + * Writing can be done through a simple lcbio_ctx_put() which simply copies data + * to an output buffer, or can be done through more efficient but complex means - + * see the lcbio_ctx_wwant() function. + * + * # Scheduling + * + * Any I/O **must always be scheduled**. For maximal efficiency the various + * read/write functions only set internal flags within the contexts to act + * as hints that the application intends to read and write data. However in + * order to actually perform these operations, kernel/OS calls are needed. + * + * To indicate that no subsequent I/O operations will be requested until the next + * event loop iteration, _and_ to apply/schedule any existing I/O within the + * current iteration, the lcbio_ctx_schedule() function should be called. + * + * + * @addtogroup lcbio-ctx + * @{ */ + +typedef struct lcbio_CTX *lcbio_pCTX; + +/** + * @brief Handlers for I/O events + */ +typedef struct { + /** Error handler invoked with the context and the received error */ + void (*cb_err)(lcbio_pCTX, lcb_error_t); + + /** Read handler invoked with the context and the number of bytes read */ + void (*cb_read)(lcbio_pCTX, unsigned total); + + /** Triggered by lcbio_ctx_wwant() */ + void (*cb_flush_ready)(lcbio_pCTX); + + /** Triggered when data has been flushed from lcbio_ctx_put_ex() */ + void (*cb_flush_done)(lcbio_pCTX, unsigned requested, unsigned nflushed); +} lcbio_CTXPROCS; + +/** + * Container buffer handle containing a backref to the original context. + * @private + */ +typedef struct { + ringbuffer_t rb; + lcbio_pCTX parent; +} lcbio__EASYRB; + +/** + * @brief Context for socket I/O + * + * The CTX structure represents an ownership of a socket. It provides routines + * for reading and writing from and to the socket, as well as associating + * application data with the socket. + */ +typedef struct lcbio_CTX { + lcbio_SOCKET *sock; /**< Socket resource */ + lcbio_pTABLE io; /**< Cached IO table */ + void *data; /**< Associative pointer */ + void *event; /**< event pointer for E-model I/O */ + lcb_sockdata_t *sd; /**< cached SD for C-model I/O */ + lcbio__EASYRB *output; /**< for lcbio_ctx_put() */ + lcb_socket_t fd; /**< cached FD for E-model I/O */ + char evactive; /**< watcher is active for E-model I/O */ + char wwant; /**< flag for lcbio_ctx_put_ex */ + char state; /**< internal state */ + char entered; /**< inside event handler */ + unsigned npending; /**< reference count on pending I/O */ + unsigned rdwant; /**< number of remaining bytes to read */ + lcb_error_t err; /**< pending error */ + rdb_IOROPE ior; /**< for reads */ + lcbio_pASYNC as_err; /**< async error handler */ + lcbio_CTXPROCS procs; /**< callbacks */ + const char *subsys; /**< Informational description of connection */ +} lcbio_CTX; + +/**@name Creating and Closing + *@{*/ + +/** + * Creates a new context object. + * + * The object remains valid until lcbio_ctx_close() is invoked. + * + * @param sock the underlying socket object. This function increments the + * socket's reference count. + * @param data user defined data to associate with context. The data may be + * obtained at a later point via lcbio_ctx_data() + * + * @param procs callback table + * @return a new context object. + */ +lcbio_CTX * +lcbio_ctx_new(lcbio_SOCKET *sock, void *data, const lcbio_CTXPROCS *procs); + + +/** + * Callback invoked when the connection is about to be release + * @param sock the socket being released. + * @param releasable whether the socket may be reused + * @param arg an argument passed to the close() function. + * + * If you wish to reuse the socket (and reusable is true) then the socket's + * reference count should be incremented via lcbio_ref(). + */ +typedef void +(*lcbio_CTXCLOSE_cb)(lcbio_SOCKET *sock, int releasable, void *arg); + +/** + * @brief Close the context object. + * + * This will invalidate any pending I/O operations + * and subsequent callbacks on the context will not be received. After calling + * this function, the pointer will be deemed invalid. + * + * @param ctx + * @param callback a callback to invoke (see above) + * @param arg argument passed to the callback + */ +void +lcbio_ctx_close(lcbio_CTX *ctx, lcbio_CTXCLOSE_cb callback, void *arg); + +typedef void (*lcbio_CTXDTOR_cb)(lcbio_pCTX); +void +lcbio_ctx_close_ex(lcbio_CTX *ctx, lcbio_CTXCLOSE_cb cb, void *cbarg, + lcbio_CTXDTOR_cb dtor, void *dtor_arg); +/**@}*/ + + +/**@name Informational Routines + * @{*/ + +/** + * Get the data associated with the context. This is the pointer specified + * during the constructor + */ +#define lcbio_ctx_data(ctx) (ctx)->data + +/** Get the associated lcbio_SOCKET object */ +#define lcbio_ctx_sock(ctx) (ctx)->sock + +/** Dump a textual representation of the context to the screen */ +void +lcbio_ctx_dump(lcbio_CTX *ctx, FILE *fp); + +/**@}*/ + +/** Asynchronously trigger the error callback */ +void +lcbio_ctx_senderr(lcbio_CTX *ctx, lcb_error_t err); + + +/** + * Schedule any pending I/O to be scheduled immediately. If data was requested + * via lcbio_ctx_rwant() then a request will be sent for reading. If data was + * requested to be flushed either via lcbio_ctx_put() or lcbio_ctx_wwant() + * then those will be scheduled as well. + * + * This call is a no-op if invoked from within the current handler, as this + * function is invoked implicitly after the I/O handler itself has returned. + * + * It is safe (though typically not efficient) to invoke this function + * multiple times. Each invocation may potentially involve system calls + * and buffer allocations, depending on the I/O plugin being used. + */ +void +lcbio_ctx_schedule(lcbio_CTX *ctx); + +/** + * @brief Add output data to the write buffer. + * + * The data added is copied to an internal buffer and flushed to the network + * when appropriate. If you wish to have more control over how the data is written + * then see the lcbio_ctx_wwant() function. + * + * @param ctx + * @param buf the buffer to write + * @param nbuf the size of the buffer to write + */ +void +lcbio_ctx_put(lcbio_CTX *ctx, const void *buf, unsigned nbuf); + +/** + * Invoke the lcbio_CTXPROCS#cb_flush_ready() + * callback when a flush may be invoked. Note that the + * callback may be invoked from within this function itself, or + * it may be invoked at some point later. + * + * In order to ensure that the callback is actually invoked (in + * cases where it is not invoked immediately), call lcbio_ctx_schedule() + * before returning to the loop. + * + * When the callback is invoked, you should call the lcbio_ctx_put_ex() function + * to actually enqueue the data to be written. The lcbio_ctx_put_ex() function + * should be called multiple times until either no write buffers remain + * or the function itself returns a false value. The lcbio_ctx_put_ex() function + * may either flush the data immediately or schedule for the data to be flushed + * depending on the I/O implementation and network conditions. + * + * Once the data is actually flushed to the socket's buffers, the + * lcbio_CTXPROCS#cb_flush_done() callback is invoked. + * This callback indicates the underlying buffers are no longer required and may + * be released or reused by the application. Note that the IOV array passed + * into lcbio_ctx_put_ex() is always _Conceptually_ copied (i.e. + * this may be a stack-based structure which does not need to remain valid + * outside the function call to lcbio_ctx_put_ex() itself). + * + * Additionally, note that the number of bytes flushed within the + * lcbio_CTXPROCS#cb_flush_done() + * callback may not equal the number of bytes initially placed inside the IOVs + * (i.e. it may be less). In this case the application is expected to update + * the IOV structures and the origin buffers appropriately. + * + * This model allows for efficient handling in both completion and event based + * environments. + * + * ### Implementation Notes + * + * For completion-based models, the lcbio_CTXPROCS#cb_flush_ready() + * callback is invoked immediately from the wwant() function, while the + * flush_done() is dependent on the actual completion of the write. + * + * For event-based models, the wwant flag is set inside the context and is then + * checked by the lcbio_ctx_schedule() function. When the event handler is invoked, the + * flush_ready() callback is invoked as well - typically in a loop until an + * `EWOULDBLOCK` is received on the socket itself. + */ +void +lcbio_ctx_wwant(lcbio_CTX *ctx); + +/** + * @brief Flush data from the lcbio_CTXPROCS#cb_flush_ready() callback + * + * This function is intended to be called from within the `cb_flush_ready` + * handler (see lcbio_ctx_wwant()). + * + * @param ctx + * @param iov the IOV array. The IOV array may point to a stack-based array + * @param niov number of elements in the array + * @param nb The total number of bytes described by all the elements of the + * array. + * + * @return nonzero if more data can be written (i.e. this function may be + * invoked again), zero otherwise. + */ +int +lcbio_ctx_put_ex(lcbio_CTX *ctx, lcb_IOV *iov, unsigned niov, unsigned nb); + +/** + * Require that the read callback not be invoked until at least `n` + * bytes are available within the buffer. + * + * @param ctx + * @param n the number of bytes required to be in the buffer before the + * callback should be invoked. + * + * @note + * Note that this flag does _not_ maintain state between successive callbacks. + * You must call this function each time you need more data as it is cleared + * before the invocation into the callback. + * + * @note + * This function sets the number of **total** bytes + * which must be available in the buffer before the callback is invoked. Thus + * you should set this to the total number of bytes needed, and **not** the + * number of remaining bytes that should be read. + */ +void +lcbio_ctx_rwant(lcbio_CTX *ctx, unsigned n); + + +/** @private */ +typedef struct { + unsigned remaining; + void *buf; + unsigned nbuf; +} lcbio_CTXRDITER; + +/** + * Iterate over the read buffers + * + * @code{.c} + * static void read_callback(lcbio_CTX *ctx, void *arg, unsigned nb) { + * lcbio_CTXRDITER iter; + * LCBIO_CTX_ITERFOR(ctx, &iter, nb) { + * void *buf = lcbio_ctx_ribuf(&iter); + * unsigned nbuf = lcbio_ctx_risize(&iter); + * // do stuff with the buffer + * } + * } + * @endcode + * + * When each iteration is complete, the pointer returned by ctx_ribuf is + * no longer valid. + * + * @param ctx the context which contains the buffer + * @param[in,out] iter an empty iterator + * @param[in] nb the number of bytes to iterate over. + */ +#define LCBIO_CTX_ITERFOR(ctx, iter, nb) \ + for (lcbio_ctx_ristart(ctx, iter, nb); !lcbio_ctx_ridone(iter); \ + lcbio_ctx_rinext(ctx, iter)) + +/** Obtains the buffer from the current iterator */ +#define lcbio_ctx_ribuf(iter) ((iter)->buf) + +/** Obtains the length of the buffer from the current iterator */ +#define lcbio_ctx_risize(iter) ((iter)->nbuf) + +void +lcbio_ctx_ristart(lcbio_CTX *ctx, lcbio_CTXRDITER *iter, unsigned nb); + +void +lcbio_ctx_rinext(lcbio_CTX *ctx, lcbio_CTXRDITER *iter); + +#define lcbio_ctx_ridone(iter) (!(iter)->remaining) + +#define LCBIO_CTX_RSCHEDULE(ctx, nb) do { \ + lcbio_ctx_rwant(ctx, nb); \ + lcbio_ctx_schedule(ctx); \ +} while (0) + +#ifdef __cplusplus +} +#endif +#endif + +/** @} */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/lcbio/iotable.c b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/iotable.c new file mode 100644 index 00000000..4a5a7ef9 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/iotable.c @@ -0,0 +1,290 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LCB_IOPS_V12_NO_DEPRECATE +#include +#include +#include +#include "iotable.h" +#include "connect.h" /* prototypes for iotable functions */ + +#define GET_23_FIELD(iops, fld) ((iops)->version == 2 ? (iops)->v.v2.fld : (iops)->v.v3.fld) + +struct W_1to3_st { + lcb_ioC_write2_callback callback; + void *udata; + unsigned int refcount; + unsigned int last_error; +}; + +static void +W_1to3_callback(lcb_sockdata_t *sd, lcb_io_writebuf_t *wb, int status) +{ + struct W_1to3_st *ott = (struct W_1to3_st*)(void *)wb->buffer.root; + lcb_ioC_wbfree_fn wbfree; + + wb->buffer.root = NULL; + wb->buffer.ringbuffer = NULL; + + if (sd->parent->version >= 2) { + wbfree = GET_23_FIELD(sd->parent, iot->u_io.completion.wbfree); + } else { + wbfree = sd->parent->v.v1.release_writebuf; + } + + wbfree(sd->parent, sd, wb); + + if (status != 0 && ott->last_error == 0) { + ott->last_error = sd->parent->v.v0.error; + } + + if (--ott->refcount == 0) { + ott->callback(sd, ott->last_error, ott->udata); + free(ott); + } +} + +static int +W_1to3_write(lcb_io_opt_t iops, + lcb_sockdata_t *sd, + struct lcb_iovec_st *iov, + lcb_size_t niov, + void *uarg, + lcb_ioC_write2_callback cb) +{ + unsigned int ii = 0; + struct W_1to3_st *ott; + lcb_ioC_write_fn start_write; + lcb_ioC_wballoc_fn wballoc; + + /** Schedule IOV writes, two at a time... */ + ott = malloc(sizeof(*ott)); + ott->callback = cb; + ott->udata = uarg; + ott->refcount = 0; + ott->last_error = 0; + + if (iops->version >= 2) { + start_write = GET_23_FIELD(iops, iot->u_io.completion.write); + wballoc = GET_23_FIELD(iops, iot->u_io.completion.wballoc); + } else { + start_write = iops->v.v1.start_write; + wballoc = iops->v.v1.create_writebuf; + } + + while (ii < niov) { + int jj = 0; + lcb_io_writebuf_t *wb; + + wb = wballoc(iops, sd); + wb->buffer.root = (char*)ott; + wb->buffer.ringbuffer = NULL; + + for (jj = 0; jj < 2 && ii < niov; ii++, jj++) { + wb->buffer.iov[jj] = iov[ii]; + } + ott->refcount++; + start_write(iops, sd, wb, W_1to3_callback); + } + return 0; +} + +struct R_1to3_st { + lcb_ioC_read2_callback callback; + void *uarg; +}; + +static void +R_1to3_callback(lcb_sockdata_t *sd, lcb_ssize_t nread) +{ + struct lcb_buf_info *bi = &sd->read_buffer; + struct R_1to3_st *st = (void *)bi->root; + bi->root = NULL; + st->callback(sd, nread, st->uarg); + free(st); +} + +static int +R_1to3_read(lcb_io_opt_t io, lcb_sockdata_t *sd, lcb_IOV *iov, lcb_size_t niov, + void *uarg, lcb_ioC_read2_callback callback) +{ + unsigned ii; + int rv; + struct R_1to3_st *st; + struct lcb_buf_info *bi = &sd->read_buffer; + lcb_ioC_read_fn rdstart; + + st = calloc(1, sizeof(*st)); + st->callback = callback; + st->uarg = uarg; + + for (ii = 0; ii < 2 && ii < niov; ii++) { + bi->iov[ii] = iov[ii]; + } + + for (; ii < 2; ii++) { + bi->iov[ii].iov_base = NULL; + bi->iov[ii].iov_len = 0; + } + + + bi->root = (void *)st; + if (io->version >= 2) { + rdstart = GET_23_FIELD(io, iot->u_io.completion.read); + } else { + rdstart = io->v.v1.start_read; + } + + rv = rdstart(io, sd, R_1to3_callback); + return rv; +} + +static int dummy_bsd_chkclosed(lcb_io_opt_t io, lcb_socket_t s, int f) { + (void)io; (void)s; (void)f; return LCB_IO_SOCKCHECK_STATUS_UNKNOWN; +} +static int dummy_comp_chkclosed(lcb_io_opt_t io, lcb_sockdata_t* s, int f) { + (void)io; (void)s; (void)f; return LCB_IO_SOCKCHECK_STATUS_UNKNOWN; +} + +static void +init_v23_table(lcbio_TABLE *table, lcb_io_opt_t io) +{ + lcb_io_procs_fn fn = GET_23_FIELD(io, get_procs); + fn(LCB_IOPROCS_VERSION, &table->loop, &table->timer, &table->u_io.v0.io, + &table->u_io.v0.ev, &table->u_io.completion, &table->model); + + table->p = io; + if (table->model == LCB_IOMODEL_COMPLETION) { + if (!table->u_io.completion.write2) { + table->u_io.completion.write2 = W_1to3_write; + } + + if (!table->u_io.completion.read2) { + table->u_io.completion.read2 = R_1to3_read; + } + lcb_assert(table->u_io.completion.read2); + lcb_assert(table->u_io.completion.write2); + } + + if (table->model == LCB_IOMODEL_COMPLETION && IOT_V1(table).is_closed == NULL) { + IOT_V1(table).is_closed = dummy_comp_chkclosed; + } + + if (table->model == LCB_IOMODEL_EVENT && IOT_V0IO(table).is_closed == NULL) { + IOT_V0IO(table).is_closed = dummy_bsd_chkclosed; + } +} + +lcbio_TABLE * +lcbio_table_new(lcb_io_opt_t io) +{ + lcbio_TABLE *table = calloc(1, sizeof(*table)); + table->p = io; + table->refcount = 1; + + if (io->version == 2) { + io->v.v2.iot = table; + init_v23_table(table, io); + return table; + + } else if (io->version == 3) { + /* V3 exists exclusively for back-compat. We need to use a few tricks to + * determine if we are really v3, or if we've been 'overidden' somehow. + * + * To do this, we treat the padding fields (specifically, the event + * scheduling parts of the padding fields) as sentinel values. The + * built-in plugins should initialize this to NULL. If a client + * (e.g. Python SDK) overrides this, the field will no longer be + * NULL and will be a sign that the event fields have been + * used by a non-getprocs-aware client. + */ + + io->v.v3.iot = table; + if (io->v.v0.create_event == NULL) { + init_v23_table(table, io); + return table; + } + } + + table->timer.create = io->v.v0.create_timer; + table->timer.destroy = io->v.v0.destroy_timer; + table->timer.cancel = io->v.v0.delete_timer; + table->timer.schedule = io->v.v0.update_timer; + table->loop.start = io->v.v0.run_event_loop; + table->loop.stop = io->v.v0.stop_event_loop; + + if (io->version == 0 || io->version == 3) { + lcb_ev_procs *ev = &table->u_io.v0.ev; + lcb_bsd_procs *bsd = &table->u_io.v0.io; + + table->model = LCB_IOMODEL_EVENT; + ev->create = io->v.v0.create_event; + ev->destroy = io->v.v0.destroy_event; + ev->cancel = io->v.v0.delete_event; + ev->watch = io->v.v0.update_event; + bsd->socket0 = io->v.v0.socket; + bsd->connect0 = io->v.v0.connect; + bsd->close = io->v.v0.close; + bsd->recv = io->v.v0.recv; + bsd->recvv = io->v.v0.recvv; + bsd->send = io->v.v0.send; + bsd->sendv = io->v.v0.sendv; + bsd->is_closed = dummy_bsd_chkclosed; + + } else { + lcb_completion_procs *cp = &table->u_io.completion; + + table->model = LCB_IOMODEL_COMPLETION; + cp->socket = io->v.v1.create_socket; + cp->close = io->v.v1.close_socket; + cp->connect = io->v.v1.start_connect; + cp->read = io->v.v1.start_read; + cp->write = io->v.v1.start_write; + cp->wballoc = io->v.v1.create_writebuf; + cp->nameinfo = io->v.v1.get_nameinfo; + cp->write2 = W_1to3_write; + cp->read2 = R_1to3_read; + cp->is_closed = dummy_comp_chkclosed; + } + + return table; +} + +void +lcbio_table_unref(lcbio_TABLE *table) +{ + if (--table->refcount) { + return; + } + + if (table->dtor) { + table->dtor(table); + return; + } + + if (table->p && table->p->v.v0.need_cleanup) { + lcb_destroy_io_ops(table->p); + } + + free(table); +} + +void +lcbio_table_ref(lcbio_TABLE *table) +{ + ++table->refcount; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/lcbio/iotable.h b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/iotable.h new file mode 100644 index 00000000..ac36576f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/iotable.h @@ -0,0 +1,84 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_IOTABLE_H +#define LCB_IOTABLE_H + +#include + +/** + * @file + * @brief Internal I/O Table routines + * + * @details + * Include this file if you are actually manipulating the I/O system (i.e. + * creating timers, starting/stoping loops, or writing to/from a socket). + * + * This file defines the iotable layout as well as various macros associated + * with its use. The actual "Public" API (i.e. just for passing it around) can + * be found in + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct lcbio_TABLE { + lcb_io_opt_t p; + lcb_iomodel_t model; + lcb_timer_procs timer; + lcb_loop_procs loop; + + union { + struct { + lcb_ev_procs ev; + lcb_bsd_procs io; + } v0; + lcb_completion_procs completion; + } u_io; + unsigned refcount; + void (*dtor)(void *); +} lcbio_TABLE; + +/** Whether the underlying model is event-based */ +#define IOT_IS_EVENT(iot) ((iot)->model == LCB_IOMODEL_EVENT) + +/** Returns an lcb_ev_procs structure for event-based I/O */ +#define IOT_V0EV(iot) (iot)->u_io.v0.ev + +/** Returns an lcb_bsd_procs structure for event-based I/O */ +#define IOT_V0IO(iot) (iot)->u_io.v0.io + +/** Returns an lcb_completion_procs structure for completion-based I/O */ +#define IOT_V1(iot) (iot)->u_io.completion + +/** Error code of last I/O operation */ +#define IOT_ERRNO(iot) (iot)->p->v.v0.error + +/** Start the loop */ +#define IOT_START(iot) (iot)->loop.start((iot)->p) + +/** Stop the loop */ +#define IOT_STOP(iot) (iot)->loop.stop((iot)->p) + +/** First argument to IO Table */ +#define IOT_ARG(iot) (iot)->p + +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/lcbio/ioutils.c b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/ioutils.c new file mode 100644 index 00000000..2aa468cb --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/ioutils.c @@ -0,0 +1,350 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _WIN32 +#include +#endif + +#include "connect.h" +#include "ioutils.h" +#include "hostlist.h" +#include "manager.h" +#include "iotable.h" +#include +#include "ssl.h" + +lcbio_CSERR +lcbio_mkcserr(int syserr) +{ + switch (syserr) { + case 0: + return LCBIO_CSERR_CONNECTED; + + case EINTR: + return LCBIO_CSERR_INTR; + + case EWOULDBLOCK: +#ifdef USE_EAGAIN + case EAGAIN: +#endif + case EINPROGRESS: + case EALREADY: + return LCBIO_CSERR_BUSY; + + + case EISCONN: + return LCBIO_CSERR_CONNECTED; + +#ifdef _WIN32 + case EINVAL: + return LCBIO_CSERR_EINVAL; +#endif + default: + return LCBIO_CSERR_EFAIL; + } +} + +void +lcbio_mksyserr(lcbio_OSERR in, lcbio_OSERR *out) +{ + switch (in) { + case EINTR: + case EWOULDBLOCK: +#ifdef USE_EAGAIN + case EAGAIN: +#endif + case EINVAL: + case EINPROGRESS: + case EISCONN: + case EALREADY: + return; + default: + *out = in; + break; + } +} + +static lcb_error_t +ioerr2lcberr(lcbio_OSERR in, const lcb_settings *settings) +{ + switch (in) { + case 0: + return LCB_ESOCKSHUTDOWN; + case ECONNREFUSED: + return LCB_ECONNREFUSED; + case ENETUNREACH: + case EHOSTUNREACH: + case EHOSTDOWN: + return LCB_ENETUNREACH; + case EMFILE: + case ENFILE: + return LCB_EFDLIMITREACHED; + case EADDRINUSE: + case EADDRNOTAVAIL: + return LCB_ECANTGETPORT; + case ECONNRESET: + case ECONNABORTED: + return LCB_ECONNRESET; + default: + lcb_log(settings, "lcbio", LCB_LOG_WARN, __FILE__, __LINE__, "FIXME: Unknown iops/os error code %d. Using NETWORK_ERROR", in); + return LCB_NETWORK_ERROR; + } +} + +lcb_error_t +lcbio_mklcberr(lcbio_OSERR in, const lcb_settings *settings) +{ + if (settings->detailed_neterr == 0) { + lcb_log(settings, "lcbio", LCB_LOG_INFO, __FILE__, __LINE__, "Translating errno=%d, lcb=0x%x to NETWORK_ERROR", + in, ioerr2lcberr(in, settings)); + return LCB_NETWORK_ERROR; + } + + return ioerr2lcberr(in, settings); +} + +lcb_socket_t +lcbio_E_ai2sock(lcbio_TABLE *io, struct addrinfo **ai, int *connerr) +{ + lcb_socket_t ret = INVALID_SOCKET; + *connerr = 0; + + for (; *ai; *ai = (*ai)->ai_next) { + ret = IOT_V0IO(io).socket0( + IOT_ARG(io), (*ai)->ai_family, (*ai)->ai_socktype, + (*ai)->ai_protocol); + + if (ret != INVALID_SOCKET) { + return ret; + } else { + *connerr = IOT_ERRNO(io); + } + } + + return ret; +} + +lcb_sockdata_t * +lcbio_C_ai2sock(lcbio_TABLE *io, struct addrinfo **ai, int *connerr) +{ + lcb_sockdata_t *ret = NULL; + for (; *ai; *ai = (*ai)->ai_next) { + ret = IOT_V1(io).socket( + IOT_ARG(io), (*ai)->ai_family, (*ai)->ai_socktype, + (*ai)->ai_protocol); + if (ret) { + return ret; + } else { + *connerr = IOT_ERRNO(io); + } + } + return ret; +} + +struct nameinfo_common { + char remote[NI_MAXHOST + NI_MAXSERV + 2]; + char local[NI_MAXHOST + NI_MAXSERV + 2]; +}; + +static int +saddr_to_string(struct sockaddr *saddr, int len, char *buf, lcb_size_t nbuf) +{ + char h[NI_MAXHOST + 1]; + char p[NI_MAXSERV + 1]; + int rv; + + rv = getnameinfo(saddr, len, h, sizeof(h), p, sizeof(p), + NI_NUMERICHOST | NI_NUMERICSERV); + if (rv < 0) { + return 0; + } + + if (snprintf(buf, nbuf, "%s;%s", h, p) < 0) { + return 0; + } + + return 1; +} + +void +lcbio__load_socknames(lcbio_SOCKET *sock) +{ + int n_salocal, n_saremote, rv; + struct lcb_nameinfo_st ni; + lcbio_CONNINFO *info = sock->info; + + n_salocal = sizeof(info->sa_local); + n_saremote = sizeof(info->sa_remote); + ni.local.name = (struct sockaddr *)&info->sa_local; + ni.local.len = &n_salocal; + ni.remote.name = (struct sockaddr *)&info->sa_remote; + ni.remote.len = &n_saremote; + + if (!IOT_IS_EVENT(sock->io)) { + if (!sock->u.sd) { + return; + } + + rv = IOT_V1(sock->io).nameinfo(IOT_ARG(sock->io), sock->u.sd, &ni); + + if (ni.local.len == 0 || ni.remote.len == 0 || rv < 0) { + return; + } + + } else { + socklen_t sl_tmp = sizeof(info->sa_local); + if (sock->u.fd == INVALID_SOCKET) { + return; + } + + rv = getsockname(sock->u.fd, ni.local.name, &sl_tmp); + n_salocal = sl_tmp; + if (rv < 0) { + return; + } + rv = getpeername(sock->u.fd, ni.remote.name, &sl_tmp); + n_saremote = sl_tmp; + if (rv < 0) { + return; + } + } + info->naddr = n_salocal; +} + +int +lcbio_get_nameinfo(lcbio_SOCKET *sock, struct lcbio_NAMEINFO *nistrs) +{ + lcbio_CONNINFO *info = sock->info; + if (!info) { + return 0; + } + if (!info->naddr) { + return 0; + } + + if (!saddr_to_string((struct sockaddr*)&info->sa_remote, info->naddr, + nistrs->remote, sizeof(nistrs->remote))) { + return 0; + } + + if (!saddr_to_string((struct sockaddr*)&info->sa_local, info->naddr, + nistrs->local, sizeof(nistrs->local))) { + return 0; + } + + return 1; +} + +int +lcbio_is_netclosed(lcbio_SOCKET *sock, int flags) +{ + lcbio_pTABLE iot = sock->io; + + if (IOT_IS_EVENT(iot)) { + return IOT_V0IO(iot).is_closed(IOT_ARG(iot), sock->u.fd, flags); + } else { + return IOT_V1(iot).is_closed(IOT_ARG(iot), sock->u.sd, flags); + } +} + +lcb_error_t +lcbio_disable_nagle(lcbio_SOCKET *s) +{ + lcbio_pTABLE iot = s->io; + int val = 1, rv; + int is_supp = ((IOT_IS_EVENT(iot) && IOT_V0IO(iot).cntl)) || + (!IOT_IS_EVENT(iot) && IOT_V1(iot).cntl); + + if (!is_supp) { + return LCB_NOT_SUPPORTED; + } + + if (IOT_IS_EVENT(iot)) { + rv = IOT_V0IO(iot).cntl(IOT_ARG(iot), s->u.fd, LCB_IO_CNTL_SET, + LCB_IO_CNTL_TCP_NODELAY, &val); + } else { + rv = IOT_V1(iot).cntl(IOT_ARG(iot), s->u.sd, LCB_IO_CNTL_SET, + LCB_IO_CNTL_TCP_NODELAY, &val); + } + if (rv != 0) { + return lcbio_mklcberr(IOT_ERRNO(iot), s->settings); + } else { + return LCB_SUCCESS; + } +} + +void +lcbio_connreq_cancel(lcbio_CONNREQ *req) +{ + if (!req->u.cs) { + return; + } + + if (req->type == LCBIO_CONNREQ_POOLED) { + lcbio_mgr_cancel(req->u.preq); + } else if (req->type == LCBIO_CONNREQ_RAW) { + lcbio_connect_cancel(req->u.cs); + } else { + req->dtor(req->u.p_generic); + } + + req->u.cs = NULL; +} + +int +lcbio_ssl_supported(void) +{ +#ifdef LCB_NO_SSL + return 0; +#else + return 1; +#endif +} + +lcbio_pSSLCTX +lcbio_ssl_new__fallback(const char *ca, int noverify, lcb_error_t *errp, + lcb_settings *settings) +{ + (void)ca; (void)noverify; (void)settings; + if (errp) { *errp = LCB_CLIENT_FEATURE_UNAVAILABLE; } + return NULL; +} + + +#ifdef LCB_NO_SSL +void lcbio_ssl_free(lcbio_pSSLCTX a) { + (void)a; +} +lcb_error_t lcbio_ssl_apply(lcbio_SOCKET *a, lcbio_pSSLCTX b) { + (void)a;(void)b; + return LCB_CLIENT_FEATURE_UNAVAILABLE; +} +int lcbio_ssl_check(lcbio_SOCKET *s) { + (void)s; + return 0; +} +lcb_error_t lcbio_ssl_get_error(lcbio_SOCKET *s) { + (void)s; + return LCB_SUCCESS; +} +void lcbio_ssl_global_init(void) { +} +lcb_error_t lcbio_sslify_if_needed(lcbio_SOCKET *s, lcb_settings *st) { + (void)s;(void)st; + return LCB_SUCCESS; +} +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/lcbio/ioutils.h b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/ioutils.h new file mode 100644 index 00000000..19e5e5e1 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/ioutils.h @@ -0,0 +1,203 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCBIO_UTILS_H +#define LCBIO_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * @brief Various I/O-related utilities + */ + +typedef enum { + LCBIO_CSERR_BUSY, /* request pending */ + LCBIO_CSERR_INTR, /* eintr */ + LCBIO_CSERR_EINVAL, /* einval */ + LCBIO_CSERR_EFAIL, /* hard failure */ + LCBIO_CSERR_CONNECTED /* connection established */ +} lcbio_CSERR; + +/** + * Convert the system errno (indicated by 'syserr') + * @param syserr system error code + * @return a status code simplifying the error + */ +lcbio_CSERR +lcbio_mkcserr(int syserr); + +/** + * Assigns the target error code if it indicates a 'fatal' or 'relevant' error + * code. + * + * @param in Error code to inspect + * @param[out] out target pointer + */ +void +lcbio_mksyserr(lcbio_OSERR in, lcbio_OSERR *out); + +/** + * Convert a system error code into one suitable for returning to the user + * @param in The code received. This can be 0, for graceful shutdown + * @param settings The settings for the library + * @return An error code. + */ +lcb_error_t +lcbio_mklcberr(lcbio_OSERR in, const lcb_settings *settings); + +/** + * Traverse the addrinfo structure and return a socket. + * @param io the iotable structure used to create the socket + * @param[in,out] ai an addrinfo structure + * @param[out] connerr an error if a socket could not be established. + * @return a new socket, or INVALID_SOCKET + * + * The ai structure should be considered as an opaque iterator. This function + * will look at the first entry in the list and attempt to create a socket. + * It will traverse through each entry and break when either a socket has + * been successfully created, or no more addrinfo entries remain. + */ +lcb_socket_t +lcbio_E_ai2sock(lcbio_pTABLE io, struct addrinfo **ai, int *connerr); + +lcb_sockdata_t * +lcbio_C_ai2sock(lcbio_pTABLE io, struct addrinfo **ai, int *conerr); + +struct lcbio_NAMEINFO { + char local[NI_MAXHOST + NI_MAXSERV + 2]; + char remote[NI_MAXHOST + NI_MAXSERV + 2]; +}; + +int +lcbio_get_nameinfo(lcbio_SOCKET *sock, struct lcbio_NAMEINFO *nistrs); + +/** Basic wrapper around the @ref lcb_ioE_chkclosed_fn family */ +int +lcbio_is_netclosed(lcbio_SOCKET *sock, int flags); + +/** Disable Nagle's algorithm on the socket */ +lcb_error_t +lcbio_disable_nagle(lcbio_SOCKET *s); + +void +lcbio__load_socknames(lcbio_SOCKET *sock); + +#ifdef _WIN32 +#define lcbio_syserrno GetLastError() +#else +#define lcbio_syserrno errno +#endif + + +/** + * @addtogroup lcbio + * @{ + * + * @name Pending Requests + * @{ + * + * The lcbio_CONNREQ structure may contain various forms of pending requests + * which are _cancellable_. This is useful to act as an abstraction over the + * various helper functions which may be employed to initiate a new connection + * or negotiate an existing one. + * + * The semantics of a _cancellable_ request are: + * + *
      + *
    • They represent a pending operation
    • + *
    • When cancelled, the pending operation is invalidated. This means that + * any callbacks scheduled as a result of the operation will not be invoked. + *
    • + * + *
    • If the pending operation has completed, the request is invalidated. This + * means the pointer for the request is considered invalid once the operation + * has completed + *
    • + *
    + */ + +/** + * @brief Container object for various forms of cancellable requests. + */ +typedef struct { + int type; /**< One of the LCBIO_CONNREQ_* constants */ + #define LCBIO_CONNREQ_RAW 1 + #define LCBIO_CONNREQ_POOLED 2 + #define LCBIO_CONNREQ_GENERIC 3 + union { + struct lcbio_CONNSTART *cs; /**< from lcbio_connect() */ + struct lcbio_MGRREQ *preq; /**< from lcbio_mgr_get() */ + void *p_generic; /**< Generic pointer. Destroyed via the dtor field */ + } u; + void (*dtor)(void *); +} lcbio_CONNREQ; + +/** @brief Clear an existing request, setting the pointer to NULL */ +#define LCBIO_CONNREQ_CLEAR(req) (req)->u.p_generic = NULL + +/** + * @brief Initialize a connreq with an lcbio_pCONNSTART handle + * @param req the request to initialize + * @param cs the lcbio_pCONNSTART returned by lcbio_connect() + */ +#define LCBIO_CONNREQ_MKRAW(req, cs) do { \ + (req)->u.cs = cs; \ + (req)->type = LCBIO_CONNREQ_RAW; \ +} while (0); + +/** + * @brief Initialize a connreq with an lcbio_pMGRREQ + * @param req The request to initialize + * @param sreq the lcbio_pMGRREQ returned by lcbio_mgr_get() + */ +#define LCBIO_CONNREQ_MKPOOLED(req, sreq) do { \ + (req)->u.preq = sreq; \ + (req)->type = LCBIO_CONNREQ_POOLED; \ +} while (0); + +/** + * Initialize a connreq with a generic pointer. + * @param req The request to initialize + * @param p the pointer representing the request + * @param dtorcb a callback invoked with `p` when the request has been cancelled + */ +#define LCBIO_CONNREQ_MKGENERIC(req, p, dtorcb) do { \ + (req)->u.p_generic = p; \ + (req)->type = LCBIO_CONNREQ_GENERIC; \ + (req)->dtor = (void (*)(void *))dtorcb; \ +} while (0); + +/** + * @brief cancels a pending request, if not yet cancelled. + * + * Cancels a pending request. If the request has already been cancelled (by + * calling this function), then this function does nothing + * @param req The request to cancel + */ +void +lcbio_connreq_cancel(lcbio_CONNREQ *req); + +/**@}*/ +/**@}*/ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/lcbio/lcbio.h b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/lcbio.h new file mode 100644 index 00000000..ceebee58 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/lcbio.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCBIO_H +#define LCBIO_H +#include "connect.h" +#include "manager.h" +#include "ioutils.h" +#include "ctx.h" +#endif + +/** + * @defgroup lcbio I/O + * @brief IO Core + * + * @details + * + * This module represents the I/O core of libcouchbase. Effort has been made + * so that this module in theory is usable outside of libcouchbase. + * + * # Architectural Overview + * + * The I/O core (_LCBIO_) has been designed to support different I/O models and + * operating environments, with the goal of being able to integrate natively + * into such environments with minimal performance loss. Integration is acheived + * through several layers. The first layer is the _IOPS_ system defined in + * and defines integration APIs for different I/O models. + * + * Afterwards, this is flattened and normalized into an _IO Table_ (`lcbio_TABLE`) + * which serves as a context and abstraction layer for unifying those two APIs + * where applicable. + * + * Finally the "End-user" APIs (in this case end-user means the application which + * requests a TCP connection or I/O on a socket) employs the + * `lcbio_connect` and `lcbio_CTX` systems to provide a uniform interface, the + * end result being that the underlying I/O model is completely abstracted. + */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/lcbio/manager.c b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/manager.c new file mode 100644 index 00000000..701bdb20 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/manager.c @@ -0,0 +1,584 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "manager.h" +#include "hostlist.h" +#include "iotable.h" +#include "timer-ng.h" +#include "internal.h" + +#define LOGARGS(mgr, lvl) mgr->settings, "lcbio_mgr", LCB_LOG_##lvl, __FILE__, __LINE__ + +typedef enum { + CS_PENDING, + CS_IDLE, + CS_LEASED +} cinfo_state; + +typedef enum { + RS_PENDING, + RS_ASSIGNED +} request_state; + +typedef char mgr_KEY[NI_MAXSERV + NI_MAXHOST + 2]; + +typedef struct lcbio_MGRHOST { + lcb_clist_t ll_idle; /* idle connections */ + lcb_clist_t ll_pending; /* pending cinfo */ + lcb_clist_t requests; /* pending requests */ + mgr_KEY key; /* host:port */ + struct lcbio_MGR *parent; + lcbio_pASYNC async; + unsigned n_total; /* number of total connections */ + unsigned refcount; +} mgr_HOST; + +typedef struct mgr_CINFO_st { + lcbio_PROTOCTX base; + lcb_list_t llnode; + mgr_HOST *parent; + lcbio_SOCKET *sock; + struct lcbio_CONNSTART *cs; + lcbio_pTIMER idle_timer; + int state; +} mgr_CINFO; + +typedef struct lcbio_MGRREQ { + lcb_list_t llnode; + lcbio_CONNDONE_cb callback; + void *arg; + mgr_HOST *host; + lcbio_pTIMER timer; + int state; + lcbio_SOCKET *sock; + lcb_error_t err; +} mgr_REQ; + +#define HE_NPEND(he) LCB_CLIST_SIZE(&(he)->ll_pending) +#define HE_NIDLE(he) LCB_CLIST_SIZE(&(he)->ll_idle) +#define HE_NREQS(he) LCB_CLIST_SIZE(&(he)->requests) +#define HE_NLEASED(he) ((he)->n_total - (HE_NIDLE(he) + HE_NPEND(he))) + +static void on_idle_timeout(void *cookie); +static void he_available_notify(void *cookie); +static void he_dump(mgr_HOST *he, FILE *out); +static void he_unref(mgr_HOST *he); +static void mgr_unref(lcbio_MGR *mgr); + +#define he_ref(he) (he)->refcount++ +#define mgr_ref(mgr) (mgr)->refcount++ + +static const char *get_hehost(mgr_HOST *h) { + if (!h) { return "NOHOST:NOPORT"; } + return h->key; +} + +/** Format string arguments for %p%s:%s */ +#define HE_LOGID(h) get_hehost(h), (void*)h +#define HE_LOGFMT "<%s> (HE=%p) " + +static mgr_CINFO * +cinfo_from_sock(lcbio_SOCKET *sock) +{ + lcbio_PROTOCTX *ctx = lcbio_protoctx_get(sock, LCBIO_PROTOCTX_POOL); + return (mgr_CINFO *)ctx; +} + +static void +destroy_cinfo(mgr_CINFO *info) +{ + info->parent->n_total--; + if (info->state == CS_IDLE) { + lcb_clist_delete(&info->parent->ll_idle, &info->llnode); + + } else if (info->state == CS_PENDING && info->cs) { + lcbio_connect_cancel(info->cs); + } + + if (info->sock) { + lcbio_SOCKET *s = info->sock; + info->sock = NULL; + lcbio_protoctx_delptr(s, &info->base, 0); + lcbio_unref(s); + } + lcbio_timer_destroy(info->idle_timer); + he_unref(info->parent); + free(info); +} + +static void +cinfo_protoctx_dtor(lcbio_PROTOCTX *ctx) +{ + mgr_CINFO *info = (void *)ctx; + info->sock = NULL; + destroy_cinfo(info); +} + +lcbio_MGR * +lcbio_mgr_create(lcb_settings *settings, lcbio_TABLE *io) +{ + lcbio_MGR *pool = calloc(1, sizeof(*pool)); + if (!pool) { + return NULL; + } + + if ((pool->ht = lcb_hashtable_nc_new(32)) == NULL) { + free(pool); + return NULL; + } + + pool->settings = settings; + pool->io = io; + mgr_ref(pool); + return pool; +} + +static void +iterfunc(const void *k, lcb_size_t nk, const void *v, lcb_size_t nv, void *arg) +{ + lcb_clist_t *he_list = (lcb_clist_t *)arg; + mgr_HOST *he = (mgr_HOST *)v; + lcb_list_t *cur, *next; + + LCB_LIST_SAFE_FOR(cur, next, (lcb_list_t *)&he->ll_idle) { + mgr_CINFO *info = LCB_LIST_ITEM(cur, mgr_CINFO, llnode); + destroy_cinfo(info); + } + + LCB_LIST_SAFE_FOR(cur, next, (lcb_list_t *)&he->ll_pending) { + mgr_CINFO *info = LCB_LIST_ITEM(cur, mgr_CINFO, llnode); + destroy_cinfo(info); + } + + memset(&he->ll_idle, 0, sizeof(he->ll_idle)); + lcb_clist_append(he_list, (lcb_list_t *)&he->ll_idle); + + (void)k; (void)nk; (void)nv; +} + +static void +he_unref(mgr_HOST *host) +{ + if (--host->refcount) { + return; + } + + mgr_unref(host->parent); + free(host); +} + +static void +mgr_unref(lcbio_MGR *mgr) +{ + if (--mgr->refcount) { + return; + } + genhash_free(mgr->ht); + free(mgr); +} + +void +lcbio_mgr_destroy(lcbio_MGR *mgr) +{ + lcb_clist_t hes; + lcb_list_t *cur, *next; + lcb_clist_init(&hes); + + genhash_iter(mgr->ht, iterfunc, &hes); + + LCB_LIST_SAFE_FOR(cur, next, (lcb_list_t*)&hes) { + mgr_HOST *he = LCB_LIST_ITEM(cur, mgr_HOST, ll_idle); + genhash_delete(mgr->ht, he->key, strlen(he->key)); + lcb_clist_delete(&hes, (lcb_list_t *)&he->ll_idle); + lcbio_timer_destroy(he->async); + he->async = NULL; + he_unref(he); + } + mgr_unref(mgr); +} + +static void +invoke_request(mgr_REQ *req) +{ + if (req->sock) { + mgr_CINFO *info = cinfo_from_sock(req->sock); + lcb_assert(info->state == CS_IDLE); + info->state = CS_LEASED; + req->state = RS_ASSIGNED; + lcbio_timer_disarm(info->idle_timer); + lcb_log(LOGARGS(info->parent->parent, DEBUG), HE_LOGFMT "Assigning R=%p SOCKET=%p",HE_LOGID(info->parent), (void*)req, (void*)req->sock); + } + + if (req->timer) { + lcbio_timer_destroy(req->timer); + req->timer = NULL; + } + + req->callback(req->sock, req->arg, req->err, 0); + if (req->sock) { + lcbio_unref(req->sock); + } + free(req); +} + +/** + * Called to notify that a connection has become available. + */ +static void +connection_available(mgr_HOST *he) +{ + while (LCB_CLIST_SIZE(&he->requests) && LCB_CLIST_SIZE(&he->ll_idle)) { + mgr_REQ *req; + mgr_CINFO *info; + lcb_list_t *reqitem, *connitem; + + reqitem = lcb_clist_shift(&he->requests); + connitem = lcb_clist_pop(&he->ll_idle); + + req = LCB_LIST_ITEM(reqitem, mgr_REQ, llnode); + info = LCB_LIST_ITEM(connitem, mgr_CINFO, llnode); + req->sock = info->sock; + req->err = LCB_SUCCESS; + invoke_request(req); + } +} + +/** + * Connection callback invoked from lcbio_connect() when a result is received + */ +static void +on_connected(lcbio_SOCKET *sock, void *arg, lcb_error_t err, lcbio_OSERR oserr) +{ + mgr_CINFO *info = arg; + mgr_HOST *he = info->parent; + lcb_assert(info->state == CS_PENDING); + info->cs = NULL; + + lcb_log(LOGARGS(he->parent, DEBUG), HE_LOGFMT "Received result for I=%p,C=%p; E=0x%x", HE_LOGID(he), (void*)info, (void*)sock, err); + lcb_clist_delete(&he->ll_pending, &info->llnode); + + if (err != LCB_SUCCESS) { + /** If the connection failed, fail out all remaining requests */ + lcb_list_t *cur, *next; + LCB_LIST_SAFE_FOR(cur, next, (lcb_list_t *)&he->requests) { + mgr_REQ *req = LCB_LIST_ITEM(cur, mgr_REQ, llnode); + lcb_clist_delete(&he->requests, &req->llnode); + req->sock = NULL; + req->err = err; + invoke_request(req); + } + + destroy_cinfo(info); + + } else { + info->state = CS_IDLE; + info->sock = sock; + lcbio_ref(info->sock); + lcbio_protoctx_add(sock, &info->base); + + lcb_clist_append(&he->ll_idle, &info->llnode); + lcbio_timer_rearm(info->idle_timer, he->parent->tmoidle); + connection_available(info->parent); + } + + (void)oserr; +} + +static void +start_new_connection(mgr_HOST *he, uint32_t tmo) +{ + lcb_host_t tmphost; + lcb_error_t err; + + mgr_CINFO *info = calloc(1, sizeof(*info)); + info->state = CS_PENDING; + info->parent = he; + + info->base.id = LCBIO_PROTOCTX_POOL; + info->base.dtor = cinfo_protoctx_dtor; + info->idle_timer = lcbio_timer_new(he->parent->io, info, on_idle_timeout); + + err = lcb_host_parsez(&tmphost, he->key, 80); + if (err != LCB_SUCCESS) { + lcb_log(LOGARGS(he->parent, ERROR), HE_LOGFMT "Could not parse host! Will supply dummy host", HE_LOGID(he)); + strcpy(tmphost.host, "BADHOST"); + strcpy(tmphost.port, "BADPORT"); + } + lcb_log(LOGARGS(he->parent, DEBUG), HE_LOGFMT "Starting connection on I=%p", HE_LOGID(he), (void*)info); + + info->cs = lcbio_connect(he->parent->io, he->parent->settings, &tmphost, + tmo, on_connected, info); + + lcb_clist_append(&he->ll_pending, &info->llnode); + he->n_total++; + he_ref(he); +} + +static void +on_request_timeout(void *cookie) +{ + mgr_REQ *req = cookie; + lcb_clist_delete(&req->host->requests, &req->llnode); + req->err = LCB_ETIMEDOUT; + invoke_request(req); +} + +static void +async_invoke_request(void *cookie) +{ + + mgr_REQ *req = cookie; + mgr_CINFO *cinfo = cinfo_from_sock(req->sock); + cinfo->state = CS_IDLE; + invoke_request(req); +} + +mgr_REQ * +lcbio_mgr_get(lcbio_MGR *pool, lcb_host_t *dest, uint32_t timeout, + lcbio_CONNDONE_cb handler, void *arg) +{ + mgr_HOST *he; + lcb_list_t *cur; + mgr_REQ *req = calloc(1, sizeof(*req)); + mgr_KEY key = { 0 }; + + sprintf(key, "%s:%s", dest->host, dest->port); + + req->callback = handler; + req->arg = arg; + + he = genhash_find(pool->ht, key, strlen(key)); + if (!he) { + he = calloc(1, sizeof(*he)); + he->parent = pool; + he->async = lcbio_timer_new(pool->io, he, he_available_notify); + strcpy(he->key, key); + + lcb_clist_init(&he->ll_idle); + lcb_clist_init(&he->ll_pending); + lcb_clist_init(&he->requests); + + /** Not copied */ + genhash_store(pool->ht, he->key, strlen(he->key), he, 0); + he_ref(he); + mgr_ref(pool); + } + + req->host = he; + + GT_POPAGAIN: + + cur = lcb_clist_pop(&he->ll_idle); + if (cur) { + int clstatus; + mgr_CINFO *info = LCB_LIST_ITEM(cur, mgr_CINFO, llnode); + + clstatus = lcbio_is_netclosed(info->sock, LCB_IO_SOCKCHECK_PEND_IS_ERROR); + + if (clstatus == LCB_IO_SOCKCHECK_STATUS_CLOSED) { + lcb_log(LOGARGS(pool, WARN), HE_LOGFMT "Pooled socket is dead. Continuing to next one", HE_LOGID(he)); + + /* Set to CS_LEASED, since it's not inside any of our lists */ + info->state = CS_LEASED; + destroy_cinfo(info); + goto GT_POPAGAIN; + } + + lcbio_timer_disarm(info->idle_timer); + req->sock = info->sock; + req->state = RS_ASSIGNED; + req->timer = lcbio_timer_new(pool->io, req, async_invoke_request); + info->state = CS_LEASED; + lcbio_async_signal(req->timer); + lcb_log(LOGARGS(pool, INFO), HE_LOGFMT "Found ready connection in pool. Reusing socket and not creating new connection", HE_LOGID(he)); + + } else { + req->state = RS_PENDING; + req->timer = lcbio_timer_new(pool->io, req, on_request_timeout); + lcbio_timer_rearm(req->timer, timeout); + + lcb_clist_append(&he->requests, &req->llnode); + if (HE_NPEND(he) < HE_NREQS(he)) { + lcb_log(LOGARGS(pool, DEBUG), HE_LOGFMT "Creating new connection because none are available in the pool", HE_LOGID(he)); + start_new_connection(he, timeout); + + } else { + lcb_log(LOGARGS(pool, DEBUG), HE_LOGFMT "Not creating a new connection. There are still pending ones", HE_LOGID(he)); + } + } + + return req; +} + +/** + * Invoked when a new socket is available for allocation within the + * request queue. + */ +static void +he_available_notify(void *cookie) +{ + connection_available((mgr_HOST *)cookie); +} + +void +lcbio_mgr_cancel(mgr_REQ *req) +{ + mgr_HOST *he = req->host; + lcbio_MGR *mgr = he->parent; + if (req->timer) { + lcbio_timer_destroy(req->timer); + req->timer = NULL; + } + + if (req->sock) { + lcb_log(LOGARGS(mgr, DEBUG), HE_LOGFMT "Cancelling request=%p with existing connection", HE_LOGID(he), (void*)req); + lcbio_mgr_put(req->sock); + lcbio_async_signal(he->async); + + } else { + lcb_log(LOGARGS(mgr, DEBUG), HE_LOGFMT "Request=%p has no connection.. yet", HE_LOGID(he), (void*)req); + lcb_clist_delete(&he->requests, &req->llnode); + } + free(req); +} + +static void +on_idle_timeout(void *cookie) +{ + mgr_CINFO *info = cookie; + + lcb_log(LOGARGS(info->parent->parent, DEBUG), HE_LOGFMT "Idle connection expired", HE_LOGID(info->parent)); + + lcbio_unref(info->sock); +} + +void +lcbio_mgr_put(lcbio_SOCKET *sock) +{ + mgr_HOST *he; + lcbio_MGR *mgr; + mgr_CINFO *info = cinfo_from_sock(sock); + + if (!info) { + fprintf(stderr, "Requested put() for non-pooled (or detached) socket=%p\n", (void *)sock); + lcbio_unref(sock); + return; + } + + he = info->parent; + mgr = he->parent; + + if (HE_NIDLE(he) >= mgr->maxidle) { + lcb_log(LOGARGS(mgr, INFO), HE_LOGFMT "Closing idle connection. Too many in quota", HE_LOGID(he)); + lcbio_unref(info->sock); + return; + } + + lcb_log(LOGARGS(mgr, INFO), HE_LOGFMT "Placing socket back into the pool. I=%p,C=%p", HE_LOGID(he), (void*)info, (void*)sock); + lcbio_timer_rearm(info->idle_timer, mgr->tmoidle); + lcb_clist_append(&he->ll_idle, &info->llnode); + info->state = CS_IDLE; +} + +void +lcbio_mgr_discard(lcbio_SOCKET *sock) +{ + lcbio_unref(sock); +} + +void +lcbio_mgr_detach(lcbio_SOCKET *sock) +{ + lcbio_protoctx_delid(sock, LCBIO_PROTOCTX_POOL, 1); +} + + +#define CONN_INDENT " " + +static void +write_he_list(lcb_clist_t *ll, FILE *out) +{ + lcb_list_t *llcur; + LCB_LIST_FOR(llcur, (lcb_list_t *)ll) { + mgr_CINFO *info = LCB_LIST_ITEM(llcur, mgr_CINFO, llnode); + fprintf(out, "%sCONN [I=%p,C=%p ", + CONN_INDENT, (void *)info, (void *)&info->sock); + + if (info->sock->io->model == LCB_IOMODEL_EVENT) { + fprintf(out, "SOCKFD=%d", (int)info->sock->u.fd); + } else { + fprintf(out, "SOCKDATA=%p", (void *)info->sock->u.sd); + } + fprintf(out, " STATE=0x%x", info->state); + fprintf(out, "]\n"); + } + +} + +static void +he_dump(mgr_HOST *he, FILE *out) +{ + lcb_list_t *llcur; + fprintf(out, "HOST=%s", he->key); + fprintf(out, "Requests=%d, Idle=%d, Pending=%d, Leased=%d\n", + (int)HE_NREQS(he), (int)HE_NIDLE(he), (int)HE_NPEND(he), (int)HE_NLEASED(he)); + + fprintf(out, CONN_INDENT "Idle Connections:\n"); + write_he_list(&he->ll_idle, out); + fprintf(out, CONN_INDENT "Pending Connections: \n"); + write_he_list(&he->ll_pending, out); + fprintf(out, CONN_INDENT "Pending Requests:\n"); + + LCB_LIST_FOR(llcur, (lcb_list_t *)&he->requests) { + mgr_REQ *req = LCB_LIST_ITEM(llcur, mgr_REQ, llnode); + union { + lcbio_CONNDONE_cb cb; + void *ptr; + } u_cb; + + u_cb.cb = req->callback; + + fprintf(out, "%sREQ [R=%p, Callback=%p, Data=%p, State=0x%x]\n", + CONN_INDENT, (void *)req, u_cb.ptr, (void *)req->arg, + req->state); + } + + fprintf(out, "\n"); + +} + +static void +dumpfunc(const void *k, lcb_size_t nk, const void *v, lcb_size_t nv, void *arg) +{ + FILE *out = (FILE *)arg; + mgr_HOST *he = (void *)v; + he_dump(he, out); + (void)nk;(void)k;(void)nv; +} + +/** + * Dumps the connection manager state to stderr + */ +LCB_INTERNAL_API +void lcbio_mgr_dump(lcbio_MGR *mgr, FILE *out) +{ + if (out == NULL) { + out = stderr; + } + + genhash_iter(mgr->ht, dumpfunc, out); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/lcbio/manager.h b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/manager.h new file mode 100644 index 00000000..9b2bbfad --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/manager.h @@ -0,0 +1,156 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCBIO_MANAGER_H +#define LCBIO_MANAGER_H +#include "connect.h" +#include "settings.h" +#include "contrib/genhash/genhash.h" +#include "list.h" +#include + + +/** + * @file + * @brief Socket pooling routines + * + * @details + * General purpose connection manager for LCB sockets. This object is + * responsible for maintaining and properly handling idle connections + * and pooling them (optionally). + */ + +/** + * @ingroup lcbio + * @defgroup lcbio-mgr Socket Pooling + * @addtogroup lcbio-mgr + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Cancellable pool request */ +struct lcbio_MGRREQ; + +/** @brief Socket Pool */ +typedef struct lcbio_MGR { + genhash_t* ht; + lcb_settings *settings; + lcbio_pTABLE io; + + /** + * Maximum number of microseconds for a connection to idle inside the pool + * before being closed + */ + uint32_t tmoidle; + unsigned maxtotal; + unsigned maxidle; /**< Maximum number of idle connections, per host */ + unsigned refcount; +} lcbio_MGR; + +/** + * Create a socket pool controlled by the given settings and IO structure. + * This function will increment the refcount on both the settings and table + * objects. + */ +LCB_INTERNAL_API +lcbio_MGR* +lcbio_mgr_create(lcb_settings *settings, lcbio_pTABLE io); + +/** + * Destroy the socket pool. Note that internally this just decrements the + * reference count. The object is only destroyed when its count hits zero. + */ +LCB_INTERNAL_API +void +lcbio_mgr_destroy(lcbio_MGR *); + +/** + * Request a connection from the socket pool. The semantics and prototype + * of this function are by design similar to lcbio_connect() as they do the + * same things. + * + * @param mgr + * @param dest the host to connect to + * @param timeout amount of time to wait for a connection to be estblished + * @param handler a callback to invoke when the result is ready + * @param arg an argument passed to the callback + * @return a request handle which may be cancelled + * @see lcbio_connect() + */ +LCB_INTERNAL_API +struct lcbio_MGRREQ * +lcbio_mgr_get(lcbio_MGR *mgr, lcb_host_t *dest, uint32_t timeout, + lcbio_CONNDONE_cb handler, void *arg); + +/** + * Cancel a pending request. The callback for the request must have not already + * been invoked (if it has, use sockpool_put) + * @param req the request to cancel + */ +LCB_INTERNAL_API +void +lcbio_mgr_cancel(struct lcbio_MGRREQ *req); + +/** + * Release a socket back into the pool. This means the socket is no longer + * used and shall be available for reuse for another request. To verify these + * constraints, the socket's reference count must be one. Once the socket + * has been released its reference count should not be modified. + */ +LCB_INTERNAL_API +void lcbio_mgr_put(lcbio_SOCKET *sock); + +/** + * Mark a slot as available but discard the current connection. This should be + * done if the connection itself is "dirty", i.e. has a protocol error on it + * or is otherwise not suitable for reuse + */ +LCB_INTERNAL_API +void lcbio_mgr_discard(lcbio_SOCKET *sock); + +/** + * Like lcbio_mgr_discard() except the source connection is left untouched. It + * is removed from the pool instead. + * + * Because the lcbio_MGR object itself has internal limits and thresholds on how + * many leased and/or open connections it can contain, when a connection receives + * an error it must either be discarded back to the pool (in which case the + * connection is cleaned up and is freed) or it must be detached (in which case + * the connection object itself still remains valid, but the pool does not know + * about it, and all its counters are restored, as with lcbio_mgr_discard()). + * + * lcbio_mgr_discard() itself is now implemented as the equivalent to: + * `lcbio_mgr_detach(mgr, conn)`; + */ +LCB_INTERNAL_API +void lcbio_mgr_detach(lcbio_SOCKET *sock); + +/** + * Dumps the connection manager state to stderr + */ +LCB_INTERNAL_API +void lcbio_mgr_dump(lcbio_MGR *mgr, FILE *out); + +#ifdef __cplusplus +} +#endif +/**@}*/ + +#endif /* LCB_SOCKPOOL_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/lcbio/protoctx.c b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/protoctx.c new file mode 100644 index 00000000..99b27aa8 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/protoctx.c @@ -0,0 +1,84 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "connect.h" + +void +lcbio_protoctx_add(lcbio_SOCKET *sock, lcbio_PROTOCTX *ctx) +{ + lcb_list_append(&sock->protos, &ctx->ll); +} + +lcbio_PROTOCTX * +lcbio_protoctx_get(lcbio_SOCKET *sock, lcbio_PROTOID id) +{ + lcb_list_t *ll; + LCB_LIST_FOR(ll, &sock->protos) { + lcbio_PROTOCTX *cur = LCB_LIST_ITEM(ll, lcbio_PROTOCTX, ll); + if (cur->id == id) { + return cur; + } + } + return NULL; +} + + +#define DEL_BY_ID 1 +#define DEL_BY_PTR 2 +static lcbio_PROTOCTX * +del_common(lcbio_SOCKET *sock, int mode, lcbio_PROTOID id, + lcbio_PROTOCTX *ctx, int dtor) +{ + lcb_list_t *ll, *next; + LCB_LIST_SAFE_FOR(ll, next, &sock->protos) { + lcbio_PROTOCTX *cur = LCB_LIST_ITEM(ll, lcbio_PROTOCTX, ll); + if (mode == DEL_BY_ID && cur->id != id) { + continue; + } else if (cur != ctx) { + continue; + } + lcb_list_delete(&cur->ll); + if (dtor) { + cur->dtor(cur); + } + return cur; + } + return NULL; +} + +lcbio_PROTOCTX * +lcbio_protoctx_delid(lcbio_SOCKET *s, lcbio_PROTOID id, int dtor) +{ + return del_common(s, DEL_BY_ID, id, NULL, dtor); +} + +void +lcbio_protoctx_delptr(lcbio_SOCKET *s, lcbio_PROTOCTX *ctx, int dtor) +{ + del_common(s, DEL_BY_PTR, 0, ctx, dtor); +} + +void +lcbio__protoctx_delall(lcbio_SOCKET *s) +{ + lcb_list_t *llcur, *llnext; + LCB_LIST_SAFE_FOR(llcur, llnext, &s->protos) { + lcbio_PROTOCTX *cur = LCB_LIST_ITEM(llcur, lcbio_PROTOCTX, ll); + lcb_list_delete(llcur); + cur->dtor(cur); + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/lcbio/rw-inl.h b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/rw-inl.h new file mode 100644 index 00000000..a2a4b062 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/rw-inl.h @@ -0,0 +1,115 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Inline routines for reading and writing socket buffers + */ +#include +#include /* For IOV_MAX */ +#ifndef INLINE +#ifdef _MSC_VER +#define INLINE __inline +#elif __GNUC__ +#define INLINE __inline__ +#else +#define INLINE inline +#endif /* MSC_VER */ +#endif /* !INLINE */ + +#define RWINL_IOVSIZE 32 +#if defined(IOV_MAX) && IOV_MAX < RWINL_IOVSIZE +#undef RWINL_IOVSIZE +#define RWINL_IOVSIZE IOV_MAX +#endif + +#ifndef USE_EAGAIN +#define C_EAGAIN 0 +#else +#define C_EAGAIN EAGAIN +#endif + +static INLINE lcbio_IOSTATUS +lcbio_E_rdb_slurp(lcbio_CTX *ctx, rdb_IOROPE *ior) +{ + lcb_ssize_t rv; + lcb_IOV iov[RWINL_IOVSIZE]; + unsigned niov; + lcbio_TABLE *iot = ctx->io; + const lcb_U32 rdsize = ctx->sock->settings->read_chunk_size; + lcb_U32 total_nr = 0; + + do { + niov = rdb_rdstart(ior, (nb_IOV *)iov, RWINL_IOVSIZE); + GT_READ: + rv = IOT_V0IO(iot).recvv(IOT_ARG(iot), CTX_FD(ctx), iov, niov); + if (rv > 0) { + rdb_rdend(ior, rv); + if (rdsize && (total_nr += rv) >= rdsize) { + return LCBIO_PENDING; + } + } else if (rv == -1) { + switch (IOT_ERRNO(iot)) { + case EWOULDBLOCK: + case C_EAGAIN: + return LCBIO_PENDING; + case EINTR: + goto GT_READ; + default: + ctx->sock->last_error = IOT_ERRNO(iot); + return LCBIO_IOERR; + } + } else { + return LCBIO_SHUTDOWN; + } + } while (1); + /* UNREACHED */ + return LCBIO_IOERR; +} + +static INLINE lcbio_IOSTATUS +lcbio_E_rb_write(lcbio_CTX *ctx, ringbuffer_t *buf) +{ + lcb_IOV iov[2]; + lcb_ssize_t nw; + lcbio_TABLE *iot = ctx->io; + while (buf->nbytes) { + unsigned niov; + ringbuffer_get_iov(buf, RINGBUFFER_READ, iov); +#if RWINL_IOVSIZE < 2 + niov = 1; +#else + niov = iov[1].iov_len ? 2 : 1; +#endif + nw = IOT_V0IO(iot).sendv(IOT_ARG(iot), CTX_FD(ctx), iov, niov); + if (nw == -1) { + switch (IOT_ERRNO(iot)) { + case EINTR: + break; + case EWOULDBLOCK: + case C_EAGAIN: + return LCBIO_PENDING; + default: + ctx->sock->last_error = IOT_ERRNO(iot); + return LCBIO_IOERR; + } + } + if (nw) { + ringbuffer_consumed(buf, nw); + } + } + return LCBIO_COMPLETED; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/lcbio/ssl.h b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/ssl.h new file mode 100644 index 00000000..9797a1ac --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/ssl.h @@ -0,0 +1,149 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_SSL_H +#define LCB_SSL_H +#include +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * @brief SSL Socket Routines + */ + +/** + * @ingroup lcbio + * @defgroup lcbio-ssl SSL Routines + * + * @details + * This file contains the higher level API interfacing with _LCBIO_. It provides + * APIs to "patch" a socket with SSL as well as establish settings for SSL + * encryption. + * + * @addtogroup lcbio-ssl + * @{ + */ + + +/** @brief Wrapper around OpenSSL's `SSL_CTX` */ +typedef struct lcbio_SSLCTX *lcbio_pSSLCTX; + +/** + * @brief Determine if SSL is supported in the current build + * @return true if supported, false otherwise + */ +int +lcbio_ssl_supported(void); + +lcbio_pSSLCTX +lcbio_ssl_new__fallback(const char *, int, lcb_error_t *, lcb_settings *); + +#ifndef LCB_NO_SSL +/** + * Create a new SSL context to be used to establish SSL policy. + * @param cafile Optional path to CA file + * @param noverify To not attempt to verify server's certificate + * @param errp a pointer to contain the error code if initialization failed + * @param settings settings structure, used for logging. + * + * @return A new SSL context, or NULL on error. + */ +lcbio_pSSLCTX +lcbio_ssl_new(const char *cafile, int noverify, lcb_error_t *errp, + lcb_settings *settings); +#else +#define lcbio_ssl_new lcbio_ssl_new__fallback +#endif + + +/** + * Free the SSL context. This should be done when libcouchbase has nothing else + * to do with the certificate + * @param ctx + */ +void +lcbio_ssl_free(lcbio_pSSLCTX ctx); + +/** + * Apply the SSL settings to a given socket. + * + * The socket must be newly connected and must not have already been initialized + * with SSL (i.e. lcbio_ssl_check() returns false). + * + * @param sock The socket to which SSL should be applied + * @param sctx The context returned by lcbio_ssl_new() + * @return + */ +lcb_error_t +lcbio_ssl_apply(lcbio_SOCKET *sock, lcbio_pSSLCTX sctx); + +/** + * Checks whether the given socket is using SSL + * @param sock The socket to check + * @return true if using SSL, false if plain (or not yet applied) + */ +LCB_INTERNAL_API +int +lcbio_ssl_check(lcbio_SOCKET *sock); + +/** + * Retrieves the internal error code from the SSL object within the socket. + * Should only be called if lcbio_ssl_check() is true. + * + * @param sock + * @return An error code (if present), or LCB_SUCCESS if there is no internal + * error code. + */ +LCB_INTERNAL_API +lcb_error_t +lcbio_ssl_get_error(lcbio_SOCKET *sock); + +/** + * @brief + * Initialize any application-level globals needed for SSL support + * @todo There is currently nothing checking if this hasn't been called more + * than once. + */ +void +lcbio_ssl_global_init(void); + +struct lcb_settings_st; + +/** + * Apply SSL to the socket if the socket should use SSL and is not already + * an SSL socket. This is a convenience function that: + * + * 1. Checks the settings to see if SSL is enabled + * 2. Checks to see if the socket already has SSL (lcbio_ssl_check()) + * 3. Calls lcbio_ssl_apply if (1) and (2) are true. + * + * @param sock The socket to SSLify + * @param settings The settings structure from whence the context and policy are + * derived. + * @return + */ +lcb_error_t +lcbio_sslify_if_needed(lcbio_SOCKET *sock, struct lcb_settings_st *settings); + +/**@}*/ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/lcbio/timer-ng.h b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/timer-ng.h new file mode 100644 index 00000000..f7f63808 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/timer-ng.h @@ -0,0 +1,179 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCBIO_TIMER_H +#define LCBIO_TIMER_H + +#ifdef __cplusplus +extern "C" { +#endif /** __cplusplus */ + +/** + * @file + * @brief Timer Routines + * + * This file contains the timer routines. This provides a simpler interface + * than the one provided via the `lcb_timer_*` functions. + */ + +/** + * @ingroup lcbio + * @defgroup lcbio-timers Timer Routines + * + * @details + * + * The timer routines here allow for an asynchronous event to be scheduled + * within a given amount of time, or "immediately". The basic idea is that + * these allow "Safe" invocation of routines without worrying about reentrancy + * issues. + * + * There is natually a performance hit in using these functions (since this needs + * to operate with the event loop) so this shouldn't be used for normal steady + * state operations (such as memcached operations). + * + * A timer may be created via lcbio_timer_new(). The timer's initial state + * is _unarmed_, meaning it will not be invoked until one of the scheduling + * routines are invoked. + * + * When a timer is armed, its callback (passed lcbio_timer_new()) will be + * invoked with the argument provided to lcbio_timer_new() as well. + * + * To schedule a timer, use the lcbio_timer_rearm() to unconditionally schedule + * an event within a certain timeframe, or lcbio_async_signal() to invoke the + * timer as soon as possible, once the event loop regains control. + * + * To cancel an armed timer (that is, to ensure the event is _not_ called), use + * the lcbio_timer_disarm() function or the lcbio_async_cancel() function (which + * itself is just an alias). + * + * Timers are not persistent, meaning that once they are fired they will enter + * an inactive state. + * + * @addtogroup lcbio-timers + * @{ + */ + +/** @private */ +typedef enum { + LCBIO_TIMER_S_ENTERED = 0x01, + LCBIO_TIMER_S_DESTROYED = 0x02, + LCBIO_TIMER_S_ARMED = 0x04 +} lcbio_TIMERSTATE; + +/** + * @brief Timer callback + * @see lcb_timer_new() + */ +typedef void (*lcbio_TIMER_cb)(void *); + +typedef struct lcbio_TIMER { + void *event; + void *data; + lcbio_TIMER_cb callback; + uint32_t usec_; + lcbio_TIMERSTATE state; + lcbio_pTABLE io; +} lcbio_TIMER, lcbio_ASYNC; + +/** + * @brief Creates a new timer object. + * + * The newly created timer will be in an _unarmed_ state, but may + * may be activated with lcbio_timer_rearm() + * + * @param iot + * @param data + * @param callback + * @return A new timer object. Destroy with lcbio_timer_destroy() + */ +lcbio_TIMER * +lcbio_timer_new(lcbio_pTABLE iot, void *data, lcbio_TIMER_cb callback); + +/** + * @brief Release the memory allocated by the timers + * @param tm the timer to free + */ +void +lcbio_timer_destroy(lcbio_TIMER *tm); + +/** + * @brief Schedule the timer invocation + * @param timer The timer + * @param usec The number of microseconds (from now) in which the callback + * should be invoked + */ +void +lcbio_timer_rearm(lcbio_TIMER *timer, uint32_t usec); + +/** + * @brief Cancel a pending invocation + * @param timer The timer + * If no pending invocation is present, this does nothing + */ +void +lcbio_timer_disarm(lcbio_TIMER *timer); + +/** + * @brief Schedule an asynchronous call + * @param timer The timer to schedule + * + * This function is equivalent to calling + * @code{.c} + * lcbio_timer_rearm(timer, 0); + * @endcode + */ +void +lcbio_async_signal(lcbio_TIMER *timer); + +/** + * @brief alias for lcbio_timer_disarm() + * @param timer + */ +void +lcbio_async_cancel(lcbio_TIMER *timer); + +/** + * @brief Check if timer is armed + * @param timer the timer to inspect + * @return nonzero if armed, zero if unarmed. + */ +#define lcbio_timer_armed(timer) ((timer)->state & LCBIO_TIMER_S_ARMED) + +/** + * Get the callback that is to be invoked for the timer + * @param timer the timer to query + * @return the current callback + * @see lcbio_timer_set_target() + */ +#define lcbio_timer_get_target(timer) (timer)->callback + +/** + * Change the target callback for the timer + * @param timer the timer to modify + * @param tgt the target callback to set. + */ +#define lcbio_timer_set_target(timer, tgt) (timer)->callback = tgt + +void +lcbio_timer_dump(lcbio_TIMER *timer, FILE *fp); + +/**@}*/ + +#ifdef __cplusplus +} +#endif /** __cplusplus */ +#endif /* LCBIO_TIMER_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/lcbio/timer.c b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/timer.c new file mode 100644 index 00000000..28ba0fb0 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/lcbio/timer.c @@ -0,0 +1,132 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "connect.h" +#include "iotable.h" +#include "timer-ng.h" + +#define TMR_IS_DESTROYED(timer) ((timer)->state & LCBIO_TIMER_S_DESTROYED) +#define TMR_IS_ARMED(timer) ((timer)->state & LCBIO_TIMER_S_ARMED) + +static void +destroy_timer(lcbio_TIMER *timer) +{ + if (timer->event) { + timer->io->timer.destroy(timer->io->p, timer->event); + } + lcbio_table_unref(timer->io); + free(timer); +} + +static void +timer_callback(lcb_socket_t sock, short which, void *arg) +{ + lcbio_TIMER *timer = arg; + + lcb_assert(TMR_IS_ARMED(timer)); + lcb_assert(!TMR_IS_DESTROYED(timer)); + timer->state |= LCBIO_TIMER_S_ENTERED; + + lcbio_timer_disarm(timer); + timer->callback(timer->data); + + if (TMR_IS_DESTROYED(timer)) { + destroy_timer(timer); + } else { + timer->state &= ~LCBIO_TIMER_S_ENTERED; + } + + (void)sock; + (void)which; +} + + +lcbio_TIMER * +lcbio_timer_new(lcbio_TABLE *io, void *data, lcbio_TIMER_cb callback) +{ + lcbio_TIMER *ret = calloc(1, sizeof(*ret)); + + if (!ret) { + return NULL; + } + + ret->callback = callback; + ret->data = data; + ret->io = io; + ret->event = io->timer.create(IOT_ARG(io)); + lcbio_table_ref(io); + return ret; +} + +void +lcbio_timer_destroy(lcbio_TIMER *timer) +{ + lcbio_timer_disarm(timer); + if (timer->state & LCBIO_TIMER_S_ENTERED) { + timer->state |= LCBIO_TIMER_S_DESTROYED; + } else { + destroy_timer(timer); + } +} + +void +lcbio_timer_disarm(lcbio_TIMER *timer) +{ + if (!TMR_IS_ARMED(timer)) { + return; + } + + timer->state &= ~LCBIO_TIMER_S_ARMED; + timer->io->timer.cancel(timer->io->p, timer->event); +} + +void +lcbio_timer_rearm(lcbio_TIMER * timer, uint32_t usec) +{ + if (TMR_IS_ARMED(timer)) { + lcbio_timer_disarm(timer); + } + + timer->usec_ = usec; + timer->io->timer.schedule(timer->io->p, + timer->event, usec, timer, timer_callback); + timer->state |= LCBIO_TIMER_S_ARMED; +} + +void +lcbio_async_signal(lcbio_TIMER *timer) +{ + lcbio_timer_rearm(timer, 0); +} + +void +lcbio_async_cancel(lcbio_TIMER *timer) +{ + lcbio_timer_disarm(timer); +} + +void +lcbio_timer_dump(lcbio_TIMER *timer, FILE *fp) +{ + fprintf(fp, "~~ DUMP TIMER BEGIN ~~\n"); + fprintf(fp, "TIMER=%p\n", (void*)timer); + fprintf(fp, "INNER PTR=%p\n", timer->event); + fprintf(fp, "USERDATA=%p\n", timer->data); + fprintf(fp, "ACTIVE: %s\n", (timer->state & LCBIO_TIMER_S_ARMED) ? "YES":"NO"); + fprintf(fp, "INTERVAL: %lu\n", (unsigned long)timer->usec_); + fprintf(fp, "~~ DUMP TIMER END ~~\n"); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/legacy.c b/couchbase-sys/libcouchbase-2.7.0/src/legacy.c new file mode 100644 index 00000000..144286fe --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/legacy.c @@ -0,0 +1,430 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" +#include + +#if defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2) +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +typedef enum { + LCB_TIMER_STANDALONE = 1 << 0, + LCB_TIMER_PERIODIC = 1 << 1, + LCB_TIMER_EX = 1 << 2 +} lcb_timer_options; + +typedef enum { + LCB_TIMER_S_ENTERED = 0x01, + LCB_TIMER_S_DESTROYED = 0x02, + LCB_TIMER_S_ARMED = 0x04 +} lcb_timer_state; + +struct lcb_timer_st { + lcb_uint32_t usec_; + lcb_timer_state state; + lcb_timer_options options; + void *event; + const void *cookie; + lcb_timer_callback callback; + lcb_t instance; + struct lcbio_TABLE *io; +}; + +static void timer_rearm(lcb_timer_t timer, lcb_uint32_t usec); +static void timer_disarm(lcb_timer_t timer); +#define lcb_timer_armed(timer) ((timer)->state & LCB_TIMER_S_ARMED) +#define lcb_async_signal(async) lcb_timer_rearm(async, 0) +#define lcb_async_cancel(async) lcb_timer_disarm(async) +#define TMR_IS_PERIODIC(timer) ((timer)->options & LCB_TIMER_PERIODIC) +#define TMR_IS_DESTROYED(timer) ((timer)->state & LCB_TIMER_S_DESTROYED) +#define TMR_IS_STANDALONE(timer) ((timer)->options & LCB_TIMER_STANDALONE) +#define TMR_IS_ARMED(timer) ((timer)->state & LCB_TIMER_S_ARMED) + +static void destroy_timer(lcb_timer_t timer) { + if (timer->event) { + timer->io->timer.destroy(timer->io->p, timer->event); + } + lcbio_table_unref(timer->io); + memset(timer, 0xff, sizeof(*timer)); + free(timer); +} + +static void timer_callback(lcb_socket_t sock, short which, void *arg) { + lcb_timer_t timer = arg; + lcb_t instance = timer->instance; + + lcb_assert(TMR_IS_ARMED(timer)); + lcb_assert(!TMR_IS_DESTROYED(timer)); + + timer->state |= LCB_TIMER_S_ENTERED; + timer_disarm(timer); + timer->callback(timer, instance, timer->cookie); + + if (TMR_IS_DESTROYED(timer) == 0 && TMR_IS_PERIODIC(timer) != 0) { + timer_rearm(timer, timer->usec_); + return; + } + if (! TMR_IS_STANDALONE(timer)) { + lcb_aspend_del(&instance->pendops, LCB_PENDTYPE_TIMER, timer); + lcb_maybe_breakout(instance); + } + if (TMR_IS_DESTROYED(timer)) { + destroy_timer(timer); + } else { + timer->state &= ~LCB_TIMER_S_ENTERED; + } + (void)sock;(void)which; +} + +LIBCOUCHBASE_API +lcb_timer_t lcb_timer_create(lcb_t instance, const void *command_cookie, lcb_uint32_t usec, int periodic, lcb_timer_callback callback, lcb_error_t *error) { + lcb_timer_options options = 0; + lcb_timer_t tmr = calloc(1, sizeof(struct lcb_timer_st)); + tmr->io = instance->iotable; + + if (periodic) { + options |= LCB_TIMER_PERIODIC; + } + if (!tmr) { + *error = LCB_CLIENT_ENOMEM; + return NULL; + } + if (!callback) { + *error = LCB_EINVAL; + return NULL; + } + if (! (options & LCB_TIMER_STANDALONE)) { + lcb_assert(instance); + } + + lcbio_table_ref(tmr->io); + tmr->instance = instance; + tmr->callback = callback; + tmr->cookie = command_cookie; + tmr->options = options; + tmr->event = tmr->io->timer.create(tmr->io->p); + + if (tmr->event == NULL) { + free(tmr); + *error = LCB_CLIENT_ENOMEM; + return NULL; + } + + if ( (options & LCB_TIMER_STANDALONE) == 0) { + lcb_aspend_add(&instance->pendops, LCB_PENDTYPE_TIMER, tmr); + } + + timer_rearm(tmr, usec); + + *error = LCB_SUCCESS; + return tmr; +} + +LIBCOUCHBASE_API +lcb_error_t lcb_timer_destroy(lcb_t instance, lcb_timer_t timer) { + int standalone = timer->options & LCB_TIMER_STANDALONE; + if (!standalone) { + lcb_aspend_del(&instance->pendops, LCB_PENDTYPE_TIMER, timer); + } + timer_disarm(timer); + if (timer->state & LCB_TIMER_S_ENTERED) { + timer->state |= LCB_TIMER_S_DESTROYED; + lcb_assert(TMR_IS_DESTROYED(timer)); + } else { + destroy_timer(timer); + } + return LCB_SUCCESS; +} +static void timer_disarm(lcb_timer_t timer) { + if (!TMR_IS_ARMED(timer)) { + return; + } + timer->state &= ~LCB_TIMER_S_ARMED; + timer->io->timer.cancel(timer->io->p, timer->event); +} +static void timer_rearm(lcb_timer_t timer, lcb_uint32_t usec) { + if (TMR_IS_ARMED(timer)) { + timer_disarm(timer); + } + timer->usec_ = usec; + timer->io->timer.schedule(timer->io->p, timer->event, usec, timer, timer_callback); + timer->state |= LCB_TIMER_S_ARMED; +} + +LCB_INTERNAL_API void lcb__timer_destroy_nowarn(lcb_t instance, lcb_timer_t timer) { + lcb_timer_destroy(instance, timer); +} + +struct user_cookie { + void *cookie; + struct lcb_callback_st callbacks; + lcb_error_t retcode; +}; + +static void restore_user_env(lcb_t instance); +static void restore_wrapping_env(lcb_t instance, struct user_cookie *user, lcb_error_t error); +static void bootstrap_callback(lcb_t instance, lcb_error_t err) { + struct user_cookie *c = (void *)instance->cookie; + c->retcode = err; +} +static void stat_callback(lcb_t instance, const void *command_cookie, lcb_error_t error, const lcb_server_stat_resp_t *resp) { + struct user_cookie *c = (void *)instance->cookie; + restore_user_env(instance); + c->callbacks.stat(instance, command_cookie, error, resp); + restore_wrapping_env(instance, c, error); +} +static void version_callback(lcb_t instance, const void *command_cookie, lcb_error_t error, const lcb_server_version_resp_t *resp) { + struct user_cookie *c = (void *)instance->cookie; + restore_user_env(instance); + c->callbacks.version(instance, command_cookie, error, resp); + restore_wrapping_env(instance, c, error); +} +static void verbosity_callback(lcb_t instance, const void *command_cookie, lcb_error_t error, const lcb_verbosity_resp_t *resp) { + struct user_cookie *c = (void *)instance->cookie; + restore_user_env(instance); + c->callbacks.verbosity(instance, command_cookie, error, resp); + restore_wrapping_env(instance, c, error); +} +static void get_callback(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_get_resp_t *resp) { + struct user_cookie *c = (void *)instance->cookie; + restore_user_env(instance); + c->callbacks.get(instance, cookie, error, resp); + restore_wrapping_env(instance, c, error); +} +static void store_callback(lcb_t instance, const void *cookie, lcb_storage_t operation, lcb_error_t error, const lcb_store_resp_t *resp) { + struct user_cookie *c = (void *)instance->cookie; + restore_user_env(instance); + c->callbacks.store(instance, cookie, operation, error, resp); + restore_wrapping_env(instance, c, error); +} +static void arithmetic_callback(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_arithmetic_resp_t *resp) { + struct user_cookie *c = (void *)instance->cookie; + restore_user_env(instance); + c->callbacks.arithmetic(instance, cookie, error, resp); + restore_wrapping_env(instance, c, error); +} +static void remove_callback(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_remove_resp_t *resp) { + struct user_cookie *c = (void *)instance->cookie; + restore_user_env(instance); + c->callbacks.remove(instance, cookie, error, resp); + restore_wrapping_env(instance, c, error); +} +static void touch_callback(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_touch_resp_t *resp) { + struct user_cookie *c = (void *)instance->cookie; + restore_user_env(instance); + c->callbacks.touch(instance, cookie, error, resp); + restore_wrapping_env(instance, c, error); +} +static void http_complete_callback(lcb_http_request_t request, lcb_t instance, const void *cookie, lcb_error_t error, const lcb_http_resp_t *resp) { + struct user_cookie *c = (void *)instance->cookie; + restore_user_env(instance); + c->callbacks.http_complete(request, instance, cookie, error, resp); + restore_wrapping_env(instance, c, error); +} +static void http_data_callback(lcb_http_request_t request, lcb_t instance, const void *cookie, lcb_error_t error, const lcb_http_resp_t *resp) { + struct user_cookie *c = (void *)instance->cookie; + restore_user_env(instance); + c->callbacks.http_data(request, instance, cookie, error, resp); + restore_wrapping_env(instance, c, error); +} +static void flush_callback(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_flush_resp_t *resp) { + struct user_cookie *c = (void *)instance->cookie; + restore_user_env(instance); + c->callbacks.flush(instance, cookie, error, resp); + restore_wrapping_env(instance, c, error); +} +static void observe_callback(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_observe_resp_t *resp) { + struct user_cookie *c = (void *)instance->cookie; + restore_user_env(instance); + c->callbacks.observe(instance, cookie, error, resp); + restore_wrapping_env(instance, c, error); +} +static void durability_callback(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_durability_resp_t *resp) { + struct user_cookie *c = (void *)instance->cookie; + restore_user_env(instance); + c->callbacks.durability(instance, cookie, error, resp); + restore_wrapping_env(instance, c, error); +} +static void unlock_callback(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_unlock_resp_t *resp) { + struct user_cookie *c = (void *)instance->cookie; + restore_user_env(instance); + c->callbacks.unlock(instance, cookie, error, resp); + restore_wrapping_env(instance, c, error); +} +static void restore_user_env(lcb_t instance) { + struct user_cookie *cookie = (void *)instance->cookie; + /* Restore the users environment */ + instance->cookie = cookie->cookie; + instance->callbacks = cookie->callbacks; +} + +static void +restore_wrapping_env(lcb_t instance, struct user_cookie *user, lcb_error_t error) { + user->callbacks = instance->callbacks; + /* Install new callbacks */ + instance->callbacks.get = get_callback; + instance->callbacks.store = store_callback; + instance->callbacks.arithmetic = arithmetic_callback; + instance->callbacks.remove = remove_callback; + instance->callbacks.stat = stat_callback; + instance->callbacks.version = version_callback; + instance->callbacks.verbosity = verbosity_callback; + instance->callbacks.touch = touch_callback; + instance->callbacks.flush = flush_callback; + instance->callbacks.bootstrap = bootstrap_callback; + instance->callbacks.http_complete = http_complete_callback; + instance->callbacks.http_data = http_data_callback; + instance->callbacks.observe = observe_callback; + instance->callbacks.unlock = unlock_callback; + instance->callbacks.durability = durability_callback; + + user->cookie = (void *)instance->cookie; + user->retcode = error; + instance->cookie = user; +} + +lcb_error_t +lcb__synchandler_return(lcb_t instance) +{ + struct user_cookie cookie; + restore_wrapping_env(instance, &cookie, LCB_SUCCESS); + lcb_wait(instance); + restore_user_env(instance); + return cookie.retcode; +} + +LIBCOUCHBASE_API +void +lcb_behavior_set_syncmode(lcb_t instance, lcb_syncmode_t mode) +{ + LCBT_SETTING(instance, syncmode) = mode; +} +LIBCOUCHBASE_API +lcb_syncmode_t +lcb_behavior_get_syncmode(lcb_t instance) +{ + return LCBT_SETTING(instance, syncmode); +} + +LIBCOUCHBASE_API +lcb_error_t lcb_get_last_error(lcb_t instance){return instance->last_error;} + +LIBCOUCHBASE_API +lcb_error_t lcb__create_compat_230(lcb_cluster_t type, const void *specific, lcb_t *instance, struct lcb_io_opt_st *io) +{ + struct lcb_create_st cst = { 0 }; + const struct lcb_cached_config_st *cfg = specific; + const struct lcb_create_st *crp = &cfg->createopt; + lcb_error_t err; + lcb_size_t to_copy = 0; + + if (type != LCB_CACHED_CONFIG) { + return LCB_NOT_SUPPORTED; + } + + if (crp->version == 0) { + to_copy = sizeof(cst.v.v0); + } else if (crp->version == 1) { + to_copy = sizeof(cst.v.v1); + } else if (crp->version >= 2) { + to_copy = sizeof(cst.v.v2); + } else { + /* using version 3? */ + return LCB_NOT_SUPPORTED; + } + memcpy(&cst, crp, to_copy); + + if (io) { + cst.v.v0.io = io; + } + err = lcb_create(instance, &cst); + if (err != LCB_SUCCESS) { + return err; + } + err = lcb_cntl(*instance, LCB_CNTL_SET, LCB_CNTL_CONFIGCACHE, + (void *)cfg->cachefile); + if (err != LCB_SUCCESS) { + lcb_destroy(*instance); + } + return err; +} +struct compat_220 { + struct { + int version; + struct lcb_create_st1 v1; + } createopt; + const char *cachefile; +}; + +struct compat_230 { + struct { + int version; + struct lcb_create_st2 v2; + } createopt; + const char *cachefile; +}; + +#undef lcb_create_compat +/** + * This is _only_ called for versions <= 2.3.0. + * >= 2.3.0 uses the _230() symbol. + * + * The big difference between this and the _230 function is the struct layout, + * where the newer one contains the filename _before_ the creation options. + * + * Woe to he who relies on the compat_st as a 'subclass' of create_st.. + */ + +LIBCOUCHBASE_API +lcb_error_t lcb_create_compat(lcb_cluster_t type, const void *specific, lcb_t *instance, struct lcb_io_opt_st *io); +LIBCOUCHBASE_API +lcb_error_t lcb_create_compat(lcb_cluster_t type, const void *specific, lcb_t *instance, struct lcb_io_opt_st *io) +{ + struct lcb_cached_config_st dst; + const struct compat_220* src220 = specific; + + if (type == LCB_MEMCACHED_CLUSTER) { + return lcb__create_compat_230(type, specific, instance, io); + } else if (type != LCB_CACHED_CONFIG) { + return LCB_NOT_SUPPORTED; + } +#define copy_compat(v) \ + memcpy(&dst.createopt, &v->createopt, sizeof(v->createopt)); \ + dst.cachefile = v->cachefile; + + if (src220->createopt.version >= 2 || src220->cachefile == NULL) { + const struct compat_230* src230 = specific; + copy_compat(src230); + } else { + copy_compat(src220); + } + return lcb__create_compat_230(type, &dst, instance, io); +} +LIBCOUCHBASE_API void lcb_flush_buffers(lcb_t instance, const void *cookie) { + (void)instance;(void)cookie; +} + +LIBCOUCHBASE_API +lcb_error_t lcb_verify_struct_size(lcb_U32 id, lcb_U32 version, lcb_SIZE size) +{ + #define X(sname, sabbrev, idval, vernum) \ + if (idval == id && size == sizeof(sname) && version <= vernum) { return LCB_SUCCESS; } + LCB_XSSIZES(X); + #undef X + return LCB_EINVAL; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/list.c b/couchbase-sys/libcouchbase-2.7.0/src/list.c new file mode 100644 index 00000000..7fe8d3b5 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/list.c @@ -0,0 +1,144 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" + +void lcb_list_init(lcb_list_t *list) +{ + list->next = list; + list->prev = list; +} + +static void list_insert(lcb_list_t *prev, lcb_list_t *next, lcb_list_t *item) +{ + item->next = next; + item->prev = prev; + next->prev = item; + prev->next = item; +} + +void lcb_list_prepend(lcb_list_t *list, lcb_list_t *item) +{ + list_insert(list, list->next, item); +} + +void lcb_list_append(lcb_list_t *list, lcb_list_t *item) +{ + list_insert(list->prev, list, item); +} + +static void list_eject(lcb_list_t *prev, lcb_list_t *next) +{ + next->prev = prev; + prev->next = next; +} + +void lcb_list_delete(lcb_list_t *item) +{ + list_eject(item->prev, item->next); + item->next = item->prev = NULL; +} + + +lcb_list_t *lcb_list_shift(lcb_list_t *list) +{ + lcb_list_t *item; + + if (LCB_LIST_IS_EMPTY(list)) { + return NULL; + } + item = list->next; + lcb_list_delete(item); + return item; +} + +lcb_list_t *lcb_list_pop(lcb_list_t *list) +{ + lcb_list_t *item; + + if (LCB_LIST_IS_EMPTY(list)) { + return NULL; + } + item = list->prev; + lcb_list_delete(item); + return item; +} + +int lcb_list_contains(lcb_list_t *list, lcb_list_t *item) +{ + lcb_list_t *ptr = list->next; + + while (ptr != list && ptr != item) { + ptr = ptr->next; + } + + return (ptr == item) ? 1 : 0; +} + +void lcb_list_add_sorted(lcb_list_t *list, lcb_list_t *item, lcb_list_cmp_fn cmp) +{ + lcb_list_t *p; + + if (LCB_LIST_IS_EMPTY(list)) { + list_insert(list->prev, list, item); + } else { + LCB_LIST_FOR(p, list) { + if (cmp(item, p) < 0) { + break; + } + } + list_insert(p->prev, p, item); + } +} + + +void lcb_clist_init(lcb_clist_t *cl) +{ + lcb_list_init((lcb_list_t*)cl); + cl->size = 0; +} +void lcb_clist_append(lcb_clist_t *cl, lcb_list_t *item) +{ + lcb_list_append((lcb_list_t*)cl, item); + cl->size++; +} +void lcb_clist_prepend(lcb_clist_t *cl, lcb_list_t *item) +{ + lcb_list_prepend((lcb_list_t *)cl, item); + cl->size++; +} +void lcb_clist_delete(lcb_clist_t *cl, lcb_list_t *item) +{ + lcb_list_delete(item); + cl->size--; +} +lcb_list_t *lcb_clist_pop(lcb_clist_t *cl) +{ + lcb_list_t *ret = lcb_list_pop((lcb_list_t*)cl); + if (ret) { + cl->size--; + } + return ret; +} +lcb_list_t *lcb_clist_shift(lcb_clist_t *cl) +{ + lcb_list_t *ret = lcb_list_shift((lcb_list_t *)cl); + if (ret) { + cl->size--; + } + return ret; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/list.h b/couchbase-sys/libcouchbase-2.7.0/src/list.h new file mode 100644 index 00000000..1c49f10f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/list.h @@ -0,0 +1,127 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBCOUCHBASE_LIST_H +#define LIBCOUCHBASE_LIST_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + + /* Circular list implementation + * + * Could be used to implement queues, stacks etc. + * + * Declare list in your structure: + * + * typedef struct { + * lcb_list_t list; + * lcb_uint32_t msec; + * void (*callback)(struct timer *tm); + * } lcb_timer_t; + * + * Initialize head of list: + * + * lcb_timer_t tm; + * lcb_list_init(&tm.list); + * + * Add new items: + * + * lcb_timer_t *t; + * t->msec = 2000; + * t->callback = my_cb; + * lcb_list_append(&tm.list, t); + * + * Iterate over items: + * + * lcb_timer_t *t, *n; + * + * for (ii = tm.list.next; ii != &tm.list; ii = ii->next) { + * t = LCB_LIST_ITEM(ii, lcb_timer_t, list); + * printf("timeout: %d\n", t->msec); + * } + * + * LCB_LIST_FOR(ii, &tm.list) { + * t = LCB_LIST_ITEM(ii, lcb_timer_t, list); + * printf("timeout: %d\n", t->msec); + * } + * + * LCB_LIST_SAFE_FOR(ii, n, &tm.list) { + * t = LCB_LIST_ITEM(ii, lcb_timer_t, list); + * printf("timeout: %d\n", t->msec); + * } + */ + typedef struct lcb_list_s lcb_list_t; + struct lcb_list_s { + lcb_list_t *next; + lcb_list_t *prev; + }; + + typedef struct lcb_clist_s { + lcb_list_t *next; + lcb_list_t *prev; + lcb_size_t size; + } lcb_clist_t; + + typedef int (*lcb_list_cmp_fn)(lcb_list_t *a, lcb_list_t *b); + + void lcb_list_init(lcb_list_t *list); + void lcb_list_prepend(lcb_list_t *list, lcb_list_t *item); + void lcb_list_append(lcb_list_t *list, lcb_list_t *item); + void lcb_list_delete(lcb_list_t *item); + lcb_list_t *lcb_list_shift(lcb_list_t *list); + lcb_list_t *lcb_list_pop(lcb_list_t *list); + int lcb_list_contains(lcb_list_t *list, lcb_list_t *item); + void lcb_list_add_sorted(lcb_list_t *list, lcb_list_t *item, lcb_list_cmp_fn cmp); + + /** Definitions for type safety. Rather than macros */ + void lcb_clist_init(lcb_clist_t*); + void lcb_clist_append(lcb_clist_t*, lcb_list_t*); + void lcb_clist_prepend(lcb_clist_t*, lcb_list_t*); + void lcb_clist_delete(lcb_clist_t*,lcb_list_t*); + lcb_list_t* lcb_clist_shift(lcb_clist_t*); + lcb_list_t* lcb_clist_pop(lcb_clist_t*); + + +#define LCB_LIST_IS_EMPTY(list) \ + ((list) == (list)->next && (list) == (list)->prev) + +#define LCB_LIST_ITEM(ptr, type, member) \ + ((type *) (void *) ((char *)(ptr) - offsetof(type, member))) + +#define LCB_LIST_FOR(pos, list) \ + for (pos = (list)->next; pos != (list); pos = pos->next) + + +#define LCB_LIST_SAFE_FOR(pos, n, list) \ + for (pos = (list)->next, n = pos->next; pos != (list); pos = n, n = pos->next) + +#define LCB_LIST_HAS_NEXT(ll, item) \ + ((item)->next != ll) + +#define LCB_CLIST_SIZE(cl) (cl)->size + +#define LCB_LIST_TAIL(list) \ + ((LCB_LIST_IS_EMPTY(list)) ? NULL : (list)->prev) + +#define LCB_LIST_HEAD(list) \ + ((LCB_LIST_IS_EMPTY(list)) ? NULL : (list)->next) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/logging.c b/couchbase-sys/libcouchbase-2.7.0/src/logging.c new file mode 100644 index 00000000..3244f12f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/logging.c @@ -0,0 +1,244 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "settings.h" +#include "logging.h" +#include "internal.h" /* for lcb_getenv* */ +#include +#include + +#ifdef _WIN32 +#define flockfile(x) (void) 0 +#define funlockfile(x) (void) 0 +#endif + + +#if defined(unix) || defined(__unix__) || defined(__unix) || defined(_POSIX_VERSION) + #include + #include + #include + + /** XXX: If any of these blocks give problems for your platform, just + * erase it and have it use the fallback implementation. This isn't core + * functionality of the library, but is a rather helpful feature in order + * to get the thread/process identifier + */ + + #if defined(__linux__) + #include + #define GET_THREAD_ID() (long)syscall(SYS_gettid) + #define THREAD_ID_FMT "ld" + #elif defined(__APPLE__) + #define GET_THREAD_ID() getpid(), pthread_mach_thread_np(pthread_self()) + #define THREAD_ID_FMT "d/%x" + #elif defined(__sun) && defined(__SVR4) + #include + /* Thread IDs are not global in solaris, so it's nice to print the PID alongside it */ + #define GET_THREAD_ID() getpid(), thr_self() + #define THREAD_ID_FMT "ld/%u" + #elif defined(__FreeBSD__) + /* Like solaris, but thr_self is a bit different here */ + #include + static long ret_thr_self(void) { long tmp; thr_self(&tmp); return tmp; } + #define GET_THREAD_ID() getpid(), ret_thr_self() + #define THREAD_ID_FMT "d/%ld" + #else + /* other unix? */ + #define GET_THREAD_ID() 0 + #define THREAD_ID_FMT "d" + #endif +#elif defined(_WIN32) + #define GET_THREAD_ID() GetCurrentThreadId() + #define THREAD_ID_FMT "d" +#else + #define GET_THREAD_ID() 0 + #define THREAD_ID_FMT "d" +#endif + +static hrtime_t start_time = 0; + +static void console_log(struct lcb_logprocs_st *procs, + unsigned int iid, + const char *subsys, + int severity, + const char *srcfile, + int srcline, + const char *fmt, + va_list ap); + +static struct lcb_CONSOLELOGGER console_logprocs = { + {0 /* version */, {{console_log} /* v1 */} /*v*/}, + NULL, + /** Minimum severity */ + LCB_LOG_INFO +}; + +struct lcb_logprocs_st *lcb_console_logprocs = &console_logprocs.base; + + +/** + * Return a string representation of the severity level + */ +static const char * level_to_string(int severity) +{ + switch (severity) { + case LCB_LOG_TRACE: + return "TRACE"; + case LCB_LOG_DEBUG: + return "DEBUG"; + case LCB_LOG_INFO: + return "INFO"; + case LCB_LOG_WARN: + return "WARN"; + case LCB_LOG_ERROR: + return "ERROR"; + case LCB_LOG_FATAL: + return "FATAL"; + default: + return ""; + } +} + +/** + * Default logging callback for the verbose logger. + */ +static void console_log(struct lcb_logprocs_st *procs, + unsigned int iid, + const char *subsys, + int severity, + const char *srcfile, + int srcline, + const char *fmt, + va_list ap) +{ + FILE *fp; + hrtime_t now; + struct lcb_CONSOLELOGGER *vprocs = (struct lcb_CONSOLELOGGER *)procs; + + if (severity < vprocs->minlevel) { + return; + } + + if (!start_time) { + start_time = gethrtime(); + } + + now = gethrtime(); + if (now == start_time) { + now++; + } + + fp = vprocs->fp ? vprocs->fp : stderr; + + flockfile(fp); + fprintf(fp, "%lums ", (unsigned long)(now - start_time) / 1000000); + + fprintf(fp, "[I%d] {%"THREAD_ID_FMT"} [%s] (%s - L:%d) ", + iid, + GET_THREAD_ID(), + level_to_string(severity), + subsys, + srcline); + vfprintf(fp, fmt, ap); + fprintf(fp, "\n"); + funlockfile(fp); + + (void)procs; + (void)srcfile; +} + + +LCB_INTERNAL_API +void lcb_log(const struct lcb_settings_st *settings, + const char *subsys, + int severity, + const char *srcfile, + int srcline, + const char *fmt, + ...) +{ + va_list ap; + lcb_logging_callback callback; + + if (!settings->logger) { + return; + } + + if (settings->logger->version != 0) { + return; + } + + callback = settings->logger->v.v0.callback; + + va_start(ap, fmt); + callback(settings->logger, settings->iid, subsys, severity, srcfile, srcline, fmt, ap); + va_end(ap); +} + +LCB_INTERNAL_API +void lcb_log_badconfig(const struct lcb_settings_st *settings, + const char *subsys, int severity, const char *srcfile, int srcline, + const lcbvb_CONFIG *vbc, const char *origin_txt) +{ + const char *errstr = lcbvb_get_error(vbc); + if (!errstr) { + errstr = ""; + } + + lcb_log(settings, subsys, severity, srcfile, srcline, + "vBucket config parsing failed: %s. Raw text in DEBUG level", errstr); + if (!origin_txt) { + errstr = ""; + } + lcb_log(settings, subsys, LCB_LOG_DEBUG, srcfile, srcline, "%s", origin_txt); +} + +lcb_logprocs * lcb_init_console_logger(void) +{ + char vbuf[1024]; + char namebuf[PATH_MAX] = { 0 }; + int lvl = 0; + int has_file = 0; + + has_file = lcb_getenv_nonempty("LCB_LOGFILE", namebuf, sizeof(namebuf)); + if (has_file && console_logprocs.fp == NULL) { + FILE *fp = fopen(namebuf, "a"); + if (!fp) { + fprintf(stderr, "libcouchbase: could not open file '%s' for logging output. (%s)\n", + namebuf, strerror(errno)); + } + console_logprocs.fp = fp; + } + + if (!lcb_getenv_nonempty("LCB_LOGLEVEL", vbuf, sizeof(vbuf))) { + return NULL; + } + + if (sscanf(vbuf, "%d", &lvl) != 1) { + return NULL; + } + + if (!lvl) { + /** "0" */ + return NULL; + } + + /** The "lowest" level we can expose is WARN, e.g. ERROR-1 */ + lvl = LCB_LOG_ERROR - lvl; + console_logprocs.minlevel = lvl; + return lcb_console_logprocs; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/logging.h b/couchbase-sys/libcouchbase-2.7.0/src/logging.h new file mode 100644 index 00000000..ea61e526 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/logging.h @@ -0,0 +1,86 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_LOGGING_H +#define LCB_LOGGING_H +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +struct lcb_settings_st; +struct lcb_st; +struct lcbvb_CONFIG_st; + +/** + * Default printf logger which is enabled via LCB_LOGLEVEL in the + * environment + */ +extern struct lcb_logprocs_st *lcb_console_logprocs; + +struct lcb_CONSOLELOGGER { + struct lcb_logprocs_st base; + FILE *fp; + int minlevel; +}; + +/** + * Log a message via the installed logger. The parameters correlate to the + * arguments passed to the lcb_logging_callback function. + * + * Typically a subsystem may wish to define macros in order to reduce the + * number of arguments manually passed for each message. + */ +LCB_INTERNAL_API +void lcb_log(const struct lcb_settings_st *settings, + const char *subsys, + int severity, + const char *srcfile, + int srcline, + const char *fmt, ...) + +#ifdef __GNUC__ + __attribute__ ((format(printf, 6, 7))) +#endif + ; + +LCB_INTERNAL_API +void lcb_log_badconfig(const struct lcb_settings_st *settings, + const char *subsys, int severity, const char *srcfile, int srcline, + const struct lcbvb_CONFIG_st *vbc, const char *origin_txt); + +lcb_logprocs * lcb_init_console_logger(void); + +#define LCB_LOGS(settings, subsys, severity, msg) \ + lcb_log(settings, subsys, severity, __FILE__, __LINE__, msg) + +#define LCB_LOG_EX(settings, subsys, severity, msg) \ + lcb_log(settings, subsys, severity, __FILE__, __LINE__, msg) + +#define LCB_LOG_BASIC(settings, msg) \ + lcb_log(settings, "unknown", 0, __FILE__, __LINE__, msg) + +/** Macro for overcoming Win32 identifiers */ +#define LCB_LOG_ERR LCB_LOG_ERROR + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* LCB_LOGGING_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/mc/compress.c b/couchbase-sys/libcouchbase-2.7.0/src/mc/compress.c new file mode 100644 index 00000000..114bdffb --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/mc/compress.c @@ -0,0 +1,90 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mcreq.h" +#include "compress.h" + +#ifndef LCB_NO_SNAPPY +#include +#endif + +int +mcreq_compress_value(mc_PIPELINE *pl, mc_PACKET *pkt, const lcb_CONTIGBUF *vbuf) +{ +#ifdef LCB_NO_SNAPPY + (void)pl;(void)pkt;(void)vbuf;return -1; +#else + /* get the desired size */ + size_t maxsize, compsize; + snappy_status status; + nb_SPAN *outspan; + + compsize = maxsize = snappy_max_compressed_length(vbuf->nbytes); + if (mcreq_reserve_value2(pl, pkt, maxsize) != LCB_SUCCESS) { + return -1; + } + + outspan = &pkt->u_value.single; + status = snappy_compress(vbuf->bytes, vbuf->nbytes, + SPAN_BUFFER(outspan), &compsize); + + if (status != SNAPPY_OK) { + return -1; + } + + if (compsize < maxsize) { + /* chop off some bytes? */ + nb_SPAN trailspan = *outspan; + trailspan.offset += compsize; + trailspan.size = maxsize - compsize; + netbuf_mblock_release(&pl->nbmgr, &trailspan); + outspan->size = compsize; + } + return 0; +#endif +} + +int +mcreq_inflate_value(const void *compressed, lcb_SIZE ncompressed, + const void **bytes, lcb_SIZE *nbytes, void **freeptr) +{ +#ifdef LCB_NO_SNAPPY + (void)compressed;(void)ncompressed;(void)bytes;(void)nbytes;(void)freeptr; + return -1; +#else + lcb_SIZE cursize, compsize; + snappy_status status; + cursize = ncompressed; + do { + cursize *= 2; + *freeptr = realloc(*freeptr, cursize); + compsize = cursize; + status = snappy_uncompress(compressed, ncompressed, *freeptr, &compsize); + } while (status == SNAPPY_BUFFER_TOO_SMALL); + + if (status != SNAPPY_OK) { + /* TODO: return an error here */ + free(*freeptr); + *freeptr = NULL; + return -1; + } + + *bytes = *freeptr; + *nbytes = compsize; + return 0; +#endif +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/mc/compress.h b/couchbase-sys/libcouchbase-2.7.0/src/mc/compress.h new file mode 100644 index 00000000..50a38025 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/mc/compress.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mcreq.h" +#ifndef LCB_MCCOMPRESS_H +#define LCB_MCCOMPRESS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Stores a compressed payload into a packet + * @param pl The pipeline which hosts the packet + * @param pkt The packet which hosts the value + * @param vbuf The user input to be compressed + * @return 0 if successful, nonzero on error. + */ +int +mcreq_compress_value(mc_PIPELINE *pl, mc_PACKET *pkt, const lcb_CONTIGBUF *vbuf); + + +/** + * Inflate a compressed value + * @param compressed The value to inflate + * @param ncompressed Size of value to inflate + * @param[out] bytes The inflated value + * @param[out] nbytes The size of the inflated value + * @param[in/out] freeptr Pointer initialized to NULL (or an malloc'd buffer) + * which on output should point to a malloc'd buffer to be freed() when no + * longer required. + * @return 0 if successful, nonzero on error. + */ +int +mcreq_inflate_value(const void *compressed, lcb_SIZE ncompressed, + const void **bytes, lcb_SIZE *nbytes, void **freeptr); + +#ifndef LCB_NO_SNAPPY +#define mcreq_compression_supported() 1 +#else +#define mcreq_compression_supported() 0 +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/mc/forward.c b/couchbase-sys/libcouchbase-2.7.0/src/mc/forward.c new file mode 100644 index 00000000..cbbde5af --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/mc/forward.c @@ -0,0 +1,186 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mcreq.h" +#include "forward.h" +#include "iovcursor-inl.h" +#include + +#define MINIMUM(a, b) (a) < (b) ? a : b + +static void +span_from_first(mc_IOVCURSOR *cursor, unsigned size, nb_SPAN *span) +{ + nb_IOV dummy; + iovcursor_adv_first(cursor, size, &dummy); + CREATE_STANDALONE_SPAN(span, dummy.iov_base, dummy.iov_len); +} + +void +mc_iovinfo_init(mc_IOVINFO *info, const nb_IOV *iov, unsigned niov) +{ + unsigned ii; + info->c.iov = (void *)iov; + info->c.niov = niov; + info->c.offset = 0; + info->total = 0; + + for (ii = 0; ii < niov; ii++) { + info->total += iov[ii].iov_len; + } +} + +#define REQLEN_HDR(req) sizeof(req.bytes) +#define REQLEN_ + +lcb_error_t +mc_forward_packet(mc_CMDQUEUE *cq, + mc_IOVINFO *info, mc_PACKET **pkt_p, mc_PIPELINE **pl_p, int options) +{ + /* stack based header with our modifications. this is copied into the + * packet's actual header */ + protocol_binary_request_header hdr; + int vbid, srvix; + mc_IOVCURSOR *mincur = &info->c; + + unsigned n_packet; /* total packet size */ + unsigned n_header; /* extras + key + memcached header */ + unsigned n_body_total; /* size of everything following the memcached header */ + unsigned n_body_key; /* length of the key */ + unsigned n_body_value; /* packetsize - hdrsize */ + + unsigned offset; + + /* stack buffer and key pointer. stack buffer is used if the key is not + * contiguous */ + char kbuf_s[256]; + const char *kptr; + + /* pipeline and packet for command */ + mc_PIPELINE *pl; + mc_PACKET *pkt; + info->wanted = 0; + + /* not enough bytes */ + if (info->total < 24) { + info->wanted = 24; + return LCB_INCOMPLETE_PACKET; + } + + iovcursor_peek(mincur, (char*)hdr.bytes, sizeof hdr.bytes, 0); + + /* Initialize our size variables */ + n_body_total = ntohl(hdr.request.bodylen); + n_body_key = ntohs(hdr.request.keylen); + n_header = sizeof hdr.bytes + n_body_key + hdr.request.extlen; + n_packet = n_body_total + sizeof hdr.bytes; + n_body_value = n_packet - n_header; + + if (n_packet > info->total) { + info->wanted = n_packet; + return LCB_INCOMPLETE_PACKET; + } + + info->total -= n_packet; + + /* seek ahead to read the item's key into the header */ + offset = sizeof hdr.bytes + hdr.request.extlen; + + iovcursor_peek_ex(mincur, kbuf_s, &kptr, n_body_key, offset); + + if (kptr == NULL) { + /* key is not contiguous? that's ok. use the static buffer */ + kptr = kbuf_s; + } + + if ((options & MC_FWD_OPT_NOMAP) == 0) { + lcbvb_map_key(cq->config, kptr, n_body_key, &vbid, &srvix); + if (srvix < 0 || (unsigned)srvix >= cq->npipelines) { + return LCB_NO_MATCHING_SERVER; + } + pl = cq->pipelines[srvix]; + hdr.request.vbucket = htons(vbid); + + } else { + pl = *pl_p; + if (!pl) { + return LCB_EINVAL; + } + srvix = pl->index; + } + + pkt = mcreq_allocate_packet(pl); + + if (pkt == NULL) { + return LCB_CLIENT_ENOMEM; + } + + hdr.request.opaque = pkt->opaque; + pkt->extlen = hdr.request.extlen; + info->consumed = n_packet; + + if (options & MC_FWD_OPT_COPY) { + /* reserve bytes for the entire packet */ + mcreq_reserve_header(pl, pkt, n_header); + iovcursor_adv_copy(mincur, SPAN_BUFFER(&pkt->kh_span), n_header); + if (n_body_value) { + mcreq_reserve_value2(pl, pkt, n_body_value); + iovcursor_adv_copy(mincur, + SPAN_BUFFER(&pkt->u_value.single), n_body_value); + pkt->flags |= MCREQ_F_HASVALUE; + } + + } else { + if (IOVCURSOR_HAS_CONTIG(mincur, n_header)) { + span_from_first(mincur, n_header, &pkt->kh_span); + pkt->flags |= MCREQ_F_KEY_NOCOPY; + + } else { + /* header is fragmented into multiple IOVs */ + mcreq_reserve_header(pl, pkt, n_header); + iovcursor_adv_copy(mincur, SPAN_BUFFER(&pkt->kh_span), n_header); + } + + /* do we have a value payload still? */ + if (n_body_value) { + pkt->flags |= MCREQ_F_HASVALUE | MCREQ_F_VALUE_NOCOPY; + if (IOVCURSOR_HAS_CONTIG(mincur, n_body_value)) { + span_from_first(mincur, n_body_value, &pkt->u_value.single); + + } else { + /* body is fragmented */ + iovcursor_adv_iovalloc(mincur, n_body_value, + (nb_IOV**)&pkt->u_value.multi.iov, + &pkt->u_value.multi.niov); + pkt->u_value.multi.total_length = n_body_value; + pkt->flags |= MCREQ_F_VALUE_IOV; + } + } + } + + /* Copy the first 24 bytes into the header span */ + memcpy(SPAN_BUFFER(&pkt->kh_span), hdr.bytes, sizeof hdr.bytes); + + *pkt_p = pkt; + *pl_p = pl; + + /* Set the UFWD flag. This causes the rest of the system to invoke the + * handler for the raw response, rather than the "Contiguous" structures*/ + pkt->flags |= MCREQ_F_UFWD; + mcreq_sched_add(pl, pkt); + return LCB_SUCCESS; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/mc/forward.h b/couchbase-sys/libcouchbase-2.7.0/src/mc/forward.h new file mode 100644 index 00000000..0d4a43be --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/mc/forward.h @@ -0,0 +1,90 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MC_FORWARD_H +#define MC_FORWARD_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "iovcursor.h" + + +/** + * Copy over the entire packet to the internal buffers. Input buffer is + * temporary. + */ +#define MC_FWD_OPT_COPY 0x01 + +/** + * The server to send to is already set as `pl`. Don't perform vbucket mapping. + */ +#define MC_FWD_OPT_NOMAP 0x02 + +void +mc_iovinfo_init(mc_IOVINFO *info, const nb_IOV *iov, unsigned niov); + +/** + * Forward a packet to an upstream server. + * @param[in] cq the command queue + * @param[in] ctx a queue context used for scheduling/flushing the packets + * @param[in] info an 'IOVINFO' structure. See the structure documentation for + * more details. + * @param[out] pkt a pointer to a packet, set to the resultant packet structure + * @param[out] pl the pipeline this packet is mapped to + * @param options Options modifying the behavior of this operation + * @return LCB_SUCCESS or an error code. + * + * Currently only some commands will successfully make sense to forward. This + * function only handles the lower level aspect of actually allocating or + * reserving the buffers required to forward the packet, but not actually + * handling the received data for the callbacks themselves. + * + * Note that this function does not currently "Collapse" consectutive IOV + * structures. Additionally the following should be noted: + * + *
      + *
    1. + * If the first IOV does not contain a contiguous buffer of the + * { header, extras, key }, then it will be copied into a library-based + * buffer. + *
    2. + * + *
    3. + * If the total number of IOVs is greater than two, then niov-1 IOV + * structures will be allocated via 'malloc'. This may not always happen + * if the header _itself_ is fragmented + *
    4. + * + *
    5. The first 24 bytes of the header WILL BE MODIFIED by the library. + * This will be used to modify the 'opaque' and 'vbucket' fields. + * Take this into note if you need to keep track of their original values. + *
    6. + * + *
    7. Check the 'niov' counter to see if it is 0. If it's 0 then you should + * make sure to reset the counter. + *
    8. + *
    + */ +lcb_error_t +mc_forward_packet(mc_CMDQUEUE *cq, + mc_IOVINFO *info, mc_PACKET **pkt, mc_PIPELINE **pl, int options); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/mc/iovcursor-inl.h b/couchbase-sys/libcouchbase-2.7.0/src/mc/iovcursor-inl.h new file mode 100644 index 00000000..2c4e211b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/mc/iovcursor-inl.h @@ -0,0 +1,279 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "iovcursor.h" +#ifndef MINIMUM +#define MINIMUM(a, b) (a) < (b) ? a : b +#endif + +/**Status code returned by the peek_ex() function. Codes below 0 are errors, + * while codes above 0 are successes*/ +typedef enum { + /** Data would be fragmented and no `copytgt` parameter was provided */ + IOVCURSOR_STATUS_FRAGMENTED = -2, + /** Pointer to data is referenced by the `contigptr` parameter */ + IOVCURSOR_STATUS_CONTIGPTR_OK = 0, + /** Pointer to contiguous data cannot be obtained. It has been copied */ + IOVCURSOR_STATUS_BUFCOPY_OK +} iovcursor_STATUS; + +/** + * Obtain data from a cursor without advancing it + * @param cursor The cursor to read from + * + * @param copytgt A buffer in which to copy the data, in case a contiguous + * pointer to `size` bytes cannot be obtained. + * + * @param[out] contigref A pointer to be set to a contiguous region of memory + * containing `size` bytes + * @param size + * @param offset The offset at which to start reading + * @return A status code indicating success or failure of the operation + * @note Either `copytgt` or `contigref` _must_ be specified. It is ok to pass + * them both in the same function call (and the return status may be inspected + * to see which one of the parameters contains the actual data). + */ +static iovcursor_STATUS +iovcursor_peek_ex(const mc_IOVCURSOR *cursor, + char *copytgt, const char **contigref, + unsigned size, unsigned offset) +{ + unsigned ii; + const nb_IOV *iov = cursor->iov; + offset += cursor->offset; + for (ii = 0; ii < cursor->niov && size > 0; ++ii) { + unsigned contiglen, tmpoff; + const char *srcbuf; + const nb_IOV *cur = iov + ii; + + if (offset) { + if (offset >= cur->iov_len) { + offset -= cur->iov_len; + continue; + } else { + tmpoff = offset; + offset = 0; + } + } else { + tmpoff = 0; + } + + contiglen = cur->iov_len - tmpoff; + srcbuf = (const char *)cur->iov_base + tmpoff; + + /* We always end up returning from these following blocks and _not_ + * the end of the loop: + * + * For contiguous buffers, contiglen >= size is true on the first + * iteration. + * + * For fragmented buffers, contiglen >= size is _eventually_ true. + * however during the first initial IOVs, the buffer is fragmented + * but as the output is copied the size variable is decremented. Note + * that during this process we also set contigref to NULL to avoid + * accidentally thinking that the full data set is contiguous + */ + if (size <= contiglen) { + if (contigref) { + *contigref = srcbuf; + return IOVCURSOR_STATUS_CONTIGPTR_OK; + } else { + memcpy(copytgt, srcbuf, size); + return IOVCURSOR_STATUS_BUFCOPY_OK; + } + } else if (copytgt == NULL) { + *contigref = NULL; + return IOVCURSOR_STATUS_FRAGMENTED; + } else { + /* copy and continue */ + unsigned to_copy = MINIMUM(size, cur->iov_len - tmpoff); + memcpy(copytgt, srcbuf, to_copy); + copytgt += to_copy; + if (contigref) { + *contigref = NULL; + contigref = NULL; + } + + /* We've copied, so ignore the 'contigref' */ + contigref = NULL; + if (!(size -= to_copy)) { + return IOVCURSOR_STATUS_BUFCOPY_OK; + } + } + } + + assert(!size); + *contigref = NULL; + return IOVCURSOR_STATUS_FRAGMENTED; +} + +/** + * Copy data to the target buffer, without modifying the offset + * @param cursor The cursror to read from + * @param buf The target buffer + * @param size The number of bytes to copy + * @param offset Position in the input at which to start copying + * @return true if there were sufficient bytes to copy, false otherwise. + */ +static int +iovcursor_peek(const mc_IOVCURSOR *cursor, char *buf, + unsigned size, unsigned offset) +{ + int rv = iovcursor_peek_ex(cursor, buf, NULL, size, offset); + return rv == IOVCURSOR_STATUS_BUFCOPY_OK; +} + +/** + * Populate an IOV structure with the effective offset of the first IOV contained + * within the cursor + * @param cursor + * @param maxsize Maximum length of resultant IOV, if the cursor's first IOV + * length is greated + * @param iov The iov to be initialized + */ +static unsigned +iovcursor_adv_first(mc_IOVCURSOR *cursor, unsigned maxsize, nb_IOV *iov) +{ + const char *srcbuf = (const char *)cursor->iov->iov_base + cursor->offset; + + /* Set the target */ + iov->iov_base = (void*)srcbuf; + iov->iov_len = MINIMUM(cursor->iov->iov_len - cursor->offset, maxsize); + + if (iov->iov_len == (cursor->iov->iov_len - cursor->offset)) { + /* did we swallow the entire source iov? - consume it */ + cursor->iov++; + cursor->niov--; + cursor->offset = 0; + } else { + /* increase the offset */ + cursor->offset += iov->iov_len; + } + + return iov->iov_len; +} + +/** + * Copy data to the target buffer, advancing the cursor + * @param cursor The cursor + * @param tgt The buffer to copy to + * @param size The number of bytes to copy + * + * @warning No check is made to see if `size` is greater than the amounf of + * data contained within the cursor. Exceeding the amount of data will result + * in undefined behavior. + */ +static void +iovcursor_adv_copy(mc_IOVCURSOR *cursor, char *tgt, unsigned size) +{ + nb_IOV tmpiov; + nb_IOV *iov; + unsigned niov; + + size -= iovcursor_adv_first(cursor, size, &tmpiov); + memcpy(tgt, tmpiov.iov_base, tmpiov.iov_len); + tgt += tmpiov.iov_len; + + /* assign iov and iov now, since adv_first() may have modified them */ + iov = cursor->iov; + niov = cursor->niov; + + while (size) { + unsigned to_copy = MINIMUM(iov->iov_len, size); + const char *srcbuf = (const char *)iov->iov_base; + memcpy(tgt, srcbuf, to_copy); + tgt += to_copy; + size -= to_copy; + + if (to_copy != iov->iov_len) { + cursor->offset = to_copy; + assert(!size); + break; + } + + iov++; + niov--; + } + + /* modify the variables */ + cursor->iov = iov; + cursor->niov = niov; +} + +/** + * Macro which determines if the specific cursor's first IOV has enough + * data to ensure a contiguous memory region of a specific amount of bytes + * @param mincur the cursor + * @param n the number of bytes to check for + * @return nonzero if the requested size is available + */ +#define IOVCURSOR_HAS_CONTIG(mincur, n) \ + ((mincur)->iov->iov_len - (mincur)->offset) >= n + +/** + * Create an allocated array of IOVs which point to a subset of IOVs within + * the current cursor. This function will also advance the cursor position. + * + * @param cursor The cursor to get the offsets from + * @param size The size the resultant array should cover + * @param[out] arr The IOV array which shall contain the offsets. The array + * should be freed by free() when no longer required. + * @param[out] narr Number of elements in the resultant array. + */ +static void +iovcursor_adv_iovalloc(mc_IOVCURSOR *cursor, unsigned size, + nb_IOV **p_arr, unsigned *p_narr) +{ + unsigned ii, narr; + nb_IOV dummy, *arr; + + /* chop off the first IOV for convenience */ + size -= iovcursor_adv_first(cursor, size, &dummy); + narr = 1; + + if (size) { + unsigned cursz = size; + for (ii = 0; cursz > 0; ++ii) { + cursz -= MINIMUM(cursz, cursor->iov[ii].iov_len); + } + narr += ii; + } + + arr = (nb_IOV*) malloc(sizeof(*arr) * narr); + arr[0] = dummy; + + for (ii = 1; size > 0; ++ii) { + unsigned to_adv = MINIMUM(size, cursor->iov->iov_len); + const char *srcbuf = (const char*)cursor->iov->iov_base; + + arr[ii].iov_base = (void*)srcbuf; + arr[ii].iov_len = MINIMUM(size, cursor->iov->iov_len); + + size -= to_adv; + + if (size == 0 && to_adv < cursor->iov->iov_len) { + /* did we not copy the entire iov? */ + cursor->offset = to_adv; + } else { + cursor->iov++; + cursor->niov--; + } + } + + *p_arr = arr; + *p_narr = narr; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/mc/iovcursor.h b/couchbase-sys/libcouchbase-2.7.0/src/mc/iovcursor.h new file mode 100644 index 00000000..797d17d8 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/mc/iovcursor.h @@ -0,0 +1,66 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MC_IOVCURSOR_H +#define MC_IOVCURSOR_H +#ifdef __cplusplus +extern "C" { +#endif + +/** Minimal cursor */ +typedef struct { + /**The IOV array containing the buffer offsets. This is initialized to the + * first element of the array on input. As data is consumed by the + * library, this pointer value will increment.*/ + nb_IOV *iov; + + /**Number of elements in the IOV array. This is decremented as the `iov` + * field is incremented.*/ + unsigned niov; + + /**Offset into first IOV structure which contains data. This is used + * if the IOV contains partially consumed data. The library sets this + * field if a packet ends in the middle of an IOV buffer*/ + unsigned offset; +} mc_IOVCURSOR; + +typedef struct { + /** Cursor element */ + mc_IOVCURSOR c; + + /**The total number of bytes used by the library in the last packet + * successfuly processed.*/ + unsigned consumed; + + /**Number of bytes wanted for next operation (OUT). This contains the + * total number of bytes (including any within the buffer already). + * The library does not read from this value. */ + unsigned wanted; + + /**The total amount of data within the IOV buffers. This is initialized + * in the mc_iovinfo_init() function by traversing through all the elements + * and adding their `iov_len` fields. If using the `IOVINFO` structure + * in a read loop, you will want to increment this whenever new data has + * been placed into buffers*/ + unsigned total; +} mc_IOVINFO; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/mc/mcreq-flush-inl.h b/couchbase-sys/libcouchbase-2.7.0/src/mc/mcreq-flush-inl.h new file mode 100644 index 00000000..629c1abc --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/mc/mcreq-flush-inl.h @@ -0,0 +1,111 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mcreq.h" +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + mc_PIPELINE *pl; + hrtime_t now; +} mc__FLUSHINFO; + +/** + * Inline operations for flush. To use this, include this file into your + * own source code. + */ + +/** + * Fill a series of IOVs with data to flush + * @param pipeline the pipeline to flush + * @param iov the iov array to fill + * @param niov the number of input items + * @param nused set to the number of IOVs actually used + * @return the number of data inside all the IOVs + */ +static unsigned int +mcreq_flush_iov_fill(mc_PIPELINE *pipeline, nb_IOV *iov, int niov, int *nused) +{ + return netbuf_start_flush(&pipeline->nbmgr, iov, niov, nused); +} + +static nb_SIZE +mcreq__pktflush_callback(void *p, nb_SIZE hint, void *arg) +{ + nb_SIZE pktsize; + mc_PACKET *pkt = (mc_PACKET *)p; + mc__FLUSHINFO *info = (mc__FLUSHINFO *)arg; + + pktsize = mcreq_get_size(pkt); + + if (info->now && hint) { + MCREQ_PKT_RDATA(pkt)->start = info->now; + } + + if (hint < pktsize) { + return pktsize; + } + + /** Packet is flushed */ + pkt->flags |= MCREQ_F_FLUSHED; + + if (pkt->flags & MCREQ_F_INVOKED) { + mcreq_packet_done(info->pl, pkt); + } + return pktsize; +} + +/** + * Called when a chunk of data has been flushed from the network. + * @param pl the pipeline which was to be flushed + * @param nflushed how much data was actually flushed + * @param expected how much data was expected to be flushed (i.e. the return + * value from the corresponding iov_fill). + * + * @param now if present, will reset the start time of each traversed packet + * to the value passed. + * + * This is a thin wrapper around netbuf_end_flush (and optionally + * nebtuf_reset_flush()) + */ +static void +mcreq_flush_done_ex(mc_PIPELINE *pl, + unsigned nflushed, unsigned expected, lcb_U64 now) +{ + if (nflushed) { + mc__FLUSHINFO info = { pl, now }; + + netbuf_end_flush2(&pl->nbmgr, nflushed, + mcreq__pktflush_callback, + offsetof(mc_PACKET, sl_flushq), &info); + } + if (nflushed < expected) { + netbuf_reset_flush(&pl->nbmgr); + } +} + +/* Mainly for tests */ +static void +mcreq_flush_done(mc_PIPELINE *pl, unsigned nflushed, unsigned expected) +{ + mcreq_flush_done_ex(pl, nflushed, expected, 0); +} + +#ifdef __cplusplus +} +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/mc/mcreq.c b/couchbase-sys/libcouchbase-2.7.0/src/mc/mcreq.c new file mode 100644 index 00000000..ef67f1d3 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/mc/mcreq.c @@ -0,0 +1,954 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mcreq.h" +#include "compress.h" +#include "sllist-inl.h" +#include "internal.h" + +#define PKT_HDRSIZE(pkt) (MCREQ_PKT_BASESIZE + (pkt)->extlen) + +lcb_error_t +mcreq_reserve_header( + mc_PIPELINE *pipeline, mc_PACKET *packet, uint8_t hdrsize) +{ + int rv; + packet->extlen = hdrsize - MCREQ_PKT_BASESIZE; + packet->kh_span.size = hdrsize; + rv = netbuf_mblock_reserve(&pipeline->nbmgr, &packet->kh_span); + if (rv != 0) { + return LCB_CLIENT_ENOMEM; + } + return LCB_SUCCESS; +} + +lcb_error_t +mcreq_reserve_key( + mc_PIPELINE *pipeline, mc_PACKET *packet, uint8_t hdrsize, + const lcb_KEYBUF *kreq) +{ + const struct lcb_CONTIGBUF *contig = &kreq->contig; + int rv; + + /** Set the key offset which is the start of the key from the buffer */ + packet->extlen = hdrsize - MCREQ_PKT_BASESIZE; + packet->kh_span.size = kreq->contig.nbytes; + + if (kreq->type == LCB_KV_COPY) { + /** + * If the key is to be copied then just allocate the span size + * for the key+24+extras + */ + packet->kh_span.size += hdrsize; + rv = netbuf_mblock_reserve(&pipeline->nbmgr, &packet->kh_span); + if (rv != 0) { + return LCB_CLIENT_ENOMEM; + } + + /** + * Copy the key into the packet starting at the extras end + */ + memcpy(SPAN_BUFFER(&packet->kh_span) + hdrsize, + contig->bytes, + contig->nbytes); + + } else if (kreq->type == LCB_KV_CONTIG) { + /** + * Don't do any copying. + * Assume the key buffer has enough space for the packet as well. + */ + CREATE_STANDALONE_SPAN(&packet->kh_span, contig->bytes, contig->nbytes); + packet->flags |= MCREQ_F_KEY_NOCOPY; + + } else { + /** IOVs not supported for keys */ + return LCB_EINVAL; + } + + return LCB_SUCCESS; +} + +lcb_error_t +mcreq_reserve_value2(mc_PIPELINE *pl, mc_PACKET *pkt, lcb_size_t n) +{ + int rv; + pkt->u_value.single.size = n; + if (!n) { + return LCB_SUCCESS; + } + + pkt->flags |= MCREQ_F_HASVALUE; + rv = netbuf_mblock_reserve(&pl->nbmgr, &pkt->u_value.single); + if (rv) { + return LCB_CLIENT_ENOMEM; + } + return LCB_SUCCESS; +} + +lcb_error_t +mcreq_reserve_value( + mc_PIPELINE *pipeline, mc_PACKET *packet, const lcb_VALBUF *vreq) +{ + const lcb_CONTIGBUF *contig = &vreq->u_buf.contig; + nb_SPAN *vspan = &packet->u_value.single; + int rv; + + if (vreq->vtype == LCB_KV_COPY) { + /** Copy the value into a single SPAN */ + if (! (vspan->size = vreq->u_buf.contig.nbytes)) { + return LCB_SUCCESS; + } + rv = netbuf_mblock_reserve(&pipeline->nbmgr, vspan); + + if (rv != 0) { + return LCB_CLIENT_ENOMEM; + } + + memcpy(SPAN_BUFFER(vspan), contig->bytes, contig->nbytes); + + } else if (vreq->vtype == LCB_KV_CONTIG) { + /** It's still contiguous so make it a 'standalone' span */ + CREATE_STANDALONE_SPAN(vspan, contig->bytes, contig->nbytes); + packet->flags |= MCREQ_F_VALUE_NOCOPY; + + } else if (vreq->vtype == LCB_KV_IOV) { + /** Multiple spans, no copy */ + unsigned int ii; + const lcb_FRAGBUF *msrc = &vreq->u_buf.multi; + lcb_FRAGBUF *mdst = &packet->u_value.multi; + + packet->flags |= MCREQ_F_VALUE_IOV | MCREQ_F_VALUE_NOCOPY; + mdst->niov = msrc->niov; + mdst->iov = malloc(mdst->niov * sizeof(*mdst->iov)); + mdst->total_length = 0; + + for (ii = 0; ii < mdst->niov; ii++) { + mdst->iov[ii] = msrc->iov[ii]; + mdst->total_length += mdst->iov[ii].iov_len; + } + } else if (vreq->vtype == LCB_KV_IOVCOPY) { + /** Multiple input buffers, normal copying output buffer */ + unsigned int ii, cur_offset; + const lcb_FRAGBUF *msrc = &vreq->u_buf.multi; + + if (msrc->total_length) { + vspan->size = msrc->total_length; + } else { + vspan->size = 0; + for (ii = 0; ii < msrc->niov; ii++) { + vspan->size += msrc->iov[ii].iov_len; + } + } + + rv = netbuf_mblock_reserve(&pipeline->nbmgr, vspan); + if (rv != 0) { + return LCB_CLIENT_ENOMEM; + } + + for (ii = 0, cur_offset = 0; ii < msrc->niov; ii++) { + char *buf = SPAN_BUFFER(vspan) + cur_offset; + memcpy(buf, msrc->iov[ii].iov_base, msrc->iov[ii].iov_len); + cur_offset += msrc->iov[ii].iov_len; + } + } + + packet->flags |= MCREQ_F_HASVALUE; + return LCB_SUCCESS; +} + +static int +pkt_tmo_compar(sllist_node *a, sllist_node *b) +{ + mc_PACKET *pa, *pb; + hrtime_t tmo_a, tmo_b; + + pa = SLLIST_ITEM(a, mc_PACKET, slnode); + pb = SLLIST_ITEM(b, mc_PACKET, slnode); + + tmo_a = MCREQ_PKT_RDATA(pa)->start; + tmo_b = MCREQ_PKT_RDATA(pb)->start; + + if (tmo_a == tmo_b) { + return 0; + } else if (tmo_a < tmo_b) { + return -1; + } else { + return 1; + } +} + +void +mcreq_reenqueue_packet(mc_PIPELINE *pipeline, mc_PACKET *packet) +{ + sllist_root *reqs = &pipeline->requests; + mcreq_enqueue_packet(pipeline, packet); + sllist_remove(reqs, &packet->slnode); + sllist_insert_sorted(reqs, &packet->slnode, pkt_tmo_compar); +} + +void +mcreq_enqueue_packet(mc_PIPELINE *pipeline, mc_PACKET *packet) +{ + nb_SPAN *vspan = &packet->u_value.single; + sllist_append(&pipeline->requests, &packet->slnode); + netbuf_enqueue_span(&pipeline->nbmgr, &packet->kh_span); + + if (!(packet->flags & MCREQ_F_HASVALUE)) { + goto GT_ENQUEUE_PDU; + } + + if (packet->flags & MCREQ_F_VALUE_IOV) { + unsigned int ii; + lcb_FRAGBUF *multi = &packet->u_value.multi; + for (ii = 0; ii < multi->niov; ii++) { + netbuf_enqueue(&pipeline->nbmgr, (nb_IOV *)multi->iov + ii); + } + + } else if (vspan->size) { + netbuf_enqueue_span(&pipeline->nbmgr, vspan); + } + + GT_ENQUEUE_PDU: + netbuf_pdu_enqueue(&pipeline->nbmgr, packet, offsetof(mc_PACKET, sl_flushq)); +} + +void +mcreq_wipe_packet(mc_PIPELINE *pipeline, mc_PACKET *packet) +{ + if (! (packet->flags & MCREQ_F_KEY_NOCOPY)) { + if (packet->flags & MCREQ_F_DETACHED) { + free(SPAN_BUFFER(&packet->kh_span)); + } else { + netbuf_mblock_release(&pipeline->nbmgr, &packet->kh_span); + } + } + + if (! (packet->flags & MCREQ_F_HASVALUE)) { + return; + } + + if (packet->flags & MCREQ_F_VALUE_NOCOPY) { + if (packet->flags & MCREQ_F_VALUE_IOV) { + free(packet->u_value.multi.iov); + } + + return; + } + + if (packet->flags & MCREQ_F_DETACHED) { + free(SPAN_BUFFER(&packet->u_value.single)); + } else { + netbuf_mblock_release(&pipeline->nbmgr, &packet->u_value.single); + } + +} + +mc_PACKET * +mcreq_allocate_packet(mc_PIPELINE *pipeline) +{ + nb_SPAN span; + int rv; + mc_PACKET *ret; + span.size = sizeof(*ret); + + rv = netbuf_mblock_reserve(&pipeline->reqpool, &span); + if (rv != 0) { + return NULL; + } + + ret = (void *) SPAN_MBUFFER_NC(&span); + ret->alloc_parent = span.parent; + ret->flags = 0; + ret->retries = 0; + ret->opaque = pipeline->parent->seq++; + return ret; +} + +void +mcreq_release_packet(mc_PIPELINE *pipeline, mc_PACKET *packet) +{ + nb_SPAN span; + if (packet->flags & MCREQ_F_DETACHED) { + sllist_iterator iter; + mc_EXPACKET *epkt = (mc_EXPACKET *)packet; + + SLLIST_ITERFOR(&epkt->data, &iter) { + mc_EPKTDATUM *d = SLLIST_ITEM(iter.cur, mc_EPKTDATUM, slnode); + sllist_iter_remove(&epkt->data, &iter); + d->dtorfn(d); + } + free(epkt); + return; + } + + span.size = sizeof(*packet); + span.parent = packet->alloc_parent; + span.offset = (char *)packet - packet->alloc_parent->root; + + netbuf_mblock_release(&pipeline->reqpool, &span); +} + +#define MCREQ_DETACH_WIPESRC 1 + +mc_PACKET * +mcreq_renew_packet(const mc_PACKET *src) +{ + char *kdata, *vdata; + unsigned nvdata; + mc_PACKET *dst; + mc_EXPACKET *edst = calloc(1, sizeof(*edst)); + + dst = &edst->base; + *dst = *src; + + kdata = malloc(src->kh_span.size); + memcpy(kdata, SPAN_BUFFER(&src->kh_span), src->kh_span.size); + CREATE_STANDALONE_SPAN(&dst->kh_span, kdata, src->kh_span.size); + + dst->flags &= ~(MCREQ_F_KEY_NOCOPY|MCREQ_F_VALUE_NOCOPY|MCREQ_F_VALUE_IOV); + dst->flags |= MCREQ_F_DETACHED; + dst->alloc_parent = NULL; + dst->sl_flushq.next = NULL; + dst->slnode.next = NULL; + dst->retries = src->retries; + + if (src->flags & MCREQ_F_HASVALUE) { + /** Get the length */ + if (src->flags & MCREQ_F_VALUE_IOV) { + unsigned ii; + unsigned offset = 0; + + nvdata = src->u_value.multi.total_length; + vdata = malloc(nvdata); + for (ii = 0; ii < src->u_value.multi.niov; ii++) { + const lcb_IOV *iov = src->u_value.multi.iov + ii; + + memcpy(vdata + offset, iov->iov_base, iov->iov_len); + offset += iov->iov_len; + } + } else { + protocol_binary_request_header hdr; + const nb_SPAN *origspan = &src->u_value.single; + mcreq_read_hdr(dst, &hdr); + + if (hdr.request.datatype & PROTOCOL_BINARY_DATATYPE_COMPRESSED) { + /* For compressed payloads we need to uncompress it first + * because it may be forwarded to a server without compression. + * TODO: might be more clever to check a setting flag somewhere + * and see if we should do this. */ + + lcb_SIZE n_inflated; + const void *inflated; + int rv; + + vdata = NULL; + rv = mcreq_inflate_value(SPAN_BUFFER(origspan), origspan->size, + &inflated, &n_inflated, (void**)&vdata); + + assert(vdata == inflated); + + if (rv != 0) { + return NULL; + } + nvdata = n_inflated; + hdr.request.datatype &= ~PROTOCOL_BINARY_DATATYPE_COMPRESSED; + hdr.request.bodylen = htonl( + ntohs(hdr.request.keylen) + + hdr.request.extlen + + n_inflated); + mcreq_write_hdr(dst, &hdr); + + } else { + nvdata = origspan->size; + vdata = malloc(nvdata); + memcpy(vdata, SPAN_BUFFER(origspan), nvdata); + } + } + + /* Declare the value as a standalone malloc'd span */ + CREATE_STANDALONE_SPAN(&dst->u_value.single, vdata, nvdata); + } + + if (src->flags & MCREQ_F_DETACHED) { + mc_EXPACKET *esrc = (mc_EXPACKET *)src; + sllist_iterator iter; + SLLIST_ITERFOR(&esrc->data, &iter) { + sllist_node *cur = iter.cur; + sllist_iter_remove(&esrc->data, &iter); + sllist_append(&edst->data, cur); + } + } + return dst; +} + +int +mcreq_epkt_insert(mc_EXPACKET *ep, mc_EPKTDATUM *datum) +{ + if (!(ep->base.flags & MCREQ_F_DETACHED)) { + return -1; + } + assert(!sllist_contains(&ep->data, &datum->slnode)); + sllist_append(&ep->data, &datum->slnode); + return 0; +} + +mc_EPKTDATUM * +mcreq_epkt_find(mc_EXPACKET *ep, const char *key) +{ + sllist_iterator iter; + SLLIST_ITERFOR(&ep->data, &iter) { + mc_EPKTDATUM *d = SLLIST_ITEM(iter.cur, mc_EPKTDATUM, slnode); + if (!strcmp(key, d->key)) { + return d; + } + } + return NULL; +} + +void +mcreq_map_key(mc_CMDQUEUE *queue, + const lcb_KEYBUF *key, const lcb_KEYBUF *hashkey, + unsigned nhdr, int *vbid, int *srvix) +{ + const void *hk; + size_t nhk = 0; + if (hashkey) { + if (hashkey->type == LCB_KV_COPY && hashkey->contig.bytes != NULL) { + hk = hashkey->contig.bytes; + nhk = hashkey->contig.nbytes; + } else if (hashkey->type == LCB_KV_VBID) { + *vbid = hashkey->contig.nbytes; + *srvix = lcbvb_vbmaster(queue->config, *vbid); + return; + } + } + if (!nhk) { + if (key->type == LCB_KV_COPY) { + hk = key->contig.bytes; + nhk = key->contig.nbytes; + } else { + const char *buf = key->contig.bytes; + buf += nhdr; + hk = buf; + nhk = key->contig.nbytes - nhdr; + } + } + lcbvb_map_key(queue->config, hk, nhk, vbid, srvix); +} + +lcb_error_t +mcreq_basic_packet( + mc_CMDQUEUE *queue, const lcb_CMDBASE *cmd, + protocol_binary_request_header *req, lcb_uint8_t extlen, + mc_PACKET **packet, mc_PIPELINE **pipeline, int options) +{ + int vb, srvix; + + if (!queue->config) { + return LCB_CLIENT_ETMPFAIL; + } + + mcreq_map_key(queue, &cmd->key, &cmd->_hashkey, + sizeof(*req) + extlen, &vb, &srvix); + if (srvix > -1 && srvix < (int)queue->npipelines) { + *pipeline = queue->pipelines[srvix]; + + } else { + if ((options & MCREQ_BASICPACKET_F_FALLBACKOK) && queue->fallback) { + *pipeline = queue->fallback; + } else { + return LCB_NO_MATCHING_SERVER; + } + } + + *packet = mcreq_allocate_packet(*pipeline); + + mcreq_reserve_key(*pipeline, *packet, sizeof(*req) + extlen, &cmd->key); + + req->request.keylen = htons((*packet)->kh_span.size - PKT_HDRSIZE(*packet)); + req->request.vbucket = htons(vb); + req->request.extlen = extlen; + return LCB_SUCCESS; +} + +void +mcreq_get_key(const mc_PACKET *packet, const void **key, lcb_size_t *nkey) +{ + *key = SPAN_BUFFER(&packet->kh_span) + PKT_HDRSIZE(packet); + *nkey = packet->kh_span.size - PKT_HDRSIZE(packet); +} + +lcb_uint32_t +mcreq_get_bodysize(const mc_PACKET *packet) +{ + lcb_uint32_t ret; + char *retptr = SPAN_BUFFER(&packet->kh_span) + 8; + if ((uintptr_t)retptr % sizeof(ret) == 0) { + return ntohl(*(lcb_uint32_t*) (void *)retptr); + } else { + memcpy(&ret, retptr, sizeof(ret)); + return ntohl(ret); + } +} + +uint16_t +mcreq_get_vbucket(const mc_PACKET *packet) +{ + uint16_t ret; + char *retptr = SPAN_BUFFER(&packet->kh_span) + 6; + if ((uintptr_t)retptr % sizeof(ret) == 0) { + return ntohs(*(uint16_t*)(void*)retptr); + } else { + memcpy(&ret, retptr, sizeof ret); + return ntohs(ret); + } +} + +uint32_t +mcreq_get_size(const mc_PACKET *packet) +{ + uint32_t sz = packet->kh_span.size; + if (packet->flags & MCREQ_F_HASVALUE) { + if (packet->flags & MCREQ_F_VALUE_IOV) { + sz += packet->u_value.multi.total_length; + } else { + sz += packet->u_value.single.size; + } + } + return sz; +} + +void +mcreq_pipeline_cleanup(mc_PIPELINE *pipeline) +{ + netbuf_cleanup(&pipeline->nbmgr); + netbuf_cleanup(&pipeline->reqpool); +} + +int +mcreq_pipeline_init(mc_PIPELINE *pipeline) +{ + nb_SETTINGS settings; + netbuf_default_settings(&settings); + + /** Initialize datapool */ + netbuf_init(&pipeline->nbmgr, &settings); + + /** Initialize request pool */ + settings.data_basealloc = sizeof(mc_PACKET) * 32; + netbuf_init(&pipeline->reqpool, &settings); + return 0; +} + +void +mcreq_queue_add_pipelines( + mc_CMDQUEUE *queue, mc_PIPELINE * const *pipelines, unsigned npipelines, + lcbvb_CONFIG* config) +{ + unsigned ii; + + lcb_assert(queue->pipelines == NULL); + queue->npipelines = npipelines; + queue->_npipelines_ex = queue->npipelines; + queue->pipelines = malloc(sizeof(*pipelines) * (npipelines + 1)); + queue->config = config; + + memcpy(queue->pipelines, pipelines, sizeof(*pipelines) * npipelines); + + free(queue->scheds); + queue->scheds = calloc(npipelines+1, 1); + + for (ii = 0; ii < npipelines; ii++) { + pipelines[ii]->parent = queue; + pipelines[ii]->index = ii; + } + + if (queue->fallback) { + queue->fallback->index = npipelines; + queue->pipelines[queue->npipelines] = queue->fallback; + queue->_npipelines_ex++; + } +} + +mc_PIPELINE ** +mcreq_queue_take_pipelines(mc_CMDQUEUE *queue, unsigned *count) +{ + mc_PIPELINE **ret = queue->pipelines; + *count = queue->npipelines; + queue->pipelines = NULL; + queue->npipelines = 0; + return ret; +} + +int +mcreq_queue_init(mc_CMDQUEUE *queue) +{ + queue->seq = 0; + queue->pipelines = NULL; + queue->scheds = NULL; + queue->fallback = NULL; + queue->npipelines = 0; + return 0; +} + +void +mcreq_queue_cleanup(mc_CMDQUEUE *queue) +{ + if (queue->fallback) { + mcreq_pipeline_cleanup(queue->fallback); + free(queue->fallback); + queue->fallback = NULL; + } + free(queue->scheds); + free(queue->pipelines); + queue->scheds = NULL; +} + +void +mcreq_sched_enter(mc_CMDQUEUE *queue) +{ + queue->ctxenter = 1; +} + + + +static void +queuectx_leave(mc_CMDQUEUE *queue, int success, int flush) +{ + unsigned ii; + + if (queue->ctxenter) { + queue->ctxenter = 0; + } + + for (ii = 0; ii < queue->_npipelines_ex; ii++) { + mc_PIPELINE *pipeline; + sllist_node *ll_next, *ll; + + if (!queue->scheds[ii]) { + continue; + } + + pipeline = queue->pipelines[ii]; + ll = SLLIST_FIRST(&pipeline->ctxqueued); + + while (ll) { + mc_PACKET *pkt = SLLIST_ITEM(ll, mc_PACKET, slnode); + ll_next = ll->next; + + if (success) { + mcreq_enqueue_packet(pipeline, pkt); + } else { + if (pkt->flags & MCREQ_F_REQEXT) { + mc_REQDATAEX *rd = pkt->u_rdata.exdata; + if (rd->procs->fail_dtor) { + rd->procs->fail_dtor(pkt); + } + } + mcreq_wipe_packet(pipeline, pkt); + mcreq_release_packet(pipeline, pkt); + } + + ll = ll_next; + } + SLLIST_FIRST(&pipeline->ctxqueued) = pipeline->ctxqueued.last = NULL; + if (flush) { + pipeline->flush_start(pipeline); + } + queue->scheds[ii] = 0; + } +} + +void +mcreq_sched_leave(mc_CMDQUEUE *queue, int do_flush) +{ + queuectx_leave(queue, 1, do_flush); +} + +void +mcreq_sched_fail(mc_CMDQUEUE *queue) +{ + queuectx_leave(queue, 0, 0); +} + +void +mcreq_sched_add(mc_PIPELINE *pipeline, mc_PACKET *pkt) +{ + mc_CMDQUEUE *cq = pipeline->parent; + if (!cq->scheds[pipeline->index]) { + cq->scheds[pipeline->index] = 1; + } + sllist_append(&pipeline->ctxqueued, &pkt->slnode); +} + +static mc_PACKET * +pipeline_find(mc_PIPELINE *pipeline, lcb_uint32_t opaque, int do_remove) +{ + sllist_iterator iter; + SLLIST_ITERFOR(&pipeline->requests, &iter) { + mc_PACKET *pkt = SLLIST_ITEM(iter.cur, mc_PACKET, slnode); + if (pkt->opaque == opaque) { + if (do_remove) { + sllist_iter_remove(&pipeline->requests, &iter); + } + return pkt; + } + } + return NULL; +} + +mc_PACKET * +mcreq_pipeline_find(mc_PIPELINE *pipeline, lcb_uint32_t opaque) +{ + return pipeline_find(pipeline, opaque, 0); +} + +mc_PACKET * +mcreq_pipeline_remove(mc_PIPELINE *pipeline, lcb_uint32_t opaque) +{ + return pipeline_find(pipeline, opaque, 1); +} + +void +mcreq_packet_done(mc_PIPELINE *pipeline, mc_PACKET *pkt) +{ + assert(pkt->flags & MCREQ_F_FLUSHED); + assert(pkt->flags & MCREQ_F_INVOKED); + if (pkt->flags & MCREQ_UBUF_FLAGS) { + void *kbuf, *vbuf; + const void *cookie; + + cookie = MCREQ_PKT_COOKIE(pkt); + if (pkt->flags & MCREQ_F_KEY_NOCOPY) { + kbuf = SPAN_BUFFER(&pkt->kh_span); + } else { + kbuf = NULL; + } + if (pkt->flags & MCREQ_F_VALUE_NOCOPY) { + if (pkt->flags & MCREQ_F_VALUE_IOV) { + vbuf = pkt->u_value.multi.iov->iov_base; + } else { + vbuf = SPAN_SABUFFER_NC(&pkt->u_value.single); + } + } else { + vbuf = NULL; + } + + pipeline->buf_done_callback(pipeline, cookie, kbuf, vbuf); + } + mcreq_wipe_packet(pipeline, pkt); + mcreq_release_packet(pipeline, pkt); +} + +void +mcreq_reset_timeouts(mc_PIPELINE *pl, lcb_U64 nstime) +{ + sllist_node *nn; + SLLIST_ITERBASIC(&pl->requests, nn) { + mc_PACKET *pkt = SLLIST_ITEM(nn, mc_PACKET, slnode); + MCREQ_PKT_RDATA(pkt)->start = nstime; + } +} + +unsigned +mcreq_pipeline_timeout( + mc_PIPELINE *pl, lcb_error_t err, mcreq_pktfail_fn failcb, void *cbarg, + hrtime_t oldest_valid, hrtime_t *oldest_start) +{ + sllist_iterator iter; + unsigned count = 0; + + SLLIST_ITERFOR(&pl->requests, &iter) { + mc_PACKET *pkt = SLLIST_ITEM(iter.cur, mc_PACKET, slnode); + mc_REQDATA *rd = MCREQ_PKT_RDATA(pkt); + + /** + * oldest_valid contains the LOWEST timestamp we can admit to being + * acceptable. If the current command is newer (i.e. has a higher + * timestamp) then we break the iteration and return. + */ + if (oldest_valid && rd->start > oldest_valid) { + if (oldest_start) { + *oldest_start = rd->start; + } + return count; + } + + sllist_iter_remove(&pl->requests, &iter); + failcb(pl, pkt, err, cbarg); + mcreq_packet_handled(pl, pkt); + count++; + } + return count; +} + +unsigned +mcreq_pipeline_fail( + mc_PIPELINE *pl, lcb_error_t err, mcreq_pktfail_fn failcb, void *arg) +{ + return mcreq_pipeline_timeout(pl, err, failcb, arg, 0, NULL); +} + +void +mcreq_iterwipe(mc_CMDQUEUE *queue, mc_PIPELINE *src, + mcreq_iterwipe_fn callback, void *arg) +{ + sllist_iterator iter; + + SLLIST_ITERFOR(&src->requests, &iter) { + int rv; + mc_PACKET *orig = SLLIST_ITEM(iter.cur, mc_PACKET, slnode); + rv = callback(queue, src, orig, arg); + if (rv == MCREQ_REMOVE_PACKET) { + sllist_iter_remove(&src->requests, &iter); + } + } +} + +#include "mcreq-flush-inl.h" +typedef struct { + mc_PIPELINE base; + mcreq_fallback_cb handler; +} mc_FALLBACKPL; + +static void +do_fallback_flush(mc_PIPELINE *pipeline) +{ + nb_IOV iov; + unsigned nb; + int nused; + sllist_iterator iter; + mc_FALLBACKPL *fpl = (mc_FALLBACKPL*)pipeline; + + while ((nb = mcreq_flush_iov_fill(pipeline, &iov, 1, &nused))) { + mcreq_flush_done(pipeline, nb, nb); + } + /* Now handle all the packets, for real */ + SLLIST_ITERFOR(&pipeline->requests, &iter) { + mc_PACKET *pkt = SLLIST_ITEM(iter.cur, mc_PACKET, slnode); + fpl->handler(pipeline->parent, pkt); + sllist_iter_remove(&pipeline->requests, &iter); + mcreq_packet_handled(pipeline, pkt); + } +} + +void +mcreq_set_fallback_handler(mc_CMDQUEUE *cq, mcreq_fallback_cb handler) +{ + assert(!cq->fallback); + cq->fallback = calloc(1, sizeof (mc_FALLBACKPL)); + mcreq_pipeline_init(cq->fallback); + cq->fallback->parent = cq; + cq->fallback->index = cq->npipelines; + ((mc_FALLBACKPL*)cq->fallback)->handler = handler; + cq->fallback->flush_start = do_fallback_flush; +} + +static void +noop_dumpfn(const void *d, unsigned n, FILE *fp) { (void)d;(void)n;(void)fp; } + +#define MCREQ_XFLAGS(X) \ + X(KEY_NOCOPY) \ + X(VALUE_NOCOPY) \ + X(VALUE_IOV) \ + X(HASVALUE) \ + X(REQEXT) \ + X(UFWD) \ + X(FLUSHED) \ + X(INVOKED) \ + X(DETACHED) + +void +mcreq_dump_packet(const mc_PACKET *packet, FILE *fp, mcreq_payload_dump_fn dumpfn) +{ + const char *indent = " "; + const mc_REQDATA *rdata = MCREQ_PKT_RDATA(packet); + + if (!dumpfn) { + dumpfn = noop_dumpfn; + } + if (!fp) { + fp = stderr; + } + + fprintf(fp, "Packet @%p\n", (void *)packet); + fprintf(fp, "%sOPAQUE: %u\n", indent, (unsigned int)packet->opaque); + + fprintf(fp, "%sPKTFLAGS: 0x%x ", indent, packet->flags); + #define X(base) \ + if (packet->flags & MCREQ_F_##base) { fprintf(fp, "%s, ", #base); } + MCREQ_XFLAGS(X) + #undef X + fprintf(fp, "\n"); + + fprintf(fp, "%sKey+Header Size: %u\n", indent, (unsigned int)packet->kh_span.size); + fprintf(fp, "%sKey Offset: %u\n", indent, MCREQ_PKT_BASESIZE + packet->extlen); + + + if (packet->flags & MCREQ_F_HASVALUE) { + if (packet->flags & MCREQ_F_VALUE_IOV) { + fprintf(fp, "%sValue Length: %u\n", indent, + packet->u_value.multi.total_length); + + fprintf(fp, "%sValue IOV: [start=%p, n=%d]\n", indent, + (void *)packet->u_value.multi.iov, packet->u_value.multi.niov); + } else { + if (packet->flags & MCREQ_F_VALUE_NOCOPY) { + fprintf(fp, "%sValue is user allocated\n", indent); + } + fprintf(fp, "%sValue: %p, %u bytes\n", indent, + SPAN_BUFFER(&packet->u_value.single), packet->u_value.single.size); + } + } + + fprintf(fp, "%sRDATA(%s): %p\n", indent, + (packet->flags & MCREQ_F_REQEXT) ? "ALLOC" : "EMBEDDED", (void *)rdata); + + indent = " "; + fprintf(fp, "%sStart: %lu\n", indent, (unsigned long)rdata->start); + fprintf(fp, "%sCookie: %p\n", indent, rdata->cookie); + + indent = " "; + fprintf(fp, "%sNEXT: %p\n", indent, (void *)packet->slnode.next); + if (dumpfn != noop_dumpfn) { + fprintf(fp, "PACKET CONTENTS:\n"); + } + + fwrite(SPAN_BUFFER(&packet->kh_span), 1, packet->kh_span.size, fp); + if (packet->flags & MCREQ_F_HASVALUE) { + if (packet->flags & MCREQ_F_VALUE_IOV) { + const lcb_IOV *iovs = packet->u_value.multi.iov; + unsigned ii, ixmax = packet->u_value.multi.niov; + for (ii = 0; ii < ixmax; ii++) { + dumpfn(iovs[ii].iov_base, iovs[ii].iov_len, fp); + } + } else { + const nb_SPAN *vspan = &packet->u_value.single; + dumpfn(SPAN_BUFFER(vspan), vspan->size, fp); + } + } +} + +void +mcreq_dump_chain(const mc_PIPELINE *pipeline, FILE *fp, mcreq_payload_dump_fn dumpfn) +{ + sllist_node *ll; + SLLIST_FOREACH(&pipeline->requests, ll) { + const mc_PACKET *pkt = SLLIST_ITEM(ll, mc_PACKET, slnode); + mcreq_dump_packet(pkt, fp, dumpfn); + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/mc/mcreq.h b/couchbase-sys/libcouchbase-2.7.0/src/mc/mcreq.h new file mode 100644 index 00000000..1032385a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/mc/mcreq.h @@ -0,0 +1,977 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_MCREQ_H +#define LCB_MCREQ_H + +#include +#include +#include +#include +#include "netbuf/netbuf.h" +#include "sllist.h" +#include "config.h" +#include "packetutils.h" + +#ifdef __cplusplus +extern "C" { +#endif /** __cplusplus */ + +/** + * @file + * @brief Core memcached client routines + */ + +/** + * @defgroup mcreq Memcached Packets + * + * @brief + * This module defines the core routines which are used to construct, handle, + * and enqueue packets. They also handle the retry mechanisms. + * + * + * # Initializing the Queue + * + * Using the mcreq system involves first establishing an mc_CMDQUEUE structure. + * This structure contains several mc_PIPELINE structures. The proper way to + * initialize the mc_CMDQEUE structure is to call mcreq_queue_init(). + * + * Once the queue has been initialized, it must be assigned a + * `lcbvb_CONFIG*` (which it will _not_ own). This is done via the + * mcreq_queue_add_pipelines(). This function takes an array of pipeline pointers, + * and this will typically be a "subclass" (mc_SERVER) allocated via + * mcserver_alloc() + * + * Once the pipelines have been established, operations may be scheduled and + * distributed across the various pipelines. + * + * # Creating a Packet + * + * For each packet sent, the packet should first be reserved via the + * mcreq_basic_packet() call which allocates space for the actual packet + * as well as provides and populates the vbucket fields as needed. + * + * The header size must be the total size of the header plus any extras + * following the header but before the actual key data. + * + * If the command carries a body in addition to the key, it should be provided + * via mcreq_reserve_value(). + * + * Once the packet has a key and value it must be assigned a cookie. The + * cookie may either be of a simple embedded type or an extended type. Whatever + * the case the appropriate flags should be set. + * + * # Scheduling Commands + * + * Scheduling commands is performed in an _enter_ and _leave_ sequence. + * mcreq_sched_enter() should be called before one or more commands are added. + * Then for each new command added, mcreq_sched_add() should be invoked with + * the new packet, and finally either mcreq_sched_leave() or mcreq_sched_fail() + * should be invoked to flush the commands to the network or free the resources + * allocated. In both cases the commands affected are scoped by the last call + * to mcreq_sched_enter(). + * + * In order for commands to actually be flushed, the mc_PIPELINE::flush_start + * field must be set. This can vary depending on what the state of the underlying + * socket is. In server.c for example, the initial callback just schedules a + * connection. While the connection is in progress this field is set to a no-op + * callback, and finally when the socket is connected this field is set to + * interact with the I/O system which actually writes the buffers. + * + * # Flushing Responses + * + * This module does not do network I/O by design. Its only bridge is the + * mc_PIPELINE::flush_start function which should be set to actually flush + * the data. + * + * # Handling Reponses + * + * The I/O system reading the responses should place the response into a + * packet_info structure. Once this is done, the request for the response must + * be found using the opaque. This may be done with mcreq_pipeline_find() + * or mcreq_pipeline_remove() depending on whether this request expects multiple + * responses (such as the 'stat' command). These parameters should be passed + * to the mcreq_dispatch_response() function which will invoke the appropriate + * user-defined handler for it. + * + * If the packet does not expect more responses (as above), the application + * should call mcreq_packet_handled() + * + * + * # Error Handling and Failing Commands + * + * This module offers facilities for failing commands from a pipeline while + * safely allowing for their sharing of user-allocated data. + * + * The mcreq_pipeline_fail() and mcreq_pipeline_timeout() will fail packets + * in a single pipeline (the former failing all packets, the latter failing + * only packets older than a specified threshold). + * + * The mcreq_iterwipe() will clean a pipeline of its packets, invoking a + * callback which allows the user to relocate the packet to another pipeline. + * In this callback the user may invoke the mcreq_renew_packet() function to + * create a copy of the packet, keeping the previous packet in tact, but + * returning a copy of the packet as the 'primary' version. + * + * @addtogroup mcreq + * @{ + */ + + +/** + * @name Core Packet Structure + * @{ + */ + +/** @brief Constant defining the size of a memcached header */ +#define MCREQ_PKT_BASESIZE 24 + +/** @brief Embedded user data for a simple request. */ +typedef struct { + const void *cookie; /**< User pointer to place in callbacks */ + hrtime_t start; /**< Time of the initial request. Used for timeouts */ +} mc_REQDATA; + +struct mc_packet_st; +struct mc_pipeline_st; + +/** This structure serves as a kind of 'vtable' for the mc_REQDATAEX structure. */ +typedef struct { + /** + * Callback to be invoked for "Extended" packet handling. This is only + * available in the mc_REQDATAEX structure + * @param pipeline the pipeline on which the response was received + * @param pkt the request packet + * @param rc the error code for the response + * @param arg opaque pointer for callback + */ + void (*handler)(struct mc_pipeline_st *pipeline, + struct mc_packet_st *pkt, lcb_error_t rc, const void *res); + + /** + * Destructor function called from within mcreq_sched_fail() for packets with + * extended data. This function should suitably free the data for the packet, + * if any. + * @param pkt The packet being unscheduled. + */ + void (*fail_dtor)(struct mc_packet_st *pkt); +} mc_REQDATAPROCS; + +/**@brief Allocated user data for an extended request. + * + * @details + * An extended request is typically used by commands which have more complex + * handling requirements, such as mapping a single user API call to multiple + * packets, or when the packet itself is generated internally rather than + * on behalf of an API request. + */ +typedef struct { + const void *cookie; /**< User data */ + hrtime_t start; /**< Start time */ + mc_REQDATAPROCS *procs; /**< Common routines for the packet */ +} mc_REQDATAEX; + +/** + * Called when the buffers for a packet have been invoked + * @param pl the pipeline + * @param ucookie the cookie passed to the scheduler + * @param kbuf the pointer to the beginning of the key/header buffer, if + * passed to the scheduler + * @param vbuf the pointer to the beginning of the value buffer or the first + * IOV within the buffer. + */ +typedef void (*mcreq_bufdone_fn)(struct mc_pipeline_st *pl, + const void *ucookie, void *kbuf, void *vbuf); + +/** + * Possible values for the mc_PACKET#flags field in the packet structure. + * These provide + * information as to which fields in the various unions are in use, and how + * to allocate/release data buffers for requests. + */ +typedef enum { + /** The key is user-allocated. Do not release to MBLOCK */ + MCREQ_F_KEY_NOCOPY = 1 << 0, + + /** The value is user-allocated. Do not release to MBLOCK */ + MCREQ_F_VALUE_NOCOPY = 1 << 1, + + /** + * The value is user allocated and in the form of an IOV. + * Use mc_VALUE#multi + */ + MCREQ_F_VALUE_IOV = 1 << 2, + + /** The request has a value. Use mc_VALUE#single unless otherwise noted */ + MCREQ_F_HASVALUE = 1 << 3, + + /** + * The request is tied to an 'extended' user data structure. + * Use mc_USER#exdata + */ + MCREQ_F_REQEXT = 1 << 4, + + /** The request is a one-to-one user forwarded packet */ + MCREQ_F_UFWD = 1 << 5, + + /** + * Indicates that the entire packet has been flushed. Specifically this + * also indicates that the packet's underlying buffers are no longer needed + * by libcouchbase. + */ + MCREQ_F_FLUSHED = 1 << 6, + + /** + * Indicates that the callback should NOT be invoked for the request. This + * is typically because the request is just present in the queue for buffer + * management purposes and has expired or otherwise been invalidated. + */ + MCREQ_F_INVOKED = 1 << 7, + + /** + * Indicates that this packet and its constituent data members are not + * part of a nb_MBLOCK but rather point to standalone malloc'd memory. This + * also indicates that the packet is actually an mc_EXPACKET extended + * type. This is set by mcreq_renew_packet() + */ + MCREQ_F_DETACHED = 1 << 8, + + /** + * Another way of signalling that the callback has an 'internal' variant. + * Dispatching this command requires a specially formatted cookie object, + * which itself is expected to _contain_ a pointer to the callback, and + * thus be formatted like so: + * @code{.c} + * struct { + * lcb_RESPCALLBACK callback; + * }; + * @endcode + */ + MCREQ_F_PRIVCALLBACK = 1 << 9 +} mcreq_flags; + +/** @brief mask of flags indicating user-allocated buffers */ +#define MCREQ_UBUF_FLAGS (MCREQ_F_KEY_NOCOPY|MCREQ_F_VALUE_NOCOPY) +/** @brief mask of flags indicating response state of the packet */ +#define MCREQ_STATE_FLAGS (MCREQ_F_INVOKED|MCREQ_F_FLUSHED) + +/** Union representing the value within a packet */ +union mc_VALUE { + /** For a single contiguous value */ + nb_SPAN single; + + /** For a set of multiple IOV buffers */ + lcb_FRAGBUF multi; +}; + +/** Union representing application/command data within a packet structure */ +union mc_USER { + /** Embedded command info for simple commands; 16 bytes, 48B */ + mc_REQDATA reqdata; + + /** Pointer to extended data */ + mc_REQDATAEX *exdata; +}; + +/** + * @brief Packet structure for a single Memcached command + * + * A single packet structure is allocated for each request + * sent to a server. A packet structure may be associated with user data in the + * u_rdata union field, either by using the embedded structure, or by referencing + * an allocated chunk of 'extended' user data. + */ +typedef struct mc_packet_st { + /** Node in the linked list for logical command ordering */ + sllist_node slnode; + + /** + * Node in the linked list for actual output ordering. + * @see netbuf_end_flush2(), netbuf_pdu_enqueue() + */ + sllist_node sl_flushq; + + /** Span for key and header */ + nb_SPAN kh_span; + + /** Extras length */ + uint8_t extlen; + + /** Retries */ + uint8_t retries; + + /** flags for request. @see mcreq_flags */ + uint16_t flags; + + /** Cached opaque value */ + uint32_t opaque; + + /** User/CMDAPI Data */ + union mc_USER u_rdata; + + /** Value data */ + union mc_VALUE u_value; + + /** Allocation data for the PACKET structure itself */ + nb_MBLOCK *alloc_parent; +} mc_PACKET; + + +/** + * @brief Gets the request data from the packet structure itself + * @return an mc_REQDATA or mc_REQDATAEX pointer + */ +#define MCREQ_PKT_RDATA(pkt) \ + (((pkt)->flags & MCREQ_F_REQEXT) \ + ? ((mc_REQDATA *)(pkt)->u_rdata.exdata) \ + : (&(pkt)->u_rdata.reqdata)) + +/** + * @brief Retrieve the cookie pointer from a packet + * @param pkt + */ +#define MCREQ_PKT_COOKIE(pkt) MCREQ_PKT_RDATA(pkt)->cookie + +/**@}*/ + +/** + * Callback invoked when APIs request that a pipeline start flushing. It + * receives a pipeline object as its sole argument. + */ +typedef void (*mcreq_flushstart_fn)(struct mc_pipeline_st *pipeline); + +/** + * @brief Structure representing a single input/output queue for memcached + * + * Memcached request pipeline. This contains the command log for + * sending/receiving requests. This is basically the non-I/O part of the server + */ +typedef struct mc_pipeline_st { + /** List of requests. Newer requests are appended at the end */ + sllist_root requests; + + /** Parent command queue */ + struct mc_cmdqueue_st *parent; + + /** + * Flush handler. This is invoked to schedule a flush operation + * the socket + */ + mcreq_flushstart_fn flush_start; + + /** Index of this server within the configuration map */ + int index; + + /** + * Intermediate queue where pending packets are placed. Moved to + * the `requests` list when mcreq_sched_leave() is called + */ + sllist_root ctxqueued; + + /** + * Callback invoked for each packet (which has user-defined buffers) when + * it is no longer required + */ + mcreq_bufdone_fn buf_done_callback; + + /** Buffer manager for the respective requests. */ + nb_MGR nbmgr; + + /** Allocator for packet structures */ + nb_MGR reqpool; +} mc_PIPELINE; + +typedef struct mc_cmdqueue_st { + /** Indexed pipelines, i.e. server map target */ + mc_PIPELINE **pipelines; + + /** + * Small array of size npipelines, for mcreq_sched_enter()/mcreq_sched_leave() + * stuff. See those functions for usage + */ + char *scheds; + + /** + * Whether a context is currently entered (i.e. sched_enter()) + */ + unsigned ctxenter; + + /** Number of pipelines in the queue */ + unsigned npipelines; + + /** Number of pipelines, with fallback included */ + unsigned _npipelines_ex; + + /** Sequence number for pipeline. Incremented for each new packet */ + uint32_t seq; + + /** Configuration handle for vBucket mapping */ + lcbvb_CONFIG* config; + + /** Opaque pointer to be used by the application (in this case, lcb core) */ + void* cqdata; + + /**Special pipeline used to contain orphaned packets within a scheduling + * context. This field is used by mcreq_set_fallback_handler() */ + mc_PIPELINE *fallback; +} mc_CMDQUEUE; + +/** + * Allocate a packet belonging to a specific pipeline. + * @param pipeline the pipeline to allocate against + * @return a new packet structure or NULL on error + */ +mc_PACKET * +mcreq_allocate_packet(mc_PIPELINE *pipeline); + + +/** + * Free the packet structure. This will simply free the skeleton structure. + * The underlying members will not be touched. + * @param pipeline the pipleine which was used to allocate the packet + * @param packet the packet to release + */ +void +mcreq_release_packet(mc_PIPELINE *pipeline, mc_PACKET *packet); + +struct mc_epkt_datum; + +/** + * Extended packet structure. This is returned by mcreq_renew_packet(). + * + * The purpose of this structure is to be able to "tag" extra data to the packet + * (typically for retries, or "special" commands) without weighing down on the + * normal packet structure; thus it should be considered a 'subclass' of the + * normal packet structure. + */ +typedef struct mc_expacket_st { + /** The base packet structure */ + mc_PACKET base; + /* Additional data for the packet itself */ + sllist_root data; +} mc_EXPACKET; + +typedef struct mc_epkt_datum { + sllist_node slnode; + + /**Unique string key by which this datum will be identified, as more + * than a single datum can exist for a packet */ + const char *key; + + /**Free the data structure + * @param datum the datum object */ + void (*dtorfn)(struct mc_epkt_datum *datum); +} mc_EPKTDATUM; + +/** + * Detatches the packet src belonging to the given pipeline. A detached + * packet has all its data allocated via malloc and does not belong to + * any particular buffer. This is typically used for relocation or retries + * where it is impractical to affect the in-order netbuf allocator. + * + * @param src the source packet to copy + * @return a new packet structure. You should still clear the packet's data + * with wipe_packet/release_packet but you may pass NULL as the pipeline + * parameter. + * + * @attention + * Any 'Extended' packet data is **MOVED** from the source to the destination + * packet. This goes well with the typical use case of this function, which is + * not to actually duplicate the packet, but rather to provide a fresh copy + * which may be re-used. + * + * @attention + * This function attempts to be "dumb" in the sense of trying to make an + * exact effective clone of the original packet (the main goal of this function + * is to move the resources of the packet over to a new block of memory). This + * means things like non-buffer-related flags (i.e. the ones not specifying + * the layout of the buffer) are _preserved_, including the so-called + * "state flags" which indicate if a packet has been flushed and/or handled. If + * calling this function to retry a packet, ensure to clear these state flags. + */ +mc_PACKET * +mcreq_renew_packet(const mc_PACKET *src); + +/** + * Associates a datum with the packet. The packet must be a standalone packet, + * indicated by the MCREQ_F_DETACHED flag in the mc_PACKET::flags field. + * @param ep The packet to which the data should be added + * @param datum The datum object to add. The object is not copied and should + * not be freed until the `dtorfn` or `copyfn` functions have been called + * @return 0 on success, nonzero on failure (i.e. if packet is not detached). + */ +int +mcreq_epkt_insert(mc_EXPACKET *ep, mc_EPKTDATUM *datum); + +/** + * Locate the datum associated with the given key for the packet. + * @param ep The packet in which to search + * @param key A NUL-terminated string matching the mc_EPKTDATUM::key field + * @return The datum, or NULL if it does not exist. + */ +mc_EPKTDATUM * +mcreq_epkt_find(mc_EXPACKET *ep, const char *key); + +/** + * Reserve the packet's basic header structure, this is for use for frames + * which do not contain keys, or contain fixed size data which does not + * need to be returned via get_key + * @param pipeline the pipeline to use + * @param packet the packet which should contain the header + * @param hdrsize the total size of the header+extras+key + */ +lcb_error_t +mcreq_reserve_header( + mc_PIPELINE *pipeline, mc_PACKET *packet, uint8_t hdrsize); + +/** + * Initialize the given packet's key structure + * @param pipeline the pipeline used to allocate the packet + * @param packet the packet which should have its key field initialized + * @param hdrsize the size of the header before the key. This should contain + * the header size (i.e. 24 bytes) PLUS any extras therein. + * @param kreq the user-provided key structure + * @return LCB_SUCCESS on success, LCB_CLIENT_ENOMEM on allocation failure + */ +lcb_error_t +mcreq_reserve_key( + mc_PIPELINE *pipeline, mc_PACKET *packet, + uint8_t hdrsize, const lcb_KEYBUF *kreq); + + +/** + * Initialize the given packet's value structure. Only applicable for storage + * operations. + * @param pipeline the pipeline used to allocate the packet + * @param packet the packet whose value field should be initialized + * @param vreq the user-provided structure containing the value parameters + * @return LCB_SUCCESS on success, LCB_CLIENT_ENOMEM on allocation failure + */ +lcb_error_t +mcreq_reserve_value(mc_PIPELINE *pipeline, mc_PACKET *packet, + const lcb_VALBUF *vreq); + +/** + * Reserves value/body space, but doesn't actually copy the contents over + * @param pipeline the pipeline to use + * @param packet the packet to host the value + * @param n the number of bytes to reserve + */ +lcb_error_t +mcreq_reserve_value2(mc_PIPELINE *pipeline, mc_PACKET *packet, lcb_size_t n); + + +/** + * Enqueue the packet to the pipeline. This packet should have fully been + * initialized. Specifically, the packet's data buffer contents (i.e. key, + * header, and value) must not be modified once this function is called + * + * @param pipeline the target pipeline that the packet will be queued in + * @param packet the packet to enqueue. + * This function always succeeds. + */ +void +mcreq_enqueue_packet(mc_PIPELINE *pipeline, mc_PACKET *packet); + +/** + * Like enqueue packet, except it will also inspect the packet's timeout field + * and if necessary, restructure the command inside the request list so that + * it appears before newer commands. + * + * The default enqueue_packet() just appends the command to the end of the + * queue while this will perform an additional check (and is less efficient) + */ +void +mcreq_reenqueue_packet(mc_PIPELINE *pipeline, mc_PACKET *packet); + +/** + * Wipe the packet's internal buffers, releasing them. This should be called + * when the underlying data buffer fields are no longer needed, usually this + * is called directly before release_packet. + * Note that release_packet should be called to free the storage for the packet + * structure itself. + * @param pipeline the pipeline structure used to allocate this packet + * @param packet the packet to wipe. + */ +void +mcreq_wipe_packet(mc_PIPELINE *pipeline, mc_PACKET *packet); + +/** + * Function to extract mapping information given a key and a hashkey + * @param queue The command queue + * @param key The structure for the key + * @param hashkey The optional hashkey structure + * @param nhdr The size of the header (for KV_CONTIG) + * @param[out] vbid The vBucket ID + * @param[out] srvix The master server's index + */ +void +mcreq_map_key(mc_CMDQUEUE *queue, + const lcb_KEYBUF *key, const lcb_KEYBUF *hashkey, + unsigned nhdr, int *vbid, int *srvix); + + +/**If the packet's vbucket does not have a master node, use the fallback pipeline + * and let it be handled by the handler installed via mcreq_set_fallback_handler() + */ +#define MCREQ_BASICPACKET_F_FALLBACKOK 0x01 + +/** + * Handle the basic requirements of a packet common to all commands + * @param queue the queue + * @param cmd the command base structure + * + * @param[out] req the request header which will be set with key, vbucket, and extlen + * fields. In other words, you do not need to initialize them once this + * function has completed. + * + * @param extlen the size of extras for this command + * @param[out] packet a pointer set to the address of the allocated packet + * @param[out] pipeline a pointer set to the target pipeline + * @param options a set of options to control creation behavior. Currently the + * only recognized options are `0` (i.e. default options), or @ref + * MCREQ_BASICPACKET_F_FALLBACKOK + */ + +lcb_error_t +mcreq_basic_packet( + mc_CMDQUEUE *queue, const lcb_CMDBASE *cmd, + protocol_binary_request_header *req, uint8_t extlen, + mc_PACKET **packet, mc_PIPELINE **pipeline, int options); + +/** + * @brief Get the key from a packet + * @param[in] packet The packet from which to retrieve the key + * @param[out] key + * @param[out] nkey + */ +void +mcreq_get_key(const mc_PACKET *packet, const void **key, lcb_size_t *nkey); + +/** @brief Returns the size of the entire packet, in bytes */ +uint32_t +mcreq_get_bodysize(const mc_PACKET *packet); + +/** + * @brief get the total packet size (header+body) + * @param pkt the packet + * @return the total size + */ +uint32_t +mcreq_get_size(const mc_PACKET *packet); + +/** + * @brief Get the vBucket for the request + * @param packet The packet + * @return The vBucket ID from the packet. + */ +uint16_t +mcreq_get_vbucket(const mc_PACKET *packet); + +/** Initializes a single pipeline object */ +int +mcreq_pipeline_init(mc_PIPELINE *pipeline); + +/** Cleans up any initialization from pipeline_init */ +void +mcreq_pipeline_cleanup(mc_PIPELINE *pipeline); + + +/** + * Set the pipelines that this queue will manage + * @param queue the queue to take the pipelines + * @param pipelines an array of pipeline pointers. The array is copied + * @param npipelines number of pipelines in the queue + * @param config the configuration handle. The configuration is _not_ owned + * and _not_ copied and the caller must ensure it remains valid + * until it is replaces. + */ +void +mcreq_queue_add_pipelines( + mc_CMDQUEUE *queue, mc_PIPELINE * const *pipelines, + unsigned npipelines, lcbvb_CONFIG* config); + + +/** + * Set the arra + * @param queue the queue + * @param count a pointer to the number of pipelines within the queue + * @return the pipeline array. + * + * When this function completes another call to add_pipelines must be performed + * in order for the queue to function properly. + */ +mc_PIPELINE ** +mcreq_queue_take_pipelines(mc_CMDQUEUE *queue, unsigned *count); + +int +mcreq_queue_init(mc_CMDQUEUE *queue); + +void +mcreq_queue_cleanup(mc_CMDQUEUE *queue); + +/** + * @brief Add a packet to the current scheduling context + * @param pipeline + * @param pkt + * @see mcreq_sched_enter() + */ +void +mcreq_sched_add(mc_PIPELINE *pipeline, mc_PACKET *pkt); + +/** + * @brief enter a scheduling scope + * @param queue + * @attention It is not safe to call this function twice + * @volatile + */ +void +mcreq_sched_enter(struct mc_cmdqueue_st *queue); + +/** + * @brief successfully exit a scheduling scope + * + * All operations enqueued since the last call to mcreq_sched_enter() will be + * placed in their respective pipelines' operation queue. + * + * @param queue + * @param do_flush Whether the items in the queue should be flushed + * @volatile + */ +void +mcreq_sched_leave(struct mc_cmdqueue_st *queue, int do_flush); + +/** + * @brief destroy all operations within the scheduling scope + * All operations enqueued since the last call to mcreq_sched_enter() will + * be destroyed + * @param queue + */ +void +mcreq_sched_fail(struct mc_cmdqueue_st *queue); + +/** + * Find a packet with the given opaque value + */ +mc_PACKET * +mcreq_pipeline_find(mc_PIPELINE *pipeline, uint32_t opaque); + +/** + * Find and remove the packet with the given opaque value + */ +mc_PACKET * +mcreq_pipeline_remove(mc_PIPELINE *pipeline, uint32_t opaque); + +/** + * Handles a received packet in response to a command + * @param pipeline the pipeline upon which the request was received + * @param request the original request + * @param response the packet received in the response + * @param immerr an immediate error message. If this is not LCB_SUCCESS then + * the packet in `response` shall contain only a header and the request itself + * should be analyzed + * + * @return 0 on success, nonzero if the handler could not be found for the + * command. + */ +int +mcreq_dispatch_response(mc_PIPELINE *pipeline, mc_PACKET *request, + packet_info *response, lcb_error_t immerr); + + +#define MCREQ_KEEP_PACKET 1 +#define MCREQ_REMOVE_PACKET 2 +/** + * Callback used for packet iteration wiping + * + * @param queue the queue + * @param srcpl the source pipeline which is being cleaned + * @param pkt the packet which is being cleaned + * @param cbarg the argument passed to the iterwipe + * + * @return one of MCREQ_KEEP_PACKET (if the packet should be kept inside the + * pipeline) or MCREQ_REMOVE_PACKET (if the packet should not be kept) + */ +typedef int (*mcreq_iterwipe_fn) + (mc_CMDQUEUE *queue, mc_PIPELINE *srcpl, mc_PACKET *pkt, void *cbarg); +/** + * Wipe a single pipeline. This may be used to move and/or relocate + * existing commands to other pipelines. + * @param queue the queue to use + * @param src the pipeline to wipe + * @param callback the callback to invoke for each packet + * @param arg the argument passed to the callback + */ +void +mcreq_iterwipe(mc_CMDQUEUE *queue, mc_PIPELINE *src, + mcreq_iterwipe_fn callback, void *arg); + + +/** + * Called when a packet does not need to have any more references to it + * remaining. A packet by itself only has two implicit references; one is + * a flush reference and the other is a handler reference. + * + * The flush reference is unset once the packet has been flushed and the + * handler reference is unset once the packet's handler callback has been + * invoked and the relevant data relayed to the user. + * + * Once this function is called, the packet passed will no longer be valid + * and thus should not be used. + */ +void +mcreq_packet_done(mc_PIPELINE *pipeline, mc_PACKET *pkt); + +/** + * @brief Indicate that the packet was handled + * @param pipeline the pipeline + * @param pkt the packet which was handled + * If the packet has also been flushed, the packet's storage will be released + * and `pkt` will no longer point to valid memory. + */ +#define mcreq_packet_handled(pipeline, pkt) do { \ + (pkt)->flags |= MCREQ_F_INVOKED; \ + if ((pkt)->flags & MCREQ_F_FLUSHED) { \ + mcreq_packet_done(pipeline, pkt); \ + } \ +} while (0); + +/** + * Reset the timeout (or rather, the start time) on all pending packets + * to the time specified. + * + * @param pl The pipeline + * @param nstime The new timestamp to use. + */ +void +mcreq_reset_timeouts(mc_PIPELINE *pl, lcb_U64 nstime); + +/** + * Callback to be invoked when a packet is about to be failed out from the + * request queue. This should be used to possibly invoke handlers. The packet + * will then be removed from the queue. + * @param pipeline the pipeline which has been errored + * @param packet the current packet + * @param err the error received + * @param arg an opaque pointer + */ +typedef void (*mcreq_pktfail_fn) + (mc_PIPELINE *pipeline, mc_PACKET *packet, lcb_error_t err, void *arg); + +/** + * Fail out a given pipeline. All commands in the pipeline will be removed + * from the pipeline (though they may still not be freed if they are pending + * a flush). + * + * @param pipeline the pipeline to fail out + * @param err the error which caused the failure + * @param failcb a callback invoked to handle each failed packet + * @param cbarg a pointer passed as the last parameter to the callback + * + * @return the number of items actually failed. + */ +unsigned +mcreq_pipeline_fail( + mc_PIPELINE *pipeline, lcb_error_t err, + mcreq_pktfail_fn failcb, void *cbarg); + +/** + * Fail out all commands in the pipeline which are older than a specified + * interval. This is similar to the pipeline_fail() function except that commands + * which are newer than the threshold are still kept + * + * @param pipeline the pipeline to fail out + * @param err the error to provide to the handlers (usually LCB_ETIMEDOUT) + * @param failcb the callback to invoke + * @param cbarg the last argument to the callback + * @param oldest_valid the _oldest_ time for a command to still be valid + * @param oldest_start set to the start time of the _oldest_ command which is + * still valid. + * + * @return the number of commands actually failed. + */ +unsigned +mcreq_pipeline_timeout( + mc_PIPELINE *pipeline, lcb_error_t err, + mcreq_pktfail_fn failcb, void *cbarg, + hrtime_t oldest_valid, + hrtime_t *oldest_start); + +/** + * This function is called when a packet could not be properly mapped to a real + * pipeline + * @param cq the command queue + * @param pkt the packet which needs to be relocated. The packet needs to be + * properly copied via mcreq_renew_packet() + */ +typedef void (*mcreq_fallback_cb)(mc_CMDQUEUE *cq, mc_PACKET *pkt); + +/** + * Set the callback function to be invoked when a packet could not be properly + * mapped to a node. The callback function is invoked from within the + * mcreq_sched_leave() function. + * + * The handler should be assigned only once, during initialization + * + * @param cq The command queue + * @param handler The handler to invoke + */ +void +mcreq_set_fallback_handler(mc_CMDQUEUE *cq, mcreq_fallback_cb handler); + +/** + * Callback used by mcreq_dump_packet() and mcreq_dump_chain() to format the + * packet's payload + * @param data the data to dump + * @param size the size of the data + * @param fp the file to write the output to + */ +typedef void (*mcreq_payload_dump_fn) + (const void *data, unsigned size, FILE *fp); + +/** + * Dumps a single packet to the file indicated by `fp` + * @param pkt the packet to dump + * @param fp The file to write to + * @param dumpfn If specified, this function is called to handle the packet's + * header and payload body + */ +void +mcreq_dump_packet(const mc_PACKET *pkt, FILE *fp, mcreq_payload_dump_fn dumpfn); + +void +mcreq_dump_chain(const mc_PIPELINE *pipeline, FILE *fp, mcreq_payload_dump_fn dumpfn); + +#define mcreq_write_hdr(pkt, hdr) \ + memcpy( SPAN_BUFFER(&(pkt)->kh_span), (hdr)->bytes, sizeof((hdr)->bytes) ) + +#define mcreq_write_exhdr(pkt, hdr, n) \ + memcpy(SPAN_BUFFER((&pkt)->kh_span), (hdr)->bytes, n) + +#define mcreq_read_hdr(pkt, hdr) \ + memcpy( (hdr)->bytes, SPAN_BUFFER(&(pkt)->kh_span), sizeof((hdr)->bytes) ) + +#define mcreq_first_packet(pipeline) \ + SLLIST_IS_EMPTY(&(pipeline)->requests) ? NULL : \ + SLLIST_ITEM(SLLIST_FIRST(&(pipeline)->requests), mc_PACKET, slnode) + +/**@}*/ + +#ifdef __cplusplus +} +#endif /** __cplusplus */ +#endif /* LCB_MCREQ_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/mcserver/mcserver.cc b/couchbase-sys/libcouchbase-2.7.0/src/mcserver/mcserver.cc new file mode 100644 index 00000000..b7ab393c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/mcserver/mcserver.cc @@ -0,0 +1,730 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2011-2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" +#include "logging.h" +#include "vbucket/aliases.h" +#include "settings.h" +#include "negotiate.h" +#include "bucketconfig/clconfig.h" +#include "mc/mcreq-flush-inl.h" +#include +#include "ctx-log-inl.h" + +#define LOGARGS(c, lvl) (c)->settings, "server", LCB_LOG_##lvl, __FILE__, __LINE__ +#define LOGARGS_T(lvl) LOGARGS(this, lvl) + +#define LOGFMT "<%s:%s> (SRV=%p,IX=%d) " + +#define LOGID(server) get_ctx_host(server->connctx), get_ctx_port(server->connctx), (void*)server, server->index +#define LOGID_T() LOGID(this) + +#define MCREQ_MAXIOV 32 +#define LCBCONN_UNWANT(conn, flags) (conn)->want &= ~(flags) + +using namespace lcb; + +static void on_error(lcbio_CTX *ctx, lcb_error_t err); + +static void +on_flush_ready(lcbio_CTX *ctx) +{ + Server *server = Server::get(ctx); + nb_IOV iov[MCREQ_MAXIOV]; + int ready; + + do { + int niov = 0; + unsigned nb; + nb = mcreq_flush_iov_fill(server, iov, MCREQ_MAXIOV, &niov); + if (!nb) { + return; + } + ready = lcbio_ctx_put_ex(ctx, (lcb_IOV *)iov, niov, nb); + } while (ready); + lcbio_ctx_wwant(ctx); +} + +static void +on_flush_done(lcbio_CTX *ctx, unsigned expected, unsigned actual) +{ + Server *server = Server::get(ctx); + lcb_U64 now = 0; + if (server->settings->readj_ts_wait) { + now = gethrtime(); + } + + mcreq_flush_done_ex(server, actual, expected, now); + server->check_closed(); +} + +void +Server::flush() +{ + /** Call into the wwant stuff.. */ + if (!connctx->rdwant) { + lcbio_ctx_rwant(connctx, 24); + } + + lcbio_ctx_wwant(connctx); + lcbio_ctx_schedule(connctx); + + if (!lcbio_timer_armed(io_timer)) { + /** + * XXX: Maybe use get_next_timeout(), although here we can assume + * that a command was just scheduled + */ + lcbio_timer_rearm(io_timer, default_timeout()); + } +} + +LIBCOUCHBASE_API +void +lcb_sched_flush(lcb_t instance) +{ + for (size_t ii = 0; ii < LCBT_NSERVERS(instance); ii++) { + Server *server = LCBT_GET_SERVER(instance, ii); + + if (!server->has_pending()) { + continue; + } + server->flush_start(server); + } +} + +/** + * Invoked when get a NOT_MY_VBUCKET response. If the response contains a JSON + * payload then we refresh the configuration with it. + * + * This function returns 1 if the operation was successfully rescheduled; + * otherwise it returns 0. If it returns 0 then we give the error back to the + * user. + */ +bool +Server::handle_nmv(MemcachedResponse& resinfo, mc_PACKET *oldpkt) +{ + protocol_binary_request_header hdr; + lcb_error_t err = LCB_ERROR; + lcb_U16 vbid; + clconfig_provider *cccp = lcb_confmon_get_provider(instance->confmon, + LCB_CLCONFIG_CCCP); + + mcreq_read_hdr(oldpkt, &hdr); + vbid = ntohs(hdr.request.vbucket); + lcb_log(LOGARGS_T(WARN), LOGFMT "NOT_MY_VBUCKET. Packet=%p (S=%u). VBID=%u", LOGID_T(), (void*)oldpkt, oldpkt->opaque, vbid); + + /* Notify of new map */ + lcb_vbguess_remap(instance, vbid, index); + + if (resinfo.bodylen() && cccp->enabled) { + std::string s(resinfo.body(), resinfo.vallen()); + err = lcb_cccp_update(cccp, curhost->host, s.c_str()); + } + + if (err != LCB_SUCCESS) { + int bs_options; + if (instance->cur_configinfo->origin == LCB_CLCONFIG_CCCP) { + /** + * XXX: Not enough to see if cccp was enabled, since cccp might + * be requested by a user, but would still not actually be active + * for clusters < 2.5 If our current config is from CCCP + * then we can be fairly certain that CCCP is indeed working. + * + * For this reason, we don't use if (cccp->enabled) {...} + */ + bs_options = LCB_BS_REFRESH_THROTTLE; + } else { + bs_options = LCB_BS_REFRESH_ALWAYS; + } + lcb_bootstrap_common(instance, bs_options); + } + + if (!lcb_should_retry(settings, oldpkt, LCB_NOT_MY_VBUCKET)) { + return false; + } + + /** Reschedule the packet again .. */ + mc_PACKET *newpkt = mcreq_renew_packet(oldpkt); + newpkt->flags &= ~MCREQ_STATE_FLAGS; + instance->retryq->nmvadd((mc_EXPACKET*)newpkt); + return true; +} + +/* This function is called within a loop to process a single packet. + * + * If a full packet is available, it will process the packet and return + * PKT_READ_COMPLETE, resulting in the `on_read()` function calling this + * function in a loop. + * + * When a complete packet is not available, PKT_READ_PARTIAL will be returned + * and the `on_read()` loop will exit, scheduling any required pending I/O. + */ +Server::ReadState +Server::try_read(lcbio_CTX *ctx, rdb_IOROPE *ior) +{ + MemcachedResponse mcresp; + mc_PACKET *request; + unsigned pktsize = 24, is_last = 1; + + #define RETURN_NEED_MORE(n) \ + if (has_pending()) { \ + lcbio_ctx_rwant(ctx, n); \ + } \ + return PKT_READ_PARTIAL; \ + + #define DO_ASSIGN_PAYLOAD() \ + rdb_consumed(ior, mcresp.hdrsize()); \ + if (mcresp.bodylen()) { \ + mcresp.payload = rdb_get_consolidated(ior, mcresp.bodylen()); \ + } { + + #define DO_SWALLOW_PAYLOAD() \ + } if (mcresp.bodylen()) { \ + rdb_consumed(ior, mcresp.bodylen()); \ + } + + if (rdb_get_nused(ior) < pktsize) { + RETURN_NEED_MORE(pktsize) + } + + /* copy bytes into the info structure */ + rdb_copyread(ior, mcresp.hdrbytes(), mcresp.hdrsize()); + + pktsize += mcresp.bodylen(); + if (rdb_get_nused(ior) < pktsize) { + RETURN_NEED_MORE(pktsize); + } + + /* Find the packet */ + if (mcresp.opcode() == PROTOCOL_BINARY_CMD_STAT && mcresp.keylen() != 0) { + is_last = 0; + request = mcreq_pipeline_find(this, mcresp.opaque()); + } else { + is_last = 1; + request = mcreq_pipeline_remove(this, mcresp.opaque()); + } + + if (!request) { + lcb_log(LOGARGS_T(WARN), LOGFMT "Found stale packet (OP=0x%x, RC=0x%x, SEQ=%u)", LOGID_T(), mcresp.opcode(), mcresp.status(), mcresp.opaque()); + rdb_consumed(ior, pktsize); + return PKT_READ_COMPLETE; + } + + if (mcresp.status() == PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET) { + /* consume the header */ + DO_ASSIGN_PAYLOAD() + if (!handle_nmv(mcresp, request)) { + mcreq_dispatch_response(this, request, &mcresp, LCB_NOT_MY_VBUCKET); + } + DO_SWALLOW_PAYLOAD() + goto GT_DONE; + } + + /* Figure out if the request is 'ufwd' or not */ + if (!(request->flags & MCREQ_F_UFWD)) { + DO_ASSIGN_PAYLOAD(); + mcresp.bufh = rdb_get_first_segment(ior); + mcreq_dispatch_response(this, request, &mcresp, LCB_SUCCESS); + DO_SWALLOW_PAYLOAD() + + } else { + /* figure out how many buffers we want to use as an upper limit for the + * IOV arrays. Currently we'll keep it simple and ensure the entire + * response is contiguous. */ + lcb_PKTFWDRESP resp = { 0 }; + rdb_ROPESEG *segs; + nb_IOV iov; + + rdb_consolidate(ior, pktsize); + rdb_refread_ex(ior, &iov, &segs, 1, pktsize); + + resp.bufs = &segs; + resp.iovs = (lcb_IOV*)&iov; + resp.nitems = 1; + resp.header = mcresp.hdrbytes(); + instance->callbacks.pktfwd( + instance, MCREQ_PKT_COOKIE(request), LCB_SUCCESS, &resp); + rdb_consumed(ior, pktsize); + } + + GT_DONE: + if (is_last) { + mcreq_packet_handled(this, request); + } + return PKT_READ_COMPLETE; +} + +static void +on_read(lcbio_CTX *ctx, unsigned) +{ + Server *server = Server::get(ctx); + rdb_IOROPE *ior = &ctx->ior; + + if (server->check_closed()) { + return; + } + + Server::ReadState rv; + while ((rv = server->try_read(ctx, ior)) == Server::PKT_READ_COMPLETE); + lcbio_ctx_schedule(ctx); + lcb_maybe_breakout(server->instance); +} + +static void flush_noop(mc_PIPELINE *pipeline) { + (void)pipeline; +} + +static void server_connect(Server *server) { + server->connect(); +} + +bool +Server::maybe_retry_packet(mc_PACKET *pkt, lcb_error_t err) +{ + lcbvb_DISTMODE dist_t = lcbvb_get_distmode(parent->config); + + if (dist_t != LCBVB_DIST_VBUCKET) { + /** memcached bucket */ + return false; + } + if (!lcb_should_retry(settings, pkt, err)) { + return false; + } + + mc_PACKET *newpkt = mcreq_renew_packet(pkt); + newpkt->flags &= ~MCREQ_STATE_FLAGS; + instance->retryq->add((mc_EXPACKET *)newpkt, err); + return true; +} + +static void +fail_callback(mc_PIPELINE *pipeline, mc_PACKET *pkt, lcb_error_t err, void *) { + static_cast(pipeline)->purge_single(pkt, err); +} + +void Server::purge_single(mc_PACKET *pkt, lcb_error_t err) { + if (maybe_retry_packet(pkt, err)) { + return; + } + + if (err == LCB_AUTH_ERROR) { + /* In-situ auth errors are actually dead servers. Let's provide this + * as the actual error code. */ + err = LCB_MAP_CHANGED; + } + + if (err == LCB_ETIMEDOUT) { + lcb_error_t tmperr = lcb::RetryQueue::error_for(pkt); + if (tmperr != LCB_SUCCESS) { + err = tmperr; + } + } + + protocol_binary_request_header hdr; + memcpy(hdr.bytes, SPAN_BUFFER(&pkt->kh_span), sizeof(hdr.bytes)); + MemcachedResponse resp(protocol_binary_command(hdr.request.opcode), + hdr.request.opaque, + PROTOCOL_BINARY_RESPONSE_EINVAL); + + lcb_log(LOGARGS_T(WARN), LOGFMT "Failing command (pkt=%p, opaque=%lu, opcode=0x%x) with error 0x%x", LOGID_T(), (void*)pkt, (unsigned long)pkt->opaque, hdr.request.opcode, err); + int rv = mcreq_dispatch_response(this, pkt, &resp, err); + lcb_assert(rv == 0); +} + +int +Server::purge(lcb_error_t error, hrtime_t thresh, hrtime_t *next, + RefreshPolicy policy) +{ + unsigned affected; + + if (thresh) { + affected = mcreq_pipeline_timeout( + this, error, fail_callback, NULL, thresh, next); + + } else { + mcreq_pipeline_fail(this, error, fail_callback, NULL); + affected = -1; + } + + if (policy == REFRESH_NEVER) { + return affected; + } + + if (affected || policy == REFRESH_ALWAYS) { + lcb_bootstrap_common(instance, + LCB_BS_REFRESH_THROTTLE|LCB_BS_REFRESH_INCRERR); + } + return affected; +} + +static void flush_errdrain(mc_PIPELINE *pipeline) +{ + /* Called when we are draining errors. */ + Server *server = (Server *)pipeline; + if (!lcbio_timer_armed(server->io_timer)) { + lcbio_timer_rearm(server->io_timer, server->default_timeout()); + } +} + +uint32_t +Server::next_timeout() const +{ + hrtime_t now, expiry, diff; + mc_PACKET *pkt = mcreq_first_packet(this); + + if (!pkt) { + return default_timeout(); + } + + now = gethrtime(); + expiry = MCREQ_PKT_RDATA(pkt)->start + LCB_US2NS(default_timeout()); + if (expiry <= now) { + diff = 0; + } else { + diff = expiry - now; + } + + return LCB_NS2US(diff); +} + +static void +timeout_server(void *arg) +{ + reinterpret_cast(arg)->io_timeout(); +} + +void Server::io_timeout() +{ + hrtime_t now = gethrtime(); + hrtime_t min_valid = now - LCB_US2NS(default_timeout()); + + hrtime_t next_ns; + int npurged = purge(LCB_ETIMEDOUT, min_valid, &next_ns, + Server::REFRESH_ONFAILED); + if (npurged) { + lcb_log(LOGARGS_T(ERR), LOGFMT "Server timed out. Some commands have failed", LOGID_T()); + } + + uint32_t next_us = next_timeout(); + lcb_log(LOGARGS_T(DEBUG), LOGFMT "Scheduling next timeout for %u ms", LOGID_T(), next_us / 1000); + lcbio_timer_rearm(io_timer, next_us); + lcb_maybe_breakout(instance); +} + +bool +Server::maybe_reconnect_on_fake_timeout(lcb_error_t err) +{ + if (err != LCB_ETIMEDOUT) { + return false; /* not a timeout */ + } + if (!settings->readj_ts_wait) { + return false; /* normal timeout behavior */ + } + if (!has_pending()) { + return false; /* nothing pending */ + } + + uint32_t next_tmo = next_timeout(); + if (next_tmo < default_timeout() / 2) { + /* Ideally we'd have a fuzz interval to shave off the actual timeout, + * since there will inevitably be some time taken off the next timeout */ + return false; + } + + lcb_log(LOGARGS_T(INFO), LOGFMT "Retrying connection. Assuming timeout because of stalled event loop", LOGID_T()); + connect(); + return true; +} + +static void +on_connected(lcbio_SOCKET *sock, void *data, lcb_error_t err, lcbio_OSERR syserr) +{ + Server *server = reinterpret_cast(data); + server->handle_connected(sock, err, syserr); +} + +static void mcserver_flush(Server *s) { s->flush(); } + +void +Server::handle_connected(lcbio_SOCKET *sock, lcb_error_t err, lcbio_OSERR syserr) +{ + LCBIO_CONNREQ_CLEAR(&connreq); + + if (err != LCB_SUCCESS) { + lcb_log(LOGARGS_T(ERR), LOGFMT "Got error for connection! (OS=%d)", LOGID_T(), syserr); + if (!maybe_reconnect_on_fake_timeout(err)) { + socket_failed(err); + } + return; + } + + lcb_assert(sock); + + /** Do we need sasl? */ + SessionInfo* sessinfo = SessionInfo::get(sock); + if (sessinfo == NULL) { + lcb_log(LOGARGS_T(TRACE), "<%s:%s> (SRV=%p) Session not yet negotiated. Negotiating", curhost->host, curhost->port, (void*)this); + SessionRequest *sreq = SessionRequest::start( + sock, settings, default_timeout(), on_connected, this); + LCBIO_CONNREQ_MKGENERIC(&connreq, sreq, lcb::sessreq_cancel); + return; + } else { + compsupport = sessinfo->has_feature(PROTOCOL_BINARY_FEATURE_DATATYPE); + mutation_tokens = sessinfo->has_feature(PROTOCOL_BINARY_FEATURE_MUTATION_SEQNO); + } + + lcbio_CTXPROCS procs; + procs.cb_err = on_error; + procs.cb_read = on_read; + procs.cb_flush_done = on_flush_done; + procs.cb_flush_ready = on_flush_ready; + connctx = lcbio_ctx_new(sock, this, &procs); + connctx->subsys = "memcached"; + mc_PIPELINE::flush_start = (mcreq_flushstart_fn)mcserver_flush; + + uint32_t tmo = next_timeout(); + lcb_log(LOGARGS_T(DEBUG), LOGFMT "Setting initial timeout=%ums", LOGID_T(), tmo/1000); + lcbio_timer_rearm(io_timer, tmo); + flush(); +} + +void +Server::connect() +{ + lcbio_pMGRREQ mr = lcbio_mgr_get(instance->memd_sockpool, curhost, + default_timeout(), on_connected, this); + LCBIO_CONNREQ_MKPOOLED(&connreq, mr); + mc_PIPELINE::flush_start = flush_noop; + state = Server::S_CLEAN; +} + +static void +buf_done_cb(mc_PIPELINE *pl, const void *cookie, void *, void *) +{ + Server *server = static_cast(pl); + server->instance->callbacks.pktflushed(server->instance, cookie); +} + +Server::Server(lcb_t instance_, int ix) + : state(S_CLEAN), + io_timer(lcbio_timer_new(instance_->iotable, this, timeout_server)), + instance(instance_), + settings(lcb_settings_ref2(instance_->settings)), + compsupport(0), + mutation_tokens(0), + connctx(NULL), + curhost(new lcb_host_t()) +{ + std::memset(static_cast(this), 0, sizeof(mc_PIPELINE)); + mcreq_pipeline_init(this); + mc_PIPELINE::flush_start = (mcreq_flushstart_fn)server_connect; + mc_PIPELINE::buf_done_callback = buf_done_cb; + mc_PIPELINE::index = ix; + + std::memset(&connreq, 0, sizeof connreq); + std::memset(curhost, 0, sizeof *curhost); + + const char *datahost = lcbvb_get_hostport( + LCBT_VBCONFIG(instance), ix, + LCBVB_SVCTYPE_DATA, settings->sslopts & LCB_SSL_ENABLED ? + LCBVB_SVCMODE_SSL : LCBVB_SVCMODE_PLAIN); + if (datahost) { + lcb_host_parsez(curhost, datahost, LCB_CONFIG_MCD_PORT); + } +} + +Server::Server() + : state(S_TEMPORARY), + io_timer(NULL), instance(NULL), settings(NULL), compsupport(0), + mutation_tokens(0), connctx(NULL), curhost(NULL) +{ +} + +Server::~Server() { + if (state == S_TEMPORARY) { + return; + } + + mcreq_pipeline_cleanup(this); + + if (io_timer) { + lcbio_timer_destroy(io_timer); + } + + delete curhost; + lcb_settings_unref(settings); +} + +static void +close_cb(lcbio_SOCKET *sock, int, void *) +{ + lcbio_ref(sock); + lcbio_mgr_discard(sock); +} + +static void +on_error(lcbio_CTX *ctx, lcb_error_t err) +{ + Server *server = Server::get(ctx); + lcb_log(LOGARGS(server, WARN), LOGFMT "Got socket error 0x%x", LOGID(server), err); + if (server->check_closed()) { + return; + } + server->socket_failed(err); +} + +/**Handle a socket error. This function will close the current connection + * and trigger a failout of any pending commands. + * This function triggers a configuration refresh */ +void +Server::socket_failed(lcb_error_t err) +{ + if (check_closed()) { + return; + } + + purge(err, 0, NULL, REFRESH_ALWAYS); + lcb_maybe_breakout(instance); + start_errored_ctx(S_ERRDRAIN); +} + +void +Server::close() +{ + /* Should never be called twice */ + lcb_assert(state != Server::S_CLOSED); + start_errored_ctx(S_CLOSED); +} + +/** + * Call to signal an error or similar on the current socket. + * @param server The server + * @param next_state The next state (S_CLOSED or S_ERRDRAIN) + */ +void +Server::start_errored_ctx(State next_state) +{ + lcbio_CTX *ctx = connctx; + + state = next_state; + /* Cancel any pending connection attempt? */ + lcbio_connreq_cancel(&connreq); + + /* If the server is being destroyed, silence the timer */ + if (next_state == Server::S_CLOSED && io_timer != NULL) { + lcbio_timer_destroy(io_timer); + io_timer = NULL; + } + + if (ctx == NULL) { + if (next_state == Server::S_CLOSED) { + delete this; + return; + } else { + /* Not closed but don't have a current context */ + mc_PIPELINE::flush_start = (mcreq_flushstart_fn)server_connect; + if (has_pending()) { + if (!lcbio_timer_armed(io_timer)) { + /* TODO: Maybe throttle reconnection attempts? */ + lcbio_timer_rearm(io_timer, default_timeout()); + } + connect(); + } + } + + } else { + if (ctx->npending) { + /* Have pending items? */ + + /* Flush any remaining events */ + lcbio_ctx_schedule(ctx); + + /* Close the socket not to leak resources */ + lcbio_shutdown(lcbio_ctx_sock(ctx)); + if (next_state == Server::S_ERRDRAIN) { + mc_PIPELINE::flush_start = (mcreq_flushstart_fn)flush_errdrain; + } + } else { + finalize_errored_ctx(); + } + } +} + +/** + * This function actually finalizes a ctx which has an error on it. If the + * ctx has pending operations remaining then this function returns immediately. + * Otherwise this will either reinitialize the connection or free the server + * object depending on the actual object state (i.e. if it was closed or + * simply errored). + */ +void +Server::finalize_errored_ctx() +{ + if (connctx->npending) { + return; + } + + lcb_log(LOGARGS_T(DEBUG), LOGFMT "Finalizing ctx %p", LOGID_T(), (void*)connctx); + + /* Always close the existing context. */ + lcbio_ctx_close(connctx, close_cb, NULL); + connctx = NULL; + + /**Marks any unflushed data inside this server as being already flushed. This + * should be done within error handling. If subsequent data is flushed on this + * pipeline to the same connection, the results are undefined. */ + + unsigned toflush; + nb_IOV iov; + while ((toflush = mcreq_flush_iov_fill(this, &iov, 1, NULL))) { + mcreq_flush_done(this, toflush, toflush); + } + + if (state == Server::S_CLOSED) { + /* If the server is closed, time to free it */ + delete this; + } else { + /* Otherwise, cycle the state back to CLEAN and reinit + * the connection */ + state = Server::S_CLEAN; + mc_PIPELINE::flush_start = (mcreq_flushstart_fn)server_connect; + connect(); + } +} + +/** + * This little function checks to see if the server struct is still valid, or + * whether it should just be cleaned once no pending I/O remainds. + * + * If this function returns false then the server is still valid; otherwise it + * is invalid and must not be used further. + */ +bool +Server::check_closed() +{ + if (state == Server::S_CLEAN) { + return false; + } + lcb_log(LOGARGS_T(INFO), LOGFMT "Got handler after close. Checking pending calls (pending=%u)", LOGID_T(), connctx->npending); + finalize_errored_ctx(); + return 1; +} + +int +mcserver_supports_compression(mc_SERVER *server) { + return server->compsupport; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/mcserver/mcserver.h b/couchbase-sys/libcouchbase-2.7.0/src/mcserver/mcserver.h new file mode 100644 index 00000000..3d77bbf0 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/mcserver/mcserver.h @@ -0,0 +1,218 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_MCSERVER_H +#define LCB_MCSERVER_H +#include +#include +#include +#include +#include + +#ifdef __cplusplus +struct lcb_settings_st; +struct lcb_server_st; + +namespace lcb { + +class RetryQueue; +struct RetryOp; + +/** + * The structure representing each couchbase server + */ +class Server : public mc_PIPELINE { +public: + /** + * Allocate and initialize a new server object. The object will not be + * connected + * @param instance the instance to which the server belongs + * @param ix the server index in the configuration + */ + Server(lcb_t, int); + + /** + * Close the server. The resources of the server may still continue to persist + * internally for a bit until all callbacks have been delivered and all buffers + * flushed and/or failed. + */ + void close(); + + /** + * Schedule a flush and potentially flush some immediate data on the server. + * This is safe to call multiple times, however performance considerations + * should be taken into account + */ + void flush(); + + /** + * Wrapper around mcreq_pipeline_timeout() and/or mcreq_pipeline_fail(). This + * function will purge all pending requests within the server and invoke + * their callbacks with the given error code passed as `err`. Depending on + * the error code, some operations may be retried. + * + * @param err the error code by which to fail the commands + * + * @note This function does not modify the server's socket or state in itself, + * but rather simply wipes the commands from its queue + */ + void purge(lcb_error_t err) { + purge(err, 0, NULL, Server::REFRESH_NEVER); + } + + /** Callback for mc_pipeline_fail_chain */ + inline void purge_single(mc_PACKET*, lcb_error_t); + + /** + * Returns true or false depending on whether there are pending commands on + * this server + */ + bool has_pending() const { + return !SLLIST_IS_EMPTY(&requests); + } + + int get_index() const { + return mc_PIPELINE::index; + } + + lcb_t get_instance() const { + return instance; + } + + const lcb_settings* get_settings() const { + return settings; + } + + void set_new_index(int new_index) { + mc_PIPELINE::index = new_index; + } + + const lcb_host_t& get_host() const { + return *curhost; + } + + bool supports_mutation_tokens() const { + return mutation_tokens; + } + + bool is_connected() const { + return connctx != NULL; + } + + /** "Temporary" constructor. Only for use in retry queue */ + Server(); + ~Server(); + + enum State { + /* There are no known errored commands on this server */ + S_CLEAN, + + /* In the process of draining remaining commands to be flushed. The commands + * being drained may have already been rescheduled to another server or + * placed inside the error queue, but are pending being flushed. This will + * only happen in completion-style I/O plugins. When this state is in effect, + * subsequent attempts to connect will be blocked until all commands have + * been properly drained. + */ + S_ERRDRAIN, + + /* The server object has been closed, either because it has been removed + * from the cluster or because the related lcb_t has been destroyed. + */ + S_CLOSED, + + /* + * Server has been temporarily constructed. + */ + S_TEMPORARY + }; + + static Server* get(lcbio_CTX *ctx) { + return reinterpret_cast(lcbio_ctx_data(ctx)); + } + + uint32_t default_timeout() const { + return settings->operation_timeout; + } + + uint32_t next_timeout() const; + + bool check_closed(); + void start_errored_ctx(State next_state); + void finalize_errored_ctx(); + void socket_failed(lcb_error_t); + void io_timeout(); + + enum RefreshPolicy { + REFRESH_ALWAYS, + REFRESH_ONFAILED, + REFRESH_NEVER + }; + + int purge(lcb_error_t error, hrtime_t thresh, hrtime_t *next, + RefreshPolicy policy); + + void connect(); + + void handle_connected(lcbio_SOCKET *socket, lcb_error_t err, lcbio_OSERR syserr); + + enum ReadState { + PKT_READ_COMPLETE, + PKT_READ_PARTIAL + }; + + ReadState try_read(lcbio_CTX *ctx, rdb_IOROPE *ior); + bool handle_nmv(MemcachedResponse& resinfo, mc_PACKET *oldpkt); + bool maybe_retry_packet(mc_PACKET *pkt, lcb_error_t err); + bool maybe_reconnect_on_fake_timeout(lcb_error_t received_error); + + /** Disable */ + Server(const Server&); + + State state; + + /** IO/Operation timer */ + lcbio_pTIMER io_timer; + + /** Pointer back to the instance */ + lcb_t instance; + + lcb_settings *settings; + + /** Whether compression is supported */ + short compsupport; + + /** Whether extended 'UUID' and 'seqno' are available for each mutation */ + short mutation_tokens; + + lcbio_CTX *connctx; + lcbio_CONNREQ connreq; + + /** Request for current connection */ + lcb_host_t *curhost; +}; +} + +typedef lcb::Server mc_SERVER; +extern "C" {int mcserver_supports_compression(mc_SERVER*);} + +#else +/* C only */ +typedef struct mc_SERVER mc_SERVER; +int mcserver_supports_compression(mc_SERVER *server); +#endif /* __cplusplus */ +#endif /* LCB_MCSERVER_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/mcserver/negotiate.cc b/couchbase-sys/libcouchbase-2.7.0/src/mcserver/negotiate.cc new file mode 100644 index 00000000..ef4e5c66 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/mcserver/negotiate.cc @@ -0,0 +1,582 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "packetutils.h" +#include "mcserver.h" +#include "logging.h" +#include "settings.h" +#include +#include +#include +#include +#include "negotiate.h" +#include "ctx-log-inl.h" + +using namespace lcb; + +#define LOGARGS(ctx, lvl) ctx->settings, "negotiation", LCB_LOG_##lvl, __FILE__, __LINE__ +static void cleanup_negotiated(SessionInfo* info); +static void handle_ioerr(lcbio_CTX *ctx, lcb_error_t err); +#define SESSREQ_LOGFMT "<%s:%s> (SASLREQ=%p) " + +static void timeout_handler(void *arg); + +#define SESSREQ_LOGID(s) get_ctx_host(s->ctx), get_ctx_port(s->ctx), (void*)s + +static void +close_cb(lcbio_SOCKET *s, int reusable, void *arg) +{ + *(lcbio_SOCKET **)arg = s; + lcbio_ref(s); + lcb_assert(reusable); +} + +/** + * Structure used only for initialization. This is only used for the duration + * of the request for negotiation and is deleted once negotiation has + * completed (or failed). + */ +class lcb::SessionRequestImpl : public SessionRequest { +public: + static SessionRequestImpl *get(void *arg) { + return reinterpret_cast(arg); + } + + bool setup(const lcbio_NAMEINFO& nistrs, const lcb_host_t& host, + const lcb::Authenticator& auth); + void start(lcbio_SOCKET *sock); + bool send_hello(); + bool send_step(const lcb::MemcachedResponse& packet); + bool read_hello(const lcb::MemcachedResponse& packet); + void send_auth(const char *sasl_data, unsigned ndata); + void handle_read(lcbio_CTX *ioctx); + + enum MechStatus { MECH_UNAVAILABLE, MECH_NOT_NEEDED, MECH_OK }; + MechStatus set_chosen_mech(std::string& mechlist, const char **data, unsigned int *ndata); + + SessionRequestImpl(lcbio_CONNDONE_cb callback, void *data, uint32_t timeout, lcbio_TABLE *iot, lcb_settings* settings_) + : ctx(NULL), cb(callback), cbdata(data), + timer(lcbio_timer_new(iot, this, timeout_handler)), + last_err(LCB_SUCCESS), info(NULL), + settings(settings_) { + + if (timeout) { + lcbio_timer_rearm(timer, timeout); + } + memset(&u_auth, 0, sizeof(u_auth)); + } + + virtual ~SessionRequestImpl(); + + void cancel() { + cb = NULL; + delete this; + } + + void fail() { + if (cb != NULL) { + cb(NULL, cbdata, last_err, 0); + cb = NULL; + } + delete this; + } + + void fail(lcb_error_t error, const char *msg) { + set_error(error, msg); + fail(); + } + + void success() { + /** Dislodge the connection, and return it back to the caller */ + lcbio_SOCKET *s; + + lcbio_ctx_close(ctx, close_cb, &s); + ctx = NULL; + + lcbio_protoctx_add(s, info); + info = NULL; + + /** Invoke the callback, marking it a success */ + cb(s, cbdata, LCB_SUCCESS, 0); + lcbio_unref(s); + + delete this; + } + + void set_error(lcb_error_t error, const char *msg = "") { + lcb_log(LOGARGS(this, ERR), SESSREQ_LOGFMT "Error: 0x%x, %s", SESSREQ_LOGID(this), error, msg); + if (last_err == LCB_SUCCESS) { + last_err = error; + } + } + + bool has_error() const { + return last_err != LCB_SUCCESS; + } + + union { + cbsasl_secret_t secret; + char buffer[256]; + } u_auth; + + lcbio_CTX *ctx; + lcbio_CONNDONE_cb cb; + void *cbdata; + lcbio_pTIMER timer; + lcb_error_t last_err; + cbsasl_conn_t *sasl_client; + SessionInfo* info; + lcb_settings *settings; +}; + +static void handle_read(lcbio_CTX *ioctx, unsigned) { + SessionRequestImpl::get(lcbio_ctx_data(ioctx))->handle_read(ioctx); +} + +static int +sasl_get_username(void *context, int id, const char **result, unsigned int *len) +{ + SessionRequestImpl *ctx = SessionRequestImpl::get(context); + const char *u = NULL, *p = NULL; + if (!context || !result || (id != CBSASL_CB_USER && id != CBSASL_CB_AUTHNAME)) { + return SASL_BADPARAM; + } + + lcbauth_get_upass(ctx->settings->auth, &u, &p); + *result = u; + if (len) { + *len = (unsigned int)strlen(*result); + } + + return SASL_OK; +} + +static int +sasl_get_password(cbsasl_conn_t *conn, void *context, int id, + cbsasl_secret_t **psecret) +{ + SessionRequestImpl *ctx = SessionRequestImpl::get(context); + if (!conn || ! psecret || id != CBSASL_CB_PASS || ctx == NULL) { + return SASL_BADPARAM; + } + + *psecret = &ctx->u_auth.secret; + return SASL_OK; +} + +SessionInfo::SessionInfo() +{ + lcbio_PROTOCTX::id = LCBIO_PROTOCTX_SESSINFO; + lcbio_PROTOCTX::dtor = (void (*)(lcbio_PROTOCTX *))cleanup_negotiated; +} + +bool +SessionRequestImpl::setup(const lcbio_NAMEINFO& nistrs, const lcb_host_t& host, + const lcb::Authenticator& auth) +{ + cbsasl_callback_t sasl_callbacks[4]; + sasl_callbacks[0].id = CBSASL_CB_USER; + sasl_callbacks[0].proc = (int( *)(void)) &sasl_get_username; + + sasl_callbacks[1].id = CBSASL_CB_AUTHNAME; + sasl_callbacks[1].proc = (int( *)(void)) &sasl_get_username; + + sasl_callbacks[2].id = CBSASL_CB_PASS; + sasl_callbacks[2].proc = (int( *)(void)) &sasl_get_password; + + sasl_callbacks[3].id = CBSASL_CB_LIST_END; + sasl_callbacks[3].proc = NULL; + sasl_callbacks[3].context = NULL; + + for (size_t ii = 0; ii < 3; ii++) { + sasl_callbacks[ii].context = this; + } + + const char *pass = NULL, *user = NULL; + lcbauth_get_upass(&auth, &user, &pass); + + if (pass) { + unsigned long pwlen = (unsigned long)strlen(pass); + size_t maxlen = sizeof(u_auth.buffer) - offsetof(cbsasl_secret_t, data); + u_auth.secret.len = pwlen; + + if (pwlen < maxlen) { + memcpy(u_auth.secret.data, pass, pwlen); + } else { + return false; + } + } + + + cbsasl_error_t saslerr = cbsasl_client_new( + "couchbase", host.host, nistrs.local, nistrs.remote, + sasl_callbacks, 0, &sasl_client); + return saslerr == SASL_OK; +} + +static void +timeout_handler(void *arg) +{ + SessionRequestImpl *sreq = SessionRequestImpl::get(arg); + sreq->fail(LCB_ETIMEDOUT, "Negotiation timed out"); +} + +/** + * Called to retrive the mechlist from the packet. + * @return 0 to continue authentication, 1 if no authentication needed, or + * -1 on error. + */ +SessionRequestImpl::MechStatus +SessionRequestImpl::set_chosen_mech(std::string& mechlist, + const char **data, unsigned int *ndata) +{ + cbsasl_error_t saslerr; + if (settings->sasl_mech_force) { + char *forcemech = settings->sasl_mech_force; + if (mechlist.find(forcemech) == std::string::npos) { + /** Requested mechanism not found */ + set_error(LCB_SASLMECH_UNAVAILABLE, mechlist.c_str()); + return MECH_UNAVAILABLE; + } + mechlist.assign(forcemech); + } + + const char *chosenmech; + saslerr = cbsasl_client_start(sasl_client, mechlist.c_str(), + NULL, data, ndata, &chosenmech); + switch (saslerr) { + case SASL_OK: + info->mech.assign(chosenmech); + return MECH_OK; + case SASL_NOMECH: + lcb_log(LOGARGS(this, INFO), SESSREQ_LOGFMT "Server does not support SASL (no mechanisms supported)", SESSREQ_LOGID(this)); + return MECH_NOT_NEEDED; + break; + default: + lcb_log(LOGARGS(this, INFO), SESSREQ_LOGFMT "cbsasl_client_start returned %d", SESSREQ_LOGID(this), saslerr); + set_error(LCB_EINTERNAL, "Couldn't start SASL client"); + return MECH_UNAVAILABLE; + } +} + +/** + * Given the specific mechanisms, send the auth packet to the server. + */ +void +SessionRequestImpl::send_auth(const char *sasl_data, unsigned ndata) +{ + lcb::MemcachedRequest hdr(PROTOCOL_BINARY_CMD_SASL_AUTH); + hdr.sizes(0, info->mech.size(), ndata); + + lcbio_ctx_put(ctx, hdr.data(), hdr.size()); + lcbio_ctx_put(ctx, info->mech.c_str(), info->mech.size()); + lcbio_ctx_put(ctx, sasl_data, ndata); + lcbio_ctx_rwant(ctx, 24); +} + +bool +SessionRequestImpl::send_step(const lcb::MemcachedResponse& packet) +{ + cbsasl_error_t saslerr; + const char *step_data; + unsigned int ndata; + + saslerr = cbsasl_client_step(sasl_client, + packet.body(), packet.bodylen(), NULL, &step_data, &ndata); + + if (saslerr != SASL_CONTINUE) { + set_error(LCB_EINTERNAL, "Unable to perform SASL STEP"); + return false; + } + + lcb::MemcachedRequest hdr(PROTOCOL_BINARY_CMD_SASL_STEP); + hdr.sizes(0, info->mech.size(), ndata); + lcbio_ctx_put(ctx, hdr.data(), hdr.size()); + lcbio_ctx_put(ctx, info->mech.c_str(), info->mech.size()); + lcbio_ctx_put(ctx, step_data, ndata); + lcbio_ctx_rwant(ctx, 24); + return true; +} + +#define LCB_HELLO_DEFL_STRING "libcouchbase/" LCB_VERSION_STRING +#define LCB_HELLO_DEFL_LENGTH (sizeof(LCB_HELLO_DEFL_STRING)-1) + +bool +SessionRequestImpl::send_hello() +{ + lcb_U16 features[MEMCACHED_TOTAL_HELLO_FEATURES]; + + unsigned nfeatures = 0; + features[nfeatures++] = PROTOCOL_BINARY_FEATURE_TLS; + if (settings->tcp_nodelay) { + features[nfeatures++] = PROTOCOL_BINARY_FEATURE_TCPNODELAY; + } + +#ifndef LCB_NO_SNAPPY + if (settings->compressopts != LCB_COMPRESS_NONE) { + features[nfeatures++] = PROTOCOL_BINARY_FEATURE_DATATYPE; + } +#endif + + if (settings->fetch_mutation_tokens) { + features[nfeatures++] = PROTOCOL_BINARY_FEATURE_MUTATION_SEQNO; + } + + std::string client_string; + const char *clistr = LCB_HELLO_DEFL_STRING; + size_t nclistr = LCB_HELLO_DEFL_LENGTH; + + if (settings->client_string) { + client_string.assign(LCB_HELLO_DEFL_STRING); + client_string += ", "; + client_string += settings->client_string; + + clistr = client_string.c_str(); + nclistr = client_string.size(); + } + + lcb::MemcachedRequest hdr(PROTOCOL_BINARY_CMD_HELLO); + hdr.sizes(0, nclistr, (sizeof features[0]) * nfeatures); + + lcbio_ctx_put(ctx, hdr.data(), hdr.size()); + lcbio_ctx_put(ctx, clistr, nclistr); + for (size_t ii = 0; ii < nfeatures; ii++) { + lcb_U16 tmp = htons(features[ii]); + lcbio_ctx_put(ctx, &tmp, sizeof tmp); + } + lcbio_ctx_rwant(ctx, 24); + return true; +} + +bool +SessionRequestImpl::read_hello(const lcb::MemcachedResponse& resp) +{ + /* some caps */ + const char *cur; + const char *payload = resp.body(); + const char *limit = payload + resp.bodylen(); + for (cur = payload; cur < limit; cur += 2) { + lcb_U16 tmp; + memcpy(&tmp, cur, sizeof(tmp)); + tmp = ntohs(tmp); + lcb_log(LOGARGS(this, DEBUG), SESSREQ_LOGFMT "Found feature 0x%x (%s)", SESSREQ_LOGID(this), tmp, protocol_feature_2_text(tmp)); + info->server_features.push_back(tmp); + } + return true; +} + +typedef enum { + SREQ_S_WAIT, + SREQ_S_AUTHDONE, + SREQ_S_HELLODONE, + SREQ_S_ERROR +} sreq_STATE; + +/** + * It's assumed the server buffers will be reset upon close(), so we must make + * sure to _not_ release the ringbuffer if that happens. + */ +void +SessionRequestImpl::handle_read(lcbio_CTX *ioctx) +{ + lcb::MemcachedResponse resp; + unsigned required; + sreq_STATE state = SREQ_S_WAIT; + + GT_NEXT_PACKET: + + if (!resp.load(ioctx, &required)) { + LCBIO_CTX_RSCHEDULE(ioctx, required); + return; + } + const uint16_t status = resp.status(); + + switch (resp.opcode()) { + case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS: { + const char *mechlist_data; + unsigned int nmechlist_data; + std::string mechs(resp.body(), resp.bodylen()); + + MechStatus mechrc = set_chosen_mech(mechs, &mechlist_data, &nmechlist_data); + if (mechrc == MECH_OK) { + send_auth(mechlist_data, nmechlist_data); + state = SREQ_S_WAIT; + } else if (mechrc == MECH_UNAVAILABLE) { + state = SREQ_S_ERROR; + } else { + state = SREQ_S_HELLODONE; + } + break; + } + + case PROTOCOL_BINARY_CMD_SASL_AUTH: { + if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) { + send_hello(); + state = SREQ_S_AUTHDONE; + break; + } + + if (status != PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE) { + set_error(LCB_AUTH_ERROR, "SASL AUTH failed"); + state = SREQ_S_ERROR; + break; + } + if (send_step(resp) && send_hello()) { + state = SREQ_S_WAIT; + } else { + state = SREQ_S_ERROR; + } + break; + } + + case PROTOCOL_BINARY_CMD_SASL_STEP: { + if (status != PROTOCOL_BINARY_RESPONSE_SUCCESS) { + lcb_log(LOGARGS(this, WARN), SESSREQ_LOGFMT "SASL auth failed with STATUS=0x%x", SESSREQ_LOGID(this), status); + set_error(LCB_AUTH_ERROR, "SASL Step Failed"); + state = SREQ_S_ERROR; + } else { + /* Wait for pipelined HELLO response */ + state = SREQ_S_AUTHDONE; + } + break; + } + + case PROTOCOL_BINARY_CMD_HELLO: { + state = SREQ_S_HELLODONE; + if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) { + if (!read_hello(resp)) { + set_error(LCB_PROTOCOL_ERROR, "Couldn't parse HELLO"); + } + } else if (status == PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND || + status == PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED) { + lcb_log(LOGARGS(this, DEBUG), SESSREQ_LOGFMT "Server does not support HELLO", SESSREQ_LOGID(this)); + /* nothing */ + } else { + set_error(LCB_PROTOCOL_ERROR, "Hello response unexpected"); + state = SREQ_S_ERROR; + } + break; + } + + default: { + state = SREQ_S_ERROR; + lcb_log(LOGARGS(this, ERROR), SESSREQ_LOGFMT "Received unknown response. OP=0x%x. RC=0x%x", SESSREQ_LOGID(this), resp.opcode(), resp.status()); + set_error(LCB_NOT_SUPPORTED, "Received unknown response"); + break; + } + } + + // We need to release the packet's buffers before actually destroying the + // underlying socket and/or buffers! + resp.release(ioctx); + + // Once there is no more any dependencies on the buffers, we can succeed + // or fail the request, potentially destroying the underlying connection + if (has_error()) { + fail(); + } else if (state == SREQ_S_ERROR) { + fail(LCB_ERROR, "FIXME: Error code set without description"); + } else if (state == SREQ_S_HELLODONE) { + success(); + } else { + goto GT_NEXT_PACKET; + } +} + +static void +handle_ioerr(lcbio_CTX *ctx, lcb_error_t err) +{ + SessionRequestImpl* sreq = SessionRequestImpl::get(lcbio_ctx_data(ctx)); + sreq->fail(err, "IO Error"); +} + +static void cleanup_negotiated(SessionInfo* ctx) { + delete ctx; +} + +void +SessionRequestImpl::start(lcbio_SOCKET *sock) { + info = new SessionInfo(); + + lcb_error_t err = lcbio_sslify_if_needed(sock, settings); + if (err != LCB_SUCCESS) { + set_error(err, "Couldn't initialized SSL on socket"); + lcbio_async_signal(timer); + return; + } + + lcbio_CTXPROCS procs; + procs.cb_err = ::handle_ioerr; + procs.cb_read = ::handle_read; + ctx = lcbio_ctx_new(sock, this, &procs); + ctx->subsys = "sasl"; + + const lcb_host_t *curhost = lcbio_get_host(sock); + lcbio_NAMEINFO nistrs; + lcbio_get_nameinfo(sock, &nistrs); + + if (!setup(nistrs, *curhost, *settings->auth)) { + set_error(LCB_EINTERNAL, "Couldn't start SASL client"); + lcbio_async_signal(timer); + return; + } + + lcb::MemcachedRequest hdr(PROTOCOL_BINARY_CMD_SASL_LIST_MECHS); + lcbio_ctx_put(ctx, hdr.data(), hdr.size()); + LCBIO_CTX_RSCHEDULE(ctx, 24); +} + +SessionRequestImpl::~SessionRequestImpl() +{ + if (info) { + delete info; + } + if (timer) { + lcbio_timer_destroy(timer); + } + if (ctx) { + lcbio_ctx_close(ctx, NULL, NULL); + } + if (sasl_client) { + cbsasl_dispose(&sasl_client); + } +} + +void lcb::sessreq_cancel(SessionRequest *sreq) { + sreq->cancel(); +} + +SessionRequest * +SessionRequest::start(lcbio_SOCKET *sock, lcb_settings_st *settings, + uint32_t tmo, lcbio_CONNDONE_cb callback, void *data) +{ + SessionRequestImpl* sreq = new SessionRequestImpl(callback, data, tmo, sock->io, settings); + sreq->start(sock); + return sreq; +} + +SessionInfo* +SessionInfo::get(lcbio_SOCKET *sock) { + return static_cast(lcbio_protoctx_get(sock, LCBIO_PROTOCTX_SESSINFO)); +} + +bool +SessionInfo::has_feature(uint16_t feature) const { + return std::find(server_features.begin(), server_features.end(), feature) + != server_features.end(); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/mcserver/negotiate.h b/couchbase-sys/libcouchbase-2.7.0/src/mcserver/negotiate.h new file mode 100644 index 00000000..52b5af9b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/mcserver/negotiate.h @@ -0,0 +1,130 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_MCSERVER_NEGOTIATE_H +#define LCB_MCSERVER_NEGOTIATE_H +#include +#include +#include +#include + +/** + * @file + * @brief SASL Negotiation routines + * + * @defgroup lcb-sasl Server/SASL Negotiation + * @details + * This module contains routines to initialize a server and authenticate it + * against a cluster. In the future this will also be used to handle things + * such as TLS negotiation and the HELLO command + * @addtogroup lcb-sasl + * @{ + */ + +struct lcb_settings_st; + +namespace lcb { +class SessionRequest { +public: + /** + * @brief Start negotiation on a connected socket + * + * This will start negotiation on the socket. Once complete (or an error has + * taken place) the `callback` will be invoked with the result. + * + * @param sock A connected socket to use. Its reference count will be increased + * @param settings A settings structure. Used for auth information as well as + * logging + * @param tmo Time in microseconds to wait until the negotiation is done + * @param callback A callback to invoke when a result has been received + * @param data User-defined pointer passed to the callback + * @return A new handle which may be cancelled via mc_sessreq_cancel(). As with + * other cancellable requests, once this handle is cancelled a callback will + * not be received for it, and once the callback is received the handle may not + * be cancelled as it will point to invalid memory. + * + * Once the socket has been negotiated successfuly, you may then use the + * mc_sess_get() function to query the socket about various negotiation aspects + * + * @code{.c} + * lcbio_CONNREQ creq; + * SessionRequest *req = SessionRequest::start(sock, settings, tmo, callback, data); + * LCBIO_CONNREQ_MKGENERIC(req, sessreq_cancel); + * @endcode + * + * @see lcb::sessreq_cancel() + * @see LCBIO_CONNREQ_MKGENERIC + */ + static SessionRequest *start(lcbio_SOCKET *sock, lcb_settings_st *settings, + uint32_t tmo, lcbio_CONNDONE_cb callback, + void *data); + + /** + * @brief Cancel a pending SASL negotiation request + * @param handle The handle to cancel + */ + virtual void cancel() = 0; + virtual ~SessionRequest(){} +}; +class SessionRequestImpl; + +extern "C" { void sessreq_cancel(SessionRequest *); } + +class SessionInfo : public lcbio_PROTOCTX { +public: + /** + * @brief Get an opaque handle representing the negotiated state of the socket + * @param sock The negotiated socket + * @return the `SASLINFO` structure if the socket is negotiated, or `NULL` if + * the socket has not been negotiated. + * + * @see get_mech() + */ + static SessionInfo* get(lcbio_SOCKET*); + + /** + * @brief Get the mechanism employed for authentication + * @param info pointer retrieved via mc_sasl_get() + * @return A string indicating the mechanism used. This may be `PLAIN` or + * `CRAM-MD5`. + */ + const std::string& get_mech() const { + return mech; + } + + /** + * @brief Determine if a specific protocol feature is supported on the server + * @param info info pointer returned via mc_sasl_get() + * @param feature A feature ID + * @return true if supported, false otherwise + */ + bool has_feature(uint16_t feature) const; + +private: + SessionInfo(); + friend class lcb::SessionRequestImpl; + + std::string mech; + std::vector server_features; +}; + + +} // namespace + +/**@}*/ + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/n1ql/ixmgmt.cc b/couchbase-sys/libcouchbase-2.7.0/src/n1ql/ixmgmt.cc new file mode 100644 index 00000000..1b0348f2 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/n1ql/ixmgmt.cc @@ -0,0 +1,860 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2016 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define NOMINMAX +#include +#include +#include +#include + +#include "contrib/lcb-jsoncpp/lcb-jsoncpp.h" +#include "lcbio/lcbio.h" +#include "lcbio/timer-ng.h" +#include "settings.h" +#include "internal.h" + +#define LOGFMT "(mgreq=%p) " +#define LOGID(req) static_cast(req) +#define LOGARGS(req, lvl) (req)->m_instance->settings, "ixmgmt", LCB_LOG_##lvl, __FILE__, __LINE__ + +using std::vector; +using std::string; + +static const char * +ixtype_2_str(unsigned ixtype) +{ + if (ixtype == LCB_N1XSPEC_T_GSI) { + return "gsi"; + } else if (ixtype == LCB_N1XSPEC_T_VIEW) { + return "view"; + } else { + return NULL; + } +} + +struct IndexOpCtx { + lcb_N1XMGMTCALLBACK callback; + void *cookie; +}; + +struct ErrorSpec { + string msg; + unsigned code; +}; + +template void my_delete(T p) { + delete p; +} + +template lcb_error_t +extract_n1ql_errors(const char *s, size_t n, T& err_out) +{ + Json::Value jresp; + if (!Json::Reader().parse(s, s + n, jresp)) { + return LCB_PROTOCOL_ERROR; + } + if (jresp["status"].asString() == "success") { + return LCB_SUCCESS; + } + + Json::Value& errors = jresp["errors"]; + if (errors.isNull()) { + return LCB_SUCCESS; + } else if (!errors.isArray()) { + return LCB_PROTOCOL_ERROR; + } + + if (errors.empty()) { + return LCB_SUCCESS; + } + + for (Json::ArrayIndex ii = 0; ii < errors.size(); ++ii) { + const Json::Value& err = errors[ii]; + if (!err.isObject()) { + continue; // expected an object! + } + ErrorSpec spec; + spec.msg = err["msg"].asString(); + spec.code = err["code"].asUInt(); + err_out.insert(err_out.end(), spec); + } + return LCB_ERROR; +} + +static lcb_error_t +get_n1ql_error(const char *s, size_t n) +{ + std::vector dummy; + return extract_n1ql_errors(s, n, dummy); +} + +// Called for generic operations and establishes existence or lack thereof +static void +cb_generic(lcb_t instance, int, const lcb_RESPN1QL *resp) +{ + // Get the real cookie.. + if (!(resp->rflags & LCB_RESP_F_FINAL)) { + return; + } + + IndexOpCtx *ctx = reinterpret_cast(resp->cookie); + lcb_RESPN1XMGMT w_resp = { 0 }; + w_resp.cookie = ctx->cookie; + + if ((w_resp.rc = resp->rc) == LCB_SUCCESS || resp->rc == LCB_HTTP_ERROR) { + // Check if the top-level N1QL response succeeded, and then + // descend to determine additional errors. This is primarily + // required to support EEXIST for GSI primary indexes + + vector errors; + lcb_error_t rc = extract_n1ql_errors(resp->row, resp->nrow, errors); + if (rc == LCB_ERROR) { + w_resp.rc = LCB_QUERY_ERROR; + for (size_t ii = 0; ii < errors.size(); ++ii) { + const std::string& msg = errors[ii].msg; + if (msg.find("already exist") != string::npos) { + w_resp.rc = LCB_KEY_EEXISTS; // Index entry already exists + } else if (msg.find("not found") != string::npos) { + w_resp.rc = LCB_KEY_ENOENT; + } + } + } else { + w_resp.rc = rc; + } + } + + w_resp.inner = resp; + w_resp.specs = NULL; + w_resp.nspecs = 0; + ctx->callback(instance, LCB_CALLBACK_IXMGMT, &w_resp); + delete ctx; +} + +/** + * Dispatch the actual operation using a N1QL query + * @param instance + * @param cookie User cookie + * @param u_callback User callback (to assign to new context) + * @param i_callback Internal callback to be invoked when N1QL response + * is done + * @param s N1QL request payload + * @param n N1QL request length + * @param obj Internal context. Created with new if NULL + * @return + * + * See other overload for passing just the query string w/o extra parameters + */ +template lcb_error_t +dispatch_common(lcb_t instance, + const void *cookie, lcb_N1XMGMTCALLBACK u_callback, + lcb_N1QLCALLBACK i_callback, const char *s, size_t n, T *obj) +{ + lcb_error_t rc = LCB_SUCCESS; + bool our_alloc = false; + lcb_CMDN1QL cmd = { 0 }; + struct { lcb_t m_instance; } ixwrap = { instance }; // For logging + + if (obj == NULL) { + obj = new T(); + our_alloc = true; + } + + if (!(obj->callback = u_callback)) { + rc = LCB_EINVAL; + goto GT_ERROR; + } + + obj->cookie = const_cast(cookie); + + cmd.query = s; + cmd.nquery = n; + cmd.callback = i_callback; + lcb_log(LOGARGS(&ixwrap, DEBUG), LOGFMT "Issuing query %.*s", LOGID(obj), (int)n, s); + rc = lcb_n1ql_query(instance, obj, &cmd); + + GT_ERROR: + if (rc != LCB_SUCCESS && our_alloc) { + delete obj; + } + return rc; +} + +template lcb_error_t +dispatch_common(lcb_t instance, + const void *cookie, lcb_N1XMGMTCALLBACK u_callback, + lcb_N1QLCALLBACK i_callback, const string& ss, T *obj = NULL) +{ + Json::Value root; + root["statement"] = ss; + string reqbuf = Json::FastWriter().write(root); + return dispatch_common(instance, + cookie, u_callback, i_callback, + reqbuf.c_str(), reqbuf.size()-1 /*newline*/, obj); +} + + +// Class to back the storage for the actual lcb_IXSPEC without doing too much +// mind-numbing buffer copies. Maybe this can be done via a macro instead? +class IndexSpec : public lcb_N1XSPEC { +public: + IndexSpec(const char *s, size_t n) { + memset(static_cast(this), 0, sizeof (lcb_N1XSPEC)); + load_json(s, n); + } + inline IndexSpec(const lcb_N1XSPEC *spec); + static inline void to_key(const lcb_N1XSPEC *spec, std::string& out); + bool is_primary() const { return flags & LCB_N1XSPEC_F_PRIMARY; } + bool is_defer() const { return flags & LCB_N1XSPEC_F_DEFER; } + void ensure_keyspace(lcb_t instance) { + if (nkeyspace) { + return; + } + keyspace = LCBT_SETTING(instance, bucket); + nkeyspace = strlen(keyspace); + } + +private: + // Load fields from a JSON string + inline void load_json(const char *s, size_t n); + + // Load all fields + inline size_t load_fields(const Json::Value& root, bool do_copy); + + size_t total_fields_size(const Json::Value& src) { + return load_fields(src, false); + } + + // Load field from a JSON object + inline size_t load_json_field( + const Json::Value& root, + const char *name, const char **tgt_ptr, size_t *tgt_len, bool do_copy); + + // Load field from another pointer + void load_field(const char **dest, const char *src, size_t n) { + m_buf.append(src, n); + if (n) { + *dest = &m_buf.c_str()[m_buf.size()-n]; + } else { + *dest = NULL; + } + } + + string m_buf; + IndexSpec(const IndexSpec&); +}; + +LIBCOUCHBASE_API +lcb_error_t +lcb_n1x_create(lcb_t instance, const void *cookie, const lcb_CMDN1XMGMT *cmd) +{ + string ss; + IndexSpec spec(&cmd->spec); + spec.ensure_keyspace(instance); + + ss = "CREATE"; + if (spec.is_primary()) { + ss += " PRIMARY"; + } else if (!spec.nname) { + return LCB_EMPTY_KEY; + } + ss.append(" INDEX"); + if (spec.nname) { + ss.append(" `").append(spec.name, spec.nname).append("` "); + } + ss.append(" ON `").append(spec.keyspace, spec.nkeyspace).append("`"); + + if (!spec.is_primary()) { + if (!spec.nfields) { + return LCB_EMPTY_KEY; + } + + // See if we can parse 'fields' properly. First, try to parse as + // JSON: + Json::Value fields_arr; + Json::Reader r; + if (!r.parse(spec.fields, spec.fields + spec.nfields, fields_arr)) { + // Invalid JSON! + return LCB_EINVAL; + } + + ss.append(" ("); + if (fields_arr.isArray()) { + if (!fields_arr.size()) { + return LCB_EMPTY_KEY; + } + for (size_t ii = 0; ii < fields_arr.size(); ++ii) { + static Json::Value empty; + const Json::Value& field = fields_arr.get(ii, empty); + if (!field.isString()) { + return LCB_EINVAL; + } + ss.append(field.asString()); + if (ii != fields_arr.size()-1) { + ss.append(","); + } + } + } else if (fields_arr.isString()) { + std::string field_list = fields_arr.asString(); + if (field_list.empty()) { + return LCB_EMPTY_KEY; + } + ss.append(field_list); + } else { + return LCB_EINVAL; + } + ss.append(") "); + } + + if (spec.ncond) { + if (spec.is_primary()) { + return LCB_EINVAL; + } + ss.append(" WHERE ").append(spec.cond, spec.ncond).append(" "); + } + + if (spec.ixtype) { + const char *ixtype = ixtype_2_str(spec.ixtype); + if (!ixtype) { + return LCB_EINVAL; + } + ss.append(" USING ").append(ixtype); + } + + if (spec.is_defer()) { + ss.append(" WITH {\"defer_build\": true}"); + } + + return dispatch_common(instance, cookie, cmd->callback, cb_generic, ss); +} + + +class ListIndexCtx : public IndexOpCtx { +public: + vector specs; + + virtual void invoke(lcb_t instance, lcb_RESPN1XMGMT *resp) { + finish(instance, resp); + } + + void finish(lcb_t instance, lcb_RESPN1XMGMT *resp = NULL) { + lcb_RESPN1XMGMT w_resp = { 0 }; + if (resp == NULL) { + resp = &w_resp; + resp->rc = LCB_SUCCESS; + } + resp->cookie = cookie; + lcb_N1XSPEC **speclist = reinterpret_cast(&specs[0]); + resp->specs = speclist; + resp->nspecs = specs.size(); + callback(instance, LCB_CALLBACK_IXMGMT, resp); + delete this; + } + + virtual ~ListIndexCtx() { + for (size_t ii = 0; ii < specs.size(); ++ii) { + delete specs[ii]; + } + specs.clear(); + } +}; + +static void +cb_index_list(lcb_t instance, int, const lcb_RESPN1QL *resp) +{ + ListIndexCtx *ctx = reinterpret_cast(resp->cookie); + if (!(resp->rflags & LCB_RESP_F_FINAL)) { + ctx->specs.push_back(new IndexSpec(resp->row, resp->nrow)); + return; + } + + lcb_RESPN1XMGMT w_resp = { 0 }; + if ((w_resp.rc = resp->rc) == LCB_SUCCESS) { + w_resp.rc = get_n1ql_error(resp->row, resp->nrow); + } + w_resp.inner = resp; + ctx->invoke(instance, &w_resp); +} + +static lcb_error_t +do_index_list(lcb_t instance, const void *cookie, const lcb_CMDN1XMGMT *cmd, + ListIndexCtx *ctx) +{ + string ss; + IndexSpec spec(&cmd->spec); + ss = "SELECT idx.* FROM system:indexes idx WHERE"; + + if (spec.flags & LCB_N1XSPEC_F_PRIMARY) { + ss.append(" is_primary=true AND"); + } + if (spec.nkeyspace) { + ss.append(" keyspace_id=\"").append(spec.keyspace, spec.nkeyspace).append("\" AND"); + } + if (spec.nnspace) { + ss.append(" namespace_id=\"").append(spec.nspace, spec.nnspace).append("\" AND"); + } + if (spec.ixtype) { + const char *s_ixtype = ixtype_2_str(spec.ixtype); + if (s_ixtype == NULL) { + if (ctx != NULL) { + delete ctx; + } + return LCB_EINVAL; + } + ss.append(" using=\"").append(s_ixtype).append("\" AND"); + } + if (spec.nname) { + ss.append(" name=\"").append(spec.name, spec.nname).append("\" AND"); + } + + // WHERE <.....> true + ss.append(" true"); + ss.append(" ORDER BY is_primary DESC, name ASC"); + + return dispatch_common(instance, + cookie, cmd->callback, cb_index_list, ss, ctx); +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_n1x_list(lcb_t instance, const void *cookie, const lcb_CMDN1XMGMT *cmd) +{ + return do_index_list(instance, cookie, cmd, NULL); +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_n1x_drop(lcb_t instance, const void *cookie, const lcb_CMDN1XMGMT *cmd) +{ + string ss; + IndexSpec spec(&cmd->spec); + spec.ensure_keyspace(instance); + + if (spec.nname) { + ss = "DROP INDEX"; + ss.append(" `").append(spec.keyspace, spec.nkeyspace).append("`"); + ss.append(".`").append(spec.name, spec.nname).append("`"); + } else if (spec.flags & LCB_N1XSPEC_F_PRIMARY) { + ss = "DROP PRIMARY INDEX ON"; + ss.append(" `").append(spec.keyspace, spec.nkeyspace).append("`"); + } else { + return LCB_EMPTY_KEY; + } + + if (spec.ixtype) { + const char *stype = ixtype_2_str(spec.ixtype); + if (!stype) { + return LCB_EINVAL; + } + ss.append(" USING ").append(stype); + } + + return dispatch_common(instance, cookie, cmd->callback, cb_generic, ss); +} + +class ListIndexCtx_BuildIndex : public ListIndexCtx { +public: + virtual inline void invoke(lcb_t instance, lcb_RESPN1XMGMT *resp); + inline lcb_error_t try_build(lcb_t instance); +}; + +static void +cb_build_submitted(lcb_t instance, int, const lcb_RESPN1QL *resp) +{ + ListIndexCtx *ctx = reinterpret_cast(resp->cookie); + + if (resp->rflags & LCB_RESP_F_FINAL) { + lcb_RESPN1XMGMT w_resp = { 0 }; + if ((w_resp.rc = resp->rc) == LCB_SUCCESS) { + w_resp.rc = get_n1ql_error(resp->row, resp->nrow); + } + ctx->finish(instance, &w_resp); + } +} + +lcb_error_t +ListIndexCtx_BuildIndex::try_build(lcb_t instance) +{ + vector pending; + for (size_t ii = 0; ii < specs.size(); ++ii) { + IndexSpec* spec = specs[ii]; + if (strncmp(spec->state, "pending", spec->nstate) == 0 || + strncmp(spec->state, "deferred", spec->nstate) == 0) { + pending.push_back(spec); + } + } + + if (pending.empty()) { + return LCB_KEY_ENOENT; + } + + string ss; + ss = "BUILD INDEX ON `"; + + ss.append(pending[0]->keyspace, pending[0]->nkeyspace).append("`"); + ss += '('; + for (size_t ii = 0; ii < pending.size(); ++ii) { + ss += '`'; + ss.append(pending[ii]->name, pending[ii]->nname); + ss += '`'; + if (ii+1 < pending.size()) { + ss += ','; + } + } + ss += ')'; + + lcb_error_t rc = dispatch_common( + instance, cookie, callback, cb_build_submitted, ss, + this); + + if (rc == LCB_SUCCESS) { + std::set to_remove(specs.begin(), specs.end()); + for (size_t ii = 0; ii < pending.size(); ++ii) { + to_remove.erase(pending[ii]); + } + + std::for_each(to_remove.begin(), to_remove.end(), my_delete); + + specs = pending; + } + return rc; +} + +void +ListIndexCtx_BuildIndex::invoke(lcb_t instance, lcb_RESPN1XMGMT *resp) +{ + if (resp->rc == LCB_SUCCESS && + (resp->rc = try_build(instance)) == LCB_SUCCESS) { + return; + } + finish(instance, resp); +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_n1x_startbuild(lcb_t instance, const void *cookie, const lcb_CMDN1XMGMT *cmd) +{ + ListIndexCtx_BuildIndex *ctx = new ListIndexCtx_BuildIndex(); + lcb_error_t rc = do_index_list(instance, cookie, cmd, ctx); + if (rc != LCB_SUCCESS) { + delete ctx; + } + return rc; +} + +struct WatchIndexCtx : public IndexOpCtx { + // Interval timer + lcbio_pTIMER m_timer; + uint32_t m_interval; + uint64_t m_tsend; + lcb_t m_instance; + std::map m_defspend; + std::vector m_defsok; + + inline void read_state(const lcb_RESPN1XMGMT *resp); + inline void reschedule(); + inline lcb_error_t do_poll(); + inline lcb_error_t load_defs(const lcb_CMDN1XWATCH *); + inline WatchIndexCtx(lcb_t, const void *, const lcb_CMDN1XWATCH*); + inline ~WatchIndexCtx(); + inline void finish(lcb_error_t rc, const lcb_RESPN1XMGMT *); +}; + +static void +cb_watchix_tm(void *arg) +{ + WatchIndexCtx *ctx = reinterpret_cast(arg); + uint64_t now = lcb_nstime(); + if (now >= ctx->m_tsend) { + ctx->finish(LCB_ETIMEDOUT, NULL); + } else { + ctx->do_poll(); + } +} + +#define DEFAULT_WATCH_TIMEOUT LCB_S2US(30) +#define DEFAULT_WATCH_INTERVAL LCB_MS2US(500) + +WatchIndexCtx::WatchIndexCtx(lcb_t instance, const void *cookie_, const lcb_CMDN1XWATCH *cmd) +: m_instance(instance) +{ + uint64_t now = lcb_nstime(); + uint32_t timeout = cmd->timeout ? cmd->timeout : DEFAULT_WATCH_TIMEOUT; + m_interval = cmd->interval ? cmd->interval : DEFAULT_WATCH_INTERVAL; + m_interval = std::min(m_interval, timeout); + m_tsend = now + LCB_US2NS(timeout); + + this->callback = cmd->callback; + this->cookie = const_cast(cookie_); + + m_timer = lcbio_timer_new(instance->iotable, this, cb_watchix_tm); + lcb_aspend_add(&instance->pendops, LCB_PENDTYPE_COUNTER, NULL); +} + +WatchIndexCtx::~WatchIndexCtx() +{ + if (m_timer) { + lcbio_timer_destroy(m_timer); + } + if (m_instance) { + lcb_aspend_del(&m_instance->pendops, LCB_PENDTYPE_COUNTER, NULL); + lcb_maybe_breakout(m_instance); + } + + std::for_each(m_defsok.begin(), m_defsok.end(), my_delete); + for (std::map::iterator ii = m_defspend.begin(); + ii != m_defspend.end(); ++ii) { + delete ii->second; + } +} + +void +IndexSpec::to_key(const lcb_N1XSPEC* spec, std::string& s) +{ + // Identity is: + // {keyspace,name,is_primary,type} + s.append(spec->nspace, spec->nnspace).append(" "); + s.append(spec->keyspace, spec->nkeyspace).append(" "); + s.append(spec->name, spec->nname).append(" "); + const char *type_s = ixtype_2_str(spec->ixtype); + if (!type_s) { + type_s = ""; + } + s.append(type_s); +} + +void +WatchIndexCtx::read_state(const lcb_RESPN1XMGMT *resp) +{ + // We examine the indexes here to see which ones have been completed + // Make them all into an std::map + if (resp->rc != LCB_SUCCESS) { + lcb_log(LOGARGS(this, INFO), LOGFMT "Error 0x%x while listing indexes. Rescheduling", LOGID(this), resp->rc); + reschedule(); + return; + } + + std::map in_specs; + for (size_t ii = 0; ii < resp->nspecs; ++ii) { + std::string key; + IndexSpec::to_key(resp->specs[ii], key); + in_specs[key] = resp->specs[ii]; + } + + std::map::iterator it_remain = m_defspend.begin(); + while (it_remain != m_defspend.end()) { + // See if the index is 'online' yet! + std::map::iterator res; + res = in_specs.find(it_remain->first); + if (res == in_specs.end()) { + lcb_log(LOGARGS(this, INFO), LOGFMT "Index [%s] not in cluster", LOGID(this), it_remain->first.c_str()); + // We can't find our own index. Someone else deleted it. Bail! + finish(LCB_KEY_ENOENT, resp); + return; + } + + std::string s_state(res->second->state, res->second->nstate); + if (s_state == "online") { + lcb_log(LOGARGS(this, DEBUG), LOGFMT "Index [%s] is ready", LOGID(this), it_remain->first.c_str()); + m_defsok.push_back(it_remain->second); + m_defspend.erase(it_remain++); + } else { + ++it_remain; + } + } + + if (m_defspend.empty()) { + finish(LCB_SUCCESS, resp); + } else { + reschedule(); + } +} + +lcb_error_t +WatchIndexCtx::load_defs(const lcb_CMDN1XWATCH *cmd) +{ + for (size_t ii = 0; ii < cmd->nspec; ++ii) { + std::string key; + IndexSpec *extspec = new IndexSpec(cmd->specs[ii]); + IndexSpec::to_key(extspec, key); + m_defspend[key] = extspec; + } + if (m_defspend.empty()) { + return LCB_ENO_COMMANDS; + } + return LCB_SUCCESS; +} + +void +WatchIndexCtx::finish(lcb_error_t rc, const lcb_RESPN1XMGMT *resp) +{ + lcb_RESPN1XMGMT my_resp = { 0 }; + my_resp.cookie = cookie; + my_resp.rc = rc; + + if (resp) { + my_resp.inner = resp->inner; + } + + lcb_N1XSPEC **speclist = reinterpret_cast(&m_defsok[0]); + my_resp.specs = speclist; + my_resp.nspecs = m_defsok.size(); + callback(m_instance, LCB_CALLBACK_IXMGMT, &my_resp); + delete this; +} + +void +WatchIndexCtx::reschedule() +{ + // Next interval! + uint64_t now = lcb_nstime(); + if (now + LCB_US2NS(m_interval) >= m_tsend) { + finish(LCB_ETIMEDOUT, NULL); + } else { + lcbio_timer_rearm(m_timer, m_interval); + } +} + +static void +cb_watch_gotlist(lcb_t, int, const lcb_RESPN1XMGMT *resp) +{ + WatchIndexCtx *ctx = reinterpret_cast(resp->cookie); + ctx->read_state(resp); +} + +lcb_error_t +WatchIndexCtx::do_poll() +{ + lcb_CMDN1XMGMT cmd; + memset(&cmd, 0, sizeof cmd); + cmd.callback = cb_watch_gotlist; + lcb_log(LOGARGS(this, DEBUG), LOGFMT "Will check for index readiness of %lu indexes. %lu completed", LOGID(this), m_defspend.size(), m_defsok.size()); + return lcb_n1x_list(m_instance, this, &cmd); +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_n1x_watchbuild(lcb_t instance, const void *cookie, const lcb_CMDN1XWATCH *cmd) +{ + WatchIndexCtx *ctx = new WatchIndexCtx(instance, cookie, cmd); + lcb_error_t rc; + if ((rc = ctx->load_defs(cmd)) != LCB_SUCCESS) { + delete ctx; + return rc; + } + if ((rc = ctx->do_poll()) != LCB_SUCCESS) { + delete ctx; + return rc; + } + return LCB_SUCCESS; +} + +void +IndexSpec::load_json(const char *s, size_t n) { + Json::Value root; + // Set the JSON first! + m_buf.assign(s, n); + nrawjson = n; + + if (!Json::Reader().parse(s, s + n, root)) { + rawjson = m_buf.c_str(); + return; + } + + m_buf.reserve(n + total_fields_size(root)); + load_fields(root, true); + + // Once all the fields are loaded, it's time to actually assign the + // rawjson field, which is simply the beginning of the buffer + rawjson = m_buf.c_str(); + + // Get the index type + string ixtype_s = root["using"].asString(); + if (ixtype_s == "gsi") { + ixtype = LCB_N1XSPEC_T_GSI; + } else if (ixtype_s == "view") { + ixtype = LCB_N1XSPEC_T_VIEW; + } + if (root["is_primary"].asBool()) { + flags |= LCB_N1XSPEC_F_PRIMARY; + } +} + +// IndexSpec stuff +IndexSpec::IndexSpec(const lcb_N1XSPEC *spec) +{ + *static_cast(this) = *spec; + if (spec->nrawjson) { + load_json(spec->rawjson, spec->nrawjson); + return; + } + // Initialize the bufs + m_buf.reserve(nname + nkeyspace + nnspace + nstate + nfields + nrawjson + nstate + ncond); + load_field(&rawjson, spec->rawjson, nrawjson); + load_field(&name, spec->name, nname); + load_field(&keyspace, spec->keyspace, nkeyspace); + load_field(&nspace, spec->nspace, nnspace); + load_field(&state, spec->state, nstate); + load_field(&fields, spec->fields, nfields); + load_field(&cond, spec->cond, ncond); +} + +size_t +IndexSpec::load_fields(const Json::Value& root, bool do_copy) +{ + size_t size = 0; + size += load_json_field(root, "name", &name, &nname, do_copy); + size += load_json_field(root, "keyspace_id", &keyspace, &nkeyspace, do_copy); + size += load_json_field(root, "namespace_id", &nspace, &nnspace, do_copy); + size += load_json_field(root, "state", &state, &nstate, do_copy); + size += load_json_field(root, "index_key", &fields, &nfields, do_copy); + size += load_json_field(root, "condition", &cond, &ncond, do_copy); + return size; +} + +size_t +IndexSpec::load_json_field(const Json::Value& root, + const char *name_, const char **tgt_ptr, size_t *tgt_len, bool do_copy) +{ + size_t namelen = strlen(name_); + const Json::Value *val = root.find(name_, name_ + namelen); + size_t n = 0; + + if (val == NULL) { + return 0; + } + + if (val->isString()) { + const char *s_begin, *s_end; + if (val->getString(&s_begin, &s_end) && (n = s_end - s_begin) && do_copy) { + m_buf.insert(m_buf.end(), s_begin, s_end); + } + } else { + std::string frag = Json::FastWriter().write(*val); + n = frag.size(); + if (do_copy) { + m_buf.append(frag); + } + } + + if (n) { + *tgt_ptr = &(m_buf.c_str()[m_buf.size()-n]); + *tgt_len = n; + } else { + *tgt_ptr = NULL; + *tgt_len = 0; + } + return n; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/n1ql/n1ql-internal.h b/couchbase-sys/libcouchbase-2.7.0/src/n1ql/n1ql-internal.h new file mode 100644 index 00000000..aec8b8db --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/n1ql/n1ql-internal.h @@ -0,0 +1,22 @@ +#ifndef LCB_N1QL_INTERNAL_H +#define LCB_N1QL_INTERNAL_H + +#ifdef __cplusplus +#include +extern "C" { +#endif + +typedef struct lcb_N1QLCACHE_st lcb_N1QLCACHE; +lcb_N1QLCACHE *lcb_n1qlcache_create(void); +void lcb_n1qlcache_destroy(lcb_N1QLCACHE*); +void lcb_n1qlcache_clear(lcb_N1QLCACHE *); + +#ifdef __cplusplus +void lcb_n1qlcache_getplan(lcb_N1QLCACHE *cache, + const std::string& key, std::string& out); + +// Parse timeout value. Exposed for tests +lcb_U32 lcb_n1qlreq_parsetmo(const std::string& s); +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/n1ql/n1ql.cc b/couchbase-sys/libcouchbase-2.7.0/src/n1ql/n1ql.cc new file mode 100644 index 00000000..c3e63a0a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/n1ql/n1ql.cc @@ -0,0 +1,730 @@ +#include +#include +#include +#include "internal.h" +#include "auth-priv.h" +#include "http/http.h" +#include "logging.h" +#include "contrib/lcb-jsoncpp/lcb-jsoncpp.h" +#include +#include +#include + +#define LOGFMT "(NR=%p) " +#define LOGID(req) static_cast(req) +#define LOGARGS(req, lvl) req->instance->settings, "n1ql", LCB_LOG_##lvl, __FILE__, __LINE__ + +// Indicate that the 'creds' field is to be used. +#define F_CMDN1QL_CREDSAUTH 1<<15 + +class Plan { +private: + friend struct lcb_N1QLCACHE_st; + std::string key; + std::string planstr; + Plan(const std::string& k) : key(k) { + } + +public: + /** + * Applies the plan to the output 'bodystr'. We don't assign the + * Json::Value directly, as this appears to be horribly slow. On my system + * an assignment took about 200ms! + * @param body The request body (e.g. N1QLREQ::json) + * @param[out] bodystr the actual request payload + */ + void apply_plan(Json::Value& body, std::string& bodystr) const { + body.removeMember("statement"); + bodystr = Json::FastWriter().write(body); + + // Assume bodystr ends with '}' + size_t pos = bodystr.rfind('}'); + bodystr.erase(pos); + + if (body.size() > 0) { + bodystr.append(","); + } + bodystr.append(planstr); + bodystr.append("}"); + } + +private: + /** + * Assign plan data to this entry + * @param plan The JSON returned from the PREPARE request + */ + void set_plan(const Json::Value& plan) { + // Set the plan as a string + planstr = "\"prepared\":"; + planstr += Json::FastWriter().write(plan["name"]); + planstr += ","; + planstr += "\"encoded_plan\":"; + planstr += Json::FastWriter().write(plan["encoded_plan"]); + } +}; + +// LRU Cache structure.. +struct lcb_N1QLCACHE_st { + typedef std::list LruCache; + typedef std::map Lookup; + + Lookup by_name; + LruCache lru; + + /** Maximum number of entries in LRU cache. This is fixed at 5000 */ + static size_t max_size() { return 5000; } + + /** + * Adds an entry for a given key + * @param key The key to add + * @param json The prepared statement returned by the server + * @return the newly added plan. + */ + const Plan& add_entry(const std::string& key, const Json::Value& json) { + if (lru.size() == max_size()) { + // Purge entry from end + remove_entry(lru.back()->key); + } + + // Remove old entry, if present + remove_entry(key); + + lru.push_front(new Plan(key)); + by_name[key] = lru.begin(); + lru.front()->set_plan(json); + return *lru.front(); + } + + /** + * Gets the entry for a given key + * @param key The statement (key) to look up + * @return a pointer to the plan if present, NULL if no entry exists for key + */ + const Plan* get_entry(const std::string& key) { + Lookup::iterator m = by_name.find(key); + if (m == by_name.end()) { + return NULL; + } + + const Plan* cur = *m->second; + + // Update LRU: + lru.splice(lru.begin(), lru, m->second); + // Note, updating of iterators is not required since splice doesn't + // invalidate iterators. + return cur; + } + + /** Removes an entry with the given key */ + void remove_entry(const std::string& key) { + Lookup::iterator m = by_name.find(key); + if (m == by_name.end()) { + return; + } + // Remove entry from map + LruCache::iterator m2 = m->second; + delete *m2; + by_name.erase(m); + lru.erase(m2); + } + + /** Clears the LRU cache */ + void clear() { + for (LruCache::iterator ii = lru.begin(); ii != lru.end(); ++ii) { + delete *ii; + } + lru.clear(); + by_name.clear(); + } + + ~lcb_N1QLCACHE_st() { + clear(); + } +}; + +typedef struct lcb_N1QLREQ { + const lcb_RESPHTTP *cur_htresp; + struct lcb_http_request_st *htreq; + lcbjsp_PARSER *parser; + const void *cookie; + lcb_N1QLCALLBACK callback; + lcb_t instance; + lcb_error_t lasterr; + lcb_U32 flags; + lcb_U32 timeout; + // How many rows were received. Used to avoid parsing the meta + size_t nrows; + + /** The PREPARE query itself */ + struct lcb_N1QLREQ *prepare_req; + + /** Request body as received from the application */ + Json::Value json; + const Json::Value& json_const() const { return json; } + + /** String of the original statement. Cached here to avoid jsoncpp lookups */ + std::string statement; + + /** Whether we're retrying this */ + bool was_retried; + + lcb_N1QLCACHE& cache() { return *instance->n1ql_cache; } + + /** + * Creates the sub-N1QLREQ for the PREPARE statement. This inspects the + * current request (see ::json) and copies it so that we execute the + * PREPARE instead of the actual query. + * @return see issue_htreq() + */ + inline lcb_error_t request_plan(); + + /** + * Use the plan to execute the given query, and issues the query + * @param plan The plan itself + * @return see issue_htreq() + */ + inline lcb_error_t apply_plan(const Plan& plan); + + /** + * Issues the HTTP request for the query + * @param payload The body to send + * @return Error code from lcb's http subsystem + */ + inline lcb_error_t issue_htreq(const std::string& payload); + + lcb_error_t issue_htreq() { + std::string s = Json::FastWriter().write(json); + return issue_htreq(s); + } + + /** + * Attempt to retry the query. This will inspect the meta (if present) + * for any errors indicating that a failure might be a result of a stale + * plan, and if this query was retried already. + * @return true if the retry was successful. + */ + inline bool maybe_retry(); + + /** + * Did the application request this query to use prepared statements + * @return true if using prepared statements + */ + inline bool use_prepcache() const { return flags & LCB_CMDN1QL_F_PREPCACHE; } + + /** + * Pass a row back to the application + * @param resp The response. This is populated with state information + * from the current query + * @param is_last Whether this is the last row. If this is the last, then + * the RESP_F_FINAL flag is set, and no further callbacks will be invoked + */ + inline void invoke_row(lcb_RESPN1QL *resp, bool is_last); + + /** + * Fail an application-level query because the prepared statement failed + * @param orig The response from the PREPARE request + * @param err The error code + */ + inline void fail_prepared(const lcb_RESPN1QL *orig, lcb_error_t err); + + inline lcb_N1QLREQ(lcb_t obj, const void *user_cookie, const lcb_CMDN1QL *cmd); + inline ~lcb_N1QLREQ(); + +} N1QLREQ; + +static bool +parse_json(const char *s, size_t n, Json::Value& res) +{ + return Json::Reader().parse(s, s + n, res); +} + +lcb_N1QLCACHE * +lcb_n1qlcache_create(void) +{ + return new lcb_N1QLCACHE; +} + +void +lcb_n1qlcache_destroy(lcb_N1QLCACHE *cache) +{ + delete cache; +} + +void +lcb_n1qlcache_clear(lcb_N1QLCACHE *cache) +{ + cache->clear(); +} + +// Special function for debugging. This returns the name and encoded form of +// the plan +void +lcb_n1qlcache_getplan(lcb_N1QLCACHE *cache, + const std::string& key, std::string& out) +{ + const Plan* plan = cache->get_entry(key); + if (plan != NULL) { + Json::Value tmp(Json::objectValue); + plan->apply_plan(tmp, out); + } +} + +#define WTF_MAGIC_STRING \ + "index deleted or node hosting the index is down - cause: queryport.indexNotFound" + +static bool +has_retriable_error(const Json::Value& root) +{ + if (!root.isObject()) { + return false; + } + const Json::Value& errors = root["errors"]; + if (!errors.isArray()) { + return false; + } + Json::Value::const_iterator ii; + for (ii = errors.begin(); ii != errors.end(); ++ii) { + const Json::Value& cur = *ii; + if (!cur.isObject()) { + continue; // eh? + } + const Json::Value& jmsg = cur["msg"]; + const Json::Value& jcode = cur["code"]; + if (jcode.isNumeric()) { + unsigned code = jcode.asUInt(); + if (code == 4050 || code == 4070) { + return true; + } + } + if (jmsg.isString() && strstr(jmsg.asCString(), WTF_MAGIC_STRING) != NULL) { + return true; + } + } + return false; +} + +bool +N1QLREQ::maybe_retry() +{ + // Examines the buffer to determine the type of error + Json::Value root; + lcb_IOV meta; + + if (callback == NULL) { + // Cancelled + return false; + } + + if (nrows) { + // Has results: + return false; + } + + if (was_retried) { + return false; + } + + if (!use_prepcache()) { + // Didn't use our built-in caching (maybe using it from elsewhere?) + return false; + } + + was_retried = true; + + lcbjsp_get_postmortem(parser, &meta); + if (!parse_json(static_cast(meta.iov_base), meta.iov_len, root)) { + return false; // Not JSON + } + if (!has_retriable_error(root)) { + return false; + } + + lcb_log(LOGARGS(this, ERROR), LOGFMT "Repreparing statement. Index or version mismatch.", LOGID(this)); + + // Let's see if we can actually retry. First remove the existing prepared + // entry: + cache().remove_entry(statement); + lcb_error_t rc = request_plan(); + if (rc != LCB_SUCCESS) { + lasterr = rc; + return false; + + } else { + // We'll be parsing more rows later on.. + lcbjsp_reset(parser); + } + return true; +} + +void +N1QLREQ::invoke_row(lcb_RESPN1QL *resp, bool is_last) +{ + resp->cookie = const_cast(cookie); + resp->htresp = cur_htresp; + + if (is_last) { + lcb_IOV meta; + resp->rflags |= LCB_RESP_F_FINAL; + resp->rc = lasterr; + lcbjsp_get_postmortem(parser, &meta); + resp->row = static_cast(meta.iov_base); + resp->nrow = meta.iov_len; + } + + if (callback) { + callback(instance, LCB_CALLBACK_N1QL, resp); + } + if (is_last) { + callback = NULL; + } +} + +lcb_N1QLREQ::~lcb_N1QLREQ() +{ + if (htreq) { + lcb_cancel_http_request(instance, htreq); + htreq = NULL; + } + + if (callback) { + lcb_RESPN1QL resp = { 0 }; + invoke_row(&resp, 1); + } + + if (parser) { + lcbjsp_free(parser); + } + if (prepare_req) { + lcb_n1ql_cancel(instance, prepare_req); + } +} + +static void +row_callback(lcbjsp_PARSER *parser, const lcbjsp_ROW *datum) +{ + N1QLREQ *req = static_cast(parser->data); + lcb_RESPN1QL resp = { 0 }; + + if (datum->type == LCBJSP_TYPE_ROW) { + resp.row = static_cast(datum->row.iov_base); + resp.nrow = datum->row.iov_len; + req->nrows++; + req->invoke_row(&resp, 0); + } else if (datum->type == LCBJSP_TYPE_ERROR) { + req->lasterr = resp.rc = LCB_PROTOCOL_ERROR; + } else if (datum->type == LCBJSP_TYPE_COMPLETE) { + /* Nothing */ + } +} + +static void +chunk_callback(lcb_t instance, int ign, const lcb_RESPBASE *rb) +{ + const lcb_RESPHTTP *rh = (const lcb_RESPHTTP *)rb; + N1QLREQ *req = static_cast(rh->cookie); + + (void)ign; (void)instance; + + req->cur_htresp = rh; + if (rh->rc != LCB_SUCCESS || rh->htstatus != 200) { + if (req->lasterr == LCB_SUCCESS || rh->htstatus != 200) { + req->lasterr = rh->rc ? rh->rc : LCB_HTTP_ERROR; + } + } + + if (rh->rflags & LCB_RESP_F_FINAL) { + req->htreq = NULL; + if (!req->maybe_retry()) { + delete req; + } + return; + } else if (req->callback == NULL) { + /* Cancelled. Similar to the block above, except the http request + * should remain alive (so we can cancel it later on) */ + delete req; + return; + } + + lcbjsp_feed(req->parser, static_cast(rh->body), rh->nbody); +} + +#define QUERY_PATH "/query/service" + +void +N1QLREQ::fail_prepared(const lcb_RESPN1QL *orig, lcb_error_t err) +{ + lcb_log(LOGARGS(this, ERROR), LOGFMT "Prepare failed!", LOGID(this)); + + lcb_RESPN1QL newresp = *orig; + newresp.rflags = LCB_RESP_F_FINAL; + newresp.cookie = const_cast(cookie); + newresp.rc = err; + if (err == LCB_SUCCESS) { + newresp.rc = LCB_ERROR; + } + + if (callback != NULL) { + callback(instance, LCB_CALLBACK_N1QL, &newresp); + callback = NULL; + } + delete this; +} + +// Received internally for PREPARE +static void +prepare_rowcb(lcb_t instance, int, const lcb_RESPN1QL *row) +{ + lcb_N1QLREQ *origreq = reinterpret_cast(row->cookie); + + lcb_n1ql_cancel(instance, origreq->prepare_req); + origreq->prepare_req = NULL; + + if (row->rc != LCB_SUCCESS || (row->rflags & LCB_RESP_F_FINAL)) { + origreq->fail_prepared(row, row->rc); + } else { + // Insert into cache + Json::Value prepared; + if (!parse_json(row->row, row->nrow, prepared)) { + lcb_log(LOGARGS(origreq, ERROR), LOGFMT "Invalid JSON returned from PREPARE", LOGID(origreq)); + origreq->fail_prepared(row, LCB_PROTOCOL_ERROR); + return; + } + + // Insert plan into cache + lcb_log(LOGARGS(origreq, DEBUG), LOGFMT "Got prepared statement. Inserting into cache and reissuing", LOGID(origreq)); + const Plan& ent = + origreq->cache().add_entry(origreq->statement, prepared); + + // Issue the query with the newly prepared plan + lcb_error_t rc = origreq->apply_plan(ent); + if (rc != LCB_SUCCESS) { + origreq->fail_prepared(row, rc); + } + } +} + +lcb_error_t +N1QLREQ::issue_htreq(const std::string& body) +{ + lcb_CMDHTTP htcmd = { 0 }; + htcmd.body = body.c_str(); + htcmd.nbody = body.size(); + + htcmd.content_type = "application/json"; + htcmd.method = LCB_HTTP_METHOD_POST; + htcmd.type = LCB_HTTP_TYPE_N1QL; + htcmd.cmdflags = LCB_CMDHTTP_F_STREAM|LCB_CMDHTTP_F_CASTMO; + if (flags & F_CMDN1QL_CREDSAUTH) { + htcmd.cmdflags |= LCB_CMDHTTP_F_NOUPASS; + } + htcmd.reqhandle = &htreq; + htcmd.cas = timeout; + + lcb_error_t rc = lcb_http3(instance, this, &htcmd); + if (rc == LCB_SUCCESS) { + lcb_htreq_setcb(htreq, chunk_callback); + } + return rc; +} + +lcb_error_t +N1QLREQ::request_plan() +{ + Json::Value newbody(Json::objectValue); + newbody["statement"] = "PREPARE " + statement; + lcb_CMDN1QL newcmd = { 0 }; + newcmd.callback = prepare_rowcb; + newcmd.cmdflags = LCB_CMDN1QL_F_JSONQUERY; + newcmd.handle = &prepare_req; + newcmd.query = reinterpret_cast(&newbody); + if (flags & F_CMDN1QL_CREDSAUTH) { + newcmd.cmdflags |= LCB_CMD_F_MULTIAUTH; + } + + return lcb_n1ql_query(instance, this, &newcmd); +} + +lcb_error_t +N1QLREQ::apply_plan(const Plan& plan) +{ + lcb_log(LOGARGS(this, DEBUG), LOGFMT "Using prepared plan", LOGID(this)); + std::string bodystr; + plan.apply_plan(json, bodystr); + return issue_htreq(bodystr); +} + +lcb_U32 +lcb_n1qlreq_parsetmo(const std::string& s) +{ + double num; + int nchars, rv; + + rv = sscanf(s.c_str(), "%lf%n", &num, &nchars); + if (rv != 1) { + return 0; + } + std::string mults = s.substr(nchars); + + // Get the actual timeout value in microseconds. Note we can't use the macros + // since they will truncate the double value. + if (mults == "s") { + return num * static_cast(LCB_S2US(1)); + } else if (mults == "ms") { + return num * static_cast(LCB_MS2US(1)); + } else if (mults == "h") { + return num * static_cast(LCB_S2US(3600)); + } else if (mults == "us") { + return num; + } else if (mults == "m") { + return num * static_cast(LCB_S2US(60)); + } else if (mults == "ns") { + return LCB_NS2US(num); + } else { + return 0; + } +} + +lcb_N1QLREQ::lcb_N1QLREQ(lcb_t obj, + const void *user_cookie, const lcb_CMDN1QL *cmd) + : cur_htresp(NULL), htreq(NULL), parser(lcbjsp_create(LCBJSP_MODE_N1QL)), + cookie(user_cookie), callback(cmd->callback), instance(obj), + lasterr(LCB_SUCCESS), flags(cmd->cmdflags), timeout(0), + nrows(0), prepare_req(NULL), was_retried(false) +{ + parser->data = this; + parser->callback = row_callback; + if (cmd->handle) { + *cmd->handle = this; + } + + if (flags & LCB_CMDN1QL_F_JSONQUERY) { + json = *reinterpret_cast(cmd->query); + } else if (!parse_json(cmd->query, cmd->nquery, json)) { + lasterr = LCB_EINVAL; + return; + } + + const Json::Value& j_statement = json_const()["statement"]; + if (j_statement.isString()) { + statement = j_statement.asString(); + } else if (!j_statement.isNull()) { + lasterr = LCB_EINVAL; + return; + } + + Json::Value& tmoval = json["timeout"]; + if (tmoval.isNull()) { + // Set the default timeout as the server-side query timeout if no + // other timeout is used. + char buf[64] = { 0 }; + sprintf(buf, "%uus", LCBT_SETTING(obj, n1ql_timeout)); + tmoval = buf; + timeout = LCBT_SETTING(obj, n1ql_timeout); + } else if (tmoval.isString()) { + timeout = lcb_n1qlreq_parsetmo(tmoval.asString()); + } else { + // Timeout is not a string! + lasterr = LCB_EINVAL; + return; + } + + // Determine if we need to add more credentials. + // Because N1QL multi-bucket auth will not work on server versions < 4.5 + // using JSON encoding, we need to only use the multi-bucket auth feature + // if there are actually multiple credentials to employ. + const lcb::Authenticator& auth = *instance->settings->auth; + if (auth.buckets().size() > 1 && (cmd->cmdflags & LCB_CMD_F_MULTIAUTH)) { + flags |= F_CMDN1QL_CREDSAUTH; + Json::Value& creds = json["creds"]; + lcb::Authenticator::Map::const_iterator ii = auth.buckets().begin(); + if (! (creds.isNull() || creds.isArray())) { + lasterr = LCB_EINVAL; + return; + } + for (; ii != auth.buckets().end(); ++ii) { + if (ii->second.empty()) { + continue; + } + Json::Value& curCreds = creds.append(Json::Value(Json::objectValue)); + curCreds["user"] = ii->first; + curCreds["pass"] = ii->second; + } + } +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_n1ql_query(lcb_t instance, const void *cookie, const lcb_CMDN1QL *cmd) +{ + lcb_error_t err; + N1QLREQ *req = NULL; + + if (cmd->query == NULL || cmd->callback == NULL) { + return LCB_EINVAL; + } + req = new lcb_N1QLREQ(instance, cookie, cmd); + if (!req) { + err = LCB_CLIENT_ENOMEM; + goto GT_DESTROY; + } + if ((err = req->lasterr) != LCB_SUCCESS) { + goto GT_DESTROY; + } + + if (cmd->cmdflags & LCB_CMDN1QL_F_PREPCACHE) { + if (req->statement.empty()) { + err = LCB_EINVAL; + goto GT_DESTROY; + } + + const Plan *cached = req->cache().get_entry(req->statement); + if (cached != NULL) { + if ((err = req->apply_plan(*cached)) != LCB_SUCCESS) { + goto GT_DESTROY; + } + } else { + lcb_log(LOGARGS(req, DEBUG), LOGFMT "No cached plan found. Issuing prepare", LOGID(req)); + if ((err = req->request_plan()) != LCB_SUCCESS) { + goto GT_DESTROY; + } + } + } else { + // No prepare + if ((err = req->issue_htreq()) != LCB_SUCCESS) { + goto GT_DESTROY; + } + } + + return LCB_SUCCESS; + + GT_DESTROY: + if (cmd->handle) { + *cmd->handle = NULL; + } + + if (req) { + req->callback = NULL; + delete req; + } + return err; +} + +LIBCOUCHBASE_API +void +lcb_n1ql_cancel(lcb_t instance, lcb_N1QLHANDLE handle) +{ + // Note that this function is just an elaborate way to nullify the + // callback. We are very particular about _not_ cancelling the underlying + // http request, because the handle's deletion is controlled + // from the HTTP callback, which checks if the callback is NULL before + // deleting. + // at worst, deferring deletion to the http response might cost a few + // extra network reads; whereas this function itself is intended as a + // bailout for unexpected destruction. + + if (handle->prepare_req) { + lcb_n1ql_cancel(instance, handle->prepare_req); + handle->prepare_req = NULL; + } + handle->callback = NULL; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/n1ql/params.cc b/couchbase-sys/libcouchbase-2.7.0/src/n1ql/params.cc new file mode 100644 index 00000000..2bd1dd5f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/n1ql/params.cc @@ -0,0 +1,215 @@ +#include +#include +#include +#include "contrib/lcb-jsoncpp/lcb-jsoncpp.h" +#include + +#define SCANVEC_NONE 0 +#define SCANVEC_PARTIAL 1 +#define SCANVEC_FULL 2 + +struct lcb_N1QLPARAMS_st { + Json::Value root; + std::string encoded; + + lcb_N1QLPARAMS_st() : root(Json::objectValue) { + } +}; + +extern "C" { +static size_t get_strlen(const char *s, size_t n) +{ + if (n == (size_t)-1) { + return strlen(s); + } else { + return n; + } +} + +static lcb_error_t +setopt(lcb_N1QLPARAMS *params, const char *k, size_t nk, const char *v, size_t nv) +{ + nv = get_strlen(v, nv); + nk = get_strlen(k, nk); + Json::Reader rdr; + Json::Value value; + bool rv = rdr.parse(v, v + nv, value); + if (!rv) { + return LCB_EINVAL; + } + + params->root[std::string(k, nk)] = value; + return LCB_SUCCESS; +} + +lcb_error_t +lcb_n1p_setopt(lcb_N1QLPARAMS *params, + const char *k, size_t nk, const char *v, size_t nv) +{ + return setopt(params, k, nk, v, nv); +} + +lcb_error_t +lcb_n1p_setquery(lcb_N1QLPARAMS *params, + const char *qstr, size_t nqstr, int type) +{ + if (type == LCB_N1P_QUERY_STATEMENT) { + size_t nstmt = get_strlen(qstr, nqstr); + params->root["statement"] = std::string(qstr, nstmt); + return LCB_SUCCESS; + } else if (type == LCB_N1P_QUERY_PREPARED) { + return lcb_n1p_setopt(params, "prepared", -1, qstr, nqstr); + } else { + return LCB_EINVAL; + } +} + +lcb_error_t +lcb_n1p_namedparam(lcb_N1QLPARAMS *params, + const char *name, size_t nname, const char *value, size_t nvalue) +{ + return lcb_n1p_setopt(params, name, nname, value, nvalue); +} + +lcb_error_t +lcb_n1p_posparam(lcb_N1QLPARAMS *params, const char *value, size_t nvalue) +{ + nvalue = get_strlen(value, nvalue); + Json::Value jval; + Json::Reader rdr; + if (!rdr.parse(value, value + nvalue, jval)) { + return LCB_EINVAL; + } + params->root["args"].append(jval); + return LCB_SUCCESS; +} + +static void +encode_mutation_token(Json::Value& sparse, const lcb_MUTATION_TOKEN *sv) +{ + char buf[64] = { 0 }; + sprintf(buf, "%u", sv->vbid_); + Json::Value& cur_sv = sparse[buf]; + + cur_sv[0] = static_cast(sv->seqno_); + sprintf(buf, "%llu", sv->uuid_); + cur_sv[1] = buf; +} + +lcb_error_t +lcb_n1p_setconsistent_token(lcb_N1QLPARAMS *params, + const char *keyspace, const lcb_MUTATION_TOKEN *sv) +{ + if (!LCB_MUTATION_TOKEN_ISVALID(sv)) { + return LCB_EINVAL; + } + + params->root["scan_consistency"] = "at_plus"; + encode_mutation_token(params->root["scan_vectors"][keyspace], sv); + return LCB_SUCCESS; +} + +lcb_error_t +lcb_n1p_setconsistent_handle(lcb_N1QLPARAMS *params, lcb_t instance) +{ + lcbvb_CONFIG *vbc; + lcb_error_t rc = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_VBCONFIG, &vbc); + if (rc != LCB_SUCCESS) { + return rc; + } + + const char *bucketname; + rc = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_BUCKETNAME, &bucketname); + if (rc != LCB_SUCCESS) { + return rc; + } + + Json::Value* sv_json = NULL; + + size_t vbmax = vbc->nvb; + for (size_t ii = 0; ii < vbmax; ++ii) { + lcb_KEYBUF kb; + kb.type = LCB_KV_VBID; + kb.contig.nbytes = ii; + kb.contig.bytes = NULL; + const lcb_MUTATION_TOKEN *mt = lcb_get_mutation_token(instance, &kb, &rc); + if (rc == LCB_SUCCESS && mt != NULL) { + if (sv_json == NULL) { + sv_json = ¶ms->root["scan_vectors"][bucketname]; + params->root["scan_consistency"] = "at_plus"; + } + encode_mutation_token(*sv_json, mt); + } + } + + if (!sv_json) { + return LCB_KEY_ENOENT; + } + + return LCB_SUCCESS; +} + +lcb_error_t +lcb_n1p_setconsistency(lcb_N1QLPARAMS *params, int mode) +{ + if (mode == LCB_N1P_CONSISTENCY_NONE) { + params->root.removeMember("scan_consistency"); + } else if (mode == LCB_N1P_CONSISTENCY_REQUEST) { + params->root["scan_consistency"] = "request_plus"; + } else if (mode == LCB_N1P_CONSISTENCY_STATEMENT) { + params->root["scan_consistency"] = "statement_plus"; + } + return LCB_SUCCESS; +} + +LIBCOUCHBASE_API +const char * +lcb_n1p_encode(lcb_N1QLPARAMS *params, lcb_error_t *err) +{ + lcb_error_t err_s = LCB_SUCCESS; + if (!err) { + err = &err_s; + } + + *err = LCB_SUCCESS; + /* Build the query */ + Json::FastWriter w; + params->encoded = w.write(params->root); + return params->encoded.c_str(); +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_n1p_mkcmd(lcb_N1QLPARAMS *params, lcb_CMDN1QL *cmd) +{ + lcb_error_t rc = LCB_SUCCESS; + lcb_n1p_encode(params, &rc); + if (rc != LCB_SUCCESS) { + return rc; + } + cmd->content_type = "application/json"; + cmd->query = params->encoded.c_str(); + cmd->nquery = params->encoded.size(); + return LCB_SUCCESS; +} + +lcb_N1QLPARAMS * +lcb_n1p_new(void) +{ + return new lcb_N1QLPARAMS; +} + +void +lcb_n1p_reset(lcb_N1QLPARAMS *params) +{ + params->encoded.clear(); + params->root.clear(); +} + +void +lcb_n1p_free(lcb_N1QLPARAMS *params) +{ + delete params; +} + +} // extern C diff --git a/couchbase-sys/libcouchbase-2.7.0/src/netbuf/netbuf-defs.h b/couchbase-sys/libcouchbase-2.7.0/src/netbuf/netbuf-defs.h new file mode 100644 index 00000000..512d32cc --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/netbuf/netbuf-defs.h @@ -0,0 +1,89 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NETBUF_DEFS_H +#define NETBUF_DEFS_H + +typedef struct netbuf_st nb_MGR; +typedef unsigned int nb_SIZE; + +/** + * @file + * Netbuf Core Structures + * @addtogroup netbufs + * @{ + */ + +/** + * @name Cache Allocation + * @{ + * + * The following settings control the default allocation policy. + * Each allocator pool has both blocks and the amount of data per block. + * + * Multiple blocks help with cache locality when traversing, while large + * data segements allow each individual element to be spaced near the next. + */ + +/** @brief How many blocks to preallocate for SNDQ elements, per manager */ +#define NB_SNDQ_CACHEBLOCKS 4 +/** @brief How many SNDQELEM structures per block */ +#define NB_SNDQ_BASEALLOC 128 + + +/** @brief How many dealloc blocks to allocated per MBLOCK */ +#define NB_MBDEALLOC_CACHEBLOCKS 0 +/** @brief Number of dealloc structures per block */ +#define NB_MBDEALLOC_BASEALLOC 24 + + +/** @brief How many data blocks to allocate per manager */ +#define NB_DATA_CACHEBLOCKS 16 +/** @brief Default data allocation size */ +#define NB_DATA_BASEALLOC 32768 +/**@}*/ + +typedef struct { + nb_SIZE sndq_cacheblocks; + nb_SIZE sndq_basealloc; + nb_SIZE dea_cacheblocks; + nb_SIZE dea_basealloc; + nb_SIZE data_cacheblocks; + nb_SIZE data_basealloc; +} nb_SETTINGS; + +#ifndef _WIN32 +typedef struct { + void *iov_base; + size_t iov_len; +} nb_IOV; + +/**Macro which serves as a static initializer for an nb_IOV. This works + * on both Windows and Unix despite the layout of the structure being different + */ +#define NETBUF_IOV_INIT(base, len) { base, len } +#else +typedef struct { + ULONG iov_len; + void *iov_base; +} nb_IOV; +#define NETBUF_IOV_INIT(base, len) { len, base } +#endif + +/**@}*/ + +#endif /* NETBUF_DEFS_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/netbuf/netbuf-mblock.h b/couchbase-sys/libcouchbase-2.7.0/src/netbuf/netbuf-mblock.h new file mode 100644 index 00000000..664b2bdf --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/netbuf/netbuf-mblock.h @@ -0,0 +1,235 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NETBUF_MBLOCK_H +#define NETBUF_MBLOCK_H + +#include "netbuf-defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * + * @ingroup netbufs + * @defgroup netbufs-mblock Netbuf Block Allocator + * @details + * + * Managed block in-order allocator. + * + * This allocator attempts to provide unaligned segments of memory in the + * order they were allocated in contiguous memory + * + * @verbatim + * + * LEGEND + * In the following comments (and within the source as well) we will try to + * display diagrams of blocks. The following symbols will be used: + * + * {$:NN} = This represents a position marker, $ will be the position type, + * and NN is the offset value. + * + * The following are the position types: + * + * [S]tart Start of the buffer (block->start) + * [W]rap Wrapping and end of the first segment (block->wrap) + * [C]ursor End of the current segment (block->cursor) + * [A]lloc Allocation limit of the buffer (block->nalloc) + * [F]lush Flush cursor (block->flushcur) + * + * Note that in some cases two position types may share the same offset. + * + * Between any of the offsets, there are data bytes (or just "Data"). These + * may be one of the following: + * + * [x] Used data. This data is owned by a span + * [o] Unused data, but available for usage + * [-] Unreachable data. This is not used but cannot be reserved + * + * A block contains a single allocated buffer. The buffer itself may be + * divided among multiple spans. We divide our buffers like so: + * + * Initially: + * + * [ {FS:0}xxxxxxx{CW:10}ooo{A:12} ] + * + * After flushing some data: + * + * [ {S:0}xx{F:5}xxxx{CW:10}oo{A:12} ] + * Note how the flush cursor is incremented + * + * + * Typically, once data is flushed, the user will release the segment, and thus + * will look something like this: + * + * [ ooo{SF:6}xxxx{CW:10}oooo{A:12} ] + * + * Appending data to a buffer (or reserving a span) depends on the span + * size requirements. In this case, if a span's size is 2 bytes or lower, + * it is appended at the end of the first segment, like so: + * [ ooo{SF:16}xxxxxx{CWA:12} ] + * + * Otherwise, it is wrapped around, like so: + * + * [ xx{C:3}oo{SF:6}xxxx{W:10}--{A:12} ] + * + * Note that [C] has been wrapped around to start at 3. + * + * + * The total size of the block's used portion is as follows: + * + * (1) The number of bytes between [S]tart and [Wrap] + * (2) If [C] != [W], then also add the value of [C] + * @endverbatim + * @addtogroup netbufs-mblock + * @{ + */ + +struct netbuf_mblock_st; +struct netbuf_st; +struct netbuf_mblock_dealloc_queue_st; + +/** + * Small header for larger structures to more efficiently find the block + * they were allocated in. + * + * Note that it is possible to also determine this information by traversing + * the list of all blocks, but this is naturally less efficient. + */ +typedef struct { + /** The parent block */ + struct netbuf_mblock_st *parent; + /** The allocation offset */ + nb_SIZE offset; +} nb_ALLOCINFO; + +/** + * @brief Structure for an out-of-order dealloc + */ +typedef struct { + sllist_node slnode; + nb_SIZE offset; /**< Offset into the nb_MBLOCK to release */ + nb_SIZE size; /**< Size to release */ +} nb_QDEALLOC; + +/** + * @brief Data Block + * This structure represents the head of an `MBLOCK`. + */ +typedef struct { + sllist_node slnode; + + /** Start position for data */ + nb_SIZE start; + + /** + * Wrap/End position for data. If the block has only one segment, + * this is always equal to cursor (see below) + * and will mark the position at which the unused portion of the + * buffer begins. + * + * If the block has two segments, this marks the end of the first segment. + * + * In both cases + * 1. `wrap` is always `> start` + * 2. `wrap-start` is the length of the first segment of data + */ + nb_SIZE wrap; + + /** + * End position for data. This always contains the position at which + * the unused data begins. + * + * If the block only has a single segment then both the following are true: + * + * 1. `cursor == wrap` + * 2. `cursor > start` (if not empty) + * + * If the block has two segments, then both the following are true: + * + * 1. `cursor != wrap` + * 2. `cursor < start` + * + * If the block is empty: + * - `cursor == start` + */ + nb_SIZE cursor; + + /** + * Total number of bytes allocated in `root`. This represents the absolute + * limit on how much data can be supplied + */ + nb_SIZE nalloc; + + /** + * Actual allocated buffer. This remains constant for the duration of the + * block's lifetime + */ + char *root; + + /** + * Pointer to a nb_DEALLOC_QUEUE structure. This is only valid if an + * out-of-order dealloc has been performed on this block. + */ + struct netbuf_mblock_dealloc_queue_st *deallocs; + struct netbuf_mblock_st *parent; +} nb_MBLOCK; + +/** + * @brief pool of nb_MBLOCK structures + */ +typedef struct netbuf_mblock_st { + /** Active blocks that have at least one reserved span */ + sllist_root active; + + /** Available blocks with data */ + sllist_root avail; + + /** Allocation size */ + nb_SIZE basealloc; + + /** Maximum number of non-cached blocks */ + unsigned int maxblocks; + + /** Current number of non-cached blocks */ + unsigned int curblocks; + + nb_MBLOCK *cacheblocks; + nb_SIZE ncacheblocks; + + struct netbuf_st *mgr; +} nb_MBPOOL; + +/** + * @brief List of out-of-order deallocs + * This is attached to an nb_MBLOCK structure if allocations have been performed + * on it in an out-of-order fashion + */ +typedef struct netbuf_mblock_dealloc_queue_st { + sllist_root pending; + nb_SIZE min_offset; /**< The first offset contained in the list */ + nb_MBPOOL qpool; /**< Used to allcate the nb_QDEALLOC structures themselves*/ +} nb_DEALLOC_QUEUE; + +/**@}*/ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/netbuf/netbuf.c b/couchbase-sys/libcouchbase-2.7.0/src/netbuf/netbuf.c new file mode 100644 index 00000000..41d30447 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/netbuf/netbuf.c @@ -0,0 +1,929 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +/* for ULONG */ +#include +#endif + +#include +#include +#include +#include + +#include "netbuf.h" +#include "sllist-inl.h" + +#ifndef lcb_assert +#include +#define lcb_assert assert +#endif + +/****************************************************************************** + ****************************************************************************** + ** Handy Macros ** + ****************************************************************************** + ******************************************************************************/ +#define MINIMUM(a, b) a < b ? a : b +#define MAXIMUM(a, b) a > b ? a : b + +#define BLOCK_IS_EMPTY(block) ((block)->start == (block)->cursor) + +#define FIRST_BLOCK(pool) \ + (SLLIST_ITEM(SLLIST_FIRST(&(pool)->active), nb_MBLOCK, slnode)) + +#define LAST_BLOCK(mgr) \ + (SLLIST_ITEM((mgr)->active_blocks.last, nb_BLOCKHDR, slnode)) + +#define NEXT_BLOCK(block) \ + (SLLIST_ITEM((block)->slnode.next, nb_BLOCKHDR, slnode)) + +#define BLOCK_HAS_DEALLOCS(block) \ + ((block)->deallocs && SLLIST_IS_EMPTY(&(block)->deallocs->pending)) + +/** Static forward decls */ +static void mblock_release_data(nb_MBPOOL*,nb_MBLOCK*,nb_SIZE,nb_SIZE); +static void mblock_release_ptr(nb_MBPOOL*,char*,nb_SIZE); +static void mblock_init(nb_MBPOOL*); +static void mblock_cleanup(nb_MBPOOL*); +static void mblock_wipe_block(nb_MBLOCK *block); + +/****************************************************************************** + ****************************************************************************** + ** Allocation/Reservation ** + ****************************************************************************** + ******************************************************************************/ + +/** + * Determines whether the block is allocated as a standalone block, or if it's + * part of a larger allocation + */ +static int +mblock_is_standalone(nb_MBLOCK *block) +{ + return block->parent == NULL; +} + +/** + * Allocates a new block with at least the given capacity and places it + * inside the active list. + */ +static nb_MBLOCK* +alloc_new_block(nb_MBPOOL *pool, nb_SIZE capacity) +{ + unsigned int ii; + nb_MBLOCK *ret = NULL; + + for (ii = 0; ii < pool->ncacheblocks; ii++) { + if (!pool->cacheblocks[ii].nalloc) { + ret = pool->cacheblocks + ii; + break; + } + } + + if (!ret) { + ret = calloc(1, sizeof(*ret)); + } + + if (!ret) { + return NULL; + } + + ret->nalloc = pool->basealloc; + + while (ret->nalloc < capacity) { + ret->nalloc *= 2; + } + + ret->wrap = 0; + ret->cursor = 0; + ret->root = malloc(ret->nalloc); + + if (!ret->root) { + if (mblock_is_standalone(ret)) { + free(ret); + } + return NULL; + } + + return ret; +} + +/** + * Finds an available block within the available list. The block will have + * room for at least capacity bytes. + */ +static nb_MBLOCK* +find_free_block(nb_MBPOOL *pool, nb_SIZE capacity) +{ + sllist_iterator iter; + SLLIST_ITERFOR(&pool->avail, &iter) { + nb_MBLOCK *cur = SLLIST_ITEM(iter.cur, nb_MBLOCK, slnode); + if (cur->nalloc >= capacity) { + sllist_iter_remove(&pool->avail, &iter); + pool->curblocks--; + return cur; + } + } + + return NULL; +} + +/** + * Find a new block for the given span and initialize it for a reserved size + * correlating to the span. + * The block may either be popped from the available section or allocated + * as a standalone depending on current constraints. + */ +static int +reserve_empty_block(nb_MBPOOL *pool, nb_SPAN *span) +{ + nb_MBLOCK *block; + + if ( (block = find_free_block(pool, span->size)) == NULL) { + block = alloc_new_block(pool, span->size); + } + + if (!block) { + return -1; + } + + span->parent = block; + span->offset = 0; + block->start = 0; + block->wrap = span->size; + block->cursor = span->size; + + block->deallocs = NULL; + + sllist_append(&pool->active, &block->slnode); + return 0; +} + +/** + * Attempt to reserve space from the currently active block for the given + * span. + * @return 0 if the active block had enough space and the span was initialized + * and nonzero otherwise. + */ +static int +reserve_active_block(nb_MBLOCK *block, nb_SPAN *span) +{ + if (BLOCK_HAS_DEALLOCS(block)) { + return -1; + } + + if (block->cursor > block->start) { + if (block->nalloc - block->cursor >= span->size) { + span->offset = block->cursor; + block->cursor += span->size; + block->wrap = block->cursor; + return 0; + + } else if (block->start >= span->size) { + /** Wrap around the wrap */ + span->offset = 0; + block->cursor = span->size; + return 0; + } else { + return -1; + } + + } else { + /* Already wrapped */ + if (block->start - block->cursor >= span->size) { + span->offset = block->cursor; + block->cursor += span->size; + return 0; + } else { + return -1; + } + } +} + +static int +mblock_reserve_data(nb_MBPOOL *pool, nb_SPAN *span) +{ + nb_MBLOCK *block; + int rv; + +#ifdef NETBUF_LIBC_PROXY + block = malloc(sizeof(*block) + span->size); + block->root = ((char *)block) + sizeof(*block); + span->parent = block; + span->offset = 0; + return 0; +#endif + + if (SLLIST_IS_EMPTY(&pool->active)) { + return reserve_empty_block(pool, span); + + } else { + block = SLLIST_ITEM(pool->active.last, nb_MBLOCK, slnode); + rv = reserve_active_block(block, span); + + if (rv != 0) { + return reserve_empty_block(pool, span); + } + + span->parent = block; + return rv; + } +} + +/****************************************************************************** + ****************************************************************************** + ** Out-Of-Order Deallocation Functions ** + ****************************************************************************** + ******************************************************************************/ +static void +ooo_queue_dealoc(nb_MGR *mgr, nb_MBLOCK *block, nb_SPAN *span) +{ + nb_QDEALLOC *qd; + nb_DEALLOC_QUEUE *queue; + nb_SPAN qespan; + + if (!block->deallocs) { + queue = calloc(1, sizeof(*queue)); + queue->qpool.basealloc = sizeof(*qd) * mgr->settings.dea_basealloc; + queue->qpool.ncacheblocks = mgr->settings.dea_cacheblocks; + queue->qpool.mgr = mgr; + mblock_init(&queue->qpool); + block->deallocs = queue; + } + + queue = block->deallocs; + + if (SLLIST_IS_EMPTY(&queue->pending)) { + queue->min_offset = span->offset; + } + + qespan.size = sizeof(*qd); + mblock_reserve_data(&queue->qpool, &qespan); + + qd = (nb_QDEALLOC *)(void *)SPAN_MBUFFER_NC(&qespan); + qd->offset = span->offset; + qd->size = span->size; + if (queue->min_offset > qd->offset) { + queue->min_offset = qd->offset; + } + sllist_append(&queue->pending, &qd->slnode); +} + +static INLINE void +maybe_unwrap_block(nb_MBLOCK *block) +{ + if (!BLOCK_IS_EMPTY(block) && block->start == block->wrap) { + block->wrap = block->cursor; + block->start = 0; + } +} + +static void +ooo_apply_dealloc(nb_MBLOCK *block) +{ + nb_SIZE min_next = -1; + sllist_iterator iter; + nb_DEALLOC_QUEUE *queue = block->deallocs; + + SLLIST_ITERFOR(&queue->pending, &iter) { + nb_QDEALLOC *cur = SLLIST_ITEM(iter.cur, nb_QDEALLOC, slnode); + if (cur->offset == block->start) { + block->start += cur->size; + maybe_unwrap_block(block); + + sllist_iter_remove(&block->deallocs->pending, &iter); + mblock_release_ptr(&queue->qpool, (char *)cur, sizeof(*cur)); + } else if (cur->offset < min_next) { + min_next = cur->offset; + } + } + queue->min_offset = min_next; +} + + +static INLINE void +mblock_release_data(nb_MBPOOL *pool, + nb_MBLOCK *block, nb_SIZE size, nb_SIZE offset) +{ + if (offset == block->start) { + /** Removing from the beginning */ + block->start += size; + + if (block->deallocs && block->deallocs->min_offset == block->start) { + ooo_apply_dealloc(block); + } + + maybe_unwrap_block(block); + + } else if (offset + size == block->cursor) { + /** Removing from the end */ + if (block->cursor == block->wrap) { + /** Single region, no wrap */ + block->cursor -= size; + block->wrap -= size; + + } else { + block->cursor -= size; + if (!block->cursor) { + /** End has reached around */ + block->cursor = block->wrap; + } + } + + } else { + nb_SPAN span; + + span.parent = block; + span.offset = offset; + span.size = size; + ooo_queue_dealoc(pool->mgr, block, &span); + return; + } + + if (!BLOCK_IS_EMPTY(block)) { + return; + } + + { + sllist_iterator iter; + SLLIST_ITERFOR(&pool->active, &iter) { + if (&block->slnode == iter.cur) { + sllist_iter_remove(&pool->active, &iter); + break; + } + } + } + + if (pool->curblocks < pool->maxblocks) { + sllist_append(&pool->avail, &block->slnode); + pool->curblocks++; + } else { + mblock_wipe_block(block); + } +} + +static void +mblock_release_ptr(nb_MBPOOL *pool, char * ptr, nb_SIZE size) +{ + nb_MBLOCK *block; + nb_SIZE offset; + sllist_node *ll; + +#ifdef NETBUF_LIBC_PROXY + block = (nb_MBLOCK *)(ptr - sizeof(*block)); + free(block); + return; +#endif + + + SLLIST_FOREACH(&pool->active, ll) { + block = SLLIST_ITEM(ll, nb_MBLOCK, slnode); + if (block->root > ptr) { + continue; + } + if (block->root + block->nalloc <= ptr) { + continue; + } + offset = ptr - block->root; + mblock_release_data(pool, block, size, offset); + return; + } + + fprintf(stderr, "NETBUF: Requested to release pointer %p which was not allocated\n", ptr); + assert(0); +} + +static int +mblock_get_next_size(const nb_MBPOOL *pool, int allow_wrap) +{ + nb_MBLOCK *block; + if (SLLIST_IS_EMPTY(&pool->avail)) { + return 0; + } + + block = FIRST_BLOCK(pool); + + if (BLOCK_HAS_DEALLOCS(block)) { + return 0; + } + + if (!block->start) { + /** Plain 'ole buffer */ + return block->nalloc - block->cursor; + } + + if (block->cursor != block->wrap) { + /** Already in second region */ + return block->start - block->cursor; + } + + if (allow_wrap) { + return MINIMUM(block->nalloc - block->wrap, block->start); + } + + return block->nalloc - block->wrap; +} + +static void +mblock_wipe_block(nb_MBLOCK *block) +{ + if (block->root) { + free(block->root); + } + if (block->deallocs) { + sllist_iterator dea_iter; + nb_DEALLOC_QUEUE *queue = block->deallocs; + + SLLIST_ITERFOR(&queue->pending, &dea_iter) { + nb_QDEALLOC *qd = SLLIST_ITEM(dea_iter.cur, nb_QDEALLOC, slnode); + sllist_iter_remove(&queue->pending, &dea_iter); + mblock_release_ptr(&queue->qpool, (char *)qd, sizeof(*qd)); + } + + mblock_cleanup(&queue->qpool); + free(queue); + block->deallocs = NULL; + } + + if (mblock_is_standalone(block)) { + free(block); + } +} + +static void +free_blocklist(nb_MBPOOL *pool, sllist_root *list) +{ + sllist_iterator iter; + SLLIST_ITERFOR(list, &iter) { + nb_MBLOCK *block = SLLIST_ITEM(iter.cur, nb_MBLOCK, slnode); + sllist_iter_remove(list, &iter); + mblock_wipe_block(block); + } + (void)pool; +} + + +static void +mblock_cleanup(nb_MBPOOL *pool) +{ + free_blocklist(pool, &pool->active); + free_blocklist(pool, &pool->avail); + free(pool->cacheblocks); +} + +static void +mblock_init(nb_MBPOOL *pool) +{ + unsigned int ii; + pool->cacheblocks = calloc(pool->ncacheblocks, sizeof(*pool->cacheblocks)); + for (ii = 0; ii < pool->ncacheblocks; ii++) { + pool->cacheblocks[ii].parent = pool; + } + if (pool->ncacheblocks) { + pool->maxblocks = pool->ncacheblocks * 2; + } +} + +int +netbuf_mblock_reserve(nb_MGR *mgr, nb_SPAN *span) +{ + return mblock_reserve_data(&mgr->datapool, span); +} + +/****************************************************************************** + ****************************************************************************** + ** Informational Routines ** + ****************************************************************************** + ******************************************************************************/ +nb_SIZE +netbuf_mblock_get_next_size(const nb_MGR *mgr, int allow_wrap) +{ + return mblock_get_next_size(&mgr->datapool, allow_wrap); +} + +unsigned int +netbuf_get_niov(nb_MGR *mgr) +{ + sllist_node *ll; + unsigned int ret = 0; + SLLIST_FOREACH(&mgr->sendq.pending, ll) { + ret++; + } + + return ret; +} + +/****************************************************************************** + ****************************************************************************** + ** Flush Routines ** + ****************************************************************************** + ******************************************************************************/ +static nb_SNDQELEM * +get_sendqe(nb_SENDQ* sq, const nb_IOV *bufinfo) +{ + nb_SNDQELEM *sndqe; + nb_SPAN span; + span.size = sizeof(*sndqe); + mblock_reserve_data(&sq->elempool, &span); + sndqe = (nb_SNDQELEM *)(void *)SPAN_MBUFFER_NC(&span); + + sndqe->base = bufinfo->iov_base; + sndqe->len = bufinfo->iov_len; + return sndqe; +} + +void +netbuf_enqueue(nb_MGR *mgr, const nb_IOV *bufinfo) +{ + nb_SENDQ *q = &mgr->sendq; + nb_SNDQELEM *win; + + if (SLLIST_IS_EMPTY(&q->pending)) { + win = get_sendqe(q, bufinfo); + sllist_append(&q->pending, &win->slnode); + + } else { + win = SLLIST_ITEM(q->pending.last, nb_SNDQELEM, slnode); + if (win->base + win->len == bufinfo->iov_base) { + win->len += bufinfo->iov_len; + + } else { + win = get_sendqe(q, bufinfo); + sllist_append(&q->pending, &win->slnode); + } + } +} + +void +netbuf_enqueue_span(nb_MGR *mgr, nb_SPAN *span) +{ + nb_IOV spinfo; + spinfo.iov_base = SPAN_BUFFER(span); + spinfo.iov_len = span->size; + netbuf_enqueue(mgr, &spinfo); +} + +nb_SIZE +netbuf_start_flush(nb_MGR *mgr, nb_IOV *iovs, int niov, int *nused) +{ + nb_SIZE ret = 0; + nb_IOV *iov_end = iovs + niov, *iov_start = iovs; + nb_IOV *iov = iovs; + sllist_node *ll; + nb_SENDQ *sq = &mgr->sendq; + nb_SNDQELEM *win = NULL; + + if (sq->last_requested) { + if (sq->last_offset != sq->last_requested->len) { + win = sq->last_requested; + assert(win->len > sq->last_offset); + + iov->iov_len = win->len - sq->last_offset; + iov->iov_base = win->base + sq->last_offset; + ret += iov->iov_len; + iov++; + } + + ll = sq->last_requested->slnode.next; + + } else { + ll = SLLIST_FIRST(&sq->pending); + } + + while (ll && iov != iov_end) { + win = SLLIST_ITEM(ll, nb_SNDQELEM, slnode); + iov->iov_len = win->len; + iov->iov_base = win->base; + + ret += iov->iov_len; + iov++; + ll = ll->next; + } + + if (win) { + sq->last_requested = win; + sq->last_offset = win->len; + } + if (ret && nused) { + *nused = iov - iov_start; + } + + return ret; +} + +void +netbuf_end_flush(nb_MGR *mgr, unsigned int nflushed) +{ + nb_SENDQ *q = &mgr->sendq; + sllist_iterator iter; + SLLIST_ITERFOR(&q->pending, &iter) { + nb_SNDQELEM *win = SLLIST_ITEM(iter.cur, nb_SNDQELEM, slnode); + nb_SIZE to_chop = MINIMUM(win->len, nflushed); + + win->len -= to_chop; + nflushed -= to_chop; + + if (!win->len) { + sllist_iter_remove(&q->pending, &iter); + mblock_release_ptr(&mgr->sendq.elempool, (char *)win, sizeof(*win)); + if (win == q->last_requested) { + q->last_requested = NULL; + q->last_offset = 0; + } + } else { + win->base += to_chop; + if (win == q->last_requested) { + q->last_offset -= to_chop; + } + } + + if (!nflushed) { + break; + } + } + assert(!nflushed); +} + +void +netbuf_pdu_enqueue(nb_MGR *mgr, void *pdu, nb_SIZE lloff) +{ + nb_SENDQ *q = &mgr->sendq; + sllist_append(&q->pdus, (sllist_node *) (void *)( (char *)pdu + lloff)); +} + +void +netbuf_end_flush2(nb_MGR *mgr, + unsigned int nflushed, + nb_getsize_fn callback, + nb_SIZE lloff, + void *arg) +{ + sllist_iterator iter; + nb_SENDQ *q = &mgr->sendq; + netbuf_end_flush(mgr, nflushed); + + /** Add to the nflushed overflow from last call */ + nflushed += q->pdu_offset; + SLLIST_ITERFOR(&q->pdus, &iter) { + nb_SIZE cursize; + char *ptmp = (char *)iter.cur; + cursize = callback(ptmp - lloff, nflushed, arg); + + if (cursize > nflushed) { + break; + } + + nflushed -= cursize; + sllist_iter_remove(&q->pdus, &iter); + + if (!nflushed) { + break; + } + } + + /** Store the remainder of data that wasn't processed for next call */ + q->pdu_offset = nflushed; +} + +/****************************************************************************** + ****************************************************************************** + ** Release ** + ****************************************************************************** + ******************************************************************************/ +void +netbuf_mblock_release(nb_MGR *mgr, nb_SPAN *span) +{ +#ifdef NETBUF_LIBC_PROXY + free(span->parent); + (void)mgr; +#else + mblock_release_data(&mgr->datapool, span->parent, span->size, span->offset); +#endif +} + +/****************************************************************************** + ****************************************************************************** + ** Init/Cleanup ** + ****************************************************************************** + ******************************************************************************/ +void netbuf_default_settings(nb_SETTINGS *settings) +{ + settings->data_basealloc = NB_DATA_BASEALLOC; + settings->data_cacheblocks = NB_DATA_CACHEBLOCKS; + settings->dea_basealloc = NB_MBDEALLOC_BASEALLOC; + settings->dea_cacheblocks = NB_MBDEALLOC_CACHEBLOCKS; + settings->sndq_basealloc = NB_SNDQ_BASEALLOC; + settings->sndq_cacheblocks = NB_SNDQ_CACHEBLOCKS; +} + +void +netbuf_init(nb_MGR *mgr, const nb_SETTINGS *user_settings) +{ + nb_MBPOOL *sqpool = &mgr->sendq.elempool; + nb_MBPOOL *bufpool = &mgr->datapool; + + memset(mgr, 0, sizeof(*mgr)); + + if (user_settings) { + mgr->settings = *user_settings; + } else { + netbuf_default_settings(&mgr->settings); + } + + /** Set our defaults */ + sqpool->basealloc = sizeof(nb_SNDQELEM) * mgr->settings.sndq_basealloc; + sqpool->ncacheblocks = mgr->settings.sndq_cacheblocks; + sqpool->mgr = mgr; + mblock_init(sqpool); + + bufpool->basealloc = mgr->settings.data_basealloc; + bufpool->ncacheblocks = mgr->settings.data_cacheblocks; + bufpool->mgr = mgr; + mblock_init(bufpool); +} + + +void +netbuf_cleanup(nb_MGR *mgr) +{ + sllist_iterator iter; + + SLLIST_ITERFOR(&mgr->sendq.pending, &iter) { + nb_SNDQELEM *e = SLLIST_ITEM(iter.cur, nb_SNDQELEM, slnode); + sllist_iter_remove(&mgr->sendq.pending, &iter); + mblock_release_ptr(&mgr->sendq.elempool, (char *)e, sizeof(*e)); + } + + mblock_cleanup(&mgr->sendq.elempool); + mblock_cleanup(&mgr->datapool); +} + +/****************************************************************************** + ****************************************************************************** + ** Block Dumping ** + ****************************************************************************** + ******************************************************************************/ + +static void +dump_managed_block(nb_MBLOCK *block, FILE *fp) +{ + const char *indent = " "; + fprintf(fp, "%sBLOCK(MANAGED)=%p; BUF=%p, %uB\n", indent, + (void *)block, block->root, block->nalloc); + indent = " "; + + fprintf(fp, "%sUSAGE:\n", indent); + fprintf(fp, "%s", indent); + if (BLOCK_IS_EMPTY(block)) { + fprintf(fp, "EMPTY\n"); + return; + } + + printf("["); + + if (block->cursor == block->wrap) { + if (block->start) { + fprintf(fp, "ooo{S:%u}xxx", block->start); + } else { + fprintf(fp, "{S:0}xxxxxx"); + } + + if (block->nalloc > block->cursor) { + fprintf(fp, "{CW:%u}ooo{A:%u}", block->cursor, block->nalloc); + } else { + fprintf(fp, "xxx{CWA:%u)}", block->cursor); + } + } else { + fprintf(fp, "xxx{C:%u}ooo{S:%u}xxx", block->cursor, block->start); + if (block->wrap != block->nalloc) { + fprintf(fp, "{W:%u}ooo{A:%u}", block->wrap, block->nalloc); + } else { + fprintf(fp, "xxx{WA:%u}", block->wrap); + } + } + fprintf(fp, "]\n"); +} + +static void +dump_sendq(nb_SENDQ *q, FILE *fp) +{ + const char *indent = " "; + sllist_node *ll; + fprintf(fp, "Send Queue\n"); + SLLIST_FOREACH(&q->pending, ll) { + nb_SNDQELEM *e = SLLIST_ITEM(ll, nb_SNDQELEM, slnode); + fprintf(fp, "%s[Base=%p, Len=%u]\n", indent, e->base, e->len); + if (q->last_requested == e) { + fprintf(fp, "%s\n", indent, q->last_offset); + } + } +} + +void +netbuf_dump_status(nb_MGR *mgr, FILE *fp) +{ + sllist_node *ll; + fprintf(fp, "Status for MGR=%p\n", (void *)mgr); + fprintf(fp, "ACTIVE:\n"); + + SLLIST_FOREACH(&mgr->datapool.active, ll) { + nb_MBLOCK *block = SLLIST_ITEM(ll, nb_MBLOCK, slnode); + dump_managed_block(block, fp); + } + fprintf(fp, "AVAILABLE:\n"); + SLLIST_FOREACH(&mgr->datapool.avail, ll) { + nb_MBLOCK *block = SLLIST_ITEM(ll, nb_MBLOCK, slnode); + const char *indent = " "; + fprintf(fp, "%sBLOCK(AVAIL)=%p; BUF=%p, %uB\n", indent, + (void*)block, block->root, block->nalloc); + } + dump_sendq(&mgr->sendq, fp); +} + +static int +is_pool_clean(const nb_MBPOOL *pool, int is_dealloc) +{ + int ret = 1; + sllist_node *ll; + + SLLIST_FOREACH(&pool->active, ll) { + nb_MBLOCK *block = SLLIST_ITEM(ll, nb_MBLOCK, slnode); + + if (!BLOCK_IS_EMPTY(block)) { + printf("MBLOCK %p: Cursor (%u) != Start (%u)\n", + (void*)block, block->cursor, block->start); + ret = 0; + } + + if (block->deallocs) { + nb_DEALLOC_QUEUE *dq = block->deallocs; + if (!SLLIST_IS_EMPTY(&dq->pending)) { + printf("MBLOCK %p: Dealloc queue still has items\n", (void*)block); + ret = 0; + } + + if (!is_dealloc) { + if (!is_pool_clean(&block->deallocs->qpool, 1)) { + ret = 0; + } + } + } + } + return ret; +} + +int +netbuf_is_clean(nb_MGR *mgr) +{ + int ret = 1; + + if (!is_pool_clean(&mgr->datapool, 0)) { + ret = 0; + } + + if (!SLLIST_IS_EMPTY(&mgr->sendq.pending)) { + printf("SENDQ @%p: Still have pending flush items\n", (void*)mgr); + ret = 0; + } + + if (!SLLIST_IS_EMPTY(&mgr->sendq.pdus)) { + printf("PDUQ @%p: Still have pending PDU items\n", (void*)mgr); + ret = 0; + } + + if (!is_pool_clean(&mgr->sendq.elempool, 0)) { + printf("SENDQ/MBLOCK @%p: Still have unfreed members in send queue\n", + (void*)mgr); + ret = 0; + } + + return ret; +} + +int +netbuf_has_flushdata(nb_MGR *mgr) +{ + if (!SLLIST_IS_EMPTY(&mgr->sendq.pending)) { + return 1; + } + if (!SLLIST_IS_EMPTY(&mgr->sendq.pdus)) { + return 1; + } + return 0; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/netbuf/netbuf.h b/couchbase-sys/libcouchbase-2.7.0/src/netbuf/netbuf.h new file mode 100644 index 00000000..d4163b97 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/netbuf/netbuf.h @@ -0,0 +1,452 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NETBUF_H +#define NETBUF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * @brief Netbuf write buffers + */ + +/** + * @defgroup netbufs Netbufs + * + * # Introduction + * + * ## GOALS + * + * 1. provide a simple buffer allocation API + * From a logic perspective it's simplest to deal with a straight + * contiguous buffer per packet. + * + * 2. provide an efficient way of sending multiple contiguous packets. This + * will reduce IOV fragmentation and reduce the number of trips to the + * I/O plugin for multiple writes. Currently this is done very efficiently + * with the ringbuffer - however this comes at the cost of copying all + * request data to the ringbuffer itself. Our aim is to reduce the + * number of copies while still maintaining a packed buffer. + * + * 3. Allow a pluggable method by which user-provided data can be plugged + * into the span/cursor/flush architecture. + * + * @addtogroup netbufs + * @{ + */ + +#include "sllist.h" +#include "netbuf-defs.h" +#include "netbuf-mblock.h" + +/** + * @brief Structure representing a buffer within netbufs + * + * @note It is recommended that you maintain the individual fields in your + * own structure and then re-create them as needed. The span structure is 16 + * bytes on 64 bit systems, but can be reduced to 12 if needed. Additionally, + * you may already have the 'size' field stored/calculated elsewhere. + */ +typedef struct { + /** @private Parent block */ + nb_MBLOCK *parent; + + /** @private Offset from root at which this buffer begins */ + nb_SIZE offset; + + /** write-once: Allocation size */ + nb_SIZE size; +} nb_SPAN; + +#define NETBUF_INVALID_OFFSET (nb_SIZE)-1 + +/** + * Creates a span from a buffer _not_ owned by netbufs. + * @param span the span to initialize + * @param buf the buffer + * @param len the length of the buffer + */ +#define CREATE_STANDALONE_SPAN(span, buf, len) \ + (span)->parent = (nb_MBLOCK *) (void *)buf; \ + (span)->offset = NETBUF_INVALID_OFFSET; \ + (span)->size = len; + +/** @private */ +typedef struct { + sllist_node slnode; + char *base; + nb_SIZE len; + /* Extra 4 bytes here. WHAT WE DO!!! */ +} nb_SNDQELEM; + +/** @private */ +typedef struct { + /** Linked list of pending spans to send */ + sllist_root pending; + + /** + * List of PDUs to be flushed. A PDU is comprised of one or more IOVs + * (or even a subsection thereof) + */ + sllist_root pdus; + + /** The last window which was part of the previous fill call */ + nb_SNDQELEM *last_requested; + + /** + * Number of bytes enqueued in the 'last request' element. This is needed + * because it is possible for the last element to grow in length during + * a subsequent flush. + */ + nb_SIZE last_offset; + + /** Offset from last PDU which was partially flushed */ + nb_SIZE pdu_offset; + + /** Pool of elements to utilize */ + nb_MBPOOL elempool; +} nb_SENDQ; + +struct netbuf_st { + /** Send Queue */ + nb_SENDQ sendq; + + /** Pool for variable-size data */ + nb_MBPOOL datapool; + + nb_SETTINGS settings; +}; + +/** + * Quick way to get the span from a buffer, when the buffer is *known* to + * be standalone (i.e. CREATE_STANDALONE_SPAN() + * @param span The span from which to extract the buffer + * @return a pointer to the buffer + */ +#define SPAN_SABUFFER_NC(span) ((char *)(span)->parent) + +/** + * Quick way to get the span from a buffer when the buffer is known *not* + * to be standalone + * @param span The span from which to extract the buffer + * @return A pointer to a buffer + */ +#define SPAN_MBUFFER_NC(span) ((span)->parent->root + (span)->offset) + +/** + * @brief Retrieves a pointer to the buffer related to this span. + * @param span the span from which to extract the buffer + * @return a pointer to the buffer. + * + * @see SPAN_SABUFFER_NC + * @see SPAN_MBUFFER_NC + */ +#define SPAN_BUFFER(span) \ + (((span)->offset == NETBUF_INVALID_OFFSET) ? SPAN_SABUFFER_NC(span) : SPAN_MBUFFER_NC(span)) + +/** + * @brief allocate a span + * + * Reserve a contiguous region of memory, in-order for a given span. The + * span will be reserved from the last block to be flushed to the network. + * + * The contents of the span are guaranteed to be contiguous (though not aligned) + * and are available via the SPAN_BUFFER macro. + * + * @return 0 if successful, -1 on error + */ +int +netbuf_mblock_reserve(nb_MGR *mgr, nb_SPAN *span); + +/** + * @brief release a span + * + * Release a span previously allocated via reserve_span. It is assumed that the + * contents of the span have either: + * + * 1. been successfully sent to the network + * 2. have just been scheduled (and are being removed due to error handling) + * 3. have been partially sent to a connection which is being closed. + * + * @param mgr the manager in which this span is reserved + * @param span the span + */ +void +netbuf_mblock_release(nb_MGR *mgr, nb_SPAN *span); + +/** + * @brief Enqueue a span for serialization + * + * Schedules an IOV to be placed inside the send queue. The storage of the + * underlying buffer must not be freed or otherwise modified until it has + * been sent. + * + * With the current usage model, flush status is implicitly completed once + * a response has arrived. + * + * Note that you may create the IOV from a SPAN object like so: + * @code{.c} + * iov->iov_len = span->size; + * iov->iov_base = SPAN_BUFFER(span); + * @endcode + */ +void +netbuf_enqueue(nb_MGR *mgr, const nb_IOV *bufinfo); + +void +netbuf_enqueue_span(nb_MGR *mgr, nb_SPAN *span); + +/** + * Gets the number of IOV structures required to flush the entire contents of + * all buffers. + */ +unsigned int +netbuf_get_niov(nb_MGR *mgr); + +/** + * @brief + * Populates an iovec structure for flushing a set of bytes from the various + * blocks. + * + * You may call this function mutltiple times, so long as each call to + * start_flush is eventually mapped with a call to end_flush. + * + * @code{.c} + * netbuf_start_flush(mgr, iov1, niov1); + * netbuf_start_flush(mgr, iov2, niov2); + * ... + * netbuf_end_flush(mgr, nbytes1); + * netbuf_end_flush(mgr, nbytes2); + * @endcode + * + * Additionally, only the LAST end_flush call may be supplied an nflushed + * parameter which is smaller than the size returned by start_flush. If the + * entire contents of the `iovs` structure cannot be flushed immediately and + * you do not wish to persist it until such a time that it may be flushed, then + * netbuf_reset_flush() should be called. See that functions' documentation + * for more details. + * + * This function may be thought of advancing a virtual cursor which is mapped + * to a send queue. Each time this function is called the cursor is advanced + * by the number of bytes that the library expects you to flush (i.e. the return + * value of this function). Typically the cursor is never rewound and any + * operation that advances the cursor places the burden on the user to + * actually flush the data contained within the IOV objects. + * The netbuf_end_flush() function merely has the task of releasing any memory + * used for mapping of already-flushed data. + * + * From a different perspective, each call to netbuf_start_flush() establishes + * a contract between the library and the user: The library guarantees that + * this specific region (referred to within the IOVs) will not be flushed by + * any other subsystem (i.e. nothing else will try to flush the same data). + * The user guarantees that the data will eventually be flushed, and that the + * data will be flushed in the order it was received via start_flush(). + * + * + * @param mgr the manager object + * @param iovs an array of iovec structures + * @param niov the number of iovec structures allocated. + * @param[out] nused how many IOVs are actually required + * + * @return the number of bytes which can be flushed in this IOV. If the + * return value is 0 then there are no more bytes to flush. + * + * Note that the return value is limited by the number of IOV structures + * provided and should not be taken as an indicator of how many bytes are + * used overall. + */ +nb_SIZE +netbuf_start_flush(nb_MGR *mgr, nb_IOV *iovs, int niov, int *nused); + +/** + * @brief Indicate that a flush has completed. + * + * Indicate that a number of bytes have been flushed. This should be called after + * the data retrieved by get_flushing_iov has been flushed to the TCP buffers. + * + * @param mgr the manager object + * @param nflushed how much data in bytes was flushed to the network. + */ +void +netbuf_end_flush(nb_MGR *mgr, nb_SIZE nflushed); + +/** + * Reset the flush context for the buffer. This should be called only when the + * following is true: + * + * (1) There is only a single open call to netbuf_start_flush + * (2) The last call to end_flush did not pass all the bytes which were to + * be flushed. + * + * In this case, the next call to start_flush() will return an IOV which begins + * where the last end_flush() finished, rather than the last start_flush(). + * As a consequence it means that the previous IOV populated with start_flush + * is no longer valid and start_flush should be called again. + */ +#define netbuf_reset_flush(mgr) do { \ + (mgr)->sendq.last_requested = NULL; \ + (mgr)->sendq.last_offset = 0; \ +} while (0); + +/** + * Informational function to get the total size of all data in the + * buffers. This traverses all blocks, so call this for debugging only. + */ +nb_SIZE +netbuf_get_size(const nb_MGR *mgr); + +/** + * Get the maximum size of a span which can be satisfied without using an + * additional block. + * + * @param mgr + * + * @param allow_wrap + * Whether to take into consideration wrapping. If this is true then the span + * size will allow wrapping. If disabled, then only the packed size will be + * available. Consider: + *
    + * [ ooooooo{S:10}xxxxxxxxx{C:10}ooooo{A:5} ]
    + * 
    + * If wrapping is allowed, then the maximum span size will be 10, from 0..10 + * but the last 5 bytes at the end will be lost for the duration of the block. + * If wrapping is not allowed then the maximum span size will be 5. + * + * @return + * the maximum span size without requiring additional blocks. + */ +nb_SIZE +netbuf_mblock_get_next_size(const nb_MGR *mgr, int allow_wrap); + +/** + * @brief Initializes an nb_MGR structure + * @param mgr the manager to initialize + * @param settings + */ +void +netbuf_init(nb_MGR *mgr, const nb_SETTINGS *settings); + +/** + * @brief Frees up any allocated resources for a given manager + * @param mgr the manager for which to release resources + */ +void +netbuf_cleanup(nb_MGR *mgr); + +/** + * Populates the settings structure with the default settings. This structure + * may then be modified or tuned and passed to netbuf_init() + */ +void +netbuf_default_settings(nb_SETTINGS *settings); + +/** + * Dump the internal structure of the manager to the screen. Useful for + * debugging. + */ +void +netbuf_dump_status(nb_MGR *mgr, FILE *fp); + + +/** + * Mark a PDU as being enqueued. This should be called whenever the final IOV + * for a given PDU has just been enqueued. + * + * The PDU itself must remain valid and is assumed to contain an 'slnode' + * structure which will point to the next PDU. The PDU will later be removed + * from the queue when 'end_flush' has been called including its range. + * + * @param mgr The manager + * + * @param pdu An opaque pointer + * + * @param lloff The offset from the pdu pointer at which the slist_node + * structure may be found. + */ +void +netbuf_pdu_enqueue(nb_MGR *mgr, void *pdu, nb_SIZE lloff); + + +/** + * This callback is invoked during 'end_flush2'. + * + * @param pdu The PDU pointer enqueued via netbuf_pdu_enqueue() + * + * @param remaining A hint passed to the callback indicating how many bytes + * remain on the stream. IFF remaining is greater than the size of the + * PDU the callback may change internal state within the packet to mark + * it as flushed. + * + * XXX: + * If nremaining < pdusize then it must be ignored. The value + * may have been previously passed to the same callback during a + * prior iteration. + * + * This is done by design in order to avoid forcing each PDU to maintain + * a variable indicating "How much was flushed". + * + * @param arg A pointer passed to the start_flush call; used to correlate any + * data common to the queue itself. + * + * @return The size of the PDU. This will be used to determine future calls + * to the callback for subsequent PDUs. + * + * If size <= remaining then this PDU will be popped off the PDU queue + * and is deemed to be no longer utilized by the send queue (and may + * be released from the mblock allocator; if it's being used). + * + * If size > remaining then no further callbacks will be invoked on + * the relevant PDUs. + */ +typedef nb_SIZE (*nb_getsize_fn)(void *pdu, nb_SIZE remaining, void *arg); + +void +netbuf_end_flush2(nb_MGR *mgr, + unsigned int nflushed, + nb_getsize_fn callback, + nb_SIZE lloff, void *arg); + + +/** + * Ensures that the given internal structures of the manager are not allocated + * or otherwise in use by other systems. This is useful for testing to ensure + * that we wouldn't accidentally think everything is OK. + * + * Because resources are released at the block level, we might have had blocks + * which were partially allocated. + * + * This also checks the PDU and send queues as well. + */ +int +netbuf_is_clean(nb_MGR *mgr); + +/** + * Determines if there is any data to be flushed to the network + */ +int +netbuf_has_flushdata(nb_MGR *mgr); + +/**@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* LCB_PACKET_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/newconfig.cc b/couchbase-sys/libcouchbase-2.7.0/src/newconfig.cc new file mode 100644 index 00000000..32ea6dfb --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/newconfig.cc @@ -0,0 +1,357 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2011-2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" +#include "packetutils.h" +#include "bucketconfig/clconfig.h" +#include "vbucket/aliases.h" +#include "sllist-inl.h" + +#define LOGARGS(instance, lvl) (instance)->settings, "newconfig", LCB_LOG_##lvl, __FILE__, __LINE__ +#define LOG(instance, lvlbase, msg) lcb_log(instance->settings, "newconfig", LCB_LOG_##lvlbase, __FILE__, __LINE__, msg) + +#define SERVER_FMT "%s:%s (%p)" +#define SERVER_ARGS(s) (s)->get_host().host, (s)->get_host().port, (void *)s + +typedef struct lcb_GUESSVB_st { + time_t last_update; /**< Last time this vBucket was heuristically set */ + char newix; /**< New index, heuristically determined */ + char oldix; /**< Original index, according to map */ + char used; /**< Flag indicating whether or not this entry has been used */ +} lcb_GUESSVB; + +/* Ignore configuration updates for heuristically guessed vBuckets for a + * maximum amount of [n] seconds */ +#define MAX_KEEP_GUESS 20 + +static int +should_keep_guess(lcb_GUESSVB *guess, lcbvb_VBUCKET *vb) +{ + if (guess->newix == guess->oldix) { + /* Heuristic position is the same as starting position */ + return 0; + } + if (vb->servers[0] != guess->oldix) { + /* Previous master changed */ + return 0; + } + + if (time(NULL) - guess->last_update > MAX_KEEP_GUESS) { + /* Last usage too old */ + return 0; + } + + return 1; +} + +void +lcb_vbguess_newconfig(lcb_t instance, lcbvb_CONFIG *cfg, lcb_GUESSVB *guesses) +{ + unsigned ii; + + if (!guesses) { + return; + } + + for (ii = 0; ii < cfg->nvb; ii++) { + lcb_GUESSVB *guess = guesses + ii; + lcbvb_VBUCKET *vb = cfg->vbuckets + ii; + + if (!guess->used) { + continue; + } + + /* IF: Heuristically learned a new index, _and_ the old index (which is + * known to be bad) is the same index stated by the new config */ + if (should_keep_guess(guess, vb)) { + lcb_log(LOGARGS(instance, TRACE), "Keeping heuristically guessed index. VBID=%d. Current=%d. Old=%d.", ii, guess->newix, guess->oldix); + vb->servers[0] = guess->newix; + } else { + /* We don't reassign to the guess structure here. The idea is that + * we will simply use the new config. If this gives us problems, the + * config will re-learn again. */ + lcb_log(LOGARGS(instance, TRACE), "Ignoring heuristically guessed index. VBID=%d. Current=%d. Old=%d. New=%d", ii, guess->newix, guess->oldix, vb->servers[0]); + guess->used = 0; + } + } +} + +int +lcb_vbguess_remap(lcb_t instance, int vbid, int bad) +{ + + if (LCBT_SETTING(instance, vb_noguess)) { + int newix = lcbvb_nmv_remap_ex(LCBT_VBCONFIG(instance), vbid, bad, 0); + if (newix > -1 && newix != bad) { + lcb_log(LOGARGS(instance, TRACE), "Got new index from ffmap. VBID=%d. Old=%d. New=%d", vbid, bad, newix); + } + return newix; + + } else { + lcb_GUESSVB *guesses = instance->vbguess; + lcb_GUESSVB *guess = guesses + vbid; + int newix = lcbvb_nmv_remap_ex(LCBT_VBCONFIG(instance), vbid, bad, 1); + if (!guesses) { + guesses = instance->vbguess = reinterpret_cast(calloc( + LCBT_VBCONFIG(instance)->nvb, sizeof *guesses)); + } + if (newix > -1 && newix != bad) { + guess->newix = newix; + guess->oldix = bad; + guess->used = 1; + guess->last_update = time(NULL); + lcb_log(LOGARGS(instance, TRACE), "Guessed new heuristic index VBID=%d. Old=%d. New=%d", vbid, bad, newix); + } + return newix; + } +} + +/** + * Finds the index of an older server using the current config. + * + * This function is used to help reuse the server structures for memcached + * packets. + * + * @param oldconfig The old configuration. This is the configuration the + * old server is bound to + * @param newconfig The new configuration. This will be inspected for new + * nodes which may have been added, and ones which may have been removed. + * @param server The server to match + * @return The new index, or -1 if the current server is not present in the new + * config. + */ +static int +find_new_data_index(lcbvb_CONFIG *oldconfig, lcbvb_CONFIG* newconfig, + lcb::Server *server) +{ + const char *old_datahost = lcbvb_get_hostport(oldconfig, + server->get_index(), LCBVB_SVCTYPE_DATA, LCBVB_SVCMODE_PLAIN); + + if (!old_datahost) { + /* Old server had no data service */ + return -1; + } + + for (size_t ii = 0; ii < LCBVB_NSERVERS(newconfig); ii++) { + const char *new_datahost = lcbvb_get_hostport(newconfig, ii, + LCBVB_SVCTYPE_DATA, LCBVB_SVCMODE_PLAIN); + if (new_datahost && strcmp(new_datahost, old_datahost) == 0) { + return ii; + } + } + return -1; +} + +static void +log_vbdiff(lcb_t instance, lcbvb_CONFIGDIFF *diff) +{ + lcb_log(LOGARGS(instance, INFO), "Config Diff: [ vBuckets Modified=%d ], [Sequence Changed=%d]", diff->n_vb_changes, diff->sequence_changed); + if (diff->servers_added) { + for (char **curserver = diff->servers_added; *curserver; curserver++) { + lcb_log(LOGARGS(instance, INFO), "Detected server %s added", *curserver); + } + } + if (diff->servers_removed) { + for (char **curserver = diff->servers_removed; *curserver; curserver++) { + lcb_log(LOGARGS(instance, INFO), "Detected server %s removed", *curserver); + } + } +} + +/** + * This callback is invoked for packet relocation twice. It tries to relocate + * commands to their destination server. Some commands may not be relocated + * either because they have no explicit "Relocation Information" (i.e. no + * specific vbucket) or because the command is tied to a specific server (i.e. + * CMD_STAT). + * + * Note that `KEEP_PACKET` here doesn't mean to "Save" the packet, but rather + * to keep the packet in the current queue (so that if the server ends up + * being removed, the command will fail); rather than being relocated to + * another server. + */ +static int +iterwipe_cb(mc_CMDQUEUE *cq, mc_PIPELINE *oldpl, mc_PACKET *oldpkt, void *) +{ + protocol_binary_request_header hdr; + lcb::Server *srv = static_cast(oldpl); + int newix; + + mcreq_read_hdr(oldpkt, &hdr); + + if (!lcb_should_retry(srv->get_settings(), oldpkt, LCB_MAX_ERROR)) { + return MCREQ_KEEP_PACKET; + } + + if (LCBVB_DISTTYPE(cq->config) == LCBVB_DIST_VBUCKET) { + newix = lcbvb_vbmaster(cq->config, ntohs(hdr.request.vbucket)); + + } else { + const void *key = NULL; + lcb_SIZE nkey = 0; + int tmpid; + + /* XXX: We ignore hashkey. This is going away soon, and is probably + * better than simply failing the items. */ + mcreq_get_key(oldpkt, &key, &nkey); + lcbvb_map_key(cq->config, key, nkey, &tmpid, &newix); + } + + if (newix < 0 || newix > (int)cq->npipelines-1) { + return MCREQ_KEEP_PACKET; + } + + + mc_PIPELINE *newpl = cq->pipelines[newix]; + if (newpl == oldpl || newpl == NULL) { + return MCREQ_KEEP_PACKET; + } + + lcb_log(LOGARGS((lcb_t)cq->cqdata, DEBUG), "Remapped packet %p (SEQ=%u) from "SERVER_FMT " to " SERVER_FMT, + (void*)oldpkt, oldpkt->opaque, SERVER_ARGS((lcb::Server*)oldpl), SERVER_ARGS((lcb::Server*)newpl)); + + /** Otherwise, copy over the packet and find the new vBucket to map to */ + mc_PACKET *newpkt = mcreq_renew_packet(oldpkt); + newpkt->flags &= ~MCREQ_STATE_FLAGS; + mcreq_reenqueue_packet(newpl, newpkt); + mcreq_packet_handled(oldpl, oldpkt); + return MCREQ_REMOVE_PACKET; +} + +static void +replace_config(lcb_t instance, lcbvb_CONFIG *oldconfig, lcbvb_CONFIG *newconfig) +{ + mc_CMDQUEUE *cq = &instance->cmdq; + mc_PIPELINE **ppold, **ppnew; + unsigned ii, nold, nnew; + + assert(LCBT_VBCONFIG(instance) == newconfig); + + nnew = LCBVB_NSERVERS(newconfig); + ppnew = reinterpret_cast(calloc(nnew, sizeof(*ppnew))); + ppold = mcreq_queue_take_pipelines(cq, &nold); + + /** + * Determine which existing servers are still part of the new cluster config + * and place it inside the new list. + */ + for (ii = 0; ii < nold; ii++) { + lcb::Server *cur = static_cast(ppold[ii]); + int newix = find_new_data_index(oldconfig, newconfig, cur); + if (newix > -1) { + cur->set_new_index(newix); + ppnew[newix] = cur; + ppold[ii] = NULL; + lcb_log(LOGARGS(instance, INFO), "Reusing server "SERVER_FMT". OldIndex=%d. NewIndex=%d", SERVER_ARGS(cur), ii, newix); + } + } + + /** + * Once we've moved the kept servers to the new list, allocate new lcb::Server + * structures for slots that don't have an existing lcb::Server. We must do + * this before add_pipelines() is called, so that there are no holes inside + * ppnew + */ + for (ii = 0; ii < nnew; ii++) { + if (!ppnew[ii]) { + ppnew[ii] = new lcb::Server(instance, ii); + } + } + + /** + * Once we have all the server structures in place for the new config, + * transfer the new config along with the new list over to the CQ structure. + */ + mcreq_queue_add_pipelines(cq, ppnew, nnew, newconfig); + for (ii = 0; ii < nnew; ii++) { + mcreq_iterwipe(cq, ppnew[ii], iterwipe_cb, NULL); + } + + /** + * Go through all the servers that are to be removed and relocate commands + * from their queues into the new queues + */ + for (ii = 0; ii < nold; ii++) { + if (!ppold[ii]) { + continue; + } + + mcreq_iterwipe(cq, ppold[ii], iterwipe_cb, NULL); + static_cast(ppold[ii])->purge(LCB_MAP_CHANGED); + static_cast(ppold[ii])->close(); + } + + for (ii = 0; ii < nnew; ii++) { + if (static_cast(ppnew[ii])->has_pending()) { + ppnew[ii]->flush_start(ppnew[ii]); + } + } + + free(ppnew); + free(ppold); +} + +void lcb_update_vbconfig(lcb_t instance, clconfig_info *config) +{ + lcb_configuration_t change_status; + clconfig_info *old_config = instance->cur_configinfo; + mc_CMDQUEUE *q = &instance->cmdq; + + instance->cur_configinfo = config; + lcb_clconfig_incref(config); + q->config = instance->cur_configinfo->vbc; + q->cqdata = instance; + + if (old_config) { + lcbvb_CONFIGDIFF *diff = lcbvb_compare(old_config->vbc, config->vbc); + + if (diff) { + log_vbdiff(instance, diff); + lcbvb_free_diff(diff); + } + + /* Apply the vb guesses */ + lcb_vbguess_newconfig(instance, config->vbc, instance->vbguess); + + replace_config(instance, old_config->vbc, config->vbc); + lcb_clconfig_decref(old_config); + change_status = LCB_CONFIGURATION_CHANGED; + } else { + size_t nservers = VB_NSERVERS(config->vbc); + std::vector servers; + + for (size_t ii = 0; ii < nservers; ii++) { + servers.push_back(new lcb::Server(instance, ii)); + } + + mcreq_queue_add_pipelines(q, &servers[0], nservers, config->vbc); + change_status = LCB_CONFIGURATION_NEW; + } + + /* Update the list of nodes here for server list */ + instance->ht_nodes->clear(); + for (size_t ii = 0; ii < LCBVB_NSERVERS(config->vbc); ++ii) { + const char *hp = lcbvb_get_hostport(config->vbc, ii, + LCBVB_SVCTYPE_MGMT, LCBVB_SVCMODE_PLAIN); + if (hp) { + instance->ht_nodes->add(hp, LCB_CONFIG_HTTP_PORT); + } + } + + instance->callbacks.configuration(instance, change_status); + lcb_maybe_breakout(instance); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/nodeinfo.cc b/couchbase-sys/libcouchbase-2.7.0/src/nodeinfo.cc new file mode 100644 index 00000000..8a032809 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/nodeinfo.cc @@ -0,0 +1,190 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" +#include +#include "bucketconfig/clconfig.h" +#include "hostlist.h" + +/**This file contains routines to assist users in retrieving valid nodes */ + +/* We're gonna try to be extra careful in this function since many SDKs use + * this to display node and/or host-port information.*/ + +static std::string& +ensure_scratch(lcb_t instance) +{ + if (!instance->scratch) { + instance->scratch = new std::string; + } + instance->scratch->clear(); + return *instance->scratch; +} + +static const char * +mk_scratch_host(lcb_t instance, const lcb_host_t *host) +{ + std::string& s = ensure_scratch(instance); + s.append(host->host); + s.append(":"); + s.append(host->port); + return s.c_str(); +} + +static const char * +return_badhost(lcb_GETNODETYPE type) +{ + if (type & LCB_NODE_NEVERNULL) { + return LCB_GETNODE_UNAVAILABLE; + } else { + return NULL; + } +} + +LIBCOUCHBASE_API +const char * +lcb_get_node(lcb_t instance, lcb_GETNODETYPE type, unsigned ix) +{ + lcbvb_SVCMODE mode; + lcbvb_CONFIG *vbc = LCBT_VBCONFIG(instance); + + if (LCBT_SETTING(instance, sslopts) & LCB_SSL_ENABLED) { + mode = LCBVB_SVCMODE_SSL; + } else { + mode = LCBVB_SVCMODE_PLAIN; + } + + if (type & LCB_NODE_HTCONFIG) { + if (type & LCB_NODE_CONNECTED) { + const lcb_host_t *host = lcb_confmon_get_rest_host(instance->confmon); + if (host) { + return mk_scratch_host(instance, host); + } else { + return return_badhost(type); + } + + } else { + /* Retrieve one from the vbucket configuration */ + const char *hp = NULL; + + if (instance->type == LCB_TYPE_BUCKET) { + if (vbc) { + ix %= LCBVB_NSERVERS(vbc); + hp = lcbvb_get_hostport(vbc, ix, LCBVB_SVCTYPE_MGMT, mode); + + } else if ((type & LCB_NODE_NEVERNULL) == 0) { + return NULL; + } + } + if (hp == NULL && instance->ht_nodes && !instance->ht_nodes->empty()) { + ix %= instance->ht_nodes->size(); + instance->ht_nodes->ensure_strlist(); + hp = instance->ht_nodes->hoststrs[ix]; + } + if (!hp) { + return return_badhost(type); + } + return ensure_scratch(instance).append(hp).c_str(); + } + } else if (type & (LCB_NODE_DATA|LCB_NODE_VIEWS)) { + ix %= LCBT_NSERVERS(instance); + const lcb::Server *server = instance->get_server(ix); + + if ((type & LCB_NODE_CONNECTED) && !server->is_connected()) { + return return_badhost(type); + } + + /* otherwise, return the actual host:port of the server */ + if (type & LCB_NODE_DATA) { + return mk_scratch_host(instance, &server->get_host()); + } else { + return lcbvb_get_hostport(vbc, ix, LCBVB_SVCTYPE_VIEWS, mode); + } + } else { + return NULL; /* don't know the type */ + } +} + +LIBCOUCHBASE_API const char * lcb_get_host(lcb_t instance) { + char *colon; + const char *rv = lcb_get_node(instance, + static_cast(LCB_NODE_HTCONFIG|LCB_NODE_NEVERNULL), 0); + if (rv != NULL && (colon = (char *)strstr(rv, ":")) != NULL) { + if (instance->scratch && rv == instance->scratch->c_str()) { + // We have a colon + size_t colon_pos = instance->scratch->find(':'); + if (colon_pos != std::string::npos) { + instance->scratch->erase(colon_pos); + } + } + } + return rv; +} + +LIBCOUCHBASE_API const char * lcb_get_port(lcb_t instance) { + const char *rv = lcb_get_node(instance, + static_cast(LCB_NODE_HTCONFIG|LCB_NODE_NEVERNULL), 0); + if (rv && (rv = strstr(rv, ":"))) { + rv++; + } + return rv; +} + +LIBCOUCHBASE_API +lcb_int32_t lcb_get_num_replicas(lcb_t instance) +{ + if (LCBT_VBCONFIG(instance)) { + return LCBT_NREPLICAS(instance); + } else { + return -1; + } +} + +LIBCOUCHBASE_API +lcb_int32_t lcb_get_num_nodes(lcb_t instance) +{ + if (LCBT_VBCONFIG(instance)) { + return LCBT_NSERVERS(instance); + } else { + return -1; + } +} + +LIBCOUCHBASE_API +const char *const *lcb_get_server_list(lcb_t instance) +{ + return hostlist_strents(instance->ht_nodes); +} + +LIBCOUCHBASE_API +const char * +lcb_get_keynode(lcb_t instance, const void *key, size_t nkey) +{ + lcbvb_CONFIG *vbc = LCBT_VBCONFIG(instance); + int srvix, vbid; + + if (!vbc) { + return NULL; + } + + lcbvb_map_key(vbc, key, nkey, &vbid, &srvix); + if (srvix < 0) { + return NULL; + } + + return lcbvb_get_hostname(vbc, srvix); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/operations/cbflush.c b/couchbase-sys/libcouchbase-2.7.0/src/operations/cbflush.c new file mode 100644 index 00000000..895edcc6 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/operations/cbflush.c @@ -0,0 +1,71 @@ +/* + * Copyright 2010-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" +#include "simplestring.h" +#include + +static void +flush_cb(lcb_t instance, int cbtype, const lcb_RESPBASE *rb) +{ + const lcb_RESPHTTP *resp = (const lcb_RESPHTTP *)rb; + lcb_RESPCBFLUSH fresp = { 0 }; + lcb_RESPCALLBACK callback = lcb_find_callback(instance, LCB_CALLBACK_CBFLUSH); + + fresp = *(lcb_RESPBASE *)rb; + fresp.rflags |= LCB_RESP_F_FINAL; + if (resp->rc == LCB_SUCCESS) { + if (resp->htstatus < 200 || resp->htstatus > 299) { + fresp.rc = LCB_HTTP_ERROR; + } + } + if (callback) { + callback(instance, LCB_CALLBACK_CBFLUSH, (lcb_RESPBASE*)&fresp); + } + (void)cbtype; +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_cbflush3(lcb_t instance, const void *cookie, const lcb_CMDBASE *cmd) +{ + lcb_http_request_t htr; + lcb_CMDHTTP htcmd = { 0 }; + lcb_string urlpath; + lcb_error_t rc; + + (void)cmd; + + lcb_string_init(&urlpath); + lcb_string_appendz(&urlpath, "/pools/default/buckets/"); + lcb_string_appendz(&urlpath, LCBT_SETTING(instance, bucket)); + lcb_string_appendz(&urlpath, "/controller/doFlush"); + + + htcmd.type = LCB_HTTP_TYPE_MANAGEMENT; + htcmd.method = LCB_HTTP_METHOD_POST; + htcmd.reqhandle = &htr; + LCB_CMD_SET_KEY(&htcmd, urlpath.base, urlpath.nused); + + rc = lcb_http3(instance, cookie, &htcmd); + lcb_string_release(&urlpath); + + if (rc != LCB_SUCCESS) { + return rc; + } + lcb_htreq_setcb(htr, flush_cb); + return LCB_SUCCESS; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/operations/counter.c b/couchbase-sys/libcouchbase-2.7.0/src/operations/counter.c new file mode 100644 index 00000000..4d611cc2 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/operations/counter.c @@ -0,0 +1,116 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "internal.h" +#include "trace.h" + +LIBCOUCHBASE_API +lcb_error_t +lcb_counter3( + lcb_t instance, const void *cookie, const lcb_CMDCOUNTER *cmd) +{ + mc_CMDQUEUE *q = &instance->cmdq; + mc_PIPELINE *pipeline; + mc_PACKET *packet; + mc_REQDATA *rdata; + lcb_error_t err; + + protocol_binary_request_incr acmd; + protocol_binary_request_header *hdr = &acmd.message.header; + + if (LCB_KEYBUF_IS_EMPTY(&cmd->key)) { + return LCB_EMPTY_KEY; + } + if (cmd->cas || (cmd->create == 0 && cmd->exptime != 0)) { + return LCB_OPTIONS_CONFLICT; + } + + err = mcreq_basic_packet(q, (const lcb_CMDBASE *)cmd, hdr, 20, &packet, + &pipeline, MCREQ_BASICPACKET_F_FALLBACKOK); + + if (err != LCB_SUCCESS) { + return err; + } + + rdata = &packet->u_rdata.reqdata; + rdata->cookie = cookie; + rdata->start = gethrtime(); + hdr->request.magic = PROTOCOL_BINARY_REQ; + hdr->request.datatype = PROTOCOL_BINARY_RAW_BYTES; + hdr->request.cas = 0; + hdr->request.opaque = packet->opaque; + hdr->request.bodylen = + htonl(hdr->request.extlen + ntohs(hdr->request.keylen)); + + acmd.message.body.delta = lcb_htonll((lcb_uint64_t)cmd->delta); + acmd.message.body.initial = lcb_htonll(cmd->initial); + if (!cmd->create) { + memset(&acmd.message.body.expiration, 0xff, + sizeof(acmd.message.body.expiration)); + } else { + acmd.message.body.expiration = htonl(cmd->exptime); + } + + if (cmd->delta < 0) { + hdr->request.opcode = PROTOCOL_BINARY_CMD_DECREMENT; + acmd.message.body.delta = lcb_htonll((lcb_uint64_t)(cmd->delta * -1)); + } else { + hdr->request.opcode = PROTOCOL_BINARY_CMD_INCREMENT; + } + + memcpy(SPAN_BUFFER(&packet->kh_span), acmd.bytes, sizeof(acmd.bytes)); + TRACE_ARITHMETIC_BEGIN(hdr, cmd); + LCB_SCHED_ADD(instance, pipeline, packet); + return LCB_SUCCESS; +} + +LIBCOUCHBASE_API +lcb_error_t lcb_arithmetic(lcb_t instance, + const void *cookie, + lcb_size_t num, + const lcb_arithmetic_cmd_t *const *items) +{ + unsigned ii; + + lcb_sched_enter(instance); + + for (ii = 0; ii < num; ii++) { + const lcb_arithmetic_cmd_t *src = items[ii]; + lcb_CMDCOUNTER dst; + lcb_error_t err; + + memset(&dst, 0, sizeof(dst)); + + dst.create = src->v.v0.create; + dst.delta = src->v.v0.delta; + dst.initial = src->v.v0.initial; + dst.key.type = LCB_KV_COPY; + dst.key.contig.bytes = src->v.v0.key; + dst.key.contig.nbytes = src->v.v0.nkey; + dst._hashkey.type = LCB_KV_COPY; + dst._hashkey.contig.bytes = src->v.v0.hashkey; + dst._hashkey.contig.nbytes = src->v.v0.nhashkey; + dst.exptime = src->v.v0.exptime; + err = lcb_counter3(instance, cookie, &dst); + if (err != LCB_SUCCESS) { + lcb_sched_fail(instance); + return err; + } + } + + lcb_sched_leave(instance); + SYNCMODE_INTERCEPT(instance) +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/operations/durability-cas.cc b/couchbase-sys/libcouchbase-2.7.0/src/operations/durability-cas.cc new file mode 100644 index 00000000..a466f01a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/operations/durability-cas.cc @@ -0,0 +1,240 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012-2015 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LCBDUR_PRIV_SYMS + +#include +#include +#include "internal.h" +#include "durability_internal.h" + +using namespace lcb::durability; + +namespace { +struct CasDurset : public Durset { + CasDurset(lcb_t instance_, const lcb_durability_opts_t *options) + : Durset(instance_, options), ht(NULL) { + } + + virtual ~CasDurset(); + + void update(lcb_error_t err, const lcb_RESPOBSERVE *resp); + Item& find(const char *s, size_t n) { + if (entries.size() == 1) { + return entries.back(); + } else { + return *reinterpret_cast(genhash_find(ht, s, n)); + } + } + + // Override + lcb_error_t prepare_schedule(); + lcb_error_t poll_impl(); + + genhash_t *ht; +}; +} + + +Durset* +Durset::createCasDurset(lcb_t instance, const lcb_durability_opts_t *options) { + return new CasDurset(instance, options); +} + + + +/* Called when the criteria is to ensure the key exists somewhow */ +static int +check_positive_durability(Item& ent, const lcb_RESPOBSERVE *res) +{ + switch (res->status) { + case LCB_OBSERVE_NOT_FOUND: + case LCB_OBSERVE_LOGICALLY_DELETED: + /* If we get NOT_FOUND from the master, this means the key + * simply does not exists (and we don't have to continue polling) */ + if (res->ismaster) { + ent.finish(LCB_KEY_ENOENT); + } + return Item::NO_CHANGES; + + case LCB_OBSERVE_PERSISTED: + return Item::UPDATE_PERSISTED | Item::UPDATE_REPLICATED; + + case LCB_OBSERVE_FOUND: + return Item::UPDATE_REPLICATED; + + default: + ent.finish(LCB_EINTERNAL); + return Item::NO_CHANGES; + } +} + +/* Called when the criteria is to ensure that the key is deleted somehow */ +static int +check_negative_durability(Item& ent, const lcb_RESPOBSERVE *res) +{ + switch (res->status) { + case LCB_OBSERVE_PERSISTED: + case LCB_OBSERVE_FOUND: + /* Still there! */ + return Item::NO_CHANGES; + + case LCB_OBSERVE_LOGICALLY_DELETED: + /* removed from cache, but not actually deleted from disk */ + return Item::UPDATE_REPLICATED; + + case LCB_OBSERVE_NOT_FOUND: + /* No knowledge of key. */ + return Item::UPDATE_PERSISTED | Item::UPDATE_REPLICATED; + + default: + ent.finish(LCB_EINTERNAL); + return Item::NO_CHANGES; + } +} + +void lcbdur_cas_update(lcb_t, void *dset, lcb_error_t err, + const lcb_RESPOBSERVE *resp) +{ + reinterpret_cast(dset)->update(err, resp); +} + +/* Observe callback. Called internally by observe.c */ +void +CasDurset::update(lcb_error_t err, const lcb_RESPOBSERVE *resp) +{ + if (resp->key == NULL) { + /* Last observe response for requests. Start polling after interval */ + on_poll_done(); + return; + } + + Item& ent = find(reinterpret_cast(resp->key), resp->nkey); + + if (ent.done) { + /* ignore subsequent errors */ + return; + } + + if (err != LCB_SUCCESS) { + ent.res().rc = err; + return; + } + + ent.res().nresponses++; + if (resp->cas && resp->ismaster) { + ent.res().cas = resp->cas; + + if (ent.reqcas && ent.reqcas != resp->cas) { + ent.finish(LCB_KEY_EEXISTS); + return; + } + } + + int flags; + if (opts.check_delete) { + flags = check_negative_durability(ent, resp); + } else { + flags = check_positive_durability(ent, resp); + } + + ent.update(flags, resp->ttp); +} + +lcb_error_t +CasDurset::poll_impl() +{ + lcb_MULTICMD_CTX *mctx; + lcb_error_t err; + + mctx = lcb_observe_ctx_dur_new(instance); + if (!mctx) { + return LCB_CLIENT_ENOMEM; + } + + for (size_t ii = 0; ii < entries.size(); ii++) { + lcb_CMDOBSERVE cmd = { 0 }; + uint16_t servers[4]; + + Item& ent = entries[ii]; + if (ent.done) { + continue; + } + + size_t nservers = ent.prepare(servers); + if (nservers == 0) { + ent.res().rc = LCB_NO_MATCHING_SERVER; + continue; + } + + LCB_KREQ_SIMPLE(&cmd.key, ent.res().key, ent.res().nkey); + LCB_CMD__SETVBID(&cmd, ent.vbid); + cmd.servers_ = servers; + cmd.nservers_ = nservers; + + err = mctx->addcmd(mctx, (lcb_CMDBASE *)&cmd); + if (err != LCB_SUCCESS) { + mctx->fail(mctx); + return err; + } + } + + lcb_sched_enter(instance); + err = mctx->done(mctx, this); + mctx = NULL; + + if (err == LCB_SUCCESS) { + lcb_sched_leave(instance); + waiting = 1; + } else { + lcb_sched_fail(instance); + } + return err; +} + +lcb_error_t +CasDurset::prepare_schedule() +{ + Durset::prepare_schedule(); + if (entries.size() < 2) { + return LCB_SUCCESS; + } + + ht = lcb_hashtable_nc_new(entries.size()); + if (!ht) { + return LCB_CLIENT_ENOMEM; + } + + for (size_t ii = 0; ii < entries.size(); ++ii) { + int mt; + Item &ent = entries[ii]; + + mt = genhash_update(ht, ent.res().key, ent.res().nkey, &ent, 0); + if (mt != NEW) { + return LCB_DUPLICATE_COMMANDS; + } + } + return LCB_SUCCESS; +} + +CasDurset::~CasDurset() +{ + if (ht) { + genhash_free(ht); + ht = NULL; + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/operations/durability-seqno.cc b/couchbase-sys/libcouchbase-2.7.0/src/operations/durability-seqno.cc new file mode 100644 index 00000000..0790b616 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/operations/durability-seqno.cc @@ -0,0 +1,163 @@ +/* + * Copyright 2015 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LCBDUR_PRIV_SYMS + +#include "internal.h" +#include +#include "durability_internal.h" + +using namespace lcb::durability; + +namespace { +class SeqnoDurset : public Durset { +public: + SeqnoDurset(lcb_t instance_, const lcb_durability_opts_t *options) + : Durset(instance_, options) { + } + + // Override + lcb_error_t poll_impl(); + + // Override + lcb_error_t after_add(Item& item, const lcb_CMDENDURE *cmd); + + void update(const lcb_RESPOBSEQNO *resp); +}; +} + +Durset * +Durset::createSeqnoDurset(lcb_t instance, const lcb_durability_opts_t *options) { + return new SeqnoDurset(instance, options); +} + +#define ENT_SEQNO(ent) (ent)->reqseqno + +static void +seqno_callback(lcb_t, int, const lcb_RESPBASE *rb) +{ + const lcb_RESPOBSEQNO *resp = (const lcb_RESPOBSEQNO*)rb; + int flags = 0; + Item *ent = static_cast(reinterpret_cast(resp->cookie)); + + /* Now, process the response */ + if (resp->rc != LCB_SUCCESS) { + ent->res().rc = resp->rc; + goto GT_TALLY; + } + + lcb_U64 seqno_mem, seqno_disk; + if (resp->old_uuid) { + /* Failover! */ + seqno_mem = seqno_disk = resp->old_seqno; + if (seqno_mem < ENT_SEQNO(ent)) { + ent->finish(LCB_MUTATION_LOST); + goto GT_TALLY; + } + } else { + seqno_mem = resp->mem_seqno; + seqno_disk = resp->persisted_seqno; + } + + if (seqno_mem < ENT_SEQNO(ent)) { + goto GT_TALLY; + } + + flags = Item::UPDATE_REPLICATED; + if (seqno_disk >= ENT_SEQNO(ent)) { + flags |= Item::UPDATE_PERSISTED; + } + + ent->update(flags, resp->server_index); + + GT_TALLY: + if (!--ent->parent->waiting) { + /* avoid ssertion (wait==0)! */ + ent->parent->waiting = 1; + ent->parent->on_poll_done(); + } +} + +lcb_error_t +SeqnoDurset::poll_impl() +{ + lcb_error_t ret_err = LCB_EINTERNAL; /* This should never be returned */ + bool has_ops = false; + + lcb_sched_enter(instance); + for (size_t ii = 0; ii < entries.size(); ii++) { + Item& ent = entries[ii]; + lcb_U16 servers[4]; + lcb_CMDOBSEQNO cmd = { 0 }; + + if (ent.done) { + continue; + } + + cmd.uuid = ent.uuid; + cmd.vbid = ent.vbid; + cmd.cmdflags = LCB_CMD_F_INTERNAL_CALLBACK; + ent.callback = seqno_callback; + + size_t nservers = ent.prepare(servers); + for (size_t jj = 0; jj < nservers; jj++) { + lcb_error_t err; + cmd.server_index = servers[jj]; + err = lcb_observe_seqno3(instance, &ent.callback, &cmd); + if (err == LCB_SUCCESS) { + waiting++; + has_ops = true; + } else { + ent.res().rc = ret_err = err; + } + } + } + lcb_sched_leave(instance); + if (!has_ops) { + return ret_err; + } else { + return LCB_SUCCESS; + } +} + +lcb_error_t +SeqnoDurset::after_add(Item &item, const lcb_CMDENDURE *cmd) +{ + const lcb_MUTATION_TOKEN *stok = NULL; + + if (cmd->cmdflags & LCB_CMDENDURE_F_MUTATION_TOKEN) { + stok = cmd->mutation_token; + } + + if (stok == NULL) { + if (!instance->dcpinfo) { + return LCB_DURABILITY_NO_MUTATION_TOKENS; + } + if (item.vbid >= LCBT_VBCONFIG(instance)->nvb) { + return LCB_EINVAL; + } + stok = instance->dcpinfo + item.vbid; + if (LCB_MUTATION_TOKEN_ID(stok) == 0) { + return LCB_DURABILITY_NO_MUTATION_TOKENS; + } + } + + /* Set the fields */ + memset(item.sinfo, 0, sizeof(item.sinfo[0]) * 4); + item.uuid = LCB_MUTATION_TOKEN_ID(stok); + ENT_SEQNO(&item) = LCB_MUTATION_TOKEN_SEQ(stok); + return LCB_SUCCESS; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/operations/durability.cc b/couchbase-sys/libcouchbase-2.7.0/src/operations/durability.cc new file mode 100644 index 00000000..30e2ed08 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/operations/durability.cc @@ -0,0 +1,663 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LCBDUR_PRIV_SYMS 1 +#define NOMINMAX + +#include "internal.h" +#include "durability_internal.h" +#include +#include + +using namespace lcb::durability; + +#define LOGARGS(c, lvl) (c)->instance->settings, "endure", LCB_LOG_##lvl, __FILE__, __LINE__ +#define LOGARGS_T(lvl) LOGARGS(this, lvl) + +static void timer_callback(lcb_socket_t sock, short which, void *arg); + +bool +Item::is_all_done() const +{ + const lcb_DURABILITYOPTSv0& opts = parent->opts; + + if (!res().exists_master) { + /** Primary cache doesn't have correct version */ + return false; + } + if (opts.persist_to) { + if (!res().persisted_master) { + return false; + } + if (res().npersisted < opts.persist_to) { + return false; + } + } + + if (opts.replicate_to) { + if (res().nreplicated < opts.replicate_to) { + return false; + } + } + + return true; +} + +bool +Item::is_server_done(const ServerInfo &info, bool is_master) const +{ + // Item not in cache. Return false + if (!info.exists) { + return false; + } + + // Item is already persisted to the server + if (info.persisted) { + return true; + } + + // Item not persisted, but no persistence requested + if (parent->opts.persist_to == 0) { + return true; + } + + // Master persistence requested, but server is not master + if (parent->opts.persist_to == 1 && !is_master) { + return true; + } + + // Require persistence from this server, but item is not persisted. + return false; +} + +size_t +Item::prepare(uint16_t ixarray[4]) +{ + size_t oix = 0, maxix = 0; + lcb_t instance = parent->instance; + + res().persisted_master = 0; + res().exists_master = 0; + res().npersisted = 0; + res().nreplicated = 0; + res().cas = 0; + res().rc = LCB_SUCCESS; + + if (parent->opts.persist_to == 1 && parent->opts.replicate_to == 0) { + maxix = 1; /* Only master! */ + } else { + maxix = LCBT_NREPLICAS(instance) + 1; + } + + for (size_t ii = 0; ii < maxix; ii++) { + int cur_ix; + ServerInfo& info = sinfo[ii]; + + cur_ix = lcbvb_vbserver(LCBT_VBCONFIG(instance), vbid, ii); + if (cur_ix < 0) { + info.clear(); + continue; + } + + const lcb::Server *s_exp = instance->get_server(cur_ix); + if (s_exp != info.server) { + info.clear(); + + } else if (is_server_done(info, ii==0)) { + /* Update counters as required */ + if (ii == 0) { + res().exists_master = 1; + } else { + res().nreplicated++; + } + + if (info.persisted) { + res().npersisted++; + if (ii == 0) { + res().persisted_master = 1; + } + } + continue; + } + + /* Otherwise, write the expected server out */ + ixarray[oix++] = s_exp->get_index(); + } + + return oix; +} + +void +Item::update(int flags, int srvix) +{ + if (!flags || done) { + return; + } + + ServerInfo *info = get_server_info(srvix); + if (!info) { + lcb_log(LOGARGS(parent, DEBUG), "Ignoring response from server %d. Not a master or replica for vBucket %d", srvix, vbid); + return; + } + + lcb_t instance = parent->instance; + bool is_master = lcbvb_vbmaster(LCBT_VBCONFIG(instance), vbid) == srvix; + const lcb::Server *server = instance->get_server(srvix); + + memset(info, 0, sizeof(*info)); + info->server = server; + + if (flags & UPDATE_PERSISTED) { + info->persisted = 1; + res().npersisted++; + if (is_master) { + res().persisted_master = 1; + } + } + + if (flags & UPDATE_REPLICATED) { + info->exists = 1; + if (is_master) { + res().exists_master = 1; + } else { + res().nreplicated++; + } + } + + if (is_all_done()) { + finish(LCB_SUCCESS); + } +} + +ServerInfo * +Item::get_server_info(int srvix) +{ + size_t ii; + lcb_t instance = parent->instance; + + for (ii = 0; ii < LCBT_NREPLICAS(instance)+1; ii++) { + int ix = lcbvb_vbserver(LCBT_VBCONFIG(instance), vbid, ii); + if (ix > -1 && ix == srvix) { + return &sinfo[ii]; + } + } + return NULL; +} + +void +Item::finish() +{ + lcb_RESPCALLBACK cb; + lcb_t instance; + + if (done) { + return; + } + + done = 1; + parent->nremaining--; + + /** Invoke the callback now :) */ + result.cookie = (void *)parent->cookie; + instance = parent->instance; + + if (parent->is_durstore) { + lcb_RESPSTOREDUR resp = { 0 }; + resp.key = result.key; + resp.nkey = result.nkey; + resp.rc = result.rc; + resp.cas = reqcas; + resp.cookie = result.cookie; + resp.store_ok = 1; + resp.dur_resp = &result; + + cb = lcb_find_callback(instance, LCB_CALLBACK_STOREDUR); + cb(instance, LCB_CALLBACK_STOREDUR, (lcb_RESPBASE*)&resp); + } else { + cb = lcb_find_callback(instance, LCB_CALLBACK_ENDURE); + cb(instance, LCB_CALLBACK_ENDURE, (lcb_RESPBASE*)&result); + } + + if (parent->nremaining == 0) { + parent->decref(); + } +} + +/** + * Called when the last (primitive) OBSERVE response is received for the entry. + */ +void +Durset::on_poll_done() +{ + lcb_assert(waiting || ("Got NULL callback twice!" && 0)); + + waiting = 0; + + if (nremaining > 0) { + switch_state(STATE_OBSPOLL); + } + decref(); +} + +/** + * Schedules a single sweep of observe requests. + * The `initial` parameter determines if this is a retry or if this is the + * initial scheduling. + */ +void +Durset::poll() +{ + lcb_error_t err; + + /* We should never be called while an 'iter' operation is still in progress */ + lcb_assert(waiting == 0); + incref(); + + err = poll_impl(); + if (err == LCB_SUCCESS) { + incref(); + switch_state(STATE_TIMEOUT); + } else { + lasterr = err; + switch_state(STATE_OBSPOLL); + } + + decref(); +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_durability_validate(lcb_t instance, + lcb_U16 *persist_to, lcb_U16 *replicate_to, int options) +{ + int replica_max = std::min( + LCBT_NREPLICAS(instance), + LCBT_NDATASERVERS(instance)-1); + int persist_max = replica_max + 1; + + if (*persist_to == 0 && *replicate_to == 0) { + /* Empty values! */ + return LCB_EINVAL; + } + + /* persist_max is always one more than replica_max */ + if (static_cast(*persist_to) > persist_max) { + if (options & LCB_DURABILITY_VALIDATE_CAPMAX) { + *persist_to = persist_max; + } else { + return LCB_DURABILITY_ETOOMANY; + } + } + + if (*replicate_to == 0) { + return LCB_SUCCESS; + } + + if (replica_max < 0) { + replica_max = 0; + } + + /* now, we need at least as many nodes as we have replicas */ + if (static_cast(*replicate_to) > replica_max) { + if (options & LCB_DURABILITY_VALIDATE_CAPMAX) { + *replicate_to = replica_max; + } else { + return LCB_DURABILITY_ETOOMANY; + } + } + return LCB_SUCCESS; + +} + +static lcb_error_t +dset_ctx_add(lcb_MULTICMD_CTX *mctx, const lcb_CMDBASE *cmd) +{ + return static_cast(mctx)->mctx_add(cmd); +} + +lcb_error_t +Durset::mctx_add(const lcb_CMDBASE *cmd) +{ + + if (LCB_KEYBUF_IS_EMPTY(&cmd->key)) { + return LCB_EMPTY_KEY; + } + + entries.resize(entries.size() + 1); + Item& ent = entries.back(); + + int vbid, srvix; + mcreq_map_key(&instance->cmdq, &cmd->key, &cmd->_hashkey, + MCREQ_PKT_BASESIZE, &vbid, &srvix); + + /* ok. now let's initialize the entry..*/ + memset(&ent, 0, sizeof (ent)); + ent.res().nkey = cmd->key.contig.nbytes; + ent.reqcas = cmd->cas; + ent.parent = this; + ent.vbid = vbid; + + kvbufs.append(reinterpret_cast(cmd->key.contig.bytes), + cmd->key.contig.nbytes); + + return after_add(ent, reinterpret_cast(cmd)); +} + +static lcb_error_t +dset_ctx_schedule(lcb_MULTICMD_CTX *mctx, const void *cookie) +{ + return static_cast(mctx)->mctx_schedule(cookie); +} + +lcb_error_t +Durset::mctx_schedule(const void *cookie_) { + lcb_error_t err; + const char *kptr = kvbufs.c_str(); + + if (entries.empty()) { + delete this; + return LCB_EINVAL; + } + + for (size_t ii = 0; ii < entries.size(); ii++) { + Item* ent = &entries[ii]; + ent->res().key = kptr; + kptr += ent->res().nkey; + } + + if ((err = prepare_schedule()) != LCB_SUCCESS) { + delete this; + return err; + } + + incref(); + + cookie = cookie_; + nremaining = entries.size(); + ns_timeout = gethrtime() + LCB_US2NS(opts.timeout); + + lcb_aspend_add(&instance->pendops, LCB_PENDTYPE_DURABILITY, this); + switch_state(STATE_INIT); + return LCB_SUCCESS; +} + +static void +dset_ctx_fail(lcb_MULTICMD_CTX *mctx) +{ + delete static_cast(mctx); +} + +void lcbdurctx_set_durstore(lcb_MULTICMD_CTX *mctx, int enabled) +{ + static_cast(mctx)->is_durstore = enabled; +} + +static lcb_U8 +get_poll_meth(lcb_t instance, const lcb_durability_opts_t *options) +{ + /* Need to call this first, so we can actually allocate the appropriate + * data for this.. */ + uint8_t meth; + if (options->version > 0) { + meth = options->v.v0.pollopts; + } else { + meth = LCB_DURABILITY_MODE_DEFAULT; + } + + if (meth == LCB_DURABILITY_MODE_DEFAULT) { + meth = LCB_DURABILITY_MODE_CAS; + + if (LCBT_SETTING(instance, fetch_mutation_tokens) && + LCBT_SETTING(instance, dur_mutation_tokens)) { + for (size_t ii = 0; ii < LCBT_NSERVERS(instance); ii++) { + if (instance->get_server(ii)->supports_mutation_tokens()) { + meth = LCB_DURABILITY_MODE_SEQNO; + break; + } + } + } + } + + return meth; +} + +Durset::Durset(lcb_t instance_, const lcb_durability_opts_t *options) + : nremaining(0), waiting(0), refcnt(0), next_state(STATE_OBSPOLL), + lasterr(LCB_SUCCESS), is_durstore(false), cookie(NULL), + ns_timeout(0), timer(NULL), instance(instance_) +{ + lcb_MULTICMD_CTX::addcmd = dset_ctx_add; + lcb_MULTICMD_CTX::done = dset_ctx_schedule; + lcb_MULTICMD_CTX::fail = dset_ctx_fail; + + const lcb_DURABILITYOPTSv0 *opts_in = &options->v.v0; + + std::memset(&opts, 0, sizeof opts); + + /* Ensure we don't clobber options from older versions */ + opts.cap_max = opts_in->cap_max; + opts.check_delete = opts_in->check_delete; + opts.interval = opts_in->interval; + opts.persist_to = opts_in->persist_to; + opts.replicate_to = opts_in->replicate_to; + opts.timeout = opts_in->timeout; + + if (!opts.timeout) { + opts.timeout = LCBT_SETTING(instance, durability_timeout); + } + + if (!opts.interval) { + opts.interval = LCBT_SETTING(instance, durability_interval); + } + + lcbio_pTABLE io = instance->iotable; + timer = io->timer.create(io->p); + + lasterr = lcb_durability_validate(instance, + &opts.persist_to, &opts.replicate_to, + opts.cap_max ? LCB_DURABILITY_VALIDATE_CAPMAX : 0); +} + +LIBCOUCHBASE_API +lcb_MULTICMD_CTX * +lcb_endure3_ctxnew(lcb_t instance, const lcb_durability_opts_t *options, + lcb_error_t *errp) +{ + lcb_error_t err_s; + if (!errp) { + errp = &err_s; + } + + *errp = LCB_SUCCESS; + + if (!LCBT_VBCONFIG(instance)) { + *errp = LCB_CLIENT_ETMPFAIL; + return NULL; + } + + Durset *dset = NULL; + uint8_t pollmeth = get_poll_meth(instance, options); + if (pollmeth == LCB_DURABILITY_MODE_CAS) { + dset = Durset::createCasDurset(instance, options); + } else if (pollmeth == LCB_DURABILITY_MODE_SEQNO) { + dset = Durset::createSeqnoDurset(instance, options); + } else { + *errp = LCB_EINVAL; + return NULL; + } + + if ((*errp = dset->lasterr) != LCB_SUCCESS) { + delete dset; + dset = NULL; + } + + return dset; +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_durability_poll(lcb_t instance, const void *cookie, + const lcb_durability_opts_t *options, lcb_size_t ncmds, + const lcb_durability_cmd_t *const *cmds) +{ + lcb_MULTICMD_CTX *mctx; + lcb_error_t err; + + if (ncmds == 0) { + return LCB_EINVAL; + } + + mctx = lcb_endure3_ctxnew(instance, options, &err); + if (!mctx) { + return err; + } + + for (size_t ii = 0; ii < ncmds; ii++) { + lcb_CMDENDURE cmd = { 0 }; + const lcb_DURABILITYCMDv0 *src = &cmds[ii]->v.v0; + cmd.key.contig.bytes = src->key; + cmd.key.contig.nbytes = src->nkey; + cmd._hashkey.contig.bytes = src->hashkey; + cmd._hashkey.contig.nbytes = src->nhashkey; + cmd.cas = src->cas; + + err = mctx->addcmd(mctx, (lcb_CMDBASE*)&cmd); + if (err != LCB_SUCCESS) { + mctx->fail(mctx); + return err; + } + } + + lcb_sched_enter(instance); + err = mctx->done(mctx, cookie); + if (err != LCB_SUCCESS) { + lcb_sched_fail(instance); + return err; + } else { + lcb_sched_leave(instance); + SYNCMODE_INTERCEPT(instance) + } +} + +/** + * Actually free the resources allocated by the dset (and all its entries). + * Called by some other functions in libcouchbase + */ +void lcbdur_destroy(void *dset) { + delete reinterpret_cast(dset); +} + +Durset::~Durset() +{ + if (timer) { + lcbio_TABLE *io = instance->iotable; + io->timer.cancel(io->p, timer); + io->timer.destroy(io->p, timer); + timer = NULL; + } + + lcb_aspend_del(&instance->pendops, LCB_PENDTYPE_DURABILITY, this); + lcb_maybe_breakout(instance); +} + +/** + * All-purpose callback dispatcher. + */ +static void timer_callback(lcb_socket_t, short, void *arg) { + reinterpret_cast(arg)->tick(); +} + +void +Durset::tick() +{ + hrtime_t now = gethrtime(); + + if (ns_timeout && now > ns_timeout) { + next_state = STATE_TIMEOUT; + } + + switch (next_state) { + case STATE_OBSPOLL: + case STATE_INIT: + poll(); + break; + + case STATE_TIMEOUT: { + lcb_error_t err = lasterr ? lasterr : LCB_ETIMEDOUT; + ns_timeout = 0; + next_state = STATE_IGNORE; + + lcb_log(LOGARGS_T(WARN), "Polling durability timed out!"); + + incref(); + + for (size_t ii = 0; ii < entries.size(); ii++) { + Item *ent = &entries[ii]; + if (ent->done) { + continue; + } + if (ent->res().rc == LCB_SUCCESS) { + ent->res().rc = err; + } + ent->finish(); + } + + decref(); + break; + } + + case STATE_IGNORE: + break; + + default: + lcb_assert("unexpected state" && 0); + break; + } +} + +/** + * Schedules us to be notified with the given state within a particular amount + * of time. This is used both for the timeout and for the interval + */ +void +Durset::switch_state(State state) +{ + uint32_t delay = 0; + lcbio_TABLE* io = instance->iotable; + hrtime_t now = gethrtime(); + + if (state == STATE_TIMEOUT) { + if (ns_timeout && now < ns_timeout) { + delay = LCB_NS2US(ns_timeout - now); + } else { + delay = 0; + } + } else if (state == STATE_OBSPOLL) { + if (now + LCB_US2NS(opts.interval) < ns_timeout) { + delay = opts.interval; + } else { + delay = 0; + state = STATE_TIMEOUT; + } + } else if (state == STATE_INIT) { + delay = 0; + } + + next_state = state; + io->timer.cancel(io->p, timer); + io->timer.schedule(io->p, timer, delay, this, timer_callback); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/operations/durability_internal.h b/couchbase-sys/libcouchbase-2.7.0/src/operations/durability_internal.h new file mode 100644 index 00000000..a5cb1b29 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/operations/durability_internal.h @@ -0,0 +1,284 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_DURABILITY_INTERNAL_H +#define LCB_DURABILITY_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name internal durability functions + * These functions are used internally beyond the durability module. + * @{ + */ + +/** + * Called from the OBSERVE codebase to update an item's status for CAS-based + * observe + */ +void lcbdur_cas_update(lcb_t, void *dset, lcb_error_t, const lcb_RESPOBSERVE *); + +/** + * Called from the OBSERVE codebase to update an item's status for seqno-based + * observe + */ +void lcbdur_update_seqno(lcb_t, void *dset, const lcb_RESPOBSEQNO*); + +/** Indicate that this durability command context is for an original storage op */ +void lcbdurctx_set_durstore(lcb_MULTICMD_CTX *ctx, int enabled); + +void lcbdur_destroy(void *dset); + +/** Called from durability-cas to request an OBSERVE with a special callback */ +lcb_MULTICMD_CTX *lcb_observe_ctx_dur_new(lcb_t instance); + +/**@} + * + * The rest of this file is internal to the various durability operations and + * is not accessed by the rest of the codebase + */ + +#ifdef __cplusplus +} +#endif + +#ifdef LCBDUR_PRIV_SYMS +namespace lcb { +namespace durability { + +/** + * Here is the internal API for the durability functions. + * + * Durability works on polling multiple observe responses and waiting until a + * key (or set of keys) have either been persisted, or the wait period has + * expired. + * + * The operation maintains an internal counter which counts how many keys + * do not have a conclusive observe response yet (i.e. how many do not have + * their criteria satisfied yet). The operation is considered complete when + * the counter reaches 0. + */ + +/** + * Information about a particular server's state -- whether it has been + * persisted to or replicated to. This is tied to a given mc_SERVER + * instance. + */ +struct ServerInfo { + const lcb::Server *server; /**< Server pointer (for comparison only) */ + lcb_U16 persisted; /**< Exists on server */ + lcb_U16 exists; /**< Persisted to server */ + + ServerInfo() : server(NULL), persisted(0), exists(0) { + } + + void clear() { + server = NULL; + persisted = 0; + exists = 0; + } +}; + +struct Durset; + +// For use in conjunction with MCREQ_F_PRIVCALLBACK +struct CallbackCookie { + lcb_RESPCALLBACK callback; +}; + +/**Information a single entry in a durability set. Each entry contains a single + * key */ +struct Item : public CallbackCookie { + /** + * Returns true if the entry is complete, false otherwise. This only assumes + * successful entries. + */ + bool is_all_done() const; + + /** + * Determine if this item has been satisfied on a specific server. This + * function is used to determine if a probe should be sent to the server. + * + * If there are no items "tied" to the server (because they have all been + * completed) then we employ a bandwidth saving optimization by not sending + * additional probes to it. + * + * @param info Server to check against + * @param is_master If this server is the master for the item's vbucket + * + * @return true if the item is both persisted and replicated on the server, + * OR if the item has been replicated, but replication is not + * required for the item. + */ + bool is_server_done(const ServerInfo& info, bool is_master) const; + + /** + * Updates the state of the given entry and synchronizes it with the + * current server list. + * + * Specifically this function will return a list of + * servers which still need to be contacted, and will increment internal + * counters on behalf of those (still active) servers which the item has + * already been replicated to (and persisted to, if requested). + * + * This will invalidate any cached information of the cluster configuration + * in respect to this item has changed -- this includes things like servers + * moving indices or being recreated entirely. + * + * This function should be called during poll(). + * @param[out] ixarray An array of server indices which should be queried + * @return the number of effective entries in the array. + */ + size_t prepare(uint16_t ixarray[4]); + + enum UpdateFlags { + NO_CHANGES = 0x00, + UPDATE_PERSISTED = 0x01, + UPDATE_REPLICATED = 0x02 + }; + + /** + * Update an item's status. + * @param flags OR'd set of UPDATE_PERSISTED and UPDATE_REPLICATED + * @param ix The server index + */ + void update(int flags, int srvix); + + /** + * Set the logical state of the entry to done, and invoke the callback. + * It is safe to call this multiple times + */ + void finish(); + + void finish(lcb_error_t err) { + result.rc = err; + finish(); + } + + lcb_RESPENDURE& res() { return result; } + const lcb_RESPENDURE& res() const { return result; } + ServerInfo* get_server_info(int index); + + lcb_U64 reqcas; /**< Last known CAS for the user */ + lcb_U64 reqseqno; /**< Last known seqno for the user */ + lcb_U64 uuid; + lcb_RESPENDURE result; /**< Result to be passed to user */ + Durset *parent; + lcb_U16 vbid; /**< vBucket ID (computed via hashkey) */ + lcb_U8 done; /**< Whether we have a conclusive result for this entry */ + + /** Array of servers which have satisfied constraints */ + ServerInfo sinfo[4]; +}; + +/** + * A collection encompassing one or more entries which are to be checked for + * persistence + */ +struct Durset : public lcb_MULTICMD_CTX { + /** + * Call this when the polling method (poll_impl()) has completed. This will + * trigger a new poll after the interval. + */ + void on_poll_done(); + + void incref() { refcnt++; } + + /** + * Decrement the refcount for the 'dset'. When it hits zero then the dset is + * freed + */ + void decref() { if (!--refcnt) { delete this; } } + + enum State { + STATE_OBSPOLL, + STATE_INIT, + STATE_TIMEOUT, + STATE_IGNORE + }; + + /** + * Schedules us to be notified with the given state within a particular amount + * of time. This is used both for the timeout and for the interval + */ + void switch_state(State state); + + /** + * Called from mctx_schedule(). This allows implementations to do any + * additional bookeeping, having guaranteed that all items are now + * added. + */ + virtual lcb_error_t prepare_schedule() { + return LCB_SUCCESS; + } + + /** + * Called from mctx_add. Called to register any item-specific data (i.e. + * to associate item data with internal structures) + * @param itm the newly added item + * @param the original command, for more context + */ + virtual lcb_error_t after_add(Item&, const lcb_CMDENDURE*) { + return LCB_SUCCESS; + } + + /** + * Called to actually check for persistence/replication. This must be + * implemented. + */ + virtual lcb_error_t poll_impl() = 0; + + virtual ~Durset(); + Durset(lcb_t instance, const lcb_durability_opts_t* options); + + // Implementation for MULTICMD_CTX + inline lcb_error_t mctx_schedule(const void *cookie); + inline lcb_error_t mctx_add(const lcb_CMDBASE *cmd); + + /** + * This function calls poll_impl(). The implementation should then call + * on_poll_done() once the polling is finished + */ + inline void poll(); + + /** Called after timeouts and intervals. */ + inline void tick(); + + static Durset* createCasDurset(lcb_t, const lcb_durability_opts_t*); + static Durset* createSeqnoDurset(lcb_t, const lcb_durability_opts_t*); + + lcb_DURABILITYOPTSv0 opts; /**< Sanitized user options */ + std::vector entries; + unsigned nremaining; /**< Number of entries remaining to poll for */ + int waiting; /**< Set if currently awaiting an observe callback */ + unsigned refcnt; /**< Reference count */ + State next_state; /**< Internal state */ + lcb_error_t lasterr; + bool is_durstore; /** Whether the callback should be DURSTORE */ + std::string kvbufs; /**< Backing storage for key buffers */ + const void *cookie; /**< User cookie */ + hrtime_t ns_timeout; /**< Timestamp of next timeout */ + void *timer; + lcb_t instance; +}; + +} // namespace durability +} // namespace lcb +#endif // __cplusplus +#endif // LCB_DURABILITY_INTERNAL_H diff --git a/couchbase-sys/libcouchbase-2.7.0/src/operations/get.c b/couchbase-sys/libcouchbase-2.7.0/src/operations/get.c new file mode 100644 index 00000000..29560762 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/operations/get.c @@ -0,0 +1,409 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" +#include "trace.h" + +LIBCOUCHBASE_API +lcb_error_t +lcb_get3(lcb_t instance, const void *cookie, const lcb_CMDGET *cmd) +{ + mc_PIPELINE *pl; + mc_PACKET *pkt; + mc_REQDATA *rdata; + mc_CMDQUEUE *q = &instance->cmdq; + lcb_error_t err; + lcb_uint8_t extlen = 0; + lcb_uint8_t opcode = PROTOCOL_BINARY_CMD_GET; + protocol_binary_request_gat gcmd; + protocol_binary_request_header *hdr = &gcmd.message.header; + + if (LCB_KEYBUF_IS_EMPTY(&cmd->key)) { + return LCB_EMPTY_KEY; + } + if (cmd->cas) { + return LCB_OPTIONS_CONFLICT; + } + + if (cmd->lock) { + extlen = 4; + opcode = PROTOCOL_BINARY_CMD_GET_LOCKED; + } else if (cmd->exptime || (cmd->cmdflags & LCB_CMDGET_F_CLEAREXP)) { + extlen = 4; + opcode = PROTOCOL_BINARY_CMD_GAT; + } + + err = mcreq_basic_packet(q, (const lcb_CMDBASE *)cmd, hdr, extlen, &pkt, &pl, + MCREQ_BASICPACKET_F_FALLBACKOK); + if (err != LCB_SUCCESS) { + return err; + } + + rdata = &pkt->u_rdata.reqdata; + rdata->cookie = cookie; + rdata->start = gethrtime(); + + hdr->request.magic = PROTOCOL_BINARY_REQ; + hdr->request.opcode = opcode; + hdr->request.datatype = PROTOCOL_BINARY_RAW_BYTES; + hdr->request.bodylen = htonl(extlen + ntohs(hdr->request.keylen)); + hdr->request.opaque = pkt->opaque; + hdr->request.cas = 0; + + if (extlen) { + gcmd.message.body.expiration = htonl(cmd->exptime); + } + + if (cmd->cmdflags & LCB_CMD_F_INTERNAL_CALLBACK) { + pkt->flags |= MCREQ_F_PRIVCALLBACK; + } + + memcpy(SPAN_BUFFER(&pkt->kh_span), gcmd.bytes, MCREQ_PKT_BASESIZE + extlen); + LCB_SCHED_ADD(instance, pl, pkt); + TRACE_GET_BEGIN(hdr, cmd); + + return LCB_SUCCESS; +} + +LIBCOUCHBASE_API +lcb_error_t lcb_get(lcb_t instance, + const void *command_cookie, + lcb_size_t num, + const lcb_get_cmd_t *const *items) +{ + unsigned ii; + lcb_sched_enter(instance); + + for (ii = 0; ii < num; ii++) { + const lcb_get_cmd_t *src = items[ii]; + lcb_CMDGET dst; + lcb_error_t err; + + memset(&dst, 0, sizeof(dst)); + dst.key.contig.bytes = src->v.v0.key; + dst.key.contig.nbytes = src->v.v0.nkey; + dst._hashkey.contig.bytes = src->v.v0.hashkey; + dst._hashkey.contig.nbytes = src->v.v0.nhashkey; + dst.lock = src->v.v0.lock; + dst.exptime = src->v.v0.exptime; + + err = lcb_get3(instance, command_cookie, &dst); + if (err != LCB_SUCCESS) { + lcb_sched_fail(instance); + return err; + } + } + lcb_sched_leave(instance); + SYNCMODE_INTERCEPT(instance) +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_unlock3(lcb_t instance, const void *cookie, const lcb_CMDUNLOCK *cmd) +{ + mc_CMDQUEUE *cq = &instance->cmdq; + mc_PIPELINE *pl; + mc_PACKET *pkt; + mc_REQDATA *rd; + lcb_error_t err; + protocol_binary_request_header hdr; + + if (LCB_KEYBUF_IS_EMPTY(&cmd->key)) { + return LCB_EMPTY_KEY; + } + + err = mcreq_basic_packet(cq, cmd, &hdr, 0, &pkt, &pl, + MCREQ_BASICPACKET_F_FALLBACKOK); + if (err != LCB_SUCCESS) { + return err; + } + + rd = &pkt->u_rdata.reqdata; + rd->cookie = cookie; + rd->start = gethrtime(); + + hdr.request.magic = PROTOCOL_BINARY_REQ; + hdr.request.opcode = PROTOCOL_BINARY_CMD_UNLOCK_KEY; + hdr.request.datatype = PROTOCOL_BINARY_RAW_BYTES; + hdr.request.bodylen = htonl((lcb_uint32_t)ntohs(hdr.request.keylen)); + hdr.request.opaque = pkt->opaque; + hdr.request.cas = lcb_htonll(cmd->cas); + + memcpy(SPAN_BUFFER(&pkt->kh_span), hdr.bytes, sizeof(hdr.bytes)); + TRACE_UNLOCK_BEGIN(&hdr, cmd); + LCB_SCHED_ADD(instance, pl, pkt); + return LCB_SUCCESS; +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_unlock(lcb_t instance, const void *cookie, lcb_size_t num, + const lcb_unlock_cmd_t * const * items) +{ + unsigned ii; + lcb_error_t err = LCB_SUCCESS; + + lcb_sched_enter(instance); + for (ii = 0; ii < num; ii++) { + const lcb_unlock_cmd_t *src = items[ii]; + lcb_CMDUNLOCK dst; + memset(&dst, 0, sizeof(dst)); + dst.key.contig.bytes = src->v.v0.key; + dst.key.contig.nbytes = src->v.v0.nkey; + dst._hashkey.contig.bytes = src->v.v0.hashkey; + dst._hashkey.contig.nbytes = src->v.v0.nhashkey; + dst.cas = src->v.v0.cas; + err = lcb_unlock3(instance, cookie, &dst); + if (err != LCB_SUCCESS) { + break; + } + } + if (err != LCB_SUCCESS) { + lcb_sched_fail(instance); + return err; + } else { + lcb_sched_leave(instance); + SYNCMODE_INTERCEPT(instance) + } +} + +typedef struct { + mc_REQDATAEX base; + unsigned r_cur; + unsigned r_max; + int remaining; + int vbucket; + lcb_replica_t strategy; + lcb_t instance; +} rget_cookie; + +static void rget_dtor(mc_PACKET *pkt) { + rget_cookie *rck = (rget_cookie *)pkt->u_rdata.exdata; + if (! --rck->remaining) { + free(rck); + } +} + +static void +rget_callback(mc_PIPELINE *pl, mc_PACKET *pkt, lcb_error_t err, const void *arg) +{ + rget_cookie *rck = (rget_cookie *)pkt->u_rdata.exdata; + lcb_RESPGET *resp = (void *)arg; + lcb_RESPCALLBACK callback; + lcb_t instance = rck->instance; + + callback = lcb_find_callback(instance, LCB_CALLBACK_GETREPLICA); + + /** Figure out what the strategy is.. */ + if (rck->strategy == LCB_REPLICA_SELECT || rck->strategy == LCB_REPLICA_ALL) { + /** Simplest */ + if (rck->strategy == LCB_REPLICA_SELECT || rck->remaining == 1) { + resp->rflags |= LCB_RESP_F_FINAL; + } + callback(instance, LCB_CALLBACK_GETREPLICA, (const lcb_RESPBASE *)resp); + } else { + mc_CMDQUEUE *cq = &instance->cmdq; + mc_PIPELINE *nextpl = NULL; + + /** FIRST */ + do { + int nextix; + rck->r_cur++; + nextix = lcbvb_vbreplica(cq->config, rck->vbucket, rck->r_cur); + if (nextix > -1 && nextix < (int)cq->npipelines) { + /* have a valid next index? */ + nextpl = cq->pipelines[nextix]; + break; + } + } while (rck->r_cur < rck->r_max); + + if (err == LCB_SUCCESS || rck->r_cur == rck->r_max || nextpl == NULL) { + resp->rflags |= LCB_RESP_F_FINAL; + callback(instance, LCB_CALLBACK_GETREPLICA, (lcb_RESPBASE *)resp); + /* refcount=1 . Free this now */ + rck->remaining = 1; + } else if (err != LCB_SUCCESS) { + mc_PACKET *newpkt = mcreq_renew_packet(pkt); + newpkt->flags &= ~MCREQ_STATE_FLAGS; + mcreq_sched_add(nextpl, newpkt); + /* Use this, rather than lcb_sched_leave(), because this is being + * invoked internally by the library. */ + mcreq_sched_leave(cq, 1); + /* wait */ + rck->remaining = 2; + } + } + + if (!--rck->remaining) { + free(rck); + } + (void)pl; +} + +static mc_REQDATAPROCS rget_procs = { + rget_callback, + rget_dtor +}; + +LIBCOUCHBASE_API +lcb_error_t +lcb_rget3(lcb_t instance, const void *cookie, const lcb_CMDGETREPLICA *cmd) +{ + /** + * Because we need to direct these commands to specific servers, we can't + * just use the 'basic_packet()' function. + */ + mc_CMDQUEUE *cq = &instance->cmdq; + int vbid, ixtmp; + protocol_binary_request_header req; + unsigned r0, r1 = 0; + rget_cookie *rck = NULL; + + if (LCB_KEYBUF_IS_EMPTY(&cmd->key)) { + return LCB_EMPTY_KEY; + } + if (!cq->config) { + return LCB_CLIENT_ETMPFAIL; + } + if (!LCBT_NREPLICAS(instance)) { + return LCB_NO_MATCHING_SERVER; + } + + mcreq_map_key(cq, &cmd->key, &cmd->_hashkey, MCREQ_PKT_BASESIZE, + &vbid, &ixtmp); + + /* The following blocks will also validate that the entire index range is + * valid. This is in order to ensure that we don't allocate the cookie + * if there aren't enough replicas online to satisfy the requirements */ + + if (cmd->strategy == LCB_REPLICA_SELECT) { + r0 = r1 = cmd->index; + if ((ixtmp = lcbvb_vbreplica(cq->config, vbid, r0)) < 0) { + return LCB_NO_MATCHING_SERVER; + } + + } else if (cmd->strategy == LCB_REPLICA_ALL) { + unsigned ii; + r0 = 0; + r1 = LCBT_NREPLICAS(instance); + /* Make sure they're all online */ + for (ii = 0; ii < LCBT_NREPLICAS(instance); ii++) { + if ((ixtmp = lcbvb_vbreplica(cq->config, vbid, ii)) < 0) { + return LCB_NO_MATCHING_SERVER; + } + } + } else { + for (r0 = 0; r0 < LCBT_NREPLICAS(instance); r0++) { + if ((ixtmp = lcbvb_vbreplica(cq->config, vbid, r0)) > -1) { + r1 = r0; + break; + } + } + if (r0 == LCBT_NREPLICAS(instance)) { + return LCB_NO_MATCHING_SERVER; + } + } + + if (r1 < r0 || r1 >= cq->npipelines) { + return LCB_NO_MATCHING_SERVER; + } + + /* Initialize the cookie */ + rck = calloc(1, sizeof(*rck)); + rck->base.cookie = cookie; + rck->base.start = gethrtime(); + rck->base.procs = &rget_procs; + rck->strategy = cmd->strategy; + rck->r_cur = r0; + rck->r_max = LCBT_NREPLICAS(instance); + rck->instance = instance; + rck->vbucket = vbid; + + /* Initialize the packet */ + req.request.magic = PROTOCOL_BINARY_REQ; + req.request.opcode = PROTOCOL_BINARY_CMD_GET_REPLICA; + req.request.datatype = PROTOCOL_BINARY_RAW_BYTES; + req.request.vbucket = htons((lcb_uint16_t)vbid); + req.request.cas = 0; + req.request.extlen = 0; + req.request.keylen = htons((lcb_uint16_t)cmd->key.contig.nbytes); + req.request.bodylen = htonl((lcb_uint32_t)cmd->key.contig.nbytes); + + do { + int curix; + mc_PIPELINE *pl; + mc_PACKET *pkt; + + curix = lcbvb_vbreplica(cq->config, vbid, r0); + /* XXX: this is always expected to be in range. For the FIRST mode + * it will seek to the first valid index (checked above), and for the + * ALL mode, it will fail if not all replicas are already online + * (also checked above) */ + pl = cq->pipelines[curix]; + pkt = mcreq_allocate_packet(pl); + if (!pkt) { + return LCB_CLIENT_ENOMEM; + } + + pkt->u_rdata.exdata = &rck->base; + pkt->flags |= MCREQ_F_REQEXT; + + mcreq_reserve_key(pl, pkt, sizeof(req.bytes), &cmd->key); + + req.request.opaque = pkt->opaque; + rck->remaining++; + mcreq_write_hdr(pkt, &req); + mcreq_sched_add(pl, pkt); + } while (++r0 < r1); + + MAYBE_SCHEDLEAVE(instance); + return LCB_SUCCESS; +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_get_replica(lcb_t instance, const void *cookie, lcb_size_t num, + const lcb_get_replica_cmd_t * const * items) +{ + unsigned ii; + lcb_error_t err = LCB_SUCCESS; + + lcb_sched_enter(instance); + for (ii = 0; ii < num; ii++) { + const lcb_get_replica_cmd_t *src = items[ii]; + lcb_CMDGETREPLICA dst; + memset(&dst, 0, sizeof(dst)); + dst.key.contig.bytes = src->v.v1.key; + dst.key.contig.nbytes = src->v.v1.nkey; + dst._hashkey.contig.bytes = src->v.v1.hashkey; + dst._hashkey.contig.nbytes = src->v.v1.nhashkey; + dst.strategy = src->v.v1.strategy; + dst.index = src->v.v1.index; + err = lcb_rget3(instance, cookie, &dst); + if (err != LCB_SUCCESS) { + break; + } + } + + if (err == LCB_SUCCESS) { + lcb_sched_leave(instance); + SYNCMODE_INTERCEPT(instance) + } else { + lcb_sched_fail(instance); + return err; + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/operations/observe-seqno.cc b/couchbase-sys/libcouchbase-2.7.0/src/operations/observe-seqno.cc new file mode 100644 index 00000000..a0a55378 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/operations/observe-seqno.cc @@ -0,0 +1,93 @@ +/* + * Copyright 2015 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "internal.h" + +lcb_error_t +lcb_observe_seqno3(lcb_t instance, const void *cookie, const lcb_CMDOBSEQNO *cmd) +{ + mc_PACKET *pkt; + protocol_binary_request_header hdr; + lcb_U64 uuid; + + if (cmd->server_index > LCBT_NSERVERS(instance)) { + return LCB_EINVAL; + } + + lcb::Server *server = instance->get_server(cmd->server_index); + pkt = mcreq_allocate_packet(server); + mcreq_reserve_header(server, pkt, MCREQ_PKT_BASESIZE); + mcreq_reserve_value2(server, pkt, 8); + + /* Set the static fields */ + MCREQ_PKT_RDATA(pkt)->cookie = cookie; + MCREQ_PKT_RDATA(pkt)->start = gethrtime(); + if (cmd->cmdflags & LCB_CMD_F_INTERNAL_CALLBACK) { + pkt->flags |= MCREQ_F_PRIVCALLBACK; + } + + memset(&hdr, 0, sizeof hdr); + hdr.request.opaque = pkt->opaque; + hdr.request.magic = PROTOCOL_BINARY_REQ; + hdr.request.opcode = PROTOCOL_BINARY_CMD_OBSERVE_SEQNO; + hdr.request.datatype = PROTOCOL_BINARY_RAW_BYTES; + hdr.request.bodylen = htonl((lcb_U32)8); + hdr.request.vbucket = htons(cmd->vbid); + memcpy(SPAN_BUFFER(&pkt->kh_span), hdr.bytes, sizeof hdr.bytes); + + uuid = lcb_htonll(cmd->uuid); + memcpy(SPAN_BUFFER(&pkt->u_value.single), &uuid, sizeof uuid); + LCB_SCHED_ADD(instance, server, pkt); + return LCB_SUCCESS; +} + +const lcb_MUTATION_TOKEN * +lcb_get_mutation_token(lcb_t instance, const lcb_KEYBUF *kb, lcb_error_t *errp) +{ + int vbix, srvix; + lcb_error_t err_s; + const lcb_MUTATION_TOKEN *existing; + + if (!errp) { + errp = &err_s; + } + + if (!LCBT_VBCONFIG(instance)) { + *errp = LCB_CLIENT_ETMPFAIL; + return NULL; + } + if (LCBT_VBCONFIG(instance)->dtype != LCBVB_DIST_VBUCKET) { + *errp = LCB_NOT_SUPPORTED; + return NULL; + } + if (!LCBT_SETTING(instance, fetch_mutation_tokens)) { + *errp = LCB_NOT_SUPPORTED; + return NULL; + } + + if (!instance->dcpinfo) { + *errp = LCB_DURABILITY_NO_MUTATION_TOKENS; + return NULL; + } + + mcreq_map_key(&instance->cmdq, kb, kb, 0, &vbix, &srvix); + existing = instance->dcpinfo + vbix; + if (existing->uuid_ == 0 && existing->seqno_ == 0) { + *errp = LCB_DURABILITY_NO_MUTATION_TOKENS; + return NULL; + } + *errp = LCB_SUCCESS; + return existing; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/operations/observe.c b/couchbase-sys/libcouchbase-2.7.0/src/operations/observe.c new file mode 100644 index 00000000..a99a853a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/operations/observe.c @@ -0,0 +1,340 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2011-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" +#include "simplestring.h" +#include "durability_internal.h" +#include "trace.h" + +typedef struct { + mc_REQDATAEX base; + lcb_MULTICMD_CTX mctx; + lcb_t instance; + + size_t remaining; + unsigned oflags; + + /* requests array contains one buffer per server. nrequest essentially + * says how many elements (and thus how many servers) */ + size_t nrequests; + lcb_string requests[1]; +} OBSERVECTX; + +typedef enum { + F_DURABILITY = 0x01, + F_DESTROY = 0x02, + F_SCHEDFAILED = 0x04 +} obs_flags; + +static void +handle_observe_callback(mc_PIPELINE *pl, + mc_PACKET *pkt, lcb_error_t err, const void *arg) +{ + OBSERVECTX *oc = (void *)pkt->u_rdata.exdata; + lcb_RESPOBSERVE *resp = (void *)arg; + lcb_t instance = oc->instance; + + (void)pl; + + if (resp == NULL) { + int nfailed = 0; + /** We need to fail the request manually.. */ + const char *ptr = SPAN_BUFFER(&pkt->u_value.single); + const char *end = ptr + pkt->u_value.single.size; + while (ptr < end) { + lcb_uint16_t nkey; + lcb_RESPOBSERVE cur = { 0 }; + cur.rflags = LCB_RESP_F_CLIENTGEN; + + ptr += 2; + memcpy(&nkey, ptr, sizeof(nkey)); + nkey = ntohs(nkey); + ptr += 2; + + memset(&cur, 0, sizeof(cur)); + cur.key = ptr; + cur.nkey = nkey; + cur.cookie = (void *)oc->base.cookie; + cur.rc = err; + handle_observe_callback(NULL, pkt, err, &cur); + ptr += nkey; + nfailed++; + } + lcb_assert(nfailed); + return; + } + + resp->cookie = (void *)oc->base.cookie; + resp->rc = err; + if (oc->oflags & F_DURABILITY) { + resp->ttp = pl ? pl->index : -1; + lcbdur_cas_update(instance, (void*)MCREQ_PKT_COOKIE(pkt), err, resp); + + } else if ((oc->oflags & F_SCHEDFAILED) == 0) { + lcb_RESPCALLBACK callback = lcb_find_callback(instance, LCB_CALLBACK_OBSERVE); + callback(instance, LCB_CALLBACK_OBSERVE, (lcb_RESPBASE *)resp); + } + + if (oc->oflags & F_DESTROY) { + return; + } + + if (--oc->remaining) { + return; + } else { + lcb_RESPOBSERVE resp2 = { 0 }; + resp2.rc = err; + resp2.rflags = LCB_RESP_F_CLIENTGEN|LCB_RESP_F_FINAL; + oc->oflags |= F_DESTROY; + handle_observe_callback(NULL, pkt, err, &resp2); + free(oc); + } +} + +static void +handle_schedfail(mc_PACKET *pkt) +{ + OBSERVECTX *oc = (void *)pkt->u_rdata.exdata; + oc->oflags |= F_SCHEDFAILED; + handle_observe_callback(NULL, pkt, LCB_SCHEDFAIL_INTERNAL, NULL); +} + +static void destroy_requests(OBSERVECTX *reqs) +{ + size_t ii; + for (ii = 0; ii < reqs->nrequests; ii++) { + lcb_string_release(reqs->requests + ii); + } +} + +#define CTX_FROM_MULTI(mcmd) (void *) ((((char *) (mcmd))) - offsetof(OBSERVECTX, mctx)) + +static lcb_error_t +obs_ctxadd(lcb_MULTICMD_CTX *mctx, const lcb_CMDBASE *cmdbase) +{ + int vbid, srvix_dummy; + unsigned ii; + const lcb_CMDOBSERVE *cmd = (const lcb_CMDOBSERVE *)cmdbase; + OBSERVECTX *ctx = CTX_FROM_MULTI(mctx); + lcb_t instance = ctx->instance; + mc_CMDQUEUE *cq = &instance->cmdq; + lcb_U16 servers_s[4]; + const lcb_U16 *servers; + size_t nservers; + + if (LCB_KEYBUF_IS_EMPTY(&cmd->key)) { + return LCB_EMPTY_KEY; + } + + if (cq->config == NULL) { + return LCB_CLIENT_ETMPFAIL; + } + + if (LCBVB_DISTTYPE(LCBT_VBCONFIG(instance)) != LCBVB_DIST_VBUCKET) { + return LCB_NOT_SUPPORTED; + } + + mcreq_map_key(cq, &cmd->key, &cmd->_hashkey, 24, &vbid, &srvix_dummy); + + if (cmd->servers_) { + servers = cmd->servers_; + nservers = cmd->nservers_; + } else { + nservers = 0; + servers = servers_s; + /* Replicas are always < 4 */ + for (ii = 0; ii < LCBVB_NREPLICAS(cq->config) + 1; ii++) { + int ix = lcbvb_vbserver(cq->config, vbid, ii); + if (ix < 0) { + if (ii == 0) { + return LCB_NO_MATCHING_SERVER; + } else { + continue; + } + } + servers_s[nservers++] = ix; + if (cmd->cmdflags & LCB_CMDOBSERVE_F_MASTER_ONLY) { + break; /* Only a single server! */ + } + } + } + + if (nservers == 0) { + return LCB_NO_MATCHING_SERVER; + } + + for (ii = 0; ii < nservers; ii++) { + lcb_string *rr; + lcb_U16 vb16, klen16; + lcb_U16 ix = servers[ii]; + + lcb_assert(ix < ctx->nrequests); + rr = ctx->requests + ix; + if (0 != lcb_string_reserve(rr, 4 + cmd->key.contig.nbytes)) { + return LCB_CLIENT_ENOMEM; + } + + vb16 = htons((lcb_U16)vbid); + klen16 = htons((lcb_U16)cmd->key.contig.nbytes); + lcb_string_append(rr, &vb16, sizeof vb16); + lcb_string_append(rr, &klen16, sizeof klen16); + lcb_string_append(rr, cmd->key.contig.bytes, cmd->key.contig.nbytes); + + ctx->remaining++; + } + return LCB_SUCCESS; +} + +static mc_REQDATAPROCS obs_procs = { + handle_observe_callback, + handle_schedfail +}; + +static lcb_error_t +obs_ctxdone(lcb_MULTICMD_CTX *mctx, const void *cookie) +{ + unsigned ii; + OBSERVECTX *ctx = CTX_FROM_MULTI(mctx); + mc_CMDQUEUE *cq = &ctx->instance->cmdq; + + for (ii = 0; ii < ctx->nrequests; ii++) { + protocol_binary_request_header hdr; + mc_PACKET *pkt; + mc_PIPELINE *pipeline; + lcb_string *rr = ctx->requests + ii; + pipeline = cq->pipelines[ii]; + + if (!rr->nused) { + continue; + } + + pkt = mcreq_allocate_packet(pipeline); + lcb_assert(pkt); + + mcreq_reserve_header(pipeline, pkt, MCREQ_PKT_BASESIZE); + mcreq_reserve_value2(pipeline, pkt, rr->nused); + + hdr.request.magic = PROTOCOL_BINARY_REQ; + hdr.request.opcode = PROTOCOL_BINARY_CMD_OBSERVE; + hdr.request.datatype = PROTOCOL_BINARY_RAW_BYTES; + hdr.request.keylen = 0; + hdr.request.cas = 0; + hdr.request.vbucket = 0; + hdr.request.extlen = 0; + hdr.request.opaque = pkt->opaque; + hdr.request.bodylen = htonl((lcb_uint32_t)rr->nused); + + memcpy(SPAN_BUFFER(&pkt->kh_span), hdr.bytes, sizeof(hdr.bytes)); + memcpy(SPAN_BUFFER(&pkt->u_value.single), rr->base, rr->nused); + + pkt->flags |= MCREQ_F_REQEXT; + pkt->u_rdata.exdata = (mc_REQDATAEX *)ctx; + mcreq_sched_add(pipeline, pkt); + TRACE_OBSERVE_BEGIN(&hdr, SPAN_BUFFER(&pkt->u_value.single)); + } + + destroy_requests(ctx); + ctx->base.start = gethrtime(); + ctx->base.cookie = cookie; + ctx->base.procs = &obs_procs; + + if (ctx->nrequests == 0 || ctx->remaining == 0) { + free(ctx); + return LCB_EINVAL; + } else { + MAYBE_SCHEDLEAVE(ctx->instance); + return LCB_SUCCESS; + } +} + +static void +obs_ctxfail(lcb_MULTICMD_CTX *mctx) +{ + OBSERVECTX *ctx = CTX_FROM_MULTI(mctx); + destroy_requests(ctx); + free(ctx); +} + +LIBCOUCHBASE_API +lcb_MULTICMD_CTX * +lcb_observe3_ctxnew(lcb_t instance) +{ + OBSERVECTX *ctx; + size_t ii, n_extra = LCBT_NSERVERS(instance)-1; + ctx = calloc(1, sizeof(*ctx) + sizeof(ctx->requests) * n_extra); + ctx->instance = instance; + ctx->nrequests = n_extra + 1; + ctx->mctx.addcmd = obs_ctxadd; + ctx->mctx.done = obs_ctxdone; + ctx->mctx.fail = obs_ctxfail; + + /* note this block doesn't do anything not done with calloc, but makes for + * easier reading/tracking */ + for (ii = 0; ii < ctx->nrequests; ii++) { + lcb_string_init(ctx->requests + ii); + } + + return &ctx->mctx; +} + +lcb_MULTICMD_CTX * +lcb_observe_ctx_dur_new(lcb_t instance) +{ + lcb_MULTICMD_CTX *mctx = lcb_observe3_ctxnew(instance); + if (mctx) { + OBSERVECTX *ctx = CTX_FROM_MULTI(mctx); + ctx->oflags |= F_DURABILITY; + } + return mctx; +} + +LIBCOUCHBASE_API +lcb_error_t lcb_observe(lcb_t instance, + const void *command_cookie, lcb_size_t num, + const lcb_observe_cmd_t *const *items) +{ + unsigned ii; + lcb_error_t err; + lcb_MULTICMD_CTX *mctx; + + lcb_sched_enter(instance); + + mctx = lcb_observe3_ctxnew(instance); + if (mctx == NULL) { + return LCB_CLIENT_ENOMEM; + } + + for (ii = 0; ii < num; ii++) { + lcb_CMDOBSERVE cmd = { 0 }; + const lcb_observe_cmd_t *src = items[ii]; + if (src->version == 1 && (src->v.v1.options & LCB_OBSERVE_MASTER_ONLY)) { + cmd.cmdflags |= LCB_CMDOBSERVE_F_MASTER_ONLY; + } + LCB_KREQ_SIMPLE(&cmd.key, src->v.v0.key, src->v.v0.nkey); + LCB_KREQ_SIMPLE(&cmd._hashkey, src->v.v0.hashkey, src->v.v0.nhashkey); + + err = mctx->addcmd(mctx, (lcb_CMDBASE *)&cmd); + if (err != LCB_SUCCESS) { + mctx->fail(mctx); + return err; + } + } + lcb_sched_enter(instance); + mctx->done(mctx, command_cookie); + lcb_sched_leave(instance); + SYNCMODE_INTERCEPT(instance) +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/operations/pktfwd.c b/couchbase-sys/libcouchbase-2.7.0/src/operations/pktfwd.c new file mode 100644 index 00000000..fc27d10c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/operations/pktfwd.c @@ -0,0 +1,86 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "mc/mcreq.h" +#include "mc/forward.h" +#include "internal.h" +#include "rdb/rope.h" + +LIBCOUCHBASE_API +lcb_error_t +lcb_pktfwd3(lcb_t instance, const void *cookie, const lcb_CMDPKTFWD *cmd) +{ + int fwdopts = 0; + mc_PIPELINE *pl; + mc_PACKET *packet; + nb_IOV *iov, iov_s; + unsigned niov; + mc_IOVINFO ioi = { { 0 } }; + lcb_error_t err; + + if (cmd->nomap) { + fwdopts |= MC_FWD_OPT_NOMAP; + if (cmd->server_index >= LCBT_NSERVERS(instance)) { + return LCB_NO_MATCHING_SERVER; + } else { + pl = (mc_PIPELINE*)LCBT_GET_SERVER(instance, cmd->server_index); + } + } + + if (cmd->vb.vtype != LCB_KV_IOV) { + iov_s.iov_base = (void *)cmd->vb.u_buf.contig.bytes; + iov_s.iov_len = cmd->vb.u_buf.contig.nbytes; + iov = &iov_s; + niov = 1; + + if (cmd->vb.vtype == LCB_KV_COPY) { + fwdopts |= MC_FWD_OPT_COPY; + } + } else { + iov = (nb_IOV*)cmd->vb.u_buf.multi.iov; + niov = cmd->vb.u_buf.multi.niov; + ioi.total = cmd->vb.u_buf.multi.total_length; + } + mc_iovinfo_init(&ioi, iov, niov); + + err = mc_forward_packet(&instance->cmdq, &ioi, &packet, &pl, fwdopts); + if (err != LCB_SUCCESS) { + return err; + } + + /* set the cookie */ + packet->u_rdata.reqdata.cookie = cookie; + packet->u_rdata.reqdata.start = gethrtime(); + return err; +} + +LIBCOUCHBASE_API +void +lcb_backbuf_ref(lcb_BACKBUF buf) +{ + rdb_seg_ref(buf); +} + +LIBCOUCHBASE_API +void +lcb_backbuf_unref(lcb_BACKBUF buf) +{ + rdb_seg_unref(buf); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/operations/remove.c b/couchbase-sys/libcouchbase-2.7.0/src/operations/remove.c new file mode 100644 index 00000000..9fd6efa6 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/operations/remove.c @@ -0,0 +1,83 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" +#include "trace.h" + +LIBCOUCHBASE_API +lcb_error_t +lcb_remove3(lcb_t instance, const void *cookie, const lcb_CMDREMOVE * cmd) +{ + mc_CMDQUEUE *cq = &instance->cmdq; + mc_PIPELINE *pl; + mc_PACKET *pkt; + lcb_error_t err; + protocol_binary_request_header hdr; + + if (LCB_KEYBUF_IS_EMPTY(&cmd->key)) { + return LCB_EMPTY_KEY; + } + + err = mcreq_basic_packet(cq, cmd, &hdr, 0, &pkt, &pl, + MCREQ_BASICPACKET_F_FALLBACKOK); + if (err != LCB_SUCCESS) { + return err; + } + + + hdr.request.datatype = PROTOCOL_BINARY_RAW_BYTES; + hdr.request.magic = PROTOCOL_BINARY_REQ; + hdr.request.opcode = PROTOCOL_BINARY_CMD_DELETE; + hdr.request.cas = lcb_htonll(cmd->cas); + hdr.request.opaque = pkt->opaque; + hdr.request.bodylen = htonl((lcb_uint32_t)ntohs(hdr.request.keylen)); + + pkt->u_rdata.reqdata.cookie = cookie; + pkt->u_rdata.reqdata.start = gethrtime(); + memcpy(SPAN_BUFFER(&pkt->kh_span), hdr.bytes, sizeof(hdr.bytes)); + TRACE_REMOVE_BEGIN(&hdr, cmd); + LCB_SCHED_ADD(instance, pl, pkt); + return LCB_SUCCESS; +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_remove(lcb_t instance, const void *cookie, lcb_size_t num, + const lcb_remove_cmd_t * const * items) +{ + unsigned ii; + lcb_sched_enter(instance); + + for (ii = 0; ii < num; ii++) { + lcb_error_t err; + const lcb_remove_cmd_t *src = items[ii]; + lcb_CMDREMOVE dst; + memset(&dst, 0, sizeof(dst)); + dst.key.contig.bytes = src->v.v0.key; + dst.key.contig.nbytes = src->v.v0.nkey; + dst._hashkey.contig.bytes = src->v.v0.hashkey; + dst._hashkey.contig.nbytes = src->v.v0.nhashkey; + dst.cas = src->v.v0.cas; + err = lcb_remove3(instance, cookie, &dst); + if (err != LCB_SUCCESS) { + lcb_sched_fail(instance); + return err; + } + } + lcb_sched_leave(instance); + SYNCMODE_INTERCEPT(instance) +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/operations/stats.cc b/couchbase-sys/libcouchbase-2.7.0/src/operations/stats.cc new file mode 100644 index 00000000..f4d30ab3 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/operations/stats.cc @@ -0,0 +1,454 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2011-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" + +struct BcastCookie : mc_REQDATAEX { + lcb_CALLBACKTYPE type; + int remaining; + + BcastCookie(lcb_CALLBACKTYPE type_, + mc_REQDATAPROCS* procs_, + const void *cookie_) { + + mc_REQDATAEX::procs = procs_; + mc_REQDATAEX::cookie = cookie_; + mc_REQDATAEX::start = gethrtime(); + type = type_; + remaining = 0; + } +}; + +static void +refcnt_dtor_common(mc_PACKET *pkt) +{ + BcastCookie *ck = static_cast(pkt->u_rdata.exdata); + if (!--ck->remaining) { + delete ck; + } +} + +static const char * +make_hp_string(const lcb::Server& server, std::string& out) { + out.assign(server.get_host().host); + out.append(":"); + out.append(server.get_host().port); + return out.c_str(); +} + +static void +stats_handler(mc_PIPELINE *pl, mc_PACKET *req, lcb_error_t err, const void *arg) +{ + BcastCookie *ck = static_cast(req->u_rdata.exdata); + lcb::Server *server = static_cast(pl); + lcb_RESPSTATS *resp = reinterpret_cast(const_cast(arg)); + + lcb_RESPCALLBACK callback; + lcb_t instance = server->get_instance(); + + callback = lcb_find_callback(instance, LCB_CALLBACK_STATS); + + if (!arg) { + lcb_RESPSTATS s_resp = { 0 }; + if (--ck->remaining) { + /* still have other servers which must reply. */ + return; + } + + s_resp.rc = err; + s_resp.cookie = const_cast(ck->cookie); + s_resp.rflags = LCB_RESP_F_CLIENTGEN|LCB_RESP_F_FINAL; + callback(instance, LCB_CALLBACK_STATS, (lcb_RESPBASE *)&s_resp); + delete ck; + + } else { + std::string epbuf; + resp->server = make_hp_string(*server, epbuf); + resp->cookie = const_cast(ck->cookie); + callback(instance, LCB_CALLBACK_STATS, (lcb_RESPBASE *)resp); + return; + } +} + +static mc_REQDATAPROCS stats_procs = { + stats_handler, + refcnt_dtor_common +}; + +LIBCOUCHBASE_API +lcb_error_t +lcb_stats3(lcb_t instance, const void *cookie, const lcb_CMDSTATS * cmd) +{ + unsigned ii; + int vbid = -1; + char ksbuf[512] = { 0 }; + mc_CMDQUEUE *cq = &instance->cmdq; + lcbvb_CONFIG *vbc = cq->config; + const lcb_CONTIGBUF *kbuf_in = &cmd->key.contig; + lcb_KEYBUF kbuf_out; + + kbuf_out.type = LCB_KV_COPY; + + if (cmd->cmdflags & LCB_CMDSTATS_F_KV) { + if (kbuf_in->nbytes == 0 || kbuf_in->nbytes > sizeof(ksbuf) - 30) { + return LCB_EINVAL; + } + if (vbc == NULL) { + return LCB_CLIENT_ETMPFAIL; + } + if (lcbvb_get_distmode(vbc) != LCBVB_DIST_VBUCKET) { + return LCB_NOT_SUPPORTED; + } + vbid = lcbvb_k2vb(vbc, kbuf_in->bytes, kbuf_in->nbytes); + if (vbid < 0) { + return LCB_CLIENT_ETMPFAIL; + } + for (ii = 0; ii < kbuf_in->nbytes; ii++) { + if (isspace( ((char *)kbuf_in->bytes)[ii])) { + return LCB_EINVAL; + } + } + sprintf(ksbuf, "key %.*s %d", (int)kbuf_in->nbytes, + (const char *)kbuf_in->bytes, vbid); + kbuf_out.contig.nbytes = strlen(ksbuf); + kbuf_out.contig.bytes = ksbuf; + } else { + kbuf_out.contig = *kbuf_in; + } + + BcastCookie *ckwrap = new BcastCookie(LCB_CALLBACK_STATS, + &stats_procs, cookie); + + for (ii = 0; ii < cq->npipelines; ii++) { + mc_PACKET *pkt; + mc_PIPELINE *pl = cq->pipelines[ii]; + protocol_binary_request_header hdr = { { 0 } }; + + if (vbid > -1 && lcbvb_has_vbucket(vbc, vbid, ii) == 0) { + continue; + } + + pkt = mcreq_allocate_packet(pl); + if (!pkt) { + return LCB_CLIENT_ENOMEM; + } + + hdr.request.opcode = PROTOCOL_BINARY_CMD_STAT; + hdr.request.magic = PROTOCOL_BINARY_REQ; + + if (cmd->key.contig.nbytes) { + mcreq_reserve_key(pl, pkt, MCREQ_PKT_BASESIZE, &kbuf_out); + hdr.request.keylen = ntohs((lcb_U16)kbuf_out.contig.nbytes); + hdr.request.bodylen = ntohl((lcb_U32)kbuf_out.contig.nbytes); + } else { + mcreq_reserve_header(pl, pkt, MCREQ_PKT_BASESIZE); + } + + pkt->u_rdata.exdata = ckwrap; + pkt->flags |= MCREQ_F_REQEXT; + + ckwrap->remaining++; + hdr.request.opaque = pkt->opaque; + memcpy(SPAN_BUFFER(&pkt->kh_span), hdr.bytes, sizeof(hdr.bytes)); + mcreq_sched_add(pl, pkt); + } + + if (!ii) { + delete ckwrap; + return LCB_NO_MATCHING_SERVER; + } + + MAYBE_SCHEDLEAVE(instance); + return LCB_SUCCESS; +} + +static void +handle_bcast(mc_PIPELINE *pipeline, mc_PACKET *req, lcb_error_t err, + const void *arg) +{ + lcb::Server *server = static_cast(pipeline); + BcastCookie *ck = (BcastCookie *)req->u_rdata.exdata; + lcb_RESPCALLBACK callback; + + union { + lcb_RESPSERVERBASE *base; + lcb_RESPVERBOSITY *verbosity; + lcb_RESPMCVERSION *version; + lcb_RESPFLUSH *flush; + } u_resp; + + union { + lcb_RESPSERVERBASE base; + lcb_RESPVERBOSITY verbosity; + lcb_RESPMCVERSION version; + lcb_RESPFLUSH flush; + } u_empty; + + memset(&u_empty, 0, sizeof(u_empty)); + + if (arg) { + u_resp.base = (lcb_RESPSERVERBASE*)arg; + } else { + u_resp.base = &u_empty.base; + u_resp.base->rflags = LCB_RESP_F_CLIENTGEN; + } + + u_resp.base->rc = err; + u_resp.base->cookie = const_cast(ck->cookie); + + std::string epbuf; + u_resp.base->server = make_hp_string(*server, epbuf); + + callback = lcb_find_callback(server->get_instance(), ck->type); + callback(server->get_instance(), ck->type, (lcb_RESPBASE *)u_resp.base); + if (--ck->remaining) { + return; + } + + u_empty.base.server = NULL; + u_empty.base.rc = err; + u_empty.base.rflags = LCB_RESP_F_CLIENTGEN|LCB_RESP_F_FINAL; + u_empty.base.cookie = const_cast(ck->cookie); + callback(server->get_instance(), ck->type, (lcb_RESPBASE *)&u_empty.base); + delete ck; +} + +static mc_REQDATAPROCS bcast_procs = { + handle_bcast, + refcnt_dtor_common +}; + +static lcb_error_t +pkt_bcast_simple(lcb_t instance, const void *cookie, lcb_CALLBACKTYPE type) +{ + mc_CMDQUEUE *cq = &instance->cmdq; + unsigned ii; + + if (!cq->config) { + return LCB_CLIENT_ETMPFAIL; + } + + BcastCookie *ckwrap = new BcastCookie(type, &bcast_procs, cookie); + + for (ii = 0; ii < cq->npipelines; ii++) { + mc_PIPELINE *pl = cq->pipelines[ii]; + mc_PACKET *pkt = mcreq_allocate_packet(pl); + protocol_binary_request_header hdr; + memset(&hdr, 0, sizeof(hdr)); + + if (!pkt) { + return LCB_CLIENT_ENOMEM; + } + + pkt->u_rdata.exdata = ckwrap; + pkt->flags |= MCREQ_F_REQEXT; + + hdr.request.magic = PROTOCOL_BINARY_REQ; + hdr.request.opaque = pkt->opaque; + if (type == LCB_CALLBACK_FLUSH) { + hdr.request.opcode = PROTOCOL_BINARY_CMD_FLUSH; + } else if (type == LCB_CALLBACK_VERSIONS) { + hdr.request.opcode = PROTOCOL_BINARY_CMD_VERSION; + } else { + fprintf(stderr, "pkt_bcast_simple passed unknown type %u\n", type); + assert(0); + } + + mcreq_reserve_header(pl, pkt, MCREQ_PKT_BASESIZE); + memcpy(SPAN_BUFFER(&pkt->kh_span), hdr.bytes, sizeof(hdr.bytes)); + mcreq_sched_add(pl, pkt); + ckwrap->remaining++; + } + + if (ii == 0) { + delete ckwrap; + return LCB_NO_MATCHING_SERVER; + } + MAYBE_SCHEDLEAVE(instance); + return LCB_SUCCESS; +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_server_versions3(lcb_t instance, const void *cookie, const lcb_CMDBASE *) +{ + return pkt_bcast_simple(instance, cookie, LCB_CALLBACK_VERSIONS); +} + + +LIBCOUCHBASE_API +lcb_error_t +lcb_flush3(lcb_t instance, const void *cookie, const lcb_CMDFLUSH *) +{ + return pkt_bcast_simple(instance, cookie, LCB_CALLBACK_FLUSH); +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_server_verbosity3(lcb_t instance, const void *cookie, + const lcb_CMDVERBOSITY *cmd) +{ + mc_CMDQUEUE *cq = &instance->cmdq; + unsigned ii; + + if (!cq->config) { + return LCB_CLIENT_ETMPFAIL; + } + + BcastCookie *ckwrap = new BcastCookie( + LCB_CALLBACK_VERBOSITY, &bcast_procs, cookie); + + for (ii = 0; ii < cq->npipelines; ii++) { + mc_PACKET *pkt; + lcb::Server *server = static_cast(cq->pipelines[ii]); + protocol_binary_request_verbosity vcmd; + protocol_binary_request_header *hdr = &vcmd.message.header; + uint32_t level; + + std::string cmpbuf; + make_hp_string(*server, cmpbuf); + if (cmd->server && cmpbuf != cmd->server) { + continue; + } + + if (cmd->level == LCB_VERBOSITY_DETAIL) { + level = 3; + } else if (cmd->level == LCB_VERBOSITY_DEBUG) { + level = 2; + } else if (cmd->level == LCB_VERBOSITY_INFO) { + level = 1; + } else { + level = 0; + } + + pkt = mcreq_allocate_packet(server); + if (!pkt) { + return LCB_CLIENT_ENOMEM; + } + + pkt->u_rdata.exdata = ckwrap; + pkt->flags |= MCREQ_F_REQEXT; + + mcreq_reserve_header(server, pkt, MCREQ_PKT_BASESIZE + 4); + hdr->request.magic = PROTOCOL_BINARY_REQ; + hdr->request.opcode = PROTOCOL_BINARY_CMD_VERBOSITY; + hdr->request.datatype = PROTOCOL_BINARY_RAW_BYTES; + hdr->request.cas = 0; + hdr->request.vbucket = 0; + hdr->request.opaque = pkt->opaque; + hdr->request.extlen = 4; + hdr->request.keylen = 0; + hdr->request.bodylen = htonl((uint32_t)hdr->request.extlen); + vcmd.message.body.level = htonl((uint32_t)level); + + memcpy(SPAN_BUFFER(&pkt->kh_span), vcmd.bytes, sizeof(vcmd.bytes)); + mcreq_sched_add(server, pkt); + ckwrap->remaining++; + } + + if (!ckwrap->remaining) { + delete ckwrap; + return LCB_NO_MATCHING_SERVER; + } + MAYBE_SCHEDLEAVE(instance); + return LCB_SUCCESS; +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_server_stats(lcb_t instance, const void *cookie, lcb_size_t num, + const lcb_server_stats_cmd_t * const * items) +{ + lcb_sched_enter(instance); + for (size_t ii = 0; ii < num; ii++) { + const lcb_server_stats_cmd_t *src = items[ii]; + lcb_CMDSTATS dst; + lcb_error_t err; + + memset(&dst, 0, sizeof(dst)); + dst.key.contig.bytes = src->v.v0.name; + dst.key.contig.nbytes = src->v.v0.nname; + err = lcb_stats3(instance, cookie, &dst); + if (err != LCB_SUCCESS) { + lcb_sched_fail(instance); + return err; + } + } + lcb_sched_leave(instance); + SYNCMODE_INTERCEPT(instance) +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_set_verbosity(lcb_t instance, const void *cookie, lcb_size_t num, + const lcb_verbosity_cmd_t * const * items) +{ + lcb_sched_enter(instance); + for (size_t ii = 0; ii < num; ii++) { + lcb_CMDVERBOSITY dst; + lcb_error_t err; + const lcb_verbosity_cmd_t *src = items[ii]; + + memset(&dst, 0, sizeof(dst)); + dst.level = src->v.v0.level; + dst.server = src->v.v0.server; + err = lcb_server_verbosity3(instance, cookie, &dst); + if (err != LCB_SUCCESS) { + lcb_sched_fail(instance); + return err; + } + } + lcb_sched_leave(instance); + SYNCMODE_INTERCEPT(instance) +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_flush(lcb_t instance, const void *cookie, lcb_size_t num, + const lcb_flush_cmd_t * const *) +{ + lcb_sched_enter(instance); + for (size_t ii = 0; ii < num; ii++) { + lcb_error_t rc = lcb_flush3(instance, cookie, NULL); + if (rc != LCB_SUCCESS) { + lcb_sched_fail(instance); + return rc; + } + } + lcb_sched_leave(instance); + SYNCMODE_INTERCEPT(instance) +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_server_versions(lcb_t instance, const void *cookie, lcb_size_t num, + const lcb_server_version_cmd_t * const *) +{ + lcb_sched_enter(instance); + + for (size_t ii = 0; ii < num; ii++) { + lcb_error_t rc = lcb_server_versions3(instance, cookie, NULL); + if (rc != LCB_SUCCESS) { + lcb_sched_fail(instance); + return rc; + } + } + + + lcb_sched_leave(instance); + SYNCMODE_INTERCEPT(instance) +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/operations/store.c b/couchbase-sys/libcouchbase-2.7.0/src/operations/store.c new file mode 100644 index 00000000..eca3111b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/operations/store.c @@ -0,0 +1,360 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "internal.h" +#include "mc/compress.h" +#include "trace.h" +#include "durability_internal.h" + +typedef struct { + mc_REQDATAEX base; + lcb_t instance; + lcb_U16 persist_to; + lcb_U16 replicate_to; +} DURSTORECTX; + +/** Observe stuff */ +static void +handle_dur_storecb(mc_PIPELINE *pl, mc_PACKET *pkt, + lcb_error_t err, const void *arg) +{ + lcb_RESPCALLBACK cb; + lcb_RESPSTOREDUR resp = { 0 }; + lcb_CMDENDURE dcmd = { 0 }; + const lcb_MUTATION_TOKEN *mt; + DURSTORECTX *dctx = (DURSTORECTX *)pkt->u_rdata.exdata; + lcb_MULTICMD_CTX *mctx; + lcb_durability_opts_t opts = { 0 }; + const lcb_RESPSTORE *sresp = (const lcb_RESPSTORE *)arg; + + if (err != LCB_SUCCESS) { + goto GT_BAIL; + } + if (sresp->rc != LCB_SUCCESS) { + err = sresp->rc; + goto GT_BAIL; + } + + resp.store_ok = 1; + LCB_CMD_SET_KEY(&dcmd, sresp->key, sresp->nkey); + dcmd.cas = sresp->cas; + + mt = lcb_resp_get_mutation_token(LCB_CALLBACK_STORE, (const lcb_RESPBASE*)sresp); + if (LCB_MUTATION_TOKEN_ISVALID(mt)) { + dcmd.mutation_token = mt; + } + + /* Set the options.. */ + opts.v.v0.persist_to = dctx->persist_to; + opts.v.v0.replicate_to = dctx->replicate_to; + + mctx = lcb_endure3_ctxnew(dctx->instance, &opts, &err); + if (mctx == NULL) { + goto GT_BAIL; + } + + lcbdurctx_set_durstore(mctx, 1); + err = mctx->addcmd(mctx, (lcb_CMDBASE*)&dcmd); + if (err != LCB_SUCCESS) { + mctx->fail(mctx); + goto GT_BAIL; + } + lcb_sched_enter(dctx->instance); + err = mctx->done(mctx, sresp->cookie); + lcb_sched_leave(dctx->instance); + + if (err == LCB_SUCCESS) { + /* Everything OK? */ + free(dctx); + return; + } + + GT_BAIL: + { + lcb_RESPENDURE dresp = { 0 }; + resp.key = sresp->key; + resp.nkey = sresp->nkey; + resp.cookie = sresp->cookie; + resp.rc = err; + resp.dur_resp = &dresp; + cb = lcb_find_callback(dctx->instance, LCB_CALLBACK_STOREDUR); + cb(dctx->instance, LCB_CALLBACK_STOREDUR, (const lcb_RESPBASE*)&resp); + free(dctx); + } + + (void)pl; +} + +static void +handle_dur_schedfail(mc_PACKET *pkt) +{ + DURSTORECTX *dctx = (void *)pkt->u_rdata.exdata; + free(dctx); +} + +mc_REQDATAPROCS storedur_procs = { + handle_dur_storecb, + handle_dur_schedfail +}; + +static lcb_size_t +get_value_size(mc_PACKET *packet) +{ + if (packet->flags & MCREQ_F_VALUE_IOV) { + return packet->u_value.multi.total_length; + } else { + return packet->u_value.single.size; + } +} + +static lcb_error_t +get_esize_and_opcode( + lcb_storage_t ucmd, lcb_uint8_t *opcode, lcb_uint8_t *esize) +{ + if (ucmd == LCB_SET || ucmd == LCB_UPSERT) { + *opcode = PROTOCOL_BINARY_CMD_SET; + *esize = 8; + } else if (ucmd == LCB_ADD) { + *opcode = PROTOCOL_BINARY_CMD_ADD; + *esize = 8; + } else if (ucmd == LCB_REPLACE) { + *opcode = PROTOCOL_BINARY_CMD_REPLACE; + *esize = 8; + } else if (ucmd == LCB_APPEND) { + *opcode = PROTOCOL_BINARY_CMD_APPEND; + *esize = 0; + } else if (ucmd == LCB_PREPEND) { + *opcode = PROTOCOL_BINARY_CMD_PREPEND; + *esize = 0; + } else { + return LCB_EINVAL; + } + return LCB_SUCCESS; +} + + +static int +can_compress(lcb_t instance, const mc_PIPELINE *pipeline, + const lcb_VALBUF *vbuf, lcb_datatype_t datatype) +{ + mc_SERVER *server = (mc_SERVER *)pipeline; + int compressopts = LCBT_SETTING(instance, compressopts); + + if (mcreq_compression_supported() == 0) { + return 0; + } + + if (vbuf->vtype != LCB_KV_COPY) { + return 0; + } + if ((compressopts & LCB_COMPRESS_OUT) == 0) { + return 0; + } + if (mcserver_supports_compression(server) == 0 && (compressopts & LCB_COMPRESS_FORCE) == 0) { + return 0; + } + if (datatype & LCB_VALUE_F_SNAPPYCOMP) { + return 0; + } + return 1; +} + +static lcb_error_t +do_store3(lcb_t instance, const void *cookie, + const lcb_CMDBASE *cmd, int is_durstore) +{ + mc_PIPELINE *pipeline; + mc_PACKET *packet; + mc_REQDATA *rdata; + mc_CMDQUEUE *cq = &instance->cmdq; + int hsize; + int should_compress = 0; + lcb_error_t err; + + lcb_storage_t operation; + lcb_U32 flags; + const lcb_VALBUF *vbuf; + lcb_datatype_t datatype; + + protocol_binary_request_set scmd; + protocol_binary_request_header *hdr = &scmd.message.header; + + if (!is_durstore) { + const lcb_CMDSTORE *simple_cmd = (const lcb_CMDSTORE *)cmd; + operation = simple_cmd->operation; + flags = simple_cmd->flags; + vbuf = &simple_cmd->value; + datatype = simple_cmd->datatype; + } else { + const lcb_CMDSTOREDUR *durcmd = (const lcb_CMDSTOREDUR *)cmd; + operation = durcmd->operation; + flags = durcmd->flags; + vbuf = &durcmd->value; + datatype = durcmd->datatype; + } + + if (LCB_KEYBUF_IS_EMPTY(&cmd->key)) { + return LCB_EMPTY_KEY; + } + + err = get_esize_and_opcode( + operation, &hdr->request.opcode, &hdr->request.extlen); + if (err != LCB_SUCCESS) { + return err; + } + + switch (operation) { + case LCB_APPEND: + case LCB_PREPEND: + if (cmd->exptime || flags) { + return LCB_OPTIONS_CONFLICT; + } + break; + case LCB_ADD: + if (cmd->cas) { + return LCB_OPTIONS_CONFLICT; + } + break; + default: + break; + } + + hsize = hdr->request.extlen + sizeof(*hdr); + + err = mcreq_basic_packet(cq, (const lcb_CMDBASE *)cmd, hdr, + hdr->request.extlen, &packet, &pipeline, MCREQ_BASICPACKET_F_FALLBACKOK); + + if (err != LCB_SUCCESS) { + return err; + } + + should_compress = can_compress(instance, pipeline, vbuf, datatype); + if (should_compress) { + int rv = mcreq_compress_value(pipeline, packet, &vbuf->u_buf.contig); + if (rv != 0) { + mcreq_release_packet(pipeline, packet); + return LCB_CLIENT_ENOMEM; + } + } else { + mcreq_reserve_value(pipeline, packet, vbuf); + } + + if (is_durstore) { + int duropts = 0; + lcb_U16 persist_u , replicate_u; + const lcb_CMDSTOREDUR *dcmd = (const lcb_CMDSTOREDUR *)cmd; + DURSTORECTX *dctx = calloc(1, sizeof(*dctx)); + + persist_u = dcmd->persist_to; + replicate_u = dcmd->replicate_to; + if (dcmd->replicate_to == -1 || dcmd->persist_to == -1) { + duropts = LCB_DURABILITY_VALIDATE_CAPMAX; + } + + err = lcb_durability_validate(instance, &persist_u, &replicate_u, duropts); + if (err != LCB_SUCCESS) { + mcreq_wipe_packet(pipeline, packet); + mcreq_release_packet(pipeline, packet); + free(dctx); + return err; + } + + dctx->instance = instance; + dctx->persist_to = persist_u; + dctx->replicate_to = replicate_u; + packet->u_rdata.exdata = &dctx->base; + packet->flags |= MCREQ_F_REQEXT; + + dctx->base.cookie = cookie; + dctx->base.procs = &storedur_procs; + } + + rdata = MCREQ_PKT_RDATA(packet); + rdata->cookie = cookie; + rdata->start = gethrtime(); + + scmd.message.body.expiration = htonl(cmd->exptime); + scmd.message.body.flags = htonl(flags); + hdr->request.magic = PROTOCOL_BINARY_REQ; + hdr->request.cas = lcb_htonll(cmd->cas); + hdr->request.datatype = PROTOCOL_BINARY_RAW_BYTES; + + if (should_compress || (datatype & LCB_VALUE_F_SNAPPYCOMP)) { + hdr->request.datatype |= PROTOCOL_BINARY_DATATYPE_COMPRESSED; + } + if (datatype & LCB_VALUE_F_JSON) { + hdr->request.datatype |= PROTOCOL_BINARY_DATATYPE_JSON; + } + + hdr->request.opaque = packet->opaque; + hdr->request.bodylen = htonl( + hdr->request.extlen + ntohs(hdr->request.keylen) + + get_value_size(packet)); + + memcpy(SPAN_BUFFER(&packet->kh_span), scmd.bytes, hsize); + LCB_SCHED_ADD(instance, pipeline, packet); + TRACE_STORE_BEGIN(hdr, (lcb_CMDSTORE* )cmd); + return LCB_SUCCESS; +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_store3(lcb_t instance, const void *cookie, const lcb_CMDSTORE *cmd) +{ + return do_store3(instance, cookie, (const lcb_CMDBASE*)cmd, 0); +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_storedur3(lcb_t instance, const void *cookie, const lcb_CMDSTOREDUR *cmd) +{ + return do_store3(instance, cookie, (const lcb_CMDBASE*)cmd, 1); +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_store(lcb_t instance, const void *cookie, lcb_size_t num, + const lcb_store_cmd_t * const * items) +{ + unsigned ii; + lcb_error_t err = LCB_SUCCESS; + + lcb_sched_enter(instance); + for (ii = 0; ii < num; ii++) { + const lcb_store_cmd_t *src = items[ii]; + lcb_CMDSTORE dst; + memset(&dst, 0, sizeof(dst)); + + dst.key.contig.bytes = src->v.v0.key; + dst.key.contig.nbytes = src->v.v0.nkey; + dst._hashkey.contig.bytes = src->v.v0.hashkey; + dst._hashkey.contig.nbytes = src->v.v0.nhashkey; + dst.value.u_buf.contig.bytes = src->v.v0.bytes; + dst.value.u_buf.contig.nbytes = src->v.v0.nbytes; + dst.operation = src->v.v0.operation; + dst.flags = src->v.v0.flags; + dst.datatype = src->v.v0.datatype; + dst.cas = src->v.v0.cas; + dst.exptime = src->v.v0.exptime; + err = lcb_store3(instance, cookie, &dst); + if (err != LCB_SUCCESS) { + lcb_sched_fail(instance); + return err; + } + } + lcb_sched_leave(instance); + SYNCMODE_INTERCEPT(instance) +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/operations/subdoc.cc b/couchbase-sys/libcouchbase-2.7.0/src/operations/subdoc.cc new file mode 100644 index 00000000..a0f9151d --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/operations/subdoc.cc @@ -0,0 +1,510 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2015 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "internal.h" +#include +#include +#include + +static lcb_size_t +get_value_size(mc_PACKET *packet) +{ + if (packet->flags & MCREQ_F_HASVALUE) { + if (packet->flags & MCREQ_F_VALUE_IOV) { + return packet->u_value.multi.total_length; + } else { + return packet->u_value.single.size; + } + } else { + return 0; + } +} + +namespace SubdocCmdTraits { +enum Options { + EMPTY_PATH = 1<<0, + ALLOW_EXPIRY = 1<<1, + HAS_VALUE = 1<<2, + ALLOW_MKDIRP = 1<<3, + IS_LOOKUP = 1<<4 +}; + +struct Traits { + const unsigned allow_empty_path; + const unsigned allow_expiry; + const unsigned has_value; + const unsigned allow_mkdir_p; + const unsigned is_lookup; + const uint8_t opcode; + + inline bool valid() const { + return opcode != 0; + } + + inline unsigned mode() const { + return is_lookup ? LCB_SDMULTI_MODE_LOOKUP : LCB_SDMULTI_MODE_MUTATE; + } + + inline Traits(uint8_t op, unsigned options) : + allow_empty_path(options & EMPTY_PATH), + allow_expiry(options & ALLOW_EXPIRY), + has_value(options & HAS_VALUE), + allow_mkdir_p(options & ALLOW_MKDIRP), + is_lookup(options & IS_LOOKUP), + opcode(op) {} +}; + +static const Traits +Get(PROTOCOL_BINARY_CMD_SUBDOC_GET, IS_LOOKUP); + +static const Traits +Exists(PROTOCOL_BINARY_CMD_SUBDOC_EXISTS, IS_LOOKUP); + +static const Traits +GetCount(PROTOCOL_BINARY_CMD_SUBDOC_GET_COUNT, IS_LOOKUP|EMPTY_PATH); + +static const Traits +DictAdd(PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD, ALLOW_EXPIRY|HAS_VALUE); + +static const Traits +DictUpsert(PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT, + ALLOW_EXPIRY|HAS_VALUE|ALLOW_MKDIRP); + +static const Traits +Remove(PROTOCOL_BINARY_CMD_SUBDOC_DELETE, ALLOW_EXPIRY); + +static const Traits +ArrayInsert(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_INSERT, + ALLOW_EXPIRY|HAS_VALUE); + +static const Traits +Replace(PROTOCOL_BINARY_CMD_SUBDOC_REPLACE, ALLOW_EXPIRY|HAS_VALUE); + +static const Traits +ArrayAddFirst(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST, + ALLOW_EXPIRY|HAS_VALUE|EMPTY_PATH|ALLOW_MKDIRP); + +static const Traits +ArrayAddLast(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST, + ALLOW_EXPIRY|HAS_VALUE|EMPTY_PATH|ALLOW_MKDIRP); + +static const Traits +ArrayAddUnique(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_ADD_UNIQUE, + ALLOW_EXPIRY|HAS_VALUE|EMPTY_PATH|ALLOW_MKDIRP); + +static const Traits +Counter(PROTOCOL_BINARY_CMD_SUBDOC_COUNTER, ALLOW_EXPIRY|HAS_VALUE|ALLOW_MKDIRP); + +static const Traits +Invalid(0, 0); + +const Traits& +find(unsigned mode) +{ + switch (mode) { + case LCB_SDCMD_REPLACE: + return Replace; + case LCB_SDCMD_DICT_ADD: + return DictAdd; + case LCB_SDCMD_DICT_UPSERT: + return DictUpsert; + case LCB_SDCMD_ARRAY_ADD_FIRST: + return ArrayAddFirst; + case LCB_SDCMD_ARRAY_ADD_LAST: + return ArrayAddLast; + case LCB_SDCMD_ARRAY_ADD_UNIQUE: + return ArrayAddUnique; + case LCB_SDCMD_ARRAY_INSERT: + return ArrayInsert; + case LCB_SDCMD_GET: + return Get; + case LCB_SDCMD_EXISTS: + return Exists; + case LCB_SDCMD_GET_COUNT: + return GetCount; + case LCB_SDCMD_REMOVE: + return Remove; + case LCB_SDCMD_COUNTER: + return Counter; + default: + return Invalid; + } +} +} + +static size_t +get_valbuf_size(const lcb_VALBUF& vb) +{ + if (vb.vtype == LCB_KV_COPY || vb.vtype == LCB_KV_CONTIG) { + return vb.u_buf.contig.nbytes; + } else { + if (vb.u_buf.multi.total_length) { + return vb.u_buf.multi.total_length; + } else { + size_t tmp = 0; + for (size_t ii = 0; ii < vb.u_buf.multi.niov; ++ii) { + tmp += vb.u_buf.multi.iov[ii].iov_len; + } + return tmp; + } + } +} + +struct MultiBuilder { + MultiBuilder(const lcb_CMDSUBDOC *cmd_) + : cmd(cmd_), payload_size(0), mode(0) { + size_t ebufsz = is_lookup() ? cmd->nspecs * 4 : cmd->nspecs * 8; + extra_body = new char[ebufsz]; + bodysz = 0; + } + + ~MultiBuilder() { + if (extra_body != NULL) { + delete[] extra_body; + } + } + + inline MultiBuilder(const MultiBuilder&); + + // IOVs which are fed into lcb_VALBUF for subsequent use + const lcb_CMDSUBDOC *cmd; + std::vector iovs; + char *extra_body; + size_t bodysz; + + // Total size of the payload itself + size_t payload_size; + + unsigned mode; + + bool is_lookup() const { + return mode == LCB_SDMULTI_MODE_LOOKUP; + } + + bool is_mutate() const { + return mode == LCB_SDMULTI_MODE_MUTATE; + } + + void maybe_setmode(const SubdocCmdTraits::Traits& t) { + if (mode == 0) { + mode = t.mode(); + } + } + + template void add_field(T itm, size_t len) { + const char *b = reinterpret_cast(&itm); + memcpy(extra_body + bodysz, b, len); + bodysz += len; + } + + const char *extra_mark() const { + return extra_body + bodysz; + } + + void add_extras_iov(const char *last_begin) { + const char *p_end = extra_mark(); + add_iov(last_begin, p_end - last_begin); + } + + void add_iov(const void *b, size_t n) { + if (!n) { + return; + } + + lcb_IOV iov; + iov.iov_base = const_cast(b); + iov.iov_len = n; + iovs.push_back(iov); + payload_size += n; + } + + void add_iov(const lcb_VALBUF& vb) { + if (vb.vtype == LCB_KV_CONTIG || vb.vtype == LCB_KV_COPY) { + add_iov(vb.u_buf.contig.bytes, vb.u_buf.contig.nbytes); + } else { + for (size_t ii = 0; ii < vb.u_buf.contig.nbytes; ++ii) { + const lcb_IOV& iov = vb.u_buf.multi.iov[ii]; + if (!iov.iov_len) { + continue; // Skip it + } + payload_size += iov.iov_len; + iovs.push_back(iov); + } + } + } + + inline lcb_error_t add_spec(const lcb_SDSPEC *); +}; + +lcb_error_t +MultiBuilder::add_spec(const lcb_SDSPEC *spec) +{ + const SubdocCmdTraits::Traits& trait = SubdocCmdTraits::find(spec->sdcmd); + if (!trait.valid()) { + return LCB_UNKNOWN_SDCMD; + } + maybe_setmode(trait); + + if (trait.mode() != mode) { + return LCB_OPTIONS_CONFLICT; + } + + const char *p_begin = extra_mark(); + // opcode + add_field(trait.opcode, 1); + // flags + uint8_t sdflags = 0; + if (spec->options & LCB_SDSPEC_F_MKINTERMEDIATES) { + sdflags = SUBDOC_FLAG_MKDIR_P; + } + if (spec->options & LCB_SDSPEC_F_MKDOCUMENT) { + sdflags |= SUBDOC_FLAG_MKDOC; + } + add_field(sdflags, 1); + + uint16_t npath = static_cast(spec->path.contig.nbytes); + if (!npath && !trait.allow_empty_path) { + return LCB_EMPTY_PATH; + } + + // Path length + add_field(static_cast(htons(npath)), 2); + + uint32_t vsize = 0; + if (is_mutate()) { + // Mutation needs an additional 'value' spec. + vsize = get_valbuf_size(spec->value); + add_field(static_cast(htonl(vsize)), 4); + } + + // Finalize the header.. + add_extras_iov(p_begin); + + // Add the actual path + add_iov(spec->path.contig.bytes, spec->path.contig.nbytes); + if (vsize) { + add_iov(spec->value); + } + return LCB_SUCCESS; +} + + +static lcb_error_t +sd3_single(lcb_t instance, const void *cookie, const lcb_CMDSUBDOC *cmd) +{ + // Find the trait + const lcb_SDSPEC *spec = cmd->specs; + const SubdocCmdTraits::Traits& traits = SubdocCmdTraits::find(spec->sdcmd); + lcb_error_t rc; + + // Any error here is implicitly related to the only spec + if (cmd->error_index) { + *cmd->error_index = 0; + } + + if (!traits.valid()) { + return LCB_UNKNOWN_SDCMD; + } + + // Determine if the trait matches the mode. Technically we don't care + // about this (since it's always a single command) but we do want the + // API to remain consistent. + if (cmd->multimode != 0 && cmd->multimode != traits.mode()) { + return LCB_OPTIONS_CONFLICT; + } + + if (LCB_KEYBUF_IS_EMPTY(&cmd->key)) { + return LCB_EMPTY_KEY; + } + if (LCB_KEYBUF_IS_EMPTY(&spec->path) && !traits.allow_empty_path) { + return LCB_EMPTY_PATH; + } + + lcb_VALBUF valbuf; + const lcb_VALBUF *valbuf_p = &valbuf; + lcb_IOV tmpiov[2]; + lcb_FRAGBUF *fbuf = &valbuf.u_buf.multi; + + valbuf.vtype = LCB_KV_IOVCOPY; + fbuf->iov = tmpiov; + fbuf->niov = 1; + fbuf->total_length = 0; + tmpiov[0].iov_base = const_cast(spec->path.contig.bytes); + tmpiov[0].iov_len = spec->path.contig.nbytes; + + if (traits.has_value) { + if (spec->value.vtype == LCB_KV_COPY) { + fbuf->niov = 2; + /* Subdoc value is the second IOV */ + tmpiov[1].iov_base = (void *)spec->value.u_buf.contig.bytes; + tmpiov[1].iov_len = spec->value.u_buf.contig.nbytes; + } else { + /* Assume properly formatted packet */ + valbuf_p = &spec->value; + } + } + + uint8_t extlen = 3; + uint32_t exptime = 0; + if (cmd->exptime) { + if (!traits.allow_expiry) { + return LCB_OPTIONS_CONFLICT; + } + exptime = cmd->exptime; + extlen = 7; + } + + protocol_binary_request_subdocument request; + protocol_binary_request_header *hdr = &request.message.header; + mc_PACKET *packet; + mc_PIPELINE *pipeline; + + rc = mcreq_basic_packet(&instance->cmdq, + (const lcb_CMDBASE*)cmd, + hdr, extlen, &packet, &pipeline, MCREQ_BASICPACKET_F_FALLBACKOK); + + if (rc != LCB_SUCCESS) { + return rc; + } + + rc = mcreq_reserve_value(pipeline, packet, valbuf_p); + if (rc != LCB_SUCCESS) { + mcreq_wipe_packet(pipeline, packet); + mcreq_release_packet(pipeline, packet); + return rc; + } + + MCREQ_PKT_RDATA(packet)->cookie = cookie; + MCREQ_PKT_RDATA(packet)->start = gethrtime(); + + hdr->request.magic = PROTOCOL_BINARY_REQ; + hdr->request.datatype = PROTOCOL_BINARY_RAW_BYTES; + hdr->request.extlen = packet->extlen; + hdr->request.opaque = packet->opaque; + hdr->request.cas = lcb_htonll(cmd->cas); + hdr->request.bodylen = htonl(hdr->request.extlen + + ntohs(hdr->request.keylen) + get_value_size(packet)); + + request.message.extras.pathlen = htons(spec->path.contig.nbytes); + request.message.extras.subdoc_flags = 0; + + if (spec->options & LCB_SDSPEC_F_MKINTERMEDIATES) { + request.message.extras.subdoc_flags |= SUBDOC_FLAG_MKDIR_P; + } + if (spec->options & LCB_SDSPEC_F_MKDOCUMENT) { + request.message.extras.subdoc_flags |= SUBDOC_FLAG_MKDOC; + } + + hdr->request.opcode = traits.opcode; + memcpy(SPAN_BUFFER(&packet->kh_span), request.bytes, sizeof request.bytes); + if (exptime) { + exptime = htonl(exptime); + memcpy(SPAN_BUFFER(&packet->kh_span) + sizeof request.bytes, &exptime, 4); + } + + LCB_SCHED_ADD(instance, pipeline, packet); + return LCB_SUCCESS; +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_subdoc3(lcb_t instance, const void *cookie, const lcb_CMDSUBDOC *cmd) +{ + // First validate the command + if (cmd->nspecs == 0) { + return LCB_ENO_COMMANDS; + } + + if (cmd->nspecs == 1) { + return sd3_single(instance, cookie, cmd); + } + + uint32_t exp = cmd->exptime; + lcb_error_t rc = LCB_SUCCESS; + + MultiBuilder ctx(cmd); + if (cmd->error_index) { + *cmd->error_index = -1; + } + + if (exp && !ctx.is_mutate()) { + return LCB_OPTIONS_CONFLICT; + } + + for (size_t ii = 0; ii < cmd->nspecs; ++ii) { + if (cmd->error_index) { + *cmd->error_index = ii; + } + rc = ctx.add_spec(cmd->specs + ii); + if (rc != LCB_SUCCESS) { + return rc; + } + } + + mc_PIPELINE *pl; + mc_PACKET *pkt; + uint8_t extlen = exp ? 4 : 0; + protocol_binary_request_header hdr; + + if (cmd->error_index) { + *cmd->error_index = -1; + } + + rc = mcreq_basic_packet( + &instance->cmdq, reinterpret_cast(cmd), + &hdr, extlen, &pkt, &pl, MCREQ_BASICPACKET_F_FALLBACKOK); + + if (rc != LCB_SUCCESS) { + return rc; + } + + lcb_VALBUF vb = { LCB_KV_IOVCOPY }; + vb.u_buf.multi.iov = &ctx.iovs[0]; + vb.u_buf.multi.niov = ctx.iovs.size(); + vb.u_buf.multi.total_length = ctx.payload_size; + rc = mcreq_reserve_value(pl, pkt, &vb); + + if (rc != LCB_SUCCESS) { + mcreq_wipe_packet(pl, pkt); + mcreq_release_packet(pl, pkt); + return rc; + } + + // Set the header fields. + hdr.request.magic = PROTOCOL_BINARY_REQ; + if (ctx.is_lookup()) { + hdr.request.opcode = PROTOCOL_BINARY_CMD_SUBDOC_MULTI_LOOKUP; + } else { + hdr.request.opcode = PROTOCOL_BINARY_CMD_SUBDOC_MULTI_MUTATION; + } + hdr.request.datatype = PROTOCOL_BINARY_RAW_BYTES; + hdr.request.extlen = pkt->extlen; + hdr.request.opaque = pkt->opaque; + hdr.request.cas = lcb_htonll(cmd->cas); + hdr.request.bodylen = htonl(hdr.request.extlen + + ntohs(hdr.request.keylen) + ctx.payload_size); + memcpy(SPAN_BUFFER(&pkt->kh_span), hdr.bytes, sizeof hdr.bytes); + if (exp) { + exp = htonl(exp); + memcpy(SPAN_BUFFER(&pkt->kh_span) + 24, &exp, 4); + } + + MCREQ_PKT_RDATA(pkt)->cookie = cookie; + MCREQ_PKT_RDATA(pkt)->start = gethrtime(); + LCB_SCHED_ADD(instance, pl, pkt); + return LCB_SUCCESS; +} + diff --git a/couchbase-sys/libcouchbase-2.7.0/src/operations/touch.c b/couchbase-sys/libcouchbase-2.7.0/src/operations/touch.c new file mode 100644 index 00000000..e6add26e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/operations/touch.c @@ -0,0 +1,81 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" + +LIBCOUCHBASE_API +lcb_error_t +lcb_touch3(lcb_t instance, const void *cookie, const lcb_CMDTOUCH *cmd) +{ + protocol_binary_request_touch tcmd; + protocol_binary_request_header *hdr = &tcmd.message.header; + mc_PIPELINE *pl; + mc_PACKET *pkt; + lcb_error_t err; + + if (LCB_KEYBUF_IS_EMPTY(&cmd->key)) { + return LCB_EMPTY_KEY; + } + + err = mcreq_basic_packet(&instance->cmdq, cmd, hdr, 4, &pkt, &pl, + MCREQ_BASICPACKET_F_FALLBACKOK); + if (err != LCB_SUCCESS) { + return err; + } + + hdr->request.magic = PROTOCOL_BINARY_REQ; + hdr->request.opcode = PROTOCOL_BINARY_CMD_TOUCH; + hdr->request.cas = 0; + hdr->request.datatype = PROTOCOL_BINARY_RAW_BYTES; + hdr->request.opaque = pkt->opaque; + hdr->request.bodylen = htonl(4 + ntohs(hdr->request.keylen)); + tcmd.message.body.expiration = htonl(cmd->exptime); + memcpy(SPAN_BUFFER(&pkt->kh_span), tcmd.bytes, sizeof(tcmd.bytes)); + pkt->u_rdata.reqdata.cookie = cookie; + pkt->u_rdata.reqdata.start = gethrtime(); + LCB_SCHED_ADD(instance, pl, pkt); + return LCB_SUCCESS; +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_touch(lcb_t instance, const void *cookie, lcb_size_t num, + const lcb_touch_cmd_t * const * items) +{ + unsigned ii; + + lcb_sched_enter(instance); + for (ii = 0; ii < num; ii++) { + const lcb_touch_cmd_t *src = items[ii]; + lcb_CMDTOUCH dst; + lcb_error_t err; + + memset(&dst, 0, sizeof(dst)); + dst.key.contig.bytes = src->v.v0.key; + dst.key.contig.nbytes = src->v.v0.nkey; + dst._hashkey.contig.bytes = src->v.v0.hashkey; + dst._hashkey.contig.nbytes = src->v.v0.nhashkey; + dst.exptime = src->v.v0.exptime; + err = lcb_touch3(instance, cookie, &dst); + if (err != LCB_SUCCESS) { + lcb_sched_fail(instance); + return err; + } + } + lcb_sched_leave(instance); + SYNCMODE_INTERCEPT(instance) +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/packetutils.c b/couchbase-sys/libcouchbase-2.7.0/src/packetutils.c new file mode 100644 index 00000000..ee55b55d --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/packetutils.c @@ -0,0 +1,37 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "packetutils.h" +#include "rdb/rope.h" + +#ifndef _WIN32 /* for win32 this is inside winsock, included in sysdefs.h */ +#include +#endif + +int +lcb_pktinfo_ior_get(packet_info *info, rdb_IOROPE *ior, unsigned *required) +{ +} + +void +lcb_pktinfo_ior_done(packet_info *info, rdb_IOROPE *ior) +{ + if (!PACKET_NBODY(info)) { + return; + } + rdb_consumed(ior, PACKET_NBODY(info)); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/packetutils.h b/couchbase-sys/libcouchbase-2.7.0/src/packetutils.h new file mode 100644 index 00000000..d4c9f656 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/packetutils.h @@ -0,0 +1,296 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_PACKETUTILS_H +#define LCB_PACKETUTILS_H + +#include "config.h" + +#include +#include +#include "rdb/rope.h" + +#ifndef __cplusplus +typedef struct packet_info_st packet_info; +#else +namespace lcb { +class Server; + +/** + * Response packet informational structure. + * + * This contains information regarding the response packet which is used by + * the response processors. + */ +class MemcachedResponse { +public: + MemcachedResponse() : payload(NULL), bufh(NULL) { + // Bodyless. Members are initialized via load! + } + + MemcachedResponse(protocol_binary_command cmd, uint32_t opaque_, + protocol_binary_response_status code) { + std::memset(this, 0, sizeof *this); + res.response.opcode = cmd; + res.response.opaque = opaque_; + res.response.status = htons(code); + } + /** + * Read from an 'IOR' structure to parse the packet information. This will + * always load a full packet. + * + * @param ior the rope structure to read from + * @param[out] required how much total bytes must remain in the buffer for the + * parse to complete. + * + * @return false if more data is needed, true otherwise + */ + bool load(rdb_IOROPE *ior, unsigned *required) { + unsigned total = rdb_get_nused(ior); + unsigned wanted = sizeof(res.bytes); + + if (total < wanted) { + *required = wanted; + return false; + } + + rdb_copyread(ior, res.bytes, sizeof(res.bytes)); + if (!bodylen()) { + rdb_consumed(ior, sizeof(res.bytes)); + return true; + } + + wanted += bodylen(); + if (total < wanted) { + *required = wanted; + return false; + } + + rdb_consumed(ior, sizeof(res.bytes)); + payload = rdb_get_consolidated(ior, bodylen()); + return true; + } + + template + bool load(T ctx, unsigned *required) { + return load(&ctx->ior, required); + } + + void release(rdb_IOROPE *ior) { + if (!bodylen()) { + return; + } + rdb_consumed(ior, bodylen()); + } + + template + void release(T ctx) { + release(&ctx->ior); + } + + /** + * Gets the command for the packet + */ + uint8_t opcode() const { + return res.response.opcode; + } + + /** + * Gets the CAS for the packet + */ + uint64_t cas() const { + return lcb_ntohll(res.response.cas); + } + + /** + * Gets the 'datatype' field for the packet. + */ + uint8_t datatype() const { + return res.response.datatype; + } + + /** + * Gets a pointer starting at the packet's key field. Only use if NKEY is 0 + */ + const char *key() const { + return body() + extlen(); + } + + /** + * Gets a pointer starting at the packet's value field. Only use if NVALUE is 0 + */ + const char *value() const { + return body() + keylen() + extlen(); + } + + /** + * Gets the size of the packet value. The value is the part of the payload + * which is after the key (if applicable) and extras (if applicable). + */ + uint32_t vallen() const { + return bodylen() - (keylen() + extlen()); + } + + + /** + * Gets the status of the packet + */ + uint16_t status() const { + return ntohs(res.response.status); + } + + /** + * Gets the payload + */ + template + const T body() const { + return reinterpret_cast(payload); + } + + /** + * Map a command 'subclass' so that its body field starts at the payload. Note + * that the return value is actually an ephemeral pointer starting 24 bytes + * _before_ the actual memory block, so only use the non-header part. + */ + const char *ephemeral_start() const { + return body() - 24; + } + + /** + * Gets the size of the _total_ non-header part of the packet. This data is + * also featured inside the payload field itself. + */ + uint32_t bodylen() const { + return ntohl(res.response.bodylen); + } + + /** + * Gets the key size, if included in the packet. + */ + uint16_t keylen() const { + return ntohs(res.response.keylen); + } + + /** + * Gets the length of the 'extras' in the body + */ + uint8_t extlen() const { + return (res.response.extlen); + } + + /** + * Gets the raw unconverted 'opaque' 32 bit field + */ + uint32_t opaque() const { + return (res.response.opaque); + } + + size_t hdrsize() const { + return sizeof (res.bytes); + } + + uint8_t *hdrbytes() { + return res.bytes; + } + + void *bufseg() const { + return bufh; + } + +protected: + /** The response header */ + protocol_binary_response_header res; + /** The payload of the response. This should only be used if there is a body */ + void *payload; + /** Segment for payload */ + void *bufh; + + friend class lcb::Server; +}; + +#define PACKET_REQUEST(pkt) \ + ( (protocol_binary_request_header *) &(pkt)->res) + +#define PACKET_REQ_VBID(pkt) \ + (ntohs(PACKET_REQUEST(pkt)->request.vbucket)) + +class MemcachedRequest { +public: + /** + * Declare the extras, key, and value size for the packet + * @param extlen Length of extras + * @param keylen Length of key + * @param valuelen Length of value (i.e. minus extras and key) + */ + void sizes(uint8_t extlen, uint16_t keylen, uint32_t valuelen) { + hdr.request.bodylen = htonl(extlen + keylen + valuelen); + hdr.request.keylen = htons(keylen); + hdr.request.extlen = extlen; + } + + void vbucket(uint16_t vb) { + hdr.request.vbucket = htons(vb); + } + + void opaque(uint32_t opaque_) { + hdr.request.opaque = opaque_; + } + + uint32_t opaque() const { + return hdr.request.opaque; + } + + uint8_t opcode() const { + return hdr.request.opcode; + } + + MemcachedRequest(uint8_t opcode_) { + assign(opcode_); + } + + MemcachedRequest(uint8_t opcode_, uint32_t opaque_) { + assign(opcode_); + hdr.request.opaque = opaque_; + } + + MemcachedRequest(const void *buf) { + memcpy(hdr.bytes, buf, sizeof hdr.bytes); + } + + const void *data() const { return hdr.bytes; } + size_t size() const { return sizeof hdr.bytes; } + +private: + protocol_binary_request_header hdr; + + void assign(uint8_t opcode_) { + hdr.request.opcode = opcode_; + hdr.request.magic = PROTOCOL_BINARY_REQ; + hdr.request.datatype = PROTOCOL_BINARY_RAW_BYTES; + hdr.request.cas = 0; + hdr.request.vbucket = 0; + hdr.request.opaque = 0; + hdr.request.bodylen = 0; + hdr.request.extlen = 0; + hdr.request.keylen = 0; + hdr.request.opaque = 0; + } +}; +} +typedef lcb::MemcachedResponse packet_info; +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/probes.d b/couchbase-sys/libcouchbase-2.7.0/src/probes.d new file mode 100644 index 00000000..48fb78bc --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/probes.d @@ -0,0 +1,211 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +provider libcouchbase { + probe get_begin(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode (get, gat, getl) */ + const char*, /* key */ + size_t, /* nkey */ + uint32_t); /* expiration */ + probe get_end(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode (get, gat, getl) */ + uint16_t, /* return code (from libcouchbase) */ + const char*, /* key */ + size_t, /* nkey */ + const void*, /* bytes */ + size_t, /* nbytes */ + uint32_t, /* flags */ + uint64_t, /* cas */ + uint8_t); /* datatype */ + + probe unlock_begin(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + const char*, /* key */ + size_t); /* nkey */ + probe unlock_end(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + uint16_t, /* return code (from libcouchbase) */ + const char*, /* key */ + size_t); /* nkey */ + + probe store_begin(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode (set, add, replace, append, prepend) */ + const char*, /* key */ + size_t, /* nkey */ + const char*, /* bytes */ + size_t, /* nbytes */ + uint32_t, /* flags */ + uint64_t, /* cas */ + uint8_t, /* datatype */ + uint32_t); /* expiration */ + probe store_end(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode (set, add, replace, append, prepend) */ + uint16_t, /* return code (from libcouchbase) */ + const char*, /* key */ + size_t, /* nkey */ + uint64_t); /* cas */ + + probe arithmetic_begin(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode (increment, decrement) */ + const char*, /* key */ + size_t, /* nkey */ + uint64_t, /* delta */ + uint64_t, /* initial */ + uint32_t); /* expiration */ + probe arithmetic_end(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode (increment, decrement) */ + uint16_t, /* return code (from libcouchbase) */ + const char*, /* key */ + size_t, /* nkey */ + uint64_t, /* value */ + uint64_t); /* cas */ + + probe touch_begin(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + const char*, /* key */ + size_t, /* nkey */ + uint32_t); /* expiration */ + probe touch_end(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + uint16_t, /* return code (from libcouchbase) */ + const char*, /* key */ + size_t, /* nkey */ + uint64_t); /* cas */ + + probe remove_begin(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + const char*, /* key */ + size_t); /* nkey */ + probe remove_end(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + uint16_t, /* return code (from libcouchbase) */ + const char*, /* key */ + size_t, /* nkey */ + uint64_t); /* cas */ + + probe flush_begin(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + const char*); /* server_endpoint */ + probe flush_progress(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + uint16_t, /* return code (from libcouchbase) */ + const char*); /* server_endpoint */ + probe flush_end(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + uint16_t); /* return code (from libcouchbase) */ + + probe versions_begin(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + const char*); /* server_endpoint */ + probe versions_progress(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + uint16_t, /* return code (from libcouchbase) */ + const char*); /* server_endpoint */ + probe versions_end(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + uint16_t); /* return code (from libcouchbase) */ + + probe stats_begin(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + const char*, /* server_endpoint */ + const char*, /* arg */ + size_t); /* narg */ + probe stats_progress(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + uint16_t, /* return code (from libcouchbase) */ + const char*, /* server_endpoint */ + const char*, /* key */ + size_t, /* nkey */ + const char*, /* bytes */ + size_t); /* nbytes */ + probe stats_end(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + uint16_t); /* return code (from libcouchbase) */ + + probe verbosity_begin(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + const char*, /* server_endpoint */ + uint32_t); /* level */ + probe verbosity_end(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + uint16_t, /* return code (from libcouchbase) */ + const char*); /* server_endpoint */ + + /* + * OBSERVE_BEGIN probe intended to be parsed in the handler. + * the bytes argument is a blob with nbytes length: + * + * +---------+---------+------------+---- + * | 16 bits | 16 bits | nkey bytes | ... + * +---------+---------+------------+---- + * | vbucket | nkey | key | ... + * +---------+---------+------------+---- + */ + probe observe_begin(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + const char*, /* bytes */ + size_t); /* nbytes */ + probe observe_progress(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + uint16_t, /* return code (from libcouchbase) */ + const char*, /* key */ + size_t, /* nkey */ + uint64_t, /* cas */ + uint8_t, /* observe status: FOUND = 0x00, PERSISTED = 0x01, NOT_FOUND = 0x80 */ + uint8_t, /* master (zero if from replica) */ + uint32_t, /* ttp, time to persist */ + uint32_t); /* ttr, time to replicate */ + probe observe_end(uint32_t, /* opaque */ + uint16_t, /* vbucket */ + uint8_t, /* opcode */ + uint16_t); /* return code (from libcouchbase) */ + + probe http_begin(const char*, /* url */ + size_t, /* nurl */ + uint8_t); /* method: GET = 0, POST = 1, PUT = 2, DELETE = 3 */ + probe http_end(const char*, /* url */ + size_t, /* nurl */ + uint8_t, /* method: GET = 0, POST = 1, PUT = 2, DELETE = 3 */ + uint16_t, /* return code (from libcouchbase) */ + uint16_t); /* HTTP status code or zero */ + +}; diff --git a/couchbase-sys/libcouchbase-2.7.0/src/rdb/bigalloc.c b/couchbase-sys/libcouchbase-2.7.0/src/rdb/bigalloc.c new file mode 100644 index 00000000..3bf48a06 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/rdb/bigalloc.c @@ -0,0 +1,225 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "rope.h" +#include "bigalloc.h" + +#define MAXIMUM(a, b) (a) > (b) ? a : b + +static void +alloc_decref(rdb_ALLOCATOR *abase) +{ + lcb_list_t *llcur, *llnext; + rdb_BIGALLOC *alloc = (rdb_BIGALLOC *)abase; + if (--alloc->refcount) { + return; + } + + LCB_LIST_SAFE_FOR(llcur, llnext, (lcb_list_t *)&alloc->bufs) { + rdb_ROPESEG *seg = LCB_LIST_ITEM(llcur, rdb_ROPESEG, llnode); + lcb_clist_delete(&alloc->bufs, &seg->llnode); + free(seg->root); + free(seg); + } + free(alloc); +} + +static void +recheck_thresholds(rdb_BIGALLOC *alloc) +{ + if (++alloc->n_requests % RDB_BIGALLOC_RECHECK_RATE) { + return; + } + + alloc->total_requests += alloc->n_requests; + alloc->total_toobig += alloc->n_toobig; + alloc->total_toosmall += alloc->n_toosmall; + + if (alloc->n_toobig == alloc->n_toosmall) { + /* all is ok */ + + } else if (alloc->n_toobig > alloc->n_toosmall) { + /* seems we need to allocate bigger chunks? */ + if (alloc->n_toobig*2 > alloc->n_toosmall) { + alloc->min_blk_alloc *= 2; + alloc->max_blk_alloc *= 2; + } + } else if (alloc->n_toosmall > alloc->n_toobig) { + if (alloc->n_toosmall*2 > alloc->n_toobig) { + alloc->min_blk_alloc /= 2; + alloc->max_blk_alloc /= 2; + } + } + + alloc->n_requests = 0; + alloc->n_toobig = 0; + alloc->n_toosmall = 0; +} + +static rdb_ROPESEG * +seg_alloc(rdb_ALLOCATOR *abase, unsigned size) +{ + lcb_list_t *llcur; + rdb_ROPESEG *newseg = NULL; + rdb_BIGALLOC *alloc = (rdb_BIGALLOC *)abase; + + recheck_thresholds(alloc); + /** + * If the allocation reaches a certain threshold (for example, for a really + * huge packet), then don't bother caching it. + */ + if (size > alloc->max_blk_alloc) { + alloc->n_toobig++; + alloc->total_malloc++; + newseg = calloc(1, sizeof(*newseg)); + newseg->root = malloc(size); + newseg->nalloc = size; + goto GT_RETNEW; + } else if (size < alloc->min_blk_alloc) { + alloc->n_toosmall++; + } + + LCB_LIST_FOR(llcur, (lcb_list_t *)&alloc->bufs) { + rdb_ROPESEG *cur = LCB_LIST_ITEM(llcur, rdb_ROPESEG, llnode); + if (cur->nalloc < size) { + continue; + } + + newseg = cur; + lcb_clist_delete(&alloc->bufs, llcur); + break; + } + + if (!newseg) { + unsigned newsize = alloc->min_blk_alloc; + if (LCB_CLIST_SIZE(&alloc->bufs) >= alloc->max_blk_count) { + lcb_list_t *llold = lcb_clist_pop(&alloc->bufs); + newseg = LCB_LIST_ITEM(llold, rdb_ROPESEG, llnode); + free(newseg->root); + } else { + newseg = calloc(1, sizeof(*newseg)); + alloc->total_malloc++; + } + + while (newsize < size) { + newsize = (unsigned) ((double)newsize * 1.5); + } + + newseg->root = malloc(newsize); + newseg->nalloc = newsize; + } + + GT_RETNEW: + newseg->shflags = RDB_ROPESEG_F_LIB; + newseg->allocator = abase; + newseg->allocid = RDB_ALLOCATOR_CHUNKED; + newseg->start = 0; + newseg->nused = 0; + alloc->refcount++; + return newseg; +} + +static void +buf_reserve(rdb_pALLOCATOR abase, rdb_ROPEBUF *buf, unsigned size) +{ + rdb_BIGALLOC *alloc = (rdb_BIGALLOC *)abase; + rdb_ROPESEG *newseg, *lastseg; + + lastseg = RDB_SEG_LAST(buf); + if (lastseg && RDB_SEG_SPACE(lastseg) + buf->nused >= size) { + return; + } + + newseg = seg_alloc(&alloc->base, size); + lcb_list_append(&buf->segments, &newseg->llnode); +} + +static rdb_ROPESEG * +seg_realloc(rdb_ALLOCATOR *abase, rdb_ROPESEG *seg, unsigned size) +{ + rdb_BIGALLOC *alloc = (rdb_BIGALLOC *)abase; + + if (size < alloc->n_toosmall) { + alloc->n_toosmall++; + } else if (size > alloc->n_toobig) { + alloc->n_toobig++; + } + + seg->root = realloc(seg->root, size); + seg->nalloc = size; + alloc->total_malloc++; + recheck_thresholds((rdb_BIGALLOC *)abase); + return seg; +} + +static void +seg_release(rdb_ALLOCATOR *abase, rdb_ROPESEG *seg) +{ + rdb_BIGALLOC *alloc = (rdb_BIGALLOC *)abase; + if (LCB_CLIST_SIZE(&alloc->bufs) >= alloc->max_blk_count || + seg->nalloc > alloc->max_blk_alloc || + seg->nalloc < alloc->min_blk_alloc) { + free(seg->root); + free(seg); + } else { + lcb_clist_prepend(&alloc->bufs, &seg->llnode); + } + alloc_decref(abase); +} + +static void +dump_wrap(rdb_pALLOCATOR alloc, FILE *fp) { rdb_bigalloc_dump((rdb_BIGALLOC*)alloc, fp); } + +rdb_ALLOCATOR * +rdb_bigalloc_new(void) +{ + rdb_ALLOCATOR *abase; + rdb_BIGALLOC *alloc = calloc(1, sizeof(*alloc)); + lcb_clist_init(&alloc->bufs); + alloc->max_blk_alloc = RDB_BIGALLOC_ALLOCSZ_MAX; + alloc->min_blk_alloc = RDB_BIGALLOC_ALLOCSZ_MIN; + alloc->max_blk_count = RDB_BIGALLOC_BLKCNT_MAX; + alloc->refcount = 1; + + abase = &alloc->base; + abase->r_reserve = buf_reserve; + abase->s_release = seg_release; + abase->s_alloc = seg_alloc; + abase->s_realloc = seg_realloc; + abase->a_release = alloc_decref; + abase->dump = dump_wrap; + return &alloc->base; +} + +void +rdb_bigalloc_dump(rdb_BIGALLOC *alloc, FILE *fp) +{ + static const char *indent = " "; + fprintf(fp, "BIGALLOC @%p\n", (void *)alloc); + fprintf(fp, "%sPooled Blocks: %lu\n", indent, LCB_CLIST_SIZE(&alloc->bufs)); + fprintf(fp, "%sMinAlloc: %u\n", indent, alloc->min_blk_alloc); + fprintf(fp, "%sMaxAlloc: %u\n", indent, alloc->max_blk_alloc); + fprintf(fp, "%sMaxBlocks: %u\n", indent, alloc->max_blk_count); + + fprintf(fp, "%sTotalMalloc: %u\n", indent, alloc->total_malloc); + fprintf(fp, "%sTotalRequests: %u\n", indent, alloc->total_requests); + fprintf(fp, "%sTotalToobig: %u\n", indent, alloc->total_toobig); + fprintf(fp, "%sTotalToosmall: %u\n", indent, alloc->total_toosmall); + +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/rdb/bigalloc.h b/couchbase-sys/libcouchbase-2.7.0/src/rdb/bigalloc.h new file mode 100644 index 00000000..f279a379 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/rdb/bigalloc.h @@ -0,0 +1,73 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RDB_BIGALLOC +#define RDB_BIGALLOC +#include "list.h" +#include +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Big block allocator. This allocator will allocate large chunks of memory with + * the assumption that will typically not be wasted too quickly. It also keeps + * track of an allocation history, so that it can adjust to the current + * "climate". + * + * This header file exists for internal use. To create an allocator instance, + * refer to rdb_bigalloc_new() in rope.h + */ + +typedef struct { + rdb_ALLOCATOR base; + lcb_clist_t bufs; /* list of pooled segments */ + unsigned refcount; + unsigned min_blk_alloc; /* minimum alloc size */ + unsigned max_blk_alloc; /* maximum alloc size (bigger than this is not pooled) */ + unsigned max_blk_count; /* maximum number of blocks to pool */ + unsigned n_requests; /* number of requests. Reset every RECHECK_RATE */ + unsigned n_toobig; /* number of requests > max_blk_alloc */ + unsigned n_toosmall; /* number of requests < min_blk_alloc */ + + /** counters updated at the end only */ + unsigned total_malloc; + unsigned total_requests; + unsigned total_toobig; + unsigned total_toosmall; +} rdb_BIGALLOC; + +#define RDB_BIGALLOC_ALLOCSZ_MAX 65536 +#define RDB_BIGALLOC_ALLOCSZ_MIN 256 +#define RDB_BIGALLOC_BLKCNT_MAX 8 + +/** Readjust thresholds every requests. is defined here */ +#define RDB_BIGALLOC_RECHECK_RATE 15 + +/** + * Dumps a textual representation of the specified allocator to a FILE + * @param alloc + * @param fp + */ +void +rdb_bigalloc_dump(rdb_BIGALLOC *alloc, FILE *fp); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/rdb/chunkalloc.c b/couchbase-sys/libcouchbase-2.7.0/src/rdb/chunkalloc.c new file mode 100644 index 00000000..d5544d8f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/rdb/chunkalloc.c @@ -0,0 +1,174 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include "rope.h" + +/** + * This is a fixed size chunk allocator. It tries to allocate chunks of a + * certain size. For constraints needing "Special" block sizes, it simply + * defers to malloc/free (in truth, it could utilize the 'bigalloc' + * allocator for malloc/frees, but that might not be necessary). + */ + +typedef struct { + rdb_ALLOCATOR base; + lcb_clist_t chunks; + unsigned refcount; + unsigned chunksize; + unsigned max_chunks; +} my_CHUNKALLOC; + +static void +alloc_decref(rdb_ALLOCATOR *abase) +{ + lcb_list_t *llcur, *llnext; + my_CHUNKALLOC *alloc = (my_CHUNKALLOC *)abase; + if (--alloc->refcount) { + return; + } + + LCB_LIST_SAFE_FOR(llcur, llnext, (lcb_list_t *)&alloc->chunks) { + rdb_ROPESEG *seg = LCB_LIST_ITEM(llcur, rdb_ROPESEG, llnode); + free(seg->root); + free(seg); + } + free(alloc); +} + +static void +release_chunk(rdb_ALLOCATOR *abase, rdb_ROPESEG *seg) +{ + my_CHUNKALLOC *alloc = (my_CHUNKALLOC *)abase; + if (seg->nalloc != alloc->chunksize || + LCB_CLIST_SIZE(&alloc->chunks) > alloc->max_chunks) { + free(seg->root); + free(seg); + } else { + lcb_clist_prepend(&alloc->chunks, &seg->llnode); + } +} + +static rdb_ROPESEG * +standalone_alloc(rdb_ALLOCATOR *abase, unsigned size) +{ + my_CHUNKALLOC *alloc = (my_CHUNKALLOC *)abase; + rdb_ROPESEG *seg; + + seg = calloc(1, sizeof(*seg)); + seg->root = malloc(size); + seg->nalloc = size; + seg->shflags = RDB_ROPESEG_F_LIB; + seg->allocid = RDB_ALLOCATOR_CHUNKED; + seg->allocator = abase; + alloc->refcount++; + return seg; +} + +static rdb_ROPESEG * +chunked_alloc(my_CHUNKALLOC *alloc) +{ + rdb_ROPESEG *chunk = NULL; + lcb_list_t *llcur, *llnext; + + LCB_LIST_SAFE_FOR(llcur, llnext, (lcb_list_t *)&alloc->chunks) { + rdb_ROPESEG *cur = LCB_LIST_ITEM(llcur, rdb_ROPESEG, llnode); + + lcb_list_delete(&cur->llnode); + if (cur->nalloc != alloc->chunksize) { + release_chunk(&alloc->base, cur); + } else { + chunk = cur; + break; + } + } + + if (chunk) { + alloc->refcount++; + } else { + chunk = standalone_alloc(&alloc->base, alloc->chunksize); + } + + chunk->start = 0; + chunk->nused = 0; + chunk->shflags = RDB_ROPESEG_F_LIB; + return chunk; +} + +static void +buf_reserve(rdb_ALLOCATOR *abase, rdb_ROPEBUF *buf, unsigned n) +{ + my_CHUNKALLOC *alloc = (my_CHUNKALLOC *)abase; + rdb_ROPESEG *lastseg = RDB_SEG_LAST(buf); + unsigned allocated = 0; + + if (lastseg) { + if (buf->nused + RDB_SEG_SPACE(lastseg) >= n) { + return; + } + + n -= (buf->nused + RDB_SEG_SPACE(lastseg)); + } + + while (allocated < n) { + rdb_ROPESEG *seg = chunked_alloc(alloc); + lcb_list_append(&buf->segments, &seg->llnode); + allocated += seg->nalloc; + } +} + +static rdb_ROPESEG * +seg_realloc(rdb_ALLOCATOR *abase, rdb_ROPESEG *seg, unsigned n) +{ + seg->nalloc = n; + seg->root = realloc(seg->root, n); + + (void)abase; + return seg; +} + +static void +seg_release(rdb_ALLOCATOR *abase, rdb_ROPESEG *seg) +{ + release_chunk(abase, seg); + alloc_decref(abase); +} + +LCB_INTERNAL_API +rdb_ALLOCATOR * +rdb_chunkalloc_new(unsigned chunksize) +{ + rdb_ALLOCATOR *ret; + my_CHUNKALLOC *alloc = calloc(1, sizeof(*alloc)); + alloc->refcount = 1; + alloc->chunksize = chunksize; + alloc->max_chunks = 512; + + lcb_clist_init(&alloc->chunks); + ret = &alloc->base; + + ret->a_release = alloc_decref; + ret->r_reserve = buf_reserve; + ret->s_alloc = standalone_alloc; + ret->s_realloc = seg_realloc; + ret->s_release = seg_release; + return ret; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/rdb/libcalloc.c b/couchbase-sys/libcouchbase-2.7.0/src/rdb/libcalloc.c new file mode 100644 index 00000000..b366e690 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/rdb/libcalloc.c @@ -0,0 +1,94 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This is the default allocator which uses chunks via malloc/free + */ + +#include +#include +#include +#include "rope.h" +#include "list.h" + +static rdb_ROPESEG * +seg_alloc(rdb_pALLOCATOR alloc, unsigned size) +{ + unsigned newsize; + rdb_ROPESEG *ret; + newsize = size + sizeof(*ret); + ret = malloc(newsize); + memset(ret, 0, sizeof(*ret)); + ret->nalloc = size; + ret->root = ((char *)ret) + sizeof(*ret); + ret->allocator = alloc; + ret->allocid = RDB_ALLOCATOR_LIBCALLOC; + + return ret; +} + +static rdb_ROPESEG * +seg_realloc(rdb_pALLOCATOR alloc, rdb_ROPESEG *seg, unsigned size) +{ + unsigned newsize = size + sizeof(*seg); + seg = realloc(seg, newsize); + seg->root = ((char *)seg) + sizeof(*seg); + + (void)alloc; + return seg; +} + +static void +seg_free(rdb_pALLOCATOR alloc, rdb_ROPESEG *seg) +{ + (void)alloc; + free(seg); +} + +static void +buf_reserve(rdb_pALLOCATOR alloc, rdb_ROPEBUF *buf, unsigned cap) +{ + rdb_ROPESEG *newseg, *lastseg; + unsigned to_alloc; + lastseg = RDB_SEG_LAST(buf); + if (lastseg && RDB_SEG_SPACE(lastseg) + buf->nused >= cap) { + return; + } + + to_alloc = cap; + if (lastseg) { + to_alloc -= lastseg->nalloc - lastseg->start; + } + newseg = alloc->s_alloc(alloc, to_alloc); + lcb_list_append(&buf->segments, &newseg->llnode); +} + +static void release_noop(rdb_pALLOCATOR alloc) { (void)alloc; } + +static rdb_ALLOCATOR lcalloc = { + buf_reserve, + seg_alloc, + seg_realloc, + seg_free, + release_noop +}; + +rdb_ALLOCATOR * +rdb_libcalloc_new(void) +{ + return &lcalloc; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/rdb/rope.c b/couchbase-sys/libcouchbase-2.7.0/src/rdb/rope.c new file mode 100644 index 00000000..919971aa --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/rdb/rope.c @@ -0,0 +1,419 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include "rope.h" + +#define MINIMUM(a, b) (a) < (b) ? a : b + +#define SEG_RELEASE(seg) (seg)->allocator->s_release((seg)->allocator, seg) +#define SEG_REALLOC(seg, n) (seg)->allocator->s_realloc((seg)->allocator, seg, n) +#define ROPE_SALLOC(rope, n) (rope)->allocator->s_alloc((rope)->allocator, n) +static void wipe_rope(rdb_ROPEBUF *rope); + +unsigned +rdb_rdstart(rdb_IOROPE *ior, nb_IOV *iov, unsigned niov) +{ + unsigned orig_niov = niov; + unsigned cur_rdsize = 0; + + lcb_list_t *ll; + rdb_ROPESEG *seg = RDB_SEG_LAST(&ior->recvd); + + if (seg && RDB_SEG_SPACE(seg)) { + iov->iov_base = RDB_SEG_WBUF(seg); + iov->iov_len = RDB_SEG_SPACE(seg); + cur_rdsize += iov->iov_len; + ++iov; + --niov; + if (cur_rdsize >= ior->rdsize) { + return 1; + } + } + + if (!niov) { + return orig_niov - niov; + } + + ior->avail.allocator->r_reserve( + ior->avail.allocator, &ior->avail, ior->rdsize - cur_rdsize); + + assert(!LCB_LIST_IS_EMPTY(&ior->avail.segments)); + + LCB_LIST_FOR(ll, &ior->avail.segments) { + rdb_ROPESEG *cur = LCB_LIST_ITEM(ll, rdb_ROPESEG, llnode); + iov->iov_base = RDB_SEG_WBUF(cur); + iov->iov_len = RDB_SEG_SPACE(cur); + ++iov; + + if (!--niov) { + break; + } + } + return orig_niov - niov; +} + + +void +rdb_rdend(rdb_IOROPE *ior, unsigned nr) +{ + unsigned to_chop; + lcb_list_t *llcur, *llnext; + + /** Chop the first segment at the end, if there's space */ + rdb_ROPESEG *seg = RDB_SEG_LAST(&ior->recvd); + if (seg && RDB_SEG_SPACE(seg)) { + to_chop = MINIMUM(nr, RDB_SEG_SPACE(seg)); + seg->nused += to_chop; + ior->recvd.nused += to_chop; + + if (! (nr -= to_chop)) { + wipe_rope(&ior->avail); + return; + } + } + + LCB_LIST_SAFE_FOR(llcur, llnext, &ior->avail.segments) { + seg = LCB_LIST_ITEM(llcur, rdb_ROPESEG, llnode); + to_chop = MINIMUM(nr, RDB_SEG_SPACE(seg)); + + seg->nused += to_chop; + ior->recvd.nused += seg->nused; + + lcb_list_delete(&seg->llnode); + lcb_list_append(&ior->recvd.segments, &seg->llnode); + if (! (nr -= to_chop)) { + wipe_rope(&ior->avail); + return; + } + } + + /** Reads didn't fit into any segment */ + fprintf(stderr, "RDB: Tried to consume more than available in the buffer (n=%u)\n", nr); + assert(0); +} + +static void +seg_consumed(rdb_ROPEBUF *rope, rdb_ROPESEG *seg, unsigned nr) +{ + assert(nr <= seg->nused); + seg->nused -= nr; + seg->start += nr; + rope->nused -= nr; + + if (!seg->nused) { + lcb_list_delete(&seg->llnode); + seg->shflags &= ~RDB_ROPESEG_F_LIB; + if (rdb_seg_recyclable(seg)) { + SEG_RELEASE(seg); + } + } +} + +static void +rope_consumed(rdb_ROPEBUF *rope, unsigned nr) +{ + lcb_list_t *llcur, *llnext; + assert(nr <= rope->nused); + + LCB_LIST_SAFE_FOR(llcur, llnext, &rope->segments) { + unsigned to_chop; + rdb_ROPESEG *seg = LCB_LIST_ITEM(llcur, rdb_ROPESEG, llnode); + to_chop = MINIMUM(nr, seg->nused); + seg_consumed(rope, seg, to_chop); + + if (! (nr -= to_chop)) { + break; + } + } +} + +void +rdb_consumed(rdb_IOROPE *ior, unsigned nr) +{ + rope_consumed(&ior->recvd, nr); +} + +static void +try_compact(rdb_ROPESEG *seg) +{ + /** Can't move stuff around.. */ + char *cp_end; + if (!rdb_seg_recyclable(seg)) { + return; + } + + /** + * Copy only if: + * (1) Waste in the beginning is >= nalloc/2 + * (3) There is no overlap between the two (i.e. memcpy) + */ + if (seg->start < seg->nalloc / 2) { + return; + } + + cp_end = seg->root + seg->nused; + if (seg->root + seg->start < cp_end) { + /** Overlap */ + return; + } + + memcpy(seg->root, seg->root + seg->start, seg->nused); + seg->start = 0; +} + +static void +rope_consolidate(rdb_ROPEBUF *rope, unsigned nr) +{ + rdb_ROPESEG *seg, *newseg; + lcb_list_t *llcur, *llnext; + + seg = RDB_SEG_FIRST(rope); + if (seg->nused + RDB_SEG_SPACE(seg) >= nr || nr < 2) { + return; + } + + try_compact(seg); + lcb_list_delete(&seg->llnode); + + if (rdb_seg_recyclable(seg)) { + unsigned to_alloc = nr + seg->start; + newseg = SEG_REALLOC(seg, to_alloc); + /* We re-add it back after traversal */ + } else { + newseg = ROPE_SALLOC(rope, nr); + memcpy(RDB_SEG_WBUF(newseg), RDB_SEG_RBUF(seg), seg->nused); + newseg->nused = seg->nused; + /* "Free" it. Since this buffer is in use, we just unset our own flag */ + seg->shflags &= ~RDB_ROPESEG_F_LIB; + } + + rope->nused -= newseg->nused; + nr -= newseg->nused; + + LCB_LIST_SAFE_FOR(llcur, llnext, &rope->segments) { + unsigned to_copy; + seg = LCB_LIST_ITEM(llcur, rdb_ROPESEG, llnode); + to_copy = MINIMUM(nr, seg->nused); + + memcpy(RDB_SEG_WBUF(newseg), RDB_SEG_RBUF(seg), to_copy); + newseg->nused += to_copy; + + seg_consumed(rope, seg, to_copy); + if (! (nr -= to_copy)) { + break; + } + } + + lcb_list_prepend(&rope->segments, &newseg->llnode); + rope->nused += newseg->nused; + assert(rope->nused >= nr); +} + +void +rdb_consolidate(rdb_IOROPE *ior, unsigned nr) +{ + rope_consolidate(&ior->recvd, nr); +} + +void +rdb_copyread(rdb_IOROPE *ior, void *tgt, unsigned n) +{ + lcb_list_t *ll; + char *p = tgt; + + LCB_LIST_FOR(ll, &ior->recvd.segments) { + rdb_ROPESEG *seg = LCB_LIST_ITEM(ll, rdb_ROPESEG, llnode); + unsigned to_copy = MINIMUM(seg->nused, n); + memcpy(p, RDB_SEG_RBUF(seg), to_copy); + p += to_copy; + n -= to_copy; + if (!n) { + break; + } + } +} + +int +rdb_refread_ex(rdb_IOROPE *ior, nb_IOV *iov, rdb_ROPESEG **segs, + unsigned nelem, unsigned ndata) +{ + unsigned orig_nelem = nelem; + lcb_list_t *ll; + LCB_LIST_FOR(ll, &ior->recvd.segments) { + rdb_ROPESEG *seg = LCB_LIST_ITEM(ll, rdb_ROPESEG, llnode); + unsigned cur_len = MINIMUM(ndata, seg->nused); + iov->iov_len = cur_len; + iov->iov_base = RDB_SEG_RBUF(seg); + *segs = seg; + + ++iov; + ++segs; + --nelem; + + ndata -= cur_len; + + if (!ndata) { + return orig_nelem - nelem; + } + + if (!nelem) { + return -1; + } + } + + /** Requested more data than we have */ + fprintf(stderr, "RDB: refread_ex was passed a size greater than our buffer (n=%u)\n", ndata); + return -1; +} + +unsigned +rdb_get_contigsize(rdb_IOROPE *ior) +{ + rdb_ROPESEG *seg = RDB_SEG_FIRST(&ior->recvd); + if (!seg) { + return 0; + } + return seg->nused; +} + +char * +rdb_get_consolidated(rdb_IOROPE *ior, unsigned n) +{ + assert(ior->recvd.nused >= n); + rdb_consolidate(ior, n); + return RDB_SEG_RBUF(RDB_SEG_FIRST(&ior->recvd)); +} + +void +rdb_seg_ref(rdb_ROPESEG *seg) +{ + seg->refcnt++; + seg->shflags |= RDB_ROPESEG_F_USER; +} + +void +rdb_seg_unref(rdb_ROPESEG *seg) +{ + if (--seg->refcnt) { + return; + } + seg->shflags &= ~RDB_ROPESEG_F_USER; + if (seg->shflags & RDB_ROPESEG_F_LIB) { + return; + } + SEG_RELEASE(seg); +} + +void +rdb_init(rdb_IOROPE *ior, rdb_ALLOCATOR *alloc) +{ + memset(ior, 0, sizeof(*ior)); + lcb_list_init(&ior->recvd.segments); + lcb_list_init(&ior->avail.segments); + rdb_challoc(ior, alloc); + ior->rdsize = 32768; +} + +static void +wipe_rope(rdb_ROPEBUF *rope) +{ + lcb_list_t *llcur, *llnext; + LCB_LIST_SAFE_FOR(llcur, llnext, &rope->segments) { + rdb_ROPESEG *seg = LCB_LIST_ITEM(llcur, rdb_ROPESEG, llnode); + seg_consumed(rope, seg, seg->nused); + } +} + +void +rdb_cleanup(rdb_IOROPE *ior) +{ + wipe_rope(&ior->recvd); + wipe_rope(&ior->avail); + ior->recvd.allocator->a_release(ior->recvd.allocator); +} + +void +rdb_challoc(rdb_IOROPE *ior, rdb_ALLOCATOR *alloc) +{ + if (ior->recvd.allocator) { + ior->recvd.allocator->a_release(ior->recvd.allocator); + } + + ior->recvd.allocator = alloc; + ior->avail.allocator = alloc; +} + +void +rdb_copywrite(rdb_IOROPE *ior, void *buf, unsigned nbuf) +{ + char *cur = buf; + + while (nbuf) { + unsigned ii; + unsigned orig_nbuf = nbuf; + nb_IOV iov[32]; + unsigned niov; + + niov = rdb_rdstart(ior, iov, 32); + for (ii = 0; ii < niov && nbuf; ii++) { + unsigned to_copy = MINIMUM(nbuf, iov[ii].iov_len); + memcpy(iov[ii].iov_base, cur, to_copy); + cur += to_copy; + nbuf -= to_copy; + } + rdb_rdend(ior, orig_nbuf - nbuf); + } +} + +static void +dump_ropebuf(const rdb_ROPEBUF *buf, FILE *fp) +{ + lcb_list_t *llcur; + fprintf(fp, "TOTAL LENGTH: %u\n", buf->nused); + fprintf(fp, "WILL DUMP SEGMENTS..\n"); + LCB_LIST_FOR(llcur, &buf->segments) { + const char *indent = " "; + rdb_ROPESEG *seg = LCB_LIST_ITEM(llcur, rdb_ROPESEG, llnode); + fprintf(fp, "%sSEG=%p\n", indent, (void*)seg); + fprintf(fp, "%sALLOCATOR=%p [%u]\n", indent, (void*)seg->allocator, seg->allocid); + fprintf(fp, "%sBUFROOT=%p\n", indent, seg->root); + fprintf(fp, "%sALLOC SIZE: %u\n", indent, seg->nalloc); + fprintf(fp, "%sDATA SIZE: %u\n", indent, seg->nused); + fprintf(fp, "%sDATS OFFSET: %u\n", indent, seg->start); + fprintf(fp, "%sSEG FLAGS: 0x%x\n", indent, seg->shflags); + fprintf(fp, "%sSEG REFCNT: %u\n", indent, seg->refcnt); + fprintf(fp, "\n"); + } +} + +void +rdb_dump(const rdb_IOROPE *ior, FILE *fp) +{ + fprintf(fp, "@@ DUMP IOROPE=%p\n", (void*)ior); + fprintf(fp, "@@ ROPEBUF[AVAIL]=%p\n", (void*)&ior->avail); + dump_ropebuf(&ior->avail, fp); + fprintf(fp, "@@ ROPEBUF[ACTIVE]=%p\n", (void*)&ior->recvd); + dump_ropebuf(&ior->recvd, fp); + if (ior->avail.allocator && ior->avail.allocator->dump) { + ior->avail.allocator->dump(ior->avail.allocator, fp); + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/rdb/rope.h b/couchbase-sys/libcouchbase-2.7.0/src/rdb/rope.h new file mode 100644 index 00000000..01d3657d --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/rdb/rope.h @@ -0,0 +1,488 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_ROPE_H +#define LCB_ROPE_H +#include +#include +#include +#include +#include "list.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup rdb Network Read Buffers + * @brief Read buffer system. + * + * # Overview + * + * This subsystem provides an extensible means by which to deal with input + * network buffers. + * + * Sequential network data is represented in something called an `rdb_ROPEBUF` + * structure. The rope structure itself consists of one or more + * `rdb_ROPESEG` structures; where a segment represents a contiguous block of + * memory. + * + * Segments are thus the base allocation unit. They are chained together in + * a ROPE structure to form a sequence of data, where the first segment contains + * previous data and the last segment contains the newest data (and potentially + * free space for additional reads). + * + * The size of each segment is determined by either the allocator or the + * library. The allocator determines the size for "Estimated" read-aheads, where it + * decides on the best form of fragmentation (if any) for the TCP buffers. + * The library determines the size of a segment if a specific sequence of data + * must be represented as contiguous bytes in memory. + * + * The differentiation between these two modes is expressed in the + * r_reserve() function (where the allocator determines the best mode of + * fragmentation) and the s_alloc/s_realloc functions where the library demands + * a specific form of continuity. + * + * The API here is divided into two sections. One API deals with allocating + * the relevant buffers (allocation API) while the other API deals with + * extracting data from said buffers. + * + * # Extraction API + * + * The extraction API provides means for receiving buffers for network reads + * and for extracting data once these reads have been completed. + * + * The read operation begins by the user providing an IOV array and passing it + * to the rdb_rdstart() function. This function will populate the fields in + * each IOV structure corresponding to the segments of the relevant IOVs. + * + * You may then forward these IOV buffers to the system-level read functions + * (e.g. recvmsg). + * + * After data has been read into the buffers, call rdb_rdend() with the number + * of bytes the last read operation received. + * + * Internally the rope structure contains a position at which data begins. + * All functions which extract data begin from this offset. + * + * To extract the data you may call rdb_get_consolidate which will return + * a pointer to a contiguous region of memory of the specified number of bytes. + * + * Finally there is rdb_copyread() which will _copy_ the contents of the read + * buffer over to a user allocated buffer. This does _not_ consolidate the + * buffers internally. + * + * Once you are done processing the data, you can call rdb_consumed() which + * will advance the start position by the specified number of bytes, releasing + * any buffers which do not contain data back to the allocator. + * + * ## Extended Extraction API + * + * The more advanced rdb_refread_ex() will populate an array of IOV and + * rdb_SEGMENT pointers with the first such element containing the offsets of + * the first data byte, and the last of these elements containing the end + * of the specified length of data. This function is useful if the calling code + * does not require the data to be contiguous. The two arrays are provided + * so that the IOV array can easily be passed to a socket function (rather + * than merely reconstructing them from the segments array). + * + * Using rdb_refread_ex() you may also use the rdb_seg_ref() function to "pin" + * the the segments contained in the output array. When the segments are pinned + * they are guaranteed not to be overwritten or released from memory until + * the last call to rdb_seg_unref() is invoked. + * + * You may call rdb_consolidate() to ensure that a certain chunk of data + * will be continuous when it is available to be read. This is useful in a + * partial read where remaining data has not been received yet, but you know + * that you will need the current segment as well as a specified length + * of incoming data to be contiguous. + * + * # Allocator API + * + * The allocator APIs determine the granularity and fragmentation of the + * read buffers. They can also perform pooling and other sorts of optimizations + * that would depend on the application-specific use case and not determinable + * by the library. + * + * + * Buffers are initially allocated by RDB using the rdb_buf_reserve_fn callback. + * This callback is typically invoked from an rdb_rdstart() function to + * provide the underlying storage for buffers to be read into. It is here + * that the allocator may choose to extend the buffer beyond the requested size + * or provide fragmented segments instead of a large contiguous segment. + * + * If a specific length of contiguous data is required, the rdb_seg_alloc_fn + * callback will be invoked (typically from one of the consolidation functions) + * and here the allocator must return a segment of at least the specified length. + * If an existing segment (which contains data) must be extended, the + * rdb_seg_realloc_fn is called. + * + * When the segment is no longer needed (i.e. its refcount is 0 and is no + * longer needed by the library) the rdb_seg_free_fn is called to release its + * memory. The allocator may place the segment into a pool or release it through + * other means. + * + * Finally, there is the a_release() field which acts as a destructor for the + * allocator. It signals to the allocator that no _new_ data will be allocated + * from it. + */ + +/** + * @addtogroup rdb + * @{ + */ + + +typedef struct rdb_ALLOCATOR *rdb_pALLOCATOR; +typedef struct rdb_ROPESEG *rdb_pROPESEG; +typedef struct rdb_ROPEBUF *rdb_pROPEBUF; + +typedef struct rdb_ROPEBUF { + lcb_list_t segments; /* linked list of buffer segments */ + unsigned nused; /* bytes of data in use */ + rdb_pALLOCATOR allocator; /* pointer to allocator structure */ +} rdb_ROPEBUF; + +struct rdb_ROPESEG; + +enum rdb_SEGFLAGS { + RDB_ROPESEG_F_USER = 0x01, /* segment has pinned data */ + RDB_ROPESEG_F_LIB = 0x02 /* segment is in use by the library */ +}; + +enum rdb_ALLOCID { + RDB_ALLOCATOR_BIGALLOC = 1, + RDB_ALLOCATOR_CHUNKED, + RDB_ALLOCATOR_LIBCALLOC, + + /** use constants higher than this for your own allocator(s) */ + RDB_ALLOCATOR_MAX +}; + +/** Segment within a rope buffer */ +typedef struct rdb_ROPESEG { + lcb_list_t llnode; /** linked list node */ + char *root; /** Allocated buffer */ + unsigned char shflags; /** rdb_SEGFLAGS */ + unsigned char allocid; /** rdb_ALLOCID */ + unsigned nalloc; /** total allocated length */ + unsigned nused; /** number of bytes containing data */ + unsigned start; /** offset where data begins */ + unsigned refcnt; /** see ref/unref */ + rdb_pALLOCATOR allocator; +} rdb_ROPESEG; + +typedef struct { + rdb_ROPEBUF recvd; /** rope containing read data */ + rdb_ROPEBUF avail; /** rope used for subsequent network reads */ + unsigned rdsize; /** preferred read size */ +} rdb_IOROPE; + + +/** + * @name Allocator API + * @{ + */ + +/** + * Extend an existing rope structure by adding additional space at the end. + * @param allocator + * @param buf the buffer to extend + * @param total_capacity the number of bytes by which to extend. This should + * be the total new target capacity. + * + * It is assumed that this will only be called when it is safe to relocate + * the contents of the underlying buffer. + * + * Each of the appended rdb_ROPESEG structures should initially have the + * `RDB_ROPESEG_F_LIB` indicating they are in use by the library. + */ +typedef void (*rdb_buf_reserve_fn) + (rdb_pALLOCATOR allocator, rdb_ROPEBUF *buf, unsigned total_capacity); + + +/** + * Allocate a new segment. + * The returned segment should have enough capacity for capacity bytes. The + * returned shflags should contain RDB_ROPESEG_F_LIBUSED + */ +typedef rdb_ROPESEG *(*rdb_seg_alloc_fn) + (rdb_pALLOCATOR allocator, unsigned capacity); + +/** + * This will resize the segment to be able to contain up to `capacity` bytes. + * Any contents previously in the buffer should not be changed, though the + * underlying `root` pointer may change. + */ +typedef rdb_ROPESEG *(*rdb_seg_realloc_fn) + (rdb_pALLOCATOR allocator, rdb_ROPESEG *orig, unsigned capacity); + +/** Release a previous segment allocated by rdb_seg_alloc_fn */ +typedef void (*rdb_seg_free_fn)(rdb_pALLOCATOR allocator, rdb_ROPESEG *seg); + + +/** Allocator routines. This table is owned by the user. */ +typedef struct rdb_ALLOCATOR { + rdb_buf_reserve_fn r_reserve; + rdb_seg_alloc_fn s_alloc; + rdb_seg_realloc_fn s_realloc; + rdb_seg_free_fn s_release; + + /** + * This is called explicitly by an IOROPE structure when the allocator + * is no longer needed. Note that the allocator should likely keep track + * of any existing segments before freeing all its resources. + */ + void (*a_release)(rdb_pALLOCATOR); + void (*dump)(rdb_pALLOCATOR,FILE*); +} rdb_ALLOCATOR; + +/** + * @} + */ + +/** + * Initialize the IOROPE structure + * @param rope a rope to use + * @param allocator the allocator to use for allocation. The IOROPE structure + * takes ownership of the allocator. + */ +void +rdb_init(rdb_IOROPE *rope, rdb_ALLOCATOR *allocator); + +/** + * Change the allocator for the rope. This can be done at any time during + * the application. + * @param rope + * @param allocator The new allocator to use + */ +void +rdb_challoc(rdb_IOROPE *rope, rdb_ALLOCATOR *allocator); + +void +rdb_cleanup(rdb_IOROPE *ior); + + +/** + * @name Basic Read API + * @{ + */ + +/** + * @brief Prepare a series of IOV structures for reading from the network. + * + * @param ior the IOROPE structure + * @param[in,out] iov an array of IOV elements + * @param niov the number of IOV elements + */ +unsigned +rdb_rdstart(rdb_IOROPE *ior, nb_IOV *iov, unsigned niov); + + +/** + * Indicate that some data was placed into the IOV structures populated with + * rdstart() + * @param ior the IOROPE structure + * @param nr the number of total bytes placed into the IOV buffers + */ +void +rdb_rdend(rdb_IOROPE *ior, unsigned nr); + +/** + * Indicate that some data at the beginning of the buffer is no longer needed + * @param ior the rope + * @param nr the number of bytes to discard. + * + * Note that if a segment was previously referenced, it is not invalidated + * but is no longer used within the IOROPE structure itself + */ +void +rdb_consumed(rdb_IOROPE *ior, unsigned nr); + +/** + * Ensure that a given chunk of data at the beginning of the rope structure + * can fit contiguously within a single char buffer + * @param ior the IOROPE structure + * @param n number of bytes which must be contiguous + */ +void +rdb_consolidate(rdb_IOROPE *ior, unsigned n); + +/** + * Convenience function to retrieve a pointer to the beginning of the + * buffer. The pointer is guaranteed to point to n contiguous bytes. + */ +char * +rdb_get_consolidated(rdb_IOROPE *ior, unsigned n); + +/** + * @} + */ + +/** + * @name Extended Read API + * @{ + */ + +/** + * Copy n bytes of data from the beginning of the rope structure into the + * buffer pointed to by buf + */ +void +rdb_copyread(rdb_IOROPE *ior, void *buf, unsigned n); + +/** Get the pointer to the beginning of the buffer in the IOROPE. */ +#define rdb_refread(ior) \ + ((LCB_LIST_ITEM(ior->recvd.segments.next, rdb_ROPESEG, llnode))->root + \ + (LCB_LIST_ITEM(ior->recvd.segments.next, rdb_ROPESEG, llnode))->start) + +/** + * Populate an array of ROPESEG and IOV structures with data from the IOROPE. + * @param ior + * @param[out] iov the iov array containing buffer offsets + * @param[out] segs an array to contain pointers to the segments for the IOVs + * @param[in] nelem number of elements in the array + * @param[in] ndata number of bytes to populate the arrays with + * @return the number of IOV elements actually used, or -1 if the arrays + * did not contain enough elements to contain the IOVs completely. + */ +int +rdb_refread_ex(rdb_IOROPE *ior, nb_IOV *iov, rdb_ROPESEG **segs, + unsigned nelem, unsigned ndata); + + +/** + * Get the maximum contiguous size of the current input. This is the size of + * data which may be read efficiently via 'get_consolidated' without actually + * reallocating memory + */ +unsigned +rdb_get_contigsize(rdb_IOROPE *ior); + +/** + * Increase the reference count on the segment. When a reference count is set + * on a segment, internal functions will no longer be able to reuse any of its + * contents (however further data may be read _into_ the segment). The contents + * actually reserved are pinned to the contextual IOV structure which should + * be available when receiving the segment. + */ +void +rdb_seg_ref(rdb_ROPESEG *seg); + +/** + * When you are done with the segment (or the borrowed IOVs thereof) call this. + * The segment may no longer be accessed by the caller. + */ +void +rdb_seg_unref(rdb_ROPESEG *seg); + +/** @private */ +#define rdb_seg_recyclable(seg) (((seg)->shflags & RDB_ROPESEG_F_USER) == 0) + +/** + * Get the first segment. Useful for associating a contiguous consolidated + * read without actually using refread + * @param ior the rope structure + * @return the segment of the contiguous read + */ +#define rdb_get_first_segment(ior) RDB_SEG_FIRST(&(ior)->recvd) + +/** + * @} + */ + +/** + * @name Utility Macros + * @{ + */ + +/** number of unused bytes remaining in the segment */ +#define RDB_SEG_SPACE(seg) (seg)->nalloc - ((seg)->nused + (seg)->start) + +/** pointer to the first used byte in the segment */ +#define RDB_SEG_RBUF(seg) (seg)->root + (seg)->start + +/** pointer to the first available unused byte in the segment */ +#define RDB_SEG_WBUF(seg) (seg)->root + (seg)->start + (seg)->nused + +/** last segment in the rope structure */ +#define RDB_SEG_LAST(rope) \ + (LCB_LIST_TAIL(&(rope)->segments)) \ + ? LCB_LIST_ITEM(LCB_LIST_TAIL(&(rope)->segments), rdb_ROPESEG, llnode) \ + : NULL + +/** first segment in the rope structure */ +#define RDB_SEG_FIRST(rope) \ + (LCB_LIST_HEAD(&(rope)->segments)) \ + ? LCB_LIST_ITEM(LCB_LIST_HEAD(&(rope)->segments), rdb_ROPESEG, llnode) \ + : NULL +/** + * @} + */ + +#define rdb_get_nused(ior) (ior)->recvd.nused + +/** + * Add data into the read buffer. This is primarily used for testing and does + * the equivalent of a network "Read". + * @param ior The iorope structure + * @param buf The buffer to copy + * @param nbuf Size of the buffer + */ +void +rdb_copywrite(rdb_IOROPE *ior, void *buf, unsigned nbuf); + +/** + * Allocator APIs + * Returns the big or "Default" allocator. + */ +LCB_INTERNAL_API +rdb_ALLOCATOR * +rdb_bigalloc_new(void); + +/** + * Returns a chunked allocator which will attempt to allocated readahead buffers + * of a specified size + * @param chunksize the desired chunk/segment size + */ +LCB_INTERNAL_API +rdb_ALLOCATOR * +rdb_chunkalloc_new(unsigned chunksize); + +/** + * Returns a simple allocator which merely proxies to malloc/calloc/realloc/free + */ +LCB_INTERNAL_API +rdb_ALLOCATOR * +rdb_libcalloc_new(void); + +/** + * Dump information about the iorope structure to a file + * @param ior The rope structure to dump + * @param fp The destination file. + */ +void +rdb_dump(const rdb_IOROPE *ior, FILE *fp); + +#ifdef __cplusplus +} +#endif +#endif + +/** + * @} + */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/retrychk.c b/couchbase-sys/libcouchbase-2.7.0/src/retrychk.c new file mode 100644 index 00000000..5ad68038 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/retrychk.c @@ -0,0 +1,113 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" + +int +lcb_should_retry(const lcb_settings *settings, const mc_PACKET *pkt, lcb_error_t err) +{ + lcb_RETRYCMDOPTS policy; + lcb_RETRYMODEOPTS mode; + protocol_binary_request_header hdr; + + mcreq_read_hdr(pkt, &hdr); + + switch (hdr.request.opcode) { + /* None of these commands can be 'redistributed' to other servers */ + case PROTOCOL_BINARY_CMD_GET_REPLICA: + case PROTOCOL_BINARY_CMD_FLUSH: + case PROTOCOL_BINARY_CMD_OBSERVE: + case PROTOCOL_BINARY_CMD_OBSERVE_SEQNO: + case PROTOCOL_BINARY_CMD_STAT: + case PROTOCOL_BINARY_CMD_VERBOSITY: + case PROTOCOL_BINARY_CMD_VERSION: + return 0; + } + + if (err == LCB_ETIMEDOUT || err == LCB_MAP_CHANGED) { + /* We can't exceed a timeout for ETIMEDOUT */ + /* MAP_CHANGED is sent after we've already called this function on the + * packet once before */ + return 0; + } else if (err == LCB_AUTH_ERROR) { + /* spurious auth error */ + return 1; + } else if (err == LCB_NOT_MY_VBUCKET) { + mode = LCB_RETRY_ON_VBMAPERR; + } else if (err == LCB_MAX_ERROR) { + /* special, topology change */ + mode = LCB_RETRY_ON_TOPOCHANGE; + } else if (LCB_EIFNET(err)) { + mode = LCB_RETRY_ON_SOCKERR; + } else { + /* invalid mode */ + return 0; + } + policy = settings->retry[mode]; + + if (policy == LCB_RETRY_CMDS_ALL) { + return 1; + } else if (policy == LCB_RETRY_CMDS_NONE) { + return 0; + } + + /** read the header */ + switch (hdr.request.opcode) { + + /* get is a safe operation which may be retried */ + case PROTOCOL_BINARY_CMD_GET: + case PROTOCOL_BINARY_CMD_GETKQ: + case PROTOCOL_BINARY_CMD_SUBDOC_GET: + case PROTOCOL_BINARY_CMD_SUBDOC_EXISTS: + case PROTOCOL_BINARY_CMD_SUBDOC_MULTI_LOOKUP: + return policy & LCB_RETRY_CMDS_GET; + + case PROTOCOL_BINARY_CMD_ADD: + return policy & LCB_RETRY_CMDS_SAFE; + + /* mutation operations are retriable so long as they provide a CAS */ + case PROTOCOL_BINARY_CMD_SET: + case PROTOCOL_BINARY_CMD_REPLACE: + case PROTOCOL_BINARY_CMD_APPEND: + case PROTOCOL_BINARY_CMD_PREPEND: + case PROTOCOL_BINARY_CMD_DELETE: + case PROTOCOL_BINARY_CMD_UNLOCK_KEY: + case PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_ADD_UNIQUE: + case PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST: + case PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST: + case PROTOCOL_BINARY_CMD_SUBDOC_COUNTER: + case PROTOCOL_BINARY_CMD_SUBDOC_DELETE: + case PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT: + case PROTOCOL_BINARY_CMD_SUBDOC_REPLACE: + case PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD: + case PROTOCOL_BINARY_CMD_SUBDOC_MULTI_MUTATION: + if (hdr.request.cas) { + return policy & LCB_RETRY_CMDS_SAFE; + } else { + return 0; + } + + /* none of these commands accept a CAS, so they are not safe */ + case PROTOCOL_BINARY_CMD_INCREMENT: + case PROTOCOL_BINARY_CMD_DECREMENT: + case PROTOCOL_BINARY_CMD_TOUCH: + case PROTOCOL_BINARY_CMD_GAT: + case PROTOCOL_BINARY_CMD_GET_LOCKED: + default: + return 0; + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/retryq.cc b/couchbase-sys/libcouchbase-2.7.0/src/retryq.cc new file mode 100644 index 00000000..094908ed --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/retryq.cc @@ -0,0 +1,394 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "packetutils.h" +#include "retryq.h" +#include "config.h" +#include "logging.h" +#include "internal.h" +#include "bucketconfig/clconfig.h" + +#define LOGARGS(rq, lvl) (rq)->settings, "retryq", LCB_LOG_##lvl, __FILE__, __LINE__ +#define RETRY_PKT_KEY "retry_queue" + +using namespace lcb; +struct SchedNode : lcb_list_t {}; +struct TmoNode : lcb_list_t {}; + +struct lcb::RetryOp : mc_EPKTDATUM, SchedNode, TmoNode { + /**Cache the actual start time of the command. Since the start time may + * change if read_ts_wait is enabled, and we don't want to end up looping + * on a command forever. */ + hrtime_t start; + hrtime_t trytime; /**< Next retry time */ + mc_PACKET *pkt; + lcb_error_t origerr; + RetryOp(); +}; + +static RetryOp *from_schednode(lcb_list_t *ll) { + return static_cast(static_cast(ll)); +} +static RetryOp *from_tmonode(lcb_list_t *ll) { + return static_cast(static_cast(ll)); +} +template +int list_cmp(const T& a, const T& b) { + if (a == b) { + return 0; + } else if (a > b) { + return 1; + } else { + return -1; + } +} + +hrtime_t +RetryQueue::get_retry_interval() const { + return LCB_US2NS(settings->retry_interval); +} + +/** + * Fuzz offset. When callback is received to schedule an operation, we may + * retry commands whose expiry is up to this many seconds in the future. This + * is to avoid excessive callbacks into the timer function + */ +#define TIMEFUZZ_NS LCB_US2NS(LCB_MS2US(5)) + +void +RetryQueue::update_trytime(RetryOp *op, hrtime_t now) +{ + /** + * Estimate the next retry timestamp. This is: + * Base interval + (Number of retries x Backoff factor) + */ + if (!now) { + now = gethrtime(); + } + op->trytime = now + (hrtime_t) ( + (float)get_retry_interval() * + (float)op->pkt->retries * + (float)settings->retry_backoff); +} + +/** Comparison routine for sorting by timeout */ +static int cmpfn_tmo(lcb_list_t *ll_a, lcb_list_t *ll_b) { + return list_cmp(from_tmonode(ll_a)->start, from_tmonode(ll_b)->start); +} + +static int cmpfn_retry(lcb_list_t *ll_a, lcb_list_t *ll_b) { + return list_cmp(from_schednode(ll_a)->trytime, from_schednode(ll_b)->trytime); +} + +static void +assign_error(RetryOp *op, lcb_error_t err) +{ + if (err == LCB_NOT_MY_VBUCKET) { + err = LCB_ETIMEDOUT; /* :( */ + } + + if (op->origerr == LCB_SUCCESS) { + op->origerr = err; + } + + if (err == LCB_ETIMEDOUT) { + return; /* Ignore timeout errors */ + } + + if (LCB_EIFNET(op->origerr) && op->origerr != LCB_ETIMEDOUT && + (err == LCB_NETWORK_ERROR || err == LCB_CONNECT_ERROR)) { + return; + } + + op->origerr = err; +} + +void +RetryQueue::erase(RetryOp *op) +{ + lcb_list_delete(static_cast(op)); + lcb_list_delete(static_cast(op)); +} + +void +RetryQueue::fail(RetryOp *op, lcb_error_t err) +{ + protocol_binary_request_header hdr; + + lcb::Server tmpsrv; /** Temporary pipeline */ + tmpsrv.instance = get_instance(); + tmpsrv.parent = cq; + + mcreq_read_hdr(op->pkt, &hdr); + MemcachedResponse resp(protocol_binary_command(hdr.request.opcode), + hdr.request.opaque, + PROTOCOL_BINARY_RESPONSE_EINVAL); + + assign_error(op, err); + lcb_log(LOGARGS(this, WARN), "Failing command (seq=%u) from retry queue with error code 0x%x", op->pkt->opaque, op->origerr); + + mcreq_dispatch_response(&tmpsrv, op->pkt, &resp, op->origerr); + op->pkt->flags |= MCREQ_F_FLUSHED|MCREQ_F_INVOKED; + erase(op); + mcreq_packet_done(&tmpsrv, op->pkt); + lcb_maybe_breakout(get_instance()); +} + +void +RetryQueue::schedule(hrtime_t now) +{ + if (empty()) { + lcbio_timer_disarm(timer); + return; + } + + if (!now) { + now = gethrtime(); + } + + /** Figure out which is first */ + RetryOp *first_tmo = from_tmonode(LCB_LIST_HEAD(&tmoops)); + RetryOp *first_sched = from_schednode(LCB_LIST_HEAD(&schedops)); + + hrtime_t schednext = first_sched->trytime; + hrtime_t tmonext = first_tmo->start +LCB_US2NS(settings->operation_timeout); + hrtime_t selected = schednext > tmonext ? tmonext : schednext; + + hrtime_t diff; + if (selected <= now) { + diff = 0; + } else { + diff = selected - now; + } + + uint32_t us_interval = LCB_NS2US(diff); + lcb_log(LOGARGS(this, TRACE), "Next tick in %u ms", (unsigned)us_interval/1000); + lcbio_timer_rearm(timer, us_interval); +} + +/** + * Flush the queue + * @param rq The queue to flush + * @param throttle Whether to throttle operations to be retried. If this is + * set to false then all operations will be attempted (assuming they have + * not timed out) + */ +void +RetryQueue::flush(bool throttle) +{ + hrtime_t now = gethrtime(); + lcb_list_t *ll, *ll_next; + lcb_list_t resched_next; + + /** Check timeouts first */ + LCB_LIST_SAFE_FOR(ll, ll_next, &tmoops) { + RetryOp *op = from_tmonode(ll); + hrtime_t curtmo = op->start + LCB_US2NS(settings->operation_timeout); + + if (curtmo <= now) { + fail(op, LCB_ETIMEDOUT); + } else { + break; + } + } + + lcb_list_init(&resched_next); + LCB_LIST_SAFE_FOR(ll, ll_next, &schedops) { + protocol_binary_request_header hdr; + int vbid, srvix; + hrtime_t curnext; + + RetryOp *op = from_schednode(ll); + curnext = op->trytime - TIMEFUZZ_NS; + + if (curnext > now && throttle) { + break; + } + + mcreq_read_hdr(op->pkt, &hdr); + vbid = ntohs(hdr.request.vbucket); + srvix = lcbvb_vbmaster(cq->config, vbid); + + if (srvix < 0 || (unsigned)srvix >= cq->npipelines) { + /* No server found to map to */ + assign_error(op, LCB_NO_MATCHING_SERVER); + + /* Request a new configuration. If it's time to request a new + * configuration (i.e. the attempt has not been throttled) then + * keep the command in there until it has a chance to be scheduled. + */ + lcb_bootstrap_common(get_instance(), LCB_BS_REFRESH_THROTTLE); + if (lcb_confmon_is_refreshing(get_instance()->confmon) || + settings->retry[LCB_RETRY_ON_MISSINGNODE]) { + + lcb_list_delete(static_cast(op)); + lcb_list_delete(static_cast(op)); + lcb_list_append(&resched_next, static_cast(op)); + op->pkt->retries++; + update_trytime(op, now); + } else { + fail(op, LCB_NO_MATCHING_SERVER); + } + } else { + mc_PIPELINE *newpl = cq->pipelines[srvix]; + mcreq_enqueue_packet(newpl, op->pkt); + newpl->flush_start(newpl); + erase(op); + } + } + + LCB_LIST_SAFE_FOR(ll, ll_next, &resched_next) { + RetryOp *op = from_schednode(ll); + lcb_list_add_sorted(&schedops, static_cast(op), cmpfn_retry); + lcb_list_add_sorted(&tmoops, static_cast(op), cmpfn_tmo); + } + + schedule(now); +} + +static void rq_tick(void *arg) { + reinterpret_cast(arg)->tick(); +} + +void RetryQueue::tick() { + flush(true); +} + +void RetryQueue::signal() { + flush(false); +} + +static void op_dtorfn(mc_EPKTDATUM *d) { + delete static_cast(d); +} + +RetryOp::RetryOp() { + memset(this, 0, sizeof *this); + mc_EPKTDATUM::dtorfn = op_dtorfn; + mc_EPKTDATUM::key = RETRY_PKT_KEY; +} + +void +RetryQueue::add(mc_EXPACKET *pkt, const lcb_error_t err, int options) +{ + RetryOp *op; + mc_EPKTDATUM *d = mcreq_epkt_find(pkt, RETRY_PKT_KEY); + if (d) { + op = static_cast(d); + } else { + op = new RetryOp(); + op->start = MCREQ_PKT_RDATA(&pkt->base)->start; + mcreq_epkt_insert(pkt, op); + } + + op->pkt = &pkt->base; + pkt->base.retries++; + assign_error(op, err); + if (options & RETRY_SCHED_IMM) { + op->trytime = gethrtime(); /* now */ + } else if (err == LCB_NOT_MY_VBUCKET) { + op->trytime = gethrtime() + LCB_US2NS(settings->retry_nmv_interval); + } else { + update_trytime(op); + } + + lcb_list_add_sorted(&schedops, static_cast(op), cmpfn_retry); + lcb_list_add_sorted(&tmoops, static_cast(op), cmpfn_tmo); + + lcb_log(LOGARGS(this, DEBUG), "Adding PKT=%p to retry queue. Try count=%u", (void*)pkt, pkt->base.retries); + schedule(); +} + +void +RetryQueue::nmvadd(mc_EXPACKET *detchpkt) +{ + int flags = 0; + if (settings->nmv_retry_imm) { + flags = RETRY_SCHED_IMM; + } + add(detchpkt, LCB_NOT_MY_VBUCKET, flags); +} + +static void +fallback_handler(mc_CMDQUEUE *cq, mc_PACKET *pkt) +{ + lcb_t instance = reinterpret_cast(cq->cqdata); + instance->retryq->add_fallback(pkt); +} + +void RetryQueue::add_fallback(mc_PACKET *pkt) { + mc_PACKET *copy = mcreq_renew_packet(pkt); + add((mc_EXPACKET*)copy, LCB_NO_MATCHING_SERVER, RETRY_SCHED_IMM); +} + +void +RetryQueue::reset_timeouts(lcb_U64 now) +{ + lcb_list_t *ll; + LCB_LIST_FOR(ll, &schedops) { + RetryOp *op = from_schednode(ll); + op->start = now; + } +} + + +RetryQueue::RetryQueue(mc_CMDQUEUE *cq_, lcbio_pTABLE table, lcb_settings *settings_) { + settings = settings_; + cq = cq_; + timer = lcbio_timer_new(table, this, rq_tick); + + lcb_settings_ref(settings); + lcb_list_init(&tmoops); + lcb_list_init(&schedops); + mcreq_set_fallback_handler(cq, fallback_handler); +} + +RetryQueue::~RetryQueue() { + lcb_list_t *llcur, *llnext; + + LCB_LIST_SAFE_FOR(llcur, llnext, &schedops) { + RetryOp *op = from_schednode(llcur); + fail(op, LCB_ERROR); + } + + lcbio_timer_destroy(timer); + lcb_settings_unref(settings); +} + +lcb_error_t +RetryQueue::error_for(const mc_PACKET *packet) +{ + if (! (packet->flags & MCREQ_F_DETACHED)) { + return LCB_SUCCESS; /* Not detached */ + } + + mc_EPKTDATUM *d = mcreq_epkt_find((mc_EXPACKET*)packet, RETRY_PKT_KEY); + if (!d) { + return LCB_SUCCESS; + } + return static_cast(d)->origerr; +} + +void +RetryQueue::dump(FILE *fp, mcreq_payload_dump_fn dumpfn) +{ + lcb_list_t *cur; + LCB_LIST_FOR(cur, &schedops) { + RetryOp *op = from_schednode(cur); + mcreq_dump_packet(op->pkt, fp, dumpfn); + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/retryq.h b/couchbase-sys/libcouchbase-2.7.0/src/retryq.h new file mode 100644 index 00000000..1e22a66e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/retryq.h @@ -0,0 +1,169 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_RETRYQ_H +#define LCB_RETRYQ_H + +#include +#include +#include +#include "list.h" + +#ifdef __cplusplus + +/** + * @file + * @brief Retry Queue + * + * @defgroup lcb-retryq Retry Queue + * + * @details + * Retry queue for operations. The retry queue accepts commands which have + * previously failed and aims to retry them within a specified interval. + * + * @addtogroup lcb-retryq + * @{ + */ + +namespace lcb { + +struct RetryOp; + +class RetryQueue { +public: + /** + * @brief Create a new retry queue. + * The retry queue serves as an asynchronous poller which will retry operations + * with a certain throttle. + * + * @param cq The parent cmdqueue object + * @param table used to create the timer + * @param settings Used for logging and interval timeouts + * @return A new retry queue object + */ + RetryQueue(mc_CMDQUEUE* cq_, lcbio_pTABLE, lcb_settings *); + ~RetryQueue(); + + /** + * @brief Enqueue a failed command + * @param detchpkt A detached packet allocated with mcreq_renew_packet() + * @param err the error code which caused the packet to be placed inside the + * retry queue. Depending on the error code and subsequent errors, this code + * will ultimately be sent back to the operation callback when the result is + * final. + * + * @attention Only simple commands containing vBuckets may be placed here. + * Complex commands such as OBSERVE or STAT may _not_ be retried through this + * mechanism. Additionally since this relies on determining a vBucket master + * it may _not_ be used for memcached buckets (which is typically OK, as we only + * map things here as a response for a not-my-vbucket). + */ + void add(mc_EXPACKET *detchpkt, lcb_error_t err) { + add(detchpkt, err, 0); + } + + /** + * Retries the given packet as a result of a NOT_MY_VBUCKET failure. Currently + * this is provided to allow for different behavior when handling these types + * of responses. + * + * @param detchpkt The new packet + */ + void nmvadd(mc_EXPACKET *detchpkt); + + /** + * @brief Retry all queued operations + * + * This should normally be called when a new server connection is made or when + * a new configuration update has arrived. + * + * @param rq The queue + */ + void signal(); + + /** + * If this packet has been previously retried, this obtains the original error + * which caused it to be enqueued in the first place. This eliminates spurious + * timeout errors which mask the real cause of the error. + * + * @param pkt The packet to check for + * @return An error code, or LCB_SUCCESS if the packet does not have an + * original error. + */ + static lcb_error_t error_for(const mc_PACKET*); + + /** + * Dumps the packets inside the queue + * @param rq The request queue + * @param fp The file to which the output should be written to + */ + void dump(FILE *fp, mcreq_payload_dump_fn dumpfn); + + /** + * @brief Check if there are operations to retry + * @return nonzero if there are pending operations + */ + bool empty() const { return LCB_LIST_IS_EMPTY(&schedops); } + + /** + * @brief Reset all timeouts on the retry queue. + * + * This will defer the timeout to start from the current time rather than + * the time it was initially placed in the queue. Items are usually placed + * in the queue after a network failure or similar; however one exception + * is items which are placed in the queue via the scheduling APIs directly + * (if there is no host for the command's vBucket) + * + * @param now The time to use + */ + void reset_timeouts(uint64_t now = 0); + + /** Event loop tick */ + inline void tick(); + + inline void add_fallback(mc_PACKET *pkt); + +private: + void erase(RetryOp*); + void fail(RetryOp*, lcb_error_t); + void schedule(hrtime_t now = 0); + void flush(bool throttle); + void update_trytime(RetryOp *op, hrtime_t now = 0); + hrtime_t get_retry_interval() const; + lcb_t get_instance() const { + return reinterpret_cast(cq->cqdata); + } + + enum AddOptions { + RETRY_SCHED_IMM = 0x01 + }; + void add(mc_EXPACKET *pkt, lcb_error_t, int options); + + /** List of operations in retry ordering. Sorted by 'crtime' */ + lcb_list_t schedops; + /** List of operations in timeout ordering. Ordered by 'start_time' */ + lcb_list_t tmoops; + /** Parent command queue */ + mc_CMDQUEUE *cq; + lcb_settings *settings; + lcbio_pTIMER timer; +}; + +} +/**@}*/ +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/ringbuffer.c b/couchbase-sys/libcouchbase-2.7.0/src/ringbuffer.c new file mode 100644 index 00000000..9b64f678 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/ringbuffer.c @@ -0,0 +1,442 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2011-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" + +static lcb_size_t minimum(lcb_size_t a, lcb_size_t b) +{ + return (a < b) ? a : b; +} + + +int ringbuffer_initialize(ringbuffer_t *buffer, lcb_size_t size) +{ + char *root = malloc(size); + if (root == NULL) { + return 0; + } + ringbuffer_take_buffer(buffer, root, size); + return 1; +} + +void ringbuffer_take_buffer(ringbuffer_t *buffer, char *buf, lcb_size_t size) +{ + memset(buffer, 0, sizeof(ringbuffer_t)); + buffer->root = buf; + buffer->size = size; + buffer->write_head = buffer->root; + buffer->read_head = buffer->root; +} + + +void ringbuffer_reset(ringbuffer_t *buffer) +{ + ringbuffer_consumed(buffer, + ringbuffer_get_nbytes(buffer)); +} + +void ringbuffer_destruct(ringbuffer_t *buffer) +{ + free(buffer->root); + buffer->root = buffer->read_head = buffer->write_head = NULL; + buffer->size = buffer->nbytes = 0; +} + +int ringbuffer_ensure_capacity(ringbuffer_t *buffer, lcb_size_t size) +{ + char *new_root; + lcb_size_t new_size = buffer->size << 1; + if (new_size == 0) { + new_size = 128; + } + + if (size < (buffer->size - buffer->nbytes)) { + /* we've got capacity! */ + return 1; + } + + /* determine the new buffer size... */ + while ((new_size - buffer->nbytes) < size) { + new_size <<= 1; + } + + /* go ahead and allocate a bigger block */ + if ((new_root = malloc(new_size)) == NULL) { + /* Allocation failed! */ + return 0; + } else { + /* copy the data over :) */ + char *old; + lcb_size_t nbytes = buffer->nbytes; + lcb_size_t nr = ringbuffer_read(buffer, new_root, nbytes); + lcb_assert(nr == nbytes); + old = buffer->root; + buffer->size = new_size; + buffer->root = new_root; + buffer->nbytes = nbytes; + buffer->read_head = buffer->root; + buffer->write_head = buffer->root + nbytes; + free(old); + return 1; + } +} + +lcb_size_t ringbuffer_get_size(ringbuffer_t *buffer) +{ + return buffer->size; +} + +void *ringbuffer_get_start(ringbuffer_t *buffer) +{ + return buffer->root; +} + +void *ringbuffer_get_read_head(ringbuffer_t *buffer) +{ + return buffer->read_head; +} + +void *ringbuffer_get_write_head(ringbuffer_t *buffer) +{ + return buffer->write_head; +} + +lcb_size_t ringbuffer_write(ringbuffer_t *buffer, + const void *src, + lcb_size_t nb) +{ + const char *s = src; + lcb_size_t nw = 0; + lcb_size_t space; + lcb_size_t toWrite; + + if (buffer->write_head >= buffer->read_head) { + /* write up to the end with data.. */ + space = buffer->size - (lcb_size_t)(buffer->write_head - buffer->root); + toWrite = minimum(space, nb); + + if (src != NULL) { + memcpy(buffer->write_head, s, toWrite); + } + buffer->nbytes += toWrite; + buffer->write_head += toWrite; + nw = toWrite; + + if (buffer->write_head == (buffer->root + buffer->size)) { + buffer->write_head = buffer->root; + } + + if (nw == nb) { + /* everything is written to the buffer.. */ + return nw; + } + + nb -= toWrite; + s += toWrite; + } + + /* Copy data up until we catch up with the read head */ + space = (lcb_size_t)(buffer->read_head - buffer->write_head); + toWrite = minimum(space, nb); + if (src != NULL) { + memcpy(buffer->write_head, s, toWrite); + } + buffer->nbytes += toWrite; + buffer->write_head += toWrite; + nw += toWrite; + + if (buffer->write_head == (buffer->root + buffer->size)) { + buffer->write_head = buffer->root; + } + + return nw; +} + +lcb_size_t ringbuffer_strcat(ringbuffer_t *buffer, const char *str) +{ + lcb_size_t len = strlen(str); + if (!ringbuffer_ensure_capacity(buffer, len)) { + return 0; + } + return ringbuffer_write(buffer, str, len); +} + +static void maybe_reset(ringbuffer_t *buffer) +{ + if (buffer->nbytes == 0) { + buffer->write_head = buffer->root; + buffer->read_head = buffer->root; + } +} + + +lcb_size_t ringbuffer_read(ringbuffer_t *buffer, void *dest, lcb_size_t nb) +{ + char *d = dest; + lcb_size_t nr = 0; + lcb_size_t space; + lcb_size_t toRead; + + if (buffer->nbytes == 0) { + return 0; + } + if (buffer->read_head >= buffer->write_head) { + /* read up to the wrap point */ + space = buffer->size - (lcb_size_t)(buffer->read_head - buffer->root); + toRead = minimum(space, nb); + + if (dest != NULL) { + memcpy(d, buffer->read_head, toRead); + } + buffer->nbytes -= toRead; + buffer->read_head += toRead; + nr = toRead; + + if (buffer->read_head == (buffer->root + buffer->size)) { + buffer->read_head = buffer->root; + } + + if (nr == nb) { + maybe_reset(buffer); + return nr; + } + + nb -= toRead; + d += toRead; + } + + space = (lcb_size_t)(buffer->write_head - buffer->read_head); + toRead = minimum(space, nb); + + if (dest != NULL) { + memcpy(d, buffer->read_head, toRead); + } + buffer->nbytes -= toRead; + buffer->read_head += toRead; + nr += toRead; + + if (buffer->read_head == (buffer->root + buffer->size)) { + buffer->read_head = buffer->root; + } + + maybe_reset(buffer); + return nr; +} + +lcb_size_t ringbuffer_peek(ringbuffer_t *buffer, void *dest, lcb_size_t nb) +{ + ringbuffer_t copy = *buffer; + return ringbuffer_read(©, dest, nb); +} + +lcb_size_t ringbuffer_peek_at(ringbuffer_t *buffer, lcb_size_t offset, + void *dest, lcb_size_t nb) +{ + ringbuffer_t copy = *buffer; + lcb_size_t n = ringbuffer_read(©, NULL, offset); + if (n != offset) { + return -1; + } + return ringbuffer_read(©, dest, nb); +} + +void ringbuffer_produced(ringbuffer_t *buffer, lcb_size_t nb) +{ + lcb_size_t n = ringbuffer_write(buffer, NULL, nb); + lcb_assert(n == nb); +} + +void ringbuffer_consumed(ringbuffer_t *buffer, lcb_size_t nb) +{ + lcb_size_t n = ringbuffer_read(buffer, NULL, nb); + lcb_assert(n == nb); +} + +lcb_size_t ringbuffer_get_nbytes(ringbuffer_t *buffer) +{ + return buffer->nbytes; +} + +lcb_size_t ringbuffer_update(ringbuffer_t *buffer, + ringbuffer_direction_t direction, + const void *src, lcb_size_t nb) +{ + const char *s = src; + lcb_size_t nw, ret = 0; + + if (direction == RINGBUFFER_READ) { + if (buffer->read_head <= buffer->write_head) { + nw = minimum(nb, buffer->nbytes); + memcpy(buffer->read_head, s, nw); + ret += nw; + } else { + nw = minimum(nb, buffer->size - (lcb_size_t)(buffer->read_head - buffer->root)); + memcpy(buffer->read_head, s, nw); + nb -= nw; + s += nw; + ret += nw; + if (nb) { + nw = minimum(nb, (lcb_size_t)(buffer->write_head - buffer->root)); + memcpy(buffer->root, s, nw); + ret += nw; + } + } + } else { + if (buffer->write_head >= buffer->read_head) { + nw = minimum(nb, buffer->nbytes); + memcpy(buffer->write_head - nw, s, nw); + ret += nw; + } else { + nb = minimum(nb, buffer->nbytes); + nw = minimum(nb, (lcb_size_t)(buffer->write_head - buffer->root)); + memcpy(buffer->write_head - nw, s + nb - nw, nw); + nb -= nw; + ret += nw; + if (nb) { + nw = minimum(nb, buffer->size - (lcb_size_t)(buffer->read_head - buffer->root)); + memcpy(buffer->root + buffer->size - nw, s, nw); + ret += nw; + } + } + } + return ret; +} + + +void ringbuffer_get_iov(ringbuffer_t *buffer, + ringbuffer_direction_t direction, + struct lcb_iovec_st *iov) +{ + iov[1].iov_base = buffer->root; + iov[1].iov_len = 0; + + if (direction == RINGBUFFER_READ) { + iov[0].iov_base = buffer->read_head; + iov[0].iov_len = buffer->nbytes; + if (buffer->read_head >= buffer->write_head) { + ptrdiff_t chunk = buffer->root + buffer->size - buffer->read_head; + if (buffer->nbytes > (lcb_size_t)chunk) { + iov[0].iov_len = (lcb_size_t)chunk; + iov[1].iov_len = buffer->nbytes - (lcb_size_t)chunk; + } + } + } else { + lcb_assert(direction == RINGBUFFER_WRITE); + iov[0].iov_base = buffer->write_head; + iov[0].iov_len = buffer->size - buffer->nbytes; + if (buffer->write_head >= buffer->read_head) { + /* I may write all the way to the end! */ + iov[0].iov_len = (lcb_size_t)((buffer->root + buffer->size) - buffer->write_head); + /* And all the way up to the read head */ + iov[1].iov_len = (lcb_size_t)(buffer->read_head - buffer->root); + } + } +} + +int ringbuffer_is_continous(ringbuffer_t *buffer, + ringbuffer_direction_t direction, + lcb_size_t nb) +{ + int ret; + + if (direction == RINGBUFFER_READ) { + ret = (nb <= buffer->nbytes); + + if (buffer->read_head >= buffer->write_head) { + ptrdiff_t chunk = buffer->root + buffer->size - buffer->read_head; + if (nb > (lcb_size_t)chunk) { + ret = 0; + } + } + } else { + ret = (nb <= buffer->size - buffer->nbytes); + if (buffer->write_head >= buffer->read_head) { + ptrdiff_t chunk = buffer->root + buffer->size - buffer->write_head; + if (nb > (lcb_size_t)chunk) { + ret = 0; + } + } + } + return ret; +} + +int ringbuffer_append(ringbuffer_t *src, ringbuffer_t *dest) +{ + char buffer[1024]; + lcb_size_t nr, nw; + + while ((nr = ringbuffer_read(src, buffer, + sizeof(buffer))) != 0) { + lcb_assert(ringbuffer_ensure_capacity(dest, nr)); + nw = ringbuffer_write(dest, buffer, nr); + lcb_assert(nw == nr); + } + + return 1; +} + +int ringbuffer_memcpy(ringbuffer_t *dst, ringbuffer_t *src, + lcb_size_t nbytes) +{ + ringbuffer_t copy = *src; + struct lcb_iovec_st iov[2]; + int ii = 0; + lcb_size_t towrite = nbytes; + lcb_size_t toread, nb; + + if (nbytes > ringbuffer_get_nbytes(src)) { + /* EINVAL */ + return -1; + } + + if (!ringbuffer_ensure_capacity(dst, nbytes)) { + /* Failed to allocate space */ + return -1; + } + + ringbuffer_get_iov(dst, RINGBUFFER_WRITE, iov); + toread = minimum(iov[ii].iov_len, nbytes); + do { + lcb_assert(ii < 2); + nb = ringbuffer_read(©, iov[ii].iov_base, toread); + toread -= nb; + towrite -= nb; + ++ii; + } while (towrite > 0); + ringbuffer_produced(dst, nbytes); + return 0; +} + +int ringbuffer_ensure_alignment(ringbuffer_t *c) +{ +#if defined(__hpux__) || defined(__hpux) || defined(__sparc__) || defined(__sparc) + intptr_t addr = (intptr_t)c->read_head; + + if (addr % 8 != 0) { + ringbuffer_t copy; + if (ringbuffer_initialize(©, c->size) == 0 || + ringbuffer_memcpy(©, c, ringbuffer_get_nbytes(c)) == -1) { + return -1; + } + ringbuffer_destruct(c); + *c = copy; + } +#else + (void)c; +#endif + return 0; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/ringbuffer.h b/couchbase-sys/libcouchbase-2.7.0/src/ringbuffer.h new file mode 100644 index 00000000..440993a2 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/ringbuffer.h @@ -0,0 +1,100 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2011-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef RINGBUFFER_H +#define RINGBUFFER_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct ringbuffer_st { + char *root; + char *read_head; + char *write_head; + lcb_size_t size; + lcb_size_t nbytes; + } ringbuffer_t; + + typedef enum { + RINGBUFFER_READ = 0x01, + RINGBUFFER_WRITE = 0x02 + } ringbuffer_direction_t; + + int ringbuffer_initialize(ringbuffer_t *buffer, + lcb_size_t size); + + /** + * Initialize a ringbuffer, taking ownership of an allocated char buffer. + * This function always succeeds. + * @param buffer a ringbuffer_t to be initialized + * @param buf the buffer to steal + * @param size the allocated size of the buffer + */ + void ringbuffer_take_buffer(ringbuffer_t *buffer, + char *buf, + lcb_size_t size); + + void ringbuffer_reset(ringbuffer_t *buffer); + + void ringbuffer_destruct(ringbuffer_t *buffer); + int ringbuffer_ensure_capacity(ringbuffer_t *buffer, + lcb_size_t size); + lcb_size_t ringbuffer_get_size(ringbuffer_t *buffer); + void *ringbuffer_get_start(ringbuffer_t *buffer); + void *ringbuffer_get_read_head(ringbuffer_t *buffer); + void *ringbuffer_get_write_head(ringbuffer_t *buffer); + lcb_size_t ringbuffer_write(ringbuffer_t *buffer, + const void *src, + lcb_size_t nb); + lcb_size_t ringbuffer_strcat(ringbuffer_t *buffer, + const char *str); + lcb_size_t ringbuffer_read(ringbuffer_t *buffer, + void *dest, + lcb_size_t nb); + lcb_size_t ringbuffer_peek(ringbuffer_t *buffer, + void *dest, + lcb_size_t nb); + lcb_size_t ringbuffer_peek_at(ringbuffer_t *buffer, + lcb_size_t offset, + void *dest, + lcb_size_t nb); + /* replace +nb+ bytes on +direction+ end of the buffer with src */ + lcb_size_t ringbuffer_update(ringbuffer_t *buffer, + ringbuffer_direction_t direction, + const void *src, lcb_size_t nb); + void ringbuffer_get_iov(ringbuffer_t *buffer, + ringbuffer_direction_t direction, + struct lcb_iovec_st *iov); + void ringbuffer_produced(ringbuffer_t *buffer, lcb_size_t nb); + void ringbuffer_consumed(ringbuffer_t *buffer, lcb_size_t nb); + lcb_size_t ringbuffer_get_nbytes(ringbuffer_t *buffer); + int ringbuffer_is_continous(ringbuffer_t *buffer, + ringbuffer_direction_t direction, + lcb_size_t nb); + + int ringbuffer_append(ringbuffer_t *src, ringbuffer_t *dest); + int ringbuffer_memcpy(ringbuffer_t *dst, ringbuffer_t *src, + lcb_size_t nbytes); + + /* Align the read head of the ringbuffer for platforms where it's needed */ + int ringbuffer_ensure_alignment(ringbuffer_t *src); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/settings.c b/couchbase-sys/libcouchbase-2.7.0/src/settings.c new file mode 100644 index 00000000..087ceb00 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/settings.c @@ -0,0 +1,94 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "settings.h" +#include +#include + +LCB_INTERNAL_API +void lcb_default_settings(lcb_settings *settings) +{ + settings->ipv6 = LCB_IPV6_DISABLED; + settings->operation_timeout = LCB_DEFAULT_TIMEOUT; + settings->config_timeout = LCB_DEFAULT_CONFIGURATION_TIMEOUT; + settings->config_node_timeout = LCB_DEFAULT_NODECONFIG_TIMEOUT; + settings->views_timeout = LCB_DEFAULT_VIEW_TIMEOUT; + settings->n1ql_timeout = LCB_DEFAULT_N1QL_TIMEOUT; + settings->durability_timeout = LCB_DEFAULT_DURABILITY_TIMEOUT; + settings->durability_interval = LCB_DEFAULT_DURABILITY_INTERVAL; + settings->http_timeout = LCB_DEFAULT_HTTP_TIMEOUT; + settings->weird_things_threshold = LCB_DEFAULT_CONFIG_ERRORS_THRESHOLD; + settings->weird_things_delay = LCB_DEFAULT_CONFIG_ERRORS_DELAY; + settings->max_redir = LCB_DEFAULT_CONFIG_MAXIMUM_REDIRECTS; + settings->grace_next_cycle = LCB_DEFAULT_CLCONFIG_GRACE_CYCLE; + settings->grace_next_provider = LCB_DEFAULT_CLCONFIG_GRACE_NEXT; + settings->bc_http_stream_time = LCB_DEFAULT_BC_HTTP_DISCONNTMO; + settings->retry_interval = LCB_DEFAULT_RETRY_INTERVAL; + settings->retry_backoff = LCB_DEFAULT_RETRY_BACKOFF; + settings->sslopts = 0; + settings->retry[LCB_RETRY_ON_SOCKERR] = LCB_DEFAULT_NETRETRY; + settings->retry[LCB_RETRY_ON_TOPOCHANGE] = LCB_DEFAULT_TOPORETRY; + settings->retry[LCB_RETRY_ON_VBMAPERR] = LCB_DEFAULT_NMVRETRY; + settings->retry[LCB_RETRY_ON_MISSINGNODE] = 0; + settings->bc_http_urltype = LCB_DEFAULT_HTCONFIG_URLTYPE; + settings->compressopts = LCB_DEFAULT_COMPRESSOPTS; + settings->allocator_factory = rdb_bigalloc_new; + settings->syncmode = LCB_ASYNCHRONOUS; + settings->detailed_neterr = 0; + settings->refresh_on_hterr = 1; + settings->sched_implicit_flush = 1; + settings->fetch_mutation_tokens = 0; + settings->dur_mutation_tokens = 1; + settings->nmv_retry_imm = LCB_DEFAULT_NVM_RETRY_IMM; + settings->tcp_nodelay = LCB_DEFAULT_TCP_NODELAY; + settings->retry_nmv_interval = LCB_DEFAULT_RETRY_NMV_INTERVAL; + settings->vb_noguess = LCB_DEFAULT_VB_NOGUESS; +} + +LCB_INTERNAL_API +lcb_settings * +lcb_settings_new(void) +{ + lcb_settings *settings = calloc(1, sizeof(*settings)); + lcb_default_settings(settings); + settings->refcount = 1; + settings->auth = lcbauth_new(); + return settings; +} + +LCB_INTERNAL_API +void +lcb_settings_unref(lcb_settings *settings) +{ + if (--settings->refcount) { + return; + } + free(settings->bucket); + free(settings->sasl_mech_force); + free(settings->certpath); + free(settings->client_string); + + lcbauth_unref(settings->auth); + + if (settings->ssl_ctx) { + lcbio_ssl_free(settings->ssl_ctx); + } + if (settings->dtorcb) { + settings->dtorcb(settings->dtorarg); + } + free(settings); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/settings.h b/couchbase-sys/libcouchbase-2.7.0/src/settings.h new file mode 100644 index 00000000..19aea513 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/settings.h @@ -0,0 +1,188 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_SETTINGS_H +#define LCB_SETTINGS_H + +/** + * Handy macros for converting between different time resolutions + */ + +/** Convert seconds to millis */ +#define LCB_S2MS(s) ((lcb_uint32_t)s) * 1000 + +/** Convert seconds to microseconds */ +#define LCB_S2US(s) ((lcb_uint32_t)s) * 1000000 + +/** Convert seconds to nanoseconds */ +#define LCB_S2NS(s) ((hrtime_t)s) * 1000000000 + +/** Convert nanoseconds to microseconds */ +#define LCB_NS2US(s) (lcb_uint32_t) ((s) / 1000) + +#define LCB_MS2US(s) (s) * 1000 + +/** Convert microseconds to nanoseconds */ +#define LCB_US2NS(s) ((hrtime_t)s) * 1000 + + +#define LCB_DEFAULT_TIMEOUT LCB_MS2US(2500) + +/** 5 seconds for total bootstrap */ +#define LCB_DEFAULT_CONFIGURATION_TIMEOUT LCB_MS2US(5000) + +/** 2 seconds per node */ +#define LCB_DEFAULT_NODECONFIG_TIMEOUT LCB_MS2US(2000) + +#define LCB_DEFAULT_VIEW_TIMEOUT LCB_MS2US(75000) +#define LCB_DEFAULT_N1QL_TIMEOUT LCB_MS2US(75000) +#define LCB_DEFAULT_DURABILITY_TIMEOUT LCB_MS2US(5000) +#define LCB_DEFAULT_DURABILITY_INTERVAL LCB_MS2US(100) +#define LCB_DEFAULT_HTTP_TIMEOUT LCB_MS2US(75000) +#define LCB_DEFAULT_CONFIG_MAXIMUM_REDIRECTS 3 +#define LCB_DEFAULT_CONFIG_ERRORS_THRESHOLD 100 + +/* 10 seconds */ +#define LCB_DEFAULT_CONFIG_ERRORS_DELAY LCB_MS2US(10000) + +/* 1 second */ +#define LCB_DEFAULT_CLCONFIG_GRACE_CYCLE LCB_MS2US(1000) + +/* 100 ms */ +#define LCB_DEFAULT_CLCONFIG_GRACE_NEXT LCB_MS2US(100) + +/* Infinite (i.e. compat mode) */ +#define LCB_DEFAULT_BC_HTTP_DISCONNTMO -1 + +/* 10ms */ +#define LCB_DEFAULT_RETRY_INTERVAL LCB_MS2US(10) + +/* 1.5x */ +#define LCB_DEFAULT_RETRY_BACKOFF 1.5 + +#define LCB_DEFAULT_TOPORETRY LCB_RETRY_CMDS_ALL +#define LCB_DEFAULT_NETRETRY LCB_RETRY_CMDS_ALL +#define LCB_DEFAULT_NMVRETRY LCB_RETRY_CMDS_ALL +#define LCB_DEFAULT_HTCONFIG_URLTYPE LCB_HTCONFIG_URLTYPE_TRYALL +#define LCB_DEFAULT_COMPRESSOPTS LCB_COMPRESS_NONE + +#define LCB_DEFAULT_NVM_RETRY_IMM 1 +#define LCB_DEFAULT_RETRY_NMV_INTERVAL LCB_MS2US(100) +#define LCB_DEFAULT_VB_NOGUESS 1 +#define LCB_DEFAULT_TCP_NODELAY 1 + +#include "config.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct lcb_logprocs_st; +struct lcbio_SSLCTX; +struct rdb_ALLOCATOR; + +/** + * Stateless setting structure. + * Specifically this contains the 'environment' of the instance for things + * which are intended to be passed around to other objects. + */ +typedef struct lcb_settings_st { + lcb_U16 iid; + lcb_U8 compressopts; + lcb_U8 syncmode; + lcb_U32 read_chunk_size; + lcb_U32 operation_timeout; + lcb_U32 views_timeout; + lcb_U32 http_timeout; + lcb_U32 n1ql_timeout; + lcb_U32 durability_timeout; + lcb_U32 durability_interval; + lcb_U32 config_timeout; + lcb_U32 config_node_timeout; + lcb_U32 retry_interval; + lcb_U32 weird_things_threshold; + lcb_U32 weird_things_delay; + + /** Grace period to wait between querying providers */ + lcb_U32 grace_next_provider; + + /** Grace period to wait between retrying from the beginning */ + lcb_U32 grace_next_cycle; + + /**For bc_http, the amount of type to keep the stream open, for future + * updates. */ + lcb_U32 bc_http_stream_time; + + unsigned bc_http_urltype : 4; + + /** Don't guess next vbucket server. Mainly for testing */ + unsigned vb_noguess : 1; + /** Whether lcb_destroy is synchronous. This mode will run the I/O event + * loop as much as possible until no outstanding events remain.*/ + unsigned syncdtor : 1; + unsigned detailed_neterr : 1; + unsigned randomize_bootstrap_nodes : 1; + unsigned conntype : 1; + unsigned refresh_on_hterr : 1; + unsigned sched_implicit_flush : 1; + unsigned nmv_retry_imm : 1; + unsigned keep_guess_vbs : 1; + unsigned fetch_mutation_tokens : 1; + unsigned dur_mutation_tokens : 1; + unsigned sslopts : 3; + unsigned ipv6 : 2; + unsigned tcp_nodelay : 1; + unsigned readj_ts_wait : 1; + + short max_redir; + unsigned refcount; + + uint8_t retry[LCB_RETRY_ON_MAX]; + float retry_backoff; + + char *bucket; + char *sasl_mech_force; + char *certpath; + lcb_AUTHENTICATOR *auth; + struct rdb_ALLOCATOR* (*allocator_factory)(void); + struct lcbio_SSLCTX *ssl_ctx; + struct lcb_logprocs_st *logger; + void (*dtorcb)(const void *); + void *dtorarg; + char *client_string; + lcb_U32 retry_nmv_interval; +} lcb_settings; + +LCB_INTERNAL_API +void lcb_default_settings(lcb_settings *settings); + +LCB_INTERNAL_API +lcb_settings * +lcb_settings_new(void); + +LCB_INTERNAL_API +void +lcb_settings_unref(lcb_settings *); + +#define lcb_settings_ref(settings) ((void)(settings)->refcount++) +#define lcb_settings_ref2(settings) ((settings)->refcount++, settings) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/simplestring.c b/couchbase-sys/libcouchbase-2.7.0/src/simplestring.c new file mode 100644 index 00000000..5f70806b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/simplestring.c @@ -0,0 +1,211 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "simplestring.h" +#include "assert.h" + +static void ensure_cstr(lcb_string *str); + +int lcb_string_init(lcb_string *str) +{ + str->base = NULL; + str->nalloc = 0; + str->nused = 0; + return 0; +} + +void lcb_string_release(lcb_string *str) +{ + if (str->base == NULL) { + return; + } + free(str->base); + memset(str, 0, sizeof(*str)); +} + +void lcb_string_clear(lcb_string *str) +{ + str->nused = 0; + if (str->nalloc) { + ensure_cstr(str); + } +} + +void lcb_string_added(lcb_string *str, lcb_size_t nused) +{ + str->nused += nused; + lcb_assert(str->nused <= str->nalloc); + ensure_cstr(str); +} + +int lcb_string_reserve(lcb_string *str, lcb_size_t size) +{ + lcb_size_t newalloc; + char *newbuf; + + /** Set size to one greater, for the terminating NUL */ + size++; + if (!size) { + return -1; + } + + if (str->nalloc - str->nused >= size) { + return 0; + } + + newalloc = str->nalloc; + if (!newalloc) { + newalloc = 1; + } + + while (newalloc - str->nused < size) { + if (newalloc * 2 < newalloc) { + return -1; + } + + newalloc *= 2; + } + + newbuf = realloc(str->base, newalloc); + if (newbuf == NULL) { + return -1; + } + + str->base = newbuf; + str->nalloc = newalloc; + return 0; +} + +static void ensure_cstr(lcb_string *str) +{ + str->base[str->nused] = '\0'; +} + +int lcb_string_append(lcb_string *str, const void *data, lcb_size_t size) +{ + if (lcb_string_reserve(str, size)) { + return -1; + } + + memcpy(str->base + str->nused, data, size); + str->nused += size; + ensure_cstr(str); + return 0; +} + +int lcb_string_appendz(lcb_string *str, const char *s) +{ + return lcb_string_append(str, s, strlen(s)); +} + +int lcb_string_appendv(lcb_string *str, ...) +{ + va_list ap; + va_start(ap, str); + + while (1) { + size_t len; + int rc; + const char *ptr; + + if ((ptr = va_arg(ap, const char *)) == NULL) { + return 0; + } + + len = va_arg(ap, size_t); + if (len == (size_t)-1) { + len = strlen(ptr); + } + + rc = lcb_string_append(str, ptr, len); + if (rc != 0) { + return -1; + } + } + return 0; +} + +int lcb_string_rbappend(lcb_string *str, ringbuffer_t *rb, int rbadvance) +{ + lcb_size_t expected, nw; + expected = rb->nbytes; + if (!expected) { + return 0; + } + + if (lcb_string_reserve(str, expected)) { + return -1; + } + + nw = ringbuffer_peek(rb, lcb_string_tail(str), expected); + if (nw != expected) { + return -1; + } + + if (rbadvance) { + ringbuffer_consumed(rb, nw); + } + + lcb_string_added(str, nw); + return 0; +} + +void lcb_string_erase_end(lcb_string *str, lcb_size_t to_remove) +{ + lcb_assert(to_remove <= str->nused); + str->nused -= to_remove; + ensure_cstr(str); +} + +void lcb_string_erase_beginning(lcb_string *str, lcb_size_t to_remove) +{ + lcb_assert(to_remove <= str->nused); + if (!to_remove) { + str->nused = 0; + return; + } + + memmove(str->base, str->base + to_remove, str->nused - to_remove); + str->nused -= to_remove; + ensure_cstr(str); +} + +int lcb_string_insert(lcb_string *str, size_t at, const char *src, size_t nsrc) +{ + if (!nsrc) { + return 0; + } + if (at == str->nused) { + return lcb_string_append(str, src, nsrc); + } + + if (lcb_string_reserve(str, nsrc) != 0) { + return -1; + } + memmove(str->base + at + nsrc, str->base + at, str->nused - at); + memcpy(str->base + at, src, nsrc); + str->nused += nsrc; + ensure_cstr(str); + return 0; +} + +void lcb_string_transfer(lcb_string *from, lcb_string *to) +{ + lcb_assert(to->base == NULL); + *to = *from; + memset(from, 0, sizeof(*from)); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/simplestring.h b/couchbase-sys/libcouchbase-2.7.0/src/simplestring.h new file mode 100644 index 00000000..ee244531 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/simplestring.h @@ -0,0 +1,228 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_STRING_H +#define LCB_STRING_H + +#include +#include + +#include "config.h" +#include "assert.h" +#include "ringbuffer.h" + +/** + * Simple string type. + * + * This structure is designed mainly for ease of use when dealing with actual + * "string" data - i.e. data which must be null-terminated and contiguous. + * + * This won't replace the ringbuffer structure as this string's removal and + * copying operations are comparatively expensive. + * + * Note that all API functions which update the position of the buffer ALSO + * add a trailing NUL byte at the end. + */ +typedef struct lcb_string_st { + /** Buffer that's allocated */ + char *base; + + /** Number of bytes allocated */ + lcb_size_t nalloc; + + /** Number of bytes used */ + lcb_size_t nused; +#ifdef __cplusplus + typedef char *iterator; + typedef const char *const_iterator; + + iterator begin() { return base; }; + iterator end() { return base + nused; } + const_iterator begin() const { return base; } + const_iterator end() const { return base + nused; } + size_t size() const { return nused; } + size_t capacity() const { return nalloc; } + const char *c_str() const { return base; } + + inline void insert(iterator p, const char *first, const char *last); +#endif +} lcb_string; + +#ifdef __cplusplus +extern "C" { +#endif + +int lcb_string_init(lcb_string *str); + +/** + * Free any storage associated with the string. The string's state will be + * empty as if string_init() had just been called. + */ +void lcb_string_release(lcb_string *str); + +/** + * Clear the contents of the string, but don't free the underlying buffer + */ +void lcb_string_clear(lcb_string *str); + + +/** + * Indicate that bytes have been added to the string. This is used in conjunction + * with reserve(). As such, the number of bytes added should not exceed the + * number of bytes passed to reserver. + * + * @param str the string + * @param nbytes the number of bytes added + */ +void lcb_string_added(lcb_string *str, lcb_size_t nbytes); + +/** + * Reserve an amount of free bytes within the string. When this is done, + * up to @c size bytes may be added to the string starting at @c base+str->nbytes + */ +int lcb_string_reserve(lcb_string *str, lcb_size_t size); + + +/** + * Adds data to the string. + * @param str the string to append to + * @param data the data to copy + * @param size the size of data to copy + */ +int lcb_string_append(lcb_string *str, const void *data, lcb_size_t size); + +/** + * Adds a C-style string + * @param str the target lcb_string + * @param zstr a NUL-terminated string to add + */ +int lcb_string_appendz(lcb_string *str, const char *zstr); + + +/** + * Appends a list of pointer-length pairs. Useful if you need to concatenate + * many small buffers + * @param str The string to append to. This should be followed by a list of + * `pointer, size` arguments (where `size` is `size_t`). If the length is + * `-1` then `strlen(pointer)` will be called to determine the length. + * + * A terminal `NULL` should be placed at the end of the argument list + * + * @return 0 if appended, nonzero on memory error + */ +int lcb_string_appendv(lcb_string *str, ...); + +/** + * Adds a string from a ringbuffer structure. This copies the contents + * of the ringbuffer into a string. + * @param str the target string + * @param rb the source ringbuffer + * @param rbadvance whether to advance the ringbuffer's read head + */ +int lcb_string_rbappend(lcb_string *str, ringbuffer_t *rb, int rbadvance); + +/** + * Removes bytes from the end of the string. The resultant string will be + * NUL-terminated + * @param str the string to operate on + * @param to_remove the number of bytes to trim from the end + */ +void lcb_string_erase_end(lcb_string *str, lcb_size_t to_remove); + + +/** + * Removes bytes from the beginning of the string. The resultant string will + * be NUL-terminated. + * @param str the string to operate on + * @param to_remove the number of bytes to remove from the beginning of + * the string. + */ +void lcb_string_erase_beginning(lcb_string *str, lcb_size_t to_remove); + +/** + * Transfers ownership of the underlying buffer contained within the structure + * 'to' to the structure 'from', as such, 'from' becomes a newly initialized + * empty string structure and 'to' contains the existing buffer. + * + * @param from the string which contains the existing buffer + * @param to a new string structure which contains no buffer. It will receive + * from's buffer + */ +void lcb_string_transfer(lcb_string *from, lcb_string *to); + +/** + * Inserts a string at a given position + * @param str the string object + * @param at position at which to insert + * @param src the string to insert + * @param len length of string to insert + */ +int lcb_string_insert(lcb_string *str, size_t at, const char *src, size_t len); + +#define lcb_string_tail(str) ((str)->base + (str)->nused) + +#ifdef __cplusplus +} + +#include +#include +void +lcb_string_st::insert(iterator p, const char *first, const char *last) { + size_t at = p - base; + size_t n = last - first; + if (lcb_string_insert(this, at, first, n) != 0) { + throw std::bad_alloc(); + } +} + +namespace lcb { +class AutoString : public lcb_string_st { +public: + AutoString() { + lcb_string_init(this); + } + ~AutoString() { + lcb_string_release(this); + } + + char *take(size_t& len, size_t& cap) { + char *ret = base; + len = nused; + cap = nalloc; + + base = NULL; + nalloc = 0; + nused = 0; + return ret; + } + char *take(size_t& len) { + size_t dummy; + return take(len, dummy); + } + char *take() { + size_t dummy; + return take(dummy, dummy); + } + +private: + AutoString(lcb_string_st&); + AutoString(const lcb_string_st&); +}; +} + +#endif /** __cplusplus */ +#endif /* LCB_STRING_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/sllist-inl.h b/couchbase-sys/libcouchbase-2.7.0/src/sllist-inl.h new file mode 100644 index 00000000..6b376ee5 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/sllist-inl.h @@ -0,0 +1,197 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sllist.h" +#include +#include +#include +#ifndef INLINE +#ifdef _MSC_VER +#define INLINE __inline +#elif __GNUC__ +#define INLINE __inline__ +#else +#define INLINE inline +#endif /* MSC_VER */ +#endif /* !INLINE */ + + +static INLINE int +sllist_contains(sllist_root *list, sllist_node *item) +{ + sllist_node *ll; + SLLIST_FOREACH(list, ll) { + if (item == ll) { + return 1; + } + } + return 0; +} + +static INLINE unsigned +sllist_get_size(sllist_root *list) +{ + unsigned ret = 0; + sllist_node *ll; + SLLIST_FOREACH(list, ll) { + ret++; + } + return ret; +} + +/* #define SLLIST_DEBUG */ + +#ifdef SLLIST_DEBUG +#define slist_sanity_insert(l, n) assert(!slist_contains(l, n)) +#else +#define slist_sanity_insert(l, n) +#endif + +static INLINE void +slist_iter_init_at(sllist_node *node, sllist_iterator *iter) +{ + iter->cur = node->next; + iter->prev = node; + iter->removed = 0; + + if (iter->cur) { + iter->next = iter->cur->next; + } else { + iter->next = NULL; + } +} + +static INLINE void +slist_iter_init(sllist_root *list, sllist_iterator *iter) +{ + slist_iter_init_at(&list->first_prev, iter); +} + +static INLINE void +slist_iter_incr(sllist_root *list, sllist_iterator *iter) +{ + if (!iter->removed) { + iter->prev = iter->prev->next; + } else { + iter->removed = 0; + } + + if ((iter->cur = iter->next)) { + iter->next = iter->cur->next; + } else { + iter->next = NULL; + } + + assert(iter->cur != iter->prev); + + (void)list; +} + +static INLINE void +sllist_iter_remove(sllist_root *list, sllist_iterator *iter) +{ + iter->prev->next = iter->next; + + /** GCC strict aliasing. Yay. */ + if (iter->prev->next == NULL && iter->prev == &list->first_prev) { + list->last = NULL; + } else if (iter->cur == list->last && iter->next == NULL) { + /* removing the last item */ + list->last = iter->prev; + } + iter->removed = 1; +} + +static INLINE void +sllist_remove_head(sllist_root *list) +{ + if (!SLLIST_FIRST(list)) { + return; + } + + SLLIST_FIRST(list) = SLLIST_FIRST(list)->next; + + if (!SLLIST_FIRST(list)) { + list->last = NULL; + } +} + +static INLINE void +sllist_remove(sllist_root *list, sllist_node *item) +{ + sllist_iterator iter; + SLLIST_ITERFOR(list, &iter) { + if (iter.cur == item) { + sllist_iter_remove(list, &iter); + return; + } + } + fprintf(stderr, "SLLIST: Requested to remove item %p which is not in %p\n", (void*)list, (void*)item); + assert(0); +} + +static INLINE void +sllist_append(sllist_root *list, sllist_node *item) +{ + if (SLLIST_IS_EMPTY(list)) { + SLLIST_FIRST(list) = list->last = item; + item->next = NULL; + } else { + slist_sanity_insert(list, item); + list->last->next = item; + list->last = item; + } + item->next = NULL; +} + +static INLINE void +sllist_prepend(sllist_root *list, sllist_node *item) +{ + if (SLLIST_IS_EMPTY(list)) { + SLLIST_FIRST(list) = list->last = item; + } else { + slist_sanity_insert(list, item); + item->next = SLLIST_FIRST(list); + SLLIST_FIRST(list) = item; + } +} + +static void +sllist_insert(sllist_root *list, sllist_node *prev, sllist_node *item) +{ + item->next = prev->next; + prev->next = item; + if (item->next == NULL) { + list->last = item; + } +} + +static INLINE void +sllist_insert_sorted(sllist_root *list, sllist_node *item, + int (*compar)(sllist_node*, sllist_node*)) +{ + sllist_iterator iter; + SLLIST_ITERFOR(list, &iter) { + int rv = compar(item, iter.cur); + /** if the item we have is before the current, prepend it here */ + if (rv <= 0) { + sllist_insert(list, iter.prev, item); + return; + } + } + sllist_append(list, item); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/sllist.h b/couchbase-sys/libcouchbase-2.7.0/src/sllist.h new file mode 100644 index 00000000..95885340 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/sllist.h @@ -0,0 +1,76 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_SLIST_H +#define LCB_SLIST_H + +struct slist_node_st; +typedef struct slist_node_st { + struct slist_node_st *next; +} sllist_node; + +typedef struct { + sllist_node first_prev; + sllist_node *last; +} sllist_root; + +/** + * Indicates whether the list is empty or not + */ +#define SLLIST_FIRST(list) (list)->first_prev.next +#define SLLIST_LAST(list) (list)->last + +#define SLLIST_IS_EMPTY(list) (SLLIST_LAST(list) == NULL) +#define SLLIST_IS_ONE(list) (SLLIST_FIRST(list) && SLLIST_FIRST(list) == SLLIST_LAST(list)) + +/** + * Iterator for list. This can be used as the 'for' statement; as such this + * macro should look like such: + * + * slist_node *ii; + * SLIST_FOREACH(list, ii) { + * my_item *item = LCB_LIST_ITEM(my_item, ii, slnode); + * } + * + * @param list the list to iterate + * @param pos a local variable to use as the iterator + */ +#define SLLIST_FOREACH(list, pos) \ + for (pos = SLLIST_FIRST(list); pos; pos = pos->next) + + +typedef struct sllist_iterator_st { + sllist_node *cur; + sllist_node *prev; + sllist_node *next; + int removed; +} sllist_iterator; + +#define sllist_iter_end(list, iter) ((iter)->cur == NULL) + +#define SLLIST_ITEM(ptr, type, member) \ + ((type *) (void *) ((char *)(ptr) - offsetof(type, member))) + +#define SLLIST_ITERFOR(list, iter) \ + for (slist_iter_init(list, iter); \ + !sllist_iter_end(list, iter); \ + slist_iter_incr(list, iter)) + +#define SLLIST_ITERBASIC(list, elem) \ + for (elem = SLLIST_FIRST(list); elem; elem = elem->next) + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/ssl/CMakeLists.txt b/couchbase-sys/libcouchbase-2.7.0/src/ssl/CMakeLists.txt new file mode 100644 index 00000000..b55a0e4c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/ssl/CMakeLists.txt @@ -0,0 +1,23 @@ +IF(APPLE) + IF(NOT OpenSSL_DIR) + EXECUTE_PROCESS(COMMAND brew --prefix openssl + OUTPUT_VARIABLE OPENSSL_ROOT_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE) + MESSAGE(STATUS "Found OpenSSL Prefix: ${OPENSSL_ROOT_DIR}") + ENDIF() +ENDIF() + +FIND_PACKAGE(OpenSSL) +IF(OPENSSL_FOUND AND (NOT LCB_NO_SSL)) + INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR}) + ADD_DEFINITIONS(${OPENSSL_DEFINITIONS}) + ADD_LIBRARY(lcbssl OBJECT ssl_e.c ssl_c.c ssl_common.c) + SET(lcb_ssl_libs ${OPENSSL_LIBRARIES} PARENT_SCOPE) + SET(lcb_ssl_objs $ PARENT_SCOPE) + LCB_UTIL(lcbssl) + MESSAGE(STATUS "SSL Found") +ELSE() + SET(LCB_NO_SSL ON PARENT_SCOPE) + SET(lcb_ssl_libs "" PARENT_SCOPE) + MESSAGE(STATUS "SSL Not Found") +ENDIF() diff --git a/couchbase-sys/libcouchbase-2.7.0/src/ssl/ssl_c.c b/couchbase-sys/libcouchbase-2.7.0/src/ssl/ssl_c.c new file mode 100644 index 00000000..b439757c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/ssl/ssl_c.c @@ -0,0 +1,415 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ssl_iot_common.h" +#include "sllist.h" +#include "sllist-inl.h" + +/* throw-away write buffer structure (for encoded data) */ +typedef struct { + void *parent; + char buf[1]; +} my_WBUF; + +/* throw-away write buffer structure (for application data) */ +typedef struct { + sllist_node slnode; + lcb_ioC_write2_callback cb; + void *uarg; + void *iovroot_; + lcb_IOV *iov; + lcb_size_t niov; +} my_WCTX; + +typedef struct { + IOTSSL_COMMON_FIELDS + lcb_sockdata_t *sd; /**< Socket pointer */ + lcbio_pTIMER as_read; /**< For callbacks when SSL_pending > 0 */ + lcbio_pTIMER as_write; /**< For callbacks when SSL_writes succeeds */ + lcb_IOV urd_iov; /**< User-defined buffer to read in applicataion data */ + void *urd_arg; /**< User-defined argument for read callback */ + my_WCTX *wctx_cached; + lcb_ioC_read2_callback urd_cb; /**< User defined read callback */ + sllist_root writes; /**< List of pending user writes */ + + /** + * Whether a current read request is active. This read request refers to + * this module reading raw data from the actual underlying socket. The + * presence of a user-level (i.e. lcbio-invoked) read request is determined + * by the presence of a non-NULL urd_cb value + */ + int rdactive; + + int closed; /**< Pending delivery of close */ + int entered; +} lcbio_CSSL; + +#define CS_FROM_IOPS(iops) (lcbio_CSSL *)IOTSSL_FROM_IOPS(iops) +#define SCHEDULE_WANT_SAFE(cs) if (!(cs)->entered) { schedule_wants(cs); } + +static void appdata_encode(lcbio_CSSL *); +static void appdata_free_flushed(lcbio_CSSL *); +static void appdata_read(lcbio_CSSL *); +static void schedule_wants(lcbio_CSSL *cs); +static int maybe_set_error(lcbio_CSSL *cs, int rv) +{ + return iotssl_maybe_error((lcbio_XSSL *)cs, rv); +} + +/* This function goes through all the pending copies of data that was scheduled + * for write and where the current IOV position is at the end (or niov==0). + * For each of those routines this function will invoke its write callback + */ +static void +appdata_free_flushed(lcbio_CSSL *cs) +{ + sllist_iterator iter; + SLLIST_ITERFOR(&cs->writes, &iter) { + my_WCTX *cur = SLLIST_ITEM(iter.cur, my_WCTX, slnode); + if (cur->niov && cs->error == 0) { + break; + } + /* invoke the callback */ + cur->cb(cs->sd, cs->error?-1:0, cur->uarg); + sllist_iter_remove(&cs->writes, &iter); + free(cur->iovroot_); + if (cs->wctx_cached) { + free(cur); + } else { + cs->wctx_cached = cur; + } + } +} + +/* This function will attempt to encode pending user data into SSL data. This + * will be output to the wbio. */ +static void +appdata_encode(lcbio_CSSL *cs) +{ + sllist_node *cur; + + /* each element here represents a used-defined write buffer */ + SLLIST_FOREACH(&cs->writes, cur) { + my_WCTX *ctx = SLLIST_ITEM(cur, my_WCTX, slnode); + + for (; ctx->niov && cs->error == 0; ctx->niov--, ctx->iov++) { + int rv; + + assert(ctx->iov->iov_len); + rv = SSL_write(cs->ssl, ctx->iov->iov_base, ctx->iov->iov_len); + if (rv > 0) { + continue; + } else if (maybe_set_error(cs, rv) == 0) { + /* SSL_ERROR_WANT_READ. Should schedule a read here. + * XXX: Note that this buffer will not be returned to the user + * until the _next_ time the appdata_free_flushed function is + * invoked; the call chain for appdata_free_flushed is like this: + * + * start_write2 => async_schedule(async_write) => appdata_free_flushed. + * OR + * start_write2 => write_callback => appdata_free_flushed + */ + SCHEDULE_WANT_SAFE(cs) + return; + } else { + IOTSSL_ERRNO(cs) = EINVAL; + } + } + } +} + +static void +async_write(void *arg) +{ + lcbio_CSSL *cs = arg; + appdata_encode(cs); + appdata_free_flushed(cs); +} + +/* Called when SSL data has been written to the socket */ +static void +write_callback(lcb_sockdata_t *sd, int status, void *arg) +{ + my_WBUF *wb = arg; + lcbio_CSSL *cs = wb->parent; + + if (status) { + IOTSSL_ERRNO(cs) = IOT_ERRNO(cs->orig); + cs->error = 1; + } + + free(wb); + + appdata_free_flushed(cs); + lcbio_table_unref(&cs->base_); + (void) sd; +} + +/* Read application data from SSL's rbio buffer. Invokes the user callback + * for the current read operation if there is data */ +static void +appdata_read(lcbio_CSSL *cs) +{ + /* either an error or an actual read event */ + int nr; + lcb_ioC_read2_callback cb = cs->urd_cb; + if (!cb) { + return; + } + assert(!cs->rdactive); + nr = SSL_read(cs->ssl, cs->urd_iov.iov_base, cs->urd_iov.iov_len); + if (nr > 0) { + /* nothing */ + } else if (cs->closed || nr == 0) { + nr = 0; + } else if (maybe_set_error(cs, nr) == 0) { + return; + } + + cs->urd_cb = NULL; + cb(cs->sd, nr, cs->urd_arg); +} + +/* Invoked when SSL data has been read from the socket */ +static void +read_callback(lcb_sockdata_t *sd, lcb_ssize_t nr, void *arg) +{ + lcbio_CSSL *cs = arg; + cs->rdactive = 0; + cs->entered++; + + if (nr > 0) { + BUF_MEM *mb; + + BIO_clear_retry_flags(cs->rbio); + BIO_get_mem_ptr(cs->rbio, &mb); + mb->length += nr; + + } else if (nr == 0) { + cs->closed = 1; + cs->error = 1; + + } else { + cs->error = 1; + IOTSSL_ERRNO(cs) = IOT_ERRNO(cs->orig); + } + + appdata_encode(cs); + appdata_read(cs); + + cs->entered--; + schedule_wants(cs); + lcbio_table_unref(&cs->base_); + (void) sd; +} + + +/* This function schedules any I/O on the actual socket. It writes encoded + * data and requests to read decoded data */ +static void +schedule_wants(lcbio_CSSL *cs) +{ + size_t npend = BIO_ctrl_pending(cs->wbio); + char dummy; + + int has_appdata = 0; + + if (SSL_peek(cs->ssl, &dummy, 1) == 1) { + has_appdata = 1; + } + + if (npend) { + /* Have pending data to write. The buffer is copied here because the + * BIO structure doesn't support "lockdown" semantics like netbuf/rdb + * do. We might transplant this with a different sort of BIO eventually.. + */ + my_WBUF *wb = malloc(sizeof(*wb) + npend); + lcb_IOV iov; + BIO_read(cs->wbio, wb->buf, npend); + iov.iov_base = wb->buf; + iov.iov_len = npend; + wb->parent = cs; + + /* Increment the reference count. This is decremented when we get back + * the callback. The goal is that a pending internal SSL_write() should + * keep the object alive despite the user having called lcbio_table_unref() + * on us. + */ + lcbio_table_ref(&cs->base_); + IOT_V1(cs->orig).write2( + IOT_ARG(cs->orig), cs->sd, &iov, 1, wb, write_callback); + } + + /* Only schedule additional reads if we're not already in the process of a + * read */ + + if (cs->rdactive == 0) { + if (cs->error) { + /* This can happen if we got an SSL error in performing something + * within this callback. + * + * In this case, just signal "as-if" a read happened. appdata_read + * will do the right thing if there is no read callback, and will + * return an error if SSL_read() fails (which it should). + */ + lcbio_async_signal(cs->as_read); + + } else if (SSL_want_read(cs->ssl) || (cs->urd_cb && has_appdata == 0)) { + /* request more data from the socket */ + BUF_MEM *mb; + lcb_IOV iov; + + cs->rdactive = 1; + BIO_get_mem_ptr(cs->rbio, &mb); + iotssl_bm_reserve(mb); + iov.iov_base = mb->data + mb->length; + iov.iov_len = mb->max - mb->length; + lcbio_table_ref(&cs->base_); + IOT_V1(cs->orig).read2( + IOT_ARG(cs->orig), cs->sd, &iov, 1, cs, read_callback); + } + + } +} + +static int +Cssl_read2(lcb_io_opt_t iops, lcb_sockdata_t *sd, lcb_IOV *iov, lcb_size_t niov, + void *uarg, lcb_ioC_read2_callback callback) +{ + lcbio_CSSL *cs = CS_FROM_IOPS(iops); + cs->urd_iov = *iov; + cs->urd_arg = uarg; + cs->urd_cb = callback; + + IOTSSL_PENDING_PRECHECK(cs->ssl); + if (IOTSSL_IS_PENDING(cs->ssl)) { + /* have data to be read. Fast path here */ + lcbio_async_signal(cs->as_read); + } else { + SCHEDULE_WANT_SAFE(cs); + } + + (void) niov; (void) sd; + return 0; +} + +static int +Cssl_write2(lcb_io_opt_t io, lcb_sockdata_t *sd, lcb_IOV *iov, lcb_size_t niov, + void *uarg, lcb_ioC_write2_callback callback) +{ + lcbio_CSSL *cs = CS_FROM_IOPS(io); + my_WCTX *wc; + + /* We keep one of these cached inside the cs structure so we don't have + * to make a new malloc for each write */ + if (cs->wctx_cached) { + wc = cs->wctx_cached; + cs->wctx_cached = NULL; + memset(wc, 0, sizeof *wc); + } else { + wc = calloc(1, sizeof(*wc)); + } + + /* assign the common parameters */ + wc->uarg = uarg; + wc->cb = callback; + + /* If the socket does not have a pending error and there are no other + * writes before this, then try to write the current buffer immediately. */ + if (cs->error == 0 && SLLIST_IS_EMPTY(&cs->writes)) { + unsigned ii; + for (ii = 0; ii < niov; ++ii) { + int rv = SSL_write(cs->ssl, iov->iov_base, iov->iov_len); + if (rv > 0) { + iov++; + niov--; + } else { + maybe_set_error(cs, rv); + break; + } + } + } + + /* We add this now in order for the SLLIST_IS_EMPTY to be false before, if + * no other items were pending */ + sllist_append(&cs->writes, &wc->slnode); + + /* If we have some IOVs remaining then it means we couldn't write all the + * data. If so, reschedule and place in the queue for later */ + if (niov && cs->error == 0) { + wc->niov = niov; + wc->iov = malloc(sizeof (*iov) * wc->niov); + wc->iovroot_ = wc->iov; + memcpy(wc->iov, iov, sizeof (*iov) * niov); + /* This function will try to schedule the proper events. We need at least + * one SSL_write() in order to advance the state machine. In the future + * we could determine if we performed a previous SSL_write above */ + appdata_encode(cs); + } + + /* In most cases we will want to deliver the "flushed" notification */ + lcbio_async_signal(cs->as_write); + (void) sd; + return 0; +} + +static unsigned +Cssl_close(lcb_io_opt_t io, lcb_sockdata_t *sd) +{ + lcbio_CSSL *cs = CS_FROM_IOPS(io); + IOT_V1(cs->orig).close(IOT_ARG(cs->orig), sd); + cs->error = 1; + if (!SLLIST_IS_EMPTY(&cs->writes)) { + /* It is possible that a prior call to SSL_write returned an SSL_want_read + * and the next subsequent call to the underlying read API returned an + * error. For this reason we signal to the as_write function (which + * then calls the appdata_free_flushed function) in case we have such + * leftover data. + */ + lcbio_async_signal(cs->as_write); + } + return 0; +} + +static void +Cssl_dtor(void *arg) +{ + lcbio_CSSL *cs = arg; + assert(SLLIST_IS_EMPTY(&cs->writes)); + lcbio_timer_destroy(cs->as_read); + lcbio_timer_destroy(cs->as_write); + iotssl_destroy_common((lcbio_XSSL *)cs); + free(cs->wctx_cached); + free(arg); +} + +lcbio_pTABLE +lcbio_Cssl_new(lcbio_pTABLE orig, lcb_sockdata_t *sd, SSL_CTX *sctx) +{ + lcbio_CSSL *ret = calloc(1, sizeof(*ret)); + lcbio_pTABLE iot = &ret->base_; + ret->sd = sd; + ret->as_read = lcbio_timer_new(orig, ret, (void (*)(void*))appdata_read); + ret->as_write = lcbio_timer_new(orig, ret, async_write); + ret->base_.dtor = Cssl_dtor; + + iot->u_io.completion.read2 = Cssl_read2; + iot->u_io.completion.write2 = Cssl_write2; + iot->u_io.completion.close = Cssl_close; + iotssl_init_common((lcbio_XSSL *)ret, orig, sctx); + return iot; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/ssl/ssl_common.c b/couchbase-sys/libcouchbase-2.7.0/src/ssl/ssl_common.c new file mode 100644 index 00000000..3a5327b1 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/ssl/ssl_common.c @@ -0,0 +1,454 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This file contains the common bucket of routines necessary for interfacing + * with OpenSSL. + */ +#include "ssl_iot_common.h" +#include "settings.h" +#include "logging.h" +#include + +#define LOGARGS(ssl, lvl) \ + ((lcbio_SOCKET*)SSL_get_app_data(ssl))->settings, "SSL", lvl, __FILE__, __LINE__ +static char *global_event = "dummy event for ssl"; + +/****************************************************************************** + ****************************************************************************** + ** Boilerplate lcbio_TABLE Wrappers ** + ****************************************************************************** + ******************************************************************************/ +static void loop_run(lcb_io_opt_t io) { + lcbio_XSSL *xs = IOTSSL_FROM_IOPS(io); + IOT_START(xs->orig); +} +static void loop_stop(lcb_io_opt_t io) { + lcbio_XSSL *xs = IOTSSL_FROM_IOPS(io); + IOT_STOP(xs->orig); +} +static void *create_event(lcb_io_opt_t io) { + (void)io; + return global_event; +} +static void destroy_event(lcb_io_opt_t io, void *event) { + (void) io; (void) event; +} +static void *create_timer(lcb_io_opt_t io) { + lcbio_XSSL *xs = IOTSSL_FROM_IOPS(io); + return xs->orig->timer.create(IOT_ARG(xs->orig)); +} +static int schedule_timer(lcb_io_opt_t io, void *timer, lcb_uint32_t us, + void *arg, lcb_ioE_callback callback) { + lcbio_XSSL *xs = IOTSSL_FROM_IOPS(io); + return xs->orig->timer.schedule(IOT_ARG(xs->orig), timer, us, arg, callback); +} +static void destroy_timer(lcb_io_opt_t io, void *timer) { + lcbio_XSSL *xs = IOTSSL_FROM_IOPS(io); + xs->orig->timer.destroy(IOT_ARG(xs->orig), timer); +} +static void cancel_timer(lcb_io_opt_t io, void *timer) { + lcbio_XSSL *xs = IOTSSL_FROM_IOPS(io); + xs->orig->timer.cancel(IOT_ARG(xs->orig), timer); +} +static int Eis_closed(lcb_io_opt_t io, lcb_socket_t sock, int flags) { + lcbio_XSSL *xs = IOTSSL_FROM_IOPS(io); + return xs->orig->u_io.v0.io.is_closed(IOT_ARG(xs->orig), sock, flags); +} +static int Cis_closed(lcb_io_opt_t io, lcb_sockdata_t *sd, int flags) { + lcbio_XSSL *xs = IOTSSL_FROM_IOPS(io); + return xs->orig->u_io.completion.is_closed(IOT_ARG(xs->orig), sd, flags); +} + +/****************************************************************************** + ****************************************************************************** + ** Common Routines for lcbio_TABLE Emulation ** + ****************************************************************************** + ******************************************************************************/ +void +iotssl_init_common(lcbio_XSSL *xs, lcbio_TABLE *orig, SSL_CTX *sctx) +{ + lcbio_TABLE *base = &xs->base_; + xs->iops_dummy_ = calloc(1, sizeof(*xs->iops_dummy_)); + xs->iops_dummy_->v.v0.cookie = xs; + xs->orig = orig; + base->model = xs->orig->model; + base->p = xs->iops_dummy_; + base->refcount = 1; + base->loop.start = loop_run; + base->loop.stop = loop_stop; + base->timer.create = create_timer; + base->timer.destroy = destroy_timer; + base->timer.schedule = schedule_timer; + base->timer.cancel = cancel_timer; + + if (orig->model == LCB_IOMODEL_EVENT) { + base->u_io.v0.ev.create = create_event; + base->u_io.v0.ev.destroy = destroy_event; + base->u_io.v0.io.is_closed = Eis_closed; + } else { + base->u_io.completion.is_closed = Cis_closed; + } + + lcbio_table_ref(xs->orig); + + xs->error = 0; + xs->ssl = SSL_new(sctx); + + xs->rbio = BIO_new(BIO_s_mem()); + xs->wbio = BIO_new(BIO_s_mem()); + + SSL_set_bio(xs->ssl, xs->rbio, xs->wbio); + SSL_set_read_ahead(xs->ssl, 0); + + /* Indicate that we are a client */ + SSL_set_connect_state(xs->ssl); +} + +void +iotssl_destroy_common(lcbio_XSSL *xs) +{ + free(xs->iops_dummy_); + SSL_free(xs->ssl); + lcbio_table_unref(xs->orig); +} + +void +iotssl_bm_reserve(BUF_MEM *bm) +{ + int oldlen; + oldlen = bm->length; + while (bm->max - bm->length < 4096) { + /* there's also a BUF_MEM_grow_clean() but that actually clears the + * used portion of the buffer */ + BUF_MEM_grow(bm, bm->max + 4096); + } + bm->length = oldlen; +} + +void +iotssl_log_errors(lcbio_XSSL *xs) +{ + unsigned long curerr; + while ((curerr = ERR_get_error())) { + char errbuf[4096]; + ERR_error_string_n(curerr, errbuf, sizeof errbuf); + lcb_log(LOGARGS(xs->ssl, LCB_LOG_ERROR), "%s", errbuf); + + if (xs->errcode != LCB_SUCCESS) { + continue; /* Already set */ + } + + if (ERR_GET_LIB(curerr) == ERR_LIB_SSL) { + switch (ERR_GET_REASON(curerr)) { + case SSL_R_CERTIFICATE_VERIFY_FAILED: + case SSL_R_MISSING_VERIFY_MESSAGE: + xs->errcode = LCB_SSL_CANTVERIFY; + break; + + case SSL_R_BAD_PROTOCOL_VERSION_NUMBER: + case SSL_R_UNKNOWN_PROTOCOL: + case SSL_R_WRONG_VERSION_NUMBER: + case SSL_R_UNKNOWN_SSL_VERSION: + case SSL_R_UNSUPPORTED_SSL_VERSION: + xs->errcode = LCB_PROTOCOL_ERROR; + break; + default: + xs->errcode = LCB_SSL_ERROR; + } + } + } +} + +static void +log_global_errors(lcb_settings *settings) +{ + unsigned long curerr; + while ((curerr = ERR_get_error())) { + char errbuf[4096]; + ERR_error_string_n(curerr, errbuf, sizeof errbuf); + lcb_log(settings, "SSL", LCB_LOG_ERROR, __FILE__, __LINE__, "SSL Error: %ld, %s", curerr, errbuf); + } +} + +int +iotssl_maybe_error(lcbio_XSSL *xs, int rv) +{ + assert(rv < 1); + if (rv == -1) { + int err = SSL_get_error(xs->ssl, rv); + if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { + /* this is ok. */ + return 0; + } + } + iotssl_log_errors(xs); + return -1; +} + +/****************************************************************************** + ****************************************************************************** + ** Higher Level SSL_CTX Wrappers ** + ****************************************************************************** + ******************************************************************************/ +static void +log_callback(const SSL *ssl, int where, int ret) +{ + const char *retstr = ""; + int should_log = 0; + lcbio_SOCKET *sock = SSL_get_app_data(ssl); + /* Ignore low-level SSL stuff */ + + if (where & SSL_CB_ALERT) { + should_log = 1; + } + if (where == SSL_CB_HANDSHAKE_START || where == SSL_CB_HANDSHAKE_DONE) { + should_log = 1; + } + if ((where & SSL_CB_EXIT) && ret == 0) { + should_log = 1; + } + + if (!should_log) { + return; + } + + retstr = SSL_alert_type_string(ret); + lcb_log(LOGARGS(ssl, LCB_LOG_TRACE), "sock=%p: ST(0x%x). %s. R(0x%x)%s", + (void*)sock, where, SSL_state_string_long(ssl), ret, retstr); + + if (where == SSL_CB_HANDSHAKE_DONE) { + lcb_log(LOGARGS(ssl, LCB_LOG_DEBUG), "sock=%p. Using SSL version %s. Cipher=%s", (void*)sock, SSL_get_version(ssl), SSL_get_cipher_name(ssl)); + } +} + +#if 0 +static void +msg_callback(int write_p, int version, int ctype, const void *buf, size_t n, + SSL *ssl, void *arg) +{ + printf("Got message (%s). V=0x%x. T=%d. N=%lu\n", + write_p ? ">" : "<", version, ctype, n); + (void)ssl; (void)arg; (void)buf; +} +#endif + +struct lcbio_SSLCTX { + SSL_CTX *ctx; +}; + +lcbio_pSSLCTX +lcbio_ssl_new(const char *cafile, int noverify, lcb_error_t *errp, + lcb_settings *settings) +{ + lcb_error_t err_s; + lcbio_pSSLCTX ret; + + if (!errp) { + errp = &err_s; + } + + ret = calloc(1, sizeof(*ret)); + if (!ret) { + *errp = LCB_CLIENT_ENOMEM; + goto GT_ERR; + } + ret->ctx = SSL_CTX_new(SSLv23_client_method()); + if (!ret->ctx) { + *errp = LCB_SSL_ERROR; + goto GT_ERR; + + } + SSL_CTX_set_cipher_list(ret->ctx, "DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:AES256-SHA:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DES-CBC3-SHA:DES-CBC3-MD5:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:AES128-SHA:DHE-RSA-SEED-SHA:DHE-DSS-SEED-SHA:SEED-SHA:RC2-CBC-MD5:RC4-SHA:RC4-MD5:RC4-MD5:EDH-RSA-DES-CBC-SHA:EDH-DSS-DES-CBC-SHA:DES-CBC-SHA:DES-CBC-MD5:EXP-EDH-RSA-DES-CBC-SHA:EXP-EDH-DSS-DES-CBC-SHA:EXP-DES-CBC-SHA:EXP-RC2-CBC-MD5:EXP-RC2-CBC-MD5:EXP-RC4-MD5:EXP-RC4-MD5"); +// SSL_CTX_set_cipher_list(ret->ctx, "!NULL"); + + if (cafile) { + if (!SSL_CTX_load_verify_locations(ret->ctx, cafile, NULL)) { + *errp = LCB_SSL_ERROR; + goto GT_ERR; + } + } + + if (noverify) { + SSL_CTX_set_verify(ret->ctx, SSL_VERIFY_NONE, NULL); + } else { + SSL_CTX_set_verify(ret->ctx, SSL_VERIFY_PEER, NULL); + } + + SSL_CTX_set_info_callback(ret->ctx, log_callback); + #if 0 + SSL_CTX_set_msg_callback(ret->ctx, msg_callback); + #endif + + /* this will allow us to do SSL_write and use a different buffer if the + * first one fails. This is helpful in the scenario where an initial + * SSL_write() returns an SSL_ERROR_WANT_READ in the ssl_e.c plugin. In + * such a scenario the correct behavior is to return EWOULDBLOCK. However + * we have no guarantee that the next time we get a write request we would + * be using the same buffer. + */ + SSL_CTX_set_mode(ret->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + SSL_CTX_set_options(ret->ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3); + return ret; + + GT_ERR: + log_global_errors(settings); + if (ret) { + if (ret->ctx) { + SSL_CTX_free(ret->ctx); + } + free(ret); + } + return NULL; +} + +static void +noop_dtor(lcbio_PROTOCTX *arg) { + free(arg); +} + +lcb_error_t +lcbio_ssl_apply(lcbio_SOCKET *sock, lcbio_pSSLCTX sctx) +{ + lcbio_pTABLE old_iot = sock->io, new_iot; + lcbio_PROTOCTX *sproto; + + if (old_iot->model == LCB_IOMODEL_EVENT) { + new_iot = lcbio_Essl_new(old_iot, sock->u.fd, sctx->ctx); + } else { + new_iot = lcbio_Cssl_new(old_iot, sock->u.sd, sctx->ctx); + } + + if (new_iot) { + sproto = calloc(1, sizeof(*sproto)); + sproto->id = LCBIO_PROTOCTX_SSL; + sproto->dtor = noop_dtor; + lcbio_protoctx_add(sock, sproto); + lcbio_table_unref(old_iot); + sock->io = new_iot; + /* just for logging */ + SSL_set_app_data(((lcbio_XSSL *)new_iot)->ssl, sock); + return LCB_SUCCESS; + + } else { + return LCB_ERROR; + } +} + +int +lcbio_ssl_check(lcbio_SOCKET *sock) +{ + return lcbio_protoctx_get(sock, LCBIO_PROTOCTX_SSL) != NULL; +} + +lcb_error_t +lcbio_ssl_get_error(lcbio_SOCKET *sock) +{ + lcbio_XSSL *xs = (lcbio_XSSL *)sock->io; + return xs->errcode; +} + +void +lcbio_ssl_free(lcbio_pSSLCTX ctx) +{ + SSL_CTX_free(ctx->ctx); + free(ctx); +} + + +/** + * According to https://www.openssl.org/docs/crypto/threads.html we need + * to install two functions for locking support, a function that returns + * a thread ID, and a function which performs locking/unlocking. However later + * on in the link it says it will select a default implementation to return + * the thread ID, and thus we only need supply the locking function. + */ +#if defined(_POSIX_THREADS) +#include +typedef pthread_mutex_t ossl_LOCKTYPE; +static void ossl_lock_init(ossl_LOCKTYPE *l) { pthread_mutex_init(l, NULL); } +static void ossl_lock_acquire(ossl_LOCKTYPE *l) { pthread_mutex_lock(l); } +static void ossl_lock_release(ossl_LOCKTYPE *l) { pthread_mutex_unlock(l); } +#elif defined(_WIN32) +#include +typedef CRITICAL_SECTION ossl_LOCKTYPE; +static void ossl_lock_init(ossl_LOCKTYPE *l) { InitializeCriticalSection(l); } +static void ossl_lock_acquire(ossl_LOCKTYPE *l) { EnterCriticalSection(l); } +static void ossl_lock_release(ossl_LOCKTYPE *l) { LeaveCriticalSection(l); } +#else +typedef char ossl_LOCKTYPE; +#define ossl_lock_init(l) +#define ossl_lock_acquire(l) +#define ossl_lock_release(l) +#endif + + +static ossl_LOCKTYPE *ossl_locks; +static void +ossl_lockfn(int mode, int lkid, const char *f, int line) +{ + ossl_LOCKTYPE *l = ossl_locks + lkid; + + if (mode & CRYPTO_LOCK) { + ossl_lock_acquire(l); + } else { + ossl_lock_release(l); + } + + (void)f; + (void)line; +} + +static void +ossl_init_locks(void) +{ + unsigned ii, nlocks; + if (CRYPTO_get_locking_callback() != NULL) { + /* Someone already set the callback before us. Don't destroy it! */ + return; + } + nlocks = CRYPTO_num_locks(); + ossl_locks = malloc(sizeof(*ossl_locks) * nlocks); + for (ii = 0; ii < nlocks; ii++) { + ossl_lock_init(ossl_locks + ii); + } + CRYPTO_set_locking_callback(ossl_lockfn); +} + +static volatile int ossl_initialized = 0; +void lcbio_ssl_global_init(void) +{ + if (ossl_initialized) { + return; + } + ossl_initialized = 1; + SSL_library_init(); + SSL_load_error_strings(); + ossl_init_locks(); +} + +lcb_error_t +lcbio_sslify_if_needed(lcbio_SOCKET *sock, lcb_settings *settings) +{ + if (!(settings->sslopts & LCB_SSL_ENABLED)) { + return LCB_SUCCESS; /*not needed*/ + } + if (lcbio_ssl_check(sock)) { + return LCB_SUCCESS; /*already ssl*/ + } + return lcbio_ssl_apply(sock, settings->ssl_ctx); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/ssl/ssl_e.c b/couchbase-sys/libcouchbase-2.7.0/src/ssl/ssl_e.c new file mode 100644 index 00000000..044df4d1 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/ssl/ssl_e.c @@ -0,0 +1,408 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ssl_iot_common.h" +#include "simplestring.h" +#include +/** + * Event-Style SSL Wrapping. + * + * This wraps the IO Table for SSL I/O + * + * + * Writes and reads will always use SSL_write and SSL_read methods respectively. + * I/O will be preemptively scheduled whenever: + * + * - SSL_want_read() is true + * - The wbio is not empty + */ + +typedef struct { + IOTSSL_COMMON_FIELDS + void *event; /**< Event pointer (parent->create_event) */ + void *arg; /**< Argument to pass for user-defined callback */ + short requested; /**< User defined event flags */ + short fakewhich; /**< Flags to deliver immediately */ + lcb_ioE_callback ucb; /**< User defined callback */ + int entered; /**< Whether we are inside a handler */ + int closed; + lcb_socket_t fd; /**< Socket descriptor */ + lcbio_pTIMER as_fake; + lcb_SIZE last_nw; /**< Last failed call to SSL_write() */ +} lcbio_ESSL; + +#ifdef USE_EAGAIN +#define C_EAGAIN EWOULDBLOCK: case EAGAIN +#else +#define C_EAGAIN EWOULDBLOCK +#endif + +#define ES_FROM_IOPS(iops) (lcbio_ESSL *)(IOTSSL_FROM_IOPS(iops)) +#define MINIMUM(a,b) a < b ? a : b + +static int maybe_error(lcbio_ESSL *es, int rv) { + return iotssl_maybe_error((lcbio_XSSL *)es, rv); +} + +static void event_handler(lcb_socket_t, short, void *); + +#define SCHEDULE_PENDING_SAFE(es) if (!(es)->entered) { schedule_pending(es); } +/* Schedule watch events: + * - If we have SSL data to be written, the watcher is activated for WRITE_EVENT + * - If SSL_pending() is true and the user has requested read, the as_fake + * handler is triggered + */ +static void +schedule_pending(lcbio_ESSL *es) +{ + short avail = LCB_WRITE_EVENT; + /* Bitflags of events that the SSL pointer needs in order to progress */ + short wanted = 0; + + IOTSSL_PENDING_PRECHECK(es->ssl); + if (IOTSSL_IS_PENDING(es->ssl)) { + /* have user data in buffer */ + avail |= LCB_READ_EVENT; + } + + if (SSL_want_read(es->ssl)) { + /* SSL need data from the network */ + wanted |= LCB_READ_EVENT; + } + + if (BIO_ctrl_pending(es->wbio)) { + /* have data to flush */ + wanted |= LCB_WRITE_EVENT; + } + + /* set the events to deliver on the next 'fake' event. This will be all + * the available events AND'ed with all the events the user cared about */ + es->fakewhich = avail; + if (es->fakewhich & es->requested) { + lcbio_async_signal(es->as_fake); + } + + if ((es->requested & LCB_READ_EVENT) && (avail & LCB_READ_EVENT) == 0) { + /* if the user wanted application data but nothing is currently + * available in the SSL's internal buffer, request a read as demanded + * by the application. */ + wanted |= LCB_READ_EVENT; + } + + /* Schedule events to watch for here */ + IOT_V0EV(es->orig).watch( + IOT_ARG(es->orig), es->fd, es->event, wanted, es, event_handler); + +} + +/* Reads encrypted data from the socket into SSL */ +static int +read_ssl_data(lcbio_ESSL *es) +{ + BUF_MEM *rmb; + int nr; + lcbio_pTABLE iot = es->orig; + + /* This block is an optimization over BIO_write to avoid copying the memory + * to a temporary buffer and _then_ copying it into the BIO */ + + BIO_get_mem_ptr(es->rbio, &rmb); + while (1) { + /* I don't know why this is here, but it's found inside BIO_write */ + BIO_clear_retry_flags(es->rbio); + iotssl_bm_reserve(rmb); + nr = IOT_V0IO(iot).recv(IOT_ARG(iot), es->fd, + rmb->data + rmb->length, rmb->max - rmb->length, 0); + + if (nr > 0) { + /* Extend the BIO used length */ + rmb->length += nr; + } else if (nr == 0) { + es->closed = 1; + return -1; + } else { + switch (IOT_ERRNO(iot)) { + case C_EAGAIN: + return 0; + case EINTR: + continue; + default: + return -1; + } + } + } + /* CONSTCOND */ + return 0; +} + +/* Writes encrypted data from SSL over to the network */ +static int +flush_ssl_data(lcbio_ESSL *es) +{ + BUF_MEM *wmb; + char *tmp_p; + int tmp_len, nw; + lcbio_pTABLE iot = es->orig; + + BIO_get_mem_ptr(es->wbio, &wmb); + tmp_p = wmb->data; + tmp_len = wmb->length; + + /* We use this block of code over BIO_read() as we have no guarantee that + * we'll be able to flush all the bytes received from BIO_read(), and + * BIO has no way to "put back" some bytes. Thus this block is not an + * optimization but a necessity. + * + * tmp_len is the number of bytes originally inside the BUF_MEM structure. + * It is decremented each time we write data from the network. + * + * The loop here terminates until we get a WOULDBLOCK from the socket or we + * have no more data left to write. + */ + while (tmp_len) { + nw = IOT_V0IO(iot).send(IOT_ARG(iot), es->fd, tmp_p, tmp_len, 0); + if (nw > 0) { + tmp_len -= nw; + tmp_p += nw; + continue; + } else if (nw == 0) { + return -1; + } else { + switch (IOT_ERRNO(iot)) { + case C_EAGAIN: + goto GT_WRITE_DONE; + case EINTR: + continue; + default: + return -1; + } + } + } + + /* TODO: This block is inefficient as it results in a bunch of memmove() + * calls. While we could have done this inline with the send() call this + * would make future optimization more difficult. */ + GT_WRITE_DONE: + while (wmb->length > (size_t)tmp_len) { + char dummy[4096]; + unsigned to_read = MINIMUM(wmb->length-tmp_len, sizeof dummy); + BIO_read(es->wbio, dummy, to_read); + } + BIO_clear_retry_flags(es->wbio); + return 0; +} + +/* This is the raw event handler called from the underlying IOPS */ +static void +event_handler(lcb_socket_t fd, short which, void *arg) +{ + lcbio_ESSL *es = arg; + int rv = 0; + int u_which; + es->entered++; + + if (which & LCB_READ_EVENT) { + rv = read_ssl_data(es); + } + if (rv == 0 && (which & LCB_WRITE_EVENT)) { + rv = flush_ssl_data(es); + } + + if (rv == -1) { + es->error = 1; + + /* stop internal watcher */ + IOT_V0EV(es->orig).watch( + IOT_ARG(es->orig), es->fd, es->event, 0, NULL, NULL); + + /* send/recv will detect es->error and return -1/EINVAL appropriately */ + if (es->requested && es->ucb) { + es->ucb(fd, es->requested, es->arg); + } + es->entered--; + return; + } + + /* deliver stuff back to the user: */ + u_which = 0; + if (es->requested & LCB_READ_EVENT) { + u_which |= LCB_READ_EVENT; + } + if (es->requested & LCB_WRITE_EVENT) { + u_which |= LCB_WRITE_EVENT; + } + if (es->ucb && u_which) { + es->ucb(fd, u_which & es->requested, es->arg); + } + + /* socket closed. Don't reschedule */ + if (es->fd == -1) { + es->entered--; + return; + } + + es->entered--; + schedule_pending(es); +} + +/* User events are delivered out-of-sync with SSL events. This is mainly with + * respect to write events. */ +static void +fake_signal(void *arg) +{ + lcbio_ESSL *es = arg; + short which = es->fakewhich; + es->fakewhich = 0; + es->entered++; + + /* invoke the callback */ + which &= es->requested; + if (which && es->ucb) { + es->ucb(es->fd, which, es->arg); + } + es->entered--; + schedule_pending(es); +} + +static int +start_watch(lcb_io_opt_t iops, lcb_socket_t sock, void *event, short which, + void *uarg, lcb_ioE_callback callback) +{ + lcbio_ESSL *es = ES_FROM_IOPS(iops); + es->arg = uarg; + es->requested = which; + es->ucb = callback; + + SCHEDULE_PENDING_SAFE(es); + + (void)event; + (void)sock; + + return 0; + +} + +static void +stop_watch(lcb_io_opt_t iops, lcb_socket_t sock, void *event) +{ + start_watch(iops, sock, event, 0, NULL, NULL); +} + + +/** socket routines go here now.. */ +static lcb_ssize_t +Essl_recv(lcb_io_opt_t iops, lcb_socket_t sock, void *buf, lcb_size_t nbuf, + int ign) +{ + lcbio_ESSL *es = ES_FROM_IOPS(iops); + int rv = SSL_read(es->ssl, buf, nbuf); + + if (es->error) { + IOTSSL_ERRNO(es) = EINVAL; + return -1; + } + + if (rv >= 0) { + /* data or clean shutdown */ + return rv; + } else if (es->closed) { + return 0; + } else if (maybe_error(es, rv) != 0) { + IOTSSL_ERRNO(es) = EINVAL; + } else { + IOTSSL_ERRNO(es) = EWOULDBLOCK; + } + (void) ign; (void) sock; + return -1; +} + +static lcb_ssize_t +Essl_send(lcb_io_opt_t iops, lcb_socket_t sock, const void *buf, lcb_size_t nbuf, + int ign) +{ + lcbio_ESSL *es = ES_FROM_IOPS(iops); + int rv; + (void) ign; (void) sock; + + if (es->error) { + IOTSSL_ERRNO(es) = EINVAL; + return -1; + } + + rv = SSL_write(es->ssl, buf, nbuf); + if (rv >= 0) { + /* still need to schedule data to get flushed to the network */ + SCHEDULE_PENDING_SAFE(es); + return rv; + } else if (maybe_error(es, rv)) { + IOTSSL_ERRNO(es) = EINVAL; + return -1; + } else { + IOTSSL_ERRNO(es) = EWOULDBLOCK; + return -1; + } +} + +static lcb_ssize_t +Essl_recvv(lcb_io_opt_t iops, lcb_socket_t sock, lcb_IOV *iov, lcb_size_t niov) { + (void) niov; + return Essl_recv(iops, sock, iov->iov_base, iov->iov_len, 0); +} + +static lcb_ssize_t +Essl_sendv(lcb_io_opt_t iops, lcb_socket_t sock, lcb_IOV *iov, lcb_size_t niov) { + (void) niov; + return Essl_send(iops, sock, iov->iov_base, iov->iov_len, 0); +} + +static void +Essl_close(lcb_io_opt_t iops, lcb_socket_t fd) +{ + lcbio_ESSL *es = ES_FROM_IOPS(iops); + IOT_V0IO(es->orig).close(IOT_ARG(es->orig), fd); + es->fd = -1; +} + +static void +Essl_dtor(void *arg) +{ + lcbio_ESSL *es = arg; + IOT_V0EV(es->orig).destroy(IOT_ARG(es->orig), es->event); + lcbio_timer_destroy(es->as_fake); + iotssl_destroy_common((lcbio_XSSL *)es); + free(es); +} + +lcbio_pTABLE +lcbio_Essl_new(lcbio_pTABLE orig, lcb_socket_t fd, SSL_CTX *sctx) +{ + lcbio_ESSL *es = calloc(1, sizeof(*es)); + lcbio_TABLE *iot = &es->base_; + es->fd = fd; + es->as_fake = lcbio_timer_new(orig, es, fake_signal); + es->event = IOT_V0EV(orig).create(IOT_ARG(orig)); + iot->u_io.v0.ev.watch = start_watch; + iot->u_io.v0.ev.cancel = stop_watch; + iot->u_io.v0.io.recv = Essl_recv; + iot->u_io.v0.io.send = Essl_send; + iot->u_io.v0.io.recvv = Essl_recvv; + iot->u_io.v0.io.sendv = Essl_sendv; + iot->u_io.v0.io.close = Essl_close; + iot->dtor = Essl_dtor; + iotssl_init_common((lcbio_XSSL *)es, orig, sctx); + return iot; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/ssl/ssl_iot_common.h b/couchbase-sys/libcouchbase-2.7.0/src/ssl/ssl_iot_common.h new file mode 100644 index 00000000..f8c7e568 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/ssl/ssl_iot_common.h @@ -0,0 +1,180 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_SSL_IOTCOMMON +#define LCB_SSL_IOTCOMMON + +#include +#include +#include +#include +#include +#include +#include + +#define IOTSSL_COMMON_FIELDS \ + lcbio_TABLE base_; /**< Base table structure to export */ \ + lcbio_pTABLE orig; /**< Table pointer we are wrapping */ \ + SSL *ssl; /**< SSL object */ \ + BIO *wbio; /**< BIO used for writing data to network */ \ + BIO *rbio; /**<< BIO used for reading data from network */\ + lcb_io_opt_t iops_dummy_; /**< Dummy IOPS structure which is exposed to LCB */ \ + int error; /**< Internal error flag set once a fatal error is detect */\ + lcb_error_t errcode; /**< The error, converted into libcouchbase */ + +/** + * @brief + * This is the 'base' class for the lcbio_TABLE with SSL. This contains the + * core BIO and SSL structures as well as some other boilerplate needed to + * expose a complete lcbio_TABLE interface to the rest of the library. + * + * This is 'subclassed' as lcbio_CSSL and lcbio_ESSL for Completion and Event + * based I/O models respectively. + */ +typedef struct { + IOTSSL_COMMON_FIELDS +} lcbio_XSSL; + +/** + * @brief Get the associated lcbio_XSSL from an iops pointer + * @param iops the IOPS structure + * @return the lcbio_XSSL pointer + */ +#define IOTSSL_FROM_IOPS(iops) (iops)->v.v0.cookie + +/** + * @brief Access the iops `error` field which is exposed to the rest of LCBIO. + * The rest of the code will inspect this variable when a call fails to retrieve + * the errno. + * @param xs the lcbio_XSSL base pointer + * @return An lvaue to the error field + */ +#define IOTSSL_ERRNO(xs) (xs)->iops_dummy_->v.v0.error + +/** + * @brief Check and handle SSL errors + * + * This function inspects the error state of the current `SSL` object. If a + * fatal error is detected, the internal error flag is set and pending errors + * are logged. + * + * @param xs The XSSL context + * @param rv The return code received from an `SSL_read()` or `SSL_write()` + * @return nonzero if a fatal error has occurred, 0 if the error is transient + * and is `SSL_ERROR_WANT_READ` or `SSL_ERROR_WANT_WRITE`. + * + * @note Do not call this function if `rv` is `>0`. + */ +int +iotssl_maybe_error(lcbio_XSSL *xs, int rv); + +/** + * Flush errors from the internal error queue. Call this whenever an error + * has taken place + * @param xs + */ +void +iotssl_log_errors(lcbio_XSSL *xs); + +/** + * This function acts as the 'base' constructor for lcbio_XSSL. It will + * initialize and proxy the various timer and run/stop routines to the underlying + * iops plugin. The 'subclass' is still expected to implement the actual send, + * recv, close, and event routines. + * + * @param xs The lcbio_XSSL pointer to initialize (usually a pointer to a field + * within the real structure) + * @param orig The original lcbio_TABLE containing the actual socket I/O routines. + * @param ctx the `SSL_CTX*` which will be used to create the `SSL*` pointer + */ +void +iotssl_init_common(lcbio_XSSL *xs, lcbio_TABLE *orig, SSL_CTX *ctx); + +/** + * This function acts as the base destructor for lcbio_XSSL + * @param xs the lcbio_XSSL to clean up. + * After this function has been called, none of the base fields should be + * considered valid (unless a refcounted item is specifically kept alive). + */ +void +iotssl_destroy_common(lcbio_XSSL *xs); + +/** + * Reserve a specified amount of bytes for reading into a `BUF_MEM*` structure. + * Currently the amount reserved is hard coded. + * + * Use this function to retrievw a pointer to the unused (but allocated) portion + * of the `BUF_MEM` structure rather than doing an explicit BIO_write which + * will result in needless copying of memory. Unfortunately OpenSSL does not have + * a clean way of growing this buffer but it is possible. + * + * @param bm The `BUF_MEM*` structure. + * + * @code{.c} + * BUF_MEM *bm; + * iotssl_bm_reserve(bm); + * recv(fd, bm->data, bm->max-mb->length, 0); + * @endcode + */ +void +iotssl_bm_reserve(BUF_MEM *bm); + +/** + * Prepare the SSL structure so that a subsequent call to SSL_pending will + * actually determine if there's any data available for read + * @param ssl the SSL object + * @return + */ +#define IOTSSL_PENDING_PRECHECK(ssl) do { \ + char iotssl__dummy; \ + SSL_peek(ssl, &iotssl__dummy, 1); \ +} while (0); + +/** + * Wrapper for SSL_pending. In order to work around another bug of the well + * designed OpenSSL library, which will strangely place "undefined" errors + * into the queue unless this check is done beforehand. + * + * See: https://groups.google.com/forum/#!msg/mailing.openssl.users/so242GuI6Yo/2Jp3Qoo_gsgJ + * See: http://stackoverflow.com/questions/22753221/openssl-read-write-handshake-data-with-memory-bio + * See: http://www.opensubscriber.com/message/openssl-users@openssl.org/8638179.html + */ +#define IOTSSL_IS_PENDING(ssl) \ + (SSL_get_ssl_method(ssl) != SSLv23_client_method()) && SSL_pending(ssl) +/** + * Create and return a pointer to an lcbio_TABLE with an underlying + * completion-based I/O model + * @param orig The original table + * @param sd The socket descriptor which is already connected + * @param sctx + * @return NULL on error + */ +lcbio_pTABLE +lcbio_Cssl_new(lcbio_pTABLE orig, lcb_sockdata_t *sd, SSL_CTX *sctx); + +/** + * Create and return a pointer to an lcbio_TABLE with an underlying event + * based I/O model + * @param orig The original pointer + * @param fd Socket descriptor + * @param sctx + * @return NULL on error. + */ +lcbio_pTABLE +lcbio_Essl_new(lcbio_pTABLE orig, lcb_socket_t fd, SSL_CTX *sctx); + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/ssobuf.h b/couchbase-sys/libcouchbase-2.7.0/src/ssobuf.h new file mode 100644 index 00000000..29aa1a61 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/ssobuf.h @@ -0,0 +1,82 @@ +/* + * Copyright 2015 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Implementation of dynamic arrays or strings suitable for one or more than + * one element. If only a single element is used, extra memory is not allocated + * via malloc. + */ + +#include "simplestring.h" + +#define LCB_SSOBUF_DECLARE(T) \ + struct { \ + unsigned count; \ + union { \ + lcb_string alloc; \ + T single; \ + } u;\ + } \ + +#define LCB_SSOBUF_ALLOC(p, o, T) do { \ + if ((o)->count == 0) { /* Only allocate a single element */ \ + *(p) = &(o)->u.single; \ + (o)->count++; \ + } else if ((o)->count == 1) { /* Switch over to malloc */ \ + T ssobuf__tmp = (o)->u.single; \ + (o)->u.alloc.nused = 0; \ + (o)->u.alloc.nalloc = 0; \ + (o)->u.alloc.base = NULL; \ + if (-1 == lcb_string_reserve(&(o)->u.alloc, sizeof(T) * 2)) { \ + *(p) = NULL; \ + } else { \ + memcpy((o)->u.alloc.base, &ssobuf__tmp, sizeof(T)); \ + lcb_string_added(&(o)->u.alloc, sizeof(T)); \ + *(p) = (T*) ((o)->u.alloc.base + (o)->u.alloc.nused); \ + lcb_string_added(&(o)->u.alloc, sizeof(T)); \ + } \ + (o)->count++; \ + } else if (-1 == lcb_string_reserve(&(o)->u.alloc, sizeof(T))) { \ + *(p) = NULL; \ + } else { \ + *(p) = (T*) ((o)->u.alloc.base + (o)->u.alloc.nused); \ + lcb_string_added(&(o)->u.alloc, sizeof(T)); \ + (o)->count++; \ + } \ +} while (0); + +#define LCB_SSOBUF_ALLOC_N(p, o, T, n) do { \ + if (n == 1) { \ + LCB_SSOBUF_ALLOC(p, o, T); \ + } else if (n > 1) { \ + if (-1 == lcb_string_reserve(&(o)->u.alloc, sizeof(T) * (n) )) { \ + *(p) = NULL; \ + } else { \ + lcb_string_added(&(o)->u.alloc, sizeof(T) * (n) ); \ + *(p) = (T*)(o)->u.alloc.base; \ + (o)->count = n; \ + } \ + } \ +} while (0); + +#define LCB_SSOBUF_CLEAN(o) do { \ + if ((o)->count > 1) { \ + lcb_string_release(&(o)->u.alloc); \ + } \ +} while (0); + +#define LCB_SSOBUF_ARRAY(o, t) \ + ((o)->count > 1 ? ((t*)(o)->u.alloc.base) : &(o)->u.single) diff --git a/couchbase-sys/libcouchbase-2.7.0/src/strcodecs/base64.c b/couchbase-sys/libcouchbase-2.7.0/src/strcodecs/base64.c new file mode 100644 index 00000000..6c69e09f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/strcodecs/base64.c @@ -0,0 +1,123 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "strcodecs.h" +#include + +/* + * Function to base64 encode a text string as described in RFC 4648 + * + * @author Trond Norbye + */ + +/** + * An array of the legal charracters used for direct lookup + */ +static const lcb_uint8_t code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/** + * Encode up to 3 characters to 4 output character. + * + * @param s pointer to the input stream + * @param d pointer to the output stream + * @param num the number of characters from s to encode + * @return 0 upon success, -1 otherwise. + */ +static int encode_rest(const lcb_uint8_t *s, lcb_uint8_t *d, lcb_size_t num) +{ + lcb_uint32_t val = 0; + + switch (num) { + case 2: + val = (lcb_uint32_t)((*s << 16) | (*(s + 1) << 8)); + break; + case 1: + val = (lcb_uint32_t)((*s << 16)); + break; + default: + return -1; + } + + d[3] = '='; + + if (num == 2) { + d[2] = code[(val >> 6) & 63]; + } else { + d[2] = '='; + } + + d[1] = code[(val >> 12) & 63]; + d[0] = code[(val >> 18) & 63]; + + return 0; +} + +/** + * Encode 3 characters to 4 output character. + * + * @param s pointer to the input stream + * @param d pointer to the output stream + */ +static int encode_triplet(const lcb_uint8_t *s, lcb_uint8_t *d) +{ + lcb_uint32_t val = (lcb_uint32_t)((*s << 16) | (*(s + 1) << 8) | (*(s + 2))); + d[3] = code[val & 63] ; + d[2] = code[(val >> 6) & 63]; + d[1] = code[(val >> 12) & 63]; + d[0] = code[(val >> 18) & 63]; + + return 0; +} + +/** + * Base64 encode a string into an output buffer. + * @param src string to encode + * @param dst destination buffer + * @param sz size of destination buffer + * @return 0 if success, -1 if the destination buffer isn't big enough + */ +int lcb_base64_encode(const char *src, char *dst, lcb_size_t sz) +{ + lcb_size_t len = strlen(src); + lcb_size_t triplets = len / 3; + lcb_size_t rest = len % 3; + lcb_size_t ii; + const lcb_uint8_t *in = (const lcb_uint8_t *)src; + lcb_uint8_t *out = (lcb_uint8_t *)dst; + + if (sz < (lcb_size_t)((triplets + 1) * 4)) { + return -1; + } + + for (ii = 0; ii < triplets; ++ii) { + if (encode_triplet(in, out) != 0) { + return -1; + } + in += 3; + out += 4; + } + + if (rest > 0) { + if (encode_rest(in, out, rest) != 0) { + return -1; + } + out += 4; + } + *out = '\0'; + + return 0; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/strcodecs/strcodecs.h b/couchbase-sys/libcouchbase-2.7.0/src/strcodecs/strcodecs.h new file mode 100644 index 00000000..83bd8363 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/strcodecs/strcodecs.h @@ -0,0 +1,285 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_STRCODECS_H +#define LCB_STRCODECS_H +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif +lcb_error_t lcb_urlencode_path(const char *path, + lcb_size_t npath, + char **out, + lcb_size_t *nout); + +/** + * Decode a string from 'percent-encoding' + * @param in The input string + * @param[in,out] out The output buffer. + * If upon entry, out is not-NULL, it is assumed to be a buffer + * containing sufficient size for the percent encoding (up to + * 3x the size of input). Otherwise on exit this will contain + * a malloc'd buffer which should be free()d when no longer + * required. + * @param n The size of the input buffer. May be -1 if NUL-terminated + * @return 0 if converted successfuly, -1 on error + */ +int +lcb_urldecode(const char *in, char *out, lcb_SSIZE n); + +/** + * Base64 encode a string into an output buffer. + * @param src string to encode + * @param dst destination buffer + * @param sz size of destination buffer + * @return 0 if success, -1 if the destination buffer isn't big enough + */ +int lcb_base64_encode(const char *src, char *dst, lcb_size_t sz); + +/** + * Encodes a string suitable for being passed as either a key or value in an + * "HTTP Form" per application/x-www-form-urlencoded + * @param s The input string + * @param n The size of the input + * @param out The output buffer - should be at least 3x the input length + * @return The number of bytes actually used in the output buffer. + */ +size_t +lcb_formencode(const char *s, size_t n, char *out); + +#ifdef __cplusplus +} + +#include +namespace lcb { +namespace strcodecs { +template +bool urldecode(Ti first, Ti last, To out, size_t& nout) { + for (; first != last && *first != '\0'; ++first) { + if (*first == '%') { + char nextbuf[3] = { 0 }; + size_t jj = 0; + first++; + nextbuf[0] = *first; + for (; first != last && jj < 2; ++jj) { + nextbuf[jj] = *first; + if (jj != 1) { + first++; + } + } + if (jj != 2) { + return false; + } + + unsigned octet = 0; + if (sscanf(nextbuf, "%2X", &octet) != 1) { + return false; + } + + *out = static_cast(octet); + } else { + *out = *first; + } + + out++; + nout++; + } + return true; +} + +inline bool +urldecode(const char *input, char *output) { + const char *endp = NULL; + size_t nout = 0; + if (urldecode(input, endp, output, nout)) { + output[nout] = '\0'; + return true; + } + return false; +} + +inline bool +urldecode(char *in_out) { + return urldecode(in_out, in_out); +} + +inline bool +urldecode(std::string& s) +{ + size_t n = 0; + if (urldecode(s.begin(), s.end(), s.begin(), n)) { + s.resize(n); + return true; + } + return false; +} + +namespace priv { +inline bool +is_legal_urichar(char c) +{ + unsigned char uc = (unsigned char)c; + if (isalpha(uc) || isdigit(uc)) { + return true; + } + switch (uc) { + case '-': + case '_': + case '.': + case '~': + case '!': + case '*': + case '\'': + case '(': + case ')': + case ';': + case ':': + case '@': + case '&': + case '=': + case '+': + case '$': + case ',': + case '/': + case '?': + case '#': + case '[': + case ']': + return true; + default: + break; + } + return false; +} + +template +inline bool +is_already_escape(T first, T last) { + first++; // ignore '%' + size_t jj; + for (jj = 0; first != last && jj < 2; ++jj, ++first) { + if (!isxdigit(*first)) { + return false; + } + } + if (jj != 2) { + return false; + } + return true; +} +} // namespace: priv + +template +bool +urlencode(Ti first, Ti last, To& o, bool check_encoded=true) { + // If re-encoding detection is enabled, this flag indicates not to + // re-encode + bool skip_encoding = false; + + for (; first != last; ++first) { + if (!skip_encoding && check_encoded) { + if (*first == '%') { + skip_encoding = priv::is_already_escape(first, last); + } else if (*first == '+') { + skip_encoding = true; + } + } + if (skip_encoding || priv::is_legal_urichar(*first)) { + if (skip_encoding && *first != '%' && !priv::is_legal_urichar(*first)) { + return false; + } + + o.insert(o.end(), first, first + 1); + } else { + unsigned int c = static_cast(*first); + size_t numbytes; + + if ((c & 0x80) == 0) { /* ASCII character */ + numbytes = 1; + } else if ((c & 0xE0) == 0xC0) { /* 110x xxxx */ + numbytes = 2; + } else if ((c & 0xF0) == 0xE0) { /* 1110 xxxx */ + numbytes = 3; + } else if ((c & 0xF8) == 0xF0) { /* 1111 0xxx */ + numbytes = 4; + } else { + return false; + } + + do { + char buf[4]; + sprintf(buf, "%%%02X", static_cast(*first)); + o.insert(o.end(), &buf[0], &buf[0] + 3); + } while (--numbytes && ++first != last); + } + } + return true; +} +template +bool +urlencode(const Tin& in, Tout& out) +{ + return urlencode(in.begin(), in.end(), out); +} + +/* See: https://url.spec.whatwg.org/#urlencoded-serializing: */ +/* + * 0x2A + * 0x2D + * 0x2E + * 0x30 to 0x39 + * 0x41 to 0x5A + * 0x5F + * 0x61 to 0x7A + * Append a code point whose value is byte to output. + * Otherwise + * Append byte, percent encoded, to output. + */ +template +void +formencode(Ti first, Ti last, To& out) +{ + for (; first != last; ++first) { + unsigned char c = *first; + if (isalnum(c)) { + out.insert(out.end(), first, first + 1); + continue; + } else if (c == ' ') { + char tmp = '+'; + out.insert(out.end(), &tmp, &tmp + 1); + } else if ( + (c == 0x2A || c == 0x2D || c == 0x2E) || + (c >= 0x30 && c <= 0x39) || + (c >= 0x41 && c <= 0x5A) || + (c == 0x5F) || + (c >= 0x60 && c <= 0x7A)) { + out.insert(out.end(), static_cast(c)); + } else { + char buf[3] = { 0 }; + out.insert(out.end(), '%'); + sprintf(buf, "%02X", c); + out.insert(out.end(), &buf[0], &buf[0]+2); + } + } +} + +} // namespace: strcodecs +} // namespace: lcb +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/timings.c b/couchbase-sys/libcouchbase-2.7.0/src/timings.c new file mode 100644 index 00000000..a5ca0408 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/timings.c @@ -0,0 +1,208 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2011-2015 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "internal.h" + +/** + * Timing data in libcouchbase is stored in a structure to make + * it easy to work with. It ill consume a fair amount of data, + * but it's only allocated when you enable it ;-) + * I decided I'd rather just make it easy to work with... + */ +struct lcb_histogram_st { + /** The highest value (number of ocurrences ) in all of the buckets */ + lcb_U32 max; + + /** Number of entries below a microsecond */ + lcb_U32 nsec; + + /** + * Entries between 1-1000 microseconds. Each array element refers to a + * 10 microsecond interval + */ + lcb_U32 usec[100]; + + /** + * Entries between 1-10 milliseconds. Each array entry refers to a 100 + * microsecond interval. + */ + lcb_U32 lt10msec[100]; + + /** + * Entries between 10-1000 milliseconds. Each entry refers to a 10 millisecond + * interval + */ + lcb_U32 msec[100]; + + /** + * Seconds are collected per sec + */ + lcb_U32 sec[10]; +}; + +LCB_INTERNAL_API +lcb_HISTOGRAM * +lcb_histogram_create(void) +{ + return calloc(1, sizeof(lcb_HISTOGRAM)); +} + +LCB_INTERNAL_API +void +lcb_histogram_destroy(lcb_HISTOGRAM *hg) +{ + free(hg); +} + + +LCB_INTERNAL_API +void +lcb_histogram_read(const lcb_HISTOGRAM *hg, + const void *cookie, lcb_HISTOGRAM_CALLBACK callback) +{ + lcb_U32 max, start, ii, end; + + max = hg->max; + /* + ** @todo I should merge "empty" sets.. currently I'm only going to + ** report the nonzero ones... + */ + if (hg->nsec) { + callback(cookie, LCB_TIMEUNIT_NSEC, 0, 999, hg->nsec, max); + } + + start = 1; + for (ii = 0; ii < 100; ++ii) { + end = (ii + 1) * 10 - 1; + if (hg->usec[ii]) { + callback(cookie, LCB_TIMEUNIT_USEC, start, end, hg->usec[ii], max); + } + start = end + 1; + } + + start = 1000; + for (ii = 0; ii < 100; ++ii) { + end = (ii + 1) * 100 - 1; + if (hg->lt10msec[ii]) { + callback(cookie, LCB_TIMEUNIT_USEC, start, end, hg->lt10msec[ii], max); + } + start = end + 1; + } + + start = 1; + for (ii = 0; ii < 100; ++ii) { + end = (ii + 1) * 10 - 1; + if (hg->msec[ii]) { + callback(cookie, LCB_TIMEUNIT_MSEC, start, end, hg->msec[ii], max); + } + start = end + 1; + } + + start = 1000; + for (ii = 1; ii < 9; ++ii) { + start = ii * 1000; + end = ((ii + 1) * 1000) - 1; + if (hg->sec[ii]) { + callback(cookie, LCB_TIMEUNIT_MSEC, start, end, hg->sec[ii], max); + } + } + + if (hg->sec[9]) { + callback(cookie, LCB_TIMEUNIT_SEC, 9, 9999, hg->sec[9], max); + } +} + +static void +default_timings_callback(const void *cookie, + lcb_timeunit_t timeunit, + lcb_uint32_t min_val, lcb_uint32_t max_val, + lcb_uint32_t total, lcb_uint32_t maxtotal) { + FILE* stream = (FILE*)cookie; + const char *unit = NULL; + int ii; + int num_hash; + + fprintf(stream, "[%-4u - %-4u]", min_val, max_val); + if (timeunit == LCB_TIMEUNIT_NSEC) { + unit = "ns"; + } else if (timeunit == LCB_TIMEUNIT_USEC) { + unit = "us"; + } else if (timeunit == LCB_TIMEUNIT_MSEC) { + unit = "ms"; + } else if (timeunit == LCB_TIMEUNIT_SEC) { + unit = "s"; + } else { + unit = "?"; + } + fprintf(stream, "%s |", unit); + + num_hash = (int)((float)40.0 * (float)total / (float)maxtotal); + + for (ii = 0; ii < num_hash; ++ii) { + putw('#', stream); + } + + fprintf(stream, " - %u\n", total); +} + + +LCB_INTERNAL_API +void lcb_histogram_print(lcb_HISTOGRAM* hg, FILE* stream) +{ + lcb_histogram_read(hg, stream, default_timings_callback); +} + + +LCB_INTERNAL_API +void +lcb_histogram_record(lcb_HISTOGRAM *hg, lcb_U64 delta) +{ + lcb_U32 num; + + if (delta < 1000) { + /* nsec */ + if (++hg->nsec > hg->max) { + hg->max = hg->nsec; + } + } else if (delta < LCB_US2NS(1000)) { + /* micros */ + delta /= LCB_US2NS(1); + if ((num = ++hg->usec[delta/10]) > hg->max) { + hg->max = num; + } + } else if (delta < LCB_US2NS(10000)) { + /* 1-10ms */ + delta /= LCB_US2NS(1); + assert(delta <= 10000); + if ((num = ++hg->lt10msec[delta/100]) > hg->max) { + hg->max = num; + } + } else if (delta < LCB_S2NS(1)) { + delta /= LCB_US2NS(1000); + if ((num = ++hg->msec[delta/10]) > hg->max) { + hg->max = num; + } + } else { + delta /= LCB_S2NS(1); + if (delta > 9) { + delta = 9; + } + + if ((num = ++hg->sec[delta]) > hg->max) { + hg->max = num; + } + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/trace.h b/couchbase-sys/libcouchbase-2.7.0/src/trace.h new file mode 100644 index 00000000..4db90361 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/trace.h @@ -0,0 +1,110 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBCOUCHBASE_TRACE_H +#define LIBCOUCHBASE_TRACE_H 1 + +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic push + +#ifdef __clang__ +#pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +#else +#pragma GCC diagnostic ignored "-Wvariadic-macros" +#endif +#endif + +#ifdef HAVE_DTRACE +/* include the generated probes header and put markers in code */ +#include "probes.h" +#define TRACE(probe) probe + +#else +/* Wrap the probe to allow it to be removed when no systemtap available */ +#define TRACE(probe) +#endif + +#define TRACE_BEGIN_COMMON(TGT, req, cmd, ...) \ + TGT((req)->request.opaque, ntohs((req)->request.vbucket), (req)->request.opcode, \ + (cmd)->key.contig.bytes, (cmd)->key.contig.nbytes, ## __VA_ARGS__) + +#define TRACE_BEGIN_SIMPLE(TGT, req, cmd) \ + TGT((req)->request.opaque, ntohs((req)->request.vbucket), (req)->request.opcode, \ + (cmd)->key.contig.bytes, (cmd)->key.contig.nbytes) + +#define TRACE_END_COMMON(TGT, mcresp, resp, ...) \ + TGT(mcresp->opaque(), 0, mcresp->opcode(), (resp)->rc, (const char *)(resp)->key, (resp)->nkey, \ + ## __VA_ARGS__) + +#define TRACE_END_SIMPLE(TGT, mcresp, resp) \ + TGT(mcresp->opaque(), 0, mcresp->opcode(), (resp)->rc, (const char *)(resp)->key, (resp)->nkey) + +#define TRACE_GET_BEGIN(req, cmd) \ + TRACE(TRACE_BEGIN_COMMON(LIBCOUCHBASE_GET_BEGIN, req, cmd, (cmd)->exptime)) + +#define TRACE_GET_END(mcresp, resp) \ + TRACE(TRACE_END_COMMON(LIBCOUCHBASE_GET_END, mcresp, resp, \ + (char*)(resp)->value, (resp)->nvalue, (resp)->itmflags, (resp)->cas, \ + mcresp->datatype())) + +#define TRACE_UNLOCK_BEGIN(req, cmd) TRACE(TRACE_BEGIN_SIMPLE(LIBCOUCHBASE_UNLOCK_BEGIN, req, cmd)) +#define TRACE_UNLOCK_END(mcresp, resp) TRACE(TRACE_END_SIMPLE(LIBCOUCHBASE_UNLOCK_END, mcresp, resp)) + +#define TRACE_STORE_BEGIN(req, cmd) \ + TRACE(TRACE_BEGIN_COMMON(LIBCOUCHBASE_STORE_BEGIN, req, cmd, \ + ( (cmd)->value.vtype == LCB_KV_IOV ? NULL : (cmd)->value.u_buf.contig.bytes ),\ + ( (cmd)->value.vtype == LCB_KV_IOV ? 0 : (cmd)->value.u_buf.contig.nbytes ),\ + (cmd)->flags, (cmd)->cas, (req)->request.datatype, (cmd)->exptime)) + +#define TRACE_STORE_END(mcresp, resp) TRACE(TRACE_END_COMMON(LIBCOUCHBASE_STORE_END, mcresp, resp, (resp)->cas)) + +#define TRACE_ARITHMETIC_BEGIN(req, cmd) \ + TRACE(TRACE_BEGIN_COMMON(LIBCOUCHBASE_ARITHMETIC_BEGIN, req, cmd, \ + (cmd)->delta, (cmd)->initial, (cmd)->exptime)) + +#define TRACE_ARITHMETIC_END(mcresp, resp) \ + TRACE(TRACE_END_COMMON(LIBCOUCHBASE_ARITHMETIC_END, mcresp, resp, (resp)->value, (resp)->cas)) + +#define TRACE_TOUCH_BEGIN(req, cmd) \ + TRACE(TRACE_BEGIN_COMMON(LIBCOUCHBASE_TOUCH_BEGIN, req, cmd, (cmd)->exptime)) +#define TRACE_TOUCH_END(mcresp, resp) \ + TRACE(TRACE_END_COMMON(LIBCOUCHBASE_TOUCH_END, mcresp, resp, (resp)->cas)) + +#define TRACE_REMOVE_BEGIN(req, cmd) TRACE(TRACE_BEGIN_SIMPLE(LIBCOUCHBASE_REMOVE_BEGIN, req, cmd)) +#define TRACE_REMOVE_END(mcresp, resp) \ + TRACE(TRACE_END_COMMON(LIBCOUCHBASE_REMOVE_END, mcresp, resp, (resp)->cas)) + +#define TRACE_OBSERVE_BEGIN(req, body) \ + TRACE(LIBCOUCHBASE_OBSERVE_BEGIN(\ + (req)->request.opaque, 0, (req)->request.opcode, body, \ + ntohl( (req)->request.bodylen) )) + +#define TRACE_OBSERVE_PROGRESS(mcresp, resp) \ + TRACE(TRACE_END_COMMON(LIBCOUCHBASE_OBSERVE_PROGRESS,mcresp,resp, \ + (resp)->cas, (resp)->status, (resp)->ismaster, (resp)->ttp, (resp)->ttr)) + +#define TRACE_OBSERVE_END(mcresp) \ + TRACE(LIBCOUCHBASE_OBSERVE_END(mcresp->opaque(), 0, mcresp->opcode(), LCB_SUCCESS)) + +#define TRACE_HTTP_BEGIN(req) TRACE(LIBCOUCHBASE_HTTP_BEGIN((req)->url, (req)->nurl, (req)->method)) +#define TRACE_HTTP_END(req, rc, resp) TRACE(LIBCOUCHBASE_HTTP_END((req)->url, (req)->nurl, (req)->method, (resp)->rc, (resp)->htstatus + +#ifdef __clang__ +#pragma GCC diagnostic pop +#endif /* __clang__ */ + +#endif /* TRACE_H */ diff --git a/couchbase-sys/libcouchbase-2.7.0/src/utilities.c b/couchbase-sys/libcouchbase-2.7.0/src/utilities.c new file mode 100644 index 00000000..0dadf918 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/utilities.c @@ -0,0 +1,171 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal.h" + +/** + * This file contains utility functions which don't have another place + * to call home + */ + +extern lcb_uint64_t lcb_byteswap64(lcb_uint64_t val) +{ + lcb_size_t ii; + lcb_uint64_t ret = 0; + for (ii = 0; ii < sizeof(lcb_uint64_t); ii++) { + ret <<= 8; + ret |= val & 0xff; + val >>= 8; + } + return ret; +} + +/** + * While the C standard library uses 'putenv' for environment variable + * manipulation, POSIX defines setenv (which works sanely) but Windows + * only has putenv (via the CRT interface). + * However Windows also has the 'GetEnvironmentVariable' etc. API - which + * actually uses a different interface. + * + * We prefer to use actual API calls rather than hack into a poor excuse + * of conformance. Since putenv requires ownership of the string, its use + * is discouraged (and _putenv_s isn't available in MinGW); thus the + * assumption that the most common APIs are GetEnvironmentVariable and + * SetEnvironmentVariable. We try to abstract this away from the rest of the + * library. + */ + +#ifdef _WIN32 +int lcb_getenv_nonempty(const char *key, char *buf, lcb_size_t len) +{ + DWORD nvalue = GetEnvironmentVariable(key, buf, (DWORD)len); + + if (nvalue == 0 || nvalue >= len) { + return 0; + } + + if (!buf[0]) { + return 0; + } + return 1; +} + +#else +int lcb_getenv_nonempty(const char *key, char *buf, lcb_size_t len) +{ + const char *cur = getenv(key); + if (cur == NULL || *cur == '\0') { + return 0; + } + + strncpy(buf, cur, len); + return 1; +} +#endif + +int lcb_getenv_boolean(const char *key) +{ + char value[4096] = { 0 }; + int rv; + rv = lcb_getenv_nonempty(key, value, sizeof(value)); + return rv != 0 && value[0] != '\0' && value[0] != '0'; +} + +#ifdef _WIN32 +lcb_error_t lcb_initialize_socket_subsystem(void) +{ + static volatile LONG initialized = 0; + WSADATA wsaData; + + if (InterlockedCompareExchange(&initialized, 1, 0)) { + return LCB_SUCCESS; + } + if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) { + lcb_assert("Winsock initialization error" && 0); + } + return LCB_SUCCESS; +} +#else +lcb_error_t lcb_initialize_socket_subsystem(void) +{ + return LCB_SUCCESS; +} +#endif + +int +lcb_getenv_nonempty_multi(char *buf, lcb_size_t nbuf, ...) +{ + va_list ap; + const char *cur; + int found = 0; + + va_start(ap, nbuf); + while ((cur = va_arg(ap, const char*))) { + if ((found = lcb_getenv_nonempty(cur, buf, nbuf))) { + break; + } + } + va_end(ap); + return found; +} + +int +lcb_getenv_boolean_multi(const char *key, ...) +{ + va_list ap; + const char *cur; + int ret = 0; + + va_start(ap, key); + if (lcb_getenv_boolean(key)) { + va_end(ap); + return 1; + } + + while ((cur = va_arg(ap, const char *))) { + if ((ret = lcb_getenv_boolean(cur))) { + break; + } + } + va_end(ap); + return ret; +} + +const char * +lcb_get_tmpdir(void) +{ +#if defined(_WIN32) + static char buf[MAX_PATH+1] = { 0 }; + if (buf[0]) { + return buf; + } + GetTempPath(sizeof buf, buf); + return buf; +#else + const char *ret; + if ((ret = getenv("TMPDIR")) != NULL) { + return ret; + } else { + + #if defined(_POSIX_VERSION) + return "/tmp"; + #else + return "."; + #endif + } +#endif +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/vbucket/CMakeLists.txt b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/CMakeLists.txt new file mode 100644 index 00000000..17bd9b25 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/CMakeLists.txt @@ -0,0 +1,2 @@ +ADD_LIBRARY(vbucket OBJECT vbucket.c ketama.c ${SOURCE_ROOT}/contrib/cJSON/cJSON.c) +LCB_UTIL(vbucket) diff --git a/couchbase-sys/libcouchbase-2.7.0/src/vbucket/aliases.h b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/aliases.h new file mode 100644 index 00000000..6233bbf7 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/aliases.h @@ -0,0 +1,35 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VB_ALIASES_H +#define VB_ALIASES_H + +#define VB_NODESTR(config, index) lcbvb_get_hostport(config, index, LCBVB_SVCTYPE_DATA, LCBVB_SVCMODE_PLAIN) +#define VB_RESTURL(config, index) lcbvb_get_hostport(config, index, LCBVB_SVCTYPE_MGMT, LCBVB_SVCMODE_PLAIN) +#define VB_VIEWSURL(config, index) lcbvb_get_capibase(config, index, LCBVB_SVCMODE_PLAIN) +#define VB_SSLNODESTR(config, index) lcbvb_get_hostport(config, index, LCBVB_SVCTYPE_DATA, LCBVB_SVCMODE_SSL) +#define VB_SSLRESTURL(config, index) lcbvb_get_hostport(config, index, LCBVB_SVCTYPE_MGMT, LCBVB_SVCMODE_SSL) +#define VB_SSLVIEWSURL(config, index) lcbvb_get_capibase(config, index, LCBVB_SVCMODE_SSL) +#define VB_MEMDSTR(config, index, mode) lcbvb_get_hostport(config, index, LCBVB_SVCTYPE_DATA, mode) +#define VB_MGMTSTR(config, index, mode) lcbvb_get_hostport(config, index, LCBVB_SVCTYPE_MGMT, mode) +#define VB_CAPIURL(config, index, mode) lcbvb_get_capibase(config, index, mode) + +#define VB_DISTTYPE(config) (config)->dtype +#define VB_NREPLICAS(config) (config)->nrepl +#define VB_NSERVERS(config) (config)->nsrv + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/vbucket/crc32.h b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/crc32.h new file mode 100644 index 00000000..b5532c4f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/crc32.h @@ -0,0 +1,83 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* The crc32 functions and data was originally written by Spencer + * Garrett and was gleaned from the PostgreSQL source + * tree via the files contrib/ltree/crc32.[ch] and from FreeBSD at + * src/usr.bin/cksum/crc32.c. + */ +static const uint32_t crc32tab[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; + +static uint32_t hash_crc32(const char *key, size_t key_length) +{ + uint64_t x; + uint32_t crc= UINT32_MAX; + + for (x= 0; x < key_length; x++) + crc= (crc >> 8) ^ crc32tab[(crc ^ (uint64_t)key[x]) & 0xff]; + + return ((~crc) >> 16) & 0x7fff; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/vbucket/hash.h b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/hash.h new file mode 100644 index 00000000..da42a80d --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/hash.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010 NorthScale, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBVBUCKET_HASH_H +#define LIBVBUCKET_HASH_H 1 + +#include "config.h" +#include +#include + +uint32_t vb__hash_ketama(const char *key, size_t key_length); +void vb__hash_md5(const char *key, size_t key_length, unsigned char *result); +void* vb__hash_md5_update(void *ctx, const char *key, size_t key_length); +void vb__hash_md5_final(void *ctx, unsigned char *result); + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/vbucket/json-inl.h b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/json-inl.h new file mode 100644 index 00000000..9d27b3f3 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/json-inl.h @@ -0,0 +1,112 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Helper routines for cJSON + */ +/** + * Utility function to retrieve a string from an object + * @param parent Object + * @param key Key for item + * @param[out] value + * @return nonzero on success, zero if not found, or not a string + */ +static int +get_jstr(cJSON *parent, const char *key, char **value) +{ + cJSON *res = cJSON_GetObjectItem(parent, key); + if (res == NULL || res->type != cJSON_String) { + *value = NULL; + return 0; + } + + *value = res->valuestring; + return 1; +} + +/** + * Utility function to retrieve a sub-object from a parent object + * @param parent + * @param key + * @param[out] value + * @return nonzero on success, zero if not found or not an object + */ +static int +get_jobj(cJSON *parent, const char *key, cJSON **value) +{ + cJSON *res = cJSON_GetObjectItem(parent, key); + if (res == NULL || res->type != cJSON_Object) { + *value = NULL; + return 0; + } + + *value = res; + return 1; +} + +/** + * Utility function to extract an integer from an object + * @param parent + * @param key + * @param[out] value + * @return nonzero on success, zero if not found or not a number + */ +static int +get_jint(cJSON *parent, const char *key, int *value) +{ + cJSON *res = cJSON_GetObjectItem(parent, key); + if (res == NULL || res->type != cJSON_Number) { + *value = 0; + return 0; + } + + *value = res->valueint; + return 1; +} + +/** + * Convenience wrapper around get_jint() which writes its value to an unsigned + * int. + * + * @param parent + * @param key + * @param value + * @return + */ +static int +get_juint(cJSON *parent, const char *key, unsigned *value) +{ + int tmp = 0; + if (!get_jint(parent, key, &tmp)) { + *value = 0; + return 0; + } + *value = tmp; + return 1; +} + +static int +get_jarray(cJSON *parent, const char *key, cJSON **value) +{ + cJSON *res = cJSON_GetObjectItem(parent, key); + if (res == NULL || res->type != cJSON_Array) { + *value = NULL; + return 0; + } + *value = res; + return 1; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/vbucket/ketama.c b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/ketama.c new file mode 100644 index 00000000..0b296be0 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/ketama.c @@ -0,0 +1,66 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define PROTOTYPES 1 + +#define MD5Final vb__MD5_final +#define MD5Init vb__MD5Init +#define MD5Update vb__MD5Update + +#include +#include "rfc1321/md5c-inl.h" +#include "hash.h" + +void vb__hash_md5(const char *key, size_t key_length, unsigned char *result) +{ + MD5_CTX ctx; + + MD5Init(&ctx); + MD5Update(&ctx, (unsigned char *)key, (unsigned int)key_length); + MD5Final(result, &ctx); +} + +void* vb__hash_md5_update(void *ctx, const char *key, size_t key_length) +{ + if (ctx == NULL) { + ctx = calloc(1, sizeof(MD5_CTX)); + MD5Init(ctx); + } + MD5Update(ctx, (unsigned char *)key, (unsigned int)key_length); + return ctx; +} + +void vb__hash_md5_final(void *ctx, unsigned char *result) +{ + if (ctx == NULL) { + return; + } + MD5Final(result, ctx); + free(ctx); +} + +uint32_t vb__hash_ketama(const char *key, size_t key_length) +{ + unsigned char digest[16]; + + vb__hash_md5(key, key_length, digest); + + return (uint32_t) ( (digest[3] << 24) + |(digest[2] << 16) + |(digest[1] << 8) + | digest[0]); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/vbucket/rfc1321/global.h b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/rfc1321/global.h new file mode 100644 index 00000000..6a7a790a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/rfc1321/global.h @@ -0,0 +1,32 @@ +/* GLOBAL.H - RSAREF types and constants +*/ + +/* PROTOTYPES should be set to one if and only if the compiler supports + function argument prototyping. + The following makes PROTOTYPES default to 0 if it has not already + been defined with C compiler flags. + */ +#ifndef PROTOTYPES +#define PROTOTYPES 0 +#endif + +#include "config.h" + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; + +/* UINT2 defines a two byte word */ +typedef uint16_t UINT2; + +/* UINT4 defines a four byte word */ +typedef uint32_t UINT4; + +/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. + If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it + returns an empty list. + */ +#if PROTOTYPES +#define PROTO_LIST(list) list +#else +#define PROTO_LIST(list) () +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/vbucket/rfc1321/md5.h b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/rfc1321/md5.h new file mode 100644 index 00000000..3cc7cda5 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/rfc1321/md5.h @@ -0,0 +1,35 @@ +/* MD5.H - header file for MD5C.C +*/ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD5 Message-Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD5 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. + */ + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +void MD5Init PROTO_LIST ((MD5_CTX *)); +void MD5Update PROTO_LIST ((MD5_CTX *, unsigned char *, unsigned int)); +void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); diff --git a/couchbase-sys/libcouchbase-2.7.0/src/vbucket/rfc1321/md5c-inl.h b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/rfc1321/md5c-inl.h new file mode 100644 index 00000000..37b948e1 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/rfc1321/md5c-inl.h @@ -0,0 +1,335 @@ +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm +*/ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD5 Message-Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD5 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. + */ + +#include "global.h" +#include "md5.h" + +/* Constants for MD5Transform routine. +*/ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64])); +static void Encode PROTO_LIST +((unsigned char *, UINT4 *, unsigned int)); +static void Decode PROTO_LIST +((UINT4 *, unsigned char *, unsigned int)); +static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int)); +static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. +*/ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. +*/ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ +} +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ +} +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ +} +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ +} + +/* MD5 initialization. Begins an MD5 operation, writing a new context. +*/ +void MD5Init (context) + MD5_CTX *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. + */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void MD5Update (context, input, inputLen) + MD5_CTX *context; /* context */ + unsigned char *input; /* input block */ + unsigned int inputLen; /* length of input block */ +{ + unsigned int i, ix, partLen; + + /* Compute number of bytes mod 64 */ + ix = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - ix; + + /* Transform as many times as possible. + */ + if (inputLen >= partLen) { + MD5_memcpy + ((POINTER)&context->buffer[ix], (POINTER)input, partLen); + MD5Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + ix = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy + ((POINTER)&context->buffer[ix], (POINTER)&input[i], + inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void MD5Final (digest, context) + unsigned char digest[16]; /* message digest */ + MD5_CTX *context; /* context */ +{ + unsigned char bits[8]; + unsigned int ix, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. + */ + ix = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (ix < 56) ? (56 - ix) : (120 - ix); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. + */ + MD5_memset ((POINTER)context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. +*/ +static void MD5Transform (state, block) + UINT4 state[4]; + unsigned char block[64]; +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. + */ + MD5_memset ((POINTER)x, 0, sizeof (x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void Encode (output, input, len) + unsigned char *output; + UINT4 *input; + unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void Decode (output, input, len) + UINT4 *output; + unsigned char *input; + unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +/* Note: Replace "for loop" with standard memcpy if possible. +*/ + +static void MD5_memcpy (output, input, len) + POINTER output; + POINTER input; + unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = input[i]; +} + +/* Note: Replace "for loop" with standard memset if possible. +*/ +static void MD5_memset (output, value, len) + POINTER output; + int value; + unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/vbucket/vbucket.c b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/vbucket.c new file mode 100644 index 00000000..96fcbda6 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/vbucket/vbucket.c @@ -0,0 +1,1543 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2014 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "contrib/cJSON/cJSON.h" +#include "json-inl.h" +#include "hash.h" +#include "crc32.h" +#include "simplestring.h" + +#define STRINGIFY_(X) #X +#define STRINGIFY(X) STRINGIFY_(X) +#define MAX_AUTHORITY_SIZE 100 +#define SET_ERRSTR(cfg, s) if (!(cfg)->errstr) { \ + (cfg)->errstr = __FILE__ ":" STRINGIFY(__LINE__) " " s ; \ +} + +/****************************************************************************** + ****************************************************************************** + ** Core Parsing Routines ** + ****************************************************************************** + ******************************************************************************/ +static lcbvb_VBUCKET * +build_vbmap(lcbvb_CONFIG *cfg, cJSON *cj, unsigned *nitems) +{ + lcbvb_VBUCKET *vblist = NULL; + cJSON *jvb; + unsigned ii, nalloc; + + /** FIXME: Realloc dynamically when too small */ + if (!(nalloc = cJSON_GetArraySize(cj))) { + goto GT_ERR; + } + + if (!(vblist = calloc(nalloc, sizeof(*vblist)))) { + goto GT_ERR; + } + + /* Iterate over all the vbuckets */ + jvb = cj->child; + for (ii = 0; ii < nalloc && jvb; ++ii, jvb = jvb->next) { + cJSON *jsix; + lcbvb_VBUCKET *cvb; + unsigned jj, nservers; + + if (jvb->type != cJSON_Array) { + goto GT_ERR; + } + + nservers = cJSON_GetArraySize(jvb); + jsix = jvb->child; + cvb = vblist + ii; + + /* Iterate over each index in the vbucket */ + for (jj = 0; jj < nservers && jsix; ++jj, jsix = jsix->next) { + if (jsix->type != cJSON_Number) { + goto GT_ERR; + } + cvb->servers[jj] = jsix->valueint; + if (cvb->servers[jj] > (int)cfg->nsrv-1) { + SET_ERRSTR(cfg, "Invalid vBucket map received from server. Above-bounds vBucket target found"); + goto GT_ERR; + } + } + } + + *nitems = nalloc; + return vblist; + + GT_ERR: + free(vblist); + return NULL; +} + +static lcbvb_SERVER * +find_server_memd(lcbvb_SERVER *servers, unsigned n, const char *s) +{ + unsigned ii; + for (ii = 0; ii < n; ii++) { + char buf[4096] = { 0 }; + lcbvb_SERVER *cur = servers + ii; + snprintf(buf, sizeof(buf), "%s:%d", cur->hostname, cur->svc.data); + if (!strncmp(s, buf, sizeof(buf))) { + return cur; + } + } + return NULL; +} + +static int +assign_dumy_server(lcbvb_CONFIG *cfg, lcbvb_SERVER *dst, const char *s) +{ + int itmp; + char *colon; + if (!(dst->authority = strdup(s))) { + SET_ERRSTR(cfg, "Couldn't allocate authority string"); + goto GT_ERR; + } + + if (!(colon = strstr(s, ":"))) { + SET_ERRSTR(cfg, "Badly formatted name string"); + goto GT_ERR; + } + + if (sscanf(colon+1, "%d", &itmp) != 1) { + SET_ERRSTR(cfg, "Badly formatted port"); + goto GT_ERR; + } + + dst->svc.data = itmp; + return 1; + + GT_ERR: + free(dst->authority); + return 0; +} + +static void +set_vb_count(lcbvb_CONFIG *cfg, lcbvb_VBUCKET *vbs) +{ + unsigned ii, jj; + if (!vbs) { + return; + } + + for (ii = 0; ii < cfg->nvb; ++ii) { + for (jj = 0; jj < cfg->nrepl+1; ++jj) { + int ix = vbs[ii].servers[jj]; + if (ix < 0 || (unsigned)ix > cfg->nsrv) { + continue; + } + cfg->servers[ix].nvbs++; + } + } +} + +static int +pair_server_list(lcbvb_CONFIG *cfg, cJSON *vbconfig) +{ + cJSON *servers; + lcbvb_SERVER *newlist = NULL; + unsigned ii, nsrv; + + if (!get_jarray(vbconfig, "serverList", &servers)) { + SET_ERRSTR(cfg, "Couldn't find serverList"); + goto GT_ERROR; + } + + nsrv = cJSON_GetArraySize(servers); + + if (nsrv > cfg->nsrv) { + /* nodes in serverList which are not in nodes/nodesExt */ + void *tmp = realloc(cfg->servers, sizeof(*cfg->servers) * nsrv); + if (!tmp) { + SET_ERRSTR(cfg, "Couldn't allocate memory for server list"); + goto GT_ERROR; + } + cfg->servers = tmp; + cfg->nsrv = nsrv; + } + + /* allocate an array for the reordered server list */ + newlist = malloc(sizeof(*cfg->servers) * nsrv); + + for (ii = 0; ii < nsrv; ii++) { + char *tmp; + cJSON *jst; + lcbvb_SERVER *cur; + jst = cJSON_GetArrayItem(servers, ii); + tmp = jst->valuestring; + cur = find_server_memd(cfg->servers, cfg->nsrv, tmp); + + if (cur) { + newlist[ii] = *cur; + } else { + /* found server inside serverList but not in nodes? */ + if (!assign_dumy_server(cfg, &newlist[ii], tmp)) { + goto GT_ERROR; + } + } + } + + free(cfg->servers); + cfg->servers = newlist; + return 1; + + GT_ERROR: + free(newlist); + return 0; + +} + +static int +parse_vbucket(lcbvb_CONFIG *cfg, cJSON *cj) +{ + cJSON *vbconfig, *vbmap, *ffmap = NULL; + + if (!get_jobj(cj, "vBucketServerMap", &vbconfig)) { + SET_ERRSTR(cfg, "Expected top-level 'vBucketServerMap'"); + goto GT_ERROR; + } + + if (!get_juint(vbconfig, "numReplicas", &cfg->nrepl)) { + SET_ERRSTR(cfg, "'numReplicas' missing"); + goto GT_ERROR; + } + + if (!get_jarray(vbconfig, "vBucketMap", &vbmap)) { + SET_ERRSTR(cfg, "Missing 'vBucketMap'"); + goto GT_ERROR; + } + + get_jarray(vbconfig, "vBucketMapForward", &ffmap); + + if ((cfg->vbuckets = build_vbmap(cfg, vbmap, &cfg->nvb)) == NULL) { + goto GT_ERROR; + } + + if (ffmap && (cfg->ffvbuckets = build_vbmap(cfg, ffmap, &cfg->nvb)) == NULL) { + goto GT_ERROR; + } + + if (!cfg->is3x) { + if (!pair_server_list(cfg, vbconfig)) { + goto GT_ERROR; + } + } + + /** Now figure out which server goes where */ + set_vb_count(cfg, cfg->vbuckets); + set_vb_count(cfg, cfg->ffvbuckets); + return 1; + + GT_ERROR: + return 0; +} + +static int server_cmp(const void *s1, const void *s2) +{ + return strcmp(((const lcbvb_SERVER *)s1)->authority, + ((const lcbvb_SERVER *)s2)->authority); +} + +static int continuum_item_cmp(const void *t1, const void *t2) +{ + const lcbvb_CONTINUUM *ct1 = t1, *ct2 = t2; + + if (ct1->point == ct2->point) { + return 0; + } else if (ct1->point > ct2->point) { + return 1; + } else { + return -1; + } +} + +static int +update_ketama(lcbvb_CONFIG *cfg) +{ + char host[MAX_AUTHORITY_SIZE+10] = ""; + int nhost; + unsigned pp, hh, ss, nn; + unsigned char digest[16]; + lcbvb_CONTINUUM *new_continuum, *old_continuum; + + qsort(cfg->servers, cfg->ndatasrv, sizeof(*cfg->servers), server_cmp); + + new_continuum = calloc(160 * cfg->ndatasrv, sizeof(*new_continuum)); + /* 40 hashes, 4 numbers per hash = 160 points per server */ + for (ss = 0, pp = 0; ss < cfg->ndatasrv; ++ss) { + /* we can add more points to server which have more memory */ + for (hh = 0; hh < 40; ++hh) { + lcbvb_SERVER *srv = cfg->servers + ss; + nhost = snprintf(host, MAX_AUTHORITY_SIZE+10, "%s-%u", srv->authority, hh); + vb__hash_md5(host, nhost, digest); + for (nn = 0; nn < 4; ++nn, ++pp) { + new_continuum[pp].index = ss; + new_continuum[pp].point = ((uint32_t) (digest[3 + nn * 4] & 0xFF) << 24) + | ((uint32_t) (digest[2 + nn * 4] & 0xFF) << 16) + | ((uint32_t) (digest[1 + nn * 4] & 0xFF) << 8) + | (digest[0 + nn * 4] & 0xFF); + } + } + } + + qsort(new_continuum, pp, sizeof *new_continuum, continuum_item_cmp); + old_continuum = cfg->continuum; + cfg->continuum = new_continuum; + cfg->ncontinuum = pp; + free(old_continuum); + return 1; +} + +static int +extract_services(lcbvb_CONFIG *cfg, cJSON *jsvc, lcbvb_SERVICES *svc, int is_ssl) +{ + int itmp; + int rv; + const char *key; + + #define EXTRACT_SERVICE(k, fld) \ + key = is_ssl ? k"SSL" : k; \ + rv = get_jint(jsvc, key, &itmp); \ + if (rv) { svc->fld = itmp; } else { svc->fld = 0; } + + EXTRACT_SERVICE("kv", data); + EXTRACT_SERVICE("mgmt", mgmt); + EXTRACT_SERVICE("capi", views); + EXTRACT_SERVICE("n1ql", n1ql); + EXTRACT_SERVICE("fts", fts); + EXTRACT_SERVICE("indexAdmin", ixadmin); + EXTRACT_SERVICE("indexScan", ixquery); + + #undef EXTRACT_SERVICE + + (void)cfg; + return 1; +} + +static int +build_server_strings(lcbvb_CONFIG *cfg, lcbvb_SERVER *server) +{ + /* get the authority */ + char tmpbuf[4096]; + + sprintf(tmpbuf, "%s:%d", server->hostname, server->svc.data); + server->authority = strdup(tmpbuf); + if (!server->authority) { + SET_ERRSTR(cfg, "Couldn't allocate authority"); + return 0; + } + + server->svc.hoststrs[LCBVB_SVCTYPE_DATA] = server->authority; + if (server->viewpath == NULL && server->svc.views) { + server->viewpath = malloc(strlen(cfg->bname) + 2); + sprintf(server->viewpath, "/%s", cfg->bname); + } + if (server->querypath == NULL && server->svc.n1ql) { + server->querypath = strdup("/query/service"); + } + if (server->ftspath == NULL && server->svc.fts) { + server->ftspath = strdup("/"); + } + return 1; +} + +/** + * Parse a node from the 'nodesExt' array + * @param cfg + * @param server + * @param js + * @return + */ +static int +build_server_3x(lcbvb_CONFIG *cfg, lcbvb_SERVER *server, cJSON *js) +{ + cJSON *jsvcs; + char *htmp; + + if (!get_jstr(js, "hostname", &htmp)) { + htmp = "$HOST"; + } + if (!(server->hostname = strdup(htmp))) { + SET_ERRSTR(cfg, "Couldn't allocate memory"); + goto GT_ERR; + } + + if (!get_jobj(js, "services", &jsvcs)) { + SET_ERRSTR(cfg, "Couldn't find 'services'"); + goto GT_ERR; + } + + if (!extract_services(cfg, jsvcs, &server->svc, 0)) { + goto GT_ERR; + } + if (!extract_services(cfg, jsvcs, &server->svc_ssl, 1)) { + goto GT_ERR; + } + + if (!build_server_strings(cfg, server)) { + goto GT_ERR; + } + + return 1; + + GT_ERR: + return 0; +} + +/** + * Initialize a server from a JSON Object + * @param server The server to initialize + * @param js The object which contains the server information + * @return nonzero on success, 0 on failure. + */ +static int +build_server_2x(lcbvb_CONFIG *cfg, lcbvb_SERVER *server, cJSON *js) +{ + char *tmp = NULL, *colon; + int itmp; + cJSON *jsports; + + if (!get_jstr(js, "hostname", &tmp)) { + SET_ERRSTR(cfg, "Couldn't find hostname"); + goto GT_ERR; + } + + /** Hostname is the _rest_ API host, e.g. '8091' */ + if ((server->hostname = strdup(tmp)) == NULL) { + SET_ERRSTR(cfg, "Couldn't allocate hostname"); + goto GT_ERR; + } + + colon = strchr(server->hostname, ':'); + if (!colon) { + SET_ERRSTR(cfg, "Expected ':' in 'hostname'"); + goto GT_ERR; + } + if (sscanf(colon+1, "%d", &itmp) != 1) { + SET_ERRSTR(cfg, "Expected port after ':'"); + goto GT_ERR; + } + + /* plain mgmt port is extracted from hostname */ + server->svc.mgmt = itmp; + *colon = '\0'; + + /** Handle the views name */ + if (get_jstr(js, "couchApiBase", &tmp)) { + /** Have views */ + char *path_begin; + colon = strrchr(tmp, ':'); + + if (!colon) { + /* no port */ + goto GT_ERR; + } + if (sscanf(colon+1, "%d", &itmp) != 1) { + goto GT_ERR; + } + + /* Assign the port */ + server->svc.views = itmp; + path_begin = strstr(colon, "/"); + if (!path_begin) { + SET_ERRSTR(cfg, "Expected path in couchApiBase"); + goto GT_ERR; + } + server->viewpath = strdup(path_begin); + } else { + server->svc.views = 0; + } + + /* get the 'ports' dictionary */ + if (!get_jobj(js, "ports", &jsports)) { + SET_ERRSTR(cfg, "Expected 'ports' dictionary"); + goto GT_ERR; + } + + /* memcached port */ + if (get_jint(jsports, "direct", &itmp)) { + server->svc.data = itmp; + } else { + SET_ERRSTR(cfg, "Expected 'direct' field in 'ports'"); + goto GT_ERR; + } + + /* set the authority */ + if (!build_server_strings(cfg, server)) { + goto GT_ERR; + } + return 1; + + GT_ERR: + return 0; +} + +int +lcbvb_load_json(lcbvb_CONFIG *cfg, const char *data) +{ + cJSON *cj = NULL, *jnodes = NULL; + char *tmp = NULL; + unsigned ii; + + if ((cj = cJSON_Parse(data)) == NULL) { + SET_ERRSTR(cfg, "Couldn't parse JSON"); + goto GT_ERROR; + } + + if (!get_jstr(cj, "name", &tmp)) { + SET_ERRSTR(cfg, "Expected 'name' key"); + goto GT_ERROR; + } + cfg->bname = strdup(tmp); + + if (!get_jstr(cj, "nodeLocator", &tmp)) { + SET_ERRSTR(cfg, "Expected 'nodeLocator' key"); + goto GT_ERROR; + } + + if (get_jarray(cj, "nodesExt", &jnodes)) { + cfg->is3x = 1; + } else if (!get_jarray(cj, "nodes", &jnodes)) { + SET_ERRSTR(cfg, "expected 'nodesExt' or 'nodes' array"); + goto GT_ERROR; + } + + if (!strcmp(tmp, "ketama")) { + cfg->dtype = LCBVB_DIST_KETAMA; + } else { + cfg->dtype = LCBVB_DIST_VBUCKET; + } + + if (get_jstr(cj, "uuid", &tmp)) { + cfg->buuid = strdup(tmp); + } + + if (!get_jint(cj, "rev", &cfg->revid)) { + cfg->revid = -1; + } + + /** Get the number of nodes. This traverses the list. Yuck */ + cfg->nsrv = cJSON_GetArraySize(jnodes); + + /** Allocate a temporary one on the heap */ + cfg->servers = calloc(cfg->nsrv, sizeof(*cfg->servers)); + for (ii = 0; ii < cfg->nsrv; ii++) { + int rv; + cJSON *jsrv = cJSON_GetArrayItem(jnodes, ii); + + if (cfg->is3x) { + rv = build_server_3x(cfg, cfg->servers + ii, jsrv); + } else { + rv = build_server_2x(cfg, cfg->servers + ii, jsrv); + } + + if (!rv) { + SET_ERRSTR(cfg, "Failed to build server"); + goto GT_ERROR; + } + } + + /* Count the number of _data_ servers in the cluster. Per the spec, + * these will always appear in order (so that we won't ever have "holes") */ + for (ii = 0; ii < cfg->nsrv; ii++) { + if (!cfg->servers[ii].svc.data) { + break; + } + } + cfg->ndatasrv = ii; + + if (cfg->dtype == LCBVB_DIST_VBUCKET) { + if (!parse_vbucket(cfg, cj)) { + SET_ERRSTR(cfg, "Failed to parse vBucket map"); + goto GT_ERROR; + } + } else { + /* If there is no $HOST then we can update the ketama config, otherwise + * we must wait for the hostname to be replaced! */ + if (strstr(data, "$HOST") == NULL) { + if (!update_ketama(cfg)) { + SET_ERRSTR(cfg, "Failed to establish ketama continuums"); + } + } + } + cfg->servers = realloc(cfg->servers, sizeof(*cfg->servers) * cfg->nsrv); + cfg->randbuf = malloc(cfg->nsrv * sizeof(*cfg->randbuf)); + cJSON_Delete(cj); + return 0; + + GT_ERROR: + if (cj) { + cJSON_Delete(cj); + } + return -1; +} + +static void +replace_hoststr(char **orig, const char *replacement) +{ + char *match; + char *newbuf; + + if (!*orig) { + return; + } + + match = strstr(*orig, "$HOST"); + if (match == NULL || *match == '\0') { + return; + } + + newbuf = malloc(strlen(*orig) + strlen(replacement)); + *match = '\0'; + + /* copy until the placeholder */ + strcpy(newbuf, *orig); + /* copy the host */ + strcat(newbuf, replacement); + /* copy after the placeholder */ + match += sizeof("$HOST")-1; + strcat(newbuf, match); + free(*orig); + *orig = newbuf; +} + +LIBCOUCHBASE_API +void +lcbvb_replace_host(lcbvb_CONFIG *cfg, const char *hoststr) +{ + unsigned ii; + for (ii = 0; ii < cfg->nsrv; ++ii) { + unsigned jj; + lcbvb_SERVER *srv = cfg->servers + ii; + lcbvb_SERVICES *svcs[] = { &srv->svc, &srv->svc_ssl }; + + replace_hoststr(&srv->hostname, hoststr); + for (jj = 0; jj < 2; ++jj) { + unsigned kk; + lcbvb_SERVICES *cursvc = svcs[jj]; + replace_hoststr(&cursvc->views_base_, hoststr); + for (kk = 0; kk < LCBVB_SVCTYPE__MAX; ++kk) { + replace_hoststr(&cursvc->hoststrs[kk], hoststr); + } + } + /* reassign authority */ + srv->authority = srv->svc.hoststrs[LCBVB_SVCTYPE_DATA]; + } + if (cfg->dtype == LCBVB_DIST_KETAMA) { + update_ketama(cfg); + } +} + +lcbvb_CONFIG * +lcbvb_parse_json(const char *js) +{ + int rv; + lcbvb_CONFIG *cfg = calloc(1, sizeof(*cfg)); + rv = lcbvb_load_json(cfg, js); + if (rv) { + lcbvb_destroy(cfg); + return NULL; + } + return cfg; +} + +LIBCOUCHBASE_API +lcbvb_CONFIG * +lcbvb_create(void) +{ + return calloc(1, sizeof(lcbvb_CONFIG)); +} + +static void +free_service_strs(lcbvb_SERVICES *svc) +{ + unsigned ii; + for (ii = 0; ii < LCBVB_SVCTYPE__MAX; ii++) { + free(svc->hoststrs[ii]); + } + free(svc->views_base_); + free(svc->query_base_); + free(svc->fts_base_); +} + +void +lcbvb_destroy(lcbvb_CONFIG *conf) +{ + unsigned ii; + for (ii = 0; ii < conf->nsrv; ii++) { + lcbvb_SERVER *srv = conf->servers + ii; + free(srv->hostname); + free(srv->viewpath); + free(srv->querypath); + free(srv->ftspath); + free_service_strs(&srv->svc); + free_service_strs(&srv->svc_ssl); + } + free(conf->servers); + free(conf->continuum); + free(conf->buuid); + free(conf->bname); + free(conf->vbuckets); + free(conf->ffvbuckets); + free(conf->randbuf); + free(conf); +} + +static void +svcs_to_json(lcbvb_SERVICES *svc, cJSON *jsvc, int is_ssl) +{ + cJSON *tmp; + const char *key; + #define EXTRACT_SERVICE(name, fld) \ + if (svc->fld) { \ + key = is_ssl ? name"SSL" : name; \ + tmp = cJSON_CreateNumber(svc->fld); \ + cJSON_AddItemToObject(jsvc, key, tmp); \ + } + + EXTRACT_SERVICE("mgmt", mgmt); + EXTRACT_SERVICE("capi", views); + EXTRACT_SERVICE("kv", data); + EXTRACT_SERVICE("n1ql", n1ql); + EXTRACT_SERVICE("indexScan", ixquery); + EXTRACT_SERVICE("indexAdmin", ixadmin); + #undef EXTRACT_SERVICE +} + +LIBCOUCHBASE_API +char * +lcbvb_save_json(lcbvb_CONFIG *cfg) +{ + unsigned ii; + char *ret; + cJSON *tmp = NULL, *nodes = NULL; + cJSON *root = cJSON_CreateObject(); + + if (cfg->dtype == LCBVB_DIST_VBUCKET) { + tmp = cJSON_CreateString("vbucket"); + } else { + tmp = cJSON_CreateString("ketama"); + } + cJSON_AddItemToObject(root, "nodeLocator", tmp); + + if (cfg->buuid) { + tmp = cJSON_CreateString(cfg->buuid); + cJSON_AddItemToObject(root, "uuid", tmp); + } + if (cfg->revid > -1) { + tmp = cJSON_CreateNumber(cfg->revid); + cJSON_AddItemToObject(root, "rev", tmp); + } + tmp = cJSON_CreateString(cfg->bname); + cJSON_AddItemToObject(root, "name", tmp); + + nodes = cJSON_CreateArray(); + cJSON_AddItemToObject(root, "nodesExt", nodes); + + for (ii = 0; ii < cfg->nsrv; ii++) { + cJSON *sj = cJSON_CreateObject(), *jsvc = cJSON_CreateObject(); + lcbvb_SERVER *srv = cfg->servers + ii; + + tmp = cJSON_CreateString(srv->hostname); + cJSON_AddItemToObject(sj, "hostname", tmp); + svcs_to_json(&srv->svc, jsvc, 0); + svcs_to_json(&srv->svc_ssl, jsvc, 1); + + /* add the services to the server */ + cJSON_AddItemToObject(sj, "services", jsvc); + cJSON_AddItemToArray(nodes, sj); + } + + /* Now either add the vbucket or ketama stuff */ + if (cfg->dtype == LCBVB_DIST_VBUCKET) { + cJSON *vbroot = cJSON_CreateObject(); + cJSON *vbmap = cJSON_CreateArray(); + + tmp = cJSON_CreateNumber(cfg->nrepl); + cJSON_AddItemToObject(vbroot, "numReplicas", tmp); + + for (ii = 0; ii < cfg->nvb; ii++) { + cJSON *curvb = cJSON_CreateIntArray( + cfg->vbuckets[ii].servers, cfg->nrepl+1); + cJSON_AddItemToArray(vbmap, curvb); + } + + cJSON_AddItemToObject(vbroot, "vBucketMap", vbmap); + cJSON_AddItemToObject(root, "vBucketServerMap", vbroot); + } + + ret = cJSON_PrintUnformatted(root); + cJSON_Delete(root); + return ret; +} + +/****************************************************************************** + ****************************************************************************** + ** Mapping Routines ** + ****************************************************************************** + ******************************************************************************/ + +static int +map_ketama(lcbvb_CONFIG *cfg, const void *key, size_t nkey) +{ + uint32_t digest, mid, prev; + lcbvb_CONTINUUM *beginp, *endp, *midp, *highp, *lowp; + assert(cfg->continuum); + digest = vb__hash_ketama(key, nkey); + beginp = lowp = cfg->continuum; + endp = highp = cfg->continuum + cfg->ncontinuum; + + /* divide and conquer array search to find server with next biggest + * point after what this key hashes to */ + while (1) + { + /* pick the middle point */ + midp = lowp + (highp - lowp) / 2; + + if (midp == endp) { + /* if at the end, roll back to zeroth */ + return beginp->index; + break; + } + + mid = midp->point; + prev = (midp == beginp) ? 0 : (midp-1)->point; + + if (digest <= mid && digest > prev) { + /* we found nearest server */ + return midp->index; + break; + } + + /* adjust the limits */ + if (mid < digest) { + lowp = midp + 1; + } else { + highp = midp - 1; + } + + if (lowp > highp) { + return beginp->index; + break; + } + } + return -1; +} + +int +lcbvb_k2vb(lcbvb_CONFIG *cfg, const void *k, lcb_SIZE n) +{ + uint32_t digest = hash_crc32(k, n); + return digest % cfg->nvb; +} + +int +lcbvb_vbmaster(lcbvb_CONFIG *cfg, int vbid) +{ + return cfg->vbuckets[vbid].servers[0]; +} + +int +lcbvb_vbreplica(lcbvb_CONFIG *cfg, int vbid, unsigned ix) +{ + if (ix < cfg->nrepl) { + return cfg->vbuckets[vbid].servers[ix+1]; + } else { + return -1; + } +} + +/* + * (https://www.couchbase.com/issues/browse/MB-12268?focusedCommentId=101894&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-101894) + * + * So (from my possibly partially ignorant view of that matter) I'd do the following: + * + * 1) Send first request according to lated vbucket map you have. + * If it works, we're good. Exit. + * + * 2) if that fails, look if you've newer vbucket map. If there's newer vbucket map + * and it points to _different_ node, send request to that node and proceed + * to step 3. Otherwise go to step 4 + * + * 3) if newer node still gives you not-my-vbucket, go to step 4 + * + * 4) if there's fast forward map in latest bucket info and fast forward map + * points to different node, send request to that node. And go to step 5. + * Otherwise (not ff map or it points to one of nodes you've tried already), + * go to step 6 + * + * 5) if ff map node request succeeds. Exit. Otherwise go to step 6. + * + * 6) Try first replica unless it's one of nodes you've already tried. + * If it succeeds. Exit. Otherwise go to step 7. + * + * 7) Try all nodes in turn, prioritizing other replicas to beginning of list + * and nodes you have already tried to end. If one of nodes agrees to perform + * your request. Exit. Otherwise propagate error to back to app + */ +int +lcbvb_nmv_remap_ex(lcbvb_CONFIG *cfg, int vbid, int bad, int heuristic) +{ + int cur = cfg->vbuckets[vbid].servers[0]; + int rv = cur; + unsigned ii; + + if (bad != cur) { + return cur; + } + + /* if a forward table exists, then return the vbucket id from the forward table + * and update that information in the current table. We also need to Update the + * replica information for that vbucket */ + + if (cfg->ffvbuckets && + (rv = cfg->ffvbuckets[vbid].servers[0]) != bad && rv > -1) { + memcpy(&cfg->vbuckets[vbid], &cfg->ffvbuckets[vbid], sizeof (lcbvb_VBUCKET)); + } + + /* this path is usually only followed if fvbuckets is not present */ + if (heuristic && cur == bad) { + int validrv = -1; + for (ii = 0; ii < cfg->ndatasrv; ii++) { + rv = (rv + 1) % cfg->ndatasrv; + /* check that the new index has assigned vbuckets (master or replica) */ + if (cfg->servers[rv].nvbs) { + validrv = rv; + cfg->vbuckets[vbid].servers[0] = rv; + break; + } + } + + if (validrv == -1) { + /* this should happen when there is only one valid node remaining + * in the cluster, and we've removed serveral other nodes that are + * still present in the map due to the grace period window.*/ + return -1; + } + } + + if (rv == bad) { + return -1; + } + + return rv; +} + + +int +lcbvb_map_key(lcbvb_CONFIG *cfg, const void *key, lcb_SIZE nkey, + int *vbid, int *srvix) +{ + if (cfg->dtype == LCBVB_DIST_KETAMA) { + *srvix = map_ketama(cfg, key, nkey); + *vbid = 0; + return 0; + } else { + *vbid = lcbvb_k2vb(cfg, key, nkey); + *srvix = lcbvb_vbmaster(cfg, *vbid); + } + return 0; +} + +int +lcbvb_has_vbucket(lcbvb_CONFIG *vbc, int vbid, int ix) +{ + unsigned ii; + lcbvb_VBUCKET *vb = & vbc->vbuckets[vbid]; + for (ii = 0; ii < vbc->nrepl+1; ii++) { + if (vb->servers[ii] == ix) { + return 1; + } + } + return 0; +} + +/****************************************************************************** + ****************************************************************************** + ** Configuration Comparisons/Diffs ** + ****************************************************************************** + ******************************************************************************/ +static void +compute_vb_list_diff(lcbvb_CONFIG *from, lcbvb_CONFIG *to, char **out) +{ + int offset = 0; + unsigned ii, jj; + + for (ii = 0; ii < to->nsrv; ii++) { + int found = 0; + lcbvb_SERVER *newsrv = to->servers + ii; + for (jj = 0; !found && jj < from->nsrv; jj++) { + lcbvb_SERVER *oldsrv = from->servers + jj; + found |= (strcmp(newsrv->authority, oldsrv->authority) == 0); + } + if (!found) { + char *infostr = malloc(strlen(newsrv->authority) + 128); + assert(infostr); + sprintf(infostr, "%s(Data=%d, Index=%d, Query=%d)", + newsrv->authority, + newsrv->svc.data, newsrv->svc.n1ql, newsrv->svc.ixquery); + out[offset] = infostr; + ++offset; + } + } +} + +lcbvb_CONFIGDIFF * +lcbvb_compare(lcbvb_CONFIG *from, lcbvb_CONFIG *to) +{ + int nservers; + lcbvb_CONFIGDIFF *ret; + unsigned ii; + + ret = calloc(1, sizeof(*ret)); + nservers = (from->nsrv > to->nsrv ? from->nsrv : to->nsrv) + 1; + ret->servers_added = calloc(nservers, sizeof(*ret->servers_added)); + ret->servers_removed = calloc(nservers, sizeof(*ret->servers_removed)); + compute_vb_list_diff(from, to, ret->servers_added); + compute_vb_list_diff(to, from, ret->servers_removed); + + if (to->nsrv == from->nsrv) { + for (ii = 0; ii < from->nsrv; ii++) { + const char *sa, *sb; + sa = from->servers[ii].authority; + sb = to->servers[ii].authority; + ret->sequence_changed |= (0 != strcmp(sa, sb)); + } + } else { + ret->sequence_changed = 1; + } + + if (from->nvb == to->nvb) { + for (ii = 0; ii < from->nvb; ii++) { + lcbvb_VBUCKET *vba = from->vbuckets + ii, *vbb = to->vbuckets + ii; + if (vba->servers[0] != vbb->servers[0]) { + ret->n_vb_changes++; + } + } + } else { + ret->n_vb_changes = -1; + } + return ret; +} + +static void +free_array_helper(char **l) +{ + int ii; + for (ii = 0; l[ii]; ii++) { + free(l[ii]); + } + free(l); +} + +void +lcbvb_free_diff(lcbvb_CONFIGDIFF *diff) { + assert(diff); + free_array_helper(diff->servers_added); + free_array_helper(diff->servers_removed); + free(diff); +} + + +lcbvb_CHANGETYPE +lcbvb_get_changetype(lcbvb_CONFIGDIFF *diff) +{ + lcbvb_CHANGETYPE ret = 0; + if (diff->n_vb_changes) { + ret |= LCBVB_MAP_MODIFIED; + } + if (*diff->servers_added || *diff->servers_removed || diff->sequence_changed) { + ret |= LCBVB_SERVERS_MODIFIED; + } + return ret; +} + +/****************************************************************************** + ****************************************************************************** + ** String/Port Getters ** + ****************************************************************************** + ******************************************************************************/ +LIBCOUCHBASE_API +unsigned +lcbvb_get_port(lcbvb_CONFIG *cfg, + unsigned ix, lcbvb_SVCTYPE type, lcbvb_SVCMODE mode) +{ + lcbvb_SERVICES *svc; + lcbvb_SERVER *srv; + if (type >= LCBVB_SVCTYPE__MAX || mode >= LCBVB_SVCMODE__MAX) { + return 0; + } + if (ix >= cfg->nsrv) { + return 0; + } + + srv = cfg->servers + ix; + + if (mode == LCBVB_SVCMODE_PLAIN) { + svc = &srv->svc; + } else { + svc = &srv->svc_ssl; + } + + if (type == LCBVB_SVCTYPE_DATA) { + return svc->data; + } else if (type == LCBVB_SVCTYPE_MGMT) { + return svc->mgmt; + } else if (type == LCBVB_SVCTYPE_VIEWS) { + return svc->views; + } else if (type == LCBVB_SVCTYPE_IXADMIN) { + return svc->ixadmin; + } else if (type == LCBVB_SVCTYPE_IXQUERY) { + return svc->ixquery; + } else if (type == LCBVB_SVCTYPE_N1QL) { + return svc->n1ql; + } else if (type == LCBVB_SVCTYPE_FTS) { + return svc->fts; + } else { + return 0; + } +} + +LIBCOUCHBASE_API +const char * +lcbvb_get_hostport(lcbvb_CONFIG *cfg, + unsigned ix, lcbvb_SVCTYPE type, lcbvb_SVCMODE mode) +{ + char **strp; + lcbvb_SERVER *srv; + lcbvb_SERVICES *svc; + unsigned port = lcbvb_get_port(cfg, ix, type, mode); + + if (!port) { + return NULL; + } + + srv = cfg->servers + ix; + if (mode == LCBVB_SVCMODE_PLAIN) { + svc = &srv->svc; + } else { + svc = &srv->svc_ssl; + } + + strp = &svc->hoststrs[type]; + if (*strp == NULL) { + *strp = malloc(strlen(srv->hostname) + 20); + sprintf(*strp, "%s:%d", srv->hostname, port); + } + return *strp; +} + +LIBCOUCHBASE_API +const char * +lcbvb_get_hostname(const lcbvb_CONFIG *cfg, unsigned ix) +{ + if (cfg->nsrv > ix) { + return cfg->servers[ix].hostname; + } else { + return NULL; + } +} + +LIBCOUCHBASE_API +int +lcbvb_get_randhost_ex(const lcbvb_CONFIG *cfg, + lcbvb_SVCTYPE type, lcbvb_SVCMODE mode, int *used) +{ + size_t nn, oix = 0; + + /* + * Since not all nodes support all service types, we need to make it a + * fair selection by pre-filtering the nodes which actually support the + * service, and then proceed to actually select a suitable node. + */ + for (nn = 0; nn < cfg->nsrv; nn++) { + const lcbvb_SERVER *server = cfg->servers + nn; + const lcbvb_SERVICES *svcs = mode == LCBVB_SVCMODE_PLAIN ? + &server->svc : &server->svc_ssl; + int has_svc = 0; + + // Check if this node is in the exclude list + if (used && used[nn]) { + continue; + } + + has_svc = + (type == LCBVB_SVCTYPE_DATA && svcs->data) || + (type == LCBVB_SVCTYPE_IXADMIN && svcs->ixadmin) || + (type == LCBVB_SVCTYPE_IXQUERY && svcs->ixquery) || + (type == LCBVB_SVCTYPE_MGMT && svcs->mgmt) || + (type == LCBVB_SVCTYPE_N1QL && svcs->n1ql) || + (type == LCBVB_SVCTYPE_FTS && svcs->fts) || + (type == LCBVB_SVCTYPE_VIEWS && svcs->views); + + if (has_svc) { + cfg->randbuf[oix++] = (int)nn; + } + } + + if (!oix) { + /* nothing supports it! */ + return -1; + } + + nn = rand(); + nn %= oix; + return cfg->randbuf[nn]; +} + +LIBCOUCHBASE_API +int +lcbvb_get_randhost(const lcbvb_CONFIG *cfg, + lcbvb_SVCTYPE type, lcbvb_SVCMODE mode) +{ + return lcbvb_get_randhost_ex(cfg, type, mode, NULL); +} + +LIBCOUCHBASE_API +const char * +lcbvb_get_resturl(lcbvb_CONFIG *cfg, unsigned ix, + lcbvb_SVCTYPE svc, lcbvb_SVCMODE mode) +{ + char **strp; + char buf[4096]; + const char *prefix; + const char *path; + + lcbvb_SERVER *srv; + lcbvb_SERVICES *svcs; + unsigned port; + port = lcbvb_get_port(cfg, ix, svc, mode); + if (!port) { + return NULL; + } + + srv = cfg->servers + ix; + if (mode == LCBVB_SVCMODE_PLAIN) { + prefix = "http"; + svcs = &srv->svc; + } else { + prefix = "https"; + svcs = &srv->svc_ssl; + } + + if (svc == LCBVB_SVCTYPE_VIEWS) { + path = srv->viewpath; + strp = &svcs->views_base_; + } else if (svc == LCBVB_SVCTYPE_N1QL) { + path = srv->querypath; + strp = &svcs->query_base_; + } else if (svc == LCBVB_SVCTYPE_FTS) { + path = srv->ftspath; + strp = &svcs->fts_base_; + } else { + /* Unknown service! */ + return NULL; + } + + if (path == NULL) { + return NULL; + } else if (!*strp) { + sprintf(buf, "%s://%s:%d%s", prefix, srv->hostname, port, path); + *strp = strdup(buf); + } + + return *strp; +} + +LIBCOUCHBASE_API +const char * +lcbvb_get_capibase(lcbvb_CONFIG *cfg, unsigned ix, lcbvb_SVCMODE mode) +{ + return lcbvb_get_resturl(cfg, ix, LCBVB_SVCTYPE_VIEWS, mode); +} + +LIBCOUCHBASE_API int lcbvb_get_revision(const lcbvb_CONFIG *cfg) { + return cfg->revid; +} +LIBCOUCHBASE_API unsigned lcbvb_get_nservers(const lcbvb_CONFIG *cfg) { + return cfg->nsrv; +} +LIBCOUCHBASE_API unsigned lcbvb_get_nreplicas(const lcbvb_CONFIG *cfg) { + return cfg->nrepl; +} +LIBCOUCHBASE_API lcbvb_DISTMODE lcbvb_get_distmode(const lcbvb_CONFIG *cfg) { + return cfg->dtype; +} +LIBCOUCHBASE_API const char *lcbvb_get_error(const lcbvb_CONFIG *cfg) { + return cfg->errstr; +} +/****************************************************************************** + ****************************************************************************** + ** Generation Functions ** + ****************************************************************************** + ******************************************************************************/ + +static void +copy_service(const char *hostname, + const lcbvb_SERVICES *src, lcbvb_SERVICES *dst) +{ + char buf[4096]; + *dst = *src; + memset(&dst->hoststrs, 0, sizeof dst->hoststrs); + if (src->views_base_) { + dst->views_base_ = strdup(src->views_base_); + } + if (src->query_base_) { + dst->query_base_ = strdup(src->query_base_); + } + if (src->fts_base_) { + dst->fts_base_ = strdup(src->fts_base_); + } + if (dst->data) { + sprintf(buf, "%s:%d", hostname, dst->data); + dst->hoststrs[LCBVB_SVCTYPE_DATA] = strdup(buf); + } +} + +LIBCOUCHBASE_API +int +lcbvb_genconfig_ex(lcbvb_CONFIG *vb, + const char *name, const char *uuid, + const lcbvb_SERVER *servers, + unsigned nservers, unsigned nreplica, unsigned nvbuckets) +{ + unsigned ii, jj; + int srvix = 0, in_nondata = 0; + + assert(nservers); + + if (!name) { + name = "default"; + } + + memset(vb, 0, sizeof(*vb)); + vb->dtype = LCBVB_DIST_VBUCKET; + vb->nvb = nvbuckets; + vb->nrepl = nreplica; + vb->nsrv = nservers; + vb->bname = strdup(name); + if (uuid) { + vb->buuid = strdup(uuid); + } + + if (nreplica >= nservers) { + vb->errstr = "nservers must be > nreplicas"; + return -1; + } + + if (nreplica > 4) { + vb->errstr = "Replicas must be <= 4"; + return -1; + } + + /* Count the number of data servers.. */ + for (ii = 0; ii < nservers; ii++) { + const lcbvb_SERVER *server = servers + ii; + if (server->svc.data) { + if (in_nondata) { + vb->errstr = "All data servers must be specified before non-data servers"; + return -1; + } + vb->ndatasrv++; + } else { + in_nondata = 1; + } + } + + if (!vb->ndatasrv) { + vb->errstr = "No data servers in list"; + return -1; + } + + vb->vbuckets = malloc(vb->nvb * sizeof(*vb->vbuckets)); + if (!vb->vbuckets) { + vb->errstr = "Couldn't allocate vbucket array"; + return -1; + } + + for (ii = 0; ii < vb->nvb; ii++) { + lcbvb_VBUCKET *cur = vb->vbuckets + ii; + cur->servers[0] = srvix; + for (jj = 1; jj < vb->nrepl+1; jj++) { + cur->servers[jj] = (srvix + jj) % vb->ndatasrv; + } + srvix = (srvix + 1) % vb->ndatasrv; + } + + vb->servers = calloc(vb->nsrv, sizeof(*vb->servers)); + vb->randbuf = calloc(vb->nsrv, sizeof(*vb->randbuf)); + + for (ii = 0; ii < vb->nsrv; ii++) { + lcbvb_SERVER *dst = vb->servers + ii; + const lcbvb_SERVER *src = servers + ii; + + *dst = *src; + dst->hostname = strdup(src->hostname); + if (src->viewpath) { + dst->viewpath = strdup(src->viewpath); + } + if (src->querypath) { + dst->querypath = strdup(src->querypath); + } + if (src->ftspath) { + dst->ftspath = strdup(src->ftspath); + } + + copy_service(src->hostname, &src->svc, &dst->svc); + copy_service(src->hostname, &src->svc_ssl, &dst->svc_ssl); + dst->authority = dst->svc.hoststrs[LCBVB_SVCTYPE_DATA]; + } + + for (ii = 0; ii < vb->nvb; ii++) { + for (jj = 0; jj < vb->nrepl+1; jj++) { + int ix = vb->vbuckets[ii].servers[jj]; + if (ix >= 0) { + vb->servers[ix].nvbs++; + } + } + } + return 0; +} + +int +lcbvb_genconfig(lcbvb_CONFIG *vb, + unsigned nservers, unsigned nreplica, unsigned nvbuckets) +{ + unsigned ii; + int rv; + lcbvb_SERVER *srvarry; + + srvarry = calloc(nservers, sizeof(*srvarry)); + for (ii = 0; ii < nservers; ii++) { + srvarry[ii].svc.data = 1000 + ii; + srvarry[ii].svc.views = 2000 + ii; + srvarry[ii].svc.mgmt = 3000 + ii; + srvarry[ii].hostname = "localhost"; + srvarry[ii].svc.views_base_ = "/default"; + } + rv = lcbvb_genconfig_ex(vb, + "default", NULL, srvarry, nservers, nreplica, nvbuckets); + free(srvarry); + return rv; +} + +void +lcbvb_genffmap(lcbvb_CONFIG *cfg) +{ + size_t ii; + assert(cfg->nrepl); + if (cfg->ffvbuckets) { + free(cfg->ffvbuckets); + } + cfg->ffvbuckets = calloc(cfg->nvb, sizeof *cfg->ffvbuckets); + for (ii = 0; ii < cfg->nvb; ++ii) { + size_t jj; + lcbvb_VBUCKET *vb = cfg->ffvbuckets + ii; + memcpy(vb, cfg->vbuckets + ii, sizeof *vb); + for (jj = 0; jj < cfg->ndatasrv; ++jj) { + vb->servers[jj] = (vb->servers[jj] + 1) % cfg->ndatasrv; + } + } +} + +void +lcbvb_make_ketama(lcbvb_CONFIG *vb) +{ + if (vb->dtype == LCBVB_DIST_KETAMA) { + return; + } + vb->dtype = LCBVB_DIST_KETAMA; + vb->nrepl = 0; + vb->nvb = 0; + update_ketama(vb); +} + + +/****************************************************************************** + ****************************************************************************** + ** Compatibility APIs ** + ****************************************************************************** + ******************************************************************************/ +LIBCOUCHBASE_API lcbvb_CONFIG* vbucket_config_create(void) { + return lcbvb_create(); +} +LIBCOUCHBASE_API void vbucket_config_destroy(lcbvb_CONFIG*h) { + lcbvb_destroy(h); +} +LIBCOUCHBASE_API int vbucket_config_parse(lcbvb_CONFIG*h, vbucket_source_t src, const char *s) { + (void)src; return lcbvb_load_json(h, s); +} +LIBCOUCHBASE_API const char * vbucket_get_error_message(lcbvb_CONFIG*h) { + return h->errstr; +} +LIBCOUCHBASE_API int vbucket_config_get_num_servers(lcbvb_CONFIG *cfg) { + return cfg->nsrv; +} +LIBCOUCHBASE_API int vbucket_config_get_num_replicas(lcbvb_CONFIG *cfg) { + return cfg->nrepl; +} +LIBCOUCHBASE_API int vbucket_config_get_num_vbuckets(lcbvb_CONFIG *cfg) { + return cfg->nvb; +} +LIBCOUCHBASE_API const char *vbucket_config_get_server(lcbvb_CONFIG *cfg, int ix) { + return lcbvb_get_hostport(cfg, ix, LCBVB_SVCTYPE_DATA, LCBVB_SVCMODE_PLAIN); +} +LIBCOUCHBASE_API const char *vbucket_config_get_rest_api_server(lcbvb_CONFIG *cfg, int ix) { + return lcbvb_get_hostport(cfg, ix, LCBVB_SVCTYPE_MGMT, LCBVB_SVCMODE_PLAIN); +} +LIBCOUCHBASE_API const char *vbucket_config_get_couch_api_base(lcbvb_CONFIG *cfg, int ix) { + return lcbvb_get_capibase(cfg, ix, LCBVB_SVCMODE_PLAIN); +} +LIBCOUCHBASE_API lcbvb_DISTMODE vbucket_config_get_distribution_type(lcbvb_CONFIG *cfg) { + return cfg->dtype; +} +LIBCOUCHBASE_API int vbucket_map(lcbvb_CONFIG *cfg, const void *k, lcb_SIZE nk, int *pvb, int *pix) { + return lcbvb_map_key(cfg, k, nk, pvb, pix); +} +LIBCOUCHBASE_API int vbucket_get_vbucket_by_key(lcbvb_CONFIG *cfg, const void *k, lcb_SIZE nk) { + return lcbvb_k2vb(cfg, k, nk); +} +LIBCOUCHBASE_API int vbucket_get_master(lcbvb_CONFIG *cfg, int vb) { + return lcbvb_vbmaster(cfg, vb); +} +LIBCOUCHBASE_API int vbucket_get_replica(lcbvb_CONFIG *cfg, int vb, int repl) { + return lcbvb_vbreplica(cfg, vb, repl); +} +LIBCOUCHBASE_API lcbvb_CONFIGDIFF *vbucket_compare(lcbvb_CONFIG*a,lcbvb_CONFIG*b) { + return lcbvb_compare(a,b); +} +LIBCOUCHBASE_API void vbucket_free_diff(lcbvb_CONFIGDIFF *p) { + lcbvb_free_diff(p); +} +LIBCOUCHBASE_API int vbucket_config_get_revision(lcbvb_CONFIG *p) { + return lcbvb_get_revision(p); +} +LIBCOUCHBASE_API lcbvb_CHANGETYPE vbucket_what_changed(lcbvb_CONFIGDIFF *diff) { + return lcbvb_get_changetype(diff); +} +LIBCOUCHBASE_API int vbucket_config_generate(lcbvb_CONFIG*cfg, unsigned nsrv, unsigned nrepl, unsigned nvb) { + return lcbvb_genconfig(cfg,nsrv,nrepl,nvb); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/views/docreq.c b/couchbase-sys/libcouchbase-2.7.0/src/views/docreq.c new file mode 100644 index 00000000..42105ee2 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/views/docreq.c @@ -0,0 +1,194 @@ +#include "docreq.h" +#include "internal.h" +#include "sllist-inl.h" + +static void docreq_handler(void *arg); +static void invoke_pending(lcb_DOCQUEUE*); +static void doc_callback(lcb_t,int, const lcb_RESPBASE *); + +#define MAX_PENDING_DOCREQ 10 +#define MIN_SCHED_SIZE 5 +#define DOCQ_DELAY_US 200000 + +#define DOCQ_REF(q) (q)->refcount++ +#define DOCQ_UNREF(q) if (!--(q)->refcount) { docq_free(q); } + +lcb_DOCQUEUE * +lcbdocq_create(lcb_t instance) +{ + lcb_DOCQUEUE *q = calloc(1, sizeof *q); + q->timer = lcbio_timer_new(instance->iotable, q, docreq_handler); + q->refcount = 1; + q->instance = instance; + q->max_pending_response = MAX_PENDING_DOCREQ; + q->min_batch_size = MIN_SCHED_SIZE; + return q; +} + +static void +docq_free(lcb_DOCQUEUE *q) +{ + lcbdocq_cancel(q); + lcbio_timer_destroy(q->timer); + free(q); +} + +void +lcbdocq_unref(lcb_DOCQUEUE *q) +{ + DOCQ_UNREF(q); +} + +void +lcbdocq_cancel(lcb_DOCQUEUE *q) +{ + if (!q->cancelled) { + q->cancelled = 1; + } +} + +/* Calling this function ensures that the request will be scheduled in due + * time. This may be done at the next event loop iteration, or after a delay + * depending on how many items are actually found within the queue. */ +static void +docq_poke(lcb_DOCQUEUE *q) +{ + if (q->n_awaiting_response < q->max_pending_response) { + if (q->n_awaiting_schedule > q->min_batch_size) { + lcbio_async_signal(q->timer); + q->cb_throttle(q, 0); + } + } + + if (!lcbio_timer_armed(q->timer)) { + lcbio_timer_rearm(q->timer, DOCQ_DELAY_US); + } +} + +void +lcbdocq_add(lcb_DOCQUEUE *q, lcb_DOCQREQ *req) +{ + sllist_append(&q->pending_gets, &req->slnode); + q->n_awaiting_schedule++; + req->parent = q; + req->ready = 0; + DOCQ_REF(q); + docq_poke(q); +} + +static void +docreq_handler(void *arg) +{ + lcb_DOCQUEUE *q = arg; + sllist_iterator iter; + lcb_t instance = q->instance; + + lcb_sched_enter(instance); + SLLIST_ITERFOR(&q->pending_gets, &iter) { + lcb_DOCQREQ *cont = SLLIST_ITEM(iter.cur, lcb_DOCQREQ, slnode); + + if (q->n_awaiting_response > q->max_pending_response) { + lcbio_timer_rearm(q->timer, DOCQ_DELAY_US); + q->cb_throttle(q, 1); + break; + } + + q->n_awaiting_schedule--; + + if (q->cancelled) { + cont->docresp.rc = LCB_EINTERNAL; + cont->ready = 1; + + } else { + lcb_error_t rc; + lcb_CMDGET gcmd = { 0 }; + + LCB_CMD_SET_KEY(&gcmd, cont->docid.iov_base, cont->docid.iov_len); + cont->callback = doc_callback; + gcmd.cmdflags |= LCB_CMD_F_INTERNAL_CALLBACK; + rc = lcb_get3(instance, &cont->callback, &gcmd); + + if (rc != LCB_SUCCESS) { + cont->docresp.rc = rc; + cont->ready = 1; + } else { + q->n_awaiting_response++; + } + } + sllist_iter_remove(&q->pending_gets, &iter); + sllist_append(&q->cb_queue, &cont->slnode); + } + + lcb_sched_leave(instance); + lcb_sched_flush(instance); + + if (q->n_awaiting_schedule < q->min_batch_size) { + q->cb_throttle(q, 0); + } + + /* Ensure we're called again */ + docq_poke(q); + + /* Flush out any bad responses */ + invoke_pending(q); +} + +/* Invokes the callback on all requests which are ready, until a request which + * is not yet ready is reached. */ +static void +invoke_pending(lcb_DOCQUEUE *q) +{ + sllist_iterator iter = { NULL }; + + DOCQ_REF(q); + SLLIST_ITERFOR(&q->cb_queue, &iter) { + lcb_DOCQREQ *dreq = SLLIST_ITEM(iter.cur, lcb_DOCQREQ, slnode); + void *bufh = NULL; + + if (dreq->ready == 0) { + break; + } + + if (dreq->docresp.rc == LCB_SUCCESS && dreq->docresp.bufh) { + bufh = dreq->docresp.bufh; + } + + sllist_iter_remove(&q->cb_queue, &iter); + + q->cb_ready(q, dreq); + if (bufh) { + lcb_backbuf_unref(bufh); + } + DOCQ_UNREF(q); + } + DOCQ_UNREF(q); +} + +static void +doc_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb) +{ + const lcb_RESPGET *rg = (const lcb_RESPGET *)rb; + lcb_DOCQREQ *dreq = rb->cookie; + lcb_DOCQUEUE *q = dreq->parent; + + DOCQ_REF(q); + + q->n_awaiting_response--; + dreq->docresp = *rg; + dreq->ready = 1; + dreq->docresp.key = dreq->docid.iov_base; + dreq->docresp.nkey = dreq->docid.iov_len; + + /* Reference the response data, since we might not be invoking this right + * away */ + if (rg->rc == LCB_SUCCESS) { + lcb_backbuf_ref(dreq->docresp.bufh); + } + + /* Ensure the invoke_pending doesn't destroy us */ + invoke_pending(q); + docq_poke(q); + + DOCQ_UNREF(q); + (void)instance; (void)cbtype; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/views/docreq.h b/couchbase-sys/libcouchbase-2.7.0/src/views/docreq.h new file mode 100644 index 00000000..fb3bc050 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/views/docreq.h @@ -0,0 +1,83 @@ +/** + * Document request routines + */ +#ifndef LCB_DOCREQ_H +#define LCB_DOCREQ_H + +#include +#include +#include +#include +#include "sllist.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct lcb_DOCQUEUE_st; +struct lcb_DOCREQ_st; + +typedef struct lcb_DOCQUEUE_st{ + lcb_t instance; + void *parent; + lcbio_pTIMER timer; + + /**Called when a document is ready + * @param The queue + * @param The document */ + void (*cb_ready)(struct lcb_DOCQUEUE_st*,struct lcb_DOCREQ_st*); + + /**Called when throttle state changes. This may be used by higher layers + * for appropriate flow control + * @param The queue + * @param enabled Whether throttling has been enabled or disabled */ + void (*cb_throttle)(struct lcb_DOCQUEUE_st*, int enabled); + + /**This queue holds requests which were not yet issued to the library + * via lcb_get3(). This list is aggregated after each chunk callback and + * sent as a batch*/ + sllist_root pending_gets; + + /**This queue holds the requests which were already passed to lcb_get3(). + * It is popped when the callback arrives (and is popped in order!) */ + sllist_root cb_queue; + + unsigned n_awaiting_schedule; + unsigned n_awaiting_response; + + unsigned max_pending_response; + unsigned min_batch_size; + unsigned cancelled; + unsigned refcount; +} lcb_DOCQUEUE; + +typedef struct lcb_DOCREQ_st { + /* Callback. Must be first */ + lcb_RESPCALLBACK callback; + sllist_node slnode; + lcb_DOCQUEUE *parent; + lcb_RESPGET docresp; + /* To be filled in by the subclass */ + lcb_IOV docid; + unsigned ready; +} lcb_DOCQREQ; + +lcb_DOCQUEUE * +lcbdocq_create(lcb_t instance); + +void +lcbdocq_add(lcb_DOCQUEUE *q, lcb_DOCQREQ *req); + +void +lcbdocq_unref(lcb_DOCQUEUE *); + +void +lcbdocq_cancel(lcb_DOCQUEUE *q); + +#define lcbdocq_has_pending(q) \ + ((q)->n_awaiting_response || (q)->n_awaiting_schedule) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/src/views/viewreq.c b/couchbase-sys/libcouchbase-2.7.0/src/views/viewreq.c new file mode 100644 index 00000000..0f433476 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/views/viewreq.c @@ -0,0 +1,358 @@ +#include "viewreq.h" +#include "sllist-inl.h" +#include "http/http.h" +#include "internal.h" + +#define MAX_GET_URI_LENGTH 2048 + +static void chunk_callback(lcb_t, int, const lcb_RESPBASE*); +static void row_callback(lcbjsp_PARSER*, const lcbjsp_ROW*); +static void invoke_row(lcbview_REQUEST *req, lcb_RESPVIEWQUERY *resp); +static void unref_request(lcbview_REQUEST *req); + +#define IOV2PTRLEN(iov, ptr, len) do { \ + ptr = (iov)->iov_base; \ + len = (iov)->iov_len; \ +} while (0); + +/* Whether the request (from the user side) is still ongoing */ +#define CAN_CONTINUE(req) ((req)->callback != NULL) +#define LOGARGS(instance, lvl) instance->settings, "views", LCB_LOG_##lvl, __FILE__, __LINE__ + +static void +invoke_last(lcbview_REQUEST *req, lcb_error_t err) +{ + lcb_RESPVIEWQUERY resp = { 0 }; + if (req->callback == NULL) { + return; + } + if (req->docq && lcbdocq_has_pending(req->docq)) { + return; + } + + resp.rc = err; + resp.htresp = req->cur_htresp; + resp.cookie = (void *)req->cookie; + resp.rflags = LCB_RESP_F_FINAL; + if (req->parser && req->parser->meta_complete) { + resp.value = req->parser->meta_buf.base; + resp.nvalue = req->parser->meta_buf.nused; + } else { + resp.rflags |= LCB_RESP_F_CLIENTGEN; + } + req->callback(req->instance, LCB_CALLBACK_VIEWQUERY, &resp); + lcb_view_cancel(req->instance, req); +} + +static void +invoke_row(lcbview_REQUEST *req, lcb_RESPVIEWQUERY *resp) +{ + if (req->callback == NULL) { + return; + } + resp->htresp = req->cur_htresp; + resp->cookie = (void *)req->cookie; + req->callback(req->instance, LCB_CALLBACK_VIEWQUERY, resp); +} + +static void +chunk_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb) +{ + const lcb_RESPHTTP *rh = (const lcb_RESPHTTP *)rb; + lcbview_REQUEST *req = rh->cookie; + + (void)cbtype; + req->cur_htresp = rh; + + if (rh->rc != LCB_SUCCESS || rh->htstatus != 200 || (rh->rflags & LCB_RESP_F_FINAL)) { + if (req->lasterr == LCB_SUCCESS && rh->htstatus != 200) { + if (rh->rc != LCB_SUCCESS) { + req->lasterr = rh->rc; + } else { + lcb_log(LOGARGS(instance, DEBUG), "Got not ok http status %d", rh->htstatus); + req->lasterr = LCB_HTTP_ERROR; + } + } + req->refcount++; + invoke_last(req, req->lasterr); + if (rh->rflags & LCB_RESP_F_FINAL) { + req->htreq = NULL; + unref_request(req); + } + req->cur_htresp = NULL; + unref_request(req); + return; + } + + if (!CAN_CONTINUE(req)) { + return; + } + + req->refcount++; + lcbjsp_feed(req->parser, rh->body, rh->nbody); + req->cur_htresp = NULL; + unref_request(req); + (void)instance; +} + +static void +do_copy_iov(char *dstbuf, lcb_IOV *dstiov, const lcb_IOV *srciov, size_t *off) +{ + dstiov->iov_len = srciov->iov_len; + dstiov->iov_base = dstbuf + *off; + *off += dstiov->iov_len; + memcpy(dstiov->iov_base, srciov->iov_base, srciov->iov_len); +} + +static lcbview_DOCREQ * +mk_docreq(const lcbjsp_ROW *datum) +{ + size_t extra_alloc = 0; + size_t cur_offset = 0; + lcbview_DOCREQ *dreq; + + extra_alloc = + datum->key.iov_len + datum->value.iov_len + + datum->geo.iov_len + datum->docid.iov_len; + + dreq = calloc(1, sizeof(*dreq) + extra_alloc); + do_copy_iov(dreq->rowbuf, &dreq->key, &datum->key, &cur_offset); + do_copy_iov(dreq->rowbuf, &dreq->value, &datum->value, &cur_offset); + do_copy_iov(dreq->rowbuf, &dreq->base.docid, &datum->docid, &cur_offset); + do_copy_iov(dreq->rowbuf, &dreq->geo, &datum->geo, &cur_offset); + return dreq; +} + +static void +row_callback(lcbjsp_PARSER *parser, const lcbjsp_ROW *datum) +{ + lcbview_REQUEST *req = parser->data; + if (datum->type == LCBJSP_TYPE_ROW) { + if (!req->no_parse_rows) { + lcbjsp_parse_viewrow(req->parser, (lcbjsp_ROW*)datum); + } + + if (req->include_docs && datum->docid.iov_len && req->callback) { + lcbview_DOCREQ *dreq = mk_docreq(datum); + dreq->parent = req; + lcbdocq_add(req->docq, &dreq->base); + req->refcount++; + + } else { + lcb_RESPVIEWQUERY resp = { 0 }; + if (req->no_parse_rows) { + IOV2PTRLEN(&datum->row, resp.value, resp.nvalue); + } else { + IOV2PTRLEN(&datum->key, resp.key, resp.nkey); + IOV2PTRLEN(&datum->docid, resp.docid, resp.ndocid); + IOV2PTRLEN(&datum->value, resp.value, resp.nvalue); + IOV2PTRLEN(&datum->geo, resp.geometry, resp.ngeometry); + } + resp.htresp = req->cur_htresp; + invoke_row(req, &resp); + } + } else if (datum->type == LCBJSP_TYPE_ERROR) { + invoke_last(req, LCB_PROTOCOL_ERROR); + } else if (datum->type == LCBJSP_TYPE_COMPLETE) { + /* nothing */ + } +} + +static void +cb_doc_ready(lcb_DOCQUEUE *q, lcb_DOCQREQ *req_base) +{ + lcb_RESPVIEWQUERY resp = { 0 }; + lcbview_DOCREQ *dreq = (lcbview_DOCREQ*)req_base; + resp.docresp = &dreq->base.docresp; + IOV2PTRLEN(&dreq->key, resp.key, resp.nkey); + IOV2PTRLEN(&dreq->value, resp.value, resp.nvalue); + IOV2PTRLEN(&dreq->base.docid, resp.docid, resp.ndocid); + IOV2PTRLEN(&dreq->geo, resp.geometry, resp.ngeometry); + + if (q->parent) { + invoke_row(q->parent, &resp); + } + + free(dreq); + + if (q->parent) { + unref_request(q->parent); + } +} + +static void +cb_docq_throttle(lcb_DOCQUEUE *q, int enabled) +{ + lcbview_REQUEST *req = q->parent; + if (req == NULL || req->htreq == NULL) { + return; + } + if (enabled) { + lcb_htreq_pause(req->htreq); + } else { + lcb_htreq_resume(req->htreq); + } +} + +static void +destroy_request(lcbview_REQUEST *req) +{ + invoke_last(req, req->lasterr); + + if (req->parser != NULL) { + lcbjsp_free(req->parser); + } + if (req->htreq != NULL) { + lcb_cancel_http_request(req->instance, req->htreq); + } + if (req->docq != NULL) { + req->docq->parent = NULL; + lcbdocq_unref(req->docq); + } + free(req); +} + +static void +unref_request(lcbview_REQUEST *req) +{ + if (!--req->refcount) { + destroy_request(req); + } +} + +LIBCOUCHBASE_API +lcb_error_t +lcb_view_query(lcb_t instance, const void *cookie, const lcb_CMDVIEWQUERY *cmd) +{ + lcb_string path; + lcb_CMDHTTP htcmd = { 0 }; + lcb_error_t rc; + lcbview_REQUEST *req = NULL; + int include_docs = 0; + int no_parse_rows = 0; + const char *vpstr = NULL; + + if (cmd->nddoc == 0 || cmd->nview == 0 || cmd->callback == NULL) { + return LCB_EINVAL; + } + + htcmd.method = LCB_HTTP_METHOD_GET; + htcmd.type = LCB_HTTP_TYPE_VIEW; + htcmd.cmdflags = LCB_CMDHTTP_F_STREAM; + + include_docs = cmd->cmdflags & LCB_CMDVIEWQUERY_F_INCLUDE_DOCS; + no_parse_rows = cmd->cmdflags & LCB_CMDVIEWQUERY_F_NOROWPARSE; + + if (include_docs && no_parse_rows) { + return LCB_OPTIONS_CONFLICT; + } + + lcb_string_init(&path); + if (cmd->cmdflags & LCB_CMDVIEWQUERY_F_SPATIAL) { + vpstr = "/_spatial/"; + } else { + vpstr = "/_view/"; + } + + if (lcb_string_appendv(&path, + "_design/", (size_t)-1, cmd->ddoc, cmd->nddoc, + vpstr, (size_t)-1, cmd->view, cmd->nview, NULL) != 0) { + + lcb_string_release(&path); + return LCB_CLIENT_ENOMEM; + } + + if (cmd->optstr) { + if (cmd->noptstr > MAX_GET_URI_LENGTH) { + return LCB_E2BIG; + } else { + if (lcb_string_appendv(&path, + "?", (size_t)-1, cmd->optstr, cmd->noptstr, NULL) != 0) { + + lcb_string_release(&path); + return LCB_CLIENT_ENOMEM; + } + } + } + + if (cmd->npostdata) { + htcmd.method = LCB_HTTP_METHOD_POST; + htcmd.body = cmd->postdata; + htcmd.nbody = cmd->npostdata; + htcmd.content_type = "application/json"; + } + + if ( (req = calloc(1, sizeof(*req))) == NULL || + (req->parser = lcbjsp_create(LCBJSP_MODE_VIEWS)) == NULL) { + free(req); + lcb_string_release(&path); + return LCB_CLIENT_ENOMEM; + } + + req->instance = instance; + req->cookie = cookie; + req->include_docs = include_docs; + req->no_parse_rows = no_parse_rows; + req->callback = cmd->callback; + req->parser->callback = row_callback; + req->parser->data = req; + + LCB_CMD_SET_KEY(&htcmd, path.base, path.nused); + htcmd.reqhandle = &req->htreq; + + rc = lcb_http3(instance, req, &htcmd); + lcb_string_release(&path); + + if (rc == LCB_SUCCESS) { + lcb_htreq_setcb(req->htreq, chunk_callback); + req->refcount++; + if (cmd->handle) { + *cmd->handle = req; + } + if (include_docs) { + req->docq = lcbdocq_create(instance); + req->docq->parent = req; + req->docq->cb_ready = cb_doc_ready; + req->docq->cb_throttle = cb_docq_throttle; + if (cmd->docs_concurrent_max) { + req->docq->max_pending_response = cmd->docs_concurrent_max; + } + } + lcb_aspend_add(&instance->pendops, LCB_PENDTYPE_COUNTER, NULL); + } else { + req->callback = NULL; + destroy_request(req); + } + return rc; +} + +LIBCOUCHBASE_API +void +lcb_view_query_initcmd(lcb_CMDVIEWQUERY *vq, + const char *design, const char *view, const char *options, + lcb_VIEWQUERYCALLBACK callback) +{ + vq->view = view; + vq->nview = strlen(view); + vq->ddoc = design; + vq->nddoc = strlen(design); + if (options != NULL) { + vq->optstr = options; + vq->noptstr = strlen(options); + } + vq->callback = callback; +} + +LIBCOUCHBASE_API +void +lcb_view_cancel(lcb_t instance, lcb_VIEWHANDLE handle) +{ + if (handle->callback) { + handle->callback = NULL; + lcb_aspend_del(&instance->pendops, LCB_PENDTYPE_COUNTER, NULL); + if (handle->docq) { + lcbdocq_cancel(handle->docq); + } + } + (void)instance; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/src/views/viewreq.h b/couchbase-sys/libcouchbase-2.7.0/src/views/viewreq.h new file mode 100644 index 00000000..2e063e85 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/views/viewreq.h @@ -0,0 +1,36 @@ +#include +#include +#include + +#include +#include "docreq.h" + +struct lcbview_REQUEST_st; + +typedef struct { + lcb_DOCQREQ base; + struct lcbview_REQUEST_st *parent; + lcb_IOV key; + lcb_IOV value; + lcb_IOV geo; + char rowbuf[1]; +} lcbview_DOCREQ; + +struct lcbview_REQUEST_st { + /** Current HTTP response to provide in callbacks */ + const lcb_RESPHTTP *cur_htresp; + /** HTTP request object, in case we need to cancel prematurely */ + struct lcb_http_request_st *htreq; + lcbjsp_PARSER *parser; + const void *cookie; + lcb_DOCQUEUE *docq; + lcb_VIEWQUERYCALLBACK callback; + lcb_t instance; + + unsigned refcount; + unsigned include_docs; + unsigned no_parse_rows; + lcb_error_t lasterr; +}; + +typedef struct lcbview_REQUEST_st lcbview_REQUEST; diff --git a/couchbase-sys/libcouchbase-2.7.0/src/wait.cc b/couchbase-sys/libcouchbase-2.7.0/src/wait.cc new file mode 100644 index 00000000..bd5a2ee3 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/src/wait.cc @@ -0,0 +1,156 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "internal.h" +#include +#include + +static bool +has_pending(lcb_t instance) +{ + + if (!instance->retryq->empty()) { + return true; + } + + if (lcb_aspend_pending(&instance->pendops)) { + return true; + } + + for (size_t ii = 0; ii < LCBT_NSERVERS(instance); ii++) { + if (instance->get_server(ii)->has_pending()) { + return true; + } + } + return false; +} + +static void +maybe_reset_timeouts(lcb_t instance) +{ + + if (!LCBT_SETTING(instance, readj_ts_wait)) { + return; + } + + uint64_t now = lcb_nstime(); + for (size_t ii = 0; ii < LCBT_NSERVERS(instance); ++ii) { + mcreq_reset_timeouts(instance->get_server(ii), now); + } + instance->retryq->reset_timeouts(now); +} + +void +lcb_maybe_breakout(lcb_t instance) +{ + if (!instance->wait) { + return; + } + if (has_pending(instance)) { + return; + } + + instance->wait = 0; + instance->iotable->loop.stop(IOT_ARG(instance->iotable)); +} + +/** + * Returns non zero if the event loop is running now + * + * @param instance the instance to run the event loop for. + */ +LIBCOUCHBASE_API +int lcb_is_waiting(lcb_t instance) +{ + return instance->wait != 0; +} + +/** + * Run the event loop until we've got a response for all of our spooled + * commands. You should not call this function from within your callbacks. + * + * @param instance the instance to run the event loop for. + * + * @author Trond Norbye + */ +LIBCOUCHBASE_API +lcb_error_t lcb_wait(lcb_t instance) +{ + if (instance->wait != 0) { + return instance->last_error; + } + + if (!has_pending(instance)) { + return LCB_SUCCESS; + } + + maybe_reset_timeouts(instance); + instance->last_error = LCB_SUCCESS; + instance->wait = 1; + IOT_START(instance->iotable); + instance->wait = 0; + + if (LCBT_VBCONFIG(instance)) { + return LCB_SUCCESS; + } + + return instance->last_error; +} + +LIBCOUCHBASE_API +lcb_error_t lcb_tick_nowait(lcb_t instance) +{ + lcb_io_tick_fn tick = instance->iotable->loop.tick; + if (!tick) { + return LCB_CLIENT_FEATURE_UNAVAILABLE; + } else { + maybe_reset_timeouts(instance); + tick(IOT_ARG(instance->iotable)); + return LCB_SUCCESS; + } +} + +LIBCOUCHBASE_API +void lcb_wait3(lcb_t instance, lcb_WAITFLAGS flags) +{ + if (flags == LCB_WAIT_DEFAULT) { + if (instance->wait) { + return; + } + if (has_pending(instance)) { + return; + } + } + + maybe_reset_timeouts(instance); + instance->wait = 1; + IOT_START(instance->iotable); + instance->wait = 0; +} + +/** + * Stop event loop + * + * @param instance the instance to run the event loop for. + */ +LIBCOUCHBASE_API +void lcb_breakout(lcb_t instance) +{ + if (instance->wait) { + IOT_STOP(instance->iotable); + instance->wait = 0; + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/CMakeLists.txt b/couchbase-sys/libcouchbase-2.7.0/tests/CMakeLists.txt new file mode 100644 index 00000000..031b1f76 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/CMakeLists.txt @@ -0,0 +1,140 @@ +# For Windows+GCC we need to link statically +IF (NOT (WIN32 AND CMAKE_COMPILER_IS_GNUCC)) + SET(gtest_force_shared_crt ON CACHE BOOL "" ) + SET(BUILD_SHARED_LIBS ON) +ENDIF() +ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/contrib/gtest-1.7.0 gtest) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +ADD_EXECUTABLE(check-all EXCLUDE_FROM_ALL + check-all.cc $ mocksupport/procutil.c) + +SET_PROPERTY(TARGET check-all + APPEND PROPERTY INCLUDE_DIRECTORIES + ${lcb_plat_includes} + ${SOURCE_ROOT}) +TARGET_LINK_LIBRARIES(check-all couchbaseS) + + +FILE(GLOB T_BASIC_SRC basic/*.cc) +FILE(GLOB T_MC_SRC mc/*.cc) +FILE(GLOB T_RDB_SRC rdb/*.cc) +FILE(GLOB T_SOCK_SRC socktests/*.cc) +FILE(GLOB T_IOSERVER_SRC ioserver/*.cc) +FILE(GLOB T_MOCKSUPPORT_SRC mocksupport/*.c mocksupport/*.cc) +FILE(GLOB T_VBTEST_SRC vbucket/*.cc) + +ADD_LIBRARY(ioserver OBJECT EXCLUDE_FROM_ALL ${T_IOSERVER_SRC}) +IF(NOT LCB_NO_SSL) + GET_TARGET_PROPERTY(_ioserver_includes ioserver INCLUDE_DIRECTORIES) + SET_TARGET_PROPERTIES(ioserver PROPERTIES INCLUDE_DIRECTORIES "${_ioserver_includes};${OPENSSL_INCLUDE_DIR}") +ENDIF() + + +ADD_EXECUTABLE(nonio-tests EXCLUDE_FROM_ALL nonio_tests.cc ${T_BASIC_SRC}) + +ADD_EXECUTABLE(mc-tests EXCLUDE_FROM_ALL nonio_tests.cc ${T_MC_SRC} + $ $ $) + +ADD_EXECUTABLE(mc-malloc-tests EXCLUDE_FROM_ALL nonio_tests.cc ${T_MC_SRC} + $ $ $) + +ADD_EXECUTABLE(netbuf-tests + EXCLUDE_FROM_ALL nonio_tests.cc basic/t_netbuf.cc $) + +ADD_EXECUTABLE(rdb-tests EXCLUDE_FROM_ALL nonio_tests.cc + ${T_RDB_SRC} $ ${SOURCE_ROOT}/src/list.c) + +ADD_EXECUTABLE(sock-tests EXCLUDE_FROM_ALL nonio_tests.cc + ${T_SOCK_SRC} $) + +ADD_EXECUTABLE(vbucket-tests EXCLUDE_FROM_ALL nonio_tests.cc ${T_VBTEST_SRC}) +ADD_EXECUTABLE(htparse-tests EXCLUDE_FROM_ALL nonio_tests.cc htparse/t_basic.cc) + +FILE(GLOB T_IO_SRC iotests/*.cc) +IF(LCB_NO_MOCK) + ADD_EXECUTABLE(unit-tests EXCLUDE_FROM_ALL unit_tests.cc) + SET_TARGET_PROPERTIES(unit-tests PROPERTIES COMPILE_DEFINITIONS NO_COUCHBASEMOCK) +ELSE() + ADD_LIBRARY(mocksupport OBJECT EXCLUDE_FROM_ALL ${T_MOCKSUPPORT_SRC}) + ADD_EXECUTABLE(unit-tests EXCLUDE_FROM_ALL ${T_IO_SRC} unit_tests.cc $) +ENDIF() + +IF(CMAKE_COMPILER_IS_GNUCXX) + SET_TARGET_PROPERTIES(unit-tests + PROPERTIES + COMPILE_FLAGS "-Wno-sign-compare -Wno-missing-field-initializers") +ENDIF(CMAKE_COMPILER_IS_GNUCXX) + +IF(NOT EXISTS ${PROJECT_SOURCE_DIR}/tests/CouchbaseMock.jar AND NOT LCB_NO_MOCK) + DOWNLOAD_LCB_DEP("${COUCHBASE_MOCK_DLSERVER}/${COUCHBASE_MOCK_VERSION}" + "${PROJECT_SOURCE_DIR}/tests/CouchbaseMock.jar") +ENDIF(NOT EXISTS ${PROJECT_SOURCE_DIR}/tests/CouchbaseMock.jar AND NOT LCB_NO_MOCK) + +# We're a library using the shared CRT. Don't use the static one +INCLUDE_DIRECTORIES(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) +TARGET_LINK_LIBRARIES(unit-tests couchbaseS gtest) +TARGET_LINK_LIBRARIES(nonio-tests couchbaseS gtest) +TARGET_LINK_LIBRARIES(mc-tests gtest ${LCB_SNAPPY_LINK}) +TARGET_LINK_LIBRARIES(mc-malloc-tests + gtest ${LCB_SNAPPY_LINK}) +TARGET_LINK_LIBRARIES(netbuf-tests gtest) +TARGET_LINK_LIBRARIES(rdb-tests gtest) +TARGET_LINK_LIBRARIES(sock-tests couchbaseS gtest) +TARGET_LINK_LIBRARIES(vbucket-tests gtest couchbaseS) +TARGET_LINK_LIBRARIES(htparse-tests gtest couchbaseS) + +IF(WIN32) + TARGET_LINK_LIBRARIES(mc-tests ws2_32.lib) + TARGET_LINK_LIBRARIES(mc-malloc-tests ws2_32.lib) +ENDIF() + +ADD_CUSTOM_TARGET(alltests DEPENDS check-all unit-tests nonio-tests + rdb-tests sock-tests vbucket-tests mc-tests htparse-tests) + +ADD_TEST(NAME BUILD-TESTS COMMAND ${CMAKE_COMMAND} --build "${PROJECT_BINARY_DIR}" --target alltests) + +MACRO(DEFINE_MOCKTEST plugin test) + ADD_TEST( + NAME + check-${plugin}-${test} + COMMAND + $ + --srcdir "${PROJECT_SOURCE_DIR}" + --testdir "$" + --libdir "$" + --plugins ${plugin} + --tests ${test} + --verbose + -- + --gtest_print_time=1 + --gtest_output=xml:"${PROJECT_BINARY_DIR}/REPORT_${plugin}_${test}.xml") +ENDMACRO() + +# Since we need a plugin name, we'll use 'select'. However none of these +# actually do any I/O +DEFINE_MOCKTEST("select" "nonio-tests") +DEFINE_MOCKTEST("select" "rdb-tests") +DEFINE_MOCKTEST("select" "vbucket-tests") +DEFINE_MOCKTEST("select" "mc-tests") +DEFINE_MOCKTEST("select" "htparse-tests") + + +DEFINE_MOCKTEST("select" "unit-tests") +DEFINE_MOCKTEST("select" "sock-tests") +IF(WIN32) + DEFINE_MOCKTEST("iocp" "unit-tests") + DEFINE_MOCKTEST("iocp" "sock-tests") +ENDIF() +IF(HAVE_LIBEVENT AND LCB_BUILD_LIBEVENT) + DEFINE_MOCKTEST("libevent" "unit-tests") + DEFINE_MOCKTEST("libevent" "sock-tests") +ENDIF() +IF(HAVE_LIBEV AND LCB_BUILD_LIBEV) + DEFINE_MOCKTEST("libev" "unit-tests") + DEFINE_MOCKTEST("libev" "sock-tests") +ENDIF() +IF(HAVE_LIBUV AND LCB_BUILD_LIBUV) + DEFINE_MOCKTEST("libuv" "unit-tests") + DEFINE_MOCKTEST("libuv" "sock-tests") +ENDIF() diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_base64.cc b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_base64.cc new file mode 100644 index 00000000..25ed8747 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_base64.cc @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2011-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include + +extern "C" { + extern int lcb_base64_encode(const char *src, char *dst, + size_t sz); +} + +class Base64 : public ::testing::Test +{ +protected: + void validate(const char *src, const char *result) { + char dest[1024]; + ASSERT_NE(-1, lcb_base64_encode(src, dest, sizeof(dest))); + EXPECT_STREQ(result, dest); + } +}; + +TEST_F(Base64, testRFC4648) +{ + validate("", ""); + validate("f", "Zg=="); + validate("fo", "Zm8="); + validate("foo", "Zm9v"); + validate("foob", "Zm9vYg=="); + validate("fooba", "Zm9vYmE="); + validate("foobar", "Zm9vYmFy"); +} + +TEST_F(Base64, testWikipediaExample) +{ + /* Examples from http://en.wikipedia.org/wiki/Base64 */ + validate("Man is distinguished, not only by his reason, but by this singular " + "passion from other animals, which is a lust of the mind, that by a " + "perseverance of delight in the continued and indefatigable generation" + " of knowledge, exceeds the short vehemence of any carnal pleasure.", + "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz" + "IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg" + "dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu" + "dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo" + "ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4="); + validate("pleasure.", "cGxlYXN1cmUu"); + validate("leasure.", "bGVhc3VyZS4="); + validate("easure.", "ZWFzdXJlLg=="); + validate("asure.", "YXN1cmUu"); + validate("sure.", "c3VyZS4="); +} + + +TEST_F(Base64, testStuff) +{ + // Dummy test data. It looks like the "base64" command line + // utility from gnu coreutils adds the "\n" to the encoded data... + validate("Administrator:password", "QWRtaW5pc3RyYXRvcjpwYXNzd29yZA=="); + validate("@", "QA=="); + validate("@\n", "QAo="); + validate("@@", "QEA="); + validate("@@\n", "QEAK"); + validate("@@@", "QEBA"); + validate("@@@\n", "QEBACg=="); + validate("@@@@", "QEBAQA=="); + validate("@@@@\n", "QEBAQAo="); + validate("blahblah:bla@@h", "YmxhaGJsYWg6YmxhQEBo"); + validate("blahblah:bla@@h\n", "YmxhaGJsYWg6YmxhQEBoCg=="); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_ccbc103.cc b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_ccbc103.cc new file mode 100644 index 00000000..5d45c8ca --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_ccbc103.cc @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include +#include +#include "list.h" + + +class CCBC_103 : public ::testing::Test +{ +}; + +typedef struct { + lcb_list_t list; +} event_t; + +typedef struct { + event_t events; +} io_cookie; + +TEST_F(CCBC_103, lists) +{ + io_cookie instance; + event_t e1, e2, e3, e4; + + lcb_list_init(&instance.events.list); + lcb_list_append(&instance.events.list, &e1.list); + ASSERT_EQ(&e1.list, instance.events.list.prev); + + lcb_list_append(&instance.events.list, &e2.list); + ASSERT_EQ(&e2.list, instance.events.list.prev); + + lcb_list_append(&instance.events.list, &e3.list); + ASSERT_EQ(&e3.list, instance.events.list.prev); + + lcb_list_append(&instance.events.list, &e4.list); + ASSERT_EQ(&e4.list, instance.events.list.prev); + + ASSERT_EQ(1, lcb_list_contains(&instance.events.list, &e1.list)); + ASSERT_EQ(1, lcb_list_contains(&instance.events.list, &e2.list)); + ASSERT_EQ(1, lcb_list_contains(&instance.events.list, &e3.list)); + ASSERT_EQ(1, lcb_list_contains(&instance.events.list, &e4.list)); + + // Try to unlink the one in the middle + lcb_list_delete(&e2.list); + ASSERT_EQ(1, lcb_list_contains(&instance.events.list, &e1.list)); + ASSERT_EQ(0, lcb_list_contains(&instance.events.list, &e2.list)); + ASSERT_EQ(1, lcb_list_contains(&instance.events.list, &e3.list)); + ASSERT_EQ(1, lcb_list_contains(&instance.events.list, &e4.list)); + + // Try to unlink the last one + lcb_list_delete(&e1.list); + ASSERT_EQ(0, lcb_list_contains(&instance.events.list, &e1.list)); + ASSERT_EQ(0, lcb_list_contains(&instance.events.list, &e2.list)); + ASSERT_EQ(1, lcb_list_contains(&instance.events.list, &e3.list)); + ASSERT_EQ(1, lcb_list_contains(&instance.events.list, &e4.list)); + + // try to unlink the current head + lcb_list_delete(&e4.list); + ASSERT_EQ(0, lcb_list_contains(&instance.events.list, &e1.list)); + ASSERT_EQ(0, lcb_list_contains(&instance.events.list, &e2.list)); + ASSERT_EQ(1, lcb_list_contains(&instance.events.list, &e3.list)); + ASSERT_EQ(0, lcb_list_contains(&instance.events.list, &e4.list)); + + // try to unlink the last one + lcb_list_delete(&e3.list); + ASSERT_EQ(0, lcb_list_contains(&instance.events.list, &e1.list)); + ASSERT_EQ(0, lcb_list_contains(&instance.events.list, &e2.list)); + ASSERT_EQ(0, lcb_list_contains(&instance.events.list, &e3.list)); + ASSERT_EQ(0, lcb_list_contains(&instance.events.list, &e4.list)); + + // And we should be able to add all back + lcb_list_append(&instance.events.list, &e1.list); + lcb_list_append(&instance.events.list, &e2.list); + lcb_list_append(&instance.events.list, &e3.list); + lcb_list_append(&instance.events.list, &e4.list); + ASSERT_EQ(1, lcb_list_contains(&instance.events.list, &e1.list)); + ASSERT_EQ(1, lcb_list_contains(&instance.events.list, &e2.list)); + ASSERT_EQ(1, lcb_list_contains(&instance.events.list, &e3.list)); + ASSERT_EQ(1, lcb_list_contains(&instance.events.list, &e4.list)); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_connstr.cc b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_connstr.cc new file mode 100644 index 00000000..82855b4a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_connstr.cc @@ -0,0 +1,404 @@ +#include +#include "connspec.h" +using namespace lcb; + +static lcb_error_t lcb_connspec_parse(const char *connstr, Connspec *spec, const char **errmsg) +{ + return spec->parse(connstr, errmsg); +} +static lcb_error_t lcb_connspec_convert(Connspec *spec, const lcb_create_st *cropts) +{ + return spec->load(*cropts); +} +static size_t countHosts(const Connspec *spec) { + return spec->hosts().size(); +} + +class ConnstrTest : public ::testing::Test +{ +protected: + Connspec params; + const char *errmsg; + void reinit() { + params = Connspec(); + } + void SetUp() { + params = Connspec(); + } +}; + +const Spechost* +findHost(const Connspec& params, const char* srch) +{ + for (size_t ii = 0; ii < params.hosts().size(); ++ii) { + const Spechost *cur = ¶ms.hosts()[ii]; + if (srch == cur->hostname) { + return cur; + } + } + return NULL; +} +const Spechost* +findHost(const Connspec* params, const char *srch) +{ + return findHost(*params, srch); +} + +struct OptionPair { + std::string key; + std::string value; +}; + +bool findOption(const Connspec& params, const char *srch, OptionPair& op) +{ + int iter = 0; + Connspec::Options::const_iterator ii = params.options().begin(); + for (; ii != params.options().end(); ++ii) { + if (ii->first == srch) { + op.key = ii->first; + op.value = ii->second; + return true; + } + } + return false; +} + +bool findOption(const Connspec* params, const char *srch, OptionPair& op) +{ + return findOption(*params, srch, op); +} + +size_t +countHosts(const Connspec ¶ms) +{ + return params.hosts().size(); +} + +TEST_F(ConnstrTest, testParseBasic) +{ + lcb_error_t err; + err = params.parse("couchbase://1.2.3.4", &errmsg); + ASSERT_EQ(LCB_SUCCESS, err); + + const Spechost *tmphost; + tmphost = findHost(params, "1.2.3.4"); + ASSERT_EQ(1, countHosts(params)); + ASSERT_FALSE(NULL == tmphost); + ASSERT_EQ(0, tmphost->port); + ASSERT_EQ(0, tmphost->type); // Nothing + + reinit(); + // test with bad scheme + err = params.parse("blah://foo.com", &errmsg); + ASSERT_NE(LCB_SUCCESS, err) << "Error on bad scheme"; + + reinit(); + err = params.parse("couchbase://", &errmsg); + ASSERT_EQ(LCB_SUCCESS, err) << "Ok with scheme only"; + + reinit(); + err = params.parse("couchbase://?", &errmsg); + ASSERT_EQ(LCB_SUCCESS, err) << "Ok with only '?'"; + + reinit(); + err = params.parse("couchbase://?&", &errmsg); + ASSERT_EQ(LCB_SUCCESS, err) << "Ok with only '?&'"; + + reinit(); + err = params.parse("1.2.3.4", &errmsg); + ASSERT_EQ(LCB_SUCCESS, err) << "Ok without scheme"; + ASSERT_EQ(LCB_CONFIG_HTTP_PORT, params.default_port()); + + reinit(); + err = lcb_connspec_parse("1.2.3.4:999", ¶ms, &errmsg); + ASSERT_EQ(1, countHosts(¶ms)); + tmphost = findHost(¶ms, "1.2.3.4"); + ASSERT_FALSE(tmphost == NULL); + ASSERT_EQ(999, tmphost->port); + ASSERT_TRUE(tmphost->isHTTP()); + +} + +TEST_F(ConnstrTest, testParseHosts) +{ + lcb_error_t err; + err = lcb_connspec_parse("couchbase://foo.com,bar.com,baz.com", ¶ms, &errmsg); + ASSERT_EQ(3, countHosts(¶ms)); + ASSERT_FALSE(NULL == findHost(¶ms, "foo.com")); + ASSERT_FALSE(NULL == findHost(¶ms, "bar.com")); + ASSERT_FALSE(NULL == findHost(¶ms, "baz.com")); + + // Parse with 'legacy' format + reinit(); + err = lcb_connspec_parse("couchbase://foo.com:8091", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err); + const Spechost *dh = findHost(¶ms, "foo.com"); + ASSERT_FALSE(NULL == dh); + ASSERT_EQ("foo.com", dh->hostname); + // CCBC-599 + ASSERT_EQ(0, dh->port); + ASSERT_EQ(0, dh->type); + + // parse with invalid port, without specifying protocol + reinit(); + err = lcb_connspec_parse("couchbase://foo.com:4444", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err); + dh = findHost(¶ms, "foo.com"); + ASSERT_EQ(4444, dh->port); + ASSERT_TRUE(dh->isMCD()); + + reinit(); + err = lcb_connspec_parse("couchbases://foo.com:4444", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err); + dh = findHost(¶ms, "foo.com"); + ASSERT_EQ(LCB_SSL_ENABLED, params.sslopts()); + ASSERT_EQ(4444, dh->port); + ASSERT_TRUE(dh->isMCDS()); + + // Parse with recognized format + reinit(); + err = lcb_connspec_parse("couchbase://foo.com:4444=mcd", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err); + dh = findHost(¶ms, "foo.com"); + ASSERT_EQ("foo.com", dh->hostname); + ASSERT_EQ(4444, dh->port); + ASSERT_TRUE(dh->isMCD()); + + //Parse multiple hosts with ports + reinit(); + err = lcb_connspec_parse("couchbase://foo.com:4444=mcd,bar.com:5555=mcd", + ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err); + + dh = findHost(¶ms, "foo.com"); + ASSERT_FALSE(dh == NULL); + ASSERT_EQ("foo.com", dh->hostname); + ASSERT_EQ(4444, dh->port); + ASSERT_TRUE(dh->isMCD()); + + dh = findHost(¶ms, "bar.com"); + ASSERT_FALSE(dh == NULL); + ASSERT_EQ("bar.com", dh->hostname); + ASSERT_EQ(5555, dh->port); + ASSERT_TRUE(dh->isMCD()); + + reinit(); + err = lcb_connspec_parse("couchbase://foo.com,bar.com:4444", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err); + dh = findHost(¶ms, "bar.com"); + ASSERT_EQ(4444, dh->port); + ASSERT_TRUE(dh->isMCD()); + dh = findHost(¶ms, "foo.com"); + ASSERT_TRUE(dh->isTypeless()); + + reinit(); + err = lcb_connspec_parse("couchbase://foo.com;bar.com;baz.com", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err) << "Can parse old-style semicolons"; + ASSERT_EQ(3, countHosts(¶ms)); + ASSERT_FALSE(NULL == findHost(¶ms, "foo.com")); + ASSERT_FALSE(NULL == findHost(¶ms, "bar.com")); + ASSERT_FALSE(NULL == findHost(¶ms, "baz.com")); +} + +TEST_F(ConnstrTest, testParseBucket) +{ + lcb_error_t err; + err = lcb_connspec_parse("couchbase://foo.com/user", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ("user", params.bucket()) << "Basic bucket parse"; + + reinit(); + err = lcb_connspec_parse("couchbase://foo.com/user/", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err) << "Bucket can have a slash"; + // We can have a bucket using a slash + + reinit(); + err = lcb_connspec_parse("couchbase:///default", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err) << "Bucket without host OK"; + ASSERT_EQ("default", params.bucket()); + + reinit(); + err = lcb_connspec_parse("couchbase:///default?", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ("default", params.bucket()); + + reinit(); + err = lcb_connspec_parse("couchbase:///%2FUsers%2F?", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ("/Users/", params.bucket()); +} + +TEST_F(ConnstrTest, testOptionsPassthrough) +{ + lcb_error_t err; + err = lcb_connspec_parse("couchbase://?foo=bar", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err) << "Options only"; + ASSERT_FALSE(params.options().empty()); + ASSERT_NE(0, params.options().size()); + + OptionPair op; + ASSERT_TRUE(findOption(¶ms, "foo", op)); + ASSERT_EQ("bar", op.value); + + reinit(); + err = lcb_connspec_parse("couchbase://?foo=bar", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_TRUE(findOption(¶ms, "foo", op)); + ASSERT_EQ("bar", op.value); + + reinit(); + err = lcb_connspec_parse("couchbase://?foo", ¶ms, &errmsg); + ASSERT_NE(LCB_SUCCESS, err) << "Option without value"; + + + // Multiple options + reinit(); + err = lcb_connspec_parse("couchbase://?foo=fooval&bar=barval", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_TRUE(findOption(¶ms, "foo", op)); + ASSERT_EQ("fooval", op.value); + + ASSERT_TRUE(findOption(¶ms, "bar", op)); + ASSERT_EQ("barval", op.value); + + reinit(); + err = lcb_connspec_parse("couchbase:///protected?ssl=on&compression=off", + ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err) << "Ok with bucket and no hosts"; + ASSERT_EQ(1, countHosts(¶ms)); + ASSERT_FALSE(NULL == findHost(¶ms, "localhost")); + ASSERT_TRUE(findOption(¶ms, "compression", op)); + + reinit(); + err = lcb_connspec_parse("couchbase://?foo=foo&bar=bar&", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err) << "Ok with trailing '&'"; + + reinit(); + err = lcb_connspec_parse("couchbase://?foo=foo&bootstrap_on=all&bar=bar", + ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err) << "Ok with non-passthrough option"; + ASSERT_TRUE(findOption(¶ms, "foo", op)); + ASSERT_TRUE(findOption(¶ms, "bar", op)); + ASSERT_FALSE(findOption(¶ms, "bootstrap_on", op)); +} + +TEST_F(ConnstrTest, testRecognizedOptions) +{ + lcb_error_t err; + err = lcb_connspec_parse("couchbases://", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(LCB_SSL_ENABLED, params.sslopts()); + + reinit(); + err = lcb_connspec_parse("couchbase://?ssl=on", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(LCB_SSL_ENABLED, params.sslopts()); + + reinit(); + err = lcb_connspec_parse("couchbases://?ssl=no_verify", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(LCB_SSL_ENABLED|LCB_SSL_NOVERIFY, params.sslopts()); + + reinit(); + err = lcb_connspec_parse("couchbases://?ssl=off", ¶ms, &errmsg); + ASSERT_NE(LCB_SUCCESS, err); + + // Loglevel + reinit(); + err = lcb_connspec_parse("couchbase://?console_log_level=5", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(5, params.loglevel()); + + reinit(); + err = lcb_connspec_parse("couchbase://?console_log_level=gah", ¶ms, &errmsg); + ASSERT_NE(LCB_SUCCESS, err); + +} + +TEST_F(ConnstrTest, testTransportOptions) +{ + lcb_error_t err; + err = lcb_connspec_parse("couchbase://", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_FALSE(params.is_bs_udef()); + + reinit(); + err = lcb_connspec_parse("couchbase://?bootstrap_on=cccp", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err) << "bootstrap_on=cccp"; + ASSERT_TRUE(params.has_bsmode(LCB_CONFIG_TRANSPORT_CCCP)); + ASSERT_FALSE(params.has_bsmode(LCB_CONFIG_TRANSPORT_HTTP)); + + reinit(); + err = lcb_connspec_parse("couchbase://?bootstrap_on=http", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err) << "bootstrap_on=http"; + ASSERT_TRUE(params.has_bsmode(LCB_CONFIG_TRANSPORT_HTTP)); + ASSERT_FALSE(params.has_bsmode(LCB_CONFIG_TRANSPORT_CCCP)); + + reinit(); + err = lcb_connspec_parse("couchbase://?bootstrap_on=all", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err) << "bootstrap_on=all"; + ASSERT_TRUE(params.has_bsmode(LCB_CONFIG_TRANSPORT_CCCP)); + ASSERT_TRUE(params.has_bsmode(LCB_CONFIG_TRANSPORT_HTTP)); + + reinit(); + err = lcb_connspec_parse("couchbase://?bootstrap_on=bleh", ¶ms, &errmsg); + ASSERT_NE(LCB_SUCCESS, err) << "Error on bad bootstrap_on value"; +} + +TEST_F(ConnstrTest, testCompatConversion) +{ + lcb_error_t err; + struct lcb_create_st cropts; + memset(&cropts, 0, sizeof cropts); + cropts.version = 0; + cropts.v.v0.bucket = "users"; + cropts.v.v0.host = "foo.com;bar.com;baz.com"; + cropts.v.v0.passwd = "secret"; + + err = lcb_connspec_convert(¶ms, &cropts); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_FALSE(NULL == findHost(¶ms, "foo.com")); + ASSERT_FALSE(NULL == findHost(¶ms, "bar.com")); + ASSERT_FALSE(NULL == findHost(¶ms, "baz.com")); + ASSERT_EQ(3, countHosts(¶ms)); + ASSERT_EQ("users", params.bucket()); + ASSERT_EQ("secret", params.password()); + + // Ensure old-style port specifications are parsed and don't throw an + // error. We'd also like to verify that these actually land within + // the htport field, that's a TODO + reinit(); + memset(&cropts, 0, sizeof cropts); + cropts.version = 2; + cropts.v.v2.host = "foo.com:9030;bar.com:9040;baz.com:9050"; + cropts.v.v2.mchosts = "foo.com:7030;bar.com:7040;baz.com:7050"; + err = lcb_connspec_convert(¶ms, &cropts); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(6, countHosts(¶ms)); + + // Ensure struct fields override the URI string + reinit(); + memset(&cropts, 0, sizeof cropts); + cropts.version = 3; + cropts.v.v3.passwd = "secret"; + cropts.v.v3.connstr = "couchbase:///fluffle?password=bleh"; + err = lcb_connspec_convert(¶ms, &cropts); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ("fluffle", params.bucket()); + ASSERT_EQ(cropts.v.v3.passwd, params.password()); +} + +TEST_F(ConnstrTest, testCertificateWithoutSSL) +{ + // Ensure we get an invalid input error for certificate paths without + // couchbases:// + lcb_error_t err; + err = lcb_connspec_parse( + "couchbase://1.2.3.4/default?certpath=/foo/bar/baz", ¶ms, &errmsg); + ASSERT_NE(LCB_SUCCESS, err); + + reinit(); + err = lcb_connspec_parse( + "couchbases://1.2.3.4/default?certpath=/foo/bar/baz", ¶ms, &errmsg); + ASSERT_EQ(LCB_SUCCESS, err); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_creds.cc b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_creds.cc new file mode 100644 index 00000000..737838b9 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_creds.cc @@ -0,0 +1,54 @@ +#include "config.h" +#include "internal.h" +#include "auth-priv.h" +#include +#define LIBCOUCHBASE_INTERNAL 1 +#include + +class CredsTest : public ::testing::Test +{ +}; + +TEST_F(CredsTest, testCreds) +{ + lcb_t instance; + lcb_BUCKETCRED cred; + ASSERT_EQ(LCB_SUCCESS, lcb_create(&instance, NULL)); + lcb::Authenticator& auth = *instance->settings->auth; + ASSERT_FALSE(auth.username().empty()); + ASSERT_EQ("default", auth.username()); + + ASSERT_EQ(1, auth.buckets().size()); + ASSERT_TRUE(auth.buckets().find("default")->second.empty()); + + // Try to add another user/password: + lcb_BUCKETCRED creds = { "user2", "pass2" }; + ASSERT_EQ(LCB_SUCCESS, lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_BUCKET_CRED, creds)); + ASSERT_EQ(2, auth.buckets().size()); + ASSERT_EQ("pass2", auth.buckets().find("user2")->second); + ASSERT_EQ("default", auth.username()); + ASSERT_EQ("", auth.password()); + lcb_destroy(instance); +} + +TEST_F(CredsTest, testSharedAuth) +{ + lcb_t instance1, instance2; + ASSERT_EQ(LCB_SUCCESS, lcb_create(&instance1, NULL)); + ASSERT_EQ(LCB_SUCCESS, lcb_create(&instance2, NULL)); + + lcb_AUTHENTICATOR *auth = lcbauth_new(); + ASSERT_EQ(1, auth->refcount()); + + lcb_set_auth(instance1, auth); + ASSERT_EQ(2, auth->refcount()); + + lcb_set_auth(instance2, auth); + ASSERT_EQ(3, auth->refcount()); + + ASSERT_EQ(instance1->settings->auth, instance2->settings->auth); + lcb_destroy(instance1); + lcb_destroy(instance2); + ASSERT_EQ(1, auth->refcount()); + lcbauth_unref(auth); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_ctlcodes.cc b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_ctlcodes.cc new file mode 100644 index 00000000..53b0359e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_ctlcodes.cc @@ -0,0 +1,92 @@ +#include "config.h" +#include +#include + +class CtlTest : public ::testing::Test +{ +}; + +template +T getSetting(lcb_t instance, int ctl) +{ + T obj; + lcb_error_t err; + err = lcb_cntl(instance, LCB_CNTL_GET, ctl, &obj); + EXPECT_EQ(LCB_SUCCESS, err); + return obj; +} + +TEST_F(CtlTest, testExists) +{ + for (int ii = 0; ii < LCB_CNTL__MAX; ii++) { + ASSERT_NE(0, lcb_cntl_exists(ii)); + } + ASSERT_EQ(0, lcb_cntl_exists(-1)); + ASSERT_EQ(0, lcb_cntl_exists(LCB_CNTL__MAX)); +} + +struct PairMap { + const char *key; + int opval; +}; + +TEST_F(CtlTest, testStringCtls) +{ + lcb_t instance; + lcb_error_t err; + err = lcb_create(&instance, NULL); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_FALSE(instance == NULL); + + // These are all U32 + PairMap ctlMap[] = { + {"operation_timeout", LCB_CNTL_OP_TIMEOUT}, + {"views_timeout", LCB_CNTL_VIEW_TIMEOUT}, + {"durability_timeout", LCB_CNTL_DURABILITY_TIMEOUT}, + {"durability_interval", LCB_CNTL_DURABILITY_INTERVAL}, + {"http_timeout", LCB_CNTL_HTTP_TIMEOUT}, + {"error_thresh_delay", LCB_CNTL_CONFDELAY_THRESH}, + {"config_total_timeout", LCB_CNTL_CONFIGURATION_TIMEOUT}, + {"config_node_timeout", LCB_CNTL_CONFIG_NODE_TIMEOUT}, + {NULL,0} + }; + + for (PairMap* cur = ctlMap; cur->key; cur++) { + err = lcb_cntl_string(instance, cur->key, "50"); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(50000000, lcb_cntl_getu32(instance, cur->opval)); + } + + // try with a boolean + err = lcb_cntl_string(instance, "randomize_nodes", "false"); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(0, getSetting(instance, LCB_CNTL_RANDOMIZE_BOOTSTRAP_HOSTS)); + + err = lcb_cntl_string(instance, "randomize_nodes", "true"); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(1, getSetting(instance, LCB_CNTL_RANDOMIZE_BOOTSTRAP_HOSTS)); + + + // try with compression + err = lcb_cntl_string(instance, "compression", "on"); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(LCB_COMPRESS_INOUT, + getSetting(instance, LCB_CNTL_COMPRESSION_OPTS)); + + err = lcb_cntl_string(instance, "compression", "off"); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(LCB_COMPRESS_NONE, + getSetting(instance, LCB_CNTL_COMPRESSION_OPTS)); + + err = lcb_cntl_string(instance, "compression", "inflate_only"); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(LCB_COMPRESS_IN, + getSetting(instance, LCB_CNTL_COMPRESSION_OPTS)); + + err = lcb_cntl_string(instance, "unsafe_optimize", "1"); + ASSERT_EQ(LCB_SUCCESS, err); + err = lcb_cntl_string(instance, "unsafe_optimize", "0"); + ASSERT_NE(LCB_SUCCESS, err); + + lcb_destroy(instance); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_host.cc b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_host.cc new file mode 100644 index 00000000..c53e655b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_host.cc @@ -0,0 +1,198 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include +#include +#include "hostlist.h" + +class Hostlist : public ::testing::Test +{ +}; + +static bool hostEquals(const lcb_host_t &host, const char *addr, const char *port) +{ + return strcmp(host.host, addr) == 0 && strcmp(host.port, port) == 0; +} + +TEST_F(Hostlist, testParseBasic) +{ + lcb_host_t curhost; + lcb_error_t err; + + err = lcb_host_parsez(&curhost, "1.2.3.4", 8091); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_TRUE(hostEquals(curhost, "1.2.3.4", "8091")); + + err = lcb_host_parsez(&curhost, "1.2.3.4:9000", 8091); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_TRUE(hostEquals(curhost, "1.2.3.4", "9000")); + + err = lcb_host_parsez(&curhost, "http://1.2.3.4:900/pools/default", 8091); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_TRUE(hostEquals(curhost, "1.2.3.4", "900")); + + err = lcb_host_parsez(&curhost, "", 1000); + ASSERT_EQ(LCB_INVALID_HOST_FORMAT, err); + + err = lcb_host_parsez(&curhost, "foo.com", -1); + ASSERT_EQ(LCB_INVALID_HOST_FORMAT, err); + + err = lcb_host_parsez(&curhost, "foo.com:", 100); + ASSERT_EQ(LCB_INVALID_HOST_FORMAT, err); + + err = lcb_host_parsez(&curhost, "localhost/foo", 100); + ASSERT_EQ(LCB_SUCCESS, err); + + err = lcb_host_parsez(&curhost, "localhost:1111111111111111111111111111", 100); + ASSERT_EQ(LCB_INVALID_HOST_FORMAT, err); +} + + +TEST_F(Hostlist, testEquals) +{ + lcb_host_t host_a, host_b; + strcpy(host_a.host, "foo.com"); + strcpy(host_a.port, "1234"); + strcpy(host_b.host, "foo.com"); + strcpy(host_b.port, "1234"); + ASSERT_NE(0, lcb_host_equals(&host_a, &host_b)); + + strcpy(host_a.host, "bar.com"); + ASSERT_EQ(0, lcb_host_equals(&host_a, &host_b)); + + + strcpy(host_a.host, "foo.com"); + strcpy(host_a.port, "44444"); + ASSERT_EQ(0, lcb_host_equals(&host_a, &host_b)); +} + +TEST_F(Hostlist, testParseList) +{ + hostlist_t hosts = hostlist_create(); + ASSERT_FALSE(NULL == hosts); + + lcb_error_t err; + err = hostlist_add_stringz(hosts, "1.1.1.1", 8091); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(1, hosts->size()); + ASSERT_TRUE(hosts->exists("1.1.1.1:8091")); + + + hostlist_clear(hosts); + err = hostlist_add_stringz(hosts, "1.1.1.1;", 8091); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(1, hosts->size()); + ASSERT_TRUE(hosts->exists("1.1.1.1:8091")); + + hostlist_clear(hosts); + err = hostlist_add_stringz(hosts, ";", 8091); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(0, hosts->size()); + + hostlist_clear(hosts); + err = hostlist_add_stringz(hosts, ";;;;", 8091); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(0, hosts->size()); + + hostlist_clear(hosts); + err = hostlist_add_stringz(hosts, "1.1.1.1;2.2.2.2", 8091); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(2, hosts->size()); + ASSERT_TRUE(hosts->exists("1.1.1.1:8091")); + ASSERT_TRUE(hosts->exists("2.2.2.2:8091")); + + + hostlist_clear(hosts); + err = hostlist_add_stringz(hosts, "1.1.1.1:1000;2.2.2.2:2000;3.3.3.3", 8091); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(3, hosts->size()); + ASSERT_TRUE(hosts->exists("1.1.1.1:1000")); + ASSERT_TRUE(hosts->exists("2.2.2.2:2000")); + ASSERT_TRUE(hosts->exists("3.3.3.3:8091")); + + hostlist_clear(hosts); + err = hostlist_add_stringz(hosts, "1.1.1.1;1.1.1.1;1.1.1.1", 8091); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(1, hosts->size()); + ASSERT_TRUE(hosts->exists("1.1.1.1:8091")); + + + hostlist_clear(hosts); + err = hostlist_add_stringz(hosts, "1.1.1.1:9000;1.1.1.1:9001;1.1.1.1:9002", 8091); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(3, hosts->size()); + ASSERT_TRUE(hosts->exists("1.1.1.1:9000")); + ASSERT_TRUE(hosts->exists("1.1.1.1:9001")); + ASSERT_TRUE(hosts->exists("1.1.1.1:9002")); + + hostlist_clear(hosts); + ASSERT_EQ(LCB_SUCCESS, hostlist_add_stringz(hosts, "1.1.1.1", 8091)); + ASSERT_EQ(LCB_SUCCESS, hostlist_add_stringz(hosts, "2.2.2.2", 8091)); + ASSERT_EQ(LCB_SUCCESS, hostlist_add_stringz(hosts, "3.3.3.3", 8091)); + ASSERT_EQ(3, hosts->size()); + + ASSERT_TRUE(hosts->exists("1.1.1.1:8091")); + ASSERT_TRUE(hosts->exists("2.2.2.2:8091")); + ASSERT_TRUE(hosts->exists("3.3.3.3:8091")); + + hostlist_randomize(hosts); + hostlist_clear(hosts); + hostlist_randomize(hosts); + + hostlist_destroy(hosts); +} + +TEST_F(Hostlist, testCycle) +{ + hostlist_t hosts = hostlist_create(); + lcb_host_t *curhost; + + + // Empty list + ASSERT_EQ(NULL, hostlist_shift_next(hosts, 0)); + ASSERT_EQ(NULL, hostlist_shift_next(hosts, 1)); + hostlist_destroy(hosts); + hosts = hostlist_create(); + + hostlist_add_stringz(hosts, "1.1.1.1", 8091); + curhost = hostlist_shift_next(hosts, 0); + ASSERT_TRUE(curhost != NULL); + ASSERT_TRUE(hostEquals(*curhost, "1.1.1.1", "8091")); + + curhost = hostlist_shift_next(hosts, 0); + ASSERT_TRUE(hostlist_shift_next(hosts, 0) == NULL); + ASSERT_TRUE(hostlist_shift_next(hosts, 0) == NULL); + ASSERT_TRUE(hosts->ix == 1); + + curhost = hostlist_shift_next(hosts, 1); + ASSERT_TRUE(curhost != NULL); + ASSERT_TRUE(hostEquals(*curhost, "1.1.1.1", "8091")); + + hostlist_add_stringz(hosts, "2.2.2.2", 8091); + curhost = hostlist_shift_next(hosts, 0); + ASSERT_TRUE(hostEquals(*curhost, "2.2.2.2", "8091")); + ASSERT_TRUE(hostlist_shift_next(hosts, 0) == NULL); + + curhost = hostlist_shift_next(hosts, 1); + ASSERT_TRUE(hostEquals(*curhost, "1.1.1.1", "8091")); + curhost = hostlist_shift_next(hosts, 0); + ASSERT_TRUE(hostEquals(*curhost, "2.2.2.2", "8091")); + + hostlist_clear(hosts); + ASSERT_TRUE(hostlist_shift_next(hosts, 1) == NULL); + hostlist_destroy(hosts); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_jsparse.cc b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_jsparse.cc new file mode 100644 index 00000000..f5c91130 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_jsparse.cc @@ -0,0 +1,137 @@ +#include "config.h" +#include +#include +#include "jsparse/parser.h" +#include "contrib/lcb-jsoncpp/lcb-jsoncpp.h" +#include "t_jsparse.h" + +class JsonParseTest : public ::testing::Test { +}; + + +struct Context { + lcb_error_t rc; + bool received_done; + std::string meta; + std::vector rows; + Context() { + reset(); + } + void reset() { + rc = LCB_SUCCESS; + received_done = false; + meta.clear(); + rows.clear(); + } +}; + +class Parser { +public: + Parser(int mode_) : mode(mode_), inner(NULL) { + reset(); + } + + void reset() { + if (inner != NULL) { + lcbjsp_free(inner); + } + inner = lcbjsp_create(mode); + } + + ~Parser() { + if (inner != NULL) { + lcbjsp_free(inner); + } + } + + operator lcbjsp_PARSER* () { + return inner; + } + + lcbjsp_PARSER *getInner() { + return inner; + } + +private: + int mode; + lcbjsp_PARSER *inner; + Parser(Parser&); +}; + +static std::string iov2s(const lcb_IOV& iov) { + return std::string(reinterpret_cast(iov.iov_base), iov.iov_len); +} + +extern "C" { +static void rowCallback(lcbjsp_PARSER *parser, const lcbjsp_ROW *row) { + Context *ctx = reinterpret_cast(parser->data); + if (row->type == LCBJSP_TYPE_ERROR) { + ctx->rc = LCB_PROTOCOL_ERROR; + ctx->received_done = true; + } else if (row->type == LCBJSP_TYPE_ROW) { + ctx->rows.push_back(iov2s(row->row)); + } else if (row->type == LCBJSP_TYPE_COMPLETE) { + ctx->meta = iov2s(row->row); + ctx->received_done = true; + } +} +} + +static bool validateJsonRows(const char *txt, size_t ntxt, int mode) +{ + Parser parser(mode); + EXPECT_TRUE(parser != NULL); + if (parser == NULL) { + return false; + } + + // Feed it once + Context cx; + parser.getInner()->callback = rowCallback; + parser.getInner()->data = &cx; + + for (size_t ii = 0; ii < ntxt; ii++) { + lcbjsp_feed(parser, txt + ii, 1); + } + EXPECT_EQ(LCB_SUCCESS, cx.rc); + + lcb_IOV out; + lcbjsp_get_postmortem(parser, &out); + EXPECT_EQ(cx.meta, iov2s(out)); + Json::Value root; + EXPECT_TRUE(Json::Reader().parse(cx.meta, root)); + return true; +} + +static bool validateBadParse(const char *txt, size_t ntxt, int mode) +{ + Parser p(mode); + Context cx; + p.getInner()->callback = rowCallback; + p.getInner()->data = &cx; + lcbjsp_feed(p, JSON_fts_bad, sizeof(JSON_fts_bad)); + EXPECT_EQ(LCB_PROTOCOL_ERROR, cx.rc); + + p.reset(); + cx.reset(); + + p.getInner()->callback = rowCallback; + p.getInner()->data = &cx; + + for (size_t ii = 0; ii < ntxt; ++ii) { + } + return true; +} + +TEST_F(JsonParseTest, testFTS) +{ + ASSERT_TRUE(validateJsonRows(JSON_fts_good, sizeof(JSON_fts_good), LCBJSP_MODE_FTS)); + ASSERT_TRUE(validateBadParse(JSON_fts_bad, sizeof(JSON_fts_bad), LCBJSP_MODE_FTS)); + ASSERT_TRUE(validateBadParse(JSON_fts_bad2, sizeof(JSON_fts_bad2), LCBJSP_MODE_FTS)); +} + +TEST_F(JsonParseTest, testN1QL) { + ASSERT_TRUE(validateJsonRows(JSON_n1ql_nonempty, sizeof(JSON_n1ql_nonempty), LCBJSP_MODE_N1QL)); + ASSERT_TRUE(validateJsonRows(JSON_n1ql_empty, sizeof(JSON_n1ql_empty), LCBJSP_MODE_N1QL)); + ASSERT_TRUE(validateBadParse(JSON_n1ql_bad, sizeof(JSON_n1ql_bad), LCBJSP_MODE_N1QL)); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_jsparse.h b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_jsparse.h new file mode 100644 index 00000000..2c77bd5a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_jsparse.h @@ -0,0 +1,589 @@ +/* Generic 'good' FTS response */ + +static const char JSON_fts_good[] = { + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x22, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, + 0x68, 0x69, 0x74, 0x73, 0x22, 0x20, 0x3a, 0x20, 0x37, 0x39, 0x32, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, + 0x20, 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x22, 0x20, + 0x3a, 0x20, 0x32, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x22, 0x20, 0x3a, 0x20, 0x32, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x66, 0x61, 0x69, 0x6c, 0x65, + 0x64, 0x22, 0x20, 0x3a, 0x20, 0x30, 0x0a, 0x20, 0x20, 0x20, 0x7d, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x22, 0x68, 0x69, 0x74, 0x73, 0x22, 0x20, 0x3a, + 0x20, 0x5b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x63, 0x6f, + 0x72, 0x65, 0x22, 0x20, 0x3a, 0x20, 0x30, 0x2e, 0x38, 0x36, 0x34, 0x38, + 0x30, 0x32, 0x31, 0x31, 0x36, 0x34, 0x36, 0x32, 0x31, 0x33, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x20, 0x3a, 0x20, 0x7b, + 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x62, + 0x65, 0x65, 0x72, 0x2d, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x34, + 0x39, 0x32, 0x62, 0x38, 0x64, 0x66, 0x32, 0x32, 0x61, 0x34, 0x64, 0x35, + 0x36, 0x65, 0x36, 0x5f, 0x38, 0x62, 0x38, 0x30, 0x39, 0x35, 0x38, 0x61, + 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x69, 0x64, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x77, 0x65, 0x79, 0x65, + 0x72, 0x62, 0x61, 0x63, 0x68, 0x65, 0x72, 0x5f, 0x62, 0x72, 0x65, 0x77, + 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2d, + 0x68, 0x6f, 0x70, 0x73, 0x5f, 0x69, 0x6e, 0x66, 0x75, 0x73, 0x69, 0x6f, + 0x6e, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x22, + 0x20, 0x3a, 0x20, 0x30, 0x2e, 0x37, 0x38, 0x33, 0x38, 0x33, 0x36, 0x31, + 0x36, 0x31, 0x35, 0x30, 0x31, 0x37, 0x38, 0x36, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x20, 0x3a, 0x20, 0x7b, 0x7d, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x62, 0x65, 0x65, + 0x72, 0x2d, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x34, 0x39, 0x32, + 0x62, 0x38, 0x64, 0x66, 0x32, 0x32, 0x61, 0x34, 0x64, 0x35, 0x36, 0x65, + 0x36, 0x5f, 0x38, 0x62, 0x38, 0x30, 0x39, 0x35, 0x38, 0x61, 0x22, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x69, + 0x64, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x68, 0x61, 0x72, 0x70, 0x6f, 0x6f, + 0x6e, 0x5f, 0x62, 0x72, 0x65, 0x77, 0x65, 0x72, 0x79, 0x5f, 0x62, 0x6f, + 0x73, 0x74, 0x6f, 0x6e, 0x2d, 0x67, 0x6c, 0x61, 0x63, 0x69, 0x65, 0x72, + 0x5f, 0x68, 0x61, 0x72, 0x76, 0x65, 0x73, 0x74, 0x5f, 0x30, 0x39, 0x5f, + 0x77, 0x65, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x5f, 0x31, 0x30, 0x30, 0x5f, + 0x62, 0x61, 0x72, 0x72, 0x65, 0x6c, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x65, + 0x73, 0x5f, 0x32, 0x38, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x63, 0x6f, + 0x72, 0x65, 0x22, 0x20, 0x3a, 0x20, 0x30, 0x2e, 0x36, 0x38, 0x34, 0x33, + 0x35, 0x39, 0x37, 0x38, 0x31, 0x32, 0x37, 0x37, 0x38, 0x34, 0x31, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6c, + 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x20, 0x3a, 0x20, + 0x7b, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x20, 0x3a, 0x20, 0x22, + 0x62, 0x65, 0x65, 0x72, 0x2d, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, + 0x34, 0x39, 0x32, 0x62, 0x38, 0x64, 0x66, 0x32, 0x32, 0x61, 0x34, 0x64, + 0x35, 0x36, 0x65, 0x36, 0x5f, 0x62, 0x37, 0x66, 0x66, 0x36, 0x62, 0x36, + 0x38, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x69, 0x64, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x74, 0x72, 0x6f, + 0x65, 0x67, 0x73, 0x5f, 0x62, 0x72, 0x65, 0x77, 0x69, 0x6e, 0x67, 0x2d, + 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x62, 0x65, 0x65, 0x72, + 0x5f, 0x33, 0x31, 0x5f, 0x32, 0x30, 0x31, 0x30, 0x5f, 0x63, 0x69, 0x74, + 0x72, 0x61, 0x5f, 0x6f, 0x66, 0x5f, 0x62, 0x72, 0x6f, 0x74, 0x68, 0x65, + 0x72, 0x6c, 0x79, 0x5f, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x69, 0x70, 0x61, + 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x20, + 0x3a, 0x20, 0x30, 0x2e, 0x36, 0x37, 0x39, 0x36, 0x30, 0x31, 0x38, 0x32, + 0x37, 0x32, 0x37, 0x39, 0x35, 0x35, 0x35, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6c, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x20, 0x3a, 0x20, 0x7b, 0x7d, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x62, 0x65, 0x65, 0x72, + 0x2d, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x34, 0x39, 0x32, 0x62, + 0x38, 0x64, 0x66, 0x32, 0x32, 0x61, 0x34, 0x64, 0x35, 0x36, 0x65, 0x36, + 0x5f, 0x38, 0x62, 0x38, 0x30, 0x39, 0x35, 0x38, 0x61, 0x22, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x69, 0x64, + 0x22, 0x20, 0x3a, 0x20, 0x22, 0x77, 0x65, 0x79, 0x65, 0x72, 0x62, 0x61, + 0x63, 0x68, 0x65, 0x72, 0x5f, 0x62, 0x72, 0x65, 0x77, 0x69, 0x6e, 0x67, + 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2d, 0x65, 0x6c, 0x65, + 0x76, 0x65, 0x6e, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x69, 0x64, 0x22, 0x20, + 0x3a, 0x20, 0x22, 0x64, 0x75, 0x63, 0x6b, 0x5f, 0x72, 0x61, 0x62, 0x62, + 0x69, 0x74, 0x5f, 0x63, 0x72, 0x61, 0x66, 0x74, 0x5f, 0x62, 0x72, 0x65, + 0x77, 0x65, 0x72, 0x79, 0x2d, 0x64, 0x75, 0x63, 0x6b, 0x5f, 0x72, 0x61, + 0x62, 0x62, 0x69, 0x74, 0x5f, 0x62, 0x72, 0x6f, 0x77, 0x6e, 0x5f, 0x61, + 0x6c, 0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x20, 0x3a, 0x20, + 0x22, 0x62, 0x65, 0x65, 0x72, 0x2d, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, + 0x5f, 0x34, 0x39, 0x32, 0x62, 0x38, 0x64, 0x66, 0x32, 0x32, 0x61, 0x34, + 0x64, 0x35, 0x36, 0x65, 0x36, 0x5f, 0x62, 0x37, 0x66, 0x66, 0x36, 0x62, + 0x36, 0x38, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x22, 0x20, 0x3a, 0x20, 0x7b, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x22, + 0x20, 0x3a, 0x20, 0x30, 0x2e, 0x36, 0x37, 0x31, 0x33, 0x37, 0x32, 0x36, + 0x35, 0x31, 0x31, 0x32, 0x37, 0x34, 0x35, 0x34, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x5d, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x22, 0x74, 0x6f, 0x6f, 0x6b, 0x22, 0x20, 0x3a, 0x20, 0x38, + 0x37, 0x35, 0x37, 0x34, 0x37, 0x30, 0x35, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x22, 0x66, 0x61, 0x63, 0x65, 0x74, 0x73, 0x22, 0x20, 0x3a, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x65, 0x72, 0x6d, + 0x73, 0x22, 0x20, 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x22, 0x20, + 0x3a, 0x20, 0x33, 0x32, 0x37, 0x36, 0x31, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6f, 0x74, 0x68, 0x65, 0x72, + 0x22, 0x20, 0x3a, 0x20, 0x33, 0x30, 0x37, 0x32, 0x35, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x22, 0x20, 0x3a, 0x20, 0x39, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x65, 0x72, 0x6d, 0x73, + 0x22, 0x20, 0x3a, 0x20, 0x5b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x20, 0x3a, 0x20, 0x37, 0x38, 0x32, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x65, 0x72, 0x6d, 0x22, 0x20, + 0x3a, 0x20, 0x22, 0x68, 0x6f, 0x70, 0x73, 0x22, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, + 0x20, 0x3a, 0x20, 0x34, 0x33, 0x32, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x74, 0x65, 0x72, 0x6d, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x62, 0x65, 0x65, + 0x72, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x74, 0x65, 0x72, 0x6d, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x61, 0x6c, 0x65, + 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x22, 0x20, 0x3a, 0x20, 0x33, 0x36, 0x35, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x65, 0x72, 0x6d, 0x22, 0x20, 0x3a, + 0x20, 0x22, 0x6d, 0x61, 0x6c, 0x74, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x20, 0x3a, 0x20, 0x33, 0x32, + 0x37, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x20, 0x3a, 0x20, 0x31, 0x33, 0x30, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x65, 0x72, 0x6d, 0x22, 0x20, 0x3a, + 0x20, 0x22, 0x68, 0x6f, 0x70, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x22, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x22, + 0x20, 0x3a, 0x20, 0x30, 0x2e, 0x38, 0x36, 0x34, 0x38, 0x30, 0x32, 0x31, + 0x31, 0x36, 0x34, 0x36, 0x32, 0x31, 0x33, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x22, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x20, 0x3a, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x66, 0x72, 0x6f, + 0x6d, 0x22, 0x20, 0x3a, 0x20, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x66, 0x61, 0x63, 0x65, 0x74, 0x73, 0x22, 0x20, 0x3a, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x22, 0x20, 0x3a, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x20, 0x3a, 0x20, 0x35, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x20, 0x3a, + 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x20, 0x3a, 0x20, 0x35, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x71, 0x75, 0x65, 0x72, + 0x79, 0x22, 0x20, 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, + 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x20, 0x3a, 0x20, 0x30, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6d, + 0x61, 0x74, 0x63, 0x68, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x68, 0x6f, 0x70, + 0x73, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x22, 0x20, 0x3a, 0x20, 0x31, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x66, 0x75, 0x7a, 0x7a, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x22, 0x20, 0x3a, + 0x20, 0x30, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x68, 0x69, 0x67, 0x68, 0x6c, + 0x69, 0x67, 0x68, 0x74, 0x22, 0x20, 0x3a, 0x20, 0x6e, 0x75, 0x6c, 0x6c, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x65, 0x78, 0x70, + 0x6c, 0x61, 0x69, 0x6e, 0x22, 0x20, 0x3a, 0x20, 0x66, 0x61, 0x6c, 0x73, + 0x65, 0x0a, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x7d, 0x0a +}; + +/* Malformed FTS response #1 */ +static const char JSON_fts_bad[] = { + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x22, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, + 0x68, 0x69, 0x74, 0x73, 0x22, 0x20, 0x3a, 0x20, 0x37, 0x39, 0x32, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, + 0x20, 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x22, 0x20, + 0x3a, 0x20, 0x32, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x22, 0x20, 0x3a, 0x20, 0x32, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x66, 0x61, 0x69, 0x6c, 0x65, + 0x64, 0x22, 0x20, 0x3a, 0x20, 0x30, 0x0a, 0x20, 0x20, 0x20, 0x7d, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x22, 0x68, 0x69, 0x74, 0x73, 0x22, 0x20, 0x3a, + 0x20, 0x5b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x63, 0x6f, + 0x72, 0x65, 0x22, 0x20, 0x3a, 0x20, 0x30, 0x2e, 0x38, 0x36, 0x34, 0x38, + 0x30, 0x32, 0x31, 0x31, 0x36, 0x34, 0x36, 0x32, 0x31, 0x33, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x20, 0x3a, 0x20, 0x7b, + 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x62, + 0x65, 0x65, 0x72, 0x2d, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x34, + 0x39, 0x32, 0x62, 0x38, 0x64, 0x66, 0x32, 0x32, 0x61, 0x34, 0x64, 0x35, + 0x36, 0x65, 0x36, 0x5f, 0x38, 0x62, 0x38, 0x30, 0x39, 0x35, 0x38, 0x61, + 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x69, 0x64, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x77, 0x65, 0x79, 0x65, + 0x72, 0x62, 0x61, 0x63, 0x68, 0x65, 0x72, 0x5f, 0x62, 0x72, 0x65, 0x77, + 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2d, + 0x68, 0x6f, 0x70, 0x73, 0x5f, 0x69, 0x6e, 0x66, 0x75, 0x73, 0x69, 0x6f, + 0x6e, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x22, + 0x20, 0x3a, 0x20, 0x30, 0x2e, 0x37, 0x38, 0x33, 0x38, 0x33, 0x36, 0x31, + 0x36, 0x31, 0x35, 0x30, 0x31, 0x37, 0x38, 0x36, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x20, 0x3a, 0x20, 0x7b, 0x7d, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x62, 0x65, 0x65, + 0x72, 0x2d, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x34, 0x39, 0x32, + 0x62, 0x38, 0x64, 0x66, 0x32, 0x32, 0x61, 0x34, 0x64, 0x35, 0x36, 0x65, + 0x36, 0x5f, 0x38, 0x62, 0x38, 0x30, 0x39, 0x35, 0x38, 0x61, 0x22, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x69, + 0x64, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x68, 0x61, 0x72, 0x70, 0x6f, 0x6f, + 0x6e, 0x5f, 0x62, 0x72, 0x65, 0x77, 0x65, 0x72, 0x79, 0x5f, 0x62, 0x6f, + 0x73, 0x74, 0x6f, 0x6e, 0x2d, 0x67, 0x6c, 0x61, 0x63, 0x69, 0x65, 0x72, + 0x5f, 0x68, 0x61, 0x72, 0x76, 0x65, 0x73, 0x74, 0x5f, 0x30, 0x39, 0x5f, + 0x77, 0x65, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x5f, 0x31, 0x30, 0x30, 0x5f, + 0x62, 0x61, 0x72, 0x72, 0x65, 0x6c, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x65, + 0x73, 0x5f, 0x32, 0x38, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x63, 0x6f, + 0x72, 0x65, 0x22, 0x20, 0x3a, 0x20, 0x30, 0x2e, 0x36, 0x38, 0x34, 0x33, + 0x35, 0x39, 0x37, 0x38, 0x31, 0x32, 0x37, 0x37, 0x38, 0x34, 0x31, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6c, + 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x20, 0x3a, 0x20, + 0x7b, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x20, 0x3a, 0x20, 0x22, + 0x62, 0x65, 0x65, 0x72, 0x2d, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, + 0x34, 0x39, 0x32, 0x62, 0x38, 0x64, 0x66, 0x32, 0x32, 0x61, 0x34, 0x64, + 0x35, 0x36, 0x65, 0x36, 0x5f, 0x62, 0x37, 0x66, 0x66, 0x36, 0x62, 0x36, + 0x38, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x69, 0x64, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x74, 0x72, 0x6f, + 0x65, 0x67, 0x73, 0x5f, 0x62, 0x72, 0x65, 0x77, 0x69, 0x6e, 0x67, 0x2d, + 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x62, 0x65, 0x65, 0x72, + 0x5f, 0x33, 0x31, 0x5f, 0x32, 0x30, 0x31, 0x30, 0x5f, 0x63, 0x69, 0x74, + 0x72, 0x61, 0x5f, 0x6f, 0x66, 0x5f, 0x62, 0x72, 0x6f, 0x74, 0x68, 0x65, + 0x72, 0x6c, 0x79, 0x5f, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x69, 0x70, 0x61, + 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x20, + 0x3a, 0x20, 0x30, 0x2e, 0x36, 0x37, 0x39, 0x36, 0x30, 0x31, 0x38, 0x32, + 0x37, 0x32, 0x37, 0x39, 0x35, 0x35, 0x35, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6c, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x20, 0x3a, 0x20, 0x7b, 0x7d, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x62, 0x65, 0x65, 0x72, + 0x2d, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x34, 0x39, 0x32, 0x62, + 0x38, 0x64, 0x66, 0x32, 0x32, 0x61, 0x34, 0x64, 0x35, 0x36, 0x65, 0x36, + 0x5f, 0x38, 0x62, 0x38, 0x30, 0x39, 0x35, 0x38, 0x61, 0x22, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x69, 0x64, + 0x22, 0x20, 0x3a, 0x20, 0x22, 0x77, 0x65, 0x79, 0x65, 0x72, 0x62, 0x61, + 0x63, 0x68, 0x65, 0x72, 0x5f, 0x62, 0x72, 0x65, 0x77, 0x69, 0x6e, 0x67, + 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2d, 0x65, 0x6c, 0x65, + 0x76, 0x65, 0x6e, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x69, 0x64, 0x22, 0x20, + 0x3a, 0x20, 0x22, 0x64, 0x75, 0x63, 0x6b, 0x5f, 0x72, 0x61, 0x62, 0x62, + 0x69, 0x74, 0x5f, 0x63, 0x72, 0x61, 0x66, 0x74, 0x5f, 0x62, 0x72, 0x65, + 0x77, 0x65, 0x72, 0x79, 0x2d, 0x64, 0x75, 0x63, 0x6b, 0x5f, 0x72, 0x61, + 0x62, 0x62, 0x69, 0x74, 0x5f, 0x62, 0x72, 0x6f, 0x77, 0x6e, 0x5f, 0x61, + 0x6c, 0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x20, 0x3a, 0x20, + 0x22, 0x62, 0x65, 0x65, 0x72, 0x2d, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, + 0x5f, 0x34, 0x39, 0x32, 0x62, 0x38, 0x64, 0x66, 0x32, 0x32, 0x61, 0x34, + 0x64, 0x35, 0x36, 0x65, 0x36, 0x5f, 0x62, 0x37, 0x66, 0x66, 0x36, 0x62, + 0x36, 0x38, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x22, 0x20, 0x3a, 0x20, 0x7b, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x22, + 0x20, 0x3a, 0x20, 0x30, 0x2e, 0x36, 0x37, 0x31, 0x33, 0x37, 0x32, 0x36, + 0x35, 0x31, 0x31, 0x32, 0x37, 0x34, 0x35, 0x34, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x5d, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x22, 0x74, 0x6f, 0x6f, 0x6b, 0x22, 0x20, 0x3a, 0x20, 0x38, + 0x37, 0x35, 0x37, 0x34, 0x37, 0x30, 0x35, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x22, 0x66, 0x61, 0x63, 0x65, 0x74, 0x73, 0x22, 0x20, 0x3a, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x65, 0x72, 0x6d, + 0x73, 0x22, 0x20, 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x22, 0x20, + 0x3a, 0x20, 0x33, 0x32, 0x37, 0x36, 0x31, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6f, 0x74, 0x68, 0x65, 0x72, + 0x22, 0x20, 0x3a, 0x20, 0x33, 0x30, 0x37, 0x32, 0x35, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x22, 0x20, 0x3a, 0x20, 0x39, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x65, 0x72, 0x6d, 0x73, + 0x22, 0x20, 0x3a, 0x20, 0x5b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x20, 0x3a, 0x20, 0x37, 0x38, 0x32, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x65, 0x72, 0x6d, 0x22, 0x20, + 0x3a, 0x20, 0x22, 0x68, 0x6f, 0x70, 0x73, 0x22, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, + 0x20, 0x3a, 0x20, 0x34, 0x33, 0x32, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x74, 0x65, 0x72, 0x6d, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x62, 0x65, 0x65, + 0x72, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x74, 0x65, 0x72, 0x6d, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x61, 0x6c, 0x65, + 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x22, 0x20, 0x3a, 0x20, 0x33, 0x36, 0x35, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x65, 0x72, 0x6d, 0x22, 0x20, 0x3a, + 0x20, 0x22, 0x6d, 0x61, 0x6c, 0x74, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x20, 0x3a, 0x20, 0x33, 0x32, + 0x37, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x20, 0x3a, 0x20, 0x31, 0x33, 0x30, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x65, 0x72, 0x6d, 0x22, 0x20, 0x3a, + 0x20, 0x22, 0x68, 0x6f, 0x70, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x5b, 0x72, 0x6f, 0x77, 0x73, 0x5b, 0x0a, 0x20, 0x20, 0x20, 0x22, + 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x20, 0x3a, + 0x20, 0x30, 0x2e, 0x38, 0x36, 0x34, 0x38, 0x30, 0x32, 0x31, 0x31, 0x36, + 0x34, 0x36, 0x32, 0x31, 0x33, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x22, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x20, 0x3a, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x66, 0x72, 0x6f, 0x6d, 0x22, + 0x20, 0x3a, 0x20, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x66, 0x61, 0x63, 0x65, 0x74, 0x73, 0x22, 0x20, 0x3a, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, + 0x65, 0x72, 0x6d, 0x73, 0x22, 0x20, 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, + 0x69, 0x7a, 0x65, 0x22, 0x20, 0x3a, 0x20, 0x35, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x20, 0x3a, 0x20, 0x6e, + 0x75, 0x6c, 0x6c, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x73, 0x69, 0x7a, 0x65, 0x22, 0x20, 0x3a, 0x20, 0x35, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, + 0x20, 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x6c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x22, 0x20, 0x3a, 0x20, 0x30, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6d, 0x61, 0x74, + 0x63, 0x68, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x68, 0x6f, 0x70, 0x73, 0x22, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x22, 0x20, 0x3a, 0x20, 0x31, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x66, 0x75, + 0x7a, 0x7a, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x22, 0x20, 0x3a, 0x20, 0x30, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, + 0x68, 0x74, 0x22, 0x20, 0x3a, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x65, 0x78, 0x70, 0x6c, 0x61, + 0x69, 0x6e, 0x22, 0x20, 0x3a, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x0a, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x7d, 0x0a +}; + +/* Malformed FTS response #2 */ +static const char JSON_fts_bad2[] = { + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x22, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, + 0x68, 0x69, 0x74, 0x73, 0x22, 0x20, 0x3a, 0x20, 0x37, 0x39, 0x32, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, + 0x20, 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x22, 0x20, + 0x3a, 0x20, 0x32, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x22, 0x20, 0x3a, 0x20, 0x32, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x66, 0x61, 0x69, 0x6c, 0x65, + 0x64, 0x22, 0x20, 0x3a, 0x20, 0x30, 0x0a, 0x20, 0x20, 0x20, 0x7d, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x22, 0x68, 0x69, 0x74, 0x73, 0x22, 0x20, 0x3a, + 0x20, 0x5b, 0x5d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x22, 0x74, 0x6f, 0x6f, + 0x6b, 0x22, 0x20, 0x3a, 0x20, 0x38, 0x37, 0x35, 0x37, 0x34, 0x37, 0x30, + 0x35, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x22, 0x66, 0x61, 0x63, 0x65, 0x74, + 0x73, 0x22, 0x20, 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x22, 0x20, 0x3a, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x22, 0x20, 0x3a, 0x20, 0x33, 0x32, 0x37, 0x36, + 0x31, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x22, 0x20, 0x3a, 0x20, 0x33, 0x30, + 0x37, 0x32, 0x35, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x22, 0x20, 0x3a, 0x20, + 0x22, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x22, 0x20, 0x3a, 0x20, + 0x39, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x22, 0x20, 0x3a, 0x20, 0x5b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, + 0x20, 0x3a, 0x20, 0x37, 0x38, 0x32, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x74, 0x65, 0x72, 0x6d, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x68, 0x6f, 0x70, + 0x73, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x20, 0x3a, 0x20, 0x34, 0x33, 0x32, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x65, 0x72, 0x6d, 0x22, 0x20, + 0x3a, 0x20, 0x22, 0x62, 0x65, 0x65, 0x72, 0x22, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x65, 0x72, 0x6d, 0x22, 0x20, + 0x3a, 0x20, 0x22, 0x61, 0x6c, 0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x20, 0x3a, 0x20, 0x33, 0x36, + 0x35, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, + 0x65, 0x72, 0x6d, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x6d, 0x61, 0x6c, 0x74, + 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x22, 0x20, 0x3a, 0x20, 0x33, 0x32, 0x37, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x20, + 0x3a, 0x20, 0x31, 0x33, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, + 0x65, 0x72, 0x6d, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x68, 0x6f, 0x70, 0x22, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x5b, 0x72, 0x6f, 0x77, 0x73, + 0x5b, 0x0a, 0x20, 0x20, 0x20, 0x22, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x63, + 0x6f, 0x72, 0x65, 0x22, 0x20, 0x3a, 0x20, 0x30, 0x2e, 0x38, 0x36, 0x34, + 0x38, 0x30, 0x32, 0x31, 0x31, 0x36, 0x34, 0x36, 0x32, 0x31, 0x33, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x22, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x22, 0x20, 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x66, 0x72, 0x6f, 0x6d, 0x22, 0x20, 0x3a, 0x20, 0x30, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x66, 0x61, 0x63, 0x65, 0x74, + 0x73, 0x22, 0x20, 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x22, 0x20, + 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x20, 0x3a, + 0x20, 0x35, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x22, 0x20, + 0x3a, 0x20, 0x22, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x22, 0x20, 0x3a, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x20, + 0x3a, 0x20, 0x35, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x20, 0x3a, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x70, 0x72, 0x65, + 0x66, 0x69, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x20, + 0x3a, 0x20, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x22, 0x20, 0x3a, 0x20, + 0x22, 0x68, 0x6f, 0x70, 0x73, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x22, + 0x20, 0x3a, 0x20, 0x31, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x22, 0x66, 0x75, 0x7a, 0x7a, 0x69, 0x6e, 0x65, 0x73, + 0x73, 0x22, 0x20, 0x3a, 0x20, 0x30, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x68, + 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x22, 0x20, 0x3a, 0x20, + 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x65, 0x78, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x22, 0x20, 0x3a, 0x20, + 0x66, 0x61, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x7d, + 0x0a +}; + +/* Malformed N1QL response */ +static const char JSON_n1ql_bad[] = { + 0x5b, 0x73, 0x6f, 0x6e, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x73, 0x22, 0x3a, 0x20, 0x5b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5d, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x22, 0x3a, 0x20, 0x22, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x73, 0x22, 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x22, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x54, + 0x69, 0x6d, 0x65, 0x22, 0x3a, 0x20, 0x22, 0x37, 0x34, 0x2e, 0x36, 0x38, + 0x31, 0x30, 0x33, 0x6d, 0x73, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x65, 0x78, 0x65, 0x5d, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x3a, + 0x20, 0x22, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, + 0x22, 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x54, 0x69, 0x6d, + 0x65, 0x22, 0x3a, 0x20, 0x22, 0x37, 0x34, 0x2e, 0x36, 0x38, 0x31, 0x30, + 0x33, 0x6d, 0x73, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x54, 0x69, 0x6d, 0x65, 0x22, 0x3a, 0x20, 0x22, 0x37, 0x34, 0x2e, 0x35, + 0x37, 0x39, 0x39, 0x34, 0x39, 0x6d, 0x73, 0x22, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x20, 0x30, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x3a, 0x20, 0x30, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x7d, 0x0a, 0x5d, 0x0a +}; + +/* N1QL response without rows */ +static const char JSON_n1ql_empty[] = { + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x49, 0x44, 0x22, 0x3a, 0x20, 0x22, 0x35, 0x33, 0x36, 0x31, + 0x65, 0x35, 0x66, 0x36, 0x2d, 0x65, 0x33, 0x34, 0x33, 0x2d, 0x34, 0x35, + 0x30, 0x34, 0x2d, 0x61, 0x31, 0x36, 0x62, 0x2d, 0x35, 0x38, 0x61, 0x66, + 0x64, 0x64, 0x66, 0x31, 0x66, 0x36, 0x66, 0x31, 0x22, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x22, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x22, 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x2a, 0x22, 0x3a, 0x20, 0x22, 0x2a, 0x22, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x3a, 0x20, 0x5b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x5d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x3a, 0x20, 0x22, 0x73, 0x75, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x3a, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x65, 0x6c, 0x61, + 0x70, 0x73, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x3a, 0x20, 0x22, + 0x32, 0x32, 0x2e, 0x33, 0x31, 0x35, 0x38, 0x32, 0x33, 0x6d, 0x73, 0x22, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x65, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, + 0x22, 0x3a, 0x20, 0x22, 0x32, 0x32, 0x2e, 0x32, 0x36, 0x37, 0x30, 0x37, + 0x32, 0x6d, 0x73, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x22, 0x3a, 0x20, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, + 0x69, 0x7a, 0x65, 0x22, 0x3a, 0x20, 0x30, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x7d, 0x0a, 0x0a +}; + +/* N1QL response with rows */ +static const char JSON_n1ql_nonempty[] = { + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x49, 0x44, 0x22, 0x3a, 0x20, 0x22, 0x61, 0x38, 0x66, 0x37, + 0x64, 0x62, 0x62, 0x62, 0x2d, 0x66, 0x30, 0x35, 0x35, 0x2d, 0x34, 0x62, + 0x38, 0x33, 0x2d, 0x38, 0x39, 0x31, 0x32, 0x2d, 0x35, 0x34, 0x34, 0x31, + 0x64, 0x64, 0x63, 0x65, 0x32, 0x38, 0x31, 0x30, 0x22, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x22, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x22, 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x2a, 0x22, 0x3a, 0x20, 0x22, 0x2a, 0x22, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x3a, 0x20, 0x5b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x5c, 0x75, 0x30, + 0x30, 0x33, 0x63, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x28, 0x35, + 0x30, 0x20, 0x62, 0x29, 0x5c, 0x75, 0x30, 0x30, 0x33, 0x65, 0x22, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x5d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x22, 0x3a, 0x20, 0x22, 0x73, 0x75, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x3a, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x65, 0x6c, 0x61, 0x70, + 0x73, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x3a, 0x20, 0x22, 0x36, + 0x2e, 0x33, 0x32, 0x33, 0x30, 0x32, 0x36, 0x6d, 0x73, 0x22, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x3a, + 0x20, 0x22, 0x36, 0x2e, 0x32, 0x39, 0x38, 0x37, 0x38, 0x35, 0x6d, 0x73, + 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, + 0x3a, 0x20, 0x31, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, 0x69, 0x7a, 0x65, + 0x22, 0x3a, 0x20, 0x36, 0x32, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x7d, 0x0a, 0x0a +}; diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_list.cc b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_list.cc new file mode 100644 index 00000000..d057e358 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_list.cc @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include +#include +#include "list.h" + +typedef struct { + lcb_list_t list; + const char *desc; +} todo_t; + +class List : public ::testing::Test +{ +}; + +TEST_F(List, basicTests) +{ + todo_t root; + + memset(&root, 0, sizeof(todo_t)); + lcb_list_init(&root.list); + EXPECT_EQ(&root.list, root.list.next); + EXPECT_EQ(&root.list, root.list.prev); + + todo_t t0 = {{NULL, NULL}, "break"}; + lcb_list_append(&root.list, &t0.list); + EXPECT_EQ(&t0.list, root.list.next); + EXPECT_EQ(&t0.list, root.list.prev); + + lcb_list_delete(&t0.list); + EXPECT_EQ(NULL, t0.list.next); + EXPECT_EQ(NULL, t0.list.prev); + EXPECT_EQ(&root.list, root.list.next); + EXPECT_EQ(&root.list, root.list.prev); + + todo_t t1 = {{NULL, NULL}, "write"}; + EXPECT_STREQ("write", t1.desc); + lcb_list_append(&root.list, &t1.list); + EXPECT_EQ(&t1.list, root.list.next); + EXPECT_EQ(&t1.list, root.list.prev); + + todo_t t2 = {{NULL, NULL}, "test"}; + EXPECT_STREQ("test", t2.desc); + lcb_list_append(&root.list, &t2.list); + EXPECT_EQ(&t1.list, root.list.next); + EXPECT_EQ(&t2.list, root.list.prev); + + todo_t t3 = {{NULL, NULL}, "refactor"}; + EXPECT_STREQ("refactor", t3.desc); + lcb_list_append(&root.list, &t3.list); + EXPECT_EQ(&t1.list, root.list.next); + EXPECT_EQ(&t3.list, root.list.prev); + + todo_t t4 = {{NULL, NULL}, "read"}; + EXPECT_STREQ("read", t4.desc); + lcb_list_prepend(&root.list, &t4.list); + EXPECT_EQ(&t4.list, root.list.next); + EXPECT_EQ(&t3.list, root.list.prev); + + lcb_list_t *ii = root.list.next; + todo_t *tt; + + tt = LCB_LIST_ITEM(ii, todo_t, list); + EXPECT_STREQ("read", tt->desc); + ii = ii->next; + tt = LCB_LIST_ITEM(ii, todo_t, list); + EXPECT_STREQ("write", tt->desc); + ii = ii->next; + tt = LCB_LIST_ITEM(ii, todo_t, list); + EXPECT_STREQ("test", tt->desc); + ii = ii->next; + tt = LCB_LIST_ITEM(ii, todo_t, list); + EXPECT_STREQ("refactor", tt->desc); + + lcb_list_t *nn; + LCB_LIST_SAFE_FOR(ii, nn, &root.list) { + tt = LCB_LIST_ITEM(ii, todo_t, list); + lcb_list_delete(&tt->list); + memset(tt, 0, sizeof(todo_t)); + } + EXPECT_EQ(&root.list, root.list.next); + EXPECT_EQ(&root.list, root.list.prev); +} + +typedef struct { + lcb_list_t list; + int number; +} num_t; + +int ascending(lcb_list_t *a, lcb_list_t *b) +{ + num_t *aa, *bb; + + aa = LCB_LIST_ITEM(a, num_t, list); + bb = LCB_LIST_ITEM(b, num_t, list); + if (aa->number > bb->number) { + return 1; + } else if (aa->number < bb->number) { + return -1; + } else { + return 0; + } +} + +TEST_F(List, sortedListTest) +{ + num_t root; + + memset(&root, 0, sizeof(num_t)); + lcb_list_init(&root.list); + + num_t n0 = {{NULL, NULL}, 0}; + lcb_list_add_sorted(&root.list, &n0.list, ascending); + num_t n3 = {{NULL, NULL}, 3}; + lcb_list_add_sorted(&root.list, &n3.list, ascending); + num_t n2 = {{NULL, NULL}, 2}; + lcb_list_add_sorted(&root.list, &n2.list, ascending); + num_t n7 = {{NULL, NULL}, 7}; + lcb_list_add_sorted(&root.list, &n7.list, ascending); + num_t n1 = {{NULL, NULL}, 1}; + lcb_list_add_sorted(&root.list, &n1.list, ascending); + + lcb_list_t *ii = root.list.next; + num_t *nn; + nn = LCB_LIST_ITEM(ii, num_t, list); + EXPECT_EQ(0, nn->number); + ii = ii->next; + nn = LCB_LIST_ITEM(ii, num_t, list); + EXPECT_EQ(1, nn->number); + ii = ii->next; + nn = LCB_LIST_ITEM(ii, num_t, list); + EXPECT_EQ(2, nn->number); + ii = ii->next; + nn = LCB_LIST_ITEM(ii, num_t, list); + EXPECT_EQ(3, nn->number); + ii = ii->next; + nn = LCB_LIST_ITEM(ii, num_t, list); + EXPECT_EQ(7, nn->number); + ii = ii->next; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_logger.cc b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_logger.cc new file mode 100644 index 00000000..65547eea --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_logger.cc @@ -0,0 +1,65 @@ +#include "config.h" +#include +#include +#include "logging.h" +#include "internal.h" +#include + +using namespace std; + +class Logger : public ::testing::Test +{ +}; + + +struct MyLogprocs : lcb_logprocs { + set messages; +}; + +extern "C" { +static void fallback_logger(lcb_logprocs *procs, unsigned int, + const char *, int, const char *, + int, const char *fmt, va_list ap) +{ + char buf[2048]; + vsprintf(buf, fmt, ap); + EXPECT_FALSE(procs == NULL); + MyLogprocs *myprocs = static_cast(procs); + myprocs->messages.insert(buf); +} +} + +TEST_F(Logger, testLogger) +{ + lcb_t instance; + lcb_error_t err; + + lcb_create(&instance, NULL); + MyLogprocs procs; + lcb_logprocs *ptrprocs = static_cast(&procs); + ptrprocs->version = 0; + memset(ptrprocs, 0, sizeof(*ptrprocs)); + + err = lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_LOGGER, ptrprocs); + ASSERT_EQ(LCB_SUCCESS, err); + + procs.v.v0.callback = fallback_logger; + + LCB_LOG_BASIC(instance->getSettings(), "foo"); + LCB_LOG_BASIC(instance->getSettings(), "bar"); + LCB_LOG_BASIC(instance->getSettings(), "baz"); + + set& msgs = procs.messages; + ASSERT_FALSE(msgs.find("foo") == msgs.end()); + ASSERT_FALSE(msgs.find("bar") == msgs.end()); + ASSERT_FALSE(msgs.find("baz") == msgs.end()); + msgs.clear(); + + // Try without a logger + err = lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_LOGGER, NULL); + ASSERT_EQ(LCB_SUCCESS, err); + LCB_LOG_BASIC(instance->getSettings(), "this should not appear"); + ASSERT_TRUE(msgs.empty()); + + lcb_destroy(instance); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_misc.cc b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_misc.cc new file mode 100644 index 00000000..e15619a7 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_misc.cc @@ -0,0 +1,24 @@ +#include "config.h" +#include "internal.h" +#include +#define LIBCOUCHBASE_INTERNAL 1 +#include + +class MiscTests : public ::testing::Test +{ +}; + +TEST_F(MiscTests, testSanityCheck) +{ + ASSERT_TRUE(lcb_verify_compiler_setup()); +} + +TEST_F(MiscTests, testGetTmpdir) +{ + const char *tmpdir = lcb_get_tmpdir(); + ASSERT_FALSE(tmpdir == NULL); + ASSERT_STRNE("", tmpdir); +} +TEST_F(MiscTests, testVersionG) { + ASSERT_GT(lcb_version_g, 0); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_n1qlstrings.cc b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_n1qlstrings.cc new file mode 100644 index 00000000..f7d5fac5 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_n1qlstrings.cc @@ -0,0 +1,18 @@ +#include "config.h" +#include +#include +#include "n1ql/n1ql-internal.h" + +class N1qLStringTests : public ::testing::Test { +}; + +TEST_F(N1qLStringTests, testParseTimeout) +{ + lcb_U32 value; + ASSERT_EQ(1500000, lcb_n1qlreq_parsetmo("1.5s")); + ASSERT_EQ(1500000, lcb_n1qlreq_parsetmo("1500ms")); + ASSERT_EQ(1500000, lcb_n1qlreq_parsetmo("1500000us")); + ASSERT_EQ(0, lcb_n1qlreq_parsetmo("blahblah")); + ASSERT_EQ(0, lcb_n1qlreq_parsetmo("124")); + ASSERT_EQ(0, lcb_n1qlreq_parsetmo("99z")); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_netbuf.cc b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_netbuf.cc new file mode 100644 index 00000000..39412079 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_netbuf.cc @@ -0,0 +1,446 @@ +#include +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#endif +#include "netbuf/netbuf.h" + + +#define BIG_BUF_SIZE 5000 +#define SMALL_BUF_SIZE 50 + +class NetbufTest : public ::testing::Test +{ +}; + +static void clean_check(nb_MGR *mgr) +{ + EXPECT_NE(0, netbuf_is_clean(mgr)); + netbuf_cleanup(mgr); +} + +TEST_F(NetbufTest, testCleanCheck) +{ + nb_MGR mgr; + netbuf_init(&mgr, NULL); + nb_SPAN span; + span.size = 500; + int rv = netbuf_mblock_reserve(&mgr, &span); + ASSERT_EQ(0, rv); + ASSERT_EQ(0, netbuf_is_clean(&mgr)); + netbuf_mblock_release(&mgr, &span); + ASSERT_NE(0, netbuf_is_clean(&mgr)); + + nb_IOV iov; + iov.iov_base = (void *)0x01; + iov.iov_len = 500; + netbuf_enqueue(&mgr, &iov); + ASSERT_EQ(0, netbuf_is_clean(&mgr)); + + unsigned toFlush = netbuf_start_flush(&mgr, &iov, 1, NULL); + ASSERT_EQ(500, toFlush); + netbuf_end_flush(&mgr, toFlush); + ASSERT_NE(0, netbuf_is_clean(&mgr)); + + clean_check(&mgr); +} + +TEST_F(NetbufTest, testBasic) +{ + nb_MGR mgr; + int rv; + int ii; + int n_bigspans = 20; + int n_smallspans = 2000; + + nb_SPAN spans_big[20]; + nb_SPAN spans_small[2000]; + netbuf_init(&mgr, NULL); + clean_check(&mgr); + netbuf_init(&mgr, NULL); + + + for (ii = 0; ii < n_bigspans; ii++) { + int filler = 'a' + ii; + nb_SPAN *span = spans_big + ii; + span->size = BIG_BUF_SIZE; + rv = netbuf_mblock_reserve(&mgr, span); + ASSERT_EQ(0, rv); + memset(SPAN_BUFFER(span), filler, span->size); + } + + for (ii = 0; ii < n_smallspans; ii++) { + nb_SPAN *span = spans_small + ii; + int filler = ii; + span->size = SMALL_BUF_SIZE; + rv = netbuf_mblock_reserve(&mgr, span); + ASSERT_EQ(0, rv); + filler = ii; + memset(SPAN_BUFFER(span), filler, span->size); + } + + for (ii = 0; ii < n_bigspans; ii++) { + char expected[BIG_BUF_SIZE]; + char *curbuf = SPAN_BUFFER(spans_big + ii); + + memset(expected, 'a' + ii, BIG_BUF_SIZE); + ASSERT_EQ(0, memcmp(curbuf, expected, BIG_BUF_SIZE)); + + netbuf_mblock_release(&mgr, spans_big + ii); + } + + for (ii = 0; ii < n_smallspans; ii++) { + char expected[SMALL_BUF_SIZE]; + char *curbuf = SPAN_BUFFER(spans_small + ii); + memset(expected, ii, SMALL_BUF_SIZE); + ASSERT_EQ(0, memcmp(curbuf, expected, SMALL_BUF_SIZE)); + netbuf_mblock_release(&mgr, spans_small + ii); + } + + { + nb_IOV iov[20]; + netbuf_start_flush(&mgr, iov, 20, NULL); + } + clean_check(&mgr); +} + +TEST_F(NetbufTest, testFlush) +{ + nb_MGR mgr; + nb_SETTINGS settings; + nb_SPAN span; + nb_SPAN spans[3]; + + int ii; + int rv; + nb_IOV iov[10]; + unsigned int sz; + + netbuf_default_settings(&settings); + settings.data_basealloc = 8; + netbuf_init(&mgr, &settings); + + span.size = 32; + rv = netbuf_mblock_reserve(&mgr, &span); + ASSERT_EQ(rv, 0); + + netbuf_enqueue_span(&mgr, &span); + sz = netbuf_start_flush(&mgr, iov, 1, NULL); + ASSERT_EQ(32, sz); + ASSERT_EQ(32, iov[0].iov_len); + netbuf_end_flush(&mgr, 20); + + sz = netbuf_start_flush(&mgr, iov, 1, NULL); + ASSERT_EQ(0, sz); + netbuf_end_flush(&mgr, 12); + netbuf_mblock_release(&mgr, &span); + + for (ii = 0; ii < 3; ii++) { + spans[ii].size = 50; + ASSERT_EQ(0, netbuf_mblock_reserve(&mgr, spans + ii)); + } + + for (ii = 0; ii < 3; ii++) { + netbuf_enqueue_span(&mgr, spans + ii); + } + + sz = netbuf_start_flush(&mgr, iov, 10, NULL); + ASSERT_EQ(150, sz); + netbuf_end_flush(&mgr, 75); + netbuf_reset_flush(&mgr); + sz = netbuf_start_flush(&mgr, iov, 10, NULL); + ASSERT_EQ(75, sz); + netbuf_end_flush(&mgr, 75); + sz = netbuf_start_flush(&mgr, iov, 10, NULL); + ASSERT_EQ(0, sz); + netbuf_mblock_release(&mgr, &spans[0]); + + spans[0].size = 20; + rv = netbuf_mblock_reserve(&mgr, &spans[0]); + ASSERT_EQ(0, rv); + netbuf_mblock_release(&mgr, &spans[0]); + + for (ii = 1; ii < 3; ii++) { + netbuf_mblock_release(&mgr, spans + ii); + } + + netbuf_dump_status(&mgr, stdout); + clean_check(&mgr); +} + +TEST_F(NetbufTest, testWrappingBuffers) +{ + nb_MGR mgr; + nb_SETTINGS settings; + int rv; + nb_SPAN span1, span2, span3; + +#ifdef NETBUFS_LIBC_PROXY + return; +#endif + + netbuf_default_settings(&settings); + settings.data_basealloc = 40; + netbuf_init(&mgr, &settings); + + span1.size = 16; + span2.size = 16; + + rv = netbuf_mblock_reserve(&mgr, &span1); + ASSERT_EQ(0, rv); + rv = netbuf_mblock_reserve(&mgr, &span2); + ASSERT_EQ(0, rv); + + ASSERT_EQ(span1.parent, span2.parent); + ASSERT_EQ(0, span1.offset); + ASSERT_EQ(16, span2.offset); + + /* Wewease Wodewick! */ + netbuf_mblock_release(&mgr, &span1); + ASSERT_EQ(16, span2.parent->start); + + /* So we have 8 bytes at the end.. */ + ASSERT_EQ(32, span2.parent->wrap); + span3.size = 10; + rv = netbuf_mblock_reserve(&mgr, &span3); + + ASSERT_EQ(0, rv); + ASSERT_EQ(10, span2.parent->cursor); + ASSERT_EQ(0, span3.offset); + ASSERT_EQ(10, span3.parent->cursor); + ASSERT_EQ(16, span3.parent->start); + + netbuf_mblock_release(&mgr, &span2); + ASSERT_EQ(0, span3.parent->start); + netbuf_mblock_release(&mgr, &span3); + + netbuf_dump_status(&mgr, stdout); + + span1.size = 20; + rv = netbuf_mblock_reserve(&mgr, &span1); + ASSERT_EQ(0, span1.offset); + ASSERT_EQ(20, span1.parent->cursor); + ASSERT_EQ(0, span1.parent->start); + ASSERT_EQ(20, span1.parent->wrap); + netbuf_dump_status(&mgr, stdout); + + netbuf_mblock_release(&mgr, &span1); + + clean_check(&mgr); +} + +static void assert_iov_eq(nb_IOV *iov, nb_SIZE offset, char expected) +{ + char *buf = (char *)iov->iov_base; + ASSERT_EQ(expected, buf[offset]); +} + +TEST_F(NetbufTest, testMultipleFlush) +{ + nb_SETTINGS settings; + nb_MGR mgr; + int rv; + nb_SIZE sz; + nb_SPAN span1, span2, span3; + nb_IOV iov[10]; + + netbuf_default_settings(&settings); + netbuf_init(&mgr, &settings); + + span1.size = 50; + span2.size = 50; + span3.size = 50; + + rv = netbuf_mblock_reserve(&mgr, &span1); + ASSERT_EQ(0, rv); + rv = netbuf_mblock_reserve(&mgr, &span2); + ASSERT_EQ(0, rv); + rv = netbuf_mblock_reserve(&mgr, &span3); + ASSERT_EQ(0, rv); + + netbuf_enqueue_span(&mgr, &span1); + netbuf_enqueue_span(&mgr, &span2); + + sz = netbuf_start_flush(&mgr, iov, 10, NULL); + ASSERT_EQ(100, sz); + + memset(SPAN_BUFFER(&span1), 'A', span1.size); + memset(SPAN_BUFFER(&span2), 'B', span2.size); + memset(SPAN_BUFFER(&span3), 'C', span3.size); + +#ifndef NETBUFS_LIBC_PROXY + ASSERT_EQ(100, iov->iov_len); + assert_iov_eq(iov, 0, 'A'); + assert_iov_eq(iov, 50, 'B'); + + netbuf_enqueue_span(&mgr, &span3); + sz = netbuf_start_flush(&mgr, &iov[1], 0, NULL); + ASSERT_EQ(sz, 50); + assert_iov_eq(&iov[1], 0, 'C'); + ASSERT_EQ(50, iov[1].iov_len); + + netbuf_dump_status(&mgr, stdout); + + netbuf_end_flush(&mgr, 100); + netbuf_dump_status(&mgr, stdout); + + netbuf_end_flush(&mgr, 50); + sz = netbuf_start_flush(&mgr, iov, 10, NULL); + ASSERT_EQ(0, sz); +#endif + + netbuf_mblock_release(&mgr, &span1); + netbuf_mblock_release(&mgr, &span2); + netbuf_mblock_release(&mgr, &span3); + clean_check(&mgr); +} + +TEST_F(NetbufTest, testCyclicFlush) +{ + nb_SPAN spans[10]; + nb_IOV iov[4]; + nb_MGR mgr; + nb_SETTINGS settings; + int niov; + unsigned nb; + + // Each call to netbuf_start_flush should be considered isolated; so that + // the next call to start_flush _never_ overlaps any data from the previous + // call to start_flush. Otherwise we might end up in a situation where + // the same data ends up being sent out twice. netbuf_reset_flush() should + // be called to invalidate any outstanding start_flush() calls, so that + // the next call to start_flush() will begin from the beginning of the + // send queue, rather than from the last call to start_flush(). + + netbuf_default_settings(&settings); + settings.data_basealloc = 50; + netbuf_init(&mgr, &settings); + + for (size_t ii = 0; ii < 5; ii++) { + spans[ii].size = 10; + netbuf_mblock_reserve(&mgr, &spans[ii]); + memset(SPAN_BUFFER(&spans[ii]), ii, 10); + netbuf_enqueue_span(&mgr, &spans[ii]); + nb = netbuf_start_flush(&mgr, iov, 1, &niov); + + ASSERT_EQ(10, nb); + ASSERT_EQ(1, niov); + } + // So far have 50 inside the span + + // flush the first span (should have 40 bytes remaining) + netbuf_end_flush(&mgr, 10); + for (size_t ii = 5; ii < 7; ii++) { + spans[ii].size = 10; + netbuf_mblock_reserve(&mgr, &spans[ii]); + netbuf_enqueue_span(&mgr, &spans[ii]); + memset(SPAN_BUFFER(&spans[ii]), ii, 10); + } + + nb = netbuf_start_flush(&mgr, iov, 4, &niov); + ASSERT_EQ(20, nb); + netbuf_end_flush(&mgr, 40); + netbuf_end_flush(&mgr, nb); + nb = netbuf_start_flush(&mgr, iov, 4, &niov); + ASSERT_EQ(0, nb); + for (size_t ii = 0; ii < 7; ii++) { + netbuf_mblock_release(&mgr, &spans[ii]); + } + clean_check(&mgr); +} + +typedef struct { + sllist_node slnode; + nb_SIZE size; + int is_flushed; + nb_SPAN spans[3]; + nb_SIZE nspans; +} my_PDU; + +static nb_SIZE pdu_callback(void *p, nb_SIZE hint, void *arg) +{ + my_PDU *pdu = (my_PDU *)p; + (void)arg; + if (hint >= pdu->size) { + pdu->is_flushed = 1; + } + return pdu->size; +} + +TEST_F(NetbufTest, testPduEnqueue) +{ + nb_SETTINGS settings; + nb_MGR mgr; + my_PDU pdu; + nb_IOV iov[10]; + nb_SIZE toflush; + int ii; + + netbuf_default_settings(&settings); + settings.data_basealloc = 1; + netbuf_init(&mgr, &settings); + + memset(&pdu, 0, sizeof pdu); + pdu.size = 24; + + for (ii = 0; ii < 3; ii++) { + pdu.spans[ii].size = 8; + netbuf_mblock_reserve(&mgr, pdu.spans + ii); + } + + for (ii = 0; ii < 3; ii++) { + netbuf_enqueue_span(&mgr, pdu.spans + ii); + } + + netbuf_pdu_enqueue(&mgr, &pdu, offsetof(my_PDU, slnode)); + + /** Start the flush */ + toflush = netbuf_start_flush(&mgr, iov, 2, NULL); + ASSERT_EQ(16, toflush); + netbuf_end_flush2(&mgr, toflush, pdu_callback, 0, NULL); + ASSERT_EQ(0, pdu.is_flushed); + + toflush = netbuf_start_flush(&mgr, iov, 10, NULL); + ASSERT_EQ(8, toflush); + + netbuf_end_flush2(&mgr, toflush, pdu_callback, 0, NULL); + ASSERT_EQ(1, pdu.is_flushed); + + for (ii = 0; ii < 3; ii++) { + netbuf_mblock_release(&mgr, pdu.spans + ii); + } + + clean_check(&mgr); +} + +TEST_F(NetbufTest, testOutOfOrder) +{ + nb_MGR mgr; + nb_SPAN spans[3]; + int ii; + + netbuf_init(&mgr, NULL); + + for (ii = 0; ii < 3; ii++) { + spans[ii].size = 10; + int rv = netbuf_mblock_reserve(&mgr, spans + ii); + ASSERT_EQ(0, rv); + } + + netbuf_mblock_release(&mgr, &spans[1]); + spans[1].size = 5; + + netbuf_mblock_reserve(&mgr, &spans[1]); + ASSERT_EQ(30, spans[1].offset); + + for (ii = 0; ii < 3; ii++) { + netbuf_mblock_release(&mgr, spans + ii); + } + + clean_check(&mgr); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_packet.cc b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_packet.cc new file mode 100644 index 00000000..93fed7a5 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_packet.cc @@ -0,0 +1,215 @@ +/** for ntohl/htonl */ +#ifndef _WIN32 +#include +#else +#include "winsock2.h" +#endif + +#include +#include "config.h" +#include +#include "packetutils.h" + +class Packet : public ::testing::Test +{ +}; + +class Pkt { +public: + Pkt() : pkt(NULL), len(0) {} + + void getq(const std::string& value, + lcb_uint32_t opaque, + lcb_uint16_t status = 0, + lcb_cas_t cas = 0, + lcb_uint32_t flags = 0) + { + protocol_binary_response_getq msg; + protocol_binary_response_header *hdr = &msg.message.header; + memset(&msg, 0, sizeof(msg)); + + hdr->response.magic = PROTOCOL_BINARY_RES; + hdr->response.opaque = opaque; + hdr->response.status = htons(status); + hdr->response.opcode = PROTOCOL_BINARY_CMD_GETQ; + hdr->response.cas = lcb_htonll(cas); + hdr->response.bodylen = htonl((lcb_uint32_t)value.size() + 4); + hdr->response.extlen = 4; + msg.message.body.flags = htonl(flags); + + // Pack the response + clear(); + len = sizeof(msg.bytes) + value.size(); + pkt = new char[len]; + + EXPECT_TRUE(pkt != NULL); + + memcpy(pkt, msg.bytes, sizeof(msg.bytes)); + + memcpy((char *)pkt + sizeof(msg.bytes), + value.c_str(), + (unsigned long)value.size()); + } + + void get(const std::string& key, const std::string& value, + lcb_uint32_t opaque, + lcb_uint16_t status = 0, + lcb_cas_t cas = 0, + lcb_uint32_t flags = 0) + { + protocol_binary_response_getq msg; + protocol_binary_response_header *hdr = &msg.message.header; + hdr->response.magic = PROTOCOL_BINARY_RES; + hdr->response.opaque = opaque; + hdr->response.cas = lcb_htonll(cas); + hdr->response.opcode = PROTOCOL_BINARY_CMD_GET; + hdr->response.keylen = htons((lcb_uint16_t)key.size()); + hdr->response.extlen = 4; + hdr->response.bodylen = htonl(key.size() + value.size() + 4); + hdr->response.status = htons(status); + msg.message.body.flags = flags; + + clear(); + len = sizeof(msg.bytes) + value.size() + key.size(); + pkt = new char[len]; + char *ptr = pkt; + + memcpy(ptr, msg.bytes, sizeof(msg.bytes)); + ptr += sizeof(msg.bytes); + memcpy(ptr , key.c_str(), (unsigned long)key.size()); + ptr += key.size(); + memcpy(ptr, value.c_str(), (unsigned long)value.size()); + } + + void rbWrite(rdb_IOROPE *ior) { + rdb_copywrite(ior, pkt, len); + } + + void rbWriteHeader(rdb_IOROPE *ior) { + rdb_copywrite(ior, pkt, 24); + } + + void rbWriteBody(rdb_IOROPE *ior) { + rdb_copywrite(ior, pkt + 24, len - 24); + } + + void writeGenericHeader(unsigned long bodylen, rdb_IOROPE *ior) { + protocol_binary_response_header hdr; + memset(&hdr, 0, sizeof(hdr)); + hdr.response.opcode = 0; + hdr.response.bodylen = htonl(bodylen); + rdb_copywrite(ior, hdr.bytes, sizeof(hdr.bytes)); + } + + ~Pkt() { + clear(); + } + + void clear() { + if (pkt != NULL) { + delete[] pkt; + } + pkt = NULL; + len = 0; + } + + size_t size() { + return len; + } + +private: + char *pkt; + size_t len; + Pkt(Pkt&); +}; + + + +TEST_F(Packet, testParseBasic) +{ + std::string value = "foo"; + rdb_IOROPE ior; + rdb_init(&ior, rdb_libcalloc_new()); + + Pkt pkt; + pkt.getq(value, 0); + pkt.rbWrite(&ior); + + lcb::MemcachedResponse pi; + memset(&pi, 0, sizeof(pi)); + unsigned wanted; + ASSERT_TRUE(pi.load(&ior, &wanted)); + + ASSERT_EQ(0, pi.status()); + ASSERT_EQ(PROTOCOL_BINARY_CMD_GETQ, pi.opcode()); + ASSERT_EQ(0, pi.opaque()); + ASSERT_EQ(7, pi.bodylen()); + ASSERT_EQ(3, pi.vallen()); + ASSERT_EQ(0, pi.keylen()); + ASSERT_EQ(4, pi.extlen()); + ASSERT_EQ(pi.bodylen(), rdb_get_nused(&ior)); + ASSERT_EQ(0, strncmp(value.c_str(), pi.value(), 3)); + + pi.release(&ior); + ASSERT_EQ(0, rdb_get_nused(&ior)); + rdb_cleanup(&ior); +} + +TEST_F(Packet, testParsePartial) +{ + rdb_IOROPE ior; + Pkt pkt; + rdb_init(&ior, rdb_libcalloc_new()); + + std::string value; + value.insert(0, 1024, '*'); + + lcb::MemcachedResponse pi; + + // Test where we're missing just one byte + pkt.writeGenericHeader(10, &ior); + unsigned wanted; + ASSERT_FALSE(pi.load(&ior, &wanted)); + + for (int ii = 0; ii < 9; ii++) { + char c = 'O'; + rdb_copywrite(&ior, &c, 1); + ASSERT_FALSE(pi.load(&ior, &wanted)); + } + char tmp = 'O'; + rdb_copywrite(&ior, &tmp, 1); + ASSERT_TRUE(pi.load(&ior, &wanted)); + pi.release(&ior); + rdb_cleanup(&ior); +} + + +TEST_F(Packet, testKeys) +{ + rdb_IOROPE ior; + rdb_init(&ior, rdb_libcalloc_new()); + std::string key = "a simple key"; + std::string value = "a simple value"; + Pkt pkt; + pkt.get(key, value, 1000, PROTOCOL_BINARY_RESPONSE_ETMPFAIL, 0xdeadbeef, 50); + pkt.rbWrite(&ior); + + lcb::MemcachedResponse pi; + unsigned wanted; + ASSERT_TRUE(pi.load(&ior, &wanted)); + + ASSERT_EQ(key.size(), pi.keylen()); + ASSERT_EQ(0, memcmp(key.c_str(), pi.key(), pi.keylen())); + ASSERT_EQ(value.size(), pi.vallen()); + ASSERT_EQ(0, memcmp(value.c_str(), pi.value(), pi.vallen())); + ASSERT_EQ(0xdeadbeef, pi.cas()); + ASSERT_EQ(PROTOCOL_BINARY_RESPONSE_ETMPFAIL, pi.status()); + ASSERT_EQ(PROTOCOL_BINARY_CMD_GET, pi.opcode()); + ASSERT_EQ(4, pi.extlen()); + ASSERT_EQ(4 + key.size() + value.size(), pi.bodylen()); + ASSERT_NE(pi.body(), pi.value()); + ASSERT_EQ(4 + key.size(), pi.value() - pi.body()); + + pi.release(&ior); + rdb_cleanup(&ior); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_ringbuffer.cc b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_ringbuffer.cc new file mode 100644 index 00000000..dd77f8c4 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_ringbuffer.cc @@ -0,0 +1,278 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include +#include +#include "ringbuffer.h" + +class Ringbuffer : public ::testing::Test +{ +protected: + // Helper function used for debugging ;) + void dump_buffer(ringbuffer_t *ring) { + const char *begin = (const char *)ringbuffer_get_start(ring); + const char *end = begin + ringbuffer_get_size(ring); + const char *rd = (const char *)ringbuffer_get_read_head(ring); + const char *wr = (const char *)ringbuffer_get_write_head(ring); + const char *cur; + + /* write head */ + fprintf(stderr, " "); + for (cur = begin; cur < end; cur++) { + if (cur == wr) { + fprintf(stderr, "w"); + } else { + fprintf(stderr, " "); + } + } + fprintf(stderr, "\n"); + + /* the buffer contents */ + fprintf(stderr, "|"); + for (cur = begin; cur < end; cur++) { + fprintf(stderr, "%c", *cur ? *cur : '-'); + } + fprintf(stderr, "|\n"); + + /* the read head */ + fprintf(stderr, " "); + for (cur = begin; cur < end; cur++) { + if (cur == rd) { + fprintf(stderr, "r"); + } else { + fprintf(stderr, " "); + } + } + fprintf(stderr, "\n"); + } +}; + +TEST_F(Ringbuffer, basicTests) +{ + ringbuffer_t ring; + char buffer[1024]; + int ii; + + EXPECT_NE(0, ringbuffer_initialize(&ring, 16)); + EXPECT_EQ(0, ringbuffer_read(&ring, buffer, 1)); + EXPECT_EQ(16, ringbuffer_write(&ring, "01234567891234567", 17)); + + for (ii = 0; ii < 2; ++ii) { + memset(buffer, 0, sizeof(buffer)); + EXPECT_EQ(16, ringbuffer_peek(&ring, buffer, 16)); + EXPECT_EQ(0, memcmp(buffer, "01234567891234567", 16)); + memset(buffer, 0, sizeof(buffer)); + EXPECT_EQ(10, ringbuffer_peek_at(&ring, 6, buffer, 10)); + EXPECT_EQ(0, memcmp(buffer, "67891234567", 10)); + } + + EXPECT_EQ(16, ringbuffer_read(&ring, buffer, 16)); + EXPECT_EQ(0, ringbuffer_read(&ring, buffer, 1)); + EXPECT_EQ(16, ringbuffer_write(&ring, "01234567891234567", 17)); + EXPECT_EQ(8, ringbuffer_read(&ring, buffer, 8)); + EXPECT_NE(0, ringbuffer_ensure_capacity(&ring, 9)); + EXPECT_EQ(32, ring.size); + EXPECT_EQ(ring.root, ring.read_head); + EXPECT_EQ(8, ringbuffer_read(&ring, buffer, 9)); + EXPECT_EQ(0, memcmp(buffer, "89123456", 8)); + + ringbuffer_destruct(&ring); + + // wrapped_buffer_test(); + // my_regression_1_test(); + +} + +TEST_F(Ringbuffer, wrappedBufferTest) +{ + ringbuffer_t ring; + char buffer[128]; + + EXPECT_NE(0, ringbuffer_initialize(&ring, 10)); + + memset(ringbuffer_get_start(&ring), 0, 10); + /* w + * |----------| + * r + */ + + /* put 8 chars into the buffer */ + EXPECT_EQ(8, ringbuffer_write(&ring, "01234567", 8)); + + /* w + * |01234567--| + * r + */ + + /* consume first 5 chars */ + EXPECT_EQ(5, ringbuffer_read(&ring, buffer, 5)); + EXPECT_EQ(0, memcmp(buffer, "01234", 5)); + + /* w + * |-----567--| + * r + */ + EXPECT_EQ(0, ringbuffer_is_continous(&ring, RINGBUFFER_WRITE, 5)); + EXPECT_NE(0, ringbuffer_is_continous(&ring, RINGBUFFER_WRITE, 2)); + + /* wrapped write: write 5 more chars */ + EXPECT_EQ(5, ringbuffer_write(&ring, "abcde", 5)); + + /* w + * |cde--567ab| + * r + */ + + EXPECT_EQ(0, ringbuffer_is_continous(&ring, RINGBUFFER_READ, 7)); + EXPECT_NE(0, ringbuffer_is_continous(&ring, RINGBUFFER_READ, 2)); + + /* wrapped read: read 6 chars */ + EXPECT_EQ(6, ringbuffer_read(&ring, buffer, 6)); + EXPECT_EQ(0, memcmp(buffer, "567abc", 6)); + /* w + * |-de-------| + * r + */ + ringbuffer_destruct(&ring); +} + +// This is a crash I noticed while I was debugging the tap code +TEST_F(Ringbuffer, regression1) +{ + ringbuffer_t ring; + struct lcb_iovec_st iov[2]; + ring.root = (char *)0x477a80; + ring.read_head = (char *)0x47b0a3; + ring.write_head = (char *)0x47b555; + ring.size = 16384; + ring.nbytes = 1202; + + ringbuffer_get_iov(&ring, RINGBUFFER_WRITE, iov); + // up to the end + EXPECT_EQ(ring.write_head, iov[0].iov_base); + EXPECT_EQ(1323, iov[0].iov_len); + + // then from the beginning + EXPECT_EQ(ring.root, iov[1].iov_base); + EXPECT_EQ(13859, iov[1].iov_len); +} + +TEST_F(Ringbuffer, replace) +{ + ringbuffer_t rb; + + EXPECT_EQ(1, ringbuffer_initialize(&rb, 16)); + EXPECT_TRUE(memset(rb.root, 0, rb.size) != NULL); + EXPECT_EQ(8, ringbuffer_write(&rb, "01234567", 8)); + EXPECT_EQ(0, memcmp(rb.root, "01234567\0\0\0\0\0\0\0\0", rb.size)); + /* w + * |01234567--------| + * r + */ + + EXPECT_EQ(2, ringbuffer_update(&rb, RINGBUFFER_READ, "ab", 2)); + EXPECT_EQ(8, rb.nbytes); + EXPECT_EQ(0, memcmp(rb.root, "ab234567\0\0\0\0\0\0\0\0", rb.size)); + /* w + * |ab234567--------| + * r + */ + + EXPECT_EQ(2, ringbuffer_update(&rb, RINGBUFFER_WRITE, "cd", 2)); + EXPECT_EQ(8, rb.nbytes); + EXPECT_EQ(0, memcmp(rb.root, "ab2345cd\0\0\0\0\0\0\0\0", rb.size)); + /* w + * |ab2345cd--------| + * r + */ + + ringbuffer_consumed(&rb, 3); + EXPECT_EQ(5, rb.nbytes); + EXPECT_EQ(rb.root + 3, rb.read_head); + /* w + * |ab2345cd--------| + * r + */ + + EXPECT_EQ(5, ringbuffer_update(&rb, RINGBUFFER_READ, "efghij", 6)); + EXPECT_EQ(5, rb.nbytes); + EXPECT_EQ(0, memcmp(rb.root, "ab2efghi\0\0\0\0\0\0\0\0", rb.size)); + /* w + * |ab2efghi--------| + * r + */ + + EXPECT_EQ(5, ringbuffer_update(&rb, RINGBUFFER_WRITE, "klmnop", 6)); + EXPECT_EQ(5, rb.nbytes); + EXPECT_EQ(0, memcmp(rb.root, "ab2klmno\0\0\0\0\0\0\0\0", rb.size)); + /* w + * |ab2klmno--------| + * r + */ + + EXPECT_EQ(10, ringbuffer_write(&rb, "0123456789", 10)); + EXPECT_EQ(15, rb.nbytes); + EXPECT_EQ(0, memcmp(rb.root, "892klmno01234567", rb.size)); + /* w + * |892klmno01234567| + * r + */ + + EXPECT_EQ(10, ringbuffer_update(&rb, RINGBUFFER_WRITE, "abcdefghij", 10)); + EXPECT_EQ(15, rb.nbytes); + EXPECT_EQ(0, memcmp(rb.root, "ij2klmnoabcdefgh", rb.size)); + /* w + * |ij2klmnoabcdefgh| + * r + */ + + ringbuffer_consumed(&rb, 6); + EXPECT_EQ(9, rb.nbytes); + EXPECT_EQ(rb.root + 9, rb.read_head); + /* w + * |ij2klmnoabcdefgh| + * r + */ + + EXPECT_EQ(8, ringbuffer_update(&rb, RINGBUFFER_READ, "12345678", 8)); + EXPECT_EQ(9, rb.nbytes); + EXPECT_EQ(0, memcmp(rb.root, "8j2klmnoa1234567", rb.size)); + /* w + * |8j2klmnoa1234567| + * r + */ + ringbuffer_destruct(&rb); +} + +TEST_F(Ringbuffer, memcpy) +{ + char buffer[1024]; + ringbuffer_t src, dst; + + EXPECT_EQ(1, ringbuffer_initialize(&src, 16)); + EXPECT_EQ(8, ringbuffer_write(&src, "01234567", 8)); + + EXPECT_EQ(1, ringbuffer_initialize(&dst, 16)); + + EXPECT_EQ(0, ringbuffer_memcpy(&dst, &src, 4)); + EXPECT_EQ(4, dst.nbytes); + EXPECT_EQ(4, ringbuffer_read(&dst, buffer, 4)); + EXPECT_EQ(0, memcmp(buffer, "0123", 4)); + + ringbuffer_destruct(&src); + ringbuffer_destruct(&dst); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_slist.cc b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_slist.cc new file mode 100644 index 00000000..79bcab3b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_slist.cc @@ -0,0 +1,429 @@ +#include +#include "sllist.h" +#include "sllist-inl.h" +#include +#include + +#ifndef ASSERT_NZ +#define ASSERT_NZ(e) ASSERT_NE(0, e) +#endif + +#ifndef ASSERT_Z +#define ASSERT_Z(e) ASSERT_EQ(0, e) +#endif + +class SListTests : public ::testing::Test +{ +}; + +struct my_elem { + int value; + sllist_node slnode; +}; + +TEST_F(SListTests, testBasic) +{ + sllist_root sl; + memset(&sl, 0, sizeof(sl)); + + ASSERT_TRUE(SLLIST_IS_EMPTY(&sl)); + my_elem elem1, elem2, elem3; + + sllist_append(&sl, &elem1.slnode); + ASSERT_NZ(sllist_contains(&sl, &elem1.slnode)); + ASSERT_FALSE(SLLIST_IS_EMPTY(&sl)); + + sllist_node *tmpnode = SLLIST_FIRST(&sl); + sllist_remove_head(&sl); + ASSERT_NE(tmpnode, SLLIST_FIRST(&sl)); + ASSERT_EQ(tmpnode, &elem1.slnode); + ASSERT_EQ(&elem1, SLLIST_ITEM(tmpnode, struct my_elem, slnode)); + ASSERT_TRUE(SLLIST_IS_EMPTY(&sl)); + + sllist_append(&sl, &elem1.slnode); + sllist_append(&sl, &elem2.slnode); + sllist_append(&sl, &elem3.slnode); + ASSERT_EQ(sl.last, &elem3.slnode); + ASSERT_EQ(SLLIST_FIRST(&sl), &elem1.slnode); + + // Test prepend + my_elem elem4; + sllist_prepend(&sl, &elem4.slnode); + tmpnode = SLLIST_FIRST(&sl); + ASSERT_EQ(tmpnode, &elem4.slnode); + sllist_node *cur; + int itercount = 0; + SLLIST_ITERBASIC(&sl, cur) { + itercount++; + } + ASSERT_EQ(4, itercount); +} + +#define BASIC_NELEM 3 +TEST_F(SListTests, testBasicIter) +{ + sllist_root sl; + my_elem elems[BASIC_NELEM]; + + memset(&sl, 0, sizeof(sl)); + memset(elems, 0, sizeof(elems)); + + for (int ii = 0; ii < BASIC_NELEM; ii++) { + sllist_append(&sl, &elems[ii].slnode); + } + + sllist_node *cur; + int itercount = 0; + SLLIST_ITERBASIC(&sl, cur) { + my_elem *elem = SLLIST_ITEM(cur, struct my_elem, slnode); + itercount++; + elem->value++; + } + + ASSERT_EQ(BASIC_NELEM, itercount); + for (int ii = 0; ii < BASIC_NELEM; ii++) { + ASSERT_EQ(1, elems[ii].value); + } +} + + +static void fillDynamicSlist(sllist_root *root, my_elem **ptrs, int nptrs) +{ + sllist_iterator iter; + SLLIST_ITERFOR(root, &iter) { + sllist_iter_remove(root, &iter); + } + + for (int ii = 0; ii < nptrs; ii++) { + free(ptrs[ii]); + ptrs[ii] = (my_elem *)calloc(1, sizeof(*ptrs[ii])); + sllist_append(root, &ptrs[ii]->slnode); + } +} + +TEST_F(SListTests, testExtendedIter) +{ + sllist_root sl; + my_elem *elemp[BASIC_NELEM] = { NULL }; + memset(&sl, 0, sizeof(sl)); + + fillDynamicSlist(&sl, elemp, BASIC_NELEM); + + // Delete all elements from the list + sllist_iterator iter; + SLLIST_ITERFOR(&sl, &iter) { + my_elem *elem = SLLIST_ITEM(iter.cur, struct my_elem, slnode); + sllist_iter_remove(&sl, &iter); + memset(elem, 0xff, sizeof(*elem)); + free(elem); + } + + ASSERT_TRUE(SLLIST_IS_EMPTY(&sl)); + memset(elemp, 0, sizeof(*elemp) * BASIC_NELEM); + + // Delete only the first element of the list. Repopulate + fillDynamicSlist(&sl, elemp, BASIC_NELEM); + SLLIST_ITERFOR(&sl, &iter) { + my_elem *elem = SLLIST_ITEM(iter.cur, struct my_elem, slnode); + if (elem == elemp[0]) { + sllist_iter_remove(&sl, &iter); + memset(elem, 0xff, sizeof(*elem)); + free(elem); + elemp[0] = NULL; + } + } + + int itercount = 0; + SLLIST_ITERFOR(&sl, &iter) { + sllist_iter_remove(&sl, &iter); + itercount++; + } + ASSERT_EQ(BASIC_NELEM-1, itercount); + ASSERT_TRUE(SLLIST_IS_EMPTY(&sl)); + + // Delete only the middle element + fillDynamicSlist(&sl, elemp, BASIC_NELEM); + SLLIST_ITERFOR(&sl, &iter) { + my_elem *elem = SLLIST_ITEM(iter.cur, struct my_elem, slnode); + if (elem == elemp[1]) { + sllist_iter_remove(&sl, &iter); + memset(elem, 0xff, sizeof(*elem)); + free(elem); + elemp[1] = NULL; + } + } + ASSERT_FALSE(SLLIST_IS_EMPTY(&sl)); + + // Delete only the last element + fillDynamicSlist(&sl, elemp, BASIC_NELEM); + SLLIST_ITERFOR(&sl, &iter) { + my_elem *elem = SLLIST_ITEM(iter.cur, struct my_elem, slnode); + if (elem == elemp[BASIC_NELEM-1]) { + sllist_iter_remove(&sl, &iter); + memset(elem, 0xff, sizeof(*elem)); + free(elem); + elemp[BASIC_NELEM-1] = NULL; + } + } + ASSERT_FALSE(SLLIST_IS_EMPTY(&sl)); + SLLIST_ITERFOR(&sl, &iter) { + my_elem *elem = SLLIST_ITEM(iter.cur, struct my_elem, slnode); + sllist_iter_remove(&sl, &iter); + free(elem); + } +} + +struct NumberedItem { + sllist_node slnode; + int value; +}; +static int +ni_compare(sllist_node *a, sllist_node *b) +{ + NumberedItem *na = SLLIST_ITEM(a, NumberedItem, slnode); + NumberedItem *nb = SLLIST_ITEM(b, NumberedItem, slnode); + return na->value - nb->value; +} +TEST_F(SListTests, testSort) +{ + sllist_root l; + memset(&l, 0, sizeof(l)); + NumberedItem items[10]; + for (unsigned ii = 0; ii < 10; ii++) { + items[ii].value = ii; + sllist_insert_sorted(&l, &items[ii].slnode, ni_compare); + } + + int last = -1; + sllist_node *cur; + SLLIST_FOREACH(&l, cur) { + NumberedItem *ni = SLLIST_ITEM(cur, NumberedItem, slnode); + ASSERT_EQ(last, ni->value-1); + last = ni->value; + } + + /** Insert another item */ + NumberedItem big1; + big1.value = 100; + sllist_insert_sorted(&l, &big1.slnode, ni_compare); + ASSERT_EQ(l.last, &big1.slnode); + + NumberedItem small1; + small1.value = -100; + sllist_insert_sorted(&l, &small1.slnode, ni_compare); + ASSERT_EQ(SLLIST_FIRST(&l), &small1.slnode); + + NumberedItem middle1; + middle1.value = 5; + sllist_insert_sorted(&l, &middle1.slnode, ni_compare); + NumberedItem *ni_next = SLLIST_ITEM(middle1.slnode.next, NumberedItem, slnode); + ASSERT_EQ(5, ni_next->value); + + ni_next = SLLIST_ITEM(items[3].slnode.next, NumberedItem, slnode); + ASSERT_EQ(&middle1.slnode, ni_next->slnode.next); +} + +template +class SList : public sllist_root +{ +public: + SList() { + clear(); + }; + + void append(T& memb) { + sllist_append(this, getNode(memb)); + } + + void prepend(T& memb) { + sllist_prepend(this, getNode(memb)); + } + + void insert(T& memb, int (*compar)(sllist_node*,sllist_node*)) { + sllist_insert_sorted(this, getNode(memb), compar); + } + + bool contains(T& memb) { + return sllist_contains(this, getNode(memb)) != 0; + } + + size_t size() { + return sllist_get_size(this); + } + + void remove(T& memb) { + sllist_remove(this, getNode(memb)); + } + + bool empty() { + return SLLIST_IS_EMPTY(this); + } + + void clear() { + SLLIST_FIRST(this) = NULL; + last = NULL; + } + + T& front() { + if (empty()) { + throw std::out_of_range("List is empty"); + } + return *llToItem(SLLIST_FIRST(this)); + } + + T& back() { + if (empty()) { + throw std::out_of_range("List is empty"); + } + return *llToItem(last); + } + + T& operator[](int ix) { + int cur = 0; + sllist_node *ll; + SLLIST_FOREACH(this, ll) { + if (cur++ == ix) { + return *llToItem(ll); + } + } + throw std::out_of_range("No such index"); + } + +private: + sllist_node * getNode(T& memb) const { + return &(memb.*m); + } + + T* llToItem(sllist_node *node) { + // Get the offset + T *obj = 0; + sllist_node *offset = &(obj->*m); + char *diff = (char *) offset; + return (T *) (((char *) node) - diff); + } +}; + +TEST_F(SListTests, testSandwichSort) +{ + // moar sort tests + SList sl; + NumberedItem itm_1, itm_2, itm_3; + itm_1.value = 1; + itm_2.value = 2; + itm_3.value = 3; + + // 1, 3, 2 + sl.insert(itm_1, ni_compare); + ASSERT_TRUE(sl.contains(itm_1)); + ASSERT_EQ(1, sl.size()); + ASSERT_EQ(1, sl[0].value); + + sl.insert(itm_3, ni_compare); + ASSERT_TRUE(sl.contains(itm_3)); + ASSERT_EQ(1, sl[0].value); + ASSERT_EQ(3, sl[1].value); + + sl.insert(itm_2, ni_compare); + ASSERT_TRUE(sl.contains(itm_2)); + ASSERT_EQ(3, sl.size()); + ASSERT_EQ(1, sl[0].value); + ASSERT_EQ(2, sl[1].value); + ASSERT_EQ(3, sl[2].value); + + sl.clear(); + // Insert 3,2,1 + sl.insert(itm_3, ni_compare); + sl.insert(itm_2, ni_compare); + sl.insert(itm_1, ni_compare); + ASSERT_EQ(1, sl[0].value); + ASSERT_EQ(2, sl[1].value); + ASSERT_EQ(3, sl[2].value); +} + +TEST_F(SListTests, testPrependSort) +{ + SList sl; + NumberedItem itm_1, itm_2, itm_3; + itm_1.value = 1; + itm_2.value = 2; + itm_3.value = 3; + + // 2, 3, 1 + sl.insert(itm_2, ni_compare); + sl.insert(itm_3, ni_compare); + ASSERT_EQ(2, sl.size()); + ASSERT_EQ(2, sl[0].value); + ASSERT_EQ(3, sl[1].value); + + // Prepend item 1 + sl.insert(itm_1, ni_compare); + ASSERT_EQ(3, sl.size()); + ASSERT_EQ(1, sl[0].value); + ASSERT_EQ(2, sl[1].value); + ASSERT_EQ(3, sl[2].value); +} + +// Ensure removing the tail item inside an iterator does what we want it to +TEST_F(SListTests, testRemoveTailIter) +{ + NumberedItem itm_1, itm_2, itm_3; + itm_1.value = 1; + itm_2.value = 2; + itm_3.value = 3; + SList sl; + sl.append(itm_1); + sl.append(itm_2); + sl.append(itm_3); + + ASSERT_EQ(1, sl.front().value); + ASSERT_EQ(3, sl.back().value); + + sllist_iterator iter; + bool removed = false; + SLLIST_ITERFOR(&sl, &iter) { + if (iter.cur == &itm_3.slnode) { + sllist_iter_remove(&sl, &iter); + removed = true; + break; + } + } + + ASSERT_TRUE(removed); + ASSERT_EQ(2, sl.size()); + ASSERT_EQ(1, sl.front().value); + ASSERT_EQ(2, sl.back().value); +} + +TEST_F(SListTests, testRemoveEmptyTailIter) +{ + NumberedItem itm_1; + SList sl; + sl.append(itm_1); + sllist_iterator iter; + SLLIST_ITERFOR(&sl, &iter) { + sllist_iter_remove(&sl, &iter); + } + ASSERT_TRUE(sl.empty()); +} + +TEST_F(SListTests, testRemoveFirstIter) +{ + NumberedItem itm_1, itm_2, itm_3; + SList sl; + itm_1.value = 1; + itm_2.value = 2; + itm_3.value = 3; + sl.append(itm_1); + sl.append(itm_2); + sl.append(itm_3); + sllist_iterator iter; + SLLIST_ITERFOR(&sl, &iter) { + if (iter.cur == &itm_1.slnode) { + sllist_iter_remove(&sl, &iter); + } + } + + ASSERT_EQ(2, sl.size()); + ASSERT_EQ(2, sl.front().value); + ASSERT_EQ(3, sl.back().value); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_strerror.cc b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_strerror.cc new file mode 100644 index 00000000..f4c3af1c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_strerror.cc @@ -0,0 +1,64 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include +#define LIBCOUCHBASE_INTERNAL 1 +#include + +class Strerror : public ::testing::Test +{ +}; + +TEST_F(Strerror, testNoCrash) +{ + for (int ii = -10; ii < 0xffff; ++ii) { + EXPECT_NE((const char *)NULL, + lcb_strerror(NULL, (lcb_error_t)ii)); + } +} + +TEST_F(Strerror, allCodesDocumented) +{ + const char *generic = lcb_strerror(NULL, (lcb_error_t)( LCB_MAX_ERROR -1)); + ASSERT_NE((const char *)NULL, generic); + for (int ii = 0; ii < LCB_MAX_ERROR_VAL; ++ii) { + EXPECT_STRNE(generic, + lcb_strerror(NULL, (lcb_error_t)ii)); + } + + for (int ii = LCB_MAX_ERROR_VAL; ii < LCB_MAX_ERROR; ++ii) { + EXPECT_STREQ(generic, + lcb_strerror(NULL, (lcb_error_t)ii)); + } + +} + +TEST_F(Strerror, testErrTypes) +{ + for (int ii = 0; ii < LCB_MAX_ERROR_VAL; ii++) { + lcb_error_t err = (lcb_error_t)ii; + ASSERT_NE(-1, LCB_EIFDATA(err)); + ASSERT_NE(-1, LCB_EIFFATAL(err)); + ASSERT_NE(-1, LCB_EIFINPUT(err)); + ASSERT_NE(-1, LCB_EIFNET(err)); + ASSERT_NE(-1, LCB_EIFPLUGIN(err)); + ASSERT_NE(-1, LCB_EIFTMP(err)); + } + + ASSERT_EQ(-1, lcb_get_errtype(LCB_MAX_ERROR_VAL)); + ASSERT_EQ(-1, lcb_get_errtype((lcb_error_t)-1)); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_string.cc b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_string.cc new file mode 100644 index 00000000..0c974235 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_string.cc @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include +#include +#include "simplestring.h" + +class String : public ::testing::Test +{ +}; + +TEST_F(String, testBasic) +{ + int rv; + lcb_string str; + rv = lcb_string_init(&str); + ASSERT_EQ(0, rv); + ASSERT_EQ(NULL, str.base); + ASSERT_EQ(0, str.nalloc); + ASSERT_EQ(0, str.nused); + + rv = lcb_string_append(&str, "Hello", 5); + ASSERT_EQ(0, rv); + + rv = lcb_string_append(&str, "blah", -1); + ASSERT_EQ(-1, rv); + + rv = lcb_string_appendz(&str, "blah"); + ASSERT_EQ(0, rv); + + ASSERT_EQ(0, strcmp("Helloblah", str.base)); + + lcb_string_erase_beginning(&str, 5); + ASSERT_EQ(0, strcmp("blah", str.base)); + + lcb_string_erase_end(&str, 4); + ASSERT_EQ(0, strcmp("", str.base)); + + lcb_string_clear(&str); + ASSERT_TRUE(str.base != NULL); + + lcb_string_release(&str); + ASSERT_EQ(NULL, str.base); +} + +TEST_F(String, testAdvance) +{ + int rv; + lcb_string str; + rv = lcb_string_init(&str); + ASSERT_EQ(0, rv); + + rv = lcb_string_reserve(&str, 30); + ASSERT_EQ(0, rv); + ASSERT_TRUE(str.nalloc >= 30); + ASSERT_EQ(0, str.nused); + + memcpy(lcb_string_tail(&str), "Hello", 5); + lcb_string_added(&str, 5); + ASSERT_EQ(5, str.nused); + ASSERT_EQ(0, strcmp(str.base, "Hello")); + lcb_string_release(&str); +} + + +TEST_F(String, testRbCopy) +{ + int rv; + ringbuffer_t rb; + rv = ringbuffer_initialize(&rb, 10); + ASSERT_TRUE(rv != 0); + + lcb_string str; + rv = lcb_string_init(&str); + ASSERT_EQ(0, rv); + + const char *txt = "The quick brown fox jumped over the lazy dog"; + const int ntxt = strlen(txt); + ringbuffer_ensure_capacity(&rb, ntxt); + + ASSERT_EQ(ntxt, ringbuffer_write(&rb, txt, ntxt)); + ASSERT_EQ(ntxt, rb.nbytes); + + rv = lcb_string_rbappend(&str, &rb, 0); + ASSERT_EQ(0, rv); + + ASSERT_EQ(0, strcmp(txt, str.base)); + ASSERT_EQ(ntxt, str.nused); + + lcb_string_clear(&str); + lcb_string_rbappend(&str, &rb, 1); + ASSERT_EQ(0, rb.nbytes); + ASSERT_EQ(0, strcmp(txt, str.base)); + + ringbuffer_destruct(&rb); + lcb_string_release(&str); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_urlencode.cc b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_urlencode.cc new file mode 100644 index 00000000..e187768e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/basic/t_urlencode.cc @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include +#include +#include + +class UrlEncoding : public ::testing::Test +{ +}; + +using lcb::strcodecs::urlencode; +using lcb::strcodecs::urldecode; + +TEST_F(UrlEncoding, plainTextTests) +{ + std::string input("abcdef"); + std::string exp("abcdef"); + std::string out; + ASSERT_TRUE(urlencode(input.begin(), input.end(), out)); + ASSERT_EQ(exp, out); +} + +TEST_F(UrlEncoding, plainTextWithSlashTests) +{ + std::string input("a/b/c/d/e/f/g/h/i/j"); + std::string out; + ASSERT_TRUE(urlencode(input.begin(), input.end(), out)); + ASSERT_EQ(input, out); +} + +TEST_F(UrlEncoding, plainTextWithSpaceTests) +{ + std::string out; + std::string input("a b c d e f g"); + std::string exp("a%20b%20c%20d%20e%20f%20g"); + ASSERT_TRUE(urlencode(input.begin(), input.end(), out)); + ASSERT_EQ(exp, out); +} + +TEST_F(UrlEncoding, encodedTextWithPlusAsApaceTests) +{ + std::string input("a+b+c+d+e+g+h"); + std::string out; + ASSERT_TRUE(urlencode(input.begin(), input.end(), out)); + ASSERT_EQ(input, out); +} + +TEST_F(UrlEncoding, encodedTextWithPlusAndHexAsApaceTests) +{ + std::string input("a+b%20c%20d+e+g+h"); + std::string out; + ASSERT_TRUE(urlencode(input.begin(), input.end(), out)); + ASSERT_EQ(input, out); +} + +TEST_F(UrlEncoding, mixedLegalTextTests) +{ + std::string input("a/b/c/d/e f g+32%20"); + std::string exp("a/b/c/d/e%20f%20g+32%20"); + std::string out; + + ASSERT_TRUE(urlencode(input.begin(), input.end(), out)); + ASSERT_EQ(exp, out); +} + +TEST_F(UrlEncoding, mixedIllegalEncodingTextTests) +{ + std::string input("a+ "); + std::string out; + ASSERT_FALSE(urlencode(input.begin(), input.end(), out)); +} + +TEST_F(UrlEncoding, internationalTest) +{ + std::string input("_design/beer/_view/all?startkey=\"\xc3\xb8l\""); + std::string exp("_design/beer/_view/all?startkey=%22%C3%B8l%22"); + std::string out; + ASSERT_TRUE(urlencode(input.begin(), input.end(), out)); + ASSERT_EQ(exp, out); +} + +TEST_F(UrlEncoding, internationalEncodedTest) +{ + std::string input("_design/beer/_view/all?startkey=%22%C3%B8l%22"); + std::string exp("_design/beer/_view/all?startkey=%22%C3%B8l%22"); + std::string out; + ASSERT_TRUE(urlencode(input.begin(), input.end(), out)); + ASSERT_EQ(exp, out); +} + +TEST_F(UrlEncoding, testDecode) +{ + char obuf[4096]; + + ASSERT_TRUE(urldecode("%22", obuf)) << "Single character"; + ASSERT_STREQ("\x22", obuf); + + ASSERT_TRUE(urldecode("Hello World", obuf)) << "No pct encode"; + ASSERT_STREQ("Hello World", obuf); + + ASSERT_TRUE(urldecode("Hello%20World", obuf)); + ASSERT_STREQ("Hello World", obuf); + + ASSERT_TRUE(urldecode("%2Ffoo%2Fbar%2Fbaz%2F", obuf)); + ASSERT_STREQ("/foo/bar/baz/", obuf); + + ASSERT_TRUE(urldecode("%01%02%03%04", obuf)) << "Multiple octets"; + ASSERT_STREQ("\x01\x02\x03\x04", obuf); + + ASSERT_TRUE(urldecode("%FFFF", obuf)) << "Recognize only first two hex digits"; + // Split the hex literal so we don't confuse the preprocessor + ASSERT_STREQ("\xff" "FF", obuf); + + // Error tests + ASSERT_FALSE(urldecode("%", obuf)); + ASSERT_FALSE(urldecode("%RR", obuf)) << "Invalid hex digits"; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/check-all.cc b/couchbase-sys/libcouchbase-2.7.0/tests/check-all.cc new file mode 100644 index 00000000..5770e672 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/check-all.cc @@ -0,0 +1,608 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Rather than hacking together a shell script or depending on some scripting + * language, we'll use a simple C++ application to run 'unit-tests' + * with the appropriate settings we need. + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "mocksupport/procutil.h" +#define CLIOPTS_ENABLE_CXX +#include "contrib/cliopts/cliopts.h" +#include + +#define TESTS_BASE "sock-tests;nonio-tests;rdb-tests;mc-tests;" +#define PLUGIN_ENV_VAR "LCB_IOPS_NAME" +#define LCB_SRCROOT_ENV_VAR "srcdir" +#define DEFAULT_TEST_NAMES TESTS_BASE "unit-tests" + +#ifdef _WIN32 +const char default_plugins_string[] = "select;iocp;libuv"; +#define PATHSEP "\\" +#define usleep(n) Sleep((n) / 1000) +#define setenv(key, value, ignored) SetEnvironmentVariable(key, value) +#else +#include +#include /* usleep */ +const char default_plugins_string[] = "select" +#if defined(HAVE_LIBEV3) || defined(HAVE_LIBEV4) +";libev" +#endif +#if defined(HAVE_LIBEVENT) || defined(HAVE_LIBEVENT2) +";libevent" +#endif +#ifdef HAVE_LIBUV +";libuv" +#endif +; +#define PATHSEP "/" +#endif + +typedef std::vector strlist; + +class TestConfiguration +{ + +public: + TestConfiguration() : + opt_debugger("debugger"), opt_plugins("plugins"), opt_jobs("jobs"), + opt_srcdir("srcdir"), opt_bindir("testdir"), opt_interactive("interactive"), + opt_verbose("verbose"), opt_cycles("repeat"), opt_libdir("libdir"), + opt_bins("tests"), opt_realcluster("cluster"), + opt_gtest_filter("gtest_filter"), + opt_gtest_break_on_failure("gtest_break_on_failure"), + opt_gtest_catch_exceptions("gtest_catch_exceptions") + { + opt_debugger.abbrev('d') + .description("Verbatim string to prepend to the binary command line"); + + opt_plugins.abbrev('p') + .description("semicolon-delimited list of plugins to test") + .setDefault(default_plugins_string); + opt_jobs.abbrev('j') + .description("Execute this many processes concurrently") + .setDefault(1); + + opt_srcdir.abbrev('S') + .description("root directory of source tree (for locating mock)") + .setDefault(getEffectiveSrcroot()); + + opt_bindir.abbrev('T') + .description("Directory where test binaries are located") + .setDefault(getEffectiveTestdir()); + + opt_interactive.abbrev('I') + .description("Set this to true when using an interactive debugger. This unblocks stdin"); + + opt_bins.abbrev('B') + .description("semicolon delimited list of tests to run") + .setDefault(DEFAULT_TEST_NAMES); + + opt_cycles.abbrev('n') + .description("Number of times to run the tests") + .setDefault(1); + + opt_libdir.abbrev('L') + .description("Directory where plugins are located. Useful on OS X"); + + opt_realcluster.abbrev('C') + .description("Path to real cluster"); + + opt_verbose.abbrev('v'); + } + + ~TestConfiguration() {} + + static void splitSemicolonString(const std::string &s, strlist &l) { + std::string cur; + + for (const char *c = s.c_str(); *c; c++) { + if (*c == ';') { + l.push_back(cur); + cur.clear(); + continue; + } + cur += *c; + } + + if (!cur.empty()) { + l.push_back(cur); + } + } + + bool parseOptions(int argc, char **argv) { + std::stringstream ss; + cliopts::Parser parser("check-all"); + + parser.addOption(opt_debugger); + parser.addOption(opt_plugins); + parser.addOption(opt_jobs); + parser.addOption(opt_srcdir); + parser.addOption(opt_bindir); + parser.addOption(opt_interactive); + parser.addOption(opt_verbose); + parser.addOption(opt_cycles); + parser.addOption(opt_libdir); + parser.addOption(opt_bins); + parser.addOption(opt_realcluster); + parser.addOption(opt_gtest_filter); + parser.addOption(opt_gtest_break_on_failure); + parser.addOption(opt_gtest_catch_exceptions); + + if (!parser.parse(argc, argv, false)) { + return false; + } + + using std::vector; + using std::string; + + const vector& args = parser.getRestArgs(); + for (size_t ii = 0; ii < args.size(); ii++) { + ss << args[ii] << " "; + } + + if (!opt_gtest_filter.result().empty()) { + ss << " " << "--gtest_filter=" << opt_gtest_filter.result(); + } + if (opt_gtest_break_on_failure.passed()) { + ss << " " << "--gtest_break_on_failure=1"; + } + if (opt_gtest_catch_exceptions.passed()) { + ss << " " << "--gtest_catch_exceptions=1"; + } + + binOptions = ss.str(); + srcroot = opt_srcdir.result(); + testdir = opt_bindir.result(); + debugger = opt_debugger.result(); + libDir = opt_libdir.result(); + realClusterEnv = opt_realcluster.result(); + + // Verbosity + isVerbose = opt_verbose.result(); + + // isInteractive + isInteractive = opt_interactive.result(); + + // Jobs + maxJobs = opt_jobs.result(); + maxCycles = opt_cycles.result(); + setJobsFromEnvironment(); + + + // Plugin list: + splitSemicolonString(opt_plugins.result(), plugins); + + // Test names: + splitSemicolonString(opt_bins.result(), testnames); + + // Set the library dir, if not set + if (libDir.empty()) { + libDir = testdir + "/../" + "lib"; + } + return true; + } + + // Sets up the command line, appending any debugger info and paths + std::string setupCommandline(std::string &name) { + std::stringstream ss; + std::string ret; + + if (!debugger.empty()) { + ss << debugger << " "; + } + + ss << testdir << PATHSEP << name; + + if (!binOptions.empty()) { + ss << " " << binOptions; + } + + return ss.str(); + } + + + // Options passed to the binary itself + std::string binOptions; + std::string srcroot; + std::string testdir; + std::string debugger; + std::string libDir; + std::string realClusterEnv; + + strlist plugins; + strlist testnames; + + bool isVerbose; + bool isInteractive; + int maxJobs; + int maxCycles; + int getVerbosityLevel() { + return opt_verbose.numSpecified(); + } + +private: + cliopts::StringOption opt_debugger; + cliopts::StringOption opt_plugins; + cliopts::UIntOption opt_jobs; + cliopts::StringOption opt_srcdir; + cliopts::StringOption opt_bindir; + cliopts::BoolOption opt_interactive; + cliopts::BoolOption opt_verbose; + cliopts::IntOption opt_cycles; + cliopts::StringOption opt_libdir; + cliopts::StringOption opt_bins; + cliopts::StringOption opt_realcluster; + cliopts::StringOption opt_gtest_filter; + cliopts::BoolOption opt_gtest_break_on_failure; + cliopts::BoolOption opt_gtest_catch_exceptions; + + void setJobsFromEnvironment() { + char *tmp = getenv("MAKEFLAGS"); + + if (tmp == NULL || *tmp == '\0') { + return; + } + + if (strstr(tmp, "-j")) { + maxJobs = 32; + + } else { + maxJobs = 1; + } + } + + std::string getEffectiveSrcroot() { + const char *tmp = getenv(LCB_SRCROOT_ENV_VAR); + if (tmp && *tmp) { + return tmp; + } + + return getDefaultSrcroot(); + } + + std::string getEffectiveTestdir() { + const char *tmp = getenv("outdir"); + if (tmp && *tmp) { + return tmp; + } + return getDefaultTestdir(); + } + +#ifndef _WIN32 + // Evaluated *before* + std::string getDefaultSrcroot() { + return "."; + } + + std::string getDefaultTestdir() { + return (srcroot + PATHSEP) + "tests"; + } + +#else + std::string getSelfDirname() { + DWORD result; + char pathbuf[4096] = { 0 }; + result = GetModuleFileName(NULL, pathbuf, sizeof(pathbuf)); + assert(result > 0); + assert(result < sizeof(pathbuf)); + + for (DWORD ii = result; ii; ii--) { + if (pathbuf[ii] == '\\') { + break; + } + pathbuf[ii] = '\0'; + } + return pathbuf; + } + // For windows, we reside in the same directory as the binaries + std::string getDefaultSrcroot() { + std::string dir = getSelfDirname(); + std::stringstream ss; + ss << dir; + + int components_max; + +#ifdef _MSC_VER + // Visual Studio projects are usually something like: + // $ROOT\VS\10.0\bin\Debug + // (1)..\bin, (2)..\10.0, (3)..\VS, (4)..\$ROOT + components_max = 4; +#else + // For MinGW, it's something like $ROOT\$BUILD\bin; so + // (1) ..\BUILD, (2) ..\ROOT + components_max = 2; +#endif + + for (int ii = 0; ii < components_max; ii++) { + ss << PATHSEP << ".."; + } + + return ss.str(); + } + + std::string getDefaultTestdir() { + return getSelfDirname(); + } +#endif +}; + + +static void setPluginEnvironment(std::string &name) +{ + const char *v = NULL; + if (name != "default") { + v = name.c_str(); + } + + setenv(PLUGIN_ENV_VAR, v, 1); + + fprintf(stderr, "%s=%s ... ", PLUGIN_ENV_VAR, name.c_str()); + struct lcb_cntl_iops_info_st ioi; + memset(&ioi, 0, sizeof(ioi)); + + lcb_error_t err = lcb_cntl(NULL, LCB_CNTL_GET, LCB_CNTL_IOPS_DEFAULT_TYPES, + &ioi); + if (err != LCB_SUCCESS) { + fprintf(stderr, "LCB Error 0x%x\n", err); + } else { + fprintf(stderr, "Plugin ID: 0x%x\n", ioi.v.v0.effective); + } +} + +static void setLinkerEnvironment(std::string &path) +{ +#ifdef _WIN32 + if (false) { + return; + } +#endif + + if (path.empty()) { + return; + } +#if __APPLE__ + const char *varname = "DYLD_LIBRARY_PATH"; +#else + const char *varname = "LD_LIBRARY_PATH"; +#endif + + const char *existing = getenv(varname); + std::string newenv; + if (existing) { + newenv += existing; + newenv += ":"; + } + newenv += path; + fprintf(stderr, "%s=%s\n", varname, newenv.c_str()); + setenv(varname, newenv.c_str(), 1); +} + +struct Process { + child_process_t proc_; + std::string commandline; + std::string logfileName; + std::string pluginName; + std::string testName; + bool exitedOk; + bool verbose; + + Process(std::string &plugin, std::string &name, std::string &cmd, + TestConfiguration &config) { + this->pluginName = plugin; + this->testName = name; + this->commandline = cmd; + this->verbose = config.isVerbose; + proc_.interactive = config.isInteractive; + this->logfileName = "check-all-" + pluginName + "-" + testName + ".log"; + } + + void writeLog(const char *msg) { + std::ofstream out(logfileName.c_str(), std::ios::app); + out << msg << std::endl; + out.close(); + } + + void setupPointers() { + memset(&proc_, 0, sizeof(proc_)); + + proc_.name = commandline.c_str(); + + if (!verbose) { + proc_.redirect = logfileName.c_str(); + } + } +}; + +class TestScheduler +{ +public: + TestScheduler(unsigned int lim) : limit(lim) { + + } + + typedef std::list proclist; + std::vector _all; + + proclist executing; + proclist scheduled; + proclist completed; + + unsigned int limit; + + void schedule(Process proc) { + _all.push_back(proc); + } + + + bool runAll() { + proclist::iterator iter; + scheduleAll(); + + while (!(executing.empty() && scheduled.empty())) { + while ((!scheduled.empty()) && executing.size() < limit) { + Process *proc = scheduled.front(); + scheduled.pop_front(); + invokeScheduled(proc); + } + + // Wait for them to complete + proclist to_remove_e; + for (iter = executing.begin(); iter != executing.end(); iter++) { + Process *cur = *iter; + int rv = wait_process(&cur->proc_, -1); + + if (rv == 0) { + char msg[2048]; + cur->exitedOk = cur->proc_.status == 0; + snprintf(msg, 2048, "REAP [%s] '%s' .. %s", + cur->pluginName.c_str(), + cur->commandline.c_str(), + cur->exitedOk ? "OK" : "FAIL"); + cur->writeLog(msg); + fprintf(stderr, "%s\n", msg); + cleanup_process(&cur->proc_); + to_remove_e.push_back(cur); + } + } + + for (iter = to_remove_e.begin(); iter != to_remove_e.end(); iter++) { + executing.remove(*iter); + completed.push_back(*iter); + } + + usleep(5000); + } + + for (iter = completed.begin(); iter != completed.end(); iter++) { + if (!(*iter)->exitedOk) { + return false; + } + } + + return true; + } + +private: + void scheduleAll() { + for (unsigned int ii = 0; ii < _all.size(); ii++) { + Process *p = &_all[ii]; + scheduled.push_back(p); + } + } + void invokeScheduled(Process *proc) { + proc->setupPointers(); + setPluginEnvironment(proc->pluginName); + char msg[2048]; + snprintf(msg, 2048, "START [%s] '%s'", + proc->pluginName.c_str(), + proc->commandline.c_str()); + proc->writeLog(msg); + fprintf(stderr, "%s\n", msg); + + int rv = create_process(&proc->proc_); + if (rv < 0) { + snprintf(msg, 2048, "FAIL couldn't invoke [%s] '%s'", + proc->pluginName.c_str(), + proc->commandline.c_str()); + proc->writeLog(msg); + fprintf(stderr, "%s\n", msg); + proc->exitedOk = false; + completed.push_back(proc); + return; + } else { + executing.push_back(proc); + } + } + +}; + +static bool runSingleCycle(TestConfiguration &config) +{ + TestScheduler scheduler(config.maxJobs); + setLinkerEnvironment(config.libDir); + for (strlist::iterator iter = config.plugins.begin(); + iter != config.plugins.end(); + iter++) { + + fprintf(stderr, "Testing with plugin '%s'\n", iter->c_str()); + + for (strlist::iterator iterbins = config.testnames.begin(); + iterbins != config.testnames.end(); + iterbins++) { + + std::string cmdline = config.setupCommandline(*iterbins); + scheduler.schedule(Process(*iter, *iterbins, cmdline, config)); + } + + } + + return scheduler.runAll(); + +} + +int main(int argc, char **argv) +{ +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif + + TestConfiguration config; + if (!config.parseOptions(argc, argv)) { + exit(EXIT_FAILURE); + } + + // Set the environment for 'srcdir' + std::stringstream ss; + fprintf(stderr, "%s=%s\n", LCB_SRCROOT_ENV_VAR, config.srcroot.c_str()); + setenv(LCB_SRCROOT_ENV_VAR, config.srcroot.c_str(), 1); + setenv("LCB_VERBOSE_TESTS", "1", 1); + + char loglevel_s[4096] = { 0 }; + if (config.getVerbosityLevel() > 0) { + sprintf(loglevel_s, "%d", config.getVerbosityLevel()); + setenv("LCB_LOGLEVEL", loglevel_s, 0); + } + + if (!config.realClusterEnv.empty()) { + // format the string + setenv("LCB_TEST_CLUSTER_CONF", config.realClusterEnv.c_str(), 0); + } + + for (int ii = 0; ii < config.maxCycles; ii++) { + if (!runSingleCycle(config)) { + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/htparse/t_basic.cc b/couchbase-sys/libcouchbase-2.7.0/tests/htparse/t_basic.cc new file mode 100644 index 00000000..fe531e1e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/htparse/t_basic.cc @@ -0,0 +1,193 @@ +#include +#include +#include +#include +#include "settings.h" + +using std::string; + +class HtparseTest : public ::testing::Test { +}; + +TEST_F(HtparseTest, testBasic) +{ + lcb_settings *settings = lcb_settings_new(); + // Allocate a parser + lcbht_pPARSER parser = lcbht_new(settings); + ASSERT_FALSE(parser == NULL); + + // Feed the parser some stuff + lcbht_RESPSTATE state; + + string buf; + buf = "HTTP/1.0 200 OK\r\n"; + + state = lcbht_parse(parser, buf.c_str(), buf.size()); + ASSERT_EQ(0, state); + + buf = "Connec"; + state = lcbht_parse(parser, buf.c_str(), buf.size()); + ASSERT_EQ(0, state); + buf = "tion: Keep-Alive\r\n"; + state = lcbht_parse(parser, buf.c_str(), buf.size()); + ASSERT_EQ(0, state); + buf += "Content-Length: 5\r\n\r\n"; + state = lcbht_parse(parser, buf.c_str(), buf.size()); + ASSERT_EQ(LCBHT_S_HEADER|LCBHT_S_HTSTATUS, state); + + lcbht_RESPONSE *resp = lcbht_get_response(parser); + ASSERT_EQ(200, resp->status); + + // Add some data into the body + buf = "H"; + state = lcbht_parse(parser, buf.c_str(), buf.size()); + ASSERT_EQ(0, state & LCBHT_S_ERROR); + ASSERT_STREQ("H", resp->body.base); + + buf = "ello"; + state = lcbht_parse(parser, buf.c_str(), buf.size()); + ASSERT_NE(0, state & LCBHT_S_DONE); + ASSERT_STREQ("Hello", resp->body.base); + + // Now find the header + + lcbht_free(parser); + lcb_settings_unref(settings); +} + +TEST_F(HtparseTest, testHeaderFunctions) +{ + lcb_settings *settings = lcb_settings_new(); + lcbht_pPARSER parser = lcbht_new(settings); + + string buf = "HTTP/1.0 200 OK\r\n" + "Connection: keep-alive\r\n" + "X-Server: dummy/1.0\r\n" + "Content-Type: application/json\r\n" + "Content-Length: 0\r\n" + "\r\n"; + lcbht_RESPSTATE state; + state = lcbht_parse(parser, buf.c_str(), buf.size()); + ASSERT_NE(0, state & LCBHT_S_DONE); + + lcbht_RESPONSE *resp = lcbht_get_response(parser); + // Now fine the header value stuff + ASSERT_STREQ("keep-alive", lcbht_get_resphdr(resp, "Connection")); + ASSERT_STREQ("dummy/1.0", lcbht_get_resphdr(resp, "X-Server")); + ASSERT_STREQ("application/json", lcbht_get_resphdr(resp, "Content-Type")); + + using std::map; + map hdrmap; + char **hdrlist = lcbht_make_resphdrlist(resp); + for (char **cur = hdrlist; *cur != NULL; cur += 2) { + string k = cur[0]; + string v = cur[1]; + hdrmap[k] = v; + free(cur[0]); + free(cur[1]); + } + free(hdrlist); + + ASSERT_EQ("keep-alive", hdrmap["Connection"]); + ASSERT_EQ("dummy/1.0", hdrmap["X-Server"]); + ASSERT_EQ("application/json", hdrmap["Content-Type"]); + + lcbht_free(parser); + lcb_settings_unref(settings); +} + +TEST_F(HtparseTest, testParseErrors) +{ + lcb_settings *settings = lcb_settings_new(); + lcbht_pPARSER parser = lcbht_new(settings); + + string buf = "blahblahblah"; + lcbht_RESPSTATE state = lcbht_parse(parser, buf.c_str(), buf.size()); + ASSERT_NE(0, state & LCBHT_S_ERROR); + lcbht_free(parser); + lcb_settings_unref(settings); +} + + +TEST_F(HtparseTest, testParseExtended) +{ + lcb_settings *settings = lcb_settings_new(); + lcbht_pPARSER parser = lcbht_new(settings); + lcbht_RESPONSE *resp; + + const char *body; + unsigned nbody, nused; + + string buf = "HTTP/1.0 200 OK\r\n" + "Connection: keep-alive\r\n" + "Content-Length: 5\r\n"; + + lcbht_RESPSTATE state; + state = lcbht_parse_ex(parser, buf.c_str(), buf.size(), &nused, &nbody, &body); + ASSERT_EQ(0, state & LCBHT_S_ERROR); + ASSERT_EQ(NULL, body); + ASSERT_EQ(buf.size(), nused); + ASSERT_EQ(0, nbody); + + resp = lcbht_get_response(parser); + buf = "\r\nHello"; + // Feed the buffer + state = lcbht_parse_ex(parser, buf.c_str(), buf.size(), &nused, &nbody, &body); + ASSERT_EQ(0, state & LCBHT_S_DONE); + ASSERT_EQ(5, nbody); + ASSERT_FALSE(NULL == body); + ASSERT_STREQ("Hello", body); + ASSERT_EQ(buf.size()-1, nused); + + size_t off = nused; + + // Parse again + state = lcbht_parse_ex(parser, buf.c_str()+off, buf.size()-off, &nused, &nbody, &body); + ASSERT_EQ(nused, buf.size()-off); + ASSERT_TRUE(body == NULL); + ASSERT_EQ(0, nbody); + ASSERT_NE(0, state & LCBHT_S_DONE); + ASSERT_EQ(0, state & LCBHT_S_ERROR); + ASSERT_EQ(0, resp->body.nused); + + lcbht_free(parser); + lcb_settings_unref(settings); +} + +TEST_F(HtparseTest, testCanKeepalive) +{ + lcb_settings *settings = lcb_settings_new(); + lcbht_pPARSER parser = lcbht_new(settings); + string buf = "HTTP/1.0 200 OK\r\n" + "Content-Length: 0\r\n" + "\r\n"; + lcbht_RESPSTATE state; + state = lcbht_parse(parser, buf.c_str(), buf.size()); + ASSERT_NE(0, state & LCBHT_S_DONE); + ASSERT_EQ(0, state & LCBHT_S_ERROR); + ASSERT_EQ(0, lcbht_can_keepalive(parser)); + + // Use HTTP/1.1 with Connection: close + lcbht_reset(parser); + buf = "HTTP/1.1 200 OK\r\n" + "Content-Length: 0\r\n" + "Connection: close\r\n" + "\r\n"; + state = lcbht_parse(parser, buf.c_str(), buf.size()); + ASSERT_NE(0, state & LCBHT_S_DONE); + ASSERT_EQ(0, state & LCBHT_S_ERROR); + ASSERT_EQ(0, lcbht_can_keepalive(parser)); + + lcbht_reset(parser); + // Default HTTP/1.1 + buf = "HTTP/1.1 200 OK\r\n" + "Content-Length: 0\r\n" + "\r\n"; + state = lcbht_parse(parser, buf.c_str(), buf.size()); + ASSERT_NE(0, state & LCBHT_S_DONE); + ASSERT_EQ(0, state & LCBHT_S_ERROR); + ASSERT_NE(0, lcbht_can_keepalive(parser)); + + lcbht_free(parser); + lcb_settings_unref(settings); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/connection.cc b/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/connection.cc new file mode 100644 index 00000000..7e423ebc --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/connection.cc @@ -0,0 +1,166 @@ +#include "ioserver.h" +#include +#include + +using namespace LCBTest; + +extern "C" { +static void +client_runfunc(void *arg) +{ + TestConnection *conn = (TestConnection *)arg; + conn->_doRun(); +} +} + +void TestConnection::_doRun() { run(); } + +void +TestConnection::setCommon(void *src, void **target) +{ + mutex.lock(); + assert(*target == NULL); + *target = src; + char dummy = 0; + ctlfd_user->send(&dummy, 1); + mutex.unlock(); +} + +void +TestConnection::sendData() +{ + f_send->startUpdate(); + do { +#ifdef _WIN32 + const char *outbuf; +#else + void *outbuf; +#endif + size_t n = f_send->getBuf((void**)&outbuf); + size_t nw = datasock->send(outbuf, n); + if (nw < 0) { + f_send->bail(); + } else { + f_send->setSent(nw); + } + } while (!f_send->shouldEnd()); + f_send->endUpdate(); + f_send = NULL; +} + +void +TestConnection::recvData() +{ + f_recv->startUpdate(); + char buf[32768]; + + do { + size_t required = f_recv->getRequired(); + size_t rdsize = std::min(required, sizeof(buf)); + ssize_t nr = datasock->recv(buf, rdsize); + if (nr < 0) { + f_recv->bail(); + } else { + f_recv->setReceived(buf, nr); + } + } while (!f_recv->shouldEnd()); + + f_recv->endUpdate(); + f_recv = NULL; +} + +void +TestConnection::handleClose() +{ + f_close->startUpdate(); + datasock->close(); + f_close->setDone(); + f_close->endUpdate(); + f_close = NULL; +} + +void +TestConnection::run() +{ + char dummy = 0; + mutex.lock(); + ctlfd_loop = ctlfd_lsn->acceptClient(); + initcond.signal(); + mutex.unlock(); + + while (::recv(*ctlfd_loop, &dummy, 1, 0) == 1) { + mutex.lock(); + if (f_close && f_close->getType() == CloseFuture::BEFORE_IO) { + handleClose(); + } + + if (f_send) { + sendData(); + } + if (f_recv) { + recvData(); + } + + if (f_close && f_close->getType() == CloseFuture::AFTER_IO) { + handleClose(); + } + mutex.unlock(); + } + + mutex.lock(); + if (f_recv) { + f_recv->updateFailed(); + f_recv = NULL; + } + if (f_send) { + f_send->updateFailed(); + f_send = NULL; + } + if (f_close) { + f_close->updateFailed(); + f_close = NULL; + } + mutex.unlock(); +} + +TestConnection::TestConnection(TestServer *server, SockFD *newsock) +{ + datasock = newsock; + datasock->loadRemoteAddr(); + ctlfd_lsn = SockFD::newListener(); + ctlfd_loop = NULL; + + f_send = NULL; + f_recv = NULL; + f_close = NULL; + parent = server; + + thr = new Thread(client_runfunc, this); + ctlfd_user = SockFD::newClient(ctlfd_lsn); + ctlfd_user->setNodelay(true); + + mutex.lock(); + while (!ctlfd_loop) { + initcond.wait(mutex); + } + mutex.unlock(); +} + +TestConnection::~TestConnection() +{ + ctlfd_loop->close(); + ctlfd_user->close(); + ctlfd_lsn->close(); + datasock->close(); + // We don't want to explicitly call join() here since that + // gets called in the destructor. This is unncessary + // and broken on musl. + // thr->join(); + delete thr; + mutex.close(); + initcond.close(); + delete ctlfd_loop; + delete ctlfd_user; + delete ctlfd_lsn; + delete datasock; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/future.cc b/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/future.cc new file mode 100644 index 00000000..9302948d --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/future.cc @@ -0,0 +1,50 @@ +#include "ioserver.h" +using namespace LCBTest; + +Future::Future() +{ + failed = false; +} + +void +Future::wait() +{ + mutex.lock(); + while (!isDone() && !failed) { + cond.wait(mutex); + } + mutex.unlock(); +} + +Future::~Future() +{ + mutex.close(); + cond.close(); +} + +void +Future::startUpdate() +{ + mutex.lock(); +} + +void +Future::endUpdate() +{ + if (shouldEnd()) { + cond.signal(); + } + mutex.unlock(); +} + +bool +Future::checkDone() +{ + bool ret; + if (!mutex.tryLock()) { + return false; + } + ret = isDone(); + mutex.unlock(); + return ret; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/ioserver.cc b/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/ioserver.cc new file mode 100644 index 00000000..c5f5b14c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/ioserver.cc @@ -0,0 +1,104 @@ +#include +#include +#include "ioserver.h" +using namespace LCBTest; +using std::list; + +extern "C" { +static void +server_runfunc(void *arg) +{ + TestServer *server = (TestServer *)arg; + server->run(); +} +} + +void +TestServer::run() +{ + while (!closed) { + struct sockaddr_in newaddr; + socklen_t naddr = sizeof(newaddr); + int newsock = accept(*lsn, (struct sockaddr *)&newaddr, &naddr); + + if (newsock == -1) { + break; + } + + TestConnection *newconn = new TestConnection(this, factory(newsock)); + startConnection(newconn); + } +} + +void +TestServer::startConnection(TestConnection *conn) +{ + mutex.lock(); + if (isClosed()) { + conn->close(); + delete conn; + } else { + conns.push_back(conn); + } + mutex.unlock(); +} + +TestServer::TestServer() +{ + lsn = SockFD::newListener(); + closed = false; + factory = plainSocketFactory; + + // Now spin up a thread to start the accept loop + thr = new Thread(server_runfunc, this); +} + +TestServer::~TestServer() +{ + close(); + mutex.lock(); + std::list::iterator iter = conns.begin(); + for (; iter != conns.end(); ++iter) { + (*iter)->close(); + delete *iter; + } + mutex.unlock(); + // We don't want to explicitly call join() here since that + // gets called in the destructor. This is unncessary + // and broken on musl. + // thr->join(); + delete thr; + mutex.close(); + delete lsn; +} + +std::string +TestServer::getPortString() +{ + char buf[4096]; + sprintf(buf, "%d", lsn->getLocalPort()); + return std::string(buf); +} + +TestConnection * +TestServer::findConnection(uint16_t port) +{ + TestConnection *ret = NULL; + list::iterator iter; + + while (ret == NULL) { + sched_yield(); + mutex.lock(); + iter = conns.begin(); + + for (; iter != conns.end(); ++iter) { + if ((*iter)->getPeerPort() == port) { + ret = *iter; + break; + } + } + mutex.unlock(); + }; + + return ret; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/ioserver.h b/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/ioserver.h new file mode 100644 index 00000000..e3fc6698 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/ioserver.h @@ -0,0 +1,478 @@ +/** + * @file + * This contains the API for the test socket server used to test the library's + * core `lcbio` functionality. + */ + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include "config.h" +#ifndef _WIN32 +#include +#include +#include +#include +#include +#define closesocket close +#else +#include "config.h" +#define sched_yield() +#define SHUT_RDWR SD_BOTH +#define ssize_t SSIZE_T +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include "threads.h" + +namespace LCBTest { +class TestServer; +class TestConnection; + +/** Convenience class representing a numeric socket handle */ +class SockFD { +public: + SockFD(int sock); + virtual ~SockFD(); + virtual void close(); + + virtual int getFD() const { return fd; } + operator int() const { return getFD(); } + + void loadRemoteAddr(); + + const struct sockaddr_in& localAddr4() { + return *(struct sockaddr_in *)&sa_local; + } + + const struct sockaddr_in& remoteAddr4() { + return *(struct sockaddr_in *)&sa_remote; + } + + uint16_t getLocalPort() { + return ntohs(localAddr4().sin_port); + } + + uint16_t getRemotePort() { + return ntohs(remoteAddr4().sin_port); + } + + std::string getLocalHost() { + return getHostCommon(&sa_local); + } + + std::string getRemoteHost() { + return getHostCommon(&sa_remote); + } + + template bool setOption(int level, int option, T val) { + int rv = setsockopt(fd, level, option, (char *)&val, sizeof val); + return rv == 0; + } + + bool setNodelay(bool enabled = true) { + int isEnabled = enabled ? 1 : 0; + return setOption(IPPROTO_TCP, TCP_NODELAY, isEnabled); + } + + SockFD *acceptClient(); + + virtual size_t send(const void *buf, size_t n, int flags = 0) { + return ::send(fd, (const char *)buf, n, flags); + } + virtual ssize_t recv(void *buf, size_t n, int flags = 0) { + return ::recv(fd, (char *)buf, n, flags); + } + + static SockFD *newListener(); + static SockFD *newClient(SockFD *server); + +private: + static std::string getHostCommon(sockaddr_storage *ss); + socklen_t naddr; + struct sockaddr_storage sa_local; + struct sockaddr_storage sa_remote; +#ifdef _WIN32 + SOCKET fd; +#else + int fd; +#endif + SockFD(SockFD&); +}; + +/** + * A Future represents a certain action the server should take. Since the server + * is essentially a dumb data handler, it relies on the test logic (in this case, + * the client) to control what it does. + * + * Futures represent a certain action the server should take (see the various + * subclasses). They can be waited on (via wait()), and their status can be + * checked (via isOk()). + * + * Note that futures are executed in the context of the _server_'s thread, + * so that a future may be done before the wait() method is called. + * + * See the specific subclasses of future for more usage details. + * + * @see SendFuture + * @see RecvFuture + */ +class Future { +public: + + /**Wait until the task has been completed by the @ref TestConnection */ + void wait(); + + /**Return if the task completed successfully. Only valid once wait() has + * returned. + * @return true on success, false on failure */ + bool isOk() { return !failed; } + + /**A non-blocking way to check if the task has completed + * @return true if completed, false otherwise */ + bool checkDone(); + + virtual ~Future(); + +protected: + friend class TestConnection; + + /**Locks the state of the Future. The action to be performed should be + * done after this is called. When the action is done, call endUpdate() */ + void startUpdate(); + + /**Closing bracket for startUpdate() */ + void endUpdate(); + + void updateFailed() { + startUpdate(); + bail(); + endUpdate(); + } + + /** Indicate this action has failed. Should only be called in an active + * startUpdate()/endUpdate() block */ + void bail() { + failed = true; + last_errno = errno; + printf("Bailing: Error=%d\n", last_errno); + } + + /** Implemented by subclasses to determine if the action is done + * @return true if done, false otherwise */ + virtual bool isDone() = 0; + + Future(); + +private: + Mutex mutex; + Condvar cond; + volatile bool failed; + bool shouldEnd() { return isDone() || failed; } + volatile int last_errno; +}; + +/** Future implementation that makes the server _send_ a buffer to the client */ +class SendFuture : public Future { +public: + /**@param bytes The buffer to send + * @param nbytes The number of bytes to send */ + SendFuture(const void *bytes, size_t nbytes) : Future() { + buf.insert(buf.begin(), (char *)bytes, (char *)bytes + nbytes); + nsent = 0; + } + + SendFuture(const std::string& ss) { + buf.assign(ss.begin(), ss.end()); + nsent = 0; + } + +protected: + bool isDone() { + return nsent == buf.size(); + } + +private: + friend class TestConnection; + /**Returns the beginning of the unsent buffer + * @param[out] outbuf the pointer to contain the buffer */ + size_t getBuf(void **outbuf) { + size_t ret = buf.size() - nsent; + *outbuf = &buf[nsent]; + return ret; + } + + /**Called to update the sent count. + * @param n The number of bytes just sent. */ + void setSent(size_t n) { + nsent += n; + } + + volatile unsigned nsent; + std::vector buf; +}; + +/** + * @ref Future implementation which instructs the server to receive a number + * of bytes _sent_ by the client + */ +class RecvFuture : public Future { +public: + /** @param n The number of bytes (exactly) to receive. */ + RecvFuture(size_t n) : Future() { + reinit(n); + } + + /**Discards the internal state and modifies the number of bytes to wait for + * @param n The new number of bytes to wait for. + * This is used by some tests to save on reinitialization */ + void reinit(size_t n) { + required = n; + buf.clear(); + buf.reserve(n); + } + + /** Get the contents the server received as a `vector` + * @return The received data */ + std::vector getBuf() { + return buf; + } + + std::string getString() { + return std::string(buf.begin(), buf.end()); + } + +protected: + bool isDone() { return buf.size() == required; } + +private: + friend class TestConnection; + + /**@return The number of bytes remaining to be received. + * Used by @ref TestConnection */ + size_t getRequired() { return required - buf.size(); } + + /**Call when new bytes are received on the connection. + * @param rbuf The buffer containing the new contents + * @param nbuf The length of the buffer + * Used by @ref TestConnection */ + void setReceived(void *rbuf, size_t nbuf) { + char *cbuf = (char *)rbuf; + buf.insert(buf.end(), cbuf, cbuf + nbuf); + } + volatile size_t required; + std::vector buf; +}; + +/** + * @ref Future implementation which makes the server _close_ the connection. + * The wait() will wait until the connection has been closed. This is useful + * if you wish to test behavior on a closed socket (i.e. a socket on which the + * remote closed the connection). + */ +class CloseFuture : public Future { +public: + + /** + * @enum CloseTime + * + * Indicates _when_ the close should take place. @ref CloseFuture objects + * can be performed either before any I/O is pending (i.e. ignore any + * outstanding I/O requests), or _after_ all pending I/O (i.e. any + * @ref SendFuture or @ref RecvFuture objects) has completed. + */ + enum CloseTime { + BEFORE_IO /**< Close socket before any I/O is performed */, + AFTER_IO /**< Closed socket once all pending I/O operations have successfully completed */ + }; + + /**@param type See documentation for @ref CloseTime */ + CloseFuture(CloseTime type) : Future() { + performed = false; + closeTime = type; + } + +protected: + bool isDone() { return performed; } + +private: + friend class TestConnection; + void setDone() { performed = true; } + CloseTime getType() { return closeTime; } + + volatile bool performed; + CloseTime closeTime; +}; + + +/** + * Representation of a server side remote endpoint + * + * A TestConnection object is created whenever the @ref TestServer accepts a + * new connection. It can be used by tests to coordinate various actions + * between client and server, using the various @ref Future implementations. + * + * @note Futures of different kinds can exist concurrently within the same + * TestConnection object; however, only _one_ future of a given type can + * be active; so for example, a @ref SendFuture and a @ref RecvFuture may + * be active concurrently, but two @ref CloseFuture objects may not. + * + * Additionally, note that any @ref Future object passed must remain valid + * until it has completed (i.e. Future::wait() or Future::isDone() returns + * true). + */ +class TestConnection { +public: + + /** + * Set the @ref SendFuture object to indicate that the server should + * send data + * @param f + */ + void setSend(SendFuture *f) { + setCommon(f, (void **)&f_send); + } + + /** + * Indicate that the server should read data. The future object indicates + * how much data to read + * @param f + */ + void setRecv(RecvFuture *f) { + setCommon(f, (void **)&f_recv); + } + + /** + * Indicate that the connection should be closed, optionally before or + * after outstanding IO (See @ref CloseFuture for more details). + * @param f + */ + void setClose(CloseFuture *f) { + setCommon(f, (void **)&f_close); + } + + /** + * _Immediately_ close the underlying socket connection on the server side. + * This is not the same as @ref CloseFuture which merely _schedules_ + * a close + */ + void close() { + datasock->close(); + ctlfd_loop->close(); + ctlfd_user->close(); + ctlfd_lsn->close(); + } + + /** + * Return the remote port from which the client initiated the connection. + * This is used to determine which the object should + * be associated with a given client (i.e. @ref ESocket) object. + * @return The port of the client. + */ + uint16_t getPeerPort() { + return datasock->getRemotePort(); + } + + inline void _doRun(); + +protected: + TestConnection(TestServer *server, SockFD *newsock); + ~TestConnection(); + virtual void run(); + friend class TestServer; + +private: + SockFD *datasock; + SockFD *ctlfd_loop; + SockFD *ctlfd_lsn; + SockFD *ctlfd_user; + Mutex mutex; + Condvar initcond; + Thread *thr; + TestServer *parent; + SendFuture *f_send; + RecvFuture *f_recv; + CloseFuture *f_close; + void setCommon(void *src, void **target); + void sendData(); + void recvData(); + void handleClose(); +}; + +/** + * Represents a listening socket for a test "Server". This server accepts + * connections from clients, and for each new connection, creates a new + * @ref TestConneciton object. + */ +class TestServer { +public: + TestServer(); + ~TestServer(); + + /** Run the server. This will open a new thread */ + void run(); + + /** Stop the server. This will close the listening sokcet */ + void close() { + closed = true; + lsn->close(); + } + + bool isClosed() { + return closed; + } + + /**Find a connection with a given client port + * @param cliport The client port to search for + * @return The connection object, or `NULL` if no such connection exists. + */ + TestConnection* findConnection(uint16_t cliport); + + /** + * Get the listening port + * @return The listening ports that clients may use to connect to this + * object. + */ + uint16_t getListenPort() { + return lsn->getLocalPort(); + } + + /** + * Get the IP address (usually `127.0.0.1` as a string) + * @return The host the server is listening on + */ + std::string getHostString() { + return lsn->getLocalHost(); + } + + /** + * Get the listening port, as a string + * @return The listening port + */ + std::string getPortString(); + + typedef SockFD * (SocketFactory)(int); + + SocketFactory *factory; + static SockFD *plainSocketFactory(int fd) { return new SockFD(fd); } + static SockFD *sslSocketFactory(int fd); + +private: + friend class TestConnection; + bool closed; + SockFD *lsn; + Thread *thr; + Mutex mutex; + std::list conns; + void startConnection(TestConnection *conn); +}; + +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/socket.cc b/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/socket.cc new file mode 100644 index 00000000..522c613d --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/socket.cc @@ -0,0 +1,88 @@ +#include "ioserver.h" +using namespace LCBTest; + +SockFD::SockFD(int sock) +{ + assert(sock >= 0); + fd = sock; + naddr = sizeof(sa_local); + int rv = getsockname(fd, (struct sockaddr *)&sa_local, &naddr); + assert(rv == 0); +} + +void +SockFD::loadRemoteAddr() +{ + socklen_t lentmp = sizeof(sa_remote); + getpeername(*this, (struct sockaddr *)&sa_remote, &lentmp); +} + +void +SockFD::close() +{ + if (fd != -1) { + shutdown(fd, SHUT_RDWR); + ::closesocket(fd); + fd = -1; + } +} + +SockFD::~SockFD() +{ + close(); +} + +SockFD * +SockFD::acceptClient() +{ + struct sockaddr_storage newaddr; + socklen_t newlen = sizeof(newaddr); + int newsock = accept(*this, (struct sockaddr *)&newaddr, &newlen); + return new SockFD(newsock); +} + +SockFD * +SockFD::newListener() +{ + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + + int lsnfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + addr.sin_port = 0; + bind(lsnfd, (struct sockaddr *)&addr, sizeof(addr)); + listen(lsnfd, 5); + return new SockFD(lsnfd); +} + +SockFD * +SockFD::newClient(SockFD *server) +{ + int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + assert(sockfd >= 0); + int rv = connect(sockfd, (struct sockaddr *)&server->localAddr4(), + sizeof(struct sockaddr_in)); + assert(rv == 0); + return new SockFD(sockfd); +} + +#ifndef _WIN32 +std::string +SockFD::getHostCommon(sockaddr_storage *ss) +{ + struct sockaddr_in *addr = (struct sockaddr_in *)ss; + char buf[4096]; + inet_ntop(AF_INET, &addr->sin_addr, buf, sizeof(*addr)); + return std::string(buf); + +} +#else +std::string +SockFD::getHostCommon(sockaddr_storage *ss) +{ + struct sockaddr_in *inaddr = (struct sockaddr_in *)ss; + char *buf = inet_ntoa(inaddr->sin_addr); + return std::string(buf); +} +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/ssl_connection.cc b/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/ssl_connection.cc new file mode 100644 index 00000000..49b3116c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/ssl_connection.cc @@ -0,0 +1,145 @@ +#include "ioserver.h" + +#ifndef LCB_NO_SSL +#include + +using namespace LCBTest; +using std::vector; + +class SslSocket : public SockFD { +public: + SslSocket(SockFD *innner); + + ~SslSocket(); + + // Receive data via SSL + size_t send(const void*,size_t,int); + + // Send data via SSL + ssize_t recv(void*,size_t,int); + + // Close the SSL context first + void close(); + + // Return the real FD + int getFD() const { return sfd->getFD(); } + +private: + SSL *ssl; + SSL_CTX *ctx; + SockFD *sfd; + bool ok; +}; + +SockFD * +TestServer::sslSocketFactory(int fd) +{ + return new SslSocket(new SockFD(fd)); +} + +extern "C" { +static void +log_callback(const SSL *ssl, int where, int ret) +{ + const char *retstr = ""; + int should_log = 0; + + if (where & SSL_CB_ALERT) { + should_log = 1; + } + if (where == SSL_CB_HANDSHAKE_START || where == SSL_CB_HANDSHAKE_DONE) { + should_log = 1; + } + if ((where & SSL_CB_EXIT) && ret == 0) { + should_log = 1; + } + + if (!should_log) { + return; + } + + retstr = SSL_alert_type_string(ret); + fprintf(stderr, "SslSocket: ST(0x%x). %s. R(0x%x)%s\n", where, SSL_state_string_long(ssl), ret, retstr); + + if (where == SSL_CB_HANDSHAKE_DONE) { + fprintf(stderr, "SslSocket: Using SSL version %s. Cipher=%s\n", SSL_get_version(ssl), SSL_get_cipher_name(ssl)); + } +} +} + +// http://stackoverflow.com/questions/256405/programmatically-create-x509-certificate-using-openssl +// http://www.opensource.apple.com/source/OpenSSL/OpenSSL-22/openssl/demos/x509/mkcert.c +// Note we deviate from the examples by directly setting the certificate. + +static void genCertificate(SSL_CTX *ctx) { + EVP_PKEY * pkey = EVP_PKEY_new(); + X509 * x509 = X509_new(); + RSA * rsa = RSA_generate_key(2048, RSA_F4, NULL, NULL); + + EVP_PKEY_assign_RSA(pkey, rsa); + ASN1_INTEGER_set(X509_get_serialNumber(x509), 1); + X509_gmtime_adj(X509_get_notBefore(x509), 0); + X509_gmtime_adj(X509_get_notAfter(x509), 31536000L); + X509_set_pubkey(x509, pkey); + + X509_NAME * name = X509_get_subject_name(x509); + X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char *)"CA", -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char *)"MyCompany Inc.", -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)"localhost", -1, -1, 0); + X509_set_issuer_name(x509, name); + X509_sign(x509, pkey, EVP_sha1()); + + SSL_CTX_use_PrivateKey(ctx, pkey); + SSL_CTX_use_certificate(ctx, x509); + X509_free(x509); + EVP_PKEY_free(pkey); +} + +SslSocket::SslSocket(SockFD *inner) : SockFD(inner->getFD()) +{ + sfd = inner; + ctx = SSL_CTX_new(SSLv23_server_method()); + assert(ctx != NULL); + + SSL_CTX_set_info_callback(ctx, log_callback); + genCertificate(ctx); + SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3); + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + SSL_CTX_load_verify_locations(ctx, NULL, NULL); + + ssl = SSL_new(ctx); + assert(ssl != NULL); + SSL_set_accept_state(ssl); + SSL_set_fd(ssl, sfd->getFD()); +} + +size_t +SslSocket::send(const void *buf, size_t n, int) +{ + return SSL_write(ssl, buf, n); +} + +ssize_t +SslSocket::recv(void *buf, size_t n, int) +{ + return SSL_read(ssl, buf, n); +} + +void +SslSocket::close() +{ + SSL_shutdown(ssl); + sfd->close(); +} + +SslSocket::~SslSocket() +{ + SSL_free(ssl); + SSL_CTX_free(ctx); + delete sfd; +} + +#else +namespace { void dummySym() { } } +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/threads-pthreads.cc b/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/threads-pthreads.cc new file mode 100644 index 00000000..f05ba190 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/threads-pthreads.cc @@ -0,0 +1,119 @@ +#ifndef _WIN32 +#include "threads.h" +#include +#include +#include + +extern "C" { +static void* startfunc(void *arg) +{ + Thread *thr = reinterpret_cast(arg); + thr->doRun(); + return NULL; +} +} + +Thread::Thread(StartFunc startfn, void *arg) +{ + this->fn = startfn; + this->fnparam = arg; + initialized = true; + pthread_create(&thr, NULL, startfunc, this); +} + +void +Thread::close() +{ + if (!initialized) { + return; + } + join(); + initialized = false; +} + +Thread::~Thread() +{ + close(); +} + +void +Thread::join() +{ + void *res; + pthread_join(thr, &res); +} + + +// Mutex: +Mutex::Mutex() +{ + pthread_mutex_init(&mutex, NULL); + initialized = true; +} + +void +Mutex::lock() +{ + pthread_mutex_lock(&mutex); +} + +bool +Mutex::tryLock() +{ + return pthread_mutex_trylock(&mutex) == 0; +} + +void +Mutex::unlock() +{ + pthread_mutex_unlock(&mutex); +} + +void +Mutex::close() +{ + if (initialized) { + initialized = false; + pthread_mutex_destroy(&mutex); + } +} + +Mutex::~Mutex() +{ + close(); +} + + +// Condvar +Condvar::Condvar() +{ + pthread_cond_init(&cond, NULL); + initialized = true; +} + +void +Condvar::wait(Mutex& mutex) +{ + pthread_cond_wait(&cond, &mutex.mutex); +} + +void +Condvar::signal() +{ + pthread_cond_signal(&cond); +} + +void +Condvar::close() +{ + if (initialized) { + pthread_cond_destroy(&cond); + initialized = false; + } +} + +Condvar::~Condvar() +{ + close(); +} +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/threads-win32.cc b/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/threads-win32.cc new file mode 100644 index 00000000..1e5710a2 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/threads-win32.cc @@ -0,0 +1,117 @@ +#ifdef _WIN32 +#include +#include +#include +#include "threads.h" + +extern "C" +{ +unsigned int __stdcall +startfunc(void * param) +{ + Thread *thr = reinterpret_cast(param); + thr->doRun(); + return 0; +} +} + +Thread::Thread(StartFunc thrfn, void *param) +{ + fn = thrfn; + fnparam = param; + uintptr_t rv; + rv = _beginthreadex(NULL, 0, startfunc, this, 0, NULL); + assert(rv); + hThread = (HANDLE)rv; + initialized = true; +} + +void +Thread::join() +{ + WaitForSingleObject(hThread, INFINITE); +} + +void +Thread::close() +{ + if (initialized) { + join(); + CloseHandle(hThread); + initialized = false; + } +} + +Thread::~Thread() +{ + close(); +} + +Mutex::Mutex() +{ + InitializeCriticalSection(&cs); + initialized = true; +} + +Mutex::~Mutex() +{ + close(); +} + +void +Mutex::close() +{ + if (initialized) { + DeleteCriticalSection(&cs); + initialized = false; + } +} + +void +Mutex::lock() +{ + EnterCriticalSection(&cs); +} + +bool +Mutex::tryLock() +{ + return TryEnterCriticalSection(&cs) == TRUE; +} + +void +Mutex::unlock() +{ + LeaveCriticalSection(&cs); +} + + +Condvar::Condvar() +{ + InitializeConditionVariable(&cv); + initialized = true; +} + +void +Condvar::close() +{ + initialized = false; +} + +Condvar::~Condvar() +{ + close(); +} + +void +Condvar::signal() +{ + WakeConditionVariable(&cv); +} + +void +Condvar::wait(Mutex& mutex) +{ + SleepConditionVariableCS(&cv, &mutex.cs, INFINITE); +} +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/threads.h b/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/threads.h new file mode 100644 index 00000000..05a99935 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/ioserver/threads.h @@ -0,0 +1,66 @@ +/** + * @file + * Simple cross-platform thread abstraction + */ +#ifndef _WIN32 +#include +#endif + +class Thread { +public: + typedef void (*StartFunc)(void *); + Thread(StartFunc, void *); + ~Thread(); + void close(); + void join(); + void doRun() { + fn(fnparam); + } + +private: + StartFunc fn; + void *fnparam; + bool initialized; +#ifdef _WIN32 + HANDLE hThread; +#else + pthread_t thr; +#endif +}; + +class Condvar; +class Mutex { +public: + Mutex(); + ~Mutex(); + void lock(); + void unlock(); + bool tryLock(); + void close(); + +private: + friend class Condvar; + bool initialized; +#ifdef _WIN32 + CRITICAL_SECTION cs; +#else + pthread_mutex_t mutex; +#endif +}; + +class Condvar { +public: + Condvar(); + ~Condvar(); + void signal(); + void wait(Mutex&); + void close(); + +private: + bool initialized; +#ifdef _WIN32 + CONDITION_VARIABLE cv; +#else + pthread_cond_t cond; +#endif +}; diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/iotests.h b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/iotests.h new file mode 100644 index 00000000..d45c5a96 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/iotests.h @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#include "testutil.h" +#include "mock-unit-test.h" +#include "mock-environment.h" + +static inline void +doLcbCreate(lcb_t *instance, const lcb_create_st *options, MockEnvironment* env) +{ + lcb_error_t err = lcb_create(instance, options); + ASSERT_EQ(LCB_SUCCESS, err); + env->postCreate(*instance); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/mock-environment.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/mock-environment.cc new file mode 100644 index 00000000..771685a5 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/mock-environment.cc @@ -0,0 +1,524 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include +#include +#include +#include +#include "mock-environment.h" +#include + +MockEnvironment *MockEnvironment::instance; + +MockEnvironment *MockEnvironment::getInstance(void) +{ + if (instance == NULL) { + instance = new MockEnvironment; + } + return instance; +} + +void MockEnvironment::Reset() +{ + if (instance != NULL) { + instance->TearDown(); + instance->SetUp(); + } +} + +void MockEnvironment::init() +{ + mock = NULL; + http = NULL; + innerClient = NULL; + argv = NULL; + iops = NULL; + + numNodes = 4; + realCluster = false; + serverVersion = VERSION_UNKNOWN; +} + +MockEnvironment::MockEnvironment() +{ + init(); +} + +MockEnvironment::MockEnvironment(const char **args, std::string bucketname) +{ + init(); + this->argv = args; + this->bucketName = bucketname; + SetUp(); +} + +void MockEnvironment::failoverNode(int index, std::string bucket) +{ + MockBucketCommand bCmd(MockCommand::FAILOVER, index, bucket); + sendCommand(bCmd); + getResponse(); +} + +void MockEnvironment::respawnNode(int index, std::string bucket) +{ + MockBucketCommand bCmd(MockCommand::RESPAWN, index, bucket); + sendCommand(bCmd); + getResponse(); +} + +void MockEnvironment::hiccupNodes(int msecs, int offset) +{ + MockCommand cmd(MockCommand::HICCUP); + cmd.set("msecs", msecs); + cmd.set("offset", offset); + sendCommand(cmd); + getResponse(); +} + +void MockEnvironment::regenVbCoords(std::string bucket) { + MockBucketCommand bCmd(MockCommand::REGEN_VBCOORDS, 0, bucket); + MockResponse r; + sendCommand(bCmd); + getResponse(r); + EXPECT_TRUE(r.isOk()); +} + +std::vector MockEnvironment::getMcPorts(std::string bucket) +{ + MockCommand cmd(MockCommand::GET_MCPORTS); + if (!bucket.empty()) { + cmd.set("bucket", bucket); + } + + sendCommand(cmd); + MockResponse resp; + getResponse(resp); + EXPECT_TRUE(resp.isOk()); + const Json::Value& payload = resp.constResp()["payload"]; + + std::vector ret; + + for (int ii = 0; ii < payload.size(); ii++) { + ret.push_back(payload[ii].asInt()); + } + return ret; +} + +void MockEnvironment::setCCCP(bool enabled, std::string bucket, + const std::vector* nodes) +{ + MockCommand cmd(MockCommand::SET_CCCP); + cmd.set("enabled", enabled); + + if (!bucket.empty()) { + cmd.set("bucket", bucket); + } + + if (nodes != NULL) { + const std::vector& v = *nodes; + Json::Value array(Json::arrayValue); + + for (std::vector::const_iterator ii = v.begin(); ii != v.end(); ii++) { + array.append(*ii); + } + + cmd.set("servers", array); + } + + sendCommand(cmd); + getResponse(); +} + +void MockEnvironment::sendCommand(MockCommand &cmd) +{ + std::string s = cmd.encode(); + lcb_ssize_t nw = send(mock->client, s.c_str(), (unsigned long)s.size(), 0); + assert(nw == s.size()); +} + +void MockEnvironment::getResponse(MockResponse& ret) +{ + std::string rbuf; + do { + char c; + int rv = recv(mock->client, &c, 1, 0); + assert(rv == 1); + if (c == '\n') { + break; + } + rbuf += c; + } while (true); + + ret.assign(rbuf); + if (!ret.isOk()) { + std::cerr << "Mock command failed!" << std::endl; + std::cerr << ret; + } +} + +void MockEnvironment::postCreate(lcb_t instance) +{ + lcb_error_t err; + if (!isRealCluster()) { + lcb_HTCONFIG_URLTYPE urltype = LCB_HTCONFIG_URLTYPE_COMPAT; + err = lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_HTCONFIG_URLTYPE, &urltype); + ASSERT_EQ(LCB_SUCCESS, err); + } + err = lcb_cntl_string(instance, "fetch_mutation_tokens", "true"); + ASSERT_EQ(LCB_SUCCESS, err); +} + +void +MockEnvironment::createConnection(HandleWrap &handle, + lcb_t& instance, const lcb_create_st &user_options) +{ + lcb_io_opt_t io; + lcb_create_st options; + memcpy(&options, &user_options, sizeof user_options); + + if (lcb_create_io_ops(&io, NULL) != LCB_SUCCESS) { + fprintf(stderr, "Failed to create IO instance\n"); + exit(1); + } + + options.v.v2.io = io; + lcb_error_t err = lcb_create(&instance, &options); + ASSERT_EQ(LCB_SUCCESS, err); + postCreate(instance); + + (void)lcb_set_cookie(instance, io); + + handle.instance = instance; + handle.iops = io; +} + +void MockEnvironment::createConnection(HandleWrap &handle, lcb_t &instance) +{ + lcb_create_st options; + makeConnectParams(options, NULL); + createConnection(handle, instance, options); +} + +void MockEnvironment::createConnection(lcb_t &instance) +{ + HandleWrap handle; + createConnection(handle, instance); + + handle.iops->v.base.need_cleanup = 1; + handle.instance = NULL; + handle.iops = NULL; + +} + +#define STAT_EP_VERSION "ep_version" + +extern "C" { + static void statsCallback(lcb_t, const void *cookie, + lcb_error_t err, + const lcb_server_stat_resp_t *resp) + { + MockEnvironment *me = (MockEnvironment *)cookie; + ASSERT_EQ(LCB_SUCCESS, err); + + if (resp->v.v0.server_endpoint == NULL) { + return; + } + + if (!resp->v.v0.nkey) { + return; + } + + if (resp->v.v0.nkey != sizeof(STAT_EP_VERSION) - 1 || + memcmp(resp->v.v0.key, STAT_EP_VERSION, + sizeof(STAT_EP_VERSION) - 1) != 0) { + return; + } + int version = ((const char *)resp->v.v0.bytes)[0] - '0'; + if (version == 1) { + me->setServerVersion(MockEnvironment::VERSION_10); + } else if (version == 2) { + me->setServerVersion(MockEnvironment::VERSION_20); + + } else { + std::cerr << "Unable to determine version from string '"; + std::cerr.write((const char *)resp->v.v0.bytes, + resp->v.v0.nbytes); + std::cerr << "' assuming 1.x" << std::endl; + + me->setServerVersion(MockEnvironment::VERSION_10); + } + } +} + +void MockEnvironment::bootstrapRealCluster() +{ + serverParams = ServerParams(mock->http, mock->bucket, + mock->username, mock->password); + + lcb_t tmphandle; + lcb_error_t err; + lcb_create_st options; + serverParams.makeConnectParams(options, NULL); + + ASSERT_EQ(LCB_SUCCESS, lcb_create(&tmphandle, &options)); + postCreate(tmphandle); + ASSERT_EQ(LCB_SUCCESS, lcb_connect(tmphandle)); + lcb_wait(tmphandle); + + lcb_set_stat_callback(tmphandle, statsCallback); + lcb_server_stats_cmd_t scmd, *pscmd; + pscmd = &scmd; + err = lcb_server_stats(tmphandle, this, 1, &pscmd); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_wait(tmphandle); + + const char *const *servers = lcb_get_server_list(tmphandle); + int ii; + for (ii = 0; servers[ii] != NULL; ii++) { + // no body + } + + if (serverVersion == VERSION_20) { + // Couchbase 2.0.x + featureRegistry.insert("observe"); + featureRegistry.insert("views"); + featureRegistry.insert("http"); + featureRegistry.insert("replica_read"); + featureRegistry.insert("lock"); + } + + numNodes = ii; + lcb_destroy(tmphandle); +} + +extern "C" { +static void mock_flush_callback(lcb_t, int, const lcb_RESPBASE *resp) { + assert(resp->rc == LCB_SUCCESS); +} +} + +void MockEnvironment::clearAndReset() +{ + if (is_using_real_cluster()) { + return; + } + + for (int ii = 0; ii < getNumNodes(); ii++) { + respawnNode(ii, bucketName); + } + + std::vector mcPorts = getMcPorts(bucketName); + serverParams.setMcPorts(mcPorts); + setCCCP(true, bucketName); + + if (this != getInstance()) { + return; + } + + if (!innerClient) { + lcb_create_st crParams; + lcb_config_transport_t transports[] = { + LCB_CONFIG_TRANSPORT_CCCP, + LCB_CONFIG_TRANSPORT_LIST_END + }; + memset(&crParams, 0, sizeof(crParams)); + // Use default I/O here.. + serverParams.makeConnectParams(crParams, NULL); + crParams.v.v2.transports = transports; + lcb_error_t err = lcb_create(&innerClient, &crParams); + if (err != LCB_SUCCESS) { + printf("Error on create: 0x%x\n", err); + } + EXPECT_FALSE(NULL == innerClient); + postCreate(innerClient); + err = lcb_connect(innerClient); + EXPECT_EQ(LCB_SUCCESS, err); + lcb_wait(innerClient); + lcb_install_callback3(innerClient, LCB_CALLBACK_CBFLUSH, mock_flush_callback); + } + + lcb_CMDCBFLUSH fcmd = { 0 }; + lcb_error_t err; + + err = lcb_cbflush3(innerClient, NULL, &fcmd); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_wait(innerClient); +} + +void MockEnvironment::SetUp() +{ + numNodes = 4; + if (!mock) { + mock = (struct test_server_info *)start_test_server((char **)argv); + } + + realCluster = is_using_real_cluster() != 0; + ASSERT_NE((const void *)(NULL), mock); + http = get_mock_http_server(mock); + ASSERT_NE((const char *)(NULL), http); + + if (realCluster) { + bootstrapRealCluster(); + return; + } + + if (bucketName.empty()) { + const char *name = getenv("LCB_TEST_BUCKET"); + if (name != NULL) { + bucketName = name; + } else { + bucketName = "default"; + } + } + serverParams = ServerParams(http, + bucketName.c_str(), + bucketName.c_str(), + NULL); + + // Mock 0.6 + featureRegistry.insert("observe"); + featureRegistry.insert("views"); + featureRegistry.insert("replica_read"); + featureRegistry.insert("lock"); + + clearAndReset(); +} + +void MockEnvironment::TearDown() +{ + +} + +MockEnvironment::~MockEnvironment() +{ + shutdown_mock_server(mock); + mock = NULL; + if (innerClient != NULL) { + lcb_destroy(innerClient); + innerClient = NULL; + } +} + +void HandleWrap::destroy() +{ + if (instance) { + lcb_destroy(instance); + } + if (iops) { + lcb_destroy_io_ops(iops); + } + + instance = NULL; + iops = NULL; +} + +HandleWrap::~HandleWrap() +{ + destroy(); +} + +MockCommand::MockCommand(Code code) +{ + this->code = code; + name = GetName(code); + command["command"] = name; + payload = &(command["payload"] = Json::Value(Json::objectValue)); +} + +MockCommand::~MockCommand() +{ +} + +std::string MockCommand::encode() +{ + finalizePayload(); + return Json::FastWriter().write(command); +} + +void MockKeyCommand::finalizePayload() +{ + MockCommand::finalizePayload(); + if (vbucket != -1) { + set("vBucket", vbucket); + } + + if (!bucket.empty()) { + set("Bucket", bucket); + } + set("Key", key); +} + +void MockMutationCommand::finalizePayload() +{ + MockKeyCommand::finalizePayload(); + set("OnMaster", onMaster); + + if (!replicaList.empty()) { + Json::Value arr(Json::arrayValue); + Json::Value& arrval = (*payload)["OnReplicas"] = Json::Value(Json::arrayValue); + for (std::vector::iterator ii = replicaList.begin(); + ii != replicaList.end(); ii++) { + arrval.append(*ii); + } + } else { + set("OnReplicas", replicaCount); + } + + if (cas != 0) { + if (cas > (1LU << 30)) { + fprintf(stderr, "Detected incompatible > 31 bit integer\n"); + abort(); + } + set("CAS", static_cast(cas)); + } + + if (!value.empty()) { + set("Value", value); + } +} + +void MockBucketCommand::finalizePayload() +{ + MockCommand::finalizePayload(); + set("idx", ix); + set("bucket", bucket); +} + +void MockResponse::assign(const std::string &resp) +{ + bool rv = Json::Reader().parse(resp, jresp); + assert(rv); +} + +MockResponse::~MockResponse() +{ +} + +std::ostream& operator<<(std::ostream& os, const MockResponse& resp) +{ + os << Json::FastWriter().write(resp.jresp) << std::endl; + return os; +} + +bool MockResponse::isOk() +{ + const Json::Value& status = static_cast(jresp)["status"]; + if (!status.isString()) { + return false; + } + return tolower(status.asString()[0]) == 'o'; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/mock-environment.h b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/mock-environment.h new file mode 100644 index 00000000..b00bfc68 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/mock-environment.h @@ -0,0 +1,385 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef TESTS_MOCK_ENVIRONMENT_H +#define TESTS_MOCK_ENVIRONMENT_H 1 + +#include "config.h" +#include +#include +#include "serverparams.h" +#include "contrib/lcb-jsoncpp/lcb-jsoncpp.h" + + +class HandleWrap +{ + + friend class MockEnvironment; + +public: + lcb_t getLcb() const { + return instance; + } + + void destroy(); + + // Don't ever allow copying. C++0x allows = 0, though + HandleWrap operator= (const HandleWrap &) { + fprintf(stderr, "Can't copy this object around!\n"); + abort(); + return HandleWrap(); + } + + HandleWrap() : instance(NULL), iops(NULL) { } + virtual ~HandleWrap(); + + +private: + lcb_t instance; + lcb_io_opt_t iops; +}; + + +class MockCommand +{ +#define XMOCKCMD(X) \ + X(FAILOVER) \ + X(RESPAWN) \ + X(HICCUP) \ + X(TRUNCATE) \ + X(MOCKINFO) \ + X(PERSIST) \ + X(CACHE) \ + X(UNPERSIST) \ + X(UNCACHE) \ + X(ENDURE) \ + X(PURGE) \ + X(KEYINFO) \ + X(GET_MCPORTS) \ + X(SET_CCCP) \ + X(REGEN_VBCOORDS) \ + X(RESET_QUERYSTATE) + +public: + enum Code { +#define X(cc) cc, + XMOCKCMD(X) +#undef X + _NONE + }; + + static std::string GetName(Code code) { + +#define X(cc) if (code == cc) { return #cc; } + XMOCKCMD(X) +#undef X + + abort(); + return ""; + } + + MockCommand(Code code); + + // Various methods to set a field in the payload + template void set(const std::string& s, const T& v) { + (*payload)[s] = v; + } + virtual ~MockCommand(); + + // Encodes the command in a form suitable for sending over the network + std::string encode(); + +protected: + Code code; + std::string name; + Json::Value command; + Json::Value *payload; + virtual void finalizePayload() {} + +private: + MockCommand(const MockCommand &other); +}; + +class MockKeyCommand : public MockCommand +{ +public: + MockKeyCommand(Code code, std::string &key) + : MockCommand(code), vbucket(-1) { + this->key = key; + } + + const std::string &getKey() const { + return key; + } + + short vbucket; + std::string bucket; + std::string key; + +protected: + virtual void finalizePayload(); +}; + +class MockMutationCommand : public MockKeyCommand +{ +public: + MockMutationCommand(Code code, std::string &key) + : MockKeyCommand(code, key), onMaster(false), replicaCount(0), cas(0) {} + + bool onMaster; + int replicaCount; + std::vector replicaList; + lcb_uint64_t cas; + std::string value; + +protected: + virtual void finalizePayload(); +}; + +class MockBucketCommand : public MockCommand +{ +public: + MockBucketCommand(Code code, int index, std::string bucketstr = "default") + : MockCommand(code) { + ix = index; + bucket = bucketstr; + } + +protected: + virtual void finalizePayload(); + int ix; + std::string bucket; +}; + +class MockResponse +{ +public: + MockResponse() {} + ~MockResponse(); + void assign(const std::string& s); + + bool isOk(); + const Json::Value& getRawResponse() { + return jresp; + } + const Json::Value& constResp() const { return jresp; } + +protected: + Json::Value jresp; + friend std::ostream& operator<<(std::ostream&, const MockResponse&); +private: + MockResponse(const MockResponse&); +}; + +class MockEnvironment : public ::testing::Environment +{ +public: + enum ServerVersion { + VERSION_UNKNOWN = 0, + VERSION_10 = 1, + VERSION_20 = 2 + }; + + virtual void SetUp(); + virtual void TearDown(); + + static MockEnvironment *getInstance(void); + static void Reset(); + + /** + * Make a connect structure you may utilize to connect to + * the backend we're running the tests towards. + * + * @param crst the create structure to fill in + * @param io the io ops to use (pass NULL if you don't have a + * special io ops you want to use + */ + void makeConnectParams(lcb_create_st &crst, lcb_io_opt_t io=NULL) { + serverParams.makeConnectParams(crst, io); + } + + /** + * Get the number of nodes used in the backend + */ + int getNumNodes(void) const { + return numNodes; + } + + /** + * Are we currently using a real cluster as the backend, or + * are we using the mock server. + * + * You should try your very best to avoid using this variable, and + * rather extend the mock server to support the requested feature. + */ + bool isRealCluster(void) const { + return realCluster; + } + + /** + * Simulate node failover. In this case mock will disable server + * corresponding given index an push new configuration. No data + * rebalancing implemented on the mock. + * + * @param index the index of the node on the mock + * @param bucket the name of the bucket + */ + void failoverNode(int index, std::string bucket = "default"); + + /** + * Simulate node reconvering. In this case mock will enable server + * corresponding given index an push new configuration. No data + * rebalancing implemented on the mock. + * + * @param index the index of the node on the mock + * @param bucket the name of the bucket + */ + void respawnNode(int index, std::string bucket = "default"); + + /** + * Regenerate existing UUIDs and sequence numbers on the cluster to + * simulate a dcp-style failover. This is a separate command as triggering + * this during a "Normal" failover severly slows down the mock. + * + * @param bucket + */ + void regenVbCoords(std::string bucket = "default"); + + + /** + * Retrieve the memcached listening ports for a given bucket + * @param bucket the bucket for which to retrieve memcached port into + * @return a vector of ports to use. + */ + std::vector getMcPorts(std::string bucket = "default"); + + /** + * Enable CCCP on the mock cluster + * @param bucket the bucket on which to enable CCCP + * @param nodes a list of by-index nodes on which to enable CCCP. If NULL + * then all nodes are enabled + * @param bucket the bucket on which to + */ + void setCCCP(bool enabled, + std::string bucket = "", + const std::vector* nodes = NULL); + + /** + * Create a connection to the mock/real server. + * + * The instance will be initialized with the the connect parameters + * to either the mock or a real server (just like makeConnectParams), + * and call lcb_create. The io instance will be stored in the instance + * cookie so you may grab it from there. + * + * You should call lcb_destroy on the instance when you're done + * using it. + * + * @param instance the instane to create + */ + void createConnection(lcb_t &instance); + + void createConnection(HandleWrap &handle, lcb_t &instance); + void createConnection(HandleWrap &handle, lcb_t& instance, + const lcb_create_st &options); + + /** + * Setup mock to split response in two parts: send first "offset" bytes + * immediately and send the rest after "msecs" milliseconds. + * + * @param msecs the number of milliseconds to wait before sending the + * rest of the packet. + * @param offset the number of the bytes to send in first before delay + */ + void hiccupNodes(int msecs, int offset); + + ServerVersion getServerVersion(void) const { + return serverVersion; + } + + void setServerVersion(ServerVersion ver) { + serverVersion = ver; + } + + void sendCommand(MockCommand &cmd); + void getResponse(MockResponse &resp); + void getResponse() { MockResponse tmp; getResponse(tmp); } + + bool hasFeature(const char *feature) { + return featureRegistry.find(feature) != featureRegistry.end(); + } + + static void printSkipMessage(std::string file, int line, std::string reason) { + std::cerr << "Skipping " << file << ":" << std::dec << line; + std::cerr << "(" << reason << ")"; + std::cerr << std::endl; + } + + MockEnvironment(const char **argv, std::string name = "default"); + virtual ~MockEnvironment(); + void postCreate(lcb_t instance); + +protected: + /** + * Protected destructor to make it to a singleton + */ + MockEnvironment(); + /** + * Handle to the one and only instance of the mock environment + */ + static MockEnvironment *instance; + + void bootstrapRealCluster(); + const struct test_server_info *mock; + ServerParams serverParams; + int numNodes; + bool realCluster; + ServerVersion serverVersion; + const char *http; + lcb_io_opt_st *iops; + std::set featureRegistry; + std::string bucketName; + const char **argv; + void clearAndReset(); + +private: + lcb_t innerClient; + void setupInnerClient(); + void init(); +}; + +#define LCB_TEST_REQUIRE_CLUSTER_VERSION(v) \ + if (!MockEnvironment::getInstance()->isRealCluster()) { \ + MockEnvironment::printSkipMessage(__FILE__, __LINE__, \ + "need real cluster"); \ + return; \ + } \ + if (MockEnvironment::getInstance()->getServerVersion() < v) { \ + MockEnvironment::printSkipMessage(__FILE__, __LINE__, \ + "needs higher cluster version"); \ + return; \ + } + +#define LCB_TEST_REQUIRE_FEATURE(s) \ + if (!MockEnvironment::getInstance()->hasFeature(s)) { \ + MockEnvironment::printSkipMessage(__FILE__, __LINE__, \ + "Feature " s \ + " missing in server implementation"); \ + return; \ + } + + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/mock-unit-test.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/mock-unit-test.cc new file mode 100644 index 00000000..2716f601 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/mock-unit-test.cc @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include "iotests.h" +#include + +#include "internal.h" /* vbucket_* things from lcb_t */ +#include +#include "bucketconfig/bc_http.h" + +#define LOGARGS(instance, lvl) \ + instance->settings, "tests-MUT", LCB_LOG_##lvl, __FILE__, __LINE__ + +/** + * Keep these around in case we do something useful here in the future + */ +void MockUnitTest::SetUp() { + MockEnvironment::Reset(); +} + +void checkConnectCommon(lcb_t instance) { + ASSERT_EQ(LCB_SUCCESS, lcb_connect(instance)); + lcb_wait(instance); + ASSERT_EQ(LCB_SUCCESS, lcb_get_bootstrap_status(instance)); +} + +void MockUnitTest::createConnection(HandleWrap &handle, lcb_t &instance) +{ + MockEnvironment::getInstance()->createConnection(handle, instance); + checkConnectCommon(handle.getLcb()); +} + +void MockUnitTest::createConnection(lcb_t &instance) +{ + MockEnvironment::getInstance()->createConnection(instance); + checkConnectCommon(instance); +} + +void MockUnitTest::createConnection(HandleWrap &handle) +{ + lcb_t instance = NULL; + createConnection(handle, instance); +} + +lcb_error_t +MockUnitTest::tryCreateConnection(HandleWrap& hw, + lcb_t& instance, lcb_create_st& crparams) +{ + MockEnvironment::getInstance()->createConnection(hw, instance, crparams); + EXPECT_EQ(LCB_SUCCESS, lcb_connect(instance)); + lcb_wait(instance); + return lcb_get_bootstrap_status(instance); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/mock-unit-test.h b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/mock-unit-test.h new file mode 100644 index 00000000..902d58a4 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/mock-unit-test.h @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef TESTS_MOCK_UNIT_TESTS_H +#define TESTS_MOCK_UNIT_TESTS_H 1 + +#include "config.h" +#include +#include +#include "mock-environment.h" + +class HandleWrap; + +#define SKIP_IF_MOCK() \ + if (!getenv(LCB_TEST_REALCLUSTER_ENV)) { \ + std::cerr << "Skipping: Need real cluster\n" << std::endl; \ + return; \ + } + +#define SKIP_UNLESS_MOCK() \ + if (getenv(LCB_TEST_REALCLUSTER_ENV)) { \ + std::cerr << "Skipping: Need mock cluster\n" << std::endl; \ + return; \ + } + +#define ASSERT_ERRISA(err, et) \ + ASSERT_EQ(et, (int)lcb_get_errtype(err) & (int)et) + +class MockUnitTest : public ::testing::Test +{ +protected: + virtual void SetUp(); + virtual void createConnection(lcb_t &instance); + virtual void createConnection(HandleWrap &handle); + virtual void createConnection(HandleWrap &handle, lcb_t &instance); + virtual lcb_error_t tryCreateConnection(HandleWrap &hw, + lcb_t &instance, lcb_create_st &crparams); + + // A mock "Transaction" + void doMockTxn(MockCommand &cmd) { + MockEnvironment::getInstance()->sendCommand(cmd); + MockResponse tmp; + MockEnvironment::getInstance()->getResponse(tmp); + ASSERT_TRUE(tmp.isOk()); + } +}; + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/serverparams.h b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/serverparams.h new file mode 100644 index 00000000..00641c4e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/serverparams.h @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef TESTS_SERVER_PARAMS_H +#define TESTS_SERVER_PARAMS_H 1 + +#include "config.h" +#include +#include +#include + +class ServerParams +{ +public: + ServerParams() { } + ServerParams(const char *h, const char *b, const char *u, const char *p) { + loadParam(host, h); + loadParam(bucket, b); + loadParam(user, u); + loadParam(pass, p); + } + + void makeConnectParams(lcb_create_st &crst, lcb_io_opt_t io) { + memset(&crst, 0, sizeof(crst)); + crst.version = 2; + crst.v.v2.host = host.c_str(); + crst.v.v2.bucket = bucket.c_str(); + crst.v.v2.user = user.c_str(); + crst.v.v2.passwd = pass.c_str(); + crst.v.v2.io = io; + if (!mcNodes.empty()) { + crst.v.v2.mchosts = mcNodes.c_str(); + } + } + + void setMcPorts(const std::vector& portlist) { + std::stringstream ss; + std::vector::const_iterator ii = portlist.begin(); + for (; ii != portlist.end(); ii++) { + ss << "localhost"; + ss << ":"; + ss << std::dec << *ii; + ss << ";"; + } + mcNodes = ss.str(); + } + +protected: + std::string host; + std::string user; + std::string pass; + std::string bucket; + std::string mcNodes; + +private: + void loadParam(std::string &d, const char *s) { + if (s) { + d.assign(s); + } + } +}; + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_arithmetic.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_arithmetic.cc new file mode 100644 index 00000000..24e28cac --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_arithmetic.cc @@ -0,0 +1,143 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include "iotests.h" + +class ArithmeticUnitTest : public MockUnitTest +{ +}; + +static lcb_uint64_t arithm_val; + +extern "C" { + static void arithmetic_incr_callback(lcb_t, const void *, + lcb_error_t error, + const lcb_arithmetic_resp_t *resp) + { + ASSERT_EQ(LCB_SUCCESS, error); + ASSERT_EQ(7, resp->v.v0.nkey); + ASSERT_EQ(0, memcmp(resp->v.v0.key, "counter", 7)); + ASSERT_EQ(arithm_val + 1, resp->v.v0.value); + arithm_val = resp->v.v0.value; + } + + static void arithmetic_decr_callback(lcb_t, const void *, + lcb_error_t error, + const lcb_arithmetic_resp_t *resp) + { + ASSERT_EQ(LCB_SUCCESS, error); + ASSERT_EQ(7, resp->v.v0.nkey); + ASSERT_EQ(0, memcmp(resp->v.v0.key, "counter", 7)); + ASSERT_EQ(arithm_val - 1, resp->v.v0.value); + arithm_val = resp->v.v0.value; + } + + static void arithmetic_create_callback(lcb_t, const void *, + lcb_error_t error, + const lcb_arithmetic_resp_t *resp) + { + ASSERT_EQ(LCB_SUCCESS, error); + ASSERT_EQ(9, resp->v.v0.nkey); + ASSERT_EQ(0, memcmp(resp->v.v0.key, "mycounter", 9)); + ASSERT_EQ(0xdeadbeef, resp->v.v0.value); + } +} + +/** + * Common function to bootstrap an arithmetic key and set the expected/last + * value counter. + */ +static void initArithmeticKey(lcb_t instance, std::string key, + lcb_uint64_t value) +{ + std::stringstream ss; + ss << value; + storeKey(instance, key, ss.str()); + arithm_val = value; +} + +/** + * @test Arithmetic (incr) + * @pre initialize a global variable @c arithm_val to 0. + * Schedule 10 arithmetic operations. The arithmetic callback should check + * that the current value is one greater than @c arithm_val. Then set + * @c arithm_val to the current value. + * + * @post The callback's assertions succeed (see precondition) + */ +TEST_F(ArithmeticUnitTest, testIncr) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + (void)lcb_set_arithmetic_callback(instance, arithmetic_incr_callback); + + initArithmeticKey(instance, "counter", 0); + + for (int ii = 0; ii < 10; ++ii) { + lcb_arithmetic_cmd_t cmd("counter", 7, 1); + lcb_arithmetic_cmd_t *cmds[] = { &cmd }; + lcb_arithmetic(instance, NULL, 1, cmds); + lcb_wait(instance); + } +} + +/** + * @test Arithmetic (Decr) + * + * @pre Initialize the @c arithm_val to @c 100. Decrement the key 10 times. + * + * @post See @ref testIncr for expectations + */ +TEST_F(ArithmeticUnitTest, testDecr) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + (void)lcb_set_arithmetic_callback(instance, arithmetic_decr_callback); + + initArithmeticKey(instance, "counter", 100); + + for (int ii = 0; ii < 10; ++ii) { + lcb_arithmetic_cmd_t cmd("counter", 7, -1); + lcb_arithmetic_cmd_t *cmds[] = { &cmd }; + lcb_arithmetic(instance, NULL, 1, cmds); + lcb_wait(instance); + } + +} + +/** + * @test Arithmetic (Creation) + * @pre Perform an arithmetic operation on a non-existent key. The increment + * offset is @c 0x77 and the default value is @c 0xdeadbeef + * + * @post Value upon getting the key is @c 0xdeadbeef + */ +TEST_F(ArithmeticUnitTest, testArithmeticCreate) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + removeKey(instance, "mycounter"); + (void)lcb_set_arithmetic_callback(instance, arithmetic_create_callback); + lcb_arithmetic_cmd_t cmd("mycounter", 9, 0x77, 1, 0xdeadbeef); + lcb_arithmetic_cmd_t *cmds[] = { &cmd }; + lcb_arithmetic(instance, NULL, 1, cmds); + lcb_wait(instance); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_behavior.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_behavior.cc new file mode 100644 index 00000000..8aa836b8 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_behavior.cc @@ -0,0 +1,226 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include +#include +#include + +#define ENV_VAR_NAME "LCB_IOPS_NAME" +#define ENV_VAR_SYM "LCB_IOPS_SYMBOL" + + +#ifdef _WIN32 +#define EXPECTED_DEFAULT LCB_IO_OPS_WINIOCP +#define EXPECTED_EFFECTIVE EXPECTED_DEFAULT +#define setenv(k, v, o) SetEnvironmentVariable(k, v) +#else +#define EXPECTED_DEFAULT LCB_IO_OPS_LIBEVENT +#if defined(HAVE_LIBEVENT) || defined(HAVE_LIBEVENT2) +#define EXPECTED_EFFECTIVE EXPECTED_DEFAULT +#else +#define EXPECTED_EFFECTIVE LCB_IO_OPS_SELECT +#endif +#endif + +static void setPluginEnv(const std::string &name, const std::string &sym) +{ + setenv(ENV_VAR_NAME, name.c_str(), 1); + setenv(ENV_VAR_SYM, sym.c_str(), 1); +} + +static void clearPluginEnv() +{ + setPluginEnv("", ""); +} + +typedef std::map plugin_map; +class PluginMap +{ +public: + plugin_map kv; + + PluginMap() { + kv["select"] = LCB_IO_OPS_SELECT; + kv["libevent"] = LCB_IO_OPS_LIBEVENT; + kv["libev"] = LCB_IO_OPS_LIBEV; +#ifdef _WIN32 + kv["iocp"] = LCB_IO_OPS_WINIOCP; + kv["winsock"] = LCB_IO_OPS_WINSOCK; +#endif + } + +}; + +static PluginMap plugins; + +class Behavior : public ::testing::Test +{ +public: + virtual void SetUp() { + const char *tmp; + if ((tmp = getenv(ENV_VAR_NAME)) != NULL) { + origPluginName = tmp; + } + + if ((tmp = getenv(ENV_VAR_SYM)) != NULL) { + origPluginSymbol = tmp; + } + + // Clear it + clearPluginEnv(); + + ASSERT_EQ(LCB_SUCCESS, lcb_create(&instance, NULL)); + } + + virtual void TearDown() { + lcb_destroy(instance); + setPluginEnv(origPluginName, origPluginSymbol); + } + +protected: + lcb_t instance; + std::string origPluginName; + std::string origPluginSymbol; +}; + + +TEST_F(Behavior, CheckDefaultValues) +{ + lcb_ipv6_t val; + lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_IP6POLICY, &val); + EXPECT_EQ(LCB_IPV6_DISABLED, val); + return; +} + +TEST_F(Behavior, CheckIPv6) +{ + lcb_ipv6_t val = LCB_IPV6_ONLY; + lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_IP6POLICY, &val); + lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_IP6POLICY, &val); + EXPECT_EQ(LCB_IPV6_ONLY, val); + + val = LCB_IPV6_ALLOW; + lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_IP6POLICY, &val); + lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_IP6POLICY, &val); + ASSERT_EQ(LCB_IPV6_ALLOW, val); + + val = LCB_IPV6_DISABLED; + lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_IP6POLICY, &val); + lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_IP6POLICY, &val); + ASSERT_EQ(LCB_IPV6_DISABLED, val); +} + +TEST_F(Behavior, PluginDefaults) +{ + lcb_error_t err; + struct lcb_cntl_iops_info_st info; + memset(&info, 0, sizeof(info)); + + err = lcb_cntl(NULL, LCB_CNTL_GET, LCB_CNTL_IOPS_DEFAULT_TYPES, &info); + + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(EXPECTED_DEFAULT, info.v.v0.os_default); + ASSERT_EQ(EXPECTED_EFFECTIVE, info.v.v0.effective); +} + +TEST_F(Behavior, PluginEnvironment) +{ + + for (plugin_map::iterator iter = plugins.kv.begin(); + iter != plugins.kv.end(); iter++) { + + setPluginEnv(iter->first, ""); + + lcb_error_t err; + struct lcb_cntl_iops_info_st info; + memset(&info, 0, sizeof(info)); + + err = lcb_cntl(NULL, LCB_CNTL_GET, LCB_CNTL_IOPS_DEFAULT_TYPES, &info); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(EXPECTED_DEFAULT, info.v.v0.os_default); + ASSERT_EQ(iter->second, info.v.v0.effective) << iter->first; + } +} + +TEST_F(Behavior, PluginOverrides) +{ + // Environment is cleared + struct lcb_create_io_ops_st options; + struct lcb_cntl_iops_info_st ioinfo; + + memset(&options, 0, sizeof(options)); + memset(&ioinfo, 0, sizeof(ioinfo)); + + setPluginEnv("select", ""); + options.version = 0; + options.v.v0.type = LCB_IO_OPS_LIBEV; + lcb_error_t err; + + ioinfo.v.v0.options = &options; + err = lcb_cntl(NULL, LCB_CNTL_GET, LCB_CNTL_IOPS_DEFAULT_TYPES, &ioinfo); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(ioinfo.v.v0.effective, LCB_IO_OPS_LIBEV); + + setPluginEnv("select", ""); + options.v.v0.type = LCB_IO_OPS_DEFAULT; + err = lcb_cntl(NULL, LCB_CNTL_GET, LCB_CNTL_IOPS_DEFAULT_TYPES, &ioinfo); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(ioinfo.v.v0.effective, LCB_IO_OPS_SELECT); + + memset(&options, 0, sizeof(options)); + options.version = 1; + options.v.v1.sofile = "libfoo"; + options.v.v1.symbol = "abort"; + err = lcb_cntl(NULL, LCB_CNTL_GET, LCB_CNTL_IOPS_DEFAULT_TYPES, &ioinfo); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(ioinfo.v.v0.effective, 0); + +} + +TEST_F(Behavior, BadPluginEnvironment) +{ + lcb_error_t err; + struct lcb_cntl_iops_info_st info; + memset(&info, 0, sizeof(info)); + + setPluginEnv("foobarbaz", "non_existent_symbol"); + err = lcb_cntl(NULL, LCB_CNTL_GET, LCB_CNTL_IOPS_DEFAULT_TYPES, &info); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(info.v.v0.os_default, EXPECTED_DEFAULT); + ASSERT_EQ(info.v.v0.effective, 0); + + lcb_t instance2; + ASSERT_EQ(lcb_create(&instance2, NULL), LCB_DLOPEN_FAILED); + + setPluginEnv("foobarbaz", ""); + ASSERT_EQ(lcb_create(&instance2, NULL), LCB_BAD_ENVIRONMENT); + + // Find a DLL that we know can be loaded, but doesn't have the symbols + // we need. For windows, we use the unqualified name, +#ifdef _WIN32 + const char *dllname = "kernel32.dll."; +#elif __APPLE__ + const char *dllname = "libm.dylib"; +#else + const char *dllname = "libm.so"; +#endif + + setPluginEnv(dllname, "nonexist-symbol"); + ASSERT_EQ(lcb_create(&instance2, NULL), LCB_DLSYM_FAILED); + +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_configcache.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_configcache.cc new file mode 100644 index 00000000..6d606608 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_configcache.cc @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include "iotests.h" + +#include + +class ConfigCacheUnitTest : public MockUnitTest +{ +}; + +extern "C" { +static void bootstrap_callback(lcb_t instance, lcb_error_t err) +{ + EXPECT_EQ(LCB_SUCCESS, err); + int *pp = (int *)lcb_get_cookie(instance); + *pp += 1; +} +} + +TEST_F(ConfigCacheUnitTest, testConfigCache) +{ + lcb_t instance; + lcb_error_t err; + lcb_create_st cropts; + + // Get the filename: + char filename[L_tmpnam + 0]; + ASSERT_TRUE(NULL != tmpnam(filename)); + memset(&cropts, 0, sizeof(cropts)); + + MockEnvironment::getInstance()->makeConnectParams(cropts, NULL); + doLcbCreate(&instance, &cropts, MockEnvironment::getInstance()); + err = lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_CONFIGCACHE, (void *)filename); + ASSERT_EQ(LCB_SUCCESS, err); + + int is_loaded; + err = lcb_cntl(instance, LCB_CNTL_GET, + LCB_CNTL_CONFIG_CACHE_LOADED, &is_loaded); + + ASSERT_EQ(err, LCB_SUCCESS); + ASSERT_EQ(is_loaded, 0); + + err = lcb_connect(instance); + ASSERT_EQ(err, LCB_SUCCESS); + + err = lcb_wait(instance); + ASSERT_EQ(err, LCB_SUCCESS); + + // now try another one + lcb_destroy(instance); + doLcbCreate(&instance, &cropts, MockEnvironment::getInstance()); + ASSERT_EQ(LCB_SUCCESS, err); + err = lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_CONFIGCACHE, (void *)filename); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_set_bootstrap_callback(instance, bootstrap_callback); + int bsCalled = 0; + lcb_set_cookie(instance, &bsCalled); + + err = lcb_connect(instance); + ASSERT_EQ(LCB_SUCCESS, err); + + err = lcb_wait(instance); + ASSERT_EQ(LCB_SUCCESS, err); + + err = lcb_cntl(instance, LCB_CNTL_GET, + LCB_CNTL_CONFIG_CACHE_LOADED, &is_loaded); + ASSERT_NE(0, is_loaded); + ASSERT_EQ(1, bsCalled); + + /* Just make sure we can schedule a command */ + storeKey(instance, "a_key", "a_value"); + + lcb_destroy(instance); + + doLcbCreate(&instance, &cropts, MockEnvironment::getInstance()); + ASSERT_EQ(LCB_SUCCESS, err); + err = lcb_cntl_string(instance, "config_cache", filename); + ASSERT_EQ(LCB_SUCCESS, err); + err = lcb_connect(instance); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_wait(instance); + err = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_CONFIG_CACHE_LOADED, &is_loaded); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_NE(0, is_loaded); + lcb_destroy(instance); + + doLcbCreate(&instance, &cropts, MockEnvironment::getInstance()); + ASSERT_EQ(LCB_SUCCESS, err); + err = lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_CONFIGCACHE_RO, (void *)filename); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_destroy(instance); + + remove(filename); + + // Try one more time, with a file that does not exist.. + doLcbCreate(&instance, &cropts, MockEnvironment::getInstance()); + ASSERT_EQ(LCB_SUCCESS, err); + err = lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_CONFIGCACHE_RO, (void *)filename); + ASSERT_NE(LCB_SUCCESS, err); + lcb_destroy(instance); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_confmon.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_confmon.cc new file mode 100644 index 00000000..30acddc3 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_confmon.cc @@ -0,0 +1,241 @@ +#define LCB_BOOTSTRAP_DEFINE_STRUCT + +#include "iotests.h" +#include "config.h" +#include "internal.h" +#include "bucketconfig/clconfig.h" +#include +#include + +class Confmon : public ::testing::Test +{ + void SetUp() { + MockEnvironment::Reset(); + } +}; + +struct evstop_listener { + clconfig_listener base; + lcbio_pTABLE io; + int called; +}; + +extern "C" { +static void listen_callback1(clconfig_listener *lsn, clconfig_event_t event, + clconfig_info *info) +{ + if (event != CLCONFIG_EVENT_GOT_NEW_CONFIG) { + return; + } + + evstop_listener *me = reinterpret_cast(lsn); + me->called = 1; + IOT_STOP(me->io); +} +} + +TEST_F(Confmon, testBasic) +{ + HandleWrap hw; + lcb_t instance; + MockEnvironment::getInstance()->createConnection(hw, instance); + + + lcb_confmon *mon = lcb_confmon_create(instance->settings, instance->iotable); + clconfig_provider *http = lcb_confmon_get_provider(mon, LCB_CLCONFIG_HTTP); + lcb_clconfig_http_enable(http); + lcb_clconfig_http_set_nodes(http, instance->ht_nodes); + + lcb_confmon_prepare(mon); + + EXPECT_EQ(NULL, lcb_confmon_get_config(mon)); + EXPECT_EQ(LCB_SUCCESS, lcb_confmon_start(mon)); + EXPECT_EQ(LCB_SUCCESS, lcb_confmon_start(mon)); + EXPECT_EQ(LCB_SUCCESS, lcb_confmon_stop(mon)); + EXPECT_EQ(LCB_SUCCESS, lcb_confmon_stop(mon)); + + // Try to find a provider.. + clconfig_provider *provider = lcb_confmon_get_provider(mon, LCB_CLCONFIG_HTTP); + ASSERT_NE(0, provider->enabled); + + struct evstop_listener listener; + memset(&listener, 0, sizeof(listener)); + + listener.base.callback = listen_callback1; + listener.base.parent = mon; + listener.io = instance->iotable; + + lcb_confmon_add_listener(mon, &listener.base); + lcb_confmon_start(mon); + IOT_START(instance->iotable); + ASSERT_NE(0, listener.called); + + lcb_confmon_destroy(mon); +} + + +struct listener2 { + clconfig_listener base; + int call_count; + lcbio_pTABLE io; + clconfig_method_t last_source; + std::set expected_events; + + void reset() { + call_count = 0; + last_source = LCB_CLCONFIG_PHONY; + expected_events.clear(); + } + + listener2() { + memset(&base, 0, sizeof(base)); + io = NULL; + reset(); + } +}; + +static struct listener2* getListener2(const void *p) +{ + return reinterpret_cast(const_cast(p)); +} + +extern "C" { +static void listen_callback2(clconfig_listener *prov, + clconfig_event_t event, + clconfig_info *info) +{ + // Increase the number of times we've received a callback.. + struct listener2* lsn = getListener2(prov); + + if (event == CLCONFIG_EVENT_MONITOR_STOPPED) { + IOT_START(lsn->io); + return; + } + + if (!lsn->expected_events.empty()) { + if (lsn->expected_events.end() == + lsn->expected_events.find(event)) { + return; + } + } + + lsn->call_count++; + lsn->last_source = info->origin; + IOT_STOP(lsn->io); +} +} + +static void runConfmon(lcbio_pTABLE io, lcb_confmon *mon) +{ + IOT_START(io); +} + +TEST_F(Confmon, testCycle) +{ + HandleWrap hw; + lcb_t instance; + lcb_create_st cropts; + MockEnvironment *mock = MockEnvironment::getInstance(); + + if (mock->isRealCluster()) { + return; + } + + mock->createConnection(hw, instance); + instance->settings->bc_http_stream_time = 100000; + instance->memd_sockpool->tmoidle = 100000; + + lcb_confmon *mon = lcb_confmon_create(instance->settings, instance->iotable); + + struct listener2 lsn; + lsn.base.callback = listen_callback2; + lsn.io = instance->iotable; + lsn.reset(); + + lcb_confmon_add_listener(mon, &lsn.base); + + mock->makeConnectParams(cropts, NULL); + clconfig_provider *cccp = lcb_confmon_get_provider(mon, LCB_CLCONFIG_CCCP); + clconfig_provider *http = lcb_confmon_get_provider(mon, LCB_CLCONFIG_HTTP); + + hostlist_t hl = hostlist_create(); + hostlist_add_stringz(hl, cropts.v.v2.mchosts, 11210); + lcb_clconfig_cccp_enable(cccp, instance); + lcb_clconfig_cccp_set_nodes(cccp, hl); + + lcb_clconfig_http_enable(http); + lcb_clconfig_http_set_nodes(http, instance->ht_nodes); + hostlist_destroy(hl); + + lcb_confmon_prepare(mon); + lcb_confmon_start(mon); + lsn.expected_events.insert(CLCONFIG_EVENT_GOT_NEW_CONFIG); + runConfmon(lsn.io, mon); + + // Ensure CCCP is functioning properly and we're called only once. + ASSERT_EQ(1, lsn.call_count); + ASSERT_EQ(LCB_CLCONFIG_CCCP, lsn.last_source); + + lcb_confmon_start(mon); + lsn.reset(); + lsn.expected_events.insert(CLCONFIG_EVENT_GOT_ANY_CONFIG); + runConfmon(lsn.io, mon); + ASSERT_EQ(1, lsn.call_count); + ASSERT_EQ(LCB_CLCONFIG_CCCP, lsn.last_source); + + mock->setCCCP(false); + mock->failoverNode(5); + lsn.reset(); + lcb_confmon_start(mon); + lsn.expected_events.insert(CLCONFIG_EVENT_GOT_ANY_CONFIG); + lsn.expected_events.insert(CLCONFIG_EVENT_GOT_NEW_CONFIG); + runConfmon(lsn.io, mon); + ASSERT_EQ(LCB_CLCONFIG_HTTP, lsn.last_source); + ASSERT_EQ(1, lsn.call_count); + lcb_confmon_destroy(mon); +} + +TEST_F(Confmon, testBootstrapMethods) +{ + lcb_t instance; + HandleWrap hw; + MockEnvironment::getInstance()->createConnection(hw, instance); + lcb_error_t err = lcb_connect(instance); + ASSERT_EQ(LCB_SUCCESS, err); + + // Try the various bootstrap times + struct lcb_BOOTSTRAP *bs = instance->bootstrap; + hrtime_t last = bs->last_refresh, cur = 0; + + // Reset it for the time being + bs->last_refresh = 0; + lcb_confmon_stop(instance->confmon); + + // Refreshing now should work + lcb_bootstrap_common(instance, LCB_BS_REFRESH_THROTTLE); + ASSERT_NE(0, lcb_confmon_is_refreshing(instance->confmon)); + + cur = bs->last_refresh; + ASSERT_GT(cur, 0); + ASSERT_EQ(0, bs->errcounter); + last = cur; + + // Stop it, so the state is reset + lcb_confmon_stop(instance->confmon); + ASSERT_EQ(0, lcb_confmon_is_refreshing(instance->confmon)); + + lcb_bootstrap_common(instance, LCB_BS_REFRESH_THROTTLE|LCB_BS_REFRESH_INCRERR); + ASSERT_EQ(last, bs->last_refresh); + ASSERT_EQ(1, bs->errcounter); + + // Ensure that a throttled-without-incr doesn't actually incr + lcb_bootstrap_common(instance, LCB_BS_REFRESH_THROTTLE); + ASSERT_EQ(1, bs->errcounter); + + // No refresh yet + ASSERT_EQ(0, lcb_confmon_is_refreshing(instance->confmon)); + + lcb_bootstrap_common(instance, LCB_BS_REFRESH_ALWAYS); + ASSERT_NE(0, lcb_confmon_is_refreshing(instance->confmon)); + lcb_confmon_stop(instance->confmon); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_durability.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_durability.cc new file mode 100644 index 00000000..0aebe8f8 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_durability.cc @@ -0,0 +1,1059 @@ +#include "config.h" +#include "iotests.h" +#include "internal.h" +#include + +using namespace std; + +#define LOGARGS(instance, lvl) \ + instance->settings, "tests-dur", LCB_LOG_##lvl, __FILE__, __LINE__ +#define SECS_USECS(f) ((f) * 1000000) + +static bool supportsMutationTokens(lcb_t instance) +{ + // Ensure we have at least one connection + storeKey(instance, "dummy_stok_test", "dummy"); + + int val = 0; + lcb_error_t rc; + rc = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_MUTATION_TOKENS_SUPPORTED, &val); + + + EXPECT_EQ(LCB_SUCCESS, rc); + if (val == 0) { + printf("Current cluster does not support synctokens!\n"); + return false; + } else { + return true; + } +} + +class DurabilityUnitTest : public MockUnitTest +{ +protected: + static void defaultOptions(lcb_t instance, lcb_durability_opts_st &opts) { + lcb_size_t nservers = lcb_get_num_nodes(instance); + lcb_size_t nreplicas = lcb_get_num_replicas(instance); + + opts.v.v0.persist_to = (lcb_uint16_t) min(nreplicas + 1, nservers); + opts.v.v0.replicate_to = (lcb_uint16_t) min(nreplicas, nservers - 1); + } +}; + +extern "C" { +static void defaultDurabilityCallback(lcb_t, int, const lcb_RESPENDURE*); +static void multiDurabilityCallback(lcb_t, int, const lcb_RESPENDURE*); +} + +class DurabilityOperation +{ +public: + DurabilityOperation() { + } + + string key; + lcb_RESPENDURE resp; + lcb_CMDENDURE req; + + void assign(const lcb_RESPENDURE *resp) { + this->resp = *resp; + key.assign((const char *)resp->key, resp->nkey); + this->resp.key = NULL; + } + + void wait(lcb_t instance) { + lcb_install_callback3(instance, LCB_CALLBACK_ENDURE, + (lcb_RESPCALLBACK)defaultDurabilityCallback); + EXPECT_EQ(LCB_SUCCESS, lcb_wait(instance)); + } + + void wait(lcb_t instance, + const lcb_durability_opts_t *opts, const lcb_CMDENDURE *cmd) { + + lcb_error_t rc; + lcb_MULTICMD_CTX *mctx = lcb_endure3_ctxnew(instance, opts, &rc); + EXPECT_FALSE(mctx == NULL); + rc = mctx->addcmd(mctx, (lcb_CMDBASE*)cmd); + EXPECT_EQ(LCB_SUCCESS, rc); + rc = mctx->done(mctx, this); + EXPECT_EQ(LCB_SUCCESS, rc); + wait(instance); + } + + void run(lcb_t instance, const lcb_durability_opts_t *opts, const Item &itm) { + lcb_CMDENDURE cmd = { 0 }; + ASSERT_FALSE(itm.key.empty()); + LCB_CMD_SET_KEY(&cmd, itm.key.data(), itm.key.length()); + cmd.cas = itm.cas; + wait(instance, opts, &cmd); + } + + // Really wait(), but named as 'run()' here to make usage more consistent. + void run(lcb_t instance, const lcb_durability_opts_t *opts, + const lcb_CMDENDURE& cmd) { + wait(instance, opts, &cmd); + } + + void assertCriteriaMatch(const lcb_durability_opts_st &opts) { + ASSERT_EQ(LCB_SUCCESS, resp.rc); + ASSERT_TRUE(resp.persisted_master != 0); + ASSERT_TRUE(opts.v.v0.persist_to <= resp.npersisted); + ASSERT_TRUE(opts.v.v0.replicate_to <= resp.nreplicated); + } + + void dump(std::string &str) { + if (key.empty()) { + str = "\n"; + return; + } + std::stringstream ss; + ss << "Key: " << key << std::endl + << "Error: " << resp.rc << std::endl + << "Persisted (master?): " << resp.npersisted + << " (" << resp.persisted_master << ")" << std::endl + << "Replicated: " << resp.nreplicated << std::endl + << "CAS: 0x" << std::hex << resp.cas << std::endl; + str += ss.str(); + } + + void dump() { + string s; + dump(s); + cout << s; + } +}; + +class DurabilityMultiOperation +{ +public: + + DurabilityMultiOperation() : counter(0) { + } + + template + void run(lcb_t instance, const lcb_durability_opts_t *opts, const T &items) { + counter = 0; + unsigned ii = 0; + typename T::const_iterator iter = items.begin(); + lcb_error_t rc; + lcb_MULTICMD_CTX *mctx = lcb_endure3_ctxnew(instance, opts, &rc); + ASSERT_FALSE(mctx == NULL); + + for (; iter != items.end(); iter++, ii++) { + lcb_CMDENDURE cmd = { 0 }; + const Item &itm = *iter; + + cmd.cas = itm.cas; + LCB_CMD_SET_KEY(&cmd, itm.key.c_str(), itm.key.length()); + rc = mctx->addcmd(mctx, (lcb_CMDBASE*)&cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + kmap[itm.key] = DurabilityOperation(); + } + + lcb_install_callback3(instance, LCB_CALLBACK_ENDURE, + (lcb_RESPCALLBACK)multiDurabilityCallback); + + rc = mctx->done(mctx, this); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_EQ(items.size(), counter); + } + + void assign(const lcb_RESPENDURE *resp) { + ASSERT_GT(resp->nkey, 0U); + counter++; + + string key; + key.assign((const char *)resp->key, resp->nkey); + ASSERT_TRUE(kmap.find(key) != kmap.end()); + kmap[key].assign(resp); + } + + template + bool _findItem(const string &s, const T &items, Item &itm) { + for (typename T::const_iterator iter = items.begin(); + iter != items.end(); iter++) { + if (iter->key.compare(s) == 0) { + itm = *iter; + return true; + } + } + return false; + } + + template + void assertAllMatch(const lcb_durability_opts_t &opts, + const T &items_ok, + const T &items_missing, + lcb_error_t missing_err = LCB_KEY_ENOENT) { + + for (map::iterator iter = kmap.begin(); + iter != kmap.end(); + iter++) { + + Item itm_tmp; + // make sure we were expecting it + if (_findItem(iter->second.key, items_ok, itm_tmp)) { + iter->second.assertCriteriaMatch(opts); + + } else if (_findItem(iter->second.key, items_missing, itm_tmp)) { + ASSERT_EQ(missing_err, iter->second.resp.rc); + + } else { + ASSERT_STREQ("", + "Key not in missing or OK list"); + } + } + + // Finally, make sure they're all there + + for (typename T::const_iterator iter = items_ok.begin(); + iter != items_ok.end(); + iter++) { + ASSERT_TRUE(kmap.find(iter->key) != kmap.end()); + } + + for (typename T::const_iterator iter = items_missing.begin(); + iter != items_missing.end(); iter++) { + ASSERT_TRUE(kmap.find(iter->key) != kmap.end()); + } + } + + unsigned counter; + map kmap; +}; + +extern "C" { +static void defaultDurabilityCallback(lcb_t, int, const lcb_RESPENDURE *res) +{ + ((DurabilityOperation*)res->cookie)->assign(res); +} +static void multiDurabilityCallback(lcb_t, int, const lcb_RESPENDURE *res) +{ + ((DurabilityMultiOperation*)res->cookie)->assign(res); +} + +} + +TEST_F(DurabilityUnitTest, testInvalidCriteria) +{ + /** + * We don't schedule stuff to the network here + */ + HandleWrap hwrap; + createConnection(hwrap); + + lcb_t instance = hwrap.getLcb(); + + lcb_durability_opts_t opts = { 0 }; + lcb_durability_cmd_t cmd = { 0 }, *cmd_p = &cmd; + lcb_error_t err; + + defaultOptions(instance, opts); + + err = lcb_durability_poll(instance, NULL, &opts, 0, &cmd_p); + ASSERT_EQ(LCB_EINVAL, err); + + opts.v.v0.persist_to = 10; + opts.v.v0.replicate_to = 100; + opts.v.v0.cap_max = 0; + + err = lcb_durability_poll(instance, NULL, &opts, 1, &cmd_p); + ASSERT_EQ(err, LCB_DURABILITY_ETOOMANY); +} + +/** + * Test various criteria for durability + */ +TEST_F(DurabilityUnitTest, testDurabilityCriteria) +{ + HandleWrap hwrap; + lcb_t instance; + + createConnection(hwrap); + instance = hwrap.getLcb(); + + lcb_durability_opts_st opts = { 0 }; + lcb_durability_cmd_st cmd = { 0 }; + lcb_durability_cmd_st *cmd_p = &cmd; + + /** test with no persist/replicate */ + defaultOptions(instance, opts); + + opts.v.v0.replicate_to = 0; + opts.v.v0.persist_to = 0; +} + +/** + * @test Test several 'basic' durability functions + * + * @pre Store a key. Perform a durability check with master-only persistence + * (i.e. persist_to = 1, replicate_to = 0) + * @post Operation succeeds + * + * @pre Check the key against 'maximum possible' durability by estimating the + * maximum replica/server count + * + * @post Operation succeeds + * + * @pre Set the durability options to a very large criteria, but set the + * @c cap_max flag so the API will reduce it to a sane default. Then use it + * for a durability check + * + * @post the response is successful + */ +TEST_F(DurabilityUnitTest, testSimpleDurability) +{ + /** need real cluster for durability tests */ + LCB_TEST_REQUIRE_FEATURE("observe"); + SKIP_UNLESS_MOCK(); + + HandleWrap hwrap; + lcb_t instance; + + Item kv = Item("a_key", "a_value", 0); + createConnection(hwrap); + instance = hwrap.getLcb(); + + removeKey(instance, kv.key); + + KVOperation kvo = KVOperation(&kv); + kvo.store(instance); + + // Now wait for it to persist + lcb_durability_opts_t opts; + memset(&opts, 0, sizeof(opts)); + opts.v.v0.persist_to = 1; + opts.v.v0.replicate_to = 0; + + kvo = KVOperation(&kv); + kvo.get(instance); + + DurabilityOperation dop; + dop.run(instance, &opts, kvo.result); + + dop.assertCriteriaMatch(opts); + ASSERT_STREQ(kv.key.c_str(), dop.key.c_str()); + + + // Try with more expanded criteria + defaultOptions(instance, opts); + dop = DurabilityOperation(); + dop.run(instance, &opts, kvo.result); + dop.assertCriteriaMatch(opts); + + // Make the options to some absurd number. Ensure it's capped! + opts.v.v0.persist_to = 100; + opts.v.v0.replicate_to = 100; + opts.v.v0.cap_max = 1; + + dop = DurabilityOperation(); + dop.run(instance, &opts, kvo.result); + defaultOptions(instance, opts); + dop.assertCriteriaMatch(opts); +} + +/** + * @test Durability checks against non-existent keys + * @pre Remove a key, and perform a durability check against it + * @post Operation fails with @c LCB_KEY_ENOENT + */ +TEST_F(DurabilityUnitTest, testNonExist) +{ + LCB_TEST_REQUIRE_FEATURE("observe"); + SKIP_UNLESS_MOCK(); + + lcb_t instance; + HandleWrap hwrap; + + string key = "non-exist-key"; + + createConnection(hwrap); + instance = hwrap.getLcb(); + + removeKey(instance, key); + + Item itm = Item(key, "", 0); + + DurabilityOperation dop; + lcb_durability_opts_t opts = { 0 }; + opts.v.v0.timeout = SECS_USECS(2); + + defaultOptions(instance, opts); + + // Ensure this only uses the CAS method + opts.version = 1; + opts.v.v0.pollopts = LCB_DURABILITY_MODE_CAS; + + dop.run(instance, &opts, itm); + ASSERT_EQ(LCB_KEY_ENOENT, dop.resp.rc); +} + +/** + * @test Test negative durability (Delete) + * + * @pre Store a key, Remove it, perform a durability check against the key, + * using the @c check_delete flag + * + * @post A positive reply is received indicating the item has been deleted + * + * @pre Store the key, but don't remove it. Perform a durability check against + * the key using the delete flag + * + * @post Operation is returned with @c LCB_ETIMEDOUT + */ +TEST_F(DurabilityUnitTest, testDelete) +{ + LCB_TEST_REQUIRE_FEATURE("observe"); + SKIP_UNLESS_MOCK(); + + HandleWrap hwrap; + lcb_t instance; + lcb_durability_opts_t opts = { 0 }; + string key = "deleted-key"; + createConnection(hwrap); + instance = hwrap.getLcb(); + + storeKey(instance, key, "value"); + + Item itm = Item(key, "value", 0); + + KVOperation kvo = KVOperation(&itm); + DurabilityOperation dop; + + kvo.remove(instance); + + // Ensure the key is actually purged! + MockMutationCommand mcmd(MockCommand::PURGE, key); + mcmd.onMaster = true; + mcmd.replicaCount = lcb_get_num_replicas(instance); + doMockTxn(mcmd); + + defaultOptions(instance, opts); + opts.v.v0.check_delete = 1; + dop.run(instance, &opts, itm); + dop.assertCriteriaMatch(opts); + + kvo.clear(); + kvo.request = &itm; + kvo.store(instance); + + opts.v.v0.timeout = SECS_USECS(1); + + // With CAS + opts.version = 1; + opts.v.v0.pollopts = LCB_DURABILITY_MODE_CAS; + dop = DurabilityOperation(); + dop.run(instance, &opts, itm); + ASSERT_EQ(LCB_ETIMEDOUT, dop.resp.rc); + + // With seqno + if (supportsMutationTokens(instance)) { + opts.v.v0.pollopts = LCB_DURABILITY_MODE_SEQNO; + dop = DurabilityOperation(); + dop.run(instance, &opts, itm); + ASSERT_EQ(LCB_SUCCESS, dop.resp.rc); + } +} + +/** + * @test Test behavior when a key is modified (exists with a different CAS) + * + * @pre Store a key. Store it again. Keep the CAS from the first store as the + * stale CAS. Keep the current CAS as well. + * + * @pre Perform a durability check against the stale CAS + * @post Operation fails with @c LCB_KEY_EEXISTS + * + * @pre Perform a durability check against the new CAS + * @post Operation succeeds + */ +TEST_F(DurabilityUnitTest, testModified) +{ + LCB_TEST_REQUIRE_FEATURE("observe"); + + HandleWrap hwrap; + lcb_t instance; + lcb_durability_opts_t opts = { 0 }; + string key = "mutated-key"; + Item itm = Item(key, key); + KVOperation kvo_cur(&itm), kvo_stale(&itm); + + createConnection(hwrap); + instance = hwrap.getLcb(); + + kvo_stale.store(instance); + kvo_cur.store(instance); + + kvo_stale.result.val = kvo_cur.result.val = key; + + defaultOptions(instance, opts); + DurabilityOperation dop; + + opts.version = 1; + opts.v.v0.pollopts = LCB_DURABILITY_MODE_CAS; + dop.run(instance, &opts, kvo_stale.result); + ASSERT_EQ(LCB_KEY_EEXISTS, dop.resp.rc); + + if (supportsMutationTokens(instance)) { + opts.v.v0.pollopts = LCB_DURABILITY_MODE_SEQNO; + dop = DurabilityOperation(); + dop.run(instance, &opts, kvo_stale.result); + ASSERT_EQ(LCB_SUCCESS, dop.resp.rc); + } +} + +/** + * @test Test with very quick timeouts + * @pre Schedule an operation with an interval of 2 usec and a timeout of + * 5 usec + * + * @post Operation returns with LCB_ETIMEDOUT + */ +TEST_F(DurabilityUnitTest, testQuickTimeout) +{ + LCB_TEST_REQUIRE_FEATURE("observe"); + lcb_t instance; + HandleWrap hwrap; + lcb_durability_opts_t opts = { 0 }; + string key = "a_key"; + + createConnection(hwrap); + instance = hwrap.getLcb(); + + Item itm = Item(key, key); + KVOperation(&itm).store(instance); + + defaultOptions(instance, opts); + + /* absurd */ + opts.v.v0.timeout = 5; + opts.v.v0.interval = 2; + + for (unsigned ii = 0; ii < 10; ii++) { + DurabilityOperation dop; + dop.run(instance, &opts, itm); + ASSERT_EQ(LCB_ETIMEDOUT, dop.resp.rc); + } +} + +/** + * @test Test a durability request for multiple keys + * + * @pre Store ten keys, and check that they exist all at once + * @post all ten keys are received in the response, and they're ok + * + * @pre Check that ten missing keys exist all at once + * @post all ten keys are received in the response, and they have an error + * + * @pre Check the ten stored and ten missing keys in a single operation + * @post The ten missing keys are present and have a negative status, the ten + * stored keys are present and are OK + */ +TEST_F(DurabilityUnitTest, testMulti) +{ + LCB_TEST_REQUIRE_FEATURE("observe"); + unsigned ii; + const unsigned limit = 10; + + vector items_stored; + vector items_missing; + + lcb_durability_opts_t opts = { 0 }; + HandleWrap hwrap; + lcb_t instance; + + createConnection(hwrap); + instance = hwrap.getLcb(); + // Set the timeout to something high. For some reason this gives problem + // on a real cluster + lcb_cntl_setu32(instance, LCB_CNTL_DURABILITY_TIMEOUT, LCB_MS2US(10000)); + + for (ii = 0; ii < limit; ii++) { + char buf[64]; + sprintf(buf, "key-stored-%u", ii); + string key_stored = buf; + sprintf(buf, "key-missing-%u", ii); + string key_missing = buf; + + removeKey(instance, key_stored); + removeKey(instance, key_missing); + + Item itm_e = Item(key_stored, key_stored, 0); + Item itm_m = Item(key_missing, key_missing, 0); + + KVOperation kvo(&itm_e); + kvo.store(instance); + items_stored.push_back(kvo.result); + items_missing.push_back(itm_m); + } + + defaultOptions(instance, opts); + opts.version = 1; + opts.v.v0.pollopts = LCB_DURABILITY_MODE_CAS; + + /** + * Create the command.. + */ + DurabilityMultiOperation dmop = DurabilityMultiOperation(); + dmop.run(instance, &opts, items_stored); + dmop.assertAllMatch(opts, items_stored, vector()); + + // Store all the missing ones + opts.v.v0.timeout = (lcb_uint32_t)SECS_USECS(1.5); + dmop = DurabilityMultiOperation(); + dmop.run(instance, &opts, items_missing); + dmop.assertAllMatch(opts, vector(), items_missing, LCB_KEY_ENOENT); + + // Store them all together + opts.v.v0.timeout = 0; + vector combined; + combined.insert(combined.end(), items_stored.begin(), items_stored.end()); + combined.insert(combined.end(), items_missing.begin(), items_missing.end()); + dmop.run(instance, &opts, combined); + dmop.assertAllMatch(opts, items_stored, items_missing); +} + +struct cb_cookie { + int is_observe; + int count; +}; + +extern "C" { + static void dummyObserveCallback(lcb_t, const void *cookie, + lcb_error_t, + const lcb_observe_resp_t *) + { + struct cb_cookie *c = (struct cb_cookie *)cookie; + ASSERT_EQ(1, c->is_observe); + c->count++; + } + + static void dummyDurabilityCallback(lcb_t, const void *cookie, + lcb_error_t, + const lcb_durability_resp_t *) + { + struct cb_cookie *c = (struct cb_cookie *)cookie; + ASSERT_EQ(0, c->is_observe); + c->count++; + } +} + +/** + * @test Ensure basic observe functions as normal + * + * @pre pair up two batched commands, one a durability command, and one a + * primitive observe. Set up distinct callbacks for the two (both of which + * touch a counter, one incrementing and one decrementing an int*). + * Wait for the operations to complete via @c lcb_wait + * + * @post The durability counter is decremented, observe counter incremented + */ +TEST_F(DurabilityUnitTest, testObserveSanity) +{ + LCB_TEST_REQUIRE_FEATURE("observe"); + HandleWrap handle; + lcb_t instance; + createConnection(handle); + instance = handle.getLcb(); + lcb_error_t err; + + + lcb_set_durability_callback(instance, dummyDurabilityCallback); + lcb_set_observe_callback(instance, dummyObserveCallback); + + lcb_durability_opts_t opts = { 0 }; + + lcb_observe_cmd_t ocmd, *ocmds[] = { &ocmd }; + lcb_durability_cmd_t dcmd, *dcmds[] = { &dcmd }; + + memset(&ocmd, 0, sizeof(ocmd)); + memset(&dcmd, 0, sizeof(dcmd)); + + ocmd.v.v0.key = "key"; + ocmd.v.v0.nkey = 3; + + dcmd.v.v0.key = "key"; + dcmd.v.v0.nkey = 3; + + storeKey(instance, "key", "value"); + + defaultOptions(instance, opts); + + struct cb_cookie o_cookie = { 1, 0 }; + struct cb_cookie d_cookie = { 0, 0 }; + + err = lcb_observe(instance, &o_cookie, 1, ocmds); + ASSERT_EQ(LCB_SUCCESS, err); + + err = lcb_durability_poll(instance, &d_cookie, &opts, 1, dcmds); + ASSERT_EQ(LCB_SUCCESS, err); + + ASSERT_EQ(LCB_SUCCESS, lcb_wait(instance)); + + ASSERT_GT(o_cookie.count, 0); + ASSERT_GT(d_cookie.count, 0); +} + +TEST_F(DurabilityUnitTest, testMasterObserve) +{ + LCB_TEST_REQUIRE_FEATURE("observe"); + SKIP_UNLESS_MOCK(); + + HandleWrap handle; + createConnection(handle); + lcb_t instance = handle.getLcb(); + + lcb_set_observe_callback(instance, dummyObserveCallback); + lcb_observe_cmd_t ocmd, *ocmds[] = { &ocmd }; + memset(&ocmd, 0, sizeof(ocmd)); + ocmd.version = 1; + ocmd.v.v1.key = "key"; + ocmd.v.v1.options = LCB_OBSERVE_MASTER_ONLY; + ocmd.v.v1.nkey = 3; + storeKey(instance, "key", "value"); + struct cb_cookie o_cookie = { 1, 0 }; + lcb_error_t err = lcb_observe(instance, &o_cookie, 1, ocmds); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_wait(instance); + + // 2 == one for the callback, one for the NULL + ASSERT_EQ(2, o_cookie.count); +} + +extern "C" { +static void fo_callback(void *cookie) +{ + lcb_t instance = (lcb_t )cookie; + MockEnvironment *mock = MockEnvironment::getInstance(); + for (int ii = 1; ii < mock->getNumNodes(); ii++) { + mock->failoverNode(ii); + } + lcb_loop_unref(instance); +} +} + +/** + * Test the functionality of durability operations during things like + * node failovers. + * + * The idea behind here is to ensure that we can trigger a case where a series + * of OBSERVE packets are caught in the middle of a cluster update and end up + * being relocated to the same server. Previously (and currently) this would + * confuse the lookup_server_with_command functionality which would then invoke + * the 'NULL' callback multiple times (because it assumes it's not located + * anywhere else) + */ +TEST_F(DurabilityUnitTest, testDurabilityRelocation) +{ + SKIP_UNLESS_MOCK(); + + // Disable CCCP so that we get streaming updates + MockEnvironment *mock = MockEnvironment::getInstance(); + mock->setCCCP(false); + + HandleWrap handle; + lcb_t instance; + createConnection(handle); + instance = handle.getLcb(); + + lcb_set_durability_callback(instance, dummyDurabilityCallback); + std::string key = "key"; + + lcb_durability_cmd_t dcmd, *dcmds[] = { &dcmd }; + lcb_durability_opts_t opts = { 0 }; + + opts.v.v0.persist_to = 100; + opts.v.v0.replicate_to = 100; + opts.v.v0.cap_max = 1; + storeKey(instance, key, "value"); + + // Ensure we have to resend commands multiple times + MockMutationCommand mcmd(MockCommand::UNPERSIST, key); + mcmd.onMaster = true; + mcmd.replicaCount = lcb_get_num_replicas(instance); + doMockTxn(mcmd); + + memset(&dcmd, 0, sizeof(dcmd)); + + dcmd.v.v0.key = key.c_str(); + dcmd.v.v0.nkey = 3; + + /** + * Failover all but one node + */ + for (int ii = 1; ii < mock->getNumNodes(); ii++) { + mock->hiccupNodes(1000, 0); + } + lcbio_pTIMER tm = lcbio_timer_new(handle.getLcb()->iotable, + instance, fo_callback); + lcbio_timer_rearm(tm, 500000); + lcb_loop_ref(instance); + + struct cb_cookie cookie = { 0, 0 }; + lcb_error_t err = lcb_durability_poll(instance, &cookie, &opts, 1, dcmds); + ASSERT_EQ(LCB_SUCCESS, err); + + lcb_wait(instance); + lcbio_timer_destroy(tm); + ASSERT_EQ(1, cookie.count); +} + + +TEST_F(DurabilityUnitTest, testDuplicateCommands) +{ + HandleWrap hw; + lcb_t instance; + createConnection(hw, instance); + std::string key("key"); + std::vector cmds; + std::vector cmdlist; + lcb_durability_opts_t options = { 0 }; + options.v.v0.replicate_to = 100; + options.v.v0.persist_to = 100; + options.v.v0.cap_max = 1; + + for (int ii = 0; ii < 2; ii++) { + lcb_durability_cmd_t cmd = { 0 }; + cmd.v.v0.key = key.c_str(); + cmd.v.v0.nkey = key.size(); + cmds.push_back(cmd); + } + for (size_t ii = 0; ii < cmds.size(); ii++) { + cmdlist.push_back(&cmds[ii]); + } + lcb_error_t err; + err = lcb_durability_poll(instance, NULL, &options, 2, &cmdlist[0]); + ASSERT_EQ(LCB_DUPLICATE_COMMANDS, err); +} + +TEST_F(DurabilityUnitTest, testMissingSynctoken) +{ + HandleWrap hw; + lcb_t instance; + createConnection(hw, instance); + + if (!supportsMutationTokens(instance)) { + return; + } + + std::string("nonexist-key"); + lcb_error_t rc; + lcb_MULTICMD_CTX *mctx; + lcb_durability_opts_t options = { 0 }; + defaultOptions(instance, options); + options.version = 1; + options.v.v0.pollopts = LCB_DURABILITY_MODE_SEQNO; + + mctx = lcb_endure3_ctxnew(instance, &options, &rc); + ASSERT_FALSE(mctx == NULL); + lcb_CMDENDURE cmd = { 0 }; + LCB_CMD_SET_KEY(&cmd, "foo", 3); + + rc = mctx->addcmd(mctx, (lcb_CMDBASE*)&cmd); + ASSERT_EQ(LCB_DURABILITY_NO_MUTATION_TOKENS, rc); + + mctx->fail(mctx); +} + +TEST_F(DurabilityUnitTest, testExternalSynctoken) +{ + HandleWrap hw1, hw2; + lcb_t instance1, instance2; + createConnection(hw1, instance1); + createConnection(hw2, instance2); + + if (!supportsMutationTokens(instance1)) { + return; + } + + std::string key("hello"); + std::string value("world"); + storeKey(instance1, key, value); + + const lcb_MUTATION_TOKEN *ss; + lcb_KEYBUF kb; + lcb_error_t rc; + LCB_KREQ_SIMPLE(&kb, key.c_str(), key.size()); + ss = lcb_get_mutation_token(instance1, &kb, &rc); + ASSERT_FALSE(ss == NULL); + ASSERT_TRUE(LCB_MUTATION_TOKEN_ISVALID(ss)); + ASSERT_EQ(LCB_SUCCESS, rc); + + lcb_durability_opts_t options = { 0 }; + lcb_CMDENDURE cmd = { 0 }; + defaultOptions(instance2, options); + options.version = 1; + options.v.v0.pollopts = LCB_DURABILITY_MODE_SEQNO; + + // Initialize the command + LCB_CMD_SET_KEY(&cmd, key.c_str(), key.size()); + cmd.mutation_token = ss; + cmd.cmdflags |= LCB_CMDENDURE_F_MUTATION_TOKEN; + + DurabilityOperation dop; + dop.run(instance2, &options, cmd); + // TODO: How to actually run this? + ASSERT_EQ(LCB_SUCCESS, dop.resp.rc); +} + +TEST_F(DurabilityUnitTest, testOptionValidation) +{ + HandleWrap hw; + lcb_t instance; + lcb_U16 persist = 0, replicate = 0; + int options; + lcb_error_t rc; + + createConnection(hw, instance); + + // Validate simple mode + persist = -1; + replicate = -1; + rc = lcb_durability_validate(instance, &persist, &replicate, + LCB_DURABILITY_VALIDATE_CAPMAX); + + ASSERT_EQ(LCB_SUCCESS, rc); + ASSERT_TRUE(persist > replicate); + + lcbvb_CONFIG *vbc; + rc = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_VBCONFIG, &vbc); + ASSERT_EQ(LCB_SUCCESS, rc); + + int replica_max = min(LCBVB_NREPLICAS(vbc), LCBVB_NDATASERVERS(vbc)-1); + int persist_max = replica_max + 1; + + ASSERT_EQ(replica_max, replicate); + ASSERT_EQ(persist_max, persist); + + persist = 0; + replicate = 0; + rc = lcb_durability_validate(instance, &persist, &replicate, 0); + ASSERT_EQ(LCB_EINVAL, rc); + + persist = -1; + replicate = -1; + rc = lcb_durability_validate(instance, &persist, &replicate, 0); + ASSERT_EQ(LCB_DURABILITY_ETOOMANY, rc); + + persist = persist_max; + replicate = replica_max; + rc = lcb_durability_validate(instance, &persist, &replicate, 0); + ASSERT_EQ(LCB_SUCCESS, rc); + ASSERT_EQ(persist_max, persist); + ASSERT_EQ(replica_max, replicate); + + rc = lcb_durability_validate(instance, &persist, &replicate, + LCB_DURABILITY_VALIDATE_CAPMAX); + ASSERT_EQ(LCB_SUCCESS, rc); + ASSERT_EQ(persist_max, persist); + ASSERT_EQ(replica_max, replicate); +} + +extern "C" { +static void durstoreCallback(lcb_t, int, const lcb_RESPBASE *rb) +{ + const lcb_RESPSTOREDUR *resp = reinterpret_cast(rb); + lcb_RESPSTOREDUR *rout = reinterpret_cast(rb->cookie); + lcb_RESPENDURE *dur_resp = const_cast(rout->dur_resp); + + ASSERT_FALSE(resp->dur_resp == NULL); + + *rout = *resp; + *dur_resp = *resp->dur_resp; + rout->dur_resp = dur_resp; +} +} + +TEST_F(DurabilityUnitTest, testDurStore) +{ + HandleWrap hw; + lcb_t instance; + lcb_durability_opts_t options = { 0 }; + createConnection(hw, instance); + lcb_install_callback3(instance, LCB_CALLBACK_STOREDUR, durstoreCallback); + + std::string key("durStore"); + std::string value("value"); + + lcb_error_t rc; + lcb_RESPSTOREDUR resp = { 0 }; + lcb_RESPENDURE dur_resp = { 0 }; + + resp.dur_resp = &dur_resp; + + lcb_CMDSTOREDUR cmd = { 0 }; + LCB_CMD_SET_KEY(&cmd, key.c_str(), key.size()); + LCB_CMD_SET_VALUE(&cmd, value.c_str(), value.size()); + defaultOptions(instance, options); + cmd.operation = LCB_SET; + cmd.persist_to = options.v.v0.persist_to; + cmd.replicate_to = options.v.v0.replicate_to; + + lcb_sched_enter(instance); + resp.rc = LCB_ERROR; + rc = lcb_storedur3(instance, &resp, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_sched_leave(instance); + lcb_wait(instance); + + ASSERT_EQ(LCB_SUCCESS, resp.rc); + ASSERT_NE(0, resp.store_ok); + ASSERT_TRUE(options.v.v0.persist_to <= resp.dur_resp->npersisted); + ASSERT_TRUE(options.v.v0.replicate_to <= resp.dur_resp->nreplicated); + + lcb_sched_enter(instance); + // Try with bad criteria.. + cmd.persist_to = 100; + cmd.replicate_to = 100; + rc = lcb_storedur3(instance, &resp, &cmd); + ASSERT_EQ(LCB_DURABILITY_ETOOMANY, rc); + + // Try with no persist/replicate options + cmd.persist_to = 0; + cmd.replicate_to = 0; + rc = lcb_storedur3(instance, &resp, &cmd); + ASSERT_EQ(LCB_EINVAL, rc); + lcb_sched_fail(instance); + + // CAP_MAX should be applied here + cmd.persist_to = -1; + cmd.replicate_to = -1; + lcb_sched_enter(instance); + rc = lcb_storedur3(instance, &resp, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_sched_leave(instance); + lcb_wait(instance); + ASSERT_EQ(LCB_SUCCESS, resp.rc); + ASSERT_TRUE(options.v.v0.persist_to <= resp.dur_resp->npersisted); + ASSERT_TRUE(options.v.v0.replicate_to <= resp.dur_resp->nreplicated); + + // Use bad CAS. we should have a clear indicator that storage failed + cmd.cas = -1; + lcb_sched_enter(instance); + rc = lcb_storedur3(instance, &resp, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_sched_leave(instance); + lcb_wait(instance); + ASSERT_EQ(LCB_KEY_EEXISTS, resp.rc); + ASSERT_EQ(0, resp.store_ok); + + // Make storage succeed, but let durability fail. + // TODO: Add Mock-specific command to disable persistence/replication + lcb_U32 ustmo = 1; // 1 microsecond + rc = lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_DURABILITY_TIMEOUT, &ustmo); + ASSERT_EQ(LCB_SUCCESS, rc); + + // Reset CAS from previous command + cmd.cas = 0; + lcb_sched_enter(instance); + rc = lcb_storedur3(instance, &resp, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_sched_leave(instance); + lcb_wait(instance); + if (resp.rc == LCB_ETIMEDOUT) { + ASSERT_NE(0, resp.store_ok); + } else { + lcb_log(LOGARGS(instance, WARN), "Test skipped because mock is too fast(!)"); + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_forward.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_forward.cc new file mode 100644 index 00000000..56e150b9 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_forward.cc @@ -0,0 +1,110 @@ +#include "iotests.h" +#include +#include +#include "mc/pktmaker.h" +using std::vector; +using std::string; +using namespace PacketMaker; + +class ForwardTests : public MockUnitTest +{ +}; + +struct ForwardCookie { + vector orig; + vector respbuf; + vector iovs; + vector bkbuf; + lcb_error_t err_expected; + lcb_error_t err_received; + bool called; + bool flushed; + + ForwardCookie() { + err_expected = LCB_SUCCESS; + err_received = LCB_SUCCESS; + called = false; + flushed = false; + } +}; + +extern "C" { +static void pktfwd_callback(lcb_t, const void *cookie, lcb_error_t err, + lcb_PKTFWDRESP *resp) +{ + ForwardCookie *fc = (ForwardCookie *)cookie; + fc->called = true; + fc->err_received = err; + + if (err != LCB_SUCCESS) { + return; + } + + protocol_binary_response_header *hdr = + (protocol_binary_response_header*)resp->header; + ASSERT_EQ(PROTOCOL_BINARY_RES, hdr->response.magic); + lcb_U32 blen = ntohl(hdr->response.bodylen); + + // Gather the packets + for (unsigned ii = 0; ii < resp->nitems; ii++) { + lcb_backbuf_ref(resp->bufs[ii]); + + char *buf = (char *)resp->iovs[ii].iov_base; + size_t len = resp->iovs[ii].iov_len; + + fc->iovs.push_back(resp->iovs[ii]); + fc->bkbuf.push_back(resp->bufs[ii]); + fc->respbuf.insert(fc->respbuf.end(), buf, buf+len); + } + + ASSERT_EQ(blen+24, fc->respbuf.size()); +} + +static void pktflush_callback(lcb_t, const void *cookie) +{ + ForwardCookie *fc = (ForwardCookie *)cookie; + EXPECT_FALSE(fc->flushed); + fc->flushed = true; +} +} + +TEST_F(ForwardTests, testBasic) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + lcb_set_pktflushed_callback(instance, pktflush_callback); + lcb_set_pktfwd_callback(instance, pktfwd_callback); + + ForwardCookie fc; + StorageRequest req("Hello", "World"); + req.magic(PROTOCOL_BINARY_REQ); + req.op(PROTOCOL_BINARY_CMD_SET); + + lcb_CMDPKTFWD cmd = { 0 }; + req.serialize(fc.orig); + cmd.vb.vtype = LCB_KV_CONTIG; + cmd.vb.u_buf.contig.bytes = &fc.orig[0]; + cmd.vb.u_buf.contig.nbytes = fc.orig.size(); + lcb_error_t rc; + + lcb_sched_enter(instance); + rc = lcb_pktfwd3(instance, &fc, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_sched_leave(instance); + lcb_wait(instance); + ASSERT_TRUE(fc.called); + ASSERT_EQ(LCB_SUCCESS, fc.err_received); + for (unsigned ii = 0; ii < fc.bkbuf.size(); ++ii) { + lcb_backbuf_unref(fc.bkbuf[ii]); + } +} + +TEST_F(ForwardTests, testIncomplete) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + lcb_set_pktflushed_callback(instance, pktflush_callback); + lcb_set_pktfwd_callback(instance, pktfwd_callback); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_get.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_get.cc new file mode 100644 index 00000000..871574a3 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_get.cc @@ -0,0 +1,512 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include +#include +#include "iotests.h" + +class GetUnitTest : public MockUnitTest +{ +}; + +extern "C" { + static void testGetMissGetCallback(lcb_t, const void *cookie, + lcb_error_t error, + const lcb_get_resp_t *resp) + { + int *counter = (int *)cookie; + EXPECT_EQ(LCB_KEY_ENOENT, error); + ASSERT_NE((const lcb_get_resp_t *)NULL, resp); + EXPECT_EQ(0, resp->version); + std::string val((const char *)resp->v.v0.key, resp->v.v0.nkey); + EXPECT_TRUE(val == "testGetMiss1" || val == "testGetMiss2"); + ++(*counter); + } +} + +/** + * @test + * Get Miss + * + * @pre + * Request two non-existent keys + * + * @post + * Responses for both keys are received with error code + * @c KEY_ENOENT; response structure is not NULL, and the keys match their + * expected value + * + * @todo (maybe check the values too?) + */ +TEST_F(GetUnitTest, testGetMiss) +{ + HandleWrap hw; + lcb_t instance; + createConnection(hw, instance); + + (void)lcb_set_get_callback(instance, testGetMissGetCallback); + int numcallbacks = 0; + + removeKey(instance, "testGetMiss1"); + removeKey(instance, "testGetMiss2"); + + lcb_get_cmd_t cmd1("testGetMiss1"); + lcb_get_cmd_t cmd2("testGetMiss2"); + lcb_get_cmd_t *cmds[] = { &cmd1, &cmd2 }; + EXPECT_EQ(LCB_SUCCESS, lcb_get(instance, &numcallbacks, 2, cmds)); + + lcb_wait(instance); + EXPECT_EQ(2, numcallbacks); +} + +extern "C" { + static void testGetHitGetCallback(lcb_t, const void *cookie, + lcb_error_t error, + const lcb_get_resp_t *resp) + { + int *counter = (int *)cookie; + EXPECT_EQ(LCB_SUCCESS, error); + ASSERT_NE((const lcb_get_resp_t *)NULL, resp); + EXPECT_EQ(0, resp->version); + ++(*counter); + } +} + +/** + * @test + * Get Hit + * + * @pre + * Store two keys, and retrieve them + * + * @post + * Both keys exist, and their return code is successul + */ +TEST_F(GetUnitTest, testGetHit) +{ + HandleWrap hw; + lcb_t instance; + createConnection(hw, instance); + + (void)lcb_set_get_callback(instance, testGetHitGetCallback); + int numcallbacks = 0; + + storeKey(instance, "testGetKey1", "foo"); + storeKey(instance, "testGetKey2", "foo"); + lcb_get_cmd_t cmd1("testGetKey1"); + lcb_get_cmd_t cmd2("testGetKey2"); + lcb_get_cmd_t *cmds[] = { &cmd1, &cmd2 }; + EXPECT_EQ(LCB_SUCCESS, lcb_get(instance, &numcallbacks, 2, cmds)); + + lcb_wait(instance); + EXPECT_EQ(2, numcallbacks); +} + +extern "C" { + static void testTouchMissCallback(lcb_t, const void *cookie, + lcb_error_t error, + const lcb_touch_resp_t *resp) + { + int *counter = (int *)cookie; + EXPECT_EQ(LCB_KEY_ENOENT, error); + ASSERT_NE((const lcb_touch_resp_t *)NULL, resp); + EXPECT_EQ(0, resp->version); + ++(*counter); + } +} + +/** + * @test Touch (Miss) + * @pre Schedule a touch for a non existent key with an expiry @c 666 + * @post Touch fails with @c KEY_ENOENT + */ +TEST_F(GetUnitTest, testTouchMiss) +{ + std::string key("testTouchMissKey"); + HandleWrap hw; + lcb_t instance; + createConnection(hw, instance); + + (void)lcb_set_touch_callback(instance, testTouchMissCallback); + removeKey(instance, key); + + int numcallbacks = 0; + lcb_touch_cmd_t cmd(key.data(), key.length(), 666); + lcb_touch_cmd_t *cmds[] = { &cmd }; + EXPECT_EQ(LCB_SUCCESS, lcb_touch(instance, &numcallbacks, 1, cmds)); + lcb_wait(instance); + EXPECT_EQ(1, numcallbacks); +} + +extern "C" { + static void testTouchHitCallback(lcb_t, const void *cookie, + lcb_error_t error, + const lcb_touch_resp_t *resp) + { + int *counter = (int *)cookie; + EXPECT_EQ(LCB_SUCCESS, error); + ASSERT_NE((const lcb_touch_resp_t *)NULL, resp); + EXPECT_EQ(0, resp->version); + ++(*counter); + } +} + +/** + * @test Touch (Hit) + * @pre Store a key, and schedule a touch operation with an expiry of @c 666 + * @post Touch succeeds. + */ +TEST_F(GetUnitTest, testTouchHit) +{ + std::string key("testTouchHitKey"); + HandleWrap hw; + lcb_t instance; + createConnection(hw, instance); + + (void)lcb_set_touch_callback(instance, testTouchHitCallback); + storeKey(instance, key, "foo"); + + int numcallbacks = 0; + lcb_touch_cmd_t cmd(key.data(), key.length(), 666); + lcb_touch_cmd_t *cmds[] = { &cmd }; + EXPECT_EQ(LCB_SUCCESS, lcb_touch(instance, &numcallbacks, 1, cmds)); + lcb_wait(instance); + EXPECT_EQ(1, numcallbacks); +} + +extern "C" { + static void testMixedMultiGetCallback(lcb_t, const void *cookie, + lcb_error_t error, + const lcb_get_resp_t *resp) + { + using namespace std; + map *kmap = (map *)cookie; + + Item itm; + itm.assign(resp, error); + + (*kmap)[itm.key] = itm; + } +} + +/** + * Adopted from smoke-test.c:test_get2 + * + * @test Multi Get (Interleaved) + * + * @pre + * Create two maps of 26 key-value pairs, one called @c kexisting and one + * called @c kmissing. Store the @c kexisting map but not the @c kmissing one. + * + * Create a list of GET commands interleaving keys from @c kmissing and + * @c kexisting. Schedule the operation + * + * @post + * Returned result set has exactly 52 items. All the keys from @c kmissing + * have @c KEY_ENOENT from their result code, and all keys from @c kexisting + * contain the appropriate values. + */ +TEST_F(GetUnitTest, testMixedMultiGet) +{ + using namespace std; + HandleWrap hw; + lcb_t instance; + createConnection(hw, instance); + + + vector kexisting; + vector kmissing; + map kmap; + + vector cmds; + vector cmdptrs; + + int iterations = 4; + + for (int ii = 0; ii < iterations; ii++) { + char suffix = 'a' + ii; + string k("existingKey"); + k += suffix; + kexisting.push_back(k); + storeKey(instance, k, k); + + k = "nonExistKey"; + k += suffix; + removeKey(instance, k); + kmissing.push_back(k); + } + + for (int ii = 0; ii < iterations; ii++) { + lcb_get_cmd_t cmdhit(kexisting[ii].c_str(), kexisting[ii].size()); + lcb_get_cmd_t cmdmiss(kmissing[ii].c_str(), kmissing[ii].size()); + + cmds.push_back(cmdhit); + cmds.push_back(cmdmiss); + } + + for (unsigned int ii = 0; ii < cmds.size(); ii++) { + cmdptrs.push_back(&cmds[ii]); + } + + lcb_set_get_callback(instance, testMixedMultiGetCallback); + + EXPECT_EQ(LCB_SUCCESS, + lcb_get(instance, &kmap, cmds.size(), &cmdptrs[0])); + + lcb_wait(instance); + ASSERT_EQ(iterations * 2, kmap.size()); + + for (int ii = 0; ii < iterations; ii++) { + string k = kexisting[ii]; + ASSERT_EQ(1, kmap.count(k)); + Item itm = kmap[k]; + ASSERT_EQ(LCB_SUCCESS, itm.err); + ASSERT_EQ(k, itm.val); + + k = kmissing[ii]; + ASSERT_EQ(1, kmap.count(k)); + itm = kmap[k]; + ASSERT_EQ(LCB_KEY_ENOENT, itm.err); + } +} + +extern "C" { + static void flags_store_callback(lcb_t, + const void *, + lcb_storage_t operation, + lcb_error_t error, + const lcb_store_resp_t *resp) + { + ASSERT_EQ(LCB_SUCCESS, error); + ASSERT_EQ(5, resp->v.v0.nkey); + ASSERT_EQ(0, memcmp(resp->v.v0.key, "flags", 5)); + ASSERT_EQ(LCB_SET, operation); + } + + static void flags_get_callback(lcb_t, const void *, + lcb_error_t error, + const lcb_get_resp_t *resp) + { + ASSERT_EQ(LCB_SUCCESS, error); + ASSERT_EQ(5, resp->v.v0.nkey); + ASSERT_EQ(0, memcmp(resp->v.v0.key, "flags", 5)); + ASSERT_EQ(1, resp->v.v0.nbytes); + ASSERT_EQ(0, memcmp(resp->v.v0.bytes, "x", 1)); + ASSERT_EQ(0xdeadbeef, resp->v.v0.flags); + } +} + +TEST_F(GetUnitTest, testFlags) +{ + lcb_t instance; + HandleWrap hw; + + createConnection(hw, instance); + + (void)lcb_set_get_callback(instance, flags_get_callback); + (void)lcb_set_store_callback(instance, flags_store_callback); + + lcb_store_cmd_t storeCommand(LCB_SET, "flags", 5, "x", 1, 0xdeadbeef); + lcb_store_cmd_t *storeCommands[] = { &storeCommand }; + + ASSERT_EQ(LCB_SUCCESS, lcb_store(instance, NULL, 1, storeCommands)); + // Wait for it to be persisted + lcb_wait(instance); + + lcb_get_cmd_t cmd("flags", 5); + lcb_get_cmd_t *cmds[] = { &cmd }; + ASSERT_EQ(LCB_SUCCESS, lcb_get(instance, NULL, 1, cmds)); + + /* Wait for it to be received */ + lcb_wait(instance); +} + +struct RGetCookie { + unsigned remaining; + lcb_error_t expectrc; + std::string value; + lcb_U64 cas; +}; + +extern "C" { +static void +rget_callback(lcb_t instance, int, const lcb_RESPBASE *resp) +{ + const lcb_RESPGET *gresp = (const lcb_RESPGET *)resp; + RGetCookie *rck = (RGetCookie *)resp->cookie; + + ASSERT_EQ(rck->expectrc, resp->rc); + ASSERT_NE(0, rck->remaining); + rck->remaining--; + + if (resp->rc == LCB_SUCCESS) { + std::string value((const char*)gresp->value, gresp->nvalue); + ASSERT_STREQ(rck->value.c_str(), value.c_str()); + ASSERT_EQ(rck->cas, resp->cas); + } + +} +static void rget_noop_callback(lcb_t,int,const lcb_RESPBASE*){} +} + +TEST_F(GetUnitTest, testGetReplica) +{ + SKIP_UNLESS_MOCK(); + MockEnvironment *mock = MockEnvironment::getInstance(); + HandleWrap hw; + lcb_t instance; + createConnection(hw, instance); + std::string key("a_key_GETREPLICA"); + std::string val("a_value"); + + lcb_CMDGETREPLICA rcmd = { 0 }; + + lcb_install_callback3(instance, LCB_CALLBACK_GETREPLICA, rget_callback); + RGetCookie rck; + rck.remaining = 1; + rck.expectrc = LCB_SUCCESS; + LCB_CMD_SET_KEY(&rcmd, key.c_str(), key.size()); + unsigned nreplicas = lcb_get_num_replicas(instance); + + for (unsigned ii = 0; ii < nreplicas; ii++) { + MockMutationCommand mcCmd(MockCommand::CACHE, key); + mcCmd.cas = ii + 100; + rck.cas = mcCmd.cas; + mcCmd.replicaList.clear(); + mcCmd.replicaList.push_back(ii); + + mock->sendCommand(mcCmd); + mock->getResponse(); + + lcb_error_t err; + rcmd.index = ii; + rcmd.strategy = LCB_REPLICA_SELECT; + + rck.remaining = 1; + lcb_sched_enter(instance); + err = lcb_rget3(instance, &rck, &rcmd); + ASSERT_EQ(LCB_SUCCESS, err); + + lcb_sched_leave(instance); + lcb_wait(instance); + ASSERT_EQ(0, rck.remaining); + } + + // Test with the "All" mode + MockMutationCommand mcCmd(MockCommand::CACHE, key); + mcCmd.cas = 999; + mcCmd.onMaster = false; + mcCmd.replicaCount = nreplicas; + mock->sendCommand(mcCmd); + mock->getResponse(); + + rck.remaining = nreplicas; + rck.cas = mcCmd.cas; + rck.expectrc = LCB_SUCCESS; + rcmd.strategy = LCB_REPLICA_ALL; + + lcb_sched_enter(instance); + lcb_error_t err = lcb_rget3(instance, &rck, &rcmd); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_sched_leave(instance); + lcb_wait(instance); + ASSERT_EQ(0, rck.remaining); + + MockMutationCommand purgeCmd(MockCommand::PURGE, key); + purgeCmd.onMaster = true; + purgeCmd.replicaCount = nreplicas; + mock->sendCommand(purgeCmd); + mock->getResponse(); + + // Test with the "First" mode. Ensure that only the _last_ replica + // contains the item + mcCmd.onMaster = false; + mcCmd.replicaCount = 0 ; + mcCmd.replicaList.clear(); + mcCmd.replicaList.push_back(nreplicas-1); + mcCmd.cas = 42; + rck.cas = mcCmd.cas; + + + // Set the timeout to something higher, since we have more than one packet + // to send. + lcb_cntl_setu32(instance, LCB_CNTL_OP_TIMEOUT, 10000000); + + // The first replica should respond with ENOENT, the second should succeed + // though + mock->sendCommand(mcCmd); + mock->getResponse(); + rcmd.strategy = LCB_REPLICA_FIRST; + rck.remaining = 1; + lcb_sched_enter(instance); + err = lcb_rget3(instance, &rck, &rcmd); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_sched_leave(instance); + lcb_wait(instance); + ASSERT_EQ(0, rck.remaining); + + // Test with an invalid index + rcmd.index = nreplicas; + rcmd.strategy = LCB_REPLICA_SELECT; + err = lcb_rget3(instance, NULL, &rcmd); + ASSERT_EQ(LCB_NO_MATCHING_SERVER, err); + + // If no crash, it's good. + if (lcb_get_num_replicas(instance) > 1) { + // Use the 'first' mode, but make the second replica index be -1, so + // that in the retry we need to skip over an index. + + lcbvb_CONFIG *vbc; + err = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_VBCONFIG, (void *)&vbc); + int vbid = lcbvb_k2vb(vbc, key.c_str(), key.size()); + int oldix; + + lcbvb_VBUCKET* vb = &vbc->vbuckets[vbid]; + oldix = vb->servers[2]; + vb->servers[2] = -1; + + rck.expectrc = LCB_KEY_ENOENT; + rck.remaining = 1; + lcb_sched_enter(instance); + rcmd.strategy = LCB_REPLICA_FIRST; + err = lcb_rget3(instance, &rck, &rcmd); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_sched_leave(instance); + lcb_wait(instance); + ASSERT_EQ(0, rck.remaining); + + // Try with ALL again (should give an error) + rcmd.strategy = LCB_REPLICA_ALL; + lcb_sched_enter(instance); + err = lcb_rget3(instance, NULL, &rcmd); + ASSERT_EQ(LCB_NO_MATCHING_SERVER, err); + lcb_sched_leave(instance); + + vb->servers[2] = oldix; + } else { + printf("Not enough replicas for get-with-replica test\n"); + } + + // Test rget with a missing key. Fixes a potential bug + lcb_install_callback3(instance, LCB_CALLBACK_GETREPLICA, rget_noop_callback); + removeKey(instance, key); + rcmd.strategy = LCB_REPLICA_FIRST; + lcb_sched_enter(instance); + err = lcb_rget3(instance, NULL, &rcmd); + lcb_sched_leave(instance); + lcb_wait(instance); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_http.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_http.cc new file mode 100644 index 00000000..eeb0525e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_http.cc @@ -0,0 +1,438 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +#include "config.h" +#include "iotests.h" +#include + +#define DESIGN_DOC_NAME "lcb_design_doc" +#define VIEW_NAME "lcb-test-view" + +class HttpUnitTest : public MockUnitTest +{ +}; + +class HttpCmdContext +{ +public: + HttpCmdContext() : + received(false), dumpIfEmpty(false), dumpIfError(false), cbCount(0) + { } + + bool received; + bool dumpIfEmpty; + bool dumpIfError; + unsigned cbCount; + + lcb_http_status_t status; + lcb_error_t err; + std::string body; +}; + +static const char *view_common = + "{ " + " \"id\" : \"_design/" DESIGN_DOC_NAME "\"," + " \"language\" : \"javascript\"," + " \"views\" : { " + " \"" VIEW_NAME "\" : {" + "\"map\":" + " \"function(doc) { " + "if (doc.testid == 'lcb') { emit(doc.id) } " + " } \" " + " } " + "}" + "}"; + + +static void dumpResponse(const lcb_http_resp_t *resp) +{ + if (resp->v.v0.headers) { + const char *const *hdr; + for (hdr = resp->v.v0.headers; *hdr; hdr++) { + std::cout << "Header: " << *hdr << std::endl; + } + } + if (resp->v.v0.bytes) { + std::cout << "Data: " << std::endl; + std::cout.write((const char *)resp->v.v0.bytes, resp->v.v0.nbytes); + std::cout << std::endl; + } + + std::cout << "Path: " << std::endl; + std::cout.write(resp->v.v0.path, resp->v.v0.npath); + std::cout << std::endl; + +} + +extern "C" { + + static void httpSimpleCallback(lcb_http_request_t request, + lcb_t instance, + const void *cookie, + lcb_error_t error, + const lcb_http_resp_t *resp) + { + HttpCmdContext *htctx; + htctx = reinterpret_cast((void *)cookie); + htctx->err = error; + htctx->status = resp->v.v0.status; + htctx->received = true; + htctx->cbCount++; + + if (resp->v.v0.bytes) { + htctx->body.assign((const char *)resp->v.v0.bytes, resp->v.v0.nbytes); + } + + if ((resp->v.v0.nbytes == 0 && htctx->dumpIfEmpty) || + (error != LCB_SUCCESS && htctx->dumpIfError)) { + std::cout << "Count: " << htctx->cbCount << std::endl + << "Code: " << error << std::endl + << "nBytes: " << resp->v.v0.nbytes << std::endl; + dumpResponse(resp); + } + } +} + +/** + * @test HTTP (Put) + * + * @pre Create a valid view document and store it on the server + * @post Store succeeds and the HTTP result code is 201 + */ +TEST_F(HttpUnitTest, testPut) +{ + SKIP_IF_MOCK(); + HandleWrap hw; + lcb_t instance; + createConnection(hw, instance); + + const char *design_doc_path = "/_design/" DESIGN_DOC_NAME; + lcb_http_cmd_st cmd; + cmd = lcb_http_cmd_st(design_doc_path, strlen(design_doc_path), + view_common, strlen(view_common), + LCB_HTTP_METHOD_PUT, 0, + "application/json"); + + lcb_error_t err; + lcb_set_http_complete_callback(instance, httpSimpleCallback); + + lcb_http_request_t htreq; + HttpCmdContext ctx; + ctx.dumpIfError = true; + + err = lcb_make_http_request(instance, &ctx, LCB_HTTP_TYPE_VIEW, + &cmd, &htreq); + + ASSERT_EQ(LCB_SUCCESS, err); + lcb_wait(instance); + + ASSERT_EQ(true, ctx.received); + ASSERT_EQ(LCB_SUCCESS, ctx.err); + ASSERT_EQ(LCB_HTTP_STATUS_CREATED, ctx.status); + ASSERT_EQ(1, ctx.cbCount); + +} + +/** + * @test HTTP (Get) + * @pre Query a value view + * @post HTTP Result is @c 200, and the view contents look like valid JSON + * (i.e. the first non-whitespace char is a @c { and the last non-whitespace + * char is a @c } + */ +TEST_F(HttpUnitTest, testGet) +{ + SKIP_IF_MOCK(); + + HandleWrap hw; + lcb_t instance; + createConnection(hw, instance); + + const char *path = "_design/" DESIGN_DOC_NAME "/_view/" VIEW_NAME; + lcb_http_cmd_st cmd = lcb_http_cmd_st(path, strlen(path), NULL, 0, + LCB_HTTP_METHOD_GET, 0, + "application/json"); + + HttpCmdContext ctx; + ctx.dumpIfEmpty = true; + ctx.dumpIfError = true; + + lcb_set_http_complete_callback(instance, httpSimpleCallback); + lcb_error_t err; + lcb_http_request_t htreq; + + err = lcb_make_http_request(instance, &ctx, LCB_HTTP_TYPE_VIEW, + &cmd, &htreq); + + ASSERT_EQ(LCB_SUCCESS, err); + lcb_wait(instance); + + ASSERT_EQ(true, ctx.received); + ASSERT_EQ(LCB_HTTP_STATUS_OK, ctx.status); + ASSERT_GT(ctx.body.size(), 0U); + ASSERT_EQ(ctx.cbCount, 1); + + unsigned ii; + const char *pcur; + + for (ii = 0, pcur = ctx.body.c_str(); + ii < ctx.body.size() && isspace(*pcur); ii++, pcur++) { + /* no body */ + } + + /** + * This is a view request. If all is in order, the content should be a + * JSON object, first non-ws char is "{" and last non-ws char is "}" + */ + ASSERT_NE(ctx.body.size(), ii); + ASSERT_EQ(*pcur, '{'); + + for (pcur = ctx.body.c_str() + ctx.body.size() - 1; + ii >= 0 && isspace(*pcur); ii--, pcur--) { + /* no body */ + } + ASSERT_GE(ii, 0U); + ASSERT_EQ('}', *pcur); + +} + +/** + * @test HTTP (Connection Refused) + * @bug CCBC-132 + * @pre Create a request of type RAW to @c localhost:1 - nothing should be + * listening there + * @post Command returns. Status code is one of CONNECT_ERROR or NETWORK_ERROR + */ +TEST_F(HttpUnitTest, testRefused) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + const char *path = "non-exist-path"; + lcb_http_cmd_st cmd = lcb_http_cmd_st(); + + cmd.version = 1; + cmd.v.v1.host = "localhost:1"; // should not have anything listening on it + cmd.v.v1.path = "non-exist"; + cmd.v.v1.npath = strlen(cmd.v.v1.path); + cmd.v.v1.method = LCB_HTTP_METHOD_GET; + + + HttpCmdContext ctx; + ctx.dumpIfEmpty = false; + ctx.dumpIfError = false; + + lcb_set_http_complete_callback(instance, httpSimpleCallback); + lcb_error_t err; + lcb_http_request_t htreq; + + err = lcb_make_http_request(instance, &ctx, LCB_HTTP_TYPE_RAW, + &cmd, &htreq); + + ASSERT_EQ(LCB_SUCCESS, err); + lcb_wait(instance); + ASSERT_EQ(true, ctx.received); + ASSERT_NE(0, LCB_EIFNET(ctx.err)); + +} + +struct HtResult { + std::string body; + std::map headers; + + bool gotComplete; + bool gotChunked; + lcb_RESPHTTP res; + void reset() { + body.clear(); + gotComplete = false; + gotChunked = false; + memset(&res, 0, sizeof res); + } +}; + +extern "C" { +static void http_callback(lcb_t, int, const lcb_RESPBASE *rb) +{ + const lcb_RESPHTTP *htr = (const lcb_RESPHTTP *)rb; + HtResult *me = (HtResult *)htr->cookie; + + if (htr->nbody) { + me->body.append((const char*)htr->body, (const char*)htr->body + htr->nbody); + } + + if (htr->rflags & LCB_RESP_F_FINAL) { + me->res = *htr; + me->gotComplete = true; + const char * const * cur = htr->headers; + for (; *cur; cur+=2) { + me->headers[cur[0]] = cur[1]; + } + } else { + me->gotChunked = true; + } +} +} + +static void +makeAdminReq(lcb_CMDHTTP& cmd, std::string& bkbuf) +{ + memset(&cmd, 0, sizeof cmd); + bkbuf.assign("/pools/default/buckets/default"); + + cmd.type = LCB_HTTP_TYPE_MANAGEMENT; + cmd.method = LCB_HTTP_METHOD_GET; + LCB_CMD_SET_KEY(&cmd, bkbuf.c_str(), bkbuf.size()); +} + +// Some more basic HTTP tests for the administrative API. We use the admin +// API since it's always available. +TEST_F(HttpUnitTest, testAdminApi) +{ + lcb_t instance; + HandleWrap hw; + std::string pth; + createConnection(hw, instance); + lcb_install_callback3(instance, LCB_CALLBACK_HTTP, http_callback); + + // Make the request; this time we make it to the 'management' API + lcb_CMDHTTP cmd = { 0 }; + + makeAdminReq(cmd, pth); + HtResult htr; + htr.reset(); + + lcb_error_t err; + lcb_sched_enter(instance); + err = lcb_http3(instance, &htr, &cmd); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_sched_leave(instance); + lcb_wait(instance); + + ASSERT_TRUE(htr.gotComplete); + ASSERT_EQ(LCB_SUCCESS, htr.res.rc); + ASSERT_EQ(200, htr.res.htstatus); + ASSERT_FALSE(htr.body.empty()); + + // Try with a chunked request + htr.reset(); + cmd.cmdflags |= LCB_CMDHTTP_F_STREAM; + lcb_sched_enter(instance); + err = lcb_http3(instance, &htr, &cmd); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_sched_leave(instance); + lcb_wait(instance); + + ASSERT_TRUE(htr.gotComplete); + ASSERT_TRUE(htr.gotChunked); + + // try another one, but this time cancelling it.. + lcb_http_request_t reqh; + cmd.reqhandle = &reqh; + lcb_sched_enter(instance); + err = lcb_http3(instance, NULL, &cmd); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_FALSE(reqh == NULL); + lcb_sched_leave(instance); + lcb_cancel_http_request(instance, reqh); + + // Try another one, allocating a request body. Unfortunately, we need + // to cancel this one too, as none of the mock's endpoints support a + // request body + cmd.reqhandle = &reqh; + cmd.body = "FOO"; + cmd.nbody = 3; + cmd.method = LCB_HTTP_METHOD_PUT; + err = lcb_http3(instance, NULL, &cmd); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_FALSE(reqh == NULL); + lcb_sched_leave(instance); + lcb_cancel_http_request(instance, reqh); +} + + +extern "C" { +static void doubleCancel_callback(lcb_t instance, int, const lcb_RESPBASE *rb) +{ + const lcb_RESPHTTP *resp = (const lcb_RESPHTTP *)rb; + if (resp->rflags & LCB_RESP_F_FINAL) { + lcb_cancel_http_request(instance, resp->_htreq); + lcb_cancel_http_request(instance, resp->_htreq); + } +} +} + +TEST_F(HttpUnitTest, testDoubleCancel) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + lcb_install_callback3(instance, LCB_CALLBACK_HTTP, doubleCancel_callback); + + // Make the request; this time we make it to the 'management' API + lcb_CMDHTTP cmd = { 0 }; + std::string bk; + makeAdminReq(cmd, bk); + lcb_sched_enter(instance); + ASSERT_EQ(LCB_SUCCESS, lcb_http3(instance, NULL, &cmd)); + lcb_sched_leave(instance); + lcb_wait(instance); + // No crashes or errors here means we've done OK +} + + +extern "C" { +static void cancelVerify_callback(lcb_t instance, int, const lcb_RESPBASE *rb) +{ + const lcb_RESPHTTP *resp = (const lcb_RESPHTTP *)rb; + bool *bCancelled = (bool *)resp->cookie; + + ASSERT_EQ(0, resp->rflags & LCB_RESP_F_FINAL); + ASSERT_FALSE(*bCancelled); + + lcb_cancel_http_request(instance, resp->_htreq); + *bCancelled = true; +} +} +// Ensure cancel actually does what it claims to do +TEST_F(HttpUnitTest, testCancelWorks) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + lcb_install_callback3(instance, LCB_CALLBACK_HTTP, cancelVerify_callback); + lcb_CMDHTTP cmd; + std::string ss; + makeAdminReq(cmd, ss); + // Make it chunked + cmd.cmdflags |= LCB_CMDHTTP_F_STREAM; + bool cookie = false; + lcb_sched_enter(instance); + ASSERT_EQ(LCB_SUCCESS, lcb_http3(instance, &cookie, &cmd)); + lcb_sched_leave(instance); + lcb_wait(instance); +} + +extern "C" { +static void noInvoke_callback(lcb_t, int, const lcb_RESPBASE*) +{ + EXPECT_FALSE(true) << "This callback should not be invoked!"; +} +} +TEST_F(HttpUnitTest, testDestroyWithActiveRequest) +{ + lcb_t instance; + // Note the one-arg form of createConnection which doesn't come with the + // magical HandleWrap; this is because we destroy our instance explicitly + // here. + createConnection(instance); + + lcb_CMDHTTP cmd; + std::string ss; + makeAdminReq(cmd, ss); + + lcb_install_callback3(instance,LCB_CALLBACK_HTTP, noInvoke_callback); + lcb_sched_enter(instance); + ASSERT_EQ(LCB_SUCCESS, lcb_http3(instance, NULL, &cmd)); + lcb_sched_leave(instance); + lcb_destroy(instance); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_iops.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_iops.cc new file mode 100644 index 00000000..bd3c11ad --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_iops.cc @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include +#include +#include +#include + +typedef void (*TimerCallback)(lcb_socket_t, short, void *); + +class IOPS : public ::testing::Test +{ +public: + virtual void SetUp() { + lcb_error_t err = lcb_create_io_ops(&io, NULL); + ASSERT_EQ(err, LCB_SUCCESS); + iot = lcbio_table_new(io); + } + + virtual void TearDown() { + lcbio_table_unref(iot); + if (io) { + lcb_destroy_io_ops(io); + io = NULL; + } + } + + void *createTimer() { + void *ret = iot->timer.create(IOT_ARG(iot)); + EXPECT_TRUE(ret != NULL); + return ret; + } + + void cancelTimer(void *timer) { + iot->timer.cancel(IOT_ARG(iot), timer); + } + + void scheduleTimer(void *timer, + TimerCallback cb, + lcb_uint32_t us, + void *arg) { + + iot->timer.schedule(IOT_ARG(iot), timer, us, arg, cb); + } + + void freeTimer(void *timer) { + iot->timer.destroy(IOT_ARG(iot), timer); + } + + void startLoop() { + IOT_START(iot); + } + + void stopLoop() { + IOT_STOP(iot); + } + +protected: + lcb_io_opt_t io; + lcbio_pTABLE iot; +}; + + +class Continuation +{ +public: + virtual void nextAction() = 0; + IOPS *parent; + +}; + + + +extern "C" { + static void timer_callback(lcb_socket_t, short, void *arg) + { + reinterpret_cast(arg)->nextAction(); + } +} + +class TimerCountdown : public Continuation +{ +public: + int counter; + void *timer; + + TimerCountdown(IOPS *self) { + parent = self; + counter = 1; + timer = parent->createTimer(); + } + + virtual void nextAction() { + EXPECT_TRUE(counter > 0); + parent->cancelTimer(timer); + counter--; + } + + virtual ~TimerCountdown() { + parent->cancelTimer(timer); + parent->freeTimer(timer); + } + + void reset() { + parent->cancelTimer(timer); + parent->freeTimer(timer); + timer = parent->createTimer(); + counter = 1; + } + + +private: + TimerCountdown(const TimerCountdown &); +}; + +TEST_F(IOPS, Timers) +{ + TimerCountdown cont(this); + scheduleTimer(cont.timer, timer_callback, 0, &cont); + startLoop(); + ASSERT_EQ(0, cont.counter); + + std::vector multi; + + for (int ii = 0; ii < 10; ii++) { + TimerCountdown *cur = new TimerCountdown(this); + multi.push_back(cur); + scheduleTimer(cur->timer, timer_callback, ii, cur); + } + + startLoop(); + for (unsigned int ii = 0; ii < multi.size(); ii++) { + TimerCountdown *cur = multi[ii]; + ASSERT_EQ(0, cur->counter); + delete cur; + } + + // Try it again.. + cont.reset(); + multi.clear(); + for (int ii = 0; ii < 10; ii++) { + TimerCountdown *cur = new TimerCountdown(this); + scheduleTimer(cur->timer, timer_callback, 10000000, cur); + multi.push_back(cur); + } + + scheduleTimer(cont.timer, timer_callback, 0, &cont); + + for (unsigned int ii = 0; ii < multi.size(); ii++) { + TimerCountdown *cur = multi[ii]; + cancelTimer(cur->timer); + cur->counter = 0; + } + + startLoop(); + for (unsigned int ii = 0; ii < multi.size(); ii++) { + delete multi[ii]; + } + +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_lock.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_lock.cc new file mode 100644 index 00000000..60b6a57f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_lock.cc @@ -0,0 +1,275 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include +#include "iotests.h" + +class LockUnitTest : public MockUnitTest +{ +}; + +extern "C" { + static void getLockedCallback(lcb_t, const void *cookie, + lcb_error_t err, + const lcb_get_resp_t *resp) + { + Item *itm = (Item *)cookie; + itm->assign(resp, err); + } + static void unlockCallback(lcb_t, const void *cookie, + lcb_error_t err, + const lcb_unlock_resp_t *resp) + { + *(lcb_error_t *)cookie = err; + } +} + +/** + * @test + * Lock (lock and unlock) + * + * @pre + * Set a key, and get the value specifying the lock option with a timeout + * of @c 10. + * + * @post + * Lock operation succeeds. + * + * @pre Unlock the key using the CAS from the previous get result. + * @post Unlock succeeds + */ +TEST_F(LockUnitTest, testSimpleLockAndUnlock) +{ + LCB_TEST_REQUIRE_FEATURE("lock") + + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + std::string key = "lockKey"; + std::string value = "lockValue"; + + removeKey(instance, key); + storeKey(instance, key, value); + + lcb_get_cmd_st cmd = lcb_get_cmd_st(key.c_str(), key.size(), + 1, 10); + lcb_get_cmd_st *cmdlist = &cmd; + Item itm; + lcb_error_t err; + + lcb_set_get_callback(instance, getLockedCallback); + + err = lcb_get(instance, &itm, 1, &cmdlist); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_wait(instance); + ASSERT_EQ(LCB_SUCCESS, itm.err); + + lcb_unlock_cmd_st ucmd(key.c_str(), key.size(), itm.cas); + lcb_unlock_cmd_st *ucmdlist = &ucmd; + + lcb_error_t reserr = LCB_ERROR; + + lcb_set_unlock_callback(instance, unlockCallback); + err = lcb_unlock(instance, &reserr, 1, &ucmdlist); + ASSERT_EQ(LCB_SUCCESS, err); + + lcb_wait(instance); + + ASSERT_EQ(LCB_SUCCESS, reserr); + +} + +/** + * @test Lock (Missing CAS) + * + * @pre + * Store a key and attempt to unlock it with an invalid CAS + * + * @post + * Error result of @c ETMPFAIL + */ +TEST_F(LockUnitTest, testUnlockMissingCas) +{ + LCB_TEST_REQUIRE_FEATURE("lock") + + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + lcb_error_t err, reserr = LCB_ERROR; + + storeKey(instance, "lockKey", "lockValue"); + + lcb_unlock_cmd_t cmd("lockKey", sizeof("lockKey") - 1, 0); + lcb_unlock_cmd_t *cmdlist = &cmd; + lcb_set_unlock_callback(instance, unlockCallback); + + err = lcb_unlock(instance, &reserr, 1, &cmdlist); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_wait(instance); + + /** + * maybe I mis-understood lock, but isn't it an error to use unlock + * without a valid CAS? - + */ + ASSERT_EQ(LCB_ETMPFAIL, reserr); +} + +extern "C" { + static void lockedStorageCallback(lcb_t, + const void *cookie, + lcb_storage_t operation, + lcb_error_t err, + const lcb_store_resp_t *resp) + { + Item *itm = (Item *)cookie; + itm->assignKC(resp, err); + } +} +/** + * @test Lock (Storage Contention) + * + * @pre + * Store a key, perform a GET operation with the lock option, specifying a + * timeout of @c 10. + * + * Then attempt to store the key (without specifying any CAS). + * + * @post Store operation fails with @c KEY_EEXISTS. Getting the key retains + * the old value. + * + * @pre store the key using the CAS specified from the first GET + * @post Storage succeeds. Get returns new value. + */ +TEST_F(LockUnitTest, testStorageLockContention) +{ + LCB_TEST_REQUIRE_FEATURE("lock") + + lcb_t instance; + HandleWrap hw; + lcb_error_t err; + + createConnection(hw, instance); + Item itm; + std::string key = "lockedKey", value = "lockedValue", + newvalue = "newUnlockedValue"; + + /* undo any funny business on our key */ + removeKey(instance, key); + storeKey(instance, key, value); + + lcb_set_get_callback(instance, getLockedCallback); + lcb_set_unlock_callback(instance, unlockCallback); + lcb_set_store_callback(instance, lockedStorageCallback); + + /* get the key and lock it */ + lcb_get_cmd_st gcmd(key.c_str(), key.size(), 1, 10); + lcb_get_cmd_st *cmdlist = &gcmd; + err = lcb_get(instance, &itm, 1, &cmdlist); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_wait(instance); + ASSERT_EQ(LCB_SUCCESS, itm.err); + ASSERT_GT(itm.cas, 0); + + /* now try to set the key, while the lock is still in place */ + lcb_store_cmd_t scmd(LCB_SET, key.c_str(), key.size(), + newvalue.c_str(), newvalue.size()); + lcb_store_cmd_t *scmdlist = &scmd; + Item s_itm; + err = lcb_store(instance, &s_itm, 1, &scmdlist); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_wait(instance); + ASSERT_EQ(LCB_KEY_EEXISTS, s_itm.err); + + /* verify the value is still the old value */ + Item ritem; + getKey(instance, key, ritem); + ASSERT_EQ(ritem.val, value); + + /* now try to set it with the correct cas, implicitly unlocking the key */ + scmd.v.v0.cas = itm.cas; + err = lcb_store(instance, &s_itm, 1, &scmdlist); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_wait(instance); + ASSERT_EQ(LCB_SUCCESS, itm.err); + + /* verify the value is now the new value */ + getKey(instance, key, ritem); + ASSERT_EQ(ritem.val, newvalue); +} + +/** + * @test + * Lock (Unlocking) + * + * @pre + * Store a key, get it with the lock option, specifying an expiry of @c 10. + * Try to unlock the key (using the @c lcb_unlock function) without a valid + * CAS. + * + * @post Unlock fails with @c ETMPFAIL + * + * @pre + * Unlock the key using the valid cas retrieved from the first lock operation. + * Then try to store the key with a new value. + * + * @post Unlock succeeds and retrieval of key yields new value. + */ +TEST_F(LockUnitTest, testUnlLockContention) +{ + LCB_TEST_REQUIRE_FEATURE("lock") + + lcb_t instance; + HandleWrap hw; + lcb_error_t err, reserr = LCB_ERROR; + createConnection(hw, instance); + + std::string key = "lockedKey2", value = "lockedValue2"; + storeKey(instance, key, value); + Item gitm; + + lcb_set_get_callback(instance, getLockedCallback); + lcb_set_unlock_callback(instance, unlockCallback); + lcb_set_store_callback(instance, lockedStorageCallback); + + lcb_get_cmd_t gcmd(key.c_str(), key.size(), 1, 10); + lcb_get_cmd_t *gcmdlist = &gcmd; + + err = lcb_get(instance, &gitm, 1, &gcmdlist); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_wait(instance); + ASSERT_EQ(LCB_SUCCESS, gitm.err); + + lcb_cas_t validCas = gitm.cas; + err = lcb_get(instance, &gitm, 1, &gcmdlist); + lcb_wait(instance); + ASSERT_EQ(LCB_ETMPFAIL, gitm.err); + + lcb_unlock_cmd_t ucmd(key.c_str(), key.size(), validCas); + lcb_unlock_cmd_t *ucmdlist = &ucmd; + err = lcb_unlock(instance, &reserr, 1, &ucmdlist); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_wait(instance); + ASSERT_EQ(reserr, LCB_SUCCESS); + + std::string newval = "lockedValueNew2"; + storeKey(instance, key, newval); + getKey(instance, key, gitm); + ASSERT_EQ(gitm.val, newval); + +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_misc.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_misc.cc new file mode 100644 index 00000000..3f05a069 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_misc.cc @@ -0,0 +1,733 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include "iotests.h" +#include +#include +#include +#include "internal.h" /* vbucket_* things from lcb_t */ +#include "auth-priv.h" +#include +#include "bucketconfig/bc_http.h" + +#define LOGARGS(instance, lvl) \ + instance->settings, "tests-MUT", LCB_LOG_##lvl, __FILE__, __LINE__ + + +extern "C" { +static void timings_callback(lcb_t, const void *cookie, lcb_timeunit_t, + lcb_U32, lcb_U32, lcb_U32, lcb_U32) +{ + bool *bPtr = (bool *)cookie; + *bPtr = true; +} +} + +TEST_F(MockUnitTest, testTimings) +{ + lcb_t instance; + HandleWrap hw; + bool called = false; + createConnection(hw, instance); + + lcb_enable_timings(instance); + + lcb_store_cmd_t storecmd(LCB_SET, "counter", 7, "0", 1); + lcb_store_cmd_t *storecmds[] = { &storecmd }; + + lcb_store(instance, NULL, 1, storecmds); + lcb_wait(instance); + lcb_get_timings(instance, &called, timings_callback); + lcb_disable_timings(instance); + ASSERT_TRUE(called); +} + + +namespace { +struct TimingInfo { + lcb_U64 ns_start; + lcb_U64 ns_end; + size_t count; + + TimingInfo() : ns_start(-1), ns_end(-1), count(-1) {} + + bool operator<(const TimingInfo& other) const { + return other.ns_start > ns_start; + } + bool operator>(const TimingInfo& other) const { + return ns_start > other.ns_start; + } + + bool valid() const { + return count != -1; + } +}; + +static lcb_U64 intervalToNsec(lcb_U64 interval, lcb_timeunit_t unit) +{ + if (unit == LCB_TIMEUNIT_NSEC) { + return interval; + } else if (unit == LCB_TIMEUNIT_USEC) { + return interval * 1000; + } else if (unit == LCB_TIMEUNIT_MSEC) { + return interval * 1000000; + } else if (unit == LCB_TIMEUNIT_SEC) { + return interval * 1000000000; + } else { + return -1; + } +} + +struct LcbTimings { + LcbTimings() {} + std::vector m_info; + void load(lcb_t); + void clear(); + + TimingInfo infoAt(hrtime_t duration, lcb_timeunit_t unit = LCB_TIMEUNIT_NSEC); + size_t countAt(hrtime_t duration, lcb_timeunit_t unit = LCB_TIMEUNIT_NSEC) { + return infoAt(duration, unit).count; + } + + void dump() const; +}; + +extern "C" { +static void load_timings_callback(lcb_t, const void *cookie, lcb_timeunit_t unit, + lcb_U32 min, lcb_U32 max, lcb_U32 total, lcb_U32 maxtotal) +{ + lcb_U64 start = intervalToNsec(min, unit); + lcb_U64 end = intervalToNsec(max, unit); + LcbTimings *timings = (LcbTimings *)cookie; + TimingInfo info; + + info.ns_start = start; + info.ns_end = end; + info.count = total; + timings->m_info.push_back(info); +} +} // extern "C" + +void +LcbTimings::load(lcb_t instance) +{ + lcb_get_timings(instance, this, load_timings_callback); + std::sort(m_info.begin(), m_info.end()); +} + +TimingInfo +LcbTimings::infoAt(hrtime_t duration, lcb_timeunit_t unit) +{ + duration = intervalToNsec(duration, unit); + std::vector::iterator ii; + for (ii = m_info.begin(); ii != m_info.end(); ++ii) { + if (ii->ns_start <= duration && ii->ns_end > duration) { + return *ii; + } + } + return TimingInfo(); +} + +void +LcbTimings::dump() const +{ + std::vector::const_iterator ii = m_info.begin(); + for (; ii != m_info.end(); ii++) { + if (ii->ns_end < 1000) { + printf("[%llu-%llu ns] %lu\n", + ii->ns_start, ii->ns_end, ii->count); + } else if (ii->ns_end < 10000000) { + printf("[%llu-%llu us] %lu\n", + ii->ns_start / 1000, ii->ns_end / 1000, ii->count); + } else { + printf("[%llu-%llu ms] %lu\n", + ii->ns_start / 1000000, ii->ns_end / 1000000, ii->count); + } + } +} + +} // namespace{ + +struct UnitInterval { + lcb_U64 n; + lcb_timeunit_t unit; + UnitInterval(lcb_U64 n, lcb_timeunit_t unit) : n(n), unit(unit) {} +}; + +static void addTiming(lcb_t instance, const UnitInterval& interval) +{ + hrtime_t n = intervalToNsec(interval.n, interval.unit); + lcb_histogram_record(instance->kv_timings, n); +} + + +TEST_F(MockUnitTest, testTimingsEx) +{ + lcb_t instance; + HandleWrap hw; + + createConnection(hw, instance); + lcb_disable_timings(instance); + lcb_enable_timings(instance); + + std::vector intervals; + intervals.push_back(UnitInterval(1, LCB_TIMEUNIT_NSEC)); + intervals.push_back(UnitInterval(250, LCB_TIMEUNIT_NSEC)); + intervals.push_back(UnitInterval(4, LCB_TIMEUNIT_USEC)); + intervals.push_back(UnitInterval(32, LCB_TIMEUNIT_USEC)); + intervals.push_back(UnitInterval(942, LCB_TIMEUNIT_USEC)); + intervals.push_back(UnitInterval(1243, LCB_TIMEUNIT_USEC)); + intervals.push_back(UnitInterval(1732, LCB_TIMEUNIT_USEC)); + intervals.push_back(UnitInterval(5630, LCB_TIMEUNIT_USEC)); + intervals.push_back(UnitInterval(42, LCB_TIMEUNIT_MSEC)); + intervals.push_back(UnitInterval(434, LCB_TIMEUNIT_MSEC)); + + intervals.push_back(UnitInterval(8234, LCB_TIMEUNIT_MSEC)); + intervals.push_back(UnitInterval(1294, LCB_TIMEUNIT_MSEC)); + intervals.push_back(UnitInterval(48, LCB_TIMEUNIT_SEC)); + + for (size_t ii = 0; ii < intervals.size(); ++ii) { + addTiming(instance, intervals[ii]); + } + + // Ensure they all exist, at least. Currently we bundle everything + LcbTimings timings; + timings.load(instance); + + //timings.dump(); + + // Measuring in < us + ASSERT_EQ(2, timings.countAt(50, LCB_TIMEUNIT_NSEC)); + + ASSERT_EQ(1, timings.countAt(4, LCB_TIMEUNIT_USEC)); + ASSERT_EQ(1, timings.countAt(30, LCB_TIMEUNIT_USEC)); + ASSERT_EQ(-1, timings.countAt(900, LCB_TIMEUNIT_USEC)); + ASSERT_EQ(1, timings.countAt(940, LCB_TIMEUNIT_USEC)); + ASSERT_EQ(1, timings.countAt(1200, LCB_TIMEUNIT_USEC)); + ASSERT_EQ(1, timings.countAt(1250, LCB_TIMEUNIT_USEC)); + ASSERT_EQ(1, timings.countAt(5600, LCB_TIMEUNIT_USEC)); + ASSERT_EQ(1, timings.countAt(40, LCB_TIMEUNIT_MSEC)); + ASSERT_EQ(1, timings.countAt(430, LCB_TIMEUNIT_MSEC)); + ASSERT_EQ(1, timings.countAt(1, LCB_TIMEUNIT_SEC)); + ASSERT_EQ(1, timings.countAt(8, LCB_TIMEUNIT_SEC)); + ASSERT_EQ(1, timings.countAt(93, LCB_TIMEUNIT_SEC)); +} + + +struct async_ctx { + int count; + lcbio_pTABLE table; +}; + +extern "C" { +static void dtor_callback(const void *cookie) +{ + async_ctx *ctx = (async_ctx *)cookie; + ctx->count++; + IOT_STOP(ctx->table); +} +} + +TEST_F(MockUnitTest, testAsyncDestroy) +{ + lcb_t instance; + createConnection(instance); + lcbio_pTABLE iot = instance->iotable; + lcb_settings *settings = instance->settings; + + storeKey(instance, "foo", "bar"); + // Now destroy the instance + async_ctx ctx; + ctx.count = 0; + ctx.table = iot; + lcb_set_destroy_callback(instance, dtor_callback); + lcb_destroy_async(instance, &ctx); + lcb_settings_ref(settings); + lcbio_table_ref(iot); + lcb_run_loop(instance); + lcb_settings_unref(settings); + lcbio_table_unref(iot); + ASSERT_EQ(1, ctx.count); +} + +TEST_F(MockUnitTest, testGetHostInfo) +{ + lcb_t instance; + createConnection(instance); + lcb_config_transport_t tx; + const char *hoststr = lcb_get_node(instance, LCB_NODE_HTCONFIG, 0); + ASSERT_FALSE(hoststr == NULL); + + hoststr = lcb_get_node(instance, LCB_NODE_HTCONFIG_CONNECTED, 0); + lcb_error_t err = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_CONFIG_TRANSPORT, &tx); + + ASSERT_EQ(LCB_SUCCESS, err); + if (tx == LCB_CONFIG_TRANSPORT_HTTP) { + ASSERT_FALSE(hoststr == NULL); + hoststr = lcb_get_node(instance, LCB_NODE_HTCONFIG_CONNECTED, 99); + ASSERT_FALSE(hoststr == NULL); + } else { + if (hoststr) { + printf("%s\n", hoststr); + } + ASSERT_TRUE(hoststr == NULL); + } + + // Get any data node + using std::map; + using std::string; + map smap; + + // Ensure we only get unique nodes + for (size_t ii = 0; ii < lcb_get_num_nodes(instance); ii++) { + const char *cur = lcb_get_node(instance, LCB_NODE_DATA, ii); + ASSERT_FALSE(cur == NULL); + ASSERT_FALSE(smap[cur]); + smap[cur] = true; + } + lcb_destroy(instance); + + // Try with no connection + err = lcb_create(&instance, NULL); + ASSERT_EQ(LCB_SUCCESS, err); + + hoststr = lcb_get_node(instance, LCB_NODE_HTCONFIG_CONNECTED, 0); + ASSERT_TRUE(NULL == hoststr); + + hoststr = lcb_get_node(instance, LCB_NODE_HTCONFIG, 0); + ASSERT_TRUE(NULL == hoststr); + + + + // These older API functions are special as they should never return NULL + hoststr = lcb_get_host(instance); + ASSERT_FALSE(hoststr == NULL); + ASSERT_STREQ("localhost", hoststr); + + hoststr = lcb_get_port(instance); + ASSERT_FALSE(hoststr == NULL); + ASSERT_STREQ("8091", hoststr); + + lcb_destroy(instance); +} + +TEST_F(MockUnitTest, testEmptyKeys) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + union { + lcb_CMDGET get; + lcb_CMDSTORE store; + lcb_CMDCOUNTER counter; + lcb_CMDENDURE endure; + lcb_CMDOBSERVE observe; + lcb_CMDTOUCH touch; + lcb_CMDUNLOCK unlock; + lcb_CMDGETREPLICA rget; + lcb_CMDBASE base; + lcb_CMDSTATS stats; + } u; + memset(&u, 0, sizeof u); + + lcb_sched_enter(instance); + + ASSERT_EQ(LCB_EMPTY_KEY, lcb_get3(instance, NULL, &u.get)); + ASSERT_EQ(LCB_EMPTY_KEY, lcb_store3(instance, NULL, &u.store)); + ASSERT_EQ(LCB_EMPTY_KEY, lcb_counter3(instance, NULL, &u.counter)); + ASSERT_EQ(LCB_EMPTY_KEY, lcb_touch3(instance, NULL, &u.touch)); + ASSERT_EQ(LCB_EMPTY_KEY, lcb_unlock3(instance, NULL, &u.unlock)); + ASSERT_EQ(LCB_EMPTY_KEY, lcb_rget3(instance, NULL, &u.rget)); + + // Observe and such + lcb_MULTICMD_CTX *ctx = lcb_observe3_ctxnew(instance); + ASSERT_EQ(LCB_EMPTY_KEY, ctx->addcmd(ctx, (lcb_CMDBASE*)&u.observe)); + ctx->fail(ctx); + + lcb_durability_opts_t dopts; + memset(&dopts, 0, sizeof dopts); + dopts.v.v0.persist_to = 1; + + ctx = lcb_endure3_ctxnew(instance, &dopts, NULL); + ASSERT_TRUE(ctx != NULL); + ASSERT_EQ(LCB_EMPTY_KEY, ctx->addcmd(ctx, (lcb_CMDBASE*)&u.endure)); + ctx->fail(ctx); + + ASSERT_EQ(LCB_SUCCESS, lcb_stats3(instance, NULL, &u.stats)); + lcb_sched_fail(instance); +} + +template +static bool ctlSet(lcb_t instance, int setting, T val) +{ + lcb_error_t err = lcb_cntl(instance, LCB_CNTL_SET, setting, &val); + return err == LCB_SUCCESS; +} + +template<> +bool ctlSet(lcb_t instance, int setting, const char *val) +{ + return lcb_cntl(instance, LCB_CNTL_SET, setting, (void*)val) == LCB_SUCCESS; +} + +template +static T ctlGet(lcb_t instance, int setting) +{ + T tmp; + lcb_error_t err = lcb_cntl(instance, LCB_CNTL_GET, setting, &tmp); + EXPECT_EQ(LCB_SUCCESS, err); + return tmp; +} +template +static void ctlGetSet(lcb_t instance, int setting, T val) { + EXPECT_TRUE(ctlSet(instance, setting, val)); + EXPECT_EQ(val, ctlGet(instance, setting)); +} + +template <> +void ctlGetSet(lcb_t instance, int setting, const char *val) +{ + EXPECT_TRUE(ctlSet(instance, setting, val)); + EXPECT_STREQ(val, ctlGet(instance, setting)); +} + +static bool ctlSetInt(lcb_t instance, int setting, int val) { + return ctlSet(instance, setting, val); +} +static int ctlGetInt(lcb_t instance, int setting) { + return ctlGet(instance, setting); +} +static bool ctlSetU32(lcb_t instance, int setting, lcb_U32 val) { + return ctlSet(instance, setting, val); +} +static lcb_U32 ctlGetU32(lcb_t instance, int setting) { + return ctlGet(instance, setting); +} + +TEST_F(MockUnitTest, testCtls) +{ + lcb_t instance; + HandleWrap hw; + lcb_error_t err; + createConnection(hw, instance); + + ctlGetSet(instance, LCB_CNTL_OP_TIMEOUT, UINT_MAX); + ctlGetSet(instance, LCB_CNTL_VIEW_TIMEOUT, UINT_MAX); + + ASSERT_EQ(LCB_TYPE_BUCKET, ctlGet(instance, LCB_CNTL_HANDLETYPE)); + ASSERT_FALSE(ctlSet(instance, LCB_CNTL_HANDLETYPE, LCB_TYPE_BUCKET)); + + lcbvb_CONFIG *cfg = ctlGet(instance, LCB_CNTL_VBCONFIG); + // Do we have a way to verify this? + ASSERT_FALSE(cfg == NULL); + ASSERT_GT(cfg->nsrv, 0); + + lcb_io_opt_t io = ctlGet(instance, LCB_CNTL_IOPS); + ASSERT_TRUE(io == instance->getIOT()->p); + // Try to set it? + ASSERT_FALSE(ctlSet(instance, LCB_CNTL_IOPS, (lcb_io_opt_t)"Hello")); + + // Map a key + lcb_cntl_vbinfo_t vbi = { 0 }; + vbi.v.v0.key = "123"; + vbi.v.v0.nkey = 3; + err = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_VBMAP, &vbi); + ASSERT_EQ(LCB_SUCCESS, err); + + // Try to modify it? + err = lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_VBMAP, &vbi); + ASSERT_NE(LCB_SUCCESS, err); + + ctlGetSet(instance, LCB_CNTL_IP6POLICY, LCB_IPV6_DISABLED); + ctlGetSet(instance, LCB_CNTL_IP6POLICY, LCB_IPV6_ONLY); + ctlGetSet(instance, LCB_CNTL_CONFERRTHRESH, UINT_MAX); + ctlGetSet(instance, LCB_CNTL_DURABILITY_TIMEOUT, UINT_MAX); + ctlGetSet(instance, LCB_CNTL_DURABILITY_INTERVAL, UINT_MAX); + ctlGetSet(instance, LCB_CNTL_HTTP_TIMEOUT, UINT_MAX); + ctlGetSet(instance, LCB_CNTL_IOPS_DLOPEN_DEBUG, 55); + ctlGetSet(instance, LCB_CNTL_CONFIGURATION_TIMEOUT, UINT_MAX); + + ctlGetSet(instance, LCB_CNTL_RANDOMIZE_BOOTSTRAP_HOSTS, 1); + ctlGetSet(instance, LCB_CNTL_RANDOMIZE_BOOTSTRAP_HOSTS, 0); + + ASSERT_EQ(0, ctlGetInt(instance, LCB_CNTL_CONFIG_CACHE_LOADED)); + ASSERT_FALSE(ctlSetInt(instance, LCB_CNTL_CONFIG_CACHE_LOADED, 99)); + + ctlGetSet(instance, LCB_CNTL_FORCE_SASL_MECH, "SECRET"); + + ctlGetSet(instance, LCB_CNTL_MAX_REDIRECTS, SHRT_MAX); + ctlGetSet(instance, LCB_CNTL_MAX_REDIRECTS, -1); + ctlGetSet(instance, LCB_CNTL_MAX_REDIRECTS, 0); + + // LCB_CNTL_LOGGER handled in other tests + + ctlGetSet(instance, LCB_CNTL_CONFDELAY_THRESH, UINT_MAX); + + // CONFIG_TRANSPORT. Test that we shouldn't be able to set it + ASSERT_FALSE(ctlSet( + instance, LCB_CNTL_CONFIG_TRANSPORT, LCB_CONFIG_TRANSPORT_LIST_END)); + + ctlGetSet(instance, LCB_CNTL_CONFIG_NODE_TIMEOUT, UINT_MAX); + ctlGetSet(instance, LCB_CNTL_HTCONFIG_IDLE_TIMEOUT, UINT_MAX); + + ASSERT_FALSE(ctlSet(instance, LCB_CNTL_CHANGESET, "deadbeef")); + ASSERT_FALSE(ctlGet(instance, LCB_CNTL_CHANGESET) == NULL); + ctlGetSet(instance, LCB_CNTL_CONFIGCACHE, "/foo/bar/baz"); + ASSERT_FALSE(ctlSetInt(instance, LCB_CNTL_SSL_MODE, 90)); + ASSERT_GE(ctlGetInt(instance, LCB_CNTL_SSL_MODE), 0); + ASSERT_FALSE(ctlSet(instance, LCB_CNTL_SSL_CACERT, "/tmp")); + + lcb_U32 ro_in, ro_out; + ro_in = LCB_RETRYOPT_CREATE(LCB_RETRY_ON_SOCKERR, LCB_RETRY_CMDS_GET); + ASSERT_TRUE(ctlSet(instance, LCB_CNTL_RETRYMODE, ro_in)); + + ro_out = LCB_RETRYOPT_CREATE(LCB_RETRY_ON_SOCKERR, 0); + err = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_RETRYMODE, &ro_out); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(LCB_RETRY_CMDS_GET, LCB_RETRYOPT_GETPOLICY(ro_out)); + + ASSERT_EQ(LCB_SUCCESS, lcb_cntl_string(instance, "retry_policy", "topochange:get")); + ro_out = LCB_RETRYOPT_CREATE(LCB_RETRY_ON_TOPOCHANGE, 0); + err = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_RETRYMODE, &ro_out); + ASSERT_EQ(LCB_RETRY_CMDS_GET, LCB_RETRYOPT_GETPOLICY(ro_out)); + + + ctlGetSet(instance, LCB_CNTL_HTCONFIG_URLTYPE, LCB_HTCONFIG_URLTYPE_COMPAT); + ctlGetSet(instance, LCB_CNTL_COMPRESSION_OPTS, LCB_COMPRESS_FORCE); + + ctlSetU32(instance, LCB_CNTL_CONLOGGER_LEVEL, 3); + lcb_U32 tmp; + err = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_CONLOGGER_LEVEL, &tmp); + ASSERT_NE(LCB_SUCCESS, err); + + ctlGetSet(instance, LCB_CNTL_DETAILED_ERRCODES, 1); + ctlGetSet(instance, LCB_CNTL_RETRY_INTERVAL, UINT_MAX); + ctlGetSet(instance, LCB_CNTL_RETRY_BACKOFF, 3.4); + ctlGetSet(instance, LCB_CNTL_HTTP_POOLSIZE, UINT_MAX); + ctlGetSet(instance, LCB_CNTL_HTTP_REFRESH_CONFIG_ON_ERROR, 0); + + // Allow timeouts to be expressed as fractional seconds. + err = lcb_cntl_string(instance, "operation_timeout", "1.0"); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(1000000, ctlGet(instance, LCB_CNTL_OP_TIMEOUT)); + err = lcb_cntl_string(instance, "operation_timeout", "0.255"); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_EQ(255000, ctlGet(instance, LCB_CNTL_OP_TIMEOUT)); + + // Test default for nmv retry + int itmp = ctlGetInt(instance, LCB_CNTL_RETRY_NMV_IMM); + ASSERT_NE(0, itmp); + + err = lcb_cntl_string(instance, "retry_nmv_imm", "0"); + ASSERT_EQ(LCB_SUCCESS, err); + itmp = ctlGetInt(instance, LCB_CNTL_RETRY_NMV_IMM); + ASSERT_EQ(0, itmp); +} + +TEST_F(MockUnitTest, testConflictingOptions) +{ + HandleWrap hw; + lcb_t instance; + createConnection(hw, instance); + + lcb_sched_enter(instance); + const char *key = "key"; + size_t nkey = 3; + const char *value = "value"; + size_t nvalue = 5; + + lcb_CMDSTORE scmd = { 0 }; + scmd.operation = LCB_APPEND; + scmd.exptime = 1; + LCB_CMD_SET_KEY(&scmd, key, nkey); + LCB_CMD_SET_VALUE(&scmd, value, nvalue); + + lcb_error_t err; + err = lcb_store3(instance, NULL, &scmd); + ASSERT_EQ(LCB_OPTIONS_CONFLICT, err); + scmd.exptime = 0; + scmd.flags = 99; + err = lcb_store3(instance, NULL, &scmd); + ASSERT_EQ(LCB_OPTIONS_CONFLICT, err); + + scmd.flags = 0; + scmd.exptime = 0; + err = lcb_store3(instance, NULL, &scmd); + ASSERT_EQ(LCB_SUCCESS, err); + + scmd.operation = LCB_ADD; + scmd.cas = 0xdeadbeef; + err = lcb_store3(instance, NULL, &scmd); + ASSERT_EQ(LCB_OPTIONS_CONFLICT, err); + + scmd.cas = 0; + err = lcb_store3(instance, NULL, &scmd); + ASSERT_EQ(LCB_SUCCESS, err); + + lcb_CMDCOUNTER ccmd = { 0 }; + LCB_CMD_SET_KEY(&ccmd, key, nkey); + ccmd.cas = 0xdeadbeef; + err = lcb_counter3(instance, NULL, &ccmd); + ASSERT_EQ(LCB_OPTIONS_CONFLICT, err); + ccmd.cas = 0; + err = lcb_counter3(instance, NULL, &ccmd); + ASSERT_EQ(LCB_SUCCESS, err); + + ccmd.exptime = 10; + ccmd.initial = 0; + ccmd.create = 0; + err = lcb_counter3(instance, NULL, &ccmd); + ASSERT_EQ(LCB_OPTIONS_CONFLICT, err); + ccmd.create = 1; + err = lcb_counter3(instance, NULL, &ccmd); + ASSERT_EQ(LCB_SUCCESS, err); + + lcb_CMDGET gcmd = { 0 }; + LCB_CMD_SET_KEY(&gcmd, key, nkey); + gcmd.cas = 0xdeadbeef; + err = lcb_get3(instance, NULL, &gcmd); + ASSERT_EQ(LCB_OPTIONS_CONFLICT, err); + + gcmd.cas = 0; + err = lcb_get3(instance, NULL, &gcmd); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_sched_fail(instance); +} + +TEST_F(MockUnitTest, testDump) +{ + const char *fpname; +#ifdef _WIN32 + fpname = "NUL:"; +#else + fpname = "/dev/null"; +#endif + FILE *fp = fopen(fpname, "w"); + if (!fp) { + perror(fpname); + return; + } + + // Simply try to dump the instance; + HandleWrap hw; + lcb_t instance; + createConnection(hw, instance); + std::vector keys; + genDistKeys(LCBT_VBCONFIG(instance), keys); + for (size_t ii = 0; ii < keys.size(); ii++) { + storeKey(instance, keys[ii], keys[ii]); + } + lcb_dump(instance, fp, LCB_DUMP_ALL); + fclose(fp); +} + +TEST_F(MockUnitTest, testRefreshConfig) +{ + HandleWrap hw; + lcb_t instance; + createConnection(hw, instance); + lcb_refresh_config(instance); + lcb_wait3(instance, LCB_WAIT_NOCHECK); +} + +extern "C" { +static void tickOpCb(lcb_t, int, const lcb_RESPBASE *rb) +{ + int *p = (int *)rb->cookie; + *p -= 1; + EXPECT_EQ(LCB_SUCCESS, rb->rc); +} +} + +TEST_F(MockUnitTest, testTickLoop) +{ + HandleWrap hw; + lcb_t instance; + lcb_error_t err; + createConnection(hw, instance); + + const char *key = "tickKey"; + const char *value = "tickValue"; + + lcb_install_callback3(instance, LCB_CALLBACK_STORE, tickOpCb); + lcb_CMDSTORE cmd = { 0 }; + cmd.operation = LCB_SET; + LCB_CMD_SET_KEY(&cmd, key, strlen(key)); + LCB_CMD_SET_VALUE(&cmd, value, strlen(value)); + + err = lcb_tick_nowait(instance); + if (err == LCB_CLIENT_FEATURE_UNAVAILABLE) { + fprintf(stderr, "Current event loop does not support tick!"); + return; + } + + lcb_sched_enter(instance); + int counter = 0; + for (int ii = 0; ii < 10; ii++) { + err = lcb_store3(instance, &counter, &cmd); + ASSERT_EQ(LCB_SUCCESS, err); + counter++; + } + + lcb_sched_leave(instance); + while (counter) { + lcb_tick_nowait(instance); + } +} + +TEST_F(MockUnitTest, testEmptyCtx) +{ + HandleWrap hw; + lcb_t instance; + lcb_error_t err = LCB_SUCCESS; + createConnection(hw, instance); + + lcb_MULTICMD_CTX *mctx; + lcb_durability_opts_t duropts = { 0 }; + duropts.v.v0.persist_to = 1; + mctx = lcb_endure3_ctxnew(instance, &duropts, &err); + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_FALSE(mctx == NULL); + + err = mctx->done(mctx, NULL); + ASSERT_NE(LCB_SUCCESS, err); + + mctx = lcb_observe3_ctxnew(instance); + ASSERT_FALSE(mctx == NULL); + err = mctx->done(mctx, NULL); + ASSERT_NE(LCB_SUCCESS, err); +} + +TEST_F(MockUnitTest, testMultiCreds) +{ + using lcb::Authenticator; + + HandleWrap hw; + lcb_t instance; + createConnection(hw, instance); + + lcb_BUCKETCRED cred; + cred[0] = "protected"; + cred[1] = "secret"; + lcb_error_t rc = lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_BUCKET_CRED, cred); + ASSERT_EQ(LCB_SUCCESS, rc); + Authenticator& auth = *instance->settings->auth; + lcb::Authenticator::Map::const_iterator res = auth.buckets().find("protected"); + ASSERT_NE(auth.buckets().end(), res); + ASSERT_EQ("secret", res->second); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_mutate.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_mutate.cc new file mode 100644 index 00000000..f164eebc --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_mutate.cc @@ -0,0 +1,609 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include "iotests.h" + +class MutateUnitTest : public MockUnitTest +{ +}; + +extern "C" { + static void testSimpleSetStoreCallback(lcb_t, const void *cookie, + lcb_storage_t operation, + lcb_error_t error, + const lcb_store_resp_t *resp) + { + using namespace std; + int *counter = (int *)cookie; + ASSERT_EQ(LCB_SET, operation); + EXPECT_EQ(LCB_SUCCESS, error); + ASSERT_NE((const lcb_store_resp_t *)NULL, resp); + EXPECT_EQ(0, resp->version); + std::string val((const char *)resp->v.v0.key, resp->v.v0.nkey); + EXPECT_TRUE(val == "testSimpleStoreKey1" || val == "testSimpleStoreKey2"); + ++(*counter); + EXPECT_NE(0, resp->v.v0.cas); + } +} + +/** + * @test + * Simple Set + * + * @pre + * Set two keys + * + * @post + * + * @c SUCCESS, both keys are received + */ +TEST_F(MutateUnitTest, testSimpleSet) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + (void)lcb_set_store_callback(instance, testSimpleSetStoreCallback); + int numcallbacks = 0; + lcb_store_cmd_t cmd1(LCB_SET, "testSimpleStoreKey1", 19, "key1", 4); + lcb_store_cmd_t cmd2(LCB_SET, "testSimpleStoreKey2", 19, "key2", 4); + lcb_store_cmd_t *cmds[] = { &cmd1, &cmd2 }; + EXPECT_EQ(LCB_SUCCESS, lcb_store(instance, &numcallbacks, 2, cmds)); + lcb_wait(instance); + EXPECT_EQ(2, numcallbacks); +} + +/** + * @test Zero length key + * @pre set a zero length for a key foo + * @post should not be able to schedule operation + */ +TEST_F(MutateUnitTest, testStoreZeroLengthKey) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + lcb_sched_enter(instance); + lcb_CMDSTORE cmd = { 0 }; + LCB_CMD_SET_KEY(&cmd, NULL, 0); + LCB_CMD_SET_VALUE(&cmd, "bar", 3); + cmd.operation = LCB_SET; + EXPECT_EQ(LCB_EMPTY_KEY, lcb_store3(instance, NULL, &cmd)); + lcb_sched_leave(instance); +} + + +extern "C" { + static void + testStoreZeroLengthValueCallback(lcb_t, int, const lcb_RESPBASE *resp) + { + lcb_RESPSTORE *sresp = (lcb_RESPSTORE *)resp; + int *counter = (int *)sresp->cookie; + ASSERT_EQ(LCB_SET, sresp->op); + EXPECT_EQ(LCB_SUCCESS, sresp->rc); + ++(*counter); + } +} +/** + * @test Zero length value + * @pre set a zero length value for a key foo + * @post should be able to retreive back empty value + */ +TEST_F(MutateUnitTest, testStoreZeroLengthValue) +{ + std::string key("foo"); + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + lcb_sched_enter(instance); + (void)lcb_install_callback3(instance, LCB_CALLBACK_STORE, testStoreZeroLengthValueCallback); + lcb_CMDSTORE cmd = { 0 }; + LCB_CMD_SET_KEY(&cmd, key.data(), key.length()); + LCB_CMD_SET_VALUE(&cmd, NULL, 0); + cmd.operation = LCB_SET; + int numcallbacks = 0; + EXPECT_EQ(LCB_SUCCESS, lcb_store3(instance, &numcallbacks, &cmd)); + lcb_sched_leave(instance); + lcb_wait3(instance, LCB_WAIT_NOCHECK); + EXPECT_EQ(1, numcallbacks); + + Item itm; + getKey(instance, key, itm); + EXPECT_EQ(0, itm.val.length()); +} + +extern "C" { + static void testRemoveCallback(lcb_t, const void *cookie, + lcb_error_t error, + const lcb_remove_resp_t *resp) + { + int *counter = (int *)cookie; + EXPECT_EQ(LCB_SUCCESS, error); + ASSERT_NE((const lcb_remove_resp_t *)NULL, resp); + EXPECT_EQ(0, resp->version); + ++(*counter); + } +} + +/** + * @test Remove + * + * @pre Set two keys and remove them + * @post Remove succeeds for both keys + */ +TEST_F(MutateUnitTest, testRemove) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + (void)lcb_set_remove_callback(instance, testRemoveCallback); + int numcallbacks = 0; + storeKey(instance, "testRemoveKey1", "foo"); + storeKey(instance, "testRemoveKey2", "foo"); + lcb_remove_cmd_t cmd1("testRemoveKey1"); + lcb_remove_cmd_t cmd2("testRemoveKey2"); + lcb_remove_cmd_t *cmds[] = { &cmd1, &cmd2 }; + EXPECT_EQ(LCB_SUCCESS, lcb_remove(instance, &numcallbacks, 2, cmds)); + + lcb_wait(instance); + EXPECT_EQ(2, numcallbacks); +} + +extern "C" { + static void testRemoveMissCallback(lcb_t, const void *cookie, + lcb_error_t error, + const lcb_remove_resp_t *resp) + { + int *counter = (int *)cookie; + EXPECT_EQ(LCB_KEY_ENOENT, error); + ASSERT_NE((const lcb_remove_resp_t *)NULL, resp); + EXPECT_EQ(0, resp->version); + ++(*counter); + } +} + +/** + * @test Remove (Miss) + * @pre Remove two non-existent keys + * @post Remove fails for both keys with @c KEY_ENOENT + */ +TEST_F(MutateUnitTest, testRemoveMiss) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + (void)lcb_set_remove_callback(instance, testRemoveMissCallback); + int numcallbacks = 0; + removeKey(instance, "testRemoveMissKey1"); + removeKey(instance, "testRemoveMissKey2"); + lcb_remove_cmd_t cmd1("testRemoveMissKey1"); + lcb_remove_cmd_t cmd2("testRemoveMissKey2"); + lcb_remove_cmd_t *cmds[] = { &cmd1, &cmd2 }; + EXPECT_EQ(LCB_SUCCESS, lcb_remove(instance, &numcallbacks, 2, cmds)); + + lcb_wait(instance); + EXPECT_EQ(2, numcallbacks); +} + +extern "C" { + static void testSimpleAddStoreCallback(lcb_t, const void *cookie, + lcb_storage_t operation, + lcb_error_t error, + const lcb_store_resp_t *resp) + { + using namespace std; + int *counter = (int *)cookie; + ASSERT_EQ(LCB_ADD, operation); + ASSERT_NE((const lcb_store_resp_t *)NULL, resp); + EXPECT_EQ(0, resp->version); + std::string val((const char *)resp->v.v0.key, resp->v.v0.nkey); + EXPECT_STREQ("testSimpleAddKey", val.c_str()); + if (*counter == 0) { + EXPECT_EQ(LCB_SUCCESS, error); + EXPECT_NE(0, resp->v.v0.cas); + } else { + EXPECT_EQ(LCB_KEY_EEXISTS, error); + } + ++(*counter); + } +} + +/** + * @test Add (Simple) + * @pre Schedule to Add operations on the same key + * @post First operation is a success. Second fails with @c KEY_EEXISTS + */ +TEST_F(MutateUnitTest, testSimpleAdd) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + (void)lcb_set_store_callback(instance, testSimpleAddStoreCallback); + removeKey(instance, "testSimpleAddKey"); + int numcallbacks = 0; + lcb_store_cmd_t cmd1(LCB_ADD, "testSimpleAddKey", 16, "key1", 4); + lcb_store_cmd_t cmd2(LCB_ADD, "testSimpleAddKey", 16, "key2", 4); + lcb_store_cmd_t *cmds[] = { &cmd1, &cmd2 }; + EXPECT_EQ(LCB_SUCCESS, lcb_store(instance, &numcallbacks, 2, cmds)); + lcb_wait(instance); + EXPECT_EQ(2, numcallbacks); +} + +extern "C" { + static void testSimpleAppendStoreCallback(lcb_t, const void *cookie, + lcb_storage_t operation, + lcb_error_t error, + const lcb_store_resp_t *resp) + { + using namespace std; + int *counter = (int *)cookie; + ASSERT_EQ(LCB_APPEND, operation); + ASSERT_NE((const lcb_store_resp_t *)NULL, resp); + EXPECT_EQ(0, resp->version); + EXPECT_EQ(LCB_SUCCESS, error); + EXPECT_NE(0, resp->v.v0.cas); + ++(*counter); + } +} + +/** + * @test Append + * @pre Set a key to @c foo, append it with @c bar. Retrieve the key + * @post Key is now @c foobar + */ +TEST_F(MutateUnitTest, testSimpleAppend) +{ + std::string key("testSimpleAppendKey"); + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + (void)lcb_set_store_callback(instance, testSimpleAppendStoreCallback); + storeKey(instance, key, "foo"); + int numcallbacks = 0; + lcb_store_cmd_t cmd(LCB_APPEND, key.data(), key.length(), "bar", 3); + lcb_store_cmd_t *cmds[] = { &cmd }; + EXPECT_EQ(LCB_SUCCESS, lcb_store(instance, &numcallbacks, 1, cmds)); + lcb_wait(instance); + EXPECT_EQ(1, numcallbacks); + + Item itm; + getKey(instance, key, itm); + EXPECT_STREQ("foobar", itm.val.c_str()); + +} + +extern "C" { + static void + testAppendNonExistingKeyCallback(lcb_t, int, const lcb_RESPBASE *resp) + { + lcb_RESPSTORE *sresp = (lcb_RESPSTORE *)resp; + int *counter = (int *)sresp->cookie; + ASSERT_EQ(LCB_APPEND, sresp->op); + EXPECT_EQ(LCB_NOT_STORED, sresp->rc); + ++(*counter); + } +} + +/** + * @test Append + * @pre Append a non existing key + * @post Returns key not stored + */ +TEST_F(MutateUnitTest, testAppendNonExistingKey) +{ + std::string key("testAppendNonExistingKey"); + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + lcb_sched_enter(instance); + (void)lcb_install_callback3(instance, LCB_CALLBACK_STORE, testAppendNonExistingKeyCallback); + lcb_CMDSTORE cmd = { 0 }; + LCB_CMD_SET_KEY(&cmd, key.data(), key.length()); + LCB_CMD_SET_VALUE(&cmd, "bar", 3); + cmd.operation = LCB_APPEND; + int numcallbacks = 0; + EXPECT_EQ(LCB_SUCCESS, lcb_store3(instance, &numcallbacks, &cmd)); + lcb_sched_leave(instance); + lcb_wait3(instance, LCB_WAIT_NOCHECK); + EXPECT_EQ(1, numcallbacks); +} + +extern "C" { + static void testSimplePrependStoreCallback(lcb_t, const void *cookie, + lcb_storage_t operation, + lcb_error_t error, + const lcb_store_resp_t *resp) + { + using namespace std; + int *counter = (int *)cookie; + ASSERT_EQ(LCB_PREPEND, operation); + ASSERT_NE((const lcb_store_resp_t *)NULL, resp); + EXPECT_EQ(0, resp->version); + EXPECT_EQ(LCB_SUCCESS, error); + EXPECT_NE(0, resp->v.v0.cas); + ++(*counter); + } +} + +/** + * @test Prepend + * @pre Set a key with the value @c foo, prepend it with the value @c bar. + * Get the key + * + * @post Key is now @c barfoo + */ +TEST_F(MutateUnitTest, testSimplePrepend) +{ + std::string key("testSimplePrependKey"); + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + (void)lcb_set_store_callback(instance, testSimplePrependStoreCallback); + storeKey(instance, key, "foo"); + int numcallbacks = 0; + lcb_store_cmd_t cmd(LCB_PREPEND, key.data(), key.length(), "bar", 3); + lcb_store_cmd_t *cmds[] = { &cmd }; + EXPECT_EQ(LCB_SUCCESS, lcb_store(instance, &numcallbacks, 1, cmds)); + lcb_wait(instance); + EXPECT_EQ(1, numcallbacks); + + Item itm; + getKey(instance, key, itm); + EXPECT_STREQ("barfoo", itm.val.c_str()); +} + +extern "C" { + static void + testPrependNonExistingKeyCallback(lcb_t, int, const lcb_RESPBASE *resp) + { + lcb_RESPSTORE *sresp = (lcb_RESPSTORE *)resp; + int *counter = (int *)sresp->cookie; + ASSERT_EQ(LCB_PREPEND, sresp->op); + EXPECT_EQ(LCB_NOT_STORED, sresp->rc); + ++(*counter); + } +} + +/** + * @test Prepend + * @pre prepend a non existing key + * @post Returns key not stored + */ +TEST_F(MutateUnitTest, testPrependNonExistingKey) +{ + std::string key("testPrependNonExistingKey"); + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + lcb_sched_enter(instance); + (void)lcb_install_callback3(instance, LCB_CALLBACK_STORE, testPrependNonExistingKeyCallback); + lcb_CMDSTORE cmd = { 0 }; + LCB_CMD_SET_KEY(&cmd, key.data(), key.length()); + LCB_CMD_SET_VALUE(&cmd, "foo", 3); + cmd.operation = LCB_PREPEND; + int numcallbacks = 0; + EXPECT_EQ(LCB_SUCCESS, lcb_store3(instance, &numcallbacks, &cmd)); + lcb_sched_leave(instance); + lcb_wait3(instance, LCB_WAIT_NOCHECK); + EXPECT_EQ(1, numcallbacks); +} + +extern "C" { + static void testSimpleReplaceNonexistingStoreCallback(lcb_t, const void *cookie, + lcb_storage_t operation, + lcb_error_t error, + const lcb_store_resp_t *resp) + { + int *counter = (int *)cookie; + ASSERT_EQ(LCB_REPLACE, operation); + ASSERT_NE((const lcb_store_resp_t *)NULL, resp); + EXPECT_EQ(0, resp->version); + EXPECT_EQ(LCB_KEY_ENOENT, error); + ++(*counter); + } +} + +/** + * @test Replace (Non-Existing) + * + * @pre Replace a non-existing key + * @post Fails with @c KEY_ENOENT + */ +TEST_F(MutateUnitTest, testSimpleReplaceNonexisting) +{ + std::string key("testSimpleReplaceNonexistingKey"); + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + (void)lcb_set_store_callback(instance, testSimpleReplaceNonexistingStoreCallback); + removeKey(instance, key); + int numcallbacks = 0; + lcb_store_cmd_t cmd(LCB_REPLACE, key.data(), key.length(), "bar", 3); + lcb_store_cmd_t *cmds[] = { &cmd }; + EXPECT_EQ(LCB_SUCCESS, lcb_store(instance, &numcallbacks, 1, cmds)); + lcb_wait(instance); + EXPECT_EQ(1, numcallbacks); +} + +extern "C" { + static void testSimpleReplaceStoreCallback(lcb_t, const void *cookie, + lcb_storage_t operation, + lcb_error_t error, + const lcb_store_resp_t *resp) + { + int *counter = (int *)cookie; + ASSERT_EQ(LCB_REPLACE, operation); + ASSERT_NE((const lcb_store_resp_t *)NULL, resp); + EXPECT_EQ(0, resp->version); + EXPECT_EQ(LCB_SUCCESS, error); + EXPECT_NE(0, resp->v.v0.cas); + ++(*counter); + } +} + +/** + * @test Replace (Hit) + * @pre + * Set a key to the value @c foo, replace it with the value @c bar, get the key + * + * @post + * Replace is a success, and the value is now @c bar + */ +TEST_F(MutateUnitTest, testSimpleReplace) +{ + std::string key("testSimpleReplaceKey"); + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + (void)lcb_set_store_callback(instance, testSimpleReplaceStoreCallback); + storeKey(instance, key, "foo"); + int numcallbacks = 0; + lcb_store_cmd_t cmd(LCB_REPLACE, key.data(), key.length(), "bar", 3); + lcb_store_cmd_t *cmds[] = { &cmd }; + EXPECT_EQ(LCB_SUCCESS, lcb_store(instance, &numcallbacks, 1, cmds)); + lcb_wait(instance); + EXPECT_EQ(1, numcallbacks); + Item itm; + getKey(instance, key, itm); + EXPECT_STREQ("bar", itm.val.c_str()); +} + +extern "C" { + static void testIncorrectCasReplaceStoreCallback(lcb_t, const void *cookie, + lcb_storage_t operation, + lcb_error_t error, + const lcb_store_resp_t *resp) + { + int *counter = (int *)cookie; + ASSERT_EQ(LCB_REPLACE, operation); + EXPECT_EQ(LCB_KEY_EEXISTS, error); + ASSERT_NE((const lcb_store_resp_t *)NULL, resp); + EXPECT_EQ(0, resp->version); + ++(*counter); + } +} + +/** + * @test Replace (Invalid CAS) + * + * @pre Set a key to the value @c foo. Replace the key specifying a garbage + * CAS value. + * + * @post Replace fails with @c KEY_EEXISTS + */ +TEST_F(MutateUnitTest, testIncorrectCasReplace) +{ + std::string key("testIncorrectCasReplaceKey"); + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + (void)lcb_set_store_callback(instance, testIncorrectCasReplaceStoreCallback); + storeKey(instance, key, "foo"); + Item itm; + getKey(instance, key, itm); + + int numcallbacks = 0; + lcb_store_cmd_t cmd(LCB_REPLACE, key.data(), key.length(), "bar", 3); + lcb_store_cmd_t *cmds[] = { &cmd }; + + cmd.v.v0.cas = itm.cas + 1; + EXPECT_EQ(LCB_SUCCESS, lcb_store(instance, &numcallbacks, 1, cmds)); + lcb_wait(instance); + EXPECT_EQ(1, numcallbacks); +} +extern "C" { + static void testCasReplaceStoreCallback(lcb_t, const void *cookie, + lcb_storage_t operation, + lcb_error_t error, + const lcb_store_resp_t *resp) + { + int *counter = (int *)cookie; + ASSERT_EQ(LCB_REPLACE, operation); + EXPECT_EQ(LCB_SUCCESS, error); + ASSERT_NE((const lcb_store_resp_t *)NULL, resp); + EXPECT_EQ(0, resp->version); + ++(*counter); + } +} + +/** + * @test Replace (CAS) + * + * @pre Store a key with the value @c foo, retrieve its CAS, and use retrieved + * cas to replace the value with @c bar + * + * @post Replace succeeds, get on the key yields the new value @c bar. + */ +TEST_F(MutateUnitTest, testCasReplace) +{ + std::string key("testCasReplaceKey"); + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + (void)lcb_set_store_callback(instance, testCasReplaceStoreCallback); + storeKey(instance, key, "foo"); + Item itm; + getKey(instance, key, itm); + + int numcallbacks = 0; + lcb_store_cmd_t cmd(LCB_REPLACE, key.data(), key.length(), "bar", 3); + lcb_store_cmd_t *cmds[] = { &cmd }; + + cmd.v.v0.cas = itm.cas; + EXPECT_EQ(LCB_SUCCESS, lcb_store(instance, &numcallbacks, 1, cmds)); + lcb_wait(instance); + EXPECT_EQ(1, numcallbacks); + getKey(instance, key, itm); + EXPECT_STREQ("bar", itm.val.c_str()); +} + +extern "C" { +static void storeCb(lcb_t, int, const lcb_RESPBASE *rb) +{ + ASSERT_EQ(LCB_SUCCESS, rb->rc); + *(bool*)rb->cookie = true; +} +} + +TEST_F(MutateUnitTest, testSetDefault) +{ + std::string key("testDefaultMode"); + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + lcb_install_callback3(instance, LCB_CALLBACK_STORE, storeCb); + + lcb_CMDSTORE cmd = { 0 }; + LCB_CMD_SET_KEY(&cmd, key.c_str(), key.size()); + LCB_CMD_SET_VALUE(&cmd, "foo", 3); + bool cookie = false; + ASSERT_EQ(LCB_SUCCESS, lcb_store3(instance, &cookie, &cmd)); + lcb_wait(instance); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_n1ql.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_n1ql.cc new file mode 100644 index 00000000..03d8be1a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_n1ql.cc @@ -0,0 +1,270 @@ +#include "config.h" +#include "iotests.h" +#include +#include +#include "internal.h" + +using std::vector; +using std::string; + + +struct N1QLResult { + vector rows; + string meta; + int htcode; + lcb_error_t rc; + bool called; + + N1QLResult() { + reset(); + } + + void reset() { + called = false; + rc = LCB_SUCCESS; + meta.clear(); + rows.clear(); + htcode = 0; + } +}; + +#define SKIP_QUERY_TEST() \ + fprintf(stderr, "Requires recent mock with query support"); \ + return + +extern "C" { +static void rowcb(lcb_t, int, const lcb_RESPN1QL *resp) +{ + N1QLResult *res = reinterpret_cast(resp->cookie); + if (resp->rflags & LCB_RESP_F_FINAL) { + res->rc = resp->rc; + if (resp->row) { + res->meta.assign(static_cast(resp->row), resp->nrow); + } + if (resp->htresp) { + res->htcode = resp->htresp->htstatus; + } + } else { + string row(static_cast(resp->row), resp->nrow); + res->rows.push_back(row); + } + res->called = true; +} +} + +class QueryUnitTest : public MockUnitTest { +protected: + lcb_N1QLPARAMS *params; + void SetUp() { + params = lcb_n1p_new(); + } + void TearDown() { + lcb_n1p_free(params); + } + bool createQueryConnection(HandleWrap& hw, lcb_t& instance) { + if (MockEnvironment::getInstance()->isRealCluster()) { + return false; + } + createConnection(hw, instance); + const lcbvb_CONFIG *vbc; + lcb_error_t rc; + rc = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_VBCONFIG, &vbc); + EXPECT_EQ(LCB_SUCCESS, rc); + int ix = lcbvb_get_randhost(vbc, LCBVB_SVCTYPE_N1QL, LCBVB_SVCMODE_PLAIN); + return ix > -1; + } + + void makeCommand(const char *query, lcb_CMDN1QL& cmd, bool prepared=false) { + lcb_n1p_setstmtz(params, query); + lcb_error_t rc = lcb_n1p_mkcmd(params, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + + cmd.callback = rowcb; + if (prepared) { + cmd.cmdflags |= LCB_CMDN1QL_F_PREPCACHE; + } + } +}; + +TEST_F(QueryUnitTest, testSimple) +{ + lcb_t instance; + HandleWrap hw; + if (!createQueryConnection(hw, instance)) { + SKIP_QUERY_TEST(); + } + + lcb_CMDN1QL cmd = { 0 }; + N1QLResult res; + makeCommand("SELECT mockrow", cmd); + lcb_error_t rc = lcb_n1ql_query(instance, &res, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_EQ(LCB_SUCCESS, res.rc); + ASSERT_EQ(1, res.rows.size()); +} + +TEST_F(QueryUnitTest, testQueryError) +{ + lcb_t instance; + HandleWrap hw; + if (!createQueryConnection(hw, instance)) { + SKIP_QUERY_TEST(); + } + lcb_CMDN1QL cmd = { 0 }; + N1QLResult res; + makeCommand("SELECT blahblah FROM blahblah", cmd); + lcb_error_t rc = lcb_n1ql_query(instance, &res, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_TRUE(res.rows.empty()); +} + +TEST_F(QueryUnitTest, testInvalidJson) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + lcb_CMDN1QL cmd = { 0 }; + cmd.query = "blahblah"; + cmd.nquery = strlen(cmd.query); + cmd.callback = rowcb; + lcb_error_t rc = lcb_n1ql_query(instance, NULL, &cmd); + ASSERT_NE(LCB_SUCCESS, rc); +} + +TEST_F(QueryUnitTest, testPrepareOk) +{ + lcb_t instance; + HandleWrap hw; + if (!createQueryConnection(hw, instance)) { + SKIP_QUERY_TEST(); + } + lcb_CMDN1QL cmd = { 0 }; + N1QLResult res; + makeCommand("SELECT mockrow", cmd, true); + lcb_error_t rc = lcb_n1ql_query(instance, &res, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_EQ(res.rc, LCB_SUCCESS); + ASSERT_EQ(1, res.rows.size()); + + // Get the plan contents + string query("SELECT mockrow"); + string plan; + lcb_n1qlcache_getplan(instance->n1ql_cache, query, plan); + // We have the plan! + ASSERT_FALSE(plan.empty()); + + // Issue it again.. + makeCommand("SELECT mockrow", cmd, true); + res.reset(); + rc = lcb_n1ql_query(instance, &res, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + string plan2; + lcb_n1qlcache_getplan(instance->n1ql_cache, query, plan2); + ASSERT_FALSE(plan2.empty()); + ASSERT_EQ(plan, plan2) << "Reused the same query (cache works!)"; + + lcb_n1qlcache_clear(instance->n1ql_cache); + plan.clear(); + lcb_n1qlcache_getplan(instance->n1ql_cache, query, plan); + ASSERT_TRUE(plan.empty()); + + // Issue it again! + makeCommand("SELECT mockrow", cmd, true); + res.reset(); + rc = lcb_n1ql_query(instance, &res, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + + ASSERT_EQ(1, res.rows.size()); + lcb_n1qlcache_getplan(instance->n1ql_cache, query, plan); + ASSERT_FALSE(plan.empty()); +} + +TEST_F(QueryUnitTest, testPrepareStale) +{ + lcb_t instance; + HandleWrap hw; + if (!createQueryConnection(hw, instance)) { + SKIP_QUERY_TEST(); + } + lcb_CMDN1QL cmd = { 0 }; + N1QLResult res; + makeCommand("SELECT mockrow", cmd, true); + lcb_error_t rc = lcb_n1ql_query(instance, &res, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_EQ(1, res.rows.size()); + + // Reset the index "state" + MockCommand mcmd(MockCommand::RESET_QUERYSTATE); + doMockTxn(mcmd); + + // Ensure the previous plan fails + string query("SELECT mockrow"); + string raw; + lcb_n1qlcache_getplan(instance->n1ql_cache, query, raw); + ASSERT_FALSE(raw.empty()); + + cmd.query = raw.c_str(); + cmd.nquery = raw.size(); + cmd.cmdflags = 0; + + res.reset(); + rc = lcb_n1ql_query(instance, &res, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_TRUE(res.rows.empty()); + ASSERT_FALSE(res.meta.empty()); + ASSERT_NE(string::npos, res.meta.find("indexNotFound")); + + // Now that we've verified our current plan isn't working, let's try to + // issue the cached plan again. lcb should get us a new plan + makeCommand("SELECT mockrow", cmd , true); + res.reset(); + rc = lcb_n1ql_query(instance, &res, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_EQ(1, res.rows.size()); +} + +TEST_F(QueryUnitTest, testPrepareFailure) +{ + lcb_t instance; + HandleWrap hw; + if (!createQueryConnection(hw, instance)) { + SKIP_QUERY_TEST(); + } + lcb_CMDN1QL cmd = { 0 }; + N1QLResult res; + makeCommand("SELECT blahblah", cmd, true); + lcb_error_t rc = lcb_n1ql_query(instance, &res, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_TRUE(res.called); + ASSERT_NE(LCB_SUCCESS, res.rc); + ASSERT_TRUE(res.rows.empty()); +} + +TEST_F(QueryUnitTest, testCancellation) +{ + lcb_t instance; + HandleWrap hw; + if (!createQueryConnection(hw, instance)) { + SKIP_QUERY_TEST(); + } + lcb_CMDN1QL cmd = { 0 }; + N1QLResult res; + makeCommand("SELECT mockrow", cmd); + lcb_N1QLHANDLE handle = NULL; + cmd.handle = &handle; + lcb_error_t rc = lcb_n1ql_query(instance, &res, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + ASSERT_TRUE(handle != NULL); + lcb_n1ql_cancel(instance, handle); + lcb_wait(instance); + ASSERT_FALSE(res.called); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_netfail.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_netfail.cc new file mode 100644 index 00000000..3cc77307 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_netfail.cc @@ -0,0 +1,654 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include "iotests.h" +#include + +#include "internal.h" /* vbucket_* things from lcb_t */ +#include +#include "bucketconfig/bc_http.h" + +#define LOGARGS(instance, lvl) \ + instance->settings, "tests-MUT", LCB_LOG_##lvl, __FILE__, __LINE__ + +#if defined(_WIN32) && !defined(usleep) +#define usleep(n) Sleep((n) / 1000) +#endif + +namespace { +class Retryer { +public: + Retryer(time_t maxDuration) : maxDuration(maxDuration) {} + bool run() { + time_t maxTime = time(NULL) + maxDuration; + while (!checkCondition()) { + trigger(); + if (checkCondition()) { + break; + } + if (time(NULL) > maxTime) { + printf("Time expired and condition still false!\n"); + break; + } else { + printf("Sleeping for a bit to allow failover/respawn propagation\n"); + usleep(100000); // Sleep for 100ms + } + } + return checkCondition(); + } +protected: + virtual bool checkCondition() = 0; + virtual void trigger() = 0; +private: + time_t maxDuration; +}; + +extern "C" { +static void nopStoreCb(lcb_t, int, const lcb_RESPBASE *) {} +} + +class NumNodeRetryer : public Retryer { +public: + NumNodeRetryer(time_t duration, lcb_t instance, size_t expCount) : + Retryer(duration), instance(instance), expCount(expCount) { + genDistKeys(LCBT_VBCONFIG(instance), distKeys); + } + virtual ~NumNodeRetryer() {} + +protected: + virtual bool checkCondition() { + return lcb_get_num_nodes(instance) == expCount; + } + virtual void trigger() { + lcb_RESPCALLBACK oldCb = lcb_install_callback3(instance, LCB_CALLBACK_STORE, nopStoreCb); + lcb_CMDSTORE scmd = { 0 }; + scmd.operation = LCB_SET; + lcb_sched_enter(instance); + + size_t nSubmit = 0; + for (size_t ii = 0; ii < distKeys.size(); ii++) { + LCB_CMD_SET_KEY(&scmd, distKeys[ii].c_str(), distKeys[ii].size()); + LCB_CMD_SET_VALUE(&scmd, distKeys[ii].c_str(), distKeys[ii].size()); + lcb_error_t rc = lcb_store3(instance, NULL, &scmd); + if (rc != LCB_SUCCESS) { + continue; + } + nSubmit++; + } + if (nSubmit) { + lcb_sched_leave(instance); + lcb_wait(instance); + } + + lcb_install_callback3(instance, LCB_CALLBACK_STORE, oldCb); + } + +private: + lcb_t instance; + size_t expCount; + std::vector distKeys; +}; +} + +static bool +syncWithNodeCount_(lcb_t instance, size_t expCount) +{ + NumNodeRetryer rr(60, instance, expCount); + return rr.run(); +} + +#define SYNC_WITH_NODECOUNT(instance, expCount) \ + if (!syncWithNodeCount_(instance, expCount)) { \ + lcb_log(LOGARGS(instance, WARN), "Timed out waiting for new configuration. Slow system?"); \ + fprintf(stderr, "*** FIXME: TEST NOT RUN! (not an SDK error)\n"); \ + return; \ + } + + + +extern "C" { +static void opFromCallback_storeCB(lcb_t, const void *, lcb_storage_t, + lcb_error_t error, const lcb_store_resp_t *) { + ASSERT_EQ(LCB_SUCCESS, error); +} + +static void opFromCallback_statsCB(lcb_t instance, const void *, + lcb_error_t error, const lcb_server_stat_resp_t *resp) +{ + lcb_error_t err; + char *statkey; + lcb_size_t nstatkey; + + ASSERT_EQ(0, resp->version); + const char *server_endpoint = resp->v.v0.server_endpoint; + const void *key = resp->v.v0.key; + lcb_size_t nkey = resp->v.v0.nkey; + const void *bytes = resp->v.v0.bytes; + lcb_size_t nbytes = resp->v.v0.nbytes; + + ASSERT_EQ(LCB_SUCCESS, error); + if (server_endpoint != NULL) { + nstatkey = strlen(server_endpoint) + nkey + 2; + statkey = new char[nstatkey]; + snprintf(statkey, nstatkey, "%s-%.*s", server_endpoint, + (int)nkey, (const char *)key); + + lcb_store_cmd_t storecmd(LCB_SET, statkey, nstatkey, bytes, nbytes); + lcb_store_cmd_t *storecmds[] = { &storecmd }; + err = lcb_store(instance, NULL, 1, storecmds); + ASSERT_EQ(LCB_SUCCESS, err); + delete []statkey; + } +} +} + +TEST_F(MockUnitTest, testOpFromCallback) +{ + // @todo we need to have a test that actually tests the timeout callback.. + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + lcb_set_stat_callback(instance, opFromCallback_statsCB); + lcb_set_store_callback(instance, opFromCallback_storeCB); + + lcb_server_stats_cmd_t stat; + lcb_server_stats_cmd_t *commands[] = {&stat }; + + ASSERT_EQ(LCB_SUCCESS, lcb_cntl_string(instance, "operation_timeout", "5.0")); + ASSERT_EQ(LCB_SUCCESS, lcb_server_stats(instance, NULL, 1, commands)); + lcb_wait(instance); +} + +struct timeout_test_cookie { + int *counter; + lcb_error_t expected; +}; +extern "C" { +static void set_callback(lcb_t instance, + const void *cookie, + lcb_storage_t, lcb_error_t err, + const lcb_store_resp_t *) +{ + timeout_test_cookie *tc = (timeout_test_cookie*)cookie;; + lcb_log(LOGARGS(instance, INFO), "Got code 0x%x. Expected 0x%x", err, tc->expected); + EXPECT_EQ(tc->expected, err); + if (err == LCB_ETIMEDOUT) { + // Remove the hiccup at the first timeout failure + MockEnvironment::getInstance()->hiccupNodes(0, 0); + } + *tc->counter -= 1; +} + +struct next_store_st { + lcb_t instance; + struct timeout_test_cookie *tc; + const lcb_store_cmd_t * const * cmdpp; +}; + +static void reschedule_callback(void *cookie) +{ + lcb_error_t err; + struct next_store_st *ns = (struct next_store_st *)cookie; + lcb_log(LOGARGS(ns->instance, INFO), "Rescheduling operation.."); + err = lcb_store(ns->instance, ns->tc, 1, ns->cmdpp); + lcb_loop_unref(ns->instance); + EXPECT_EQ(LCB_SUCCESS, err); +} + +} + +TEST_F(MockUnitTest, testTimeoutOnlyStale) +{ + SKIP_UNLESS_MOCK(); + + HandleWrap hw; + createConnection(hw); + lcb_t instance = hw.getLcb(); + lcb_uint32_t tmoval = 1000000; + int nremaining = 2; + struct timeout_test_cookie cookies[2]; + MockEnvironment *mock = MockEnvironment::getInstance(); + + // Set the timeout + lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_OP_TIMEOUT, &tmoval); + + lcb_set_store_callback(instance, set_callback); + + lcb_store_cmd_t scmd, *cmdp; + const char *key = "i'm a key"; + const char *value = "a value"; + cmdp = &scmd; + + removeKey(instance, key); + + // Make the mock timeout the first cookie. The extras length is: + mock->hiccupNodes(1500, 1); + + + + memset(&scmd, 0, sizeof(scmd)); + scmd.v.v0.key = key; + scmd.v.v0.nkey = strlen(key); + scmd.v.v0.bytes = value; + scmd.v.v0.nbytes = strlen(value); + scmd.v.v0.operation = LCB_SET; + cookies[0].counter = &nremaining; + cookies[0].expected = LCB_ETIMEDOUT; + lcb_store(instance, cookies, 1, &cmdp); + + cookies[1].counter = &nremaining; + cookies[1].expected = LCB_SUCCESS; + struct next_store_st ns; + ns.cmdpp = &cmdp; + ns.tc = cookies+1; + ns.instance = instance; + lcbio_pTIMER timer = lcbio_timer_new(instance->iotable, &ns, reschedule_callback); + lcb_loop_ref(instance); + lcbio_timer_rearm(timer, 900000); + + lcb_log(LOGARGS(instance, INFO), "Waiting.."); + lcb_wait(instance); + lcbio_timer_destroy(timer); + + ASSERT_EQ(0, nremaining); +} + + +extern "C" { + struct rvbuf { + lcb_error_t error; + lcb_cas_t cas1; + lcb_cas_t cas2; + char *bytes; + lcb_size_t nbytes; + lcb_int32_t counter; + }; + int store_cnt; + + /* Needed for "testPurgedBody", to ensure preservation of connection */ + static void io_close_wrap(lcb_io_opt_t, lcb_socket_t) + { + fprintf(stderr, "We requested to close, but we were't expecting it\n"); + abort(); + } + + static void store_callback(lcb_t instance, const void *cookie, + lcb_storage_t, lcb_error_t error, + const lcb_store_resp_t *) + { + struct rvbuf *rv = (struct rvbuf *)cookie; + lcb_log(LOGARGS(instance, INFO), + "Got storage callback for cookie %p with err=0x%x", + (void *)cookie, + (int)error); + + rv->error = error; + store_cnt++; + if (!instance->wait) { /* do not touch IO if we are using lcb_wait() */ + lcb_stop_loop(instance); + } + } + + static void get_callback(lcb_t instance, const void *cookie, + lcb_error_t error, const lcb_get_resp_t *resp) + { + struct rvbuf *rv = (struct rvbuf *)cookie; + rv->error = error; + rv->bytes = (char *)malloc(resp->v.v0.nbytes); + memcpy((void *)rv->bytes, resp->v.v0.bytes, resp->v.v0.nbytes); + rv->nbytes = resp->v.v0.nbytes; + if (!instance->wait) { /* do not touch IO if we are using lcb_wait() */ + lcb_stop_loop(instance); + } + } +} + +struct StoreContext { + std::map mm; + typedef std::map::iterator MyIter; + + void check(int expected) { + EXPECT_EQ(expected, mm.size()); + + for (MyIter iter = mm.begin(); iter != mm.end(); iter++) { + EXPECT_EQ(LCB_SUCCESS, iter->second); + } + } + + void clear() { + mm.clear(); + } +}; + +extern "C" { +static void ctx_store_callback(lcb_t, const void *cookie, lcb_storage_t, + lcb_error_t err, const lcb_store_resp_t *resp) +{ + StoreContext *ctx = reinterpret_cast( + const_cast(cookie)); + + std::string s((const char *)resp->v.v0.key, resp->v.v0.nkey); + ctx->mm[s] = err; +} +} + +TEST_F(MockUnitTest, testReconfigurationOnNodeFailover) +{ + SKIP_UNLESS_MOCK(); + lcb_t instance; + HandleWrap hw; + lcb_error_t err; + const char *argv[] = { "--replicas", "0", "--nodes", "4", NULL }; + + MockEnvironment mock_o(argv), *mock = &mock_o; + + std::vector keys; + std::vector cmds; + std::vectorppcmds; + + mock->createConnection(hw, instance); + instance->settings->vb_noguess = 1; + lcb_connect(instance); + lcb_wait(instance); + ASSERT_EQ(0, lcb_get_num_replicas(instance)); + + size_t numNodes = mock->getNumNodes(); + + genDistKeys(LCBT_VBCONFIG(instance), keys); + genStoreCommands(keys, cmds, ppcmds); + StoreContext ctx; + + mock->failoverNode(0); + SYNC_WITH_NODECOUNT(instance, numNodes-1); + + lcb_set_store_callback(instance, ctx_store_callback); + err = lcb_store(instance, &ctx, cmds.size(), &ppcmds[0]); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_wait(instance); + ctx.check((int)cmds.size()); + + mock->respawnNode(0); + SYNC_WITH_NODECOUNT(instance, numNodes); + + ctx.clear(); + err = lcb_store(instance, &ctx, cmds.size(), &ppcmds[0]); + lcb_wait(instance); + ctx.check((int)cmds.size()); +} + + + +struct fo_context_st { + MockEnvironment *env; + int index; + lcb_t instance; +}; +// Hiccup the server, then fail it over. +extern "C" { +static void fo_callback(void *cookie) +{ + fo_context_st *ctx = (fo_context_st *)cookie; + ctx->env->failoverNode(ctx->index); + ctx->env->hiccupNodes(0, 0); + lcb_loop_unref(ctx->instance); +} +} + +TEST_F(MockUnitTest, testBufferRelocationOnNodeFailover) +{ + SKIP_UNLESS_MOCK(); + lcb_error_t err; + struct rvbuf rv; + lcb_t instance; + HandleWrap hw; + std::string key = "testBufferRelocationOnNodeFailover"; + std::string val = "foo"; + + const char *argv[] = { "--replicas", "0", "--nodes", "4", NULL }; + MockEnvironment mock_o(argv), *mock = &mock_o; + + // We need to disable CCCP for this test to receive "Push" style + // configuration. + mock->setCCCP(false); + + mock->createConnection(hw, instance); + lcb_connect(instance); + lcb_wait(instance); + + // Set the timeout for 15 seconds + lcb_uint32_t tmoval = 15000000; + lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_OP_TIMEOUT, &tmoval); + + lcb_set_store_callback(instance, store_callback); + lcb_set_get_callback(instance, get_callback); + + // Initialize the nodes first.. + removeKey(instance, key); + + /* Schedule SET operation */ + lcb_store_cmd_t storecmd(LCB_SET, key.c_str(), key.size(), + val.c_str(), val.size()); + + /* Determine what server should receive that operation */ + int vb, idx; + lcbvb_map_key(LCBT_VBCONFIG(instance), key.c_str(), key.size(), &vb, &idx); + mock->hiccupNodes(5000, 1); + + struct fo_context_st ctx = { mock, idx, instance }; + lcbio_pTIMER timer; + timer = lcbio_timer_new(instance->iotable, &ctx, fo_callback); + lcb_loop_ref(instance); + lcbio_timer_rearm(timer, 500000); + + lcb_store_cmd_t *storecmds[] = { &storecmd }; + err = lcb_store(instance, &rv, 1, storecmds); + ASSERT_EQ(LCB_SUCCESS, err); + + store_cnt = 0; + lcb_wait(instance); + ASSERT_EQ(1, store_cnt); + ASSERT_EQ(LCB_SUCCESS, rv.error); + + memset(&rv, 0, sizeof(rv)); + err = lcb_store(instance, &rv, 1, storecmds); + ASSERT_EQ(LCB_SUCCESS, err); + store_cnt = 0; + lcb_wait(instance); + ASSERT_EQ(1, store_cnt); + + /* Check that value was actually set */ + lcb_get_cmd_t getcmd(key.c_str(), key.size()); + lcb_get_cmd_t *getcmds[] = { &getcmd }; + err = lcb_get(instance, &rv, 1, getcmds); + ASSERT_EQ(LCB_SUCCESS, err); + + lcb_wait(instance); + lcbio_timer_destroy(timer); + ASSERT_EQ(LCB_SUCCESS, rv.error); + ASSERT_EQ(rv.nbytes, val.size()); + std::string bytes = std::string(rv.bytes, rv.nbytes); + ASSERT_STREQ(bytes.c_str(), val.c_str()); + free(rv.bytes); +} + +TEST_F(MockUnitTest, testSaslMechs) +{ + // Ensure our SASL mech listing works. + SKIP_UNLESS_MOCK(); + + const char *argv[] = { "--buckets", "protected:secret:couchbase", NULL }; + + lcb_t instance; + lcb_error_t err; + struct lcb_create_st crParams; + MockEnvironment mock_o(argv, "protected"), *protectedEnv = &mock_o; + protectedEnv->makeConnectParams(crParams, NULL); + protectedEnv->setCCCP(false); + + crParams.v.v0.user = "protected"; + crParams.v.v0.passwd = "secret"; + crParams.v.v0.bucket = "protected"; + doLcbCreate(&instance, &crParams, protectedEnv); + + // Make the socket pool disallow idle connections + instance->memd_sockpool->maxidle = 0; + + err = lcb_connect(instance); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_wait(instance); + + // Force our SASL mech + err = lcb_cntl(instance, LCB_CNTL_SET, + LCB_CNTL_FORCE_SASL_MECH, (void *)"blah"); + ASSERT_EQ(LCB_SUCCESS, err); + + Item itm("key", "value"); + KVOperation kvo(&itm); + + kvo.allowableErrors.insert(LCB_SASLMECH_UNAVAILABLE); + kvo.allowableErrors.insert(LCB_ETIMEDOUT); + kvo.store(instance); + + ASSERT_FALSE(kvo.globalErrors.find(LCB_SASLMECH_UNAVAILABLE) == + kvo.globalErrors.end()); + + err = lcb_cntl(instance, LCB_CNTL_SET, + LCB_CNTL_FORCE_SASL_MECH, (void *)"PLAIN"); + ASSERT_EQ(LCB_SUCCESS, err); + + kvo.clear(); + kvo.store(instance); + + lcb_destroy(instance); +} + +static void +doManyItems(lcb_t instance, std::vector keys) +{ + lcb_CMDSTORE cmd = { 0 }; + cmd.operation = LCB_SET; + lcb_sched_enter(instance); + for (size_t ii = 0; ii < keys.size(); ii++) { + LCB_CMD_SET_KEY(&cmd, keys[ii].c_str(), keys[ii].size()); + LCB_CMD_SET_VALUE(&cmd, keys[ii].c_str(), keys[ii].size()); + EXPECT_EQ(LCB_SUCCESS, lcb_store3(instance, NULL, &cmd)); + } + lcb_sched_leave(instance); + lcb_wait(instance); +} + +extern "C" { +static void mcdFoVerifyCb(lcb_t, int, const lcb_RESPBASE *rb) +{ + EXPECT_EQ(LCB_SUCCESS, rb->rc); +} +} + +TEST_F(MockUnitTest, DISABLED_testMemcachedFailover) +{ + SKIP_UNLESS_MOCK(); + const char *argv[] = { "--buckets", "cache::memcache", NULL }; + lcb_t instance; + struct lcb_create_st crParams; + lcb_RESPCALLBACK oldCb; + + MockEnvironment mock_o(argv, "cache"), *mock = &mock_o; + mock->makeConnectParams(crParams, NULL); + doLcbCreate(&instance, &crParams, mock); + + // Check internal setting here + lcb_connect(instance); + lcb_wait(instance); + size_t numNodes = mock->getNumNodes(); + + oldCb = lcb_install_callback3(instance, LCB_CALLBACK_STORE, mcdFoVerifyCb); + + // Get the command list: + std::vector distKeys; + genDistKeys(LCBT_VBCONFIG(instance), distKeys); + doManyItems(instance, distKeys); + // Should succeed implicitly with callback above + + // Fail over the first node.. + mock->failoverNode(1, "cache"); + SYNC_WITH_NODECOUNT(instance, numNodes-1); + + // Set the callback to the previous one. We expect failures here + lcb_install_callback3(instance, LCB_CALLBACK_STORE, oldCb); + doManyItems(instance, distKeys); + + mock->respawnNode(1, "cache"); + SYNC_WITH_NODECOUNT(instance, numNodes); + ASSERT_EQ(numNodes, lcb_get_num_nodes(instance)); + + // Restore the verify callback + lcb_install_callback3(instance, LCB_CALLBACK_STORE, mcdFoVerifyCb); + doManyItems(instance, distKeys); + + lcb_destroy(instance); +} + +struct NegativeIx { + lcb_error_t err; + int callCount; +}; + +extern "C" { +static void get_callback3(lcb_t, int, const lcb_RESPBASE *resp) +{ + NegativeIx *ni = (NegativeIx *)resp->cookie; + ni->err = resp->rc; + ni->callCount++; +} +} +/** + * This tests the case where a negative index appears for a vbucket ID for the + * mapped key. In this case we'd expect that the command would be retried + * at least once, and not receive an LCB_NO_MATCHING_SERVER. + * + * Unfortunately this test is a bit hacky since we need to modify the vbucket + * information, and hopefully get a new config afterwards. Additionally we'd + * want to mod + */ +TEST_F(MockUnitTest, testNegativeIndex) +{ + HandleWrap hw; + lcb_t instance; + createConnection(hw, instance); + lcb_install_callback3(instance, LCB_CALLBACK_GET, get_callback3); + std::string key("ni_key"); + // Get the config + lcbvb_CONFIG *vbc = instance->cur_configinfo->vbc; + int vb = lcbvb_k2vb(vbc, key.c_str(), key.size()); + + // Set the index to -1 + vbc->vbuckets[vb].servers[0] = -1; + NegativeIx ni = { LCB_SUCCESS }; + lcb_CMDGET gcmd = { 0 }; + LCB_CMD_SET_KEY(&gcmd, key.c_str(), key.size()); + // Set the timeout to something a bit shorter + lcb_cntl_setu32(instance, LCB_CNTL_OP_TIMEOUT, 500000); + + lcb_sched_enter(instance); + lcb_error_t err = lcb_get3(instance, &ni, &gcmd); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_sched_leave(instance); + lcb_wait(instance); + ASSERT_EQ(1, ni.callCount); + // That's it +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_obseqno.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_obseqno.cc new file mode 100644 index 00000000..82509007 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_obseqno.cc @@ -0,0 +1,157 @@ +#include "iotests.h" + +using namespace std; +class ObseqnoTest : public MockUnitTest { +}; + +extern "C" { +static void storeCb_getstok(lcb_t, int cbtype, const lcb_RESPBASE *rb) +{ + EXPECT_EQ(LCB_SUCCESS, rb->rc); + const lcb_MUTATION_TOKEN *tmp = lcb_resp_get_mutation_token(cbtype, rb); + if (tmp) { + *(lcb_MUTATION_TOKEN *)rb->cookie = *tmp; + } +} +} + +static void +storeGetStok(lcb_t instance, const string& k, const string& v, lcb_MUTATION_TOKEN *st) +{ + lcb_RESPCALLBACK oldcb = lcb_get_callback3(instance, LCB_CALLBACK_STORE); + lcb_install_callback3(instance, LCB_CALLBACK_STORE, storeCb_getstok); + lcb_sched_enter(instance); + lcb_CMDSTORE cmd = { 0 }; + LCB_CMD_SET_KEY(&cmd, k.c_str(), k.size()); + LCB_CMD_SET_VALUE(&cmd, v.c_str(), v.size()); + cmd.operation = LCB_SET; + + lcb_error_t rc = lcb_store3(instance, st, &cmd); + EXPECT_EQ(LCB_SUCCESS, rc); + lcb_sched_leave(instance); + lcb_wait(instance); + lcb_install_callback3(instance, LCB_CALLBACK_STORE, oldcb); +} + +TEST_F(ObseqnoTest, testFetchImplicit) { + SKIP_UNLESS_MOCK(); + HandleWrap hw; + lcb_t instance; + lcb_error_t rc; + createConnection(hw, instance); + const char *key = "obseqBasic"; + const char *value = "value"; + + rc = lcb_cntl_string(instance, "dur_mutation_tokens", "true"); + ASSERT_EQ(LCB_SUCCESS, rc); + + lcb_MUTATION_TOKEN st_fetched = { 0 }; + storeGetStok(instance, key, value, &st_fetched); + ASSERT_TRUE(st_fetched.uuid_ != 0); + + lcb_KEYBUF kb; + LCB_KREQ_SIMPLE(&kb, key, strlen(key)); + + const lcb_MUTATION_TOKEN *ss = lcb_get_mutation_token(instance, &kb, &rc); + ASSERT_EQ(LCB_SUCCESS, rc); + ASSERT_TRUE(ss != NULL); + ASSERT_EQ(ss->uuid_, st_fetched.uuid_); + ASSERT_EQ(ss->vbid_, st_fetched.vbid_); + ASSERT_EQ(ss->seqno_, st_fetched.seqno_); +} + +extern "C" { +static void +obseqCallback(lcb_t, int, const lcb_RESPBASE *rb) { + lcb_RESPOBSEQNO *pp = (lcb_RESPOBSEQNO *)rb->cookie; + *pp = *(const lcb_RESPOBSEQNO *)rb; +} +} + +static void +doObserveSeqno(lcb_t instance, const lcb_MUTATION_TOKEN *ss, int server, lcb_RESPOBSEQNO& resp) { + lcb_CMDOBSEQNO cmd = { 0 }; + cmd.vbid = ss->vbid_; + cmd.uuid = ss->uuid_; + cmd.server_index = server; + lcb_error_t rc; + + lcb_sched_enter(instance); + rc = lcb_observe_seqno3(instance, &resp, &cmd); + if (rc != LCB_SUCCESS) { + resp.rc = rc; + resp.rflags |= LCB_RESP_F_CLIENTGEN; + return; + } + + lcb_RESPCALLBACK oldcb = lcb_get_callback3(instance, LCB_CALLBACK_OBSEQNO); + lcb_install_callback3(instance, LCB_CALLBACK_OBSEQNO, obseqCallback); + lcb_sched_leave(instance); + lcb_wait(instance); + lcb_install_callback3(instance, LCB_CALLBACK_OBSEQNO, oldcb); +} + +TEST_F(ObseqnoTest, testObserve) { + SKIP_UNLESS_MOCK(); + + HandleWrap hw; + lcb_t instance; + lcb_error_t rc; + createConnection(hw, instance); + lcbvb_CONFIG *vbc; + + lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_VBCONFIG, &vbc); + + lcb_MUTATION_TOKEN st_fetched = { 0 }; + const char *key = "testObserve"; + const char *value = "value"; + + // Get the synctoken + storeGetStok(instance, key, value, &st_fetched); + ASSERT_TRUE(LCB_MUTATION_TOKEN_ISVALID(&st_fetched)); + + for (size_t ii = 0; ii < lcbvb_get_nreplicas(vbc)+1; ii++) { + int ix = lcbvb_vbserver(vbc, st_fetched.vbid_, ii); + lcb_RESPOBSEQNO resp = { 0 }; + doObserveSeqno(instance, &st_fetched, ix, resp); + ASSERT_EQ(LCB_SUCCESS, resp.rc); + ASSERT_EQ(st_fetched.uuid_, resp.cur_uuid); + ASSERT_EQ(0, resp.old_uuid); +// printf("SEQ_MEM: %lu. SEQ_DISK: %lu\n", resp.mem_seqno, resp.persisted_seqno); + ASSERT_GT(resp.mem_seqno, 0); + ASSERT_EQ(resp.mem_seqno, resp.persisted_seqno); + } +} + +TEST_F(ObseqnoTest, testFailoverFormat) { + SKIP_UNLESS_MOCK(); + HandleWrap hw; + lcb_t instance; + createConnection(hw, instance); + lcbvb_CONFIG *vbc; + + lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_VBCONFIG, &vbc); + const char *key = "testObserve"; + const char *value = "value"; + + lcb_MUTATION_TOKEN st_fetched = { 0 }; + storeGetStok(instance, key, value, &st_fetched); + + MockEnvironment *env = MockEnvironment::getInstance(); + env->regenVbCoords(); + + // Now we should get a different sequence number + lcb_RESPOBSEQNO rr; + doObserveSeqno(instance, &st_fetched, lcbvb_vbmaster(vbc, st_fetched.vbid_), rr); + ASSERT_EQ(LCB_SUCCESS, rr.rc); +// printf("Old UUID: %llu\n", rr.old_uuid); +// printf("Cur UUID: %llu\n", rr.cur_uuid); + ASSERT_GT(rr.old_uuid, 0); + ASSERT_EQ(rr.old_uuid, st_fetched.uuid_); + ASSERT_NE(rr.old_uuid, rr.cur_uuid); + ASSERT_EQ(rr.old_seqno, st_fetched.seqno_); + +} + +// TODO: We should add more tests here, but in order to do this, we must +// first validate the mock. diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_regression.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_regression.cc new file mode 100644 index 00000000..236f60b4 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_regression.cc @@ -0,0 +1,321 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include "internal.h" +#include "iotests.h" + +class RegressionUnitTest : public MockUnitTest +{ +}; + +static bool callbackInvoked = false; + +extern "C" { + static void get_callback(lcb_t, const void *cookie, + lcb_error_t err, const lcb_get_resp_t *) + { + EXPECT_EQ(err, LCB_KEY_ENOENT); + int *counter_p = reinterpret_cast(const_cast(cookie)); + EXPECT_TRUE(counter_p != NULL); + EXPECT_GT(*counter_p, 0); + *counter_p -= 1; + callbackInvoked = true; + } + + static void stats_callback(lcb_t, const void *cookie, lcb_error_t err, + const lcb_server_stat_resp_t *resp) + { + EXPECT_EQ(err, LCB_SUCCESS); + if (resp->v.v0.nkey == 0) { + int *counter_p = reinterpret_cast(const_cast(cookie)); + *counter_p -= 1; + } + callbackInvoked = true; + } +} + +TEST_F(RegressionUnitTest, CCBC_150) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + callbackInvoked = false; + lcb_set_get_callback(instance, get_callback); + lcb_set_stat_callback(instance, stats_callback); + lcb_uint32_t tmoval = 15000000; + lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_OP_TIMEOUT, &tmoval); + + lcb_get_cmd_t getCmd1("testGetMiss1"); + lcb_get_cmd_t *getCmds[] = { &getCmd1 }; + + lcb_server_stats_cmd_t statCmd; + lcb_server_stats_cmd_t *statCmds[] = { &statCmd }; + int ii; + + // Lets spool up a lot of commands in one of the buffers so that we + // know we need to search for it a few times when we get responses.. + int callbackCounter = 1000; + void *ptr = &callbackCounter; + + for (ii = 0; ii < 1000; ++ii) { + EXPECT_EQ(LCB_SUCCESS, lcb_get(instance, ptr, 1, getCmds)); + } + + callbackCounter++; + EXPECT_EQ(LCB_SUCCESS, lcb_server_stats(instance, ptr, 1, statCmds)); + + callbackCounter += 1000; + for (ii = 0; ii < 1000; ++ii) { + EXPECT_EQ(LCB_SUCCESS, lcb_get(instance, ptr, 1, getCmds)); + } + + callbackCounter++; + EXPECT_EQ(LCB_SUCCESS, lcb_server_stats(instance, ptr, 1, statCmds)); + + callbackCounter++; + EXPECT_EQ(LCB_SUCCESS, lcb_server_stats(instance, ptr, 1, statCmds)); + + EXPECT_EQ(LCB_SUCCESS, lcb_wait(instance)); + ASSERT_TRUE(callbackInvoked); + ASSERT_EQ(0, callbackCounter); +} + +struct ccbc_275_info_st { + int call_count; + lcb_error_t last_err; +}; + +extern "C" { +static void get_callback_275(lcb_t instance, + const void *cookie, + lcb_error_t err, + const lcb_get_resp_t *) +{ + struct ccbc_275_info_st *info = (struct ccbc_275_info_st *)cookie; + info->call_count++; + info->last_err = err; + lcb_breakout(instance); +} + +} + +TEST_F(RegressionUnitTest, CCBC_275) +{ + SKIP_UNLESS_MOCK(); + lcb_t instance; + lcb_error_t err; + struct lcb_create_st crOpts; + const char *argv[] = { "--buckets", "protected:secret:couchbase", NULL }; + MockEnvironment mock_o(argv, "protected"), *mock = &mock_o; + struct ccbc_275_info_st info = { 0, LCB_SUCCESS }; + + mock->makeConnectParams(crOpts, NULL); + crOpts.v.v0.user = "protected"; + crOpts.v.v0.passwd = "secret"; + crOpts.v.v0.bucket = "protected"; + doLcbCreate(&instance, &crOpts, mock); + + err = lcb_connect(instance); + ASSERT_EQ(LCB_SUCCESS, err); + + err = lcb_wait(instance); + ASSERT_EQ(LCB_SUCCESS, err); + + std::string key = "key_CCBC_275"; + lcb_get_cmd_t cmd, *cmdp; + memset(&cmd, 0, sizeof(cmd)); + cmd.v.v0.key = key.c_str(); + cmd.v.v0.nkey = key.size(); + cmdp = &cmd; + + // Set timeout for a short interval + lcb_uint32_t tmo_usec = 100000; + lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_OP_TIMEOUT, &tmo_usec); + + // In the past this issue would result in several symptoms: + // (1) the client would crash (ringbuffer_consumed in failout_server) + // (2) the client would hang + // (3) the subsequent lcb_wait would return immediately. + // So far I've managed to reproduce (1), not clear on (2) and (3) + mock->hiccupNodes(1000, 1); + lcb_set_get_callback(instance, get_callback_275); + + err = lcb_get(instance, &info, 1, &cmdp); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_wait(instance); + ASSERT_EQ(1, info.call_count); + + ASSERT_ERRISA(info.last_err, LCB_ERRTYPE_NETWORK); + + // Make sure we've fully purged and disconnected the server + struct lcb_cntl_vbinfo_st vbi; + memset(&vbi, 0, sizeof(vbi)); + vbi.v.v0.key = key.c_str(); + vbi.v.v0.nkey = key.size(); + err = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_VBMAP, &vbi); + ASSERT_EQ(LCB_SUCCESS, err); +// ASSERT_EQ(LCB_CONNSTATE_UNINIT, +// instance->servers[vbi.v.v0.server_index].connection.state); + + // Restore the timeout to something sane + tmo_usec = 5000000; + err = lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_OP_TIMEOUT, &tmo_usec); + ASSERT_EQ(LCB_SUCCESS, err); + + mock->hiccupNodes(0, 0); + info.call_count = 0; + err = lcb_get(instance, &info, 1, &cmdp); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_wait(instance); + ASSERT_EQ(1, info.call_count); + + ASSERT_EQ(LCB_KEY_ENOENT, info.last_err); + + lcb_destroy(instance); +} + + +TEST_F(MockUnitTest, testIssue59) +{ + // lcb_wait() blocks forever if there is nothing queued + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + lcb_wait(instance); + lcb_wait(instance); + lcb_wait(instance); + lcb_wait(instance); + lcb_wait(instance); + lcb_wait(instance); + lcb_wait(instance); + lcb_wait(instance); +} + + +extern "C" { + struct rvbuf { + lcb_error_t error; + lcb_cas_t cas1; + lcb_cas_t cas2; + char *bytes; + lcb_size_t nbytes; + lcb_int32_t counter; + }; + + static void df_store_callback1(lcb_t instance, + const void *cookie, + lcb_storage_t, + lcb_error_t error, + const lcb_store_resp_t *) + { + struct rvbuf *rv = (struct rvbuf *)cookie; + rv->error = error; + lcb_stop_loop(instance); + } + + static void df_store_callback2(lcb_t instance, + const void *cookie, + lcb_storage_t, + lcb_error_t error, + const lcb_store_resp_t *resp) + { + struct rvbuf *rv = (struct rvbuf *)cookie; + rv->error = error; + rv->cas2 = resp->v.v0.cas; + lcb_stop_loop(instance); + } + + static void df_get_callback(lcb_t instance, + const void *cookie, + lcb_error_t error, + const lcb_get_resp_t *resp) + { + struct rvbuf *rv = (struct rvbuf *)cookie; + const char *value = "{\"bar\"=>1, \"baz\"=>2}"; + lcb_size_t nvalue = strlen(value); + lcb_error_t err; + + rv->error = error; + rv->cas1 = resp->v.v0.cas; + lcb_store_cmd_t storecmd(LCB_SET, resp->v.v0.key, resp->v.v0.nkey, + value, nvalue, 0, 0, resp->v.v0.cas); + lcb_store_cmd_t *storecmds[] = { &storecmd }; + + err = lcb_store(instance, rv, 1, storecmds); + ASSERT_EQ(LCB_SUCCESS, err); + } +} + +TEST_F(MockUnitTest, testDoubleFreeError) +{ + lcb_error_t err; + struct rvbuf rv; + const char *key = "test_compare_and_swap_async_", *value = "{\"bar\" => 1}"; + lcb_size_t nkey = strlen(key), nvalue = strlen(value); + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + /* prefill the bucket */ + (void)lcb_set_store_callback(instance, df_store_callback1); + + lcb_store_cmd_t storecmd(LCB_SET, key, nkey, value, nvalue); + lcb_store_cmd_t *storecmds[] = { &storecmd }; + + err = lcb_store(instance, &rv, 1, storecmds); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_run_loop(instance); + ASSERT_EQ(LCB_SUCCESS, rv.error); + + /* run exercise + * + * 1. get the valueue and its cas + * 2. atomic set new valueue using old cas + */ + (void)lcb_set_store_callback(instance, df_store_callback2); + (void)lcb_set_get_callback(instance, df_get_callback); + + lcb_get_cmd_t getcmd(key, nkey); + lcb_get_cmd_t *getcmds[] = { &getcmd }; + + err = lcb_get(instance, &rv, 1, getcmds); + ASSERT_EQ(LCB_SUCCESS, err); + rv.cas1 = rv.cas2 = 0; + lcb_run_loop(instance); + ASSERT_EQ(LCB_SUCCESS, rv.error); + ASSERT_GT(rv.cas1, 0); + ASSERT_GT(rv.cas2, 0); + ASSERT_NE(rv.cas1, rv.cas2); +} + +TEST_F(MockUnitTest, testBrokenFirstNodeInList) +{ + MockEnvironment *mock = MockEnvironment::getInstance(); + lcb_create_st options; + mock->makeConnectParams(options, NULL); + std::string nodes = options.v.v0.host; + nodes = "1.2.3.4:4321;" + nodes; + options.v.v0.host = nodes.c_str(); + + lcb_t instance; + doLcbCreate(&instance, &options, mock); + lcb_cntl_setu32(instance, LCB_CNTL_OP_TIMEOUT, LCB_MS2US(200)); + ASSERT_EQ(LCB_SUCCESS, lcb_connect(instance)); + lcb_destroy(instance); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_sched.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_sched.cc new file mode 100644 index 00000000..1910c20a --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_sched.cc @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2016 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include +#include +#include "iotests.h" +#include "internal.h" + +class SchedUnitTests : public MockUnitTest +{ +}; + +static bool +hasPendingOps(lcb_t instance) +{ + for (size_t ii = 0; ii < LCBT_NSERVERS(instance); ++ii) { + if (instance->get_server(ii)->has_pending()) { + return true; + } + } + return false; +} + +static void +opCallback(lcb_t, int, const lcb_RESPBASE *rb) { + size_t *counter = reinterpret_cast(rb->cookie); + *counter += 1; +} + +TEST_F(SchedUnitTests, testSched) +{ + HandleWrap hw; + lcb_t instance; + lcb_error_t rc; + size_t counter; + createConnection(hw, instance); + + lcb_install_callback3(instance, LCB_CALLBACK_STORE, opCallback); + + // lcb_store3 + lcb_CMDSTORE scmd = { 0 }; + LCB_CMD_SET_KEY(&scmd, "key", 3); + LCB_CMD_SET_VALUE(&scmd, "val", 3); + scmd.operation = LCB_SET; + + rc = lcb_store3(instance, &counter, &scmd); + ASSERT_EQ(LCB_SUCCESS, rc); + ASSERT_TRUE(hasPendingOps(instance)); + lcb_wait3(instance, LCB_WAIT_NOCHECK); + ASSERT_FALSE(hasPendingOps(instance)); + + lcb_sched_enter(instance); + rc = lcb_store3(instance, &counter, &scmd); + ASSERT_EQ(LCB_SUCCESS, rc); + ASSERT_FALSE(hasPendingOps(instance)); + lcb_sched_leave(instance); + ASSERT_TRUE(hasPendingOps(instance)); + lcb_wait3(instance, LCB_WAIT_NOCHECK); + ASSERT_FALSE(hasPendingOps(instance)); + + // Try with multiple operations.. + counter = 0; + for (size_t ii = 0; ii < 5; ++ii) { + rc = lcb_store3(instance, &counter, &scmd); + } + + ASSERT_TRUE(hasPendingOps(instance)); + lcb_sched_enter(instance); + rc = lcb_store3(instance, &counter, &scmd); + lcb_sched_fail(instance); + lcb_wait3(instance, LCB_WAIT_NOCHECK); + ASSERT_EQ(5, counter); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_serverops.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_serverops.cc new file mode 100644 index 00000000..cecd6551 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_serverops.cc @@ -0,0 +1,230 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include "iotests.h" +#include + +class ServeropsUnitTest : public MockUnitTest +{ +}; + +extern "C" { + static void testServerStatsCallback(lcb_t, const void *cookie, + lcb_error_t error, + const lcb_server_stat_resp_t *resp) + { + int *counter = (int *)cookie; + EXPECT_EQ(LCB_SUCCESS, error); + EXPECT_EQ(0, resp->version); + ++(*counter); + } + static void statKey_callback(lcb_t, int, const lcb_RESPBASE *resp_base) { + const lcb_RESPSTATS *resp = (const lcb_RESPSTATS *)resp_base; + if (!resp->server) { + return; + } + EXPECT_EQ(LCB_SUCCESS, resp->rc); + std::map &mm = *(std::map*)resp->cookie; + mm[resp->server] = true; + } +} + +/** + * @test Server Statistics + * @pre Schedule a server statistics command + * @post The response is a valid statistics structure and its status is + * @c SUCCESS. + * + * @post the statistics callback is invoked more than once. + */ +TEST_F(ServeropsUnitTest, testServerStats) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + (void)lcb_set_stat_callback(instance, testServerStatsCallback); + int numcallbacks = 0; + lcb_server_stats_cmd_t cmd; + lcb_server_stats_cmd_t *cmds[] = { &cmd }; + EXPECT_EQ(LCB_SUCCESS, lcb_server_stats(instance, &numcallbacks, 1, cmds)); + lcb_wait(instance); + EXPECT_LT(1, numcallbacks); +} + +TEST_F(ServeropsUnitTest, testKeyStats) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + lcb_install_callback3(instance, LCB_CALLBACK_STATS, statKey_callback); + lcb_CMDSTATS cmd = { 0 }; + + std::string key = "keystats_test"; + storeKey(instance, key, "blah blah"); + LCB_CMD_SET_KEY(&cmd, key.c_str(), key.size()); + cmd.cmdflags = LCB_CMDSTATS_F_KV; + std::map mm; + + lcb_sched_enter(instance); + lcb_error_t err = lcb_stats3(instance, &mm, &cmd); + ASSERT_EQ(LCB_SUCCESS, err); + lcb_sched_leave(instance); + + lcb_wait(instance); + ASSERT_EQ(lcb_get_num_replicas(instance)+1, mm.size()); + + // Ensure that a key with an embedded space fails + key = "key with space"; + LCB_CMD_SET_KEY(&cmd, key.c_str(), key.size()); + err = lcb_stats3(instance, NULL, &cmd); + ASSERT_NE(LCB_SUCCESS, err); +} + +extern "C" { + static void testServerVersionsCallback(lcb_t, const void *cookie, + lcb_error_t error, + const lcb_server_version_resp_t *resp) + { + int *counter = (int *)cookie; + EXPECT_EQ(LCB_SUCCESS, error); + EXPECT_EQ(0, resp->version); + ++(*counter); + } +} + +/** + * @test Server Versions + * @pre Request the server versions + * @post Response is successful, and the version callback is invoked more than + * once. + */ +TEST_F(ServeropsUnitTest, testServerVersion) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + (void)lcb_set_version_callback(instance, testServerVersionsCallback); + int numcallbacks = 0; + lcb_server_version_cmd_t cmd; + lcb_server_version_cmd_t *cmds[] = { &cmd }; + EXPECT_EQ(LCB_SUCCESS, lcb_server_versions(instance, &numcallbacks, 1, cmds)); + lcb_wait(instance); + EXPECT_LT(1, numcallbacks); +} + + +extern "C" { + static void testFlushCallback(lcb_t, const void *cookie, + lcb_error_t error, + const lcb_flush_resp_t *resp) + { + int *counter = (int *)cookie; + EXPECT_TRUE(error == LCB_SUCCESS || error == LCB_NOT_SUPPORTED); + EXPECT_EQ(0, resp->version); + ++(*counter); + } +} + +/** + * @test Flush + * @pre Request a flush operation + * @post Response is either a success or a @c NOT_SUPPORTED return + */ +TEST_F(ServeropsUnitTest, testFlush) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + (void)lcb_set_flush_callback(instance, testFlushCallback); + int numcallbacks = 0; + lcb_flush_cmd_t cmd; + lcb_flush_cmd_t *cmds[] = { &cmd }; + EXPECT_EQ(LCB_SUCCESS, lcb_flush(instance, &numcallbacks, 1, cmds)); + lcb_wait(instance); + EXPECT_LT(1, numcallbacks); +} + +extern "C" { + static char *verbosity_endpoint; + + static void verbosity_all_callback(lcb_t instance, + const void *cookie, + lcb_error_t error, + const lcb_verbosity_resp_t *resp) + { + int *counter = (int *)cookie; + ASSERT_EQ(0, resp->version); + ASSERT_EQ(LCB_SUCCESS, error); + if (resp->v.v0.server_endpoint == NULL) { + EXPECT_EQ(MockEnvironment::getInstance()->getNumNodes(), *counter); + return; + } else if (verbosity_endpoint == NULL) { + verbosity_endpoint = strdup(resp->v.v0.server_endpoint); + } + ++(*counter); + } + + static void verbosity_single_callback(lcb_t instance, + const void *, + lcb_error_t error, + const lcb_verbosity_resp_t *resp) + { + ASSERT_EQ(0, resp->version); + ASSERT_EQ(LCB_SUCCESS, error); + if (resp->v.v0.server_endpoint == NULL) { + return; + } else { + EXPECT_STREQ(verbosity_endpoint, resp->v.v0.server_endpoint); + } + } +} + +/** + * @test Server Verbosity + * @todo (document this..) + */ +TEST_F(ServeropsUnitTest, testVerbosity) +{ + lcb_t instance; + HandleWrap hw; + createConnection(hw, instance); + + (void)lcb_set_verbosity_callback(instance, verbosity_all_callback); + + int counter = 0; + + lcb_verbosity_cmd_t cmd(LCB_VERBOSITY_DEBUG); + lcb_verbosity_cmd_t *commands[] = { &cmd }; + EXPECT_EQ(LCB_SUCCESS, lcb_set_verbosity(instance, &counter, 1, commands)); + lcb_wait(instance); + + EXPECT_EQ(MockEnvironment::getInstance()->getNumNodes(), counter); + EXPECT_NE((char *)NULL, verbosity_endpoint); + + (void)lcb_set_verbosity_callback(instance, verbosity_single_callback); + + lcb_verbosity_cmd_t cmd2(LCB_VERBOSITY_DEBUG, verbosity_endpoint); + lcb_verbosity_cmd_t *commands2[] = { &cmd2 }; + EXPECT_EQ(LCB_SUCCESS, lcb_set_verbosity(instance, &counter, 1, commands2)); + lcb_wait(instance); + free((void *)verbosity_endpoint); + verbosity_endpoint = NULL; + +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_smoke.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_smoke.cc new file mode 100644 index 00000000..3f51e830 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_smoke.cc @@ -0,0 +1,528 @@ +#include +#include "iotests.h" +using std::vector; +using std::string; + +// This file contains the 'migrated' tests from smoke-test.c + +static lcb_config_transport_t transports[] = { + LCB_CONFIG_TRANSPORT_HTTP, LCB_CONFIG_TRANSPORT_LIST_END }; +struct rvbuf { + lcb_error_t error; + lcb_storage_t operation; + vector bytes; + vector key; + lcb_cas_t cas; + lcb_uint32_t flags; + lcb_int32_t counter; + lcb_uint32_t errorCount; + + template void setKey(const T* resp) { + const char *ktmp, *kend; + ktmp = (const char*)resp->v.v0.key; + kend = ktmp + resp->v.v0.nkey; + key.assign(ktmp, kend); + } + + void setValue(const lcb_get_resp_t *resp) { + const char *btmp = (const char*)resp->v.v0.bytes; + const char *bend = btmp + resp->v.v0.nbytes; + bytes.assign(btmp, bend); + } + + string getKeyString() { + return string(key.begin(), key.end()); + } + + string getValueString() { + return string(bytes.begin(), bytes.end()); + } + + rvbuf() { + reset(); + } + + void reset() { + error = LCB_SUCCESS; + operation = LCB_SET; + cas = 0; + flags = 0; + counter = 0; + errorCount = 0; + key.clear(); + bytes.clear(); + } + + void setError(lcb_error_t err) { + EXPECT_GT(counter, 0); + counter--; + if (err != LCB_SUCCESS) { + error = err; + errorCount++; + } + } + void incRemaining() { counter++; } +}; + +extern "C" +{ +static void store_callback(lcb_t, const void *cookie, lcb_storage_t op, + lcb_error_t err, const lcb_store_resp_t *resp) +{ + rvbuf *rv = (rvbuf *)cookie; + rv->setError(err); + rv->setKey(resp); + rv->operation = op; +} +static void get_callback(lcb_t, const void *cookie, lcb_error_t err, + const lcb_get_resp_t *resp) +{ + rvbuf *rv = (rvbuf*)cookie; + rv->setError(err); + rv->setKey(resp); + if (err == LCB_SUCCESS) { + rv->setValue(resp); + } +} +static void touch_callback(lcb_t, const void *cookie, lcb_error_t err, + const lcb_touch_resp_t *resp) +{ + rvbuf *rv = (rvbuf *)cookie; + rv->setError(err); + rv->setKey(resp); + EXPECT_EQ(LCB_SUCCESS, err); +} +static void version_callback(lcb_t, const void *cookie, lcb_error_t err, + const lcb_server_version_resp_t *resp) +{ + const char *server_endpoint = (const char *)resp->v.v0.server_endpoint; + const char *vstring = (const char *)resp->v.v0.vstring; + lcb_size_t nvstring = resp->v.v0.nvstring; + rvbuf *rv = (rvbuf *)cookie; + char *str; + EXPECT_EQ(LCB_SUCCESS, err); + + if (server_endpoint == NULL) { + assert(rv->counter == 0); + return; + } + + rv->setError(err); + /*copy the key to an allocated buffer and ensure the key read from vstring + * will not segfault + */ + str = (char *)malloc(nvstring); + memcpy(str, vstring, nvstring); + free(str); + +} +} //extern "C" +static void +setupCallbacks(lcb_t instance) +{ + lcb_set_store_callback(instance, store_callback); + lcb_set_get_callback(instance, get_callback); + lcb_set_touch_callback(instance, touch_callback); + lcb_set_version_callback(instance, version_callback); +} + +class SmokeTest : public ::testing::Test +{ +protected: + MockEnvironment *mock; + lcb_t session; + void SetUp() { + assert(session == NULL); + session = NULL; + mock = NULL; + } + + void TearDown() { + if (session != NULL) { + lcb_destroy(session); + } + if (mock != NULL) { + delete mock; + } + + session = NULL; + mock = NULL; + } + void destroySession() { + if (session != NULL) { + lcb_destroy(session); + session = NULL; + } + } + + SmokeTest() : mock(NULL), session(NULL) {} +public: + void testSet1(); + void testSet2(); + void testGet1(); + void testGet2(); + void testTouch1(); + void testVersion1(); + void testSpuriousSaslError(); + lcb_error_t testMissingBucket(); + + // Call to connect instance + void connectCommon(const char *password = NULL, lcb_error_t expected = LCB_SUCCESS); +}; + +void +SmokeTest::testSet1() +{ + lcb_error_t err; + rvbuf rv; + lcb_store_cmd_t cmd; + const lcb_store_cmd_t *cmds[] = { &cmd }; + memset(&cmd, 0, sizeof(cmd)); + + string key("foo"); + string value("bar"); + + cmd.v.v0.key = key.c_str(); + cmd.v.v0.nkey = key.size(); + cmd.v.v0.bytes = value.c_str(); + cmd.v.v0.nbytes = value.size(); + cmd.v.v0.operation = LCB_SET; + err = lcb_store(session, &rv, 1, cmds); + rv.incRemaining(); + EXPECT_EQ(LCB_SUCCESS, err); + lcb_wait(session); + EXPECT_EQ(LCB_SUCCESS, rv.error); + EXPECT_EQ(LCB_SET, rv.operation); + EXPECT_EQ(key, rv.getKeyString()); +} + +void +SmokeTest::testSet2() +{ + lcb_error_t err; + struct rvbuf rv; + lcb_size_t ii; + lcb_store_cmd_t cmd; + const lcb_store_cmd_t *cmds[] = { &cmd }; + string key("foo"), value("bar"); + + memset(&cmd, 0, sizeof(cmd)); + cmd.v.v0.key = key.c_str(); + cmd.v.v0.nkey = key.size(); + cmd.v.v0.bytes = value.c_str(); + cmd.v.v0.nbytes = value.size(); + cmd.v.v0.operation = LCB_SET; + + rv.errorCount = 0; + rv.counter = 0; + for (ii = 0; ii < 10; ++ii, rv.incRemaining()) { + err = lcb_store(session, &rv, 1, cmds); + EXPECT_EQ(LCB_SUCCESS, err); + } + lcb_wait(session); + EXPECT_EQ(0, rv.errorCount); +} + +void +SmokeTest::testGet1() +{ + lcb_error_t err; + struct rvbuf rv; + lcb_store_cmd_t storecmd; + const lcb_store_cmd_t *storecmds[] = { &storecmd }; + lcb_get_cmd_t getcmd; + const lcb_get_cmd_t *getcmds[] = { &getcmd }; + string key("foo"), value("bar"); + + memset(&storecmd, 0, sizeof(storecmd)); + storecmd.v.v0.key = key.c_str(); + storecmd.v.v0.nkey = key.size(); + storecmd.v.v0.bytes = value.c_str(); + storecmd.v.v0.nbytes = value.size(); + storecmd.v.v0.operation = LCB_SET; + rv.incRemaining(); + err = lcb_store(session, &rv, 1, storecmds); + EXPECT_EQ(LCB_SUCCESS, err); + lcb_wait(session); + EXPECT_EQ(LCB_SUCCESS, rv.error); + + rv.reset(); + memset(&getcmd, 0, sizeof(getcmd)); + getcmd.v.v0.key = key.c_str(); + getcmd.v.v0.nkey = key.size(); + rv.incRemaining(); + err = lcb_get(session, &rv, 1, getcmds); + EXPECT_EQ(LCB_SUCCESS, err); + lcb_wait(session); + EXPECT_EQ(rv.error, LCB_SUCCESS); + EXPECT_EQ(key, rv.getKeyString()); + EXPECT_EQ(value, rv.getValueString()); +} + +static void +genAZString(vector& coll) +{ + string base("foo"); + for (size_t ii = 0; ii < 26; ++ii) { + coll.push_back(base); + coll.back() += ('a' + ii); + } +} + +void +SmokeTest::testGet2() +{ + lcb_error_t err; + struct rvbuf rv; + string value("bar"); + lcb_store_cmd_t storecmd; + const lcb_store_cmd_t *storecmds[] = { &storecmd }; + lcb_get_cmd_t *getcmds[26]; + vector coll; + genAZString(coll); + + for (size_t ii = 0; ii < coll.size(); ii++) { + const string& curKey = coll[ii]; + + memset(&storecmd, 0, sizeof(storecmd)); + storecmd.v.v0.key = curKey.c_str(); + storecmd.v.v0.nkey = curKey.size(); + storecmd.v.v0.bytes = value.c_str(); + storecmd.v.v0.nbytes = value.size(); + storecmd.v.v0.operation = LCB_SET; + err = lcb_store(session, &rv, 1, storecmds); + EXPECT_EQ(LCB_SUCCESS, err); + rv.incRemaining(); + lcb_wait(session); + EXPECT_EQ(LCB_SUCCESS, rv.error); + + rv.reset(); + getcmds[ii] = (lcb_get_cmd_t *)calloc(1, sizeof(lcb_get_cmd_t)); + EXPECT_FALSE(NULL == getcmds[ii]); + getcmds[ii]->v.v0.key = curKey.c_str(); + getcmds[ii]->v.v0.nkey = curKey.size(); + } + + rv.counter = coll.size(); + err = lcb_get(session, &rv, coll.size(), (const lcb_get_cmd_t * const *)getcmds); + EXPECT_EQ(LCB_SUCCESS, err); + lcb_wait(session); + EXPECT_EQ(LCB_SUCCESS, rv.error); + EXPECT_EQ(value, rv.getValueString()); + + for (size_t ii = 0; ii < 26; ii++) { + free(getcmds[ii]); + } +} + +void +SmokeTest::testTouch1() +{ + lcb_error_t err; + struct rvbuf rv; + lcb_store_cmd_t storecmd; + const lcb_store_cmd_t *storecmds[] = { &storecmd }; + lcb_touch_cmd_t *touchcmds[26]; + vector coll; + string value("bar"); + genAZString(coll); + for (size_t ii = 0; ii < coll.size(); ii++) { + const string& curKey = coll[ii]; + memset(&storecmd, 0, sizeof(storecmd)); + storecmd.v.v0.key = curKey.c_str(); + storecmd.v.v0.nkey = curKey.size(); + storecmd.v.v0.bytes = value.c_str(); + storecmd.v.v0.nbytes = value.size(); + storecmd.v.v0.operation = LCB_SET; + err = lcb_store(session, &rv, 1, storecmds); + EXPECT_EQ(LCB_SUCCESS, err); + rv.incRemaining(); + lcb_wait(session); + EXPECT_EQ(LCB_SUCCESS, rv.error); + + rv.reset(); + touchcmds[ii] = (lcb_touch_cmd_t*)calloc(1, sizeof(lcb_touch_cmd_t)); + EXPECT_FALSE(touchcmds[ii] == NULL); + touchcmds[ii]->v.v0.key = curKey.c_str(); + touchcmds[ii]->v.v0.nkey = curKey.size(); + } + + rv.counter = coll.size(); + err = lcb_touch(session, &rv, coll.size(), (const lcb_touch_cmd_t * const *)touchcmds); + EXPECT_EQ(LCB_SUCCESS, err); + lcb_wait(session); + EXPECT_EQ(LCB_SUCCESS, rv.error); + for (size_t ii = 0; ii < coll.size(); ii++) { + free(touchcmds[ii]); + } +} + +void +SmokeTest::testVersion1() +{ + lcb_error_t err; + struct rvbuf rv; + lcb_server_version_cmd_t cmd; + const lcb_server_version_cmd_t *cmds[] = { &cmd }; + memset(&cmd, 0, sizeof(cmd)); + + err = lcb_server_versions(session, &rv, 1, cmds); + EXPECT_EQ(LCB_SUCCESS, err); + rv.counter = mock->getNumNodes(); + lcb_wait(session); + EXPECT_EQ(LCB_SUCCESS, rv.error); + EXPECT_EQ(0, rv.counter); +} + +lcb_error_t +SmokeTest::testMissingBucket() +{ + destroySession(); + // create a new session + lcb_create_st cropts; + mock->makeConnectParams(cropts); + cropts.v.v2.transports = transports; + cropts.v.v2.bucket = "nonexist"; + cropts.v.v2.user = "nonexist"; + lcb_error_t err; + err = lcb_create(&session, &cropts); + EXPECT_EQ(LCB_SUCCESS, err); + mock->postCreate(session); + + err = lcb_connect(session); + EXPECT_EQ(LCB_SUCCESS, err); + lcb_wait(session); + err = lcb_get_bootstrap_status(session); + EXPECT_NE(LCB_SUCCESS, err); + EXPECT_TRUE(err == LCB_BUCKET_ENOENT||err == LCB_AUTH_ERROR); + destroySession(); + return err; +} + +void +SmokeTest::testSpuriousSaslError() +{ + int iterations = 50; + rvbuf rvs[50]; + int i; + lcb_error_t err; + lcb_store_cmd_t storecmd; + const lcb_store_cmd_t *storecmds[] = { &storecmd }; + + for (i = 0; i < iterations; i++) { + rvs[i].counter = 999; + memset(&storecmd, 0, sizeof(storecmd)); + storecmd.v.v0.key = "KEY"; + storecmd.v.v0.nkey = strlen((const char *)storecmd.v.v0.key); + storecmd.v.v0.bytes = "KEY"; + storecmd.v.v0.nbytes = strlen((const char *)storecmd.v.v0.bytes); + storecmd.v.v0.operation = LCB_SET; + err = lcb_store(session, rvs + i, 1, storecmds); + EXPECT_EQ(LCB_SUCCESS, err); + } + lcb_wait(session); + + for (i = 0; i < iterations; i++) { + const char *errinfo = NULL; + if (rvs[i].errorCount != LCB_SUCCESS) { + errinfo = "Did not get success response"; + } else if (rvs[i].key.size() != 3) { + errinfo = "Did not get expected key length"; + } else if (rvs[i].getKeyString() != string("KEY")) { + errinfo = "Weird key size"; + } + if (errinfo) { + EXPECT_FALSE(true) << errinfo; + } + } +} + +void +SmokeTest::connectCommon(const char *password, lcb_error_t expected) +{ + lcb_create_st cropts; + mock->makeConnectParams(cropts, NULL); + + if (password != NULL) { + cropts.v.v2.passwd = password; + } + cropts.v.v2.transports = transports; + lcb_error_t err = lcb_create(&session, &cropts); + EXPECT_EQ(LCB_SUCCESS, err); + + mock->postCreate(session); + err = lcb_connect(session); + EXPECT_EQ(LCB_SUCCESS, err); + lcb_wait(session); + EXPECT_EQ(expected, lcb_get_bootstrap_status(session)); + setupCallbacks(session); +} + +TEST_F(SmokeTest, testMemcachedBucket) +{ + SKIP_UNLESS_MOCK(); + const char *args[] = { "--buckets", "default::memcache", NULL }; + mock = new MockEnvironment(args); + mock->setCCCP(false); + connectCommon(); + testSet1(); + testSet2(); + testGet1(); + testGet2(); + testVersion1(); + + // A bit out of place, but check that replica commands fail at schedule-time + lcb_sched_enter(session); + lcb_CMDGETREPLICA cmd = { 0 }; + LCB_CMD_SET_KEY(&cmd, "key", 3); + lcb_error_t rc; + + cmd.strategy = LCB_REPLICA_FIRST; + rc = lcb_rget3(session, NULL, &cmd); + ASSERT_EQ(LCB_NO_MATCHING_SERVER, rc); + + cmd.strategy = LCB_REPLICA_ALL; + rc = lcb_rget3(session, NULL, &cmd); + ASSERT_EQ(LCB_NO_MATCHING_SERVER, rc); + + cmd.strategy = LCB_REPLICA_SELECT; + cmd.index = 0; + rc = lcb_rget3(session, NULL, &cmd); + ASSERT_EQ(LCB_NO_MATCHING_SERVER, rc); + + + testMissingBucket(); +} + +TEST_F(SmokeTest, testCouchbaseBucket) +{ + SKIP_UNLESS_MOCK(); + const char *args[] = { "--buckets", "default::couchbase", NULL }; + mock = new MockEnvironment(args); + mock->setCCCP(false); + connectCommon(); + testSet1(); + testSet2(); + testGet1(); + testGet2(); + testVersion1(); + testMissingBucket(); +} + +TEST_F(SmokeTest, testSaslBucket) +{ + SKIP_UNLESS_MOCK(); + const char *args[] = { "--buckets", "protected:secret:couchbase", NULL }; + mock = new MockEnvironment(args, "protected"); + mock->setCCCP(false); + + + testMissingBucket(); + + connectCommon("secret"); + testSpuriousSaslError(); + + destroySession(); + connectCommon("incorrect", LCB_AUTH_ERROR); + destroySession(); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_subdoc.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_subdoc.cc new file mode 100644 index 00000000..6ac8ec77 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_subdoc.cc @@ -0,0 +1,822 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2015 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include +#include +#include "iotests.h" +#include + +namespace std { +inline ostream& operator<<(ostream& os, const lcb_error_t& rc) { + os << "LcbError<"; + os << std::hex << static_cast(rc); + os << " ("; + os << lcb_strerror(NULL, rc); + os << ")>"; + return os; +} +} + +class SubdocUnitTest : public MockUnitTest { +public: + SubdocUnitTest() { + key = "subdocItem"; + value = "{" + "\"dictkey\":\"dictval\"," + "\"array\":[" + "1,2,3,4,[10,20,30,[100,200,300]]" + "]" + "}"; + + nonJsonKey = "nonJsonItem"; + } +protected: + std::string key; + std::string value; + std::string nonJsonKey; + bool createSubdocConnection(HandleWrap& hw, lcb_t& instance); +}; + +struct Result { + lcb_error_t rc; + lcb_CAS cas; + std::string value; + int index; + + Result() { + clear(); + } + + Result(const lcb_SDENTRY *ent) { + assign(ent); + } + + void clear() { + rc = LCB_ERROR; + cas = 0; + index = -1; + value.clear(); + } + void assign(const lcb_SDENTRY *ent) { + rc = ent->status; + index = ent->index; + if (ent->nvalue) { + value.assign(reinterpret_cast(ent->value), ent->nvalue); + } + } +}; + +struct MultiResult { + std::vector results; + lcb_CAS cas; + lcb_error_t rc; + unsigned cbtype; + bool is_single; + + void clear() { + cas = 0; + results.clear(); + cbtype = 0; + rc = LCB_AUTH_CONTINUE; + is_single = false; + } + + size_t size() const { + return results.size(); + } + + const Result& operator[](size_t ix) const { + if (cbtype == LCB_CALLBACK_SDMUTATE) { + for (size_t ii = 0; ii < results.size(); ++ii) { + if (results[ix].index == ix) { + return results[ix]; + } + + // Force bad index behavior + return results[results.size()]; + } + } + return results[ix]; + } + + const std::string& single_value() const { + return results[0].value; + } + + MultiResult() { clear(); } +}; + +static +::testing::AssertionResult +verifySingleOk(const char *, const MultiResult& mr, const char *value = NULL) +{ + using namespace ::testing; + if (!mr.is_single) { + return AssertionFailure() << "RESP_F_SDSINGLE not set!"; + } + + if (mr.rc != LCB_SUCCESS) { + if (mr.rc == LCB_SUBDOC_MULTI_FAILURE) { + if (!mr.size()) { + return AssertionFailure() << "Top-level MULTI_FAILURE with no results"; + } else { + return AssertionFailure() << "Got MULTI_FAILURE with sub-code: " << mr[0].rc; + } + } + return AssertionFailure() << "Top-level error code failed. " << mr.rc; + } + if (mr.size() != 1) { + return AssertionFailure() << "Expected a single result. Got " << mr.size(); + } + + if (mr[0].rc != LCB_SUCCESS) { + return AssertionFailure() << "Nested error code is " << mr[0].rc; + } + if (!mr.cas) { + return AssertionFailure() << "Got zero CAS for successful op"; + } + if (value != NULL) { + if (value != mr.single_value()) { + return AssertionFailure() << + "Expected match: '" << value << "' Got '" << mr.single_value() << "'"; + } + } else if (!mr.single_value().empty()) { + return AssertionFailure() << "Expected empty value. Got " << mr.single_value(); + } + + return AssertionSuccess(); +} + +static +::testing::AssertionResult +verifySingleOk(const char *, const char *, const MultiResult& mr, const char *value) +{ + return verifySingleOk(NULL, mr, value); +} + +static +::testing::AssertionResult +verifySingleError(const char *, const char *, const MultiResult& mr, lcb_error_t exp) +{ + using namespace ::testing; + if (!mr.is_single) { + return AssertionFailure() << "RESP_F_SDSINGLE not set!"; + } + if (mr.rc != LCB_SUBDOC_MULTI_FAILURE) { + return AssertionFailure() << + "Top-level error code is not MULTI_FAILURE. Got" << + mr.rc; + } + if (mr.size() != 1) { + return AssertionFailure() << "Expected single result. Got " << mr.size(); + } + if (mr[0].rc != exp) { + return AssertionFailure() << "Expected sub-error " << exp << ". Got << " << mr.rc; + } + return AssertionSuccess(); +} + +#define ASSERT_SD_OK(res) ASSERT_PRED_FORMAT1(verifySingleOk, res) +#define ASSERT_SD_VAL(res, val) ASSERT_PRED_FORMAT2(verifySingleOk, res, val) +#define ASSERT_SD_ERR(res, err) ASSERT_PRED_FORMAT2(verifySingleError, res, err) + +extern "C" { +static void +subdocCallback(lcb_t, int cbtype, const lcb_RESPBASE *rb) +{ + MultiResult *mr = reinterpret_cast(rb->cookie); + const lcb_RESPSUBDOC *resp = reinterpret_cast(rb); + + mr->rc = rb->rc; + if (rb->rc == LCB_SUCCESS) { + mr->cas = resp->cas; + } + if (resp->rflags & LCB_RESP_F_SDSINGLE) { + mr->is_single = true; + } + size_t iterval = 0; + lcb_SDENTRY cur_res; + while (lcb_sdresult_next(resp, &cur_res, &iterval)) { + mr->results.push_back(Result(&cur_res)); + } +} +} + +bool +SubdocUnitTest::createSubdocConnection(HandleWrap& hw, lcb_t& instance) +{ + createConnection(hw, instance); + lcb_install_callback3(instance, LCB_CALLBACK_SDMUTATE, subdocCallback); + lcb_install_callback3(instance, LCB_CALLBACK_SDLOOKUP, subdocCallback); + + lcb_SDSPEC spec = { 0 }; + lcb_CMDSUBDOC cmd = { 0 }; + LCB_CMD_SET_KEY(&cmd, "key", 3); + cmd.specs = &spec; + cmd.nspecs = 1; + + spec.sdcmd = LCB_SDCMD_GET; + LCB_SDSPEC_SET_PATH(&spec, "pth", 3); + + MultiResult res; + lcb_error_t rc = lcb_subdoc3(instance, &res, &cmd); + EXPECT_EQ(LCB_SUCCESS, rc); + if (rc != LCB_SUCCESS) { + return false; + } + lcb_wait(instance); + + if (res.rc == LCB_NOT_SUPPORTED || res.rc == LCB_UNKNOWN_COMMAND) { + return false; + } + + storeKey(instance, key, value); + storeKey(instance, nonJsonKey, "non-json-value"); + + return true; +} + +#define CREATE_SUBDOC_CONNECTION(hw, instance) \ + do { \ + if (!createSubdocConnection(hw, instance)) { \ + fprintf(stderr, "Subdoc not supported on cluster!\n"); \ + return; \ + } \ + } while (0); + +template lcb_error_t +schedwait(lcb_t instance, MultiResult *res, const T *cmd, + lcb_error_t (*fn)(lcb_t, const void *, const T*)) +{ + res->clear(); + lcb_error_t rc = fn(instance, res, cmd); + if (rc == LCB_SUCCESS) { + lcb_wait(instance); + } + return rc; +} + +static ::testing::AssertionResult +verifyPathValue(const char *, const char *, const char *, const char *, + lcb_t instance, const std::string& docid, const char *path, const char *exp) +{ + using namespace ::testing; + MultiResult mr; + lcb_CMDSUBDOC cmd = { 0 }; + lcb_SDSPEC spec = { 0 }; + LCB_CMD_SET_KEY(&cmd, docid.c_str(), docid.size()); + spec.sdcmd = LCB_SDCMD_GET; + LCB_SDSPEC_SET_PATH(&spec, path, strlen(path)); + cmd.specs = &spec; + cmd.nspecs = 1; + lcb_error_t rc = schedwait(instance, &mr, &cmd, lcb_subdoc3); + if (rc != LCB_SUCCESS) { + return AssertionFailure() << "Couldn't schedule operation: " << rc; + } + return verifySingleOk(NULL, NULL, mr, exp); +} + +#define ASSERT_PATHVAL_EQ(exp, instance, docid, path) \ + ASSERT_PRED_FORMAT4(verifyPathValue, instance, docid, path, exp) + +TEST_F(SubdocUnitTest, testSdGetExists) +{ + HandleWrap hw; + lcb_t instance; + CREATE_SUBDOC_CONNECTION(hw, instance); + + MultiResult res; + lcb_error_t rc; + lcb_SDSPEC spec = { 0 }; + lcb_CMDSUBDOC cmd = { 0 }; + LCB_CMD_SET_KEY(&cmd, key.c_str(), key.size()); + cmd.specs = &spec; + cmd.nspecs = 1; + + LCB_SDSPEC_SET_PATH(&spec, "dictkey", strlen("dictkey")); + // get + spec.sdcmd = LCB_SDCMD_GET; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_VAL(res, "\"dictval\""); + // exists + spec.sdcmd = LCB_SDCMD_EXISTS; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_OK(res); + + LCB_SDSPEC_SET_PATH(&spec, "array", strlen("array")); + // get + spec.sdcmd = LCB_SDCMD_GET; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_VAL(res, "[1,2,3,4,[10,20,30,[100,200,300]]]"); + // exists + spec.sdcmd = LCB_SDCMD_EXISTS; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd , lcb_subdoc3)); + ASSERT_SD_OK(res); + + LCB_SDSPEC_SET_PATH(&spec, "array[0]", strlen("array[0]")); + // get + spec.sdcmd = LCB_SDCMD_GET; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_VAL(res, "1"); + // exists + spec.sdcmd = LCB_SDCMD_EXISTS; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_OK(res); + + LCB_SDSPEC_SET_PATH(&spec, "non-exist", strlen("non-exist")); + // get + spec.sdcmd = LCB_SDCMD_GET; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_ERR(res, LCB_SUBDOC_PATH_ENOENT); + // exists + spec.sdcmd = LCB_SDCMD_EXISTS; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_ERR(res, LCB_SUBDOC_PATH_ENOENT); + + LCB_CMD_SET_KEY(&cmd, "non-exist", strlen("non-exist")); + // get + spec.sdcmd = LCB_SDCMD_GET; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_EQ(LCB_KEY_ENOENT, res.rc) << "Get non-exist document"; + // exists + spec.sdcmd = LCB_SDCMD_EXISTS; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_EQ(LCB_KEY_ENOENT, res.rc); + + // Store non-JSON document + LCB_CMD_SET_KEY(&cmd, nonJsonKey.c_str(), nonJsonKey.size()); + + // Get + spec.sdcmd = LCB_SDCMD_GET; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_ERR(res, LCB_SUBDOC_DOC_NOTJSON); + // exists + spec.sdcmd = LCB_SDCMD_EXISTS; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_ERR(res, LCB_SUBDOC_DOC_NOTJSON); + + // Restore the key back to the document.. + LCB_CMD_SET_KEY(&cmd, key.c_str(), key.size()); + + // Invalid paths + spec.sdcmd = LCB_SDCMD_GET; + LCB_SDSPEC_SET_PATH(&spec, "invalid..path", strlen("invalid..path")); + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_ERR(res, LCB_SUBDOC_PATH_EINVAL); + + LCB_SDSPEC_SET_PATH(&spec, "invalid[-2]", strlen("invalid[-2]")); + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_ERR(res, LCB_SUBDOC_PATH_EINVAL); + + // Test negative paths + LCB_SDSPEC_SET_PATH(&spec, "array[-1][-1][-1]", strlen("array[-1][-1][-1]")); + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_VAL(res, "300"); + + // Test nested arrays + LCB_SDSPEC_SET_PATH(&spec, "array[4][3][2]", strlen("array[4][3][2]")); + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_VAL(res, "300"); + ASSERT_EQ("300", res.single_value()); + + // Test path mismatch + LCB_SDSPEC_SET_PATH(&spec, "array.key", strlen("array.key")); + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_ERR(res, LCB_SUBDOC_PATH_MISMATCH); +} + +TEST_F(SubdocUnitTest, testSdStore) +{ + HandleWrap hw; + lcb_t instance; + lcb_error_t rc; + CREATE_SUBDOC_CONNECTION(hw, instance); + lcb_CMDSUBDOC cmd = { 0 }; + lcb_SDSPEC spec = { 0 }; + + cmd.specs = &spec; + cmd.nspecs = 1; + + LCB_CMD_SET_KEY(&cmd, key.c_str(), key.size()); + LCB_SDSPEC_SET_PATH(&spec, "newpath", strlen("newpath")); + LCB_SDSPEC_SET_VALUE(&spec, "123", strlen("123")); + MultiResult res; + + // Insert + spec.sdcmd = LCB_SDCMD_DICT_ADD; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_OK(res); + + spec.sdcmd = LCB_SDCMD_DICT_ADD; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_ERR(res, LCB_SUBDOC_PATH_EEXISTS); + + spec.sdcmd = LCB_SDCMD_DICT_UPSERT; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_OK(res); + // See if our value actually matches + ASSERT_PATHVAL_EQ("123", instance, key, "newpath"); + + // Try with a bad CAS + cmd.cas = res.cas + 1; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_EQ(LCB_KEY_EEXISTS, res.rc); + cmd.cas = 0; // Reset CAS + + // Try to add a compound value + LCB_SDSPEC_SET_PATH(&spec, "dict", strlen("dict")); + const char *v = "{\"key\":\"value\"}"; + LCB_SDSPEC_SET_VALUE(&spec, v, strlen(v)); + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_OK(res); + // Get it back + ASSERT_PATHVAL_EQ("\"value\"", instance, key, "dict.key"); + + // Try to insert a non-JSON value + LCB_SDSPEC_SET_VALUE(&spec, "non-json", strlen("non-json")); + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_ERR(res, LCB_SUBDOC_VALUE_CANTINSERT); + + const char *p = "parent.with.missing.children"; + + // Intermediate paths + LCB_SDSPEC_SET_PATH(&spec, p, strlen(p)); + LCB_SDSPEC_SET_VALUE(&spec, "null", strlen("null")); + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_ERR(res, LCB_SUBDOC_PATH_ENOENT); + + // set MKINTERMEDIATES (MKDIR_P) + spec.options = LCB_SDSPEC_F_MKINTERMEDIATES; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_OK(res); + // Should succeed now.. + ASSERT_PATHVAL_EQ("null", instance, key, p); + + spec.options = 0; + // Test replace + spec.sdcmd = LCB_SDCMD_REPLACE; + LCB_SDSPEC_SET_PATH(&spec, "dict", strlen("dict")); + LCB_SDSPEC_SET_VALUE(&spec, "123", strlen("123")); + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_OK(res); + + // Try replacing a non-existing path + LCB_SDSPEC_SET_PATH(&spec, "non-exist", strlen("non-exist")); + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_ERR(res, LCB_SUBDOC_PATH_ENOENT); + + // Try replacing root element. Invalid path for operation + LCB_SDSPEC_SET_PATH(&spec, "", 0); + ASSERT_EQ(LCB_EMPTY_PATH, schedwait(instance, &res, &cmd, lcb_subdoc3)); + + // Try replacing array element + LCB_SDSPEC_SET_PATH(&spec, "array[1]", strlen("array[1]")); + LCB_SDSPEC_SET_VALUE(&spec, "true", strlen("true")); + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_OK(res); + ASSERT_PATHVAL_EQ("true", instance, key, "array[1]"); +} + +TEST_F(SubdocUnitTest, testMkdoc) { + HandleWrap hw; + lcb_t instance; + lcb_CMDSUBDOC cmd = { 0 }; + lcb_SDSPEC spec = { 0 }; + MultiResult res; + + CREATE_SUBDOC_CONNECTION(hw, instance); + + // Remove the item first + removeKey(instance, key); + + LCB_CMD_SET_KEY(&cmd, key.c_str(), key.size()); + cmd.specs = &spec; + cmd.nspecs = 1; + + LCB_SDSPEC_SET_PATH(&spec, "pth", 3); + LCB_SDSPEC_SET_VALUE(&spec, "123", 3); + spec.options |= LCB_SDSPEC_F_MKDOCUMENT; + spec.sdcmd = LCB_SDCMD_DICT_UPSERT; + + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_PATHVAL_EQ("123", instance, key, "pth"); +} + +TEST_F(SubdocUnitTest, testUnique) +{ + HandleWrap hw; + lcb_t instance; + lcb_CMDSUBDOC cmd = { 0 }; + lcb_SDSPEC spec = { 0 }; + MultiResult res; + + CREATE_SUBDOC_CONNECTION(hw, instance); + + LCB_CMD_SET_KEY(&cmd, key.c_str(), key.size()); + cmd.specs = &spec; + cmd.nspecs = 1; + + // Test array operations: ADD_UNIQUE + LCB_SDSPEC_SET_PATH(&spec, "a", strlen("a")); + LCB_SDSPEC_SET_VALUE(&spec, "1", strlen("1")); + spec.sdcmd = LCB_SDCMD_ARRAY_ADD_UNIQUE; + spec.options |= LCB_SDSPEC_F_MKINTERMEDIATES; + + // Push to a non-existent array (without _P) + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_OK(res); + cmd.cmdflags = 0; + + // Get the item back + ASSERT_PATHVAL_EQ("1", instance, key, "a[0]"); + + // Try adding the item again + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_ERR(res, LCB_SUBDOC_PATH_EEXISTS); + + // Try adding a primitive + LCB_SDSPEC_SET_VALUE(&spec, "{}", strlen("{}")); + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_ERR(res, LCB_SUBDOC_VALUE_CANTINSERT); + + // Add the primitive using append + spec.sdcmd = LCB_SDCMD_ARRAY_ADD_LAST; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_OK(res); + ASSERT_PATHVAL_EQ("{}", instance, key, "a[-1]"); + + spec.sdcmd = LCB_SDCMD_ARRAY_ADD_UNIQUE; + LCB_SDSPEC_SET_VALUE(&spec, "null", strlen("null")); + // Add unique to array with non-primitive + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_ERR(res, LCB_SUBDOC_PATH_MISMATCH); +} + +TEST_F(SubdocUnitTest, testCounter) +{ + HandleWrap hw; + lcb_t instance; + lcb_CMDSUBDOC cmd = { 0 }; + lcb_SDSPEC spec = { 0 }; + lcb_error_t rc; + MultiResult res; + + CREATE_SUBDOC_CONNECTION(hw, instance); + cmd.specs = &spec; + cmd.nspecs = 1; + + LCB_CMD_SET_KEY(&cmd, key.c_str(), key.size()); + LCB_SDSPEC_SET_PATH(&spec, "counter", strlen("counter")); + LCB_SDSPEC_SET_VALUE(&spec, "42", 2); + spec.sdcmd = LCB_SDCMD_COUNTER; + + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_VAL(res, "42"); + // Try it again + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_VAL(res, "84"); + + static const char *si64max = "9223372036854775807"; + // Use a large value + LCB_SDSPEC_SET_VALUE(&spec, si64max, strlen(si64max)); + spec.sdcmd = LCB_SDCMD_DICT_UPSERT; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_OK(res); + ASSERT_PATHVAL_EQ(si64max, instance, key, "counter"); + + // Try to increment by 1 + spec.sdcmd = LCB_SDCMD_COUNTER; + LCB_SDSPEC_SET_VALUE(&spec, "1", 1); + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_ERR(res, LCB_SUBDOC_BAD_DELTA); + + // Try to use an already large number (so the number is too big on the server) + std::string biggerNum(si64max); + biggerNum += "999999999999999999999999999999"; + LCB_SDSPEC_SET_VALUE(&spec, biggerNum.c_str(), biggerNum.size()); + spec.sdcmd = LCB_SDCMD_DICT_UPSERT; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_OK(res); + + // Try the counter op again + LCB_SDSPEC_SET_VALUE(&spec, "1", 1); + spec.sdcmd = LCB_SDCMD_COUNTER; + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_ERR(res, LCB_SUBDOC_NUM_ERANGE); + + // Try the counter op with a non-numeric existing value + LCB_SDSPEC_SET_PATH(&spec, "dictkey", strlen("dictkey")); + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_ERR(res, LCB_SUBDOC_PATH_MISMATCH); + + // Reset the value again to 0 + spec.sdcmd = LCB_SDCMD_DICT_UPSERT; + LCB_SDSPEC_SET_PATH(&spec, "counter", strlen("counter")); + LCB_SDSPEC_SET_VALUE(&spec, "0", 1); + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_OK(res); + ASSERT_EQ(LCB_SUCCESS, res.rc); + + // Try decrement + spec.sdcmd = LCB_SDCMD_COUNTER; + LCB_SDSPEC_SET_PATH(&spec, "counter", strlen("counter")); + LCB_SDSPEC_SET_VALUE(&spec, "-42", 3) + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_SD_VAL(res, "-42"); + // Try it again + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &res, &cmd, lcb_subdoc3)); + ASSERT_EQ(LCB_SUCCESS, res.rc); + ASSERT_SD_VAL(res, "-84"); +} + +TEST_F(SubdocUnitTest, testMultiLookup) +{ + HandleWrap hw; + lcb_t instance; + CREATE_SUBDOC_CONNECTION(hw, instance); + + MultiResult mr; + lcb_error_t rc; + + lcb_CMDSUBDOC mcmd = { 0 }; + mcmd.multimode = LCB_SDMULTI_MODE_LOOKUP; + LCB_CMD_SET_KEY(&mcmd, key.c_str(), key.size()); + + lcb_SDSPEC specs[4] = { { 0 } }; + specs[0].sdcmd = LCB_SDCMD_GET; + LCB_SDSPEC_SET_PATH(&specs[0], "dictkey", strlen("dictkey")); + + specs[1].sdcmd = LCB_SDCMD_EXISTS; + LCB_SDSPEC_SET_PATH(&specs[1], "array[0]", strlen("array[0]")); + + specs[2].sdcmd = LCB_SDCMD_GET; + LCB_SDSPEC_SET_PATH(&specs[2], "nonexist", strlen("nonexist")); + + specs[3].sdcmd = LCB_SDCMD_GET; + LCB_SDSPEC_SET_PATH(&specs[3], "array[1]", strlen("array[1]")); + + mcmd.specs = specs; + mcmd.nspecs = 4; + rc = lcb_subdoc3(instance, &mr, &mcmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + + ASSERT_EQ(LCB_SUBDOC_MULTI_FAILURE, mr.rc); + ASSERT_EQ(4, mr.results.size()); +// ASSERT_NE(0, mr.cas); + + ASSERT_EQ("\"dictval\"", mr.results[0].value); + ASSERT_EQ(LCB_SUCCESS, mr.results[0].rc); + + ASSERT_TRUE(mr.results[1].value.empty()); + ASSERT_EQ(LCB_SUCCESS, mr.results[1].rc); + + ASSERT_TRUE(mr.results[2].value.empty()); + ASSERT_EQ(LCB_SUBDOC_PATH_ENOENT, mr.results[2].rc); + + ASSERT_EQ("2", mr.results[3].value); + ASSERT_EQ(LCB_SUCCESS, mr.results[0].rc); + + // Test multi lookups with bad command types + int erridx = 0; + specs[1].sdcmd = LCB_SDCMD_REMOVE; + mcmd.error_index = &erridx; + rc = lcb_subdoc3(instance, NULL, &mcmd); + ASSERT_EQ(LCB_OPTIONS_CONFLICT, rc); + ASSERT_EQ(1, erridx); + // Reset it to its previous command + specs[1].sdcmd = LCB_SDCMD_GET; + + // Test multi lookups with missing key + std::string missing_key("missing-key"); + removeKey(instance, missing_key); + + mr.clear(); + LCB_CMD_SET_KEY(&mcmd, missing_key.c_str(), missing_key.size()); + rc = lcb_subdoc3(instance, &mr, &mcmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_EQ(LCB_KEY_ENOENT, mr.rc); + ASSERT_TRUE(mr.results.empty()); +} + +TEST_F(SubdocUnitTest, testMultiMutations) +{ + HandleWrap hw; + lcb_t instance; + CREATE_SUBDOC_CONNECTION(hw, instance); + + lcb_CMDSUBDOC mcmd = { 0 }; + std::vector specs; + LCB_CMD_SET_KEY(&mcmd, key.c_str(), key.size()); + mcmd.multimode = LCB_SDMULTI_MODE_MUTATE; + + MultiResult mr; + lcb_error_t rc; + + lcb_SDSPEC spec = { 0 }; + LCB_SDSPEC_SET_PATH(&spec, "newPath", strlen("newPath")); + LCB_SDSPEC_SET_VALUE(&spec, "true", strlen("true")); + spec.sdcmd = LCB_SDCMD_DICT_UPSERT; + specs.push_back(spec); + + LCB_SDSPEC_SET_PATH(&spec, "counter", strlen("counter")); + LCB_SDSPEC_SET_VALUE(&spec, "42", 2) + spec.sdcmd = LCB_SDCMD_COUNTER; + specs.push_back(spec); + mcmd.specs = &specs[0]; + mcmd.nspecs = specs.size(); + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &mr, &mcmd, lcb_subdoc3)); + ASSERT_EQ(LCB_SUCCESS, mr.rc); + + // COUNTER returns a value + ASSERT_EQ(1, mr.results.size()); + ASSERT_EQ("42", mr.results[0].value); + ASSERT_EQ(1, mr.results[0].index); + ASSERT_EQ(LCB_SUCCESS, mr.results[0].rc); + + // Ensure the parameters were encoded correctly.. + ASSERT_PATHVAL_EQ("true", instance, key, "newPath"); + ASSERT_PATHVAL_EQ("42", instance, key, "counter"); + + // New context. Try with mismatched commands + specs.clear(); + + LCB_SDSPEC_INIT(&spec, LCB_SDCMD_GET, "p", 1, NULL, 0); + specs.push_back(spec); + int error_index = 0; + mcmd.error_index = &error_index; + mcmd.specs = &specs[0]; + mcmd.nspecs = specs.size(); + rc = lcb_subdoc3(instance, NULL, &mcmd); + ASSERT_EQ(LCB_OPTIONS_CONFLICT, rc); + ASSERT_EQ(0, error_index); + + specs.clear(); + LCB_SDSPEC_INIT(&spec, LCB_SDCMD_REPLACE, "newPath", strlen("newPath"), "null", 4); + specs.push_back(spec); + LCB_SDSPEC_SET_PATH(&spec, "nested.nonexist", strlen("nested.nonexist")); + specs.push_back(spec); + LCB_SDSPEC_SET_PATH(&spec, "bad..bad", strlen("bad..path")); + specs.push_back(spec); + + mcmd.specs = &specs[0]; + mcmd.nspecs = specs.size(); + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &mr, &mcmd, lcb_subdoc3)); + ASSERT_EQ(LCB_SUBDOC_MULTI_FAILURE, mr.rc); + ASSERT_EQ(1, mr.size()); + ASSERT_EQ(LCB_SUBDOC_PATH_ENOENT, mr.results[0].rc); + ASSERT_EQ(1, mr.results[0].index); +} + +TEST_F(SubdocUnitTest, testGetCount) { + HandleWrap hw; + lcb_t instance; + lcb_CMDSUBDOC cmd = { 0 }; + lcb_SDSPEC spec = { 0 }; + lcb_error_t rc; + MultiResult mres; + + CREATE_SUBDOC_CONNECTION(hw, instance); + LCB_CMD_SET_KEY(&cmd, key.c_str(), key.size()); + + cmd.specs = &spec; + cmd.nspecs = 1; + LCB_SDSPEC_SET_PATH(&spec, "", 0); + spec.sdcmd = LCB_SDCMD_GET_COUNT; + + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &mres, &cmd, lcb_subdoc3)); + ASSERT_SD_VAL(mres, "2"); + + // Use this within an array of specs + lcb_SDSPEC specs[2]; + memset(specs, 0, sizeof specs); + cmd.specs = specs; + cmd.nspecs = 2; + + LCB_SDSPEC_SET_PATH(&specs[0], "404", 3); + specs[0].sdcmd = LCB_SDCMD_GET_COUNT; + LCB_SDSPEC_SET_PATH(&specs[1], "array", strlen("array")); + specs[1].sdcmd = LCB_SDCMD_GET_COUNT; + + ASSERT_EQ(LCB_SUCCESS, schedwait(instance, &mres, &cmd, lcb_subdoc3)); + ASSERT_EQ(LCB_SUBDOC_MULTI_FAILURE, mres.rc); + + ASSERT_EQ(LCB_SUBDOC_PATH_ENOENT, mres.results[0].rc); + ASSERT_EQ(LCB_SUCCESS, mres.results[1].rc); + ASSERT_EQ("5", mres.results[1].value); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_syncmode.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_syncmode.cc new file mode 100644 index 00000000..16d11839 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_syncmode.cc @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "iotests.h" + +extern "C" { + static void error_callback(lcb_t, lcb_error_t err, const char *) + { + ASSERT_EQ(LCB_SUCCESS, err); + } + + static void store_callback(lcb_t, const void *cookie, + lcb_storage_t operation, + lcb_error_t error, + const lcb_store_resp_t *) + { + int *counter = (int *)cookie; + ASSERT_EQ(LCB_SET, operation); + ASSERT_EQ(LCB_SUCCESS, error); + ++(*counter); + } + +} + +class SyncmodeUnitTest : public MockUnitTest +{ +protected: + void createConnection(lcb_t &instance) { + MockEnvironment::getInstance()->createConnection(instance); + (void)lcb_set_error_callback(instance, error_callback); + (void)lcb_behavior_set_syncmode(instance, LCB_SYNCHRONOUS); + ASSERT_EQ(LCB_SUCCESS, lcb_connect(instance)); + } +}; + +TEST_F(SyncmodeUnitTest, testSet) +{ + lcb_t instance; + createConnection(instance); + (void)lcb_set_store_callback(instance, store_callback); + + int counter = 0; + std::string key("SyncmodeUnitTest::testSet"); + std::string value("Hello World"); + lcb_store_cmd_t cmd(LCB_SET, key.data(), key.length(), + value.data(), value.length()); + lcb_store_cmd_t *cmds[] = { &cmd }; + EXPECT_EQ(LCB_SUCCESS, lcb_store(instance, &counter, 1, cmds)); + ASSERT_EQ(1, counter); + lcb_destroy(instance); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_views.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_views.cc new file mode 100644 index 00000000..fb4e007f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/t_views.cc @@ -0,0 +1,405 @@ +#include "config.h" +#include "iotests.h" +#include +#include +#include +#include "contrib/cJSON/cJSON.h" + +namespace { + +class ViewsUnitTest : public MockUnitTest +{ +protected: + void SetUp() { } + void TearDown() { } + void connectBeerSample(HandleWrap& hw, lcb_t& instance, bool first=true); +}; + +using std::string; +using std::vector; + +extern "C" { +static void bktCreateCb(lcb_t, int, const lcb_RESPBASE *rb) { + const lcb_RESPHTTP *htr = (const lcb_RESPHTTP *)rb; + ASSERT_EQ(LCB_SUCCESS, htr->rc); + ASSERT_TRUE(htr->htstatus > 199 && htr->htstatus < 300); +} +} + +void +ViewsUnitTest::connectBeerSample(HandleWrap& hw, lcb_t& instance, bool first) +{ + lcb_create_st crparams; + lcb_create_st crparamsAdmin; + MockEnvironment::getInstance()->makeConnectParams(crparams, NULL); + crparamsAdmin = crparams; + + // We could do CCCP if we really cared.. but it's simpler and makes + // the logs cleaner. + crparams.v.v2.bucket = "beer-sample"; + crparams.v.v2.user = "beer-sample"; + crparams.v.v2.mchosts = NULL; + lcb_config_transport_t transports[] = { + LCB_CONFIG_TRANSPORT_HTTP, LCB_CONFIG_TRANSPORT_LIST_END }; + crparams.v.v2.transports = transports; + + // See if we can connect: + lcb_error_t rv = tryCreateConnection(hw, instance, crparams); + if (rv == LCB_SUCCESS) { + return; + } else if (!first) { + ASSERT_EQ(LCB_SUCCESS, rv); + } + + ASSERT_TRUE(rv == LCB_BUCKET_ENOENT || rv == LCB_AUTH_ERROR); + hw.destroy(); // Should really be called clear(), since that's what it does + + // Use the management API to load the beer-sample database + crparamsAdmin.v.v2.type = LCB_TYPE_CLUSTER; + crparamsAdmin.v.v2.user = "Administrator"; + crparamsAdmin.v.v2.passwd = "password"; + crparamsAdmin.v.v2.bucket = NULL; + + rv = tryCreateConnection(hw, instance, crparamsAdmin); + ASSERT_EQ(LCB_SUCCESS, rv); + + const char *path = "/sampleBuckets/install"; + const char *body = "[\"beer-sample\"]"; + + lcb_CMDHTTP htcmd = { 0 }; + LCB_CMD_SET_KEY(&htcmd, path, strlen(path)); + + htcmd.body = body; + htcmd.nbody = strlen(body); + htcmd.method = LCB_HTTP_METHOD_POST; + htcmd.type = LCB_HTTP_TYPE_MANAGEMENT; + htcmd.content_type = "application/json"; + lcb_install_callback3(instance, LCB_CALLBACK_HTTP, bktCreateCb); + lcb_sched_enter(instance); + rv = lcb_http3(instance, NULL, &htcmd); + ASSERT_EQ(LCB_SUCCESS, rv); + lcb_sched_leave(instance); + lcb_wait(instance); + hw.destroy(); + + // Now it should all be good, so we can call recursively.. + connectBeerSample(hw, instance, false); +} + +struct ViewRow { + string key; + string value; + string docid; + lcb_RESPGET docContents; + + void clear() { + if (docContents.value) { + lcb_backbuf_unref((lcb_BACKBUF)docContents.bufh); + docContents.value = NULL; + } + } + + ViewRow(const lcb_RESPVIEWQUERY *resp) { + const lcb_RESPGET *rg = resp->docresp; + + if (resp->key != NULL) { + key.assign((const char *)resp->key, resp->nkey); + } + if (resp->value != NULL) { + value.assign((const char *)resp->value, resp->nvalue); + } + + if (resp->docid != NULL) { + docid.assign((const char *)resp->docid, resp->ndocid); + if (rg != NULL) { + string tmpId((const char *)rg->key, rg->nkey); + EXPECT_EQ(tmpId, docid); + docContents = *rg; + lcb_backbuf_ref((lcb_BACKBUF)docContents.bufh); + } else { + memset(&docContents, 0, sizeof docContents); + } + } else { + EXPECT_TRUE(rg == NULL); + memset(&docContents, 0, sizeof docContents); + } + } +}; + +struct ViewInfo { + vector rows; + size_t totalRows; + lcb_error_t err; + short http_status; + + void addRow(const lcb_RESPVIEWQUERY *resp) { + if (err == LCB_SUCCESS && resp->rc != LCB_SUCCESS) { + err = resp->rc; + } + + if (! (resp->rflags & LCB_RESP_F_FINAL)) { + rows.push_back(ViewRow(resp)); + + } else { + if (resp->value != NULL) { + // See if we have a 'value' for the final response + string vBuf(resp->value, resp->nvalue); + cJSON *cj = cJSON_Parse(resp->value); + ASSERT_FALSE(cj == NULL); + cJSON *jTotal = cJSON_GetObjectItem(cj, "total_rows"); + ASSERT_FALSE(jTotal == NULL); + totalRows = jTotal->valueint; + cJSON_Delete(cj); + } + if (resp->htresp) { + http_status = resp->htresp->htstatus; + } + } + } + + void clear() { + for (size_t ii = 0; ii < rows.size(); ii++) { + rows[ii].clear(); + } + rows.clear(); + totalRows = 0; + http_status = 0; + err = LCB_SUCCESS; + } + + ~ViewInfo() { + clear(); + } + + ViewInfo() { + clear(); + } +}; + +extern "C" { +static void viewCallback(lcb_t, int cbtype, const lcb_RESPVIEWQUERY *resp) +{ + EXPECT_EQ(LCB_CALLBACK_VIEWQUERY, cbtype); +// printf("View Callback invoked!\n"); + ViewInfo *info = reinterpret_cast(resp->cookie); + info->addRow(resp); +} +} + + +TEST_F(ViewsUnitTest, testSimpleView) +{ + // Requires beer-sample + HandleWrap hw; + lcb_t instance; + connectBeerSample(hw, instance); + + lcb_CMDVIEWQUERY vq = { 0 }; + lcb_view_query_initcmd(&vq, "beer", "brewery_beers", NULL, viewCallback); + ViewInfo vi; + + lcb_error_t rc = lcb_view_query(instance, &vi, &vq); + ASSERT_EQ(LCB_SUCCESS, rc); + + lcb_wait(instance); + ASSERT_EQ(LCB_SUCCESS, vi.err); + ASSERT_GT(vi.rows.size(), 0U); + ASSERT_EQ(7303, vi.totalRows); + + // Check the row parses correctly: + const ViewRow& row = vi.rows.front(); + // Unquoted docid + ASSERT_EQ("21st_amendment_brewery_cafe", row.docid); + ASSERT_EQ("[\"21st_amendment_brewery_cafe\"]", row.key); + ASSERT_EQ("null", row.value); + + vi.clear(); + + //apply limit + lcb_view_query_initcmd(&vq, "beer", "brewery_beers", "limit=10", viewCallback); + + rc = lcb_view_query(instance, &vi, &vq); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_EQ(LCB_SUCCESS, vi.err); + ASSERT_EQ(10, vi.rows.size()); + ASSERT_EQ(7303, vi.totalRows); + vi.clear(); + + // Set the limit to 0 + lcb_view_query_initcmd(&vq, "beer", "brewery_beers", "limit=0", viewCallback); + rc = lcb_view_query(instance, &vi, &vq); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_EQ(0, vi.rows.size()); + ASSERT_EQ(7303, vi.totalRows); +} + +TEST_F(ViewsUnitTest, testIncludeDocs) { + HandleWrap hw; + lcb_t instance; + lcb_error_t rc; + connectBeerSample(hw, instance); + + ViewInfo vi; + lcb_CMDVIEWQUERY vq = { 0 }; + lcb_view_query_initcmd(&vq, "beer", "brewery_beers", NULL, viewCallback); + vq.cmdflags |= LCB_CMDVIEWQUERY_F_INCLUDE_DOCS; + rc = lcb_view_query(instance, &vi, &vq); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + + // Again, ensure everything is OK + ASSERT_EQ(7303, vi.totalRows); + ASSERT_EQ(7303, vi.rows.size()); + + for (size_t ii = 0; ii < vi.rows.size(); ii++) { + const ViewRow& row = vi.rows[ii]; + ASSERT_FALSE(row.docContents.key == NULL); + ASSERT_EQ(row.docid.size(), row.docContents.nkey); + ASSERT_EQ(LCB_SUCCESS, row.docContents.rc); + ASSERT_NE(0, row.docContents.cas); + } +} + +TEST_F(ViewsUnitTest, testReduce) { + HandleWrap hw; + lcb_t instance; + lcb_error_t rc; + connectBeerSample(hw, instance); + + ViewInfo vi; + lcb_CMDVIEWQUERY vq = { 0 }; + lcb_view_query_initcmd(&vq, "beer", "by_location", NULL, viewCallback); + rc = lcb_view_query(instance, &vi, &vq); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_EQ(1411, vi.totalRows); + ASSERT_EQ(1, vi.rows.size()); + + vi.clear(); + // Try with include_docs + vq.cmdflags |= LCB_CMDVIEWQUERY_F_INCLUDE_DOCS; + rc = lcb_view_query(instance, &vi, &vq); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_EQ(1, vi.rows.size()); + + vi.clear(); + // Try with reduce=false + vq.cmdflags |= LCB_CMDVIEWQUERY_F_INCLUDE_DOCS; + lcb_view_query_initcmd(&vq, "beer", "by_location", "reduce=false&limit=10", viewCallback); + rc = lcb_view_query(instance, &vi, &vq); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_EQ(10, vi.rows.size()); + ASSERT_EQ(1411, vi.totalRows); + + ViewRow* firstRow = &vi.rows[0]; + ASSERT_EQ("[\"Argentina\",\"\",\"Mendoza\"]", firstRow->key); + ASSERT_EQ("1", firstRow->value); + ASSERT_EQ("cervecera_jerome", firstRow->docid); + + // try with grouplevel + vi.clear(); + memset(&vq, 0, sizeof vq); + lcb_view_query_initcmd(&vq, "beer", "by_location", "group_level=1", viewCallback); + rc = lcb_view_query(instance, &vi, &vq); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + + firstRow = &vi.rows[0]; + ASSERT_EQ("[\"Argentina\"]", firstRow->key); + ASSERT_EQ("2", firstRow->value); + ASSERT_TRUE(firstRow->docid.empty()); +} + +TEST_F(ViewsUnitTest, testEngineErrors) { + // Tests various things which can go wrong; basically negative responses + HandleWrap hw; + lcb_t instance; + connectBeerSample(hw, instance); + lcb_error_t rc; + + ViewInfo vi; + lcb_CMDVIEWQUERY cmd = { 0 }; + lcb_view_query_initcmd(&cmd, "nonexist", "nonexist", NULL, viewCallback); + rc = lcb_view_query(instance, &vi, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_EQ(LCB_HTTP_ERROR, vi.err); + ASSERT_EQ(404, vi.http_status); + + vi.clear(); + lcb_view_query_initcmd(&cmd, "beer", "badview", NULL, viewCallback); + rc = lcb_view_query(instance, &vi, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_EQ(LCB_HTTP_ERROR, vi.err); + ASSERT_EQ(404, vi.http_status); + + vi.clear(); + lcb_view_query_initcmd(&cmd, "beer", "brewery_beers", "reduce=true", viewCallback); + rc = lcb_view_query(instance, &vi, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_EQ(LCB_HTTP_ERROR, vi.err); + ASSERT_EQ(400, vi.http_status); +} + +TEST_F(ViewsUnitTest, testOptionValidation) +{ + HandleWrap hw; + lcb_t instance; + connectBeerSample(hw, instance); + + lcb_CMDVIEWQUERY cmd = { 0 }; + ASSERT_EQ(LCB_EINVAL, lcb_view_query(instance, NULL, &cmd)); + + cmd.callback = viewCallback; + ASSERT_EQ(LCB_EINVAL, lcb_view_query(instance, NULL, &cmd)); + + cmd.view = "view"; + cmd.nview = strlen("view"); + ASSERT_EQ(LCB_EINVAL, lcb_view_query(instance, NULL, &cmd)); + + cmd.ddoc = "design"; + cmd.nddoc = strlen("design"); + + // Expect it to fail with flags + cmd.cmdflags |= LCB_CMDVIEWQUERY_F_INCLUDE_DOCS; + cmd.cmdflags |= LCB_CMDVIEWQUERY_F_NOROWPARSE; + ASSERT_EQ(LCB_OPTIONS_CONFLICT, lcb_view_query(instance, NULL, &cmd)); +} + +TEST_F(ViewsUnitTest, testBackslashDocid) +{ + HandleWrap hw; + lcb_t instance; + lcb_error_t rc; + connectBeerSample(hw, instance); + + string key("backslash\\docid"); + string doc("{\"type\":\"brewery\", \"name\":\"Backslash IPA\"}"); + storeKey(instance, key, doc); + + ViewInfo vi; + lcb_CMDVIEWQUERY cmd = { 0 }; + + lcb_view_query_initcmd(&cmd, "beer", "brewery_beers", "stale=false&key=[\"backslash\\\\docid\"]", viewCallback); + rc = lcb_view_query(instance, &vi, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_EQ(LCB_SUCCESS, vi.err); + ASSERT_EQ(1, vi.rows.size()); + ASSERT_EQ(key, vi.rows[0].docid); + + vi.clear(); + cmd.cmdflags = LCB_CMDVIEWQUERY_F_INCLUDE_DOCS; + rc = lcb_view_query(instance, &vi, &cmd); + ASSERT_EQ(LCB_SUCCESS, rc); + lcb_wait(instance); + ASSERT_EQ(1, vi.rows.size()); + ASSERT_EQ(doc.size(), vi.rows[0].docContents.nvalue); +} +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/testutil.cc b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/testutil.cc new file mode 100644 index 00000000..b15e6cd9 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/testutil.cc @@ -0,0 +1,250 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012-2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" + +#include "mock-unit-test.h" +#include "testutil.h" +#include + +/* + * Helper functions + */ +extern "C" { + static void storeKvoCallback(lcb_t, const void *cookie, + lcb_storage_t operation, + lcb_error_t error, + const lcb_store_resp_t *resp) + { + + KVOperation *kvo = (KVOperation *)cookie; + kvo->cbCommon(error); + kvo->result.assignKC(resp, error); + ASSERT_EQ(LCB_SET, operation); + } + + static void getKvoCallback(lcb_t, const void *cookie, + lcb_error_t error, + const lcb_get_resp_t *resp) + { + KVOperation *kvo = (KVOperation *)cookie; + kvo->cbCommon(error); + kvo->result.assign(resp, error); + } + + static void removeKvoCallback(lcb_t, const void *cookie, + lcb_error_t error, + const lcb_remove_resp_t *resp) + { + KVOperation *kvo = (KVOperation *)cookie; + kvo->cbCommon(error); + kvo->result.assignKC(resp, error); + } +} + +void KVOperation::handleInstanceError(lcb_t instance, lcb_error_t err, + const char *) +{ + KVOperation *kvo = reinterpret_cast( + const_cast(lcb_get_cookie(instance))); + kvo->assertOk(err); + kvo->globalErrors.insert(err); +} + +void KVOperation::enter(lcb_t instance) +{ + callbacks.get = lcb_set_get_callback(instance, getKvoCallback); + callbacks.rm = lcb_set_remove_callback(instance, removeKvoCallback); + callbacks.store = lcb_set_store_callback(instance, storeKvoCallback); + oldCookie = lcb_get_cookie(instance); + lcb_set_cookie(instance, this); +} + +void KVOperation::leave(lcb_t instance) +{ + lcb_set_get_callback(instance, callbacks.get); + lcb_set_remove_callback(instance, callbacks.rm); + lcb_set_store_callback(instance, callbacks.store); + lcb_set_cookie(instance, oldCookie); +} + +void KVOperation::assertOk(lcb_error_t err) +{ + if (ignoreErrors) { + return; + } + + if (allowableErrors.empty()) { + ASSERT_EQ(LCB_SUCCESS, err); + return; + } + ASSERT_TRUE(allowableErrors.find(err) != allowableErrors.end()); +} + +void KVOperation::store(lcb_t instance) +{ + lcb_store_cmd_t cmd(LCB_SET, + request->key.data(), request->key.length(), + request->val.data(), request->val.length(), + request->flags, + request->exp, + request->cas, + request->datatype); + lcb_store_cmd_t *cmds[] = { &cmd }; + + enter(instance); + EXPECT_EQ(LCB_SUCCESS, lcb_store(instance, this, 1, cmds)); + EXPECT_EQ(LCB_SUCCESS, lcb_wait(instance)); + leave(instance); + + ASSERT_EQ(1, callCount); + +} + +void KVOperation::remove(lcb_t instance) +{ + lcb_remove_cmd_t cmd(request->key.data(), request->key.length(), + request->cas); + lcb_remove_cmd_t *cmds[] = { &cmd }; + + enter(instance); + EXPECT_EQ(LCB_SUCCESS, lcb_remove(instance, this, 1, cmds)); + EXPECT_EQ(LCB_SUCCESS, lcb_wait(instance)); + leave(instance); + + ASSERT_EQ(1, callCount); + +} + +void KVOperation::get(lcb_t instance) +{ + lcb_get_cmd_t cmd(request->key.data(), request->key.length(), request->exp); + lcb_get_cmd_t *cmds[] = { &cmd }; + + enter(instance); + EXPECT_EQ(LCB_SUCCESS, lcb_get(instance, this, 1, cmds)); + EXPECT_EQ(LCB_SUCCESS, lcb_wait(instance)); + leave(instance); + + ASSERT_EQ(1, callCount); +} + +void storeKey(lcb_t instance, const std::string &key, const std::string &value) +{ + Item req = Item(key, value); + KVOperation kvo = KVOperation(&req); + kvo.store(instance); +} + +void removeKey(lcb_t instance, const std::string &key) +{ + Item req = Item(); + req.key = key; + KVOperation kvo = KVOperation(&req); + kvo.allowableErrors.insert(LCB_SUCCESS); + kvo.allowableErrors.insert(LCB_KEY_ENOENT); + kvo.remove(instance); +} + +void getKey(lcb_t instance, const std::string &key, Item &item) +{ + Item req = Item(); + req.key = key; + KVOperation kvo = KVOperation(&req); + kvo.result.cas = 0xdeadbeef; + + kvo.get(instance); + ASSERT_NE(0xdeadbeef, kvo.result.cas); + item = kvo.result; +} + +void genDistKeys(lcbvb_CONFIG *vbc, std::vector &out) +{ + char buf[1024] = { '\0' }; + int servers_max = lcbvb_get_nservers(vbc); + std::map found_servers; + EXPECT_TRUE(servers_max > 0); + + for (int cur_num = 0; found_servers.size() != servers_max; cur_num++) { + int ksize = sprintf(buf, "VBKEY_%d", cur_num); + int vbid; + int srvix; + lcbvb_map_key(vbc, buf, ksize, &vbid, &srvix); + + if (!found_servers[srvix]) { + out.push_back(std::string(buf)); + found_servers[srvix] = true; + } + } + + EXPECT_EQ(servers_max, out.size()); +} + +void genStoreCommands(const std::vector &keys, + std::vector &cmds, + std::vector &cmdpp) +{ + for (unsigned int ii = 0; ii < keys.size(); ii++) { + lcb_store_cmd_t cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.v.v0.key = keys[ii].c_str(); + cmd.v.v0.nkey = keys[ii].size(); + cmd.v.v0.bytes = cmd.v.v0.key; + cmd.v.v0.nbytes = cmd.v.v0.nkey; + cmd.v.v0.operation = LCB_SET; + cmds.push_back(cmd); + } + + for (unsigned int ii = 0; ii < keys.size(); ii++) { + cmdpp.push_back(&cmds[ii]); + } +} + +/** + * This doesn't _actually_ attempt to make sense of an operation. It simply + * will try to keep the event loop alive. + */ +void doDummyOp(lcb_t& instance) +{ + Item itm("foo", "bar"); + KVOperation kvo(&itm); + kvo.ignoreErrors = true; + kvo.store(instance); +} + +/** + * Dump the item object to a stream + * @param out where to dump the object to + * @param item the item to print + * @return the stream + */ +std::ostream &operator<< (std::ostream &out, const Item &item) +{ + using namespace std; + out << "Key: " << item.key << endl; + if (item.val.length()) { + out << "Value: " << item.val << endl; + } + + out << ios::hex << "CAS: 0x" << item.cas << endl + << "Flags: 0x" << item.flags << endl; + + if (item.err != LCB_SUCCESS) { + out << "Error: " << item.err << endl; + } + + return out; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/iotests/testutil.h b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/testutil.h new file mode 100644 index 00000000..9b524287 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/iotests/testutil.h @@ -0,0 +1,163 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012-2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef TESTS_TESTUTIL_H +#define TESTS_TESTUTIL_H 1 + +#include +#include +#include +struct Item { + void assign(const lcb_get_resp_t *resp, lcb_error_t e = LCB_SUCCESS) { + key.assign((const char *)resp->v.v0.key, resp->v.v0.nkey); + val.assign((const char *)resp->v.v0.bytes, resp->v.v0.nbytes); + flags = resp->v.v0.flags; + cas = resp->v.v0.cas; + datatype = resp->v.v0.datatype; + err = e; + } + + /** + * Extract the key and CAS from a response. + */ + template + void assignKC(const T *resp, lcb_error_t e = LCB_SUCCESS) { + key.assign((const char *)resp->v.v0.key, resp->v.v0.nkey); + cas = resp->v.v0.cas; + err = e; + } + + Item() { + key.clear(); + val.clear(); + + err = LCB_SUCCESS; + flags = 0; + cas = 0; + datatype = 0; + exp = 0; + } + + Item(const std::string &key, + const std::string &value = "", + lcb_cas_t cas = 0) { + + this->key = key; + this->val = value; + this->cas = cas; + + flags = 0; + datatype = 0; + exp = 0; + } + + friend std::ostream &operator<< (std::ostream &out, + const Item &item); + + /** + * Dump the string representation of the item to standard output + */ + void dump() { + std::cout << *this; + } + + std::string key; + std::string val; + lcb_uint32_t flags; + lcb_cas_t cas; + lcb_datatype_t datatype; + lcb_error_t err; + lcb_time_t exp; +}; + +struct KVOperation { + /** The resultant item */ + Item result; + + /** The request item */ + const Item *request; + + /** whether the callback was at all received */ + unsigned callCount; + + /** Acceptable errors during callback */ + std::set allowableErrors; + + /** Errors received from error handler */ + std::set globalErrors; + + void assertOk(lcb_error_t err); + + KVOperation(const Item *request) { + this->request = request; + this->ignoreErrors = false; + callCount = 0; + } + + void clear() { + result = Item(); + callCount = 0; + allowableErrors.clear(); + globalErrors.clear(); + } + + void store(lcb_t instance); + void get(lcb_t instance); + void remove(lcb_t instance); + + void cbCommon(lcb_error_t error) { + callCount++; + if (error != LCB_SUCCESS) { + globalErrors.insert(error); + } + assertOk(error); + } + + static void handleInstanceError(lcb_t, lcb_error_t, const char *); + bool ignoreErrors; + +private: + void enter(lcb_t); + void leave(lcb_t); + const void *oldCookie; + + struct { + lcb_get_callback get; + lcb_store_callback store; + lcb_error_callback err; + lcb_remove_callback rm; + } callbacks; +}; + +void storeKey(lcb_t instance, const std::string &key, const std::string &value); +void removeKey(lcb_t instance, const std::string &key); +void getKey(lcb_t instance, const std::string &key, Item &item); + +/** + * Generate keys which will trigger all the servers in the map. + */ +void genDistKeys(lcbvb_CONFIG* vbc, std::vector &out); +void genStoreCommands(const std::vector &keys, + std::vector &cmds, + std::vector &cmdpp); + +/** + * This doesn't _actually_ attempt to make sense of an operation. It simply + * will try to keep the event loop alive. + */ +void doDummyOp(lcb_t& instance); + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/mc/mctest.h b/couchbase-sys/libcouchbase-2.7.0/tests/mc/mctest.h new file mode 100644 index 00000000..0a26ac97 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/mc/mctest.h @@ -0,0 +1,119 @@ +#include "mc/mcreq.h" +#include "sllist-inl.h" +#include + +#define NUM_PIPELINES 4 + +struct CQWrap : mc_CMDQUEUE { + lcbvb_CONFIG* config; + CQWrap() { + mc_PIPELINE **pll; + pll = (mc_PIPELINE **)malloc(sizeof(*pll) * NUM_PIPELINES); + config = lcbvb_create(); + for (unsigned ii = 0; ii < NUM_PIPELINES; ii++) { + mc_PIPELINE *pipeline = (mc_PIPELINE *)calloc(1, sizeof(*pipeline)); + mcreq_pipeline_init(pipeline); + pll[ii] = pipeline; + } + lcbvb_genconfig(config, NUM_PIPELINES, 3, 1024); + mcreq_queue_init(this); + this->seq = 100; + mcreq_queue_add_pipelines(this, pll, NUM_PIPELINES, config); + free(pll); + } + + ~CQWrap() { + for (int ii = 0; ii < NUM_PIPELINES; ii++) { + mc_PIPELINE *pipeline = pipelines[ii]; + EXPECT_NE(0, netbuf_is_clean(&pipeline->nbmgr)); + EXPECT_NE(0, netbuf_is_clean(&pipeline->reqpool)); + mcreq_pipeline_cleanup(pipeline); + free(pipeline); + } + mcreq_queue_cleanup(this); + lcbvb_destroy(config); + } + + void clearPipelines() { + for (unsigned ii = 0; ii < npipelines; ii++) { + mc_PIPELINE *pipeline = pipelines[ii]; + sllist_iterator iter; + SLLIST_ITERFOR(&pipeline->requests, &iter) { + mc_PACKET *pkt = SLLIST_ITEM(iter.cur, mc_PACKET, slnode); + sllist_iter_remove(&pipeline->requests, &iter); + mcreq_wipe_packet(pipeline, pkt); + mcreq_release_packet(pipeline, pkt); + } + } + } + + void setBufFreeCallback(mcreq_bufdone_fn cb) { + for (unsigned ii = 0; ii < npipelines; ii++) { + pipelines[ii]->buf_done_callback = cb; + } + } + + CQWrap(CQWrap&); +}; + +struct PacketWrap { + mc_PACKET *pkt; + mc_PIPELINE *pipeline; + protocol_binary_request_header hdr; + lcb_CMDBASE cmd; + char *pktbuf; + char *kbuf; + + PacketWrap() { + pkt = NULL; + pipeline = NULL; + pktbuf = NULL; + kbuf = NULL; + memset(&hdr, 0, sizeof(hdr)); + memset(&cmd, 0, sizeof(cmd)); + } + + void setKey(const char *key) { + size_t nkey = strlen(key); + pktbuf = new char[24 + nkey + 1]; + kbuf = pktbuf + 24; + memcpy(kbuf, key, nkey); + kbuf[nkey] = '\0'; + } + + void setContigKey(const char *key) { + setKey(key); + cmd.key.type = LCB_KV_HEADER_AND_KEY; + cmd.key.contig.bytes = pktbuf; + cmd.key.contig.nbytes = strlen(key) + 24; + } + + void setCopyKey(const char *key) { + setKey(key); + LCB_KREQ_SIMPLE(&cmd.key, kbuf, strlen(key)); + } + + void setHeaderSize() { + hdr.request.bodylen = htonl((lcb_uint32_t)strlen(kbuf)); + } + + void copyHeader() { + memcpy(SPAN_BUFFER(&pkt->kh_span), hdr.bytes, sizeof(hdr.bytes)); + } + + void setCookie(void *ptr) { + pkt->u_rdata.reqdata.cookie = ptr; + } + + bool reservePacket(mc_CMDQUEUE *cq) { + lcb_error_t err; + err = mcreq_basic_packet(cq, &cmd, &hdr, 0, &pkt, &pipeline, 0); + return err == LCB_SUCCESS; + } + + ~PacketWrap() { + if (pktbuf != NULL) { + delete[] pktbuf; + } + } +}; diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/mc/pktmaker.h b/couchbase-sys/libcouchbase-2.7.0/tests/mc/pktmaker.h new file mode 100644 index 00000000..0a837819 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/mc/pktmaker.h @@ -0,0 +1,101 @@ +#ifndef PKTMAKER_H +#define PKTMAKER_H + +#include +namespace PacketMaker { + +class Packet { +public: + Packet() { + memset(&hdr_s, 0, sizeof(hdr_s)); + req = &hdr_s; + res = (protocol_binary_response_header *)&hdr_s; + } + + uint8_t magic() const { return req->request.magic; } + void magic(uint8_t mg) { req->request.magic = mg; } + + uint8_t op() const { return req->request.opcode; } + void op(uint8_t cc) { req->request.opcode = cc; } + + uint8_t extlen() const { return req->request.extlen; } + + void opaque(uint32_t seq) { req->request.opaque = seq; } + uint32_t opaque() const { return req->request.opaque; } + + const char *keyptr() const { + return &body[extlen()]; + } + + std::string key() const { + uint16_t len = keylen(); + if (!len) { + return std::string(""); + } + return std::string(keyptr(), len); + } + + uint16_t keylen() const { return ntohs(req->request.keylen); } + + void serialize(std::vector& ret) { + req->request.bodylen = htonl(body.size()); + ret.insert(ret.end(), hdr_s.bytes, hdr_s.bytes + sizeof(hdr_s.bytes)); + ret.insert(ret.end(), body.begin(), body.end()); + } + + void load(std::vector& buf) { + memcpy(&hdr_s.bytes, &buf[0], sizeof(hdr_s.bytes)); + body.assign(&buf[24], &buf[0] + buf.size()); + } + + void setKey(const char *kbuf, uint16_t len) { + body.insert(body.end(), kbuf, kbuf + len); + req->request.keylen = htons((uint16_t)len); + } + + void setValue(const char *val, uint32_t len) { + body.insert(body.end(), val, val+len); + } + +protected: + protocol_binary_request_header *req; + protocol_binary_response_header *res; + + void addExtra(char *extbuf, uint8_t nbuf) { + body.insert(body.begin(), extbuf, extbuf+nbuf); + req->request.extlen += nbuf; + } + + +private: + protocol_binary_request_header hdr_s; + std::vector body; + Packet(Packet&); +}; + +class StorageRequest : public Packet { +public: + StorageRequest(const std::string& key, const std::string& val) : Packet() { + setKey(key.c_str(), (uint16_t)key.size()); + setValue(val.c_str(), (uint32_t)val.size()); + } +}; + +class GetRequest : public Packet { +public: + GetRequest(std::string& key) : Packet() { + setKey(key.c_str(), (uint16_t)key.size()); + } +}; + +class Response : public Packet { +public: + Response(const Packet& request, uint16_t status = 0) : Packet() { + res->response.status = htons(status); + opaque(request.opaque()); + } +}; + +} + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/mc/t_alloc.cc b/couchbase-sys/libcouchbase-2.7.0/tests/mc/t_alloc.cc new file mode 100644 index 00000000..f1db96ae --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/mc/t_alloc.cc @@ -0,0 +1,269 @@ +#include "mctest.h" + +class McAlloc : public ::testing::Test { +protected: + mc_CMDQUEUE cQueue; + + void setupPipeline(mc_PIPELINE *pipeline) { + mcreq_queue_init(&cQueue); + mcreq_pipeline_init(pipeline); + pipeline->parent = &cQueue; + } +}; + +TEST_F(McAlloc, testPipelineFreeAlloc) +{ + mc_PIPELINE pipeline; + memset(&pipeline, 0, sizeof(pipeline)); + mcreq_pipeline_init(&pipeline); + mcreq_pipeline_cleanup(&pipeline); +} + +TEST_F(McAlloc, testPacketFreeAlloc) +{ + mc_PIPELINE pipeline; + mc_PACKET *copied = NULL; + memset(&pipeline, 0, sizeof(pipeline)); + setupPipeline(&pipeline); + + mc_PACKET *packet = mcreq_allocate_packet(&pipeline); + ASSERT_TRUE(packet != NULL); + + mcreq_reserve_header(&pipeline, packet, 24); + + // Check to see that we can also detach a packet and use it after the + // other resources have been released + copied = mcreq_renew_packet(packet); + + + mcreq_wipe_packet(&pipeline, packet); + mcreq_release_packet(&pipeline, packet); + mcreq_pipeline_cleanup(&pipeline); + + // Write to the detached packet. Ensure we don't crash + memset(SPAN_BUFFER(&copied->kh_span), 0xff, copied->kh_span.size); + mcreq_wipe_packet(NULL, copied); + mcreq_release_packet(NULL, copied); +} + +struct dummy_datum { + mc_EPKTDATUM base; + int refcount; +}; +extern "C" { +static void datum_free(mc_EPKTDATUM *epd) { + dummy_datum *dd = (dummy_datum *)epd; + dd->refcount--; +} +} + +TEST_F(McAlloc, testExdataAlloc) +{ + mc_PIPELINE pipeline; + mc_PACKET *copy1, *copy2; + setupPipeline(&pipeline); + mc_PACKET *packet = mcreq_allocate_packet(&pipeline); + mcreq_reserve_header(&pipeline, packet, 24); + + copy1 = mcreq_renew_packet(packet); + ASSERT_FALSE((copy1->flags & MCREQ_F_DETACHED) == 0); + + dummy_datum dd; + dd.base.key = "Dummy"; + dd.base.dtorfn = datum_free; + dd.refcount = 1; + mcreq_epkt_insert((mc_EXPACKET*)copy1, &dd.base); + // Find it back + mc_EPKTDATUM *epd = mcreq_epkt_find((mc_EXPACKET*)copy1, "Dummy"); + ASSERT_FALSE(epd == NULL); + ASSERT_TRUE(epd == &dd.base); + + copy2 = mcreq_renew_packet(copy1); + epd = mcreq_epkt_find((mc_EXPACKET*)copy1, "Dummy"); + ASSERT_TRUE(epd == NULL); + epd = mcreq_epkt_find((mc_EXPACKET*)copy2, "Dummy"); + ASSERT_FALSE(epd == NULL); + + mcreq_wipe_packet(&pipeline, packet); + mcreq_release_packet(&pipeline, packet); + mcreq_wipe_packet(NULL, copy1); + mcreq_release_packet(NULL, copy1); + mcreq_wipe_packet(NULL, copy2); + mcreq_release_packet(NULL, copy2); + ASSERT_EQ(0, dd.refcount); + mcreq_pipeline_cleanup(&pipeline); +} + + +TEST_F(McAlloc, testKeyAlloc) +{ + CQWrap q; + mc_PACKET *packet; + mc_PIPELINE *pipeline; + lcb_CMDBASE cmd; + + protocol_binary_request_header hdr; + memset(&cmd, 0, sizeof(cmd)); + memset(&hdr, 0, sizeof(hdr)); + + cmd.key.contig.bytes = const_cast("Hello"); + cmd.key.contig.nbytes = 5; + + lcb_error_t ret; + ret = mcreq_basic_packet(&q, &cmd, &hdr, 0, &packet, &pipeline, 0); + ASSERT_EQ(LCB_SUCCESS, ret); + ASSERT_TRUE(packet != NULL); + ASSERT_TRUE(pipeline != NULL); + ASSERT_EQ(5, ntohs(hdr.request.keylen)); + + int vb = lcbvb_k2vb(q.config, "Hello", 5); + ASSERT_EQ(vb, ntohs(hdr.request.vbucket)); + + // Copy the header + memcpy(SPAN_BUFFER(&packet->kh_span), &hdr, sizeof(hdr)); + + lcb_VALBUF vreq; + memset(&vreq, 0, sizeof(vreq)); + + const void *key; + lcb_size_t nkey; + // Get back the key we just placed inside the header + mcreq_get_key(packet, &key, &nkey); + ASSERT_EQ(5, nkey); + ASSERT_EQ(0, memcmp(key, "Hello", 5)); + + mcreq_wipe_packet(pipeline, packet); + mcreq_release_packet(pipeline, packet); +} + +TEST_F(McAlloc, testValueAlloc) +{ + CQWrap q; + mc_PACKET *packet; + mc_PIPELINE *pipeline; + lcb_CMDBASE cmd; + protocol_binary_request_header hdr; + lcb_VALBUF vreq; + + memset(&cmd, 0, sizeof(cmd)); + memset(&hdr, 0, sizeof(hdr)); + memset(&vreq, 0, sizeof(vreq)); + + const char *key = "Hello"; + const char *value = "World"; + + + lcb_error_t ret; + cmd.key.contig.bytes = const_cast(key); + cmd.key.contig.nbytes = 5; + vreq.u_buf.contig.bytes = const_cast(value); + vreq.u_buf.contig.nbytes = 5; + + ret = mcreq_basic_packet(&q, &cmd, &hdr, 0, &packet, &pipeline, 0); + ASSERT_EQ(LCB_SUCCESS, ret); + ret = mcreq_reserve_value(pipeline, packet, &vreq); + ASSERT_EQ(ret, LCB_SUCCESS); + ASSERT_EQ(packet->flags, MCREQ_F_HASVALUE); + + ASSERT_EQ(0, memcmp(SPAN_BUFFER(&packet->u_value.single), value, 5)); + ASSERT_NE(SPAN_BUFFER(&packet->u_value.single), value); + mcreq_wipe_packet(pipeline, packet); + mcreq_release_packet(pipeline, packet); + + // Allocate another packet, but this time, use our own reserved value + ret = mcreq_basic_packet(&q, &cmd, &hdr, 0, &packet, &pipeline, 0); + ASSERT_EQ(ret, LCB_SUCCESS); + vreq.vtype = LCB_KV_CONTIG; + ret = mcreq_reserve_value(pipeline, packet, &vreq); + ASSERT_EQ(SPAN_BUFFER(&packet->u_value.single), value); + ASSERT_EQ(MCREQ_F_HASVALUE|MCREQ_F_VALUE_NOCOPY, packet->flags); + mcreq_wipe_packet(pipeline, packet); + mcreq_release_packet(pipeline, packet); + + + nb_IOV iov[2]; + iov[0].iov_base = (void *)value; + iov[0].iov_len = 3; + iov[1].iov_base = (void *)(value + 3); + iov[1].iov_len = 2; + + vreq.u_buf.multi.iov = (lcb_IOV *)iov; + vreq.u_buf.multi.niov = 2; + vreq.vtype = LCB_KV_IOV; + ret = mcreq_basic_packet(&q, &cmd, &hdr, 0, &packet, &pipeline, 0); + ASSERT_EQ(LCB_SUCCESS, ret); + ret = mcreq_reserve_value(pipeline, packet, &vreq); + ASSERT_EQ(LCB_SUCCESS, ret); + ASSERT_EQ(MCREQ_F_HASVALUE|MCREQ_F_VALUE_IOV|MCREQ_F_VALUE_NOCOPY, + packet->flags); + ASSERT_NE(&iov[0], (nb_IOV *)packet->u_value.multi.iov); + ASSERT_EQ(2, packet->u_value.multi.niov); + ASSERT_EQ(5, packet->u_value.multi.total_length); + mcreq_wipe_packet(pipeline, packet); + mcreq_release_packet(pipeline, packet); + + iov[0].iov_base = (void *)value; + iov[0].iov_len = 3; + iov[1].iov_base = (void *)(value + 3); + iov[1].iov_len = 2; + vreq.u_buf.multi.iov = (lcb_IOV *)iov; + vreq.u_buf.multi.niov = 2; + vreq.u_buf.multi.total_length = 0; + + vreq.vtype = LCB_KV_IOVCOPY; + ret = mcreq_basic_packet(&q, &cmd, &hdr, 0, &packet, &pipeline, 0); + ASSERT_EQ(LCB_SUCCESS, ret); + + ret = mcreq_reserve_value(pipeline, packet, &vreq); + ASSERT_EQ(LCB_SUCCESS, ret); + + ASSERT_EQ(MCREQ_F_HASVALUE, packet->flags); + ASSERT_EQ(0, memcmp(SPAN_BUFFER(&packet->u_value.single), value, 5)); + mcreq_wipe_packet(pipeline, packet); + mcreq_release_packet(pipeline, packet); +} + +struct ExtraCookie { + mc_REQDATAEX base; + int remaining; +}; + +extern "C" { +static void pkt_dtor(mc_PACKET *pkt) { + mc_REQDATAEX *rd = pkt->u_rdata.exdata; + ExtraCookie *ec = (ExtraCookie *)rd; + ec->remaining--; +} +} + +TEST_F(McAlloc, testRdataExDtor) +{ + CQWrap q; + lcb_CMDBASE basecmd; + ExtraCookie ec; + protocol_binary_request_header hdr; + + memset(&hdr, 0, sizeof hdr); + memset(&basecmd, 0, sizeof basecmd); + memset(&ec, 0, sizeof ec); + + mc_REQDATAPROCS procs = { NULL, pkt_dtor }; + ec.base.procs = &procs; + basecmd.key.contig.bytes = "foo"; + basecmd.key.contig.nbytes = 3; + + mcreq_sched_enter(&q); + for (unsigned ii = 0; ii < 5; ii++) { + lcb_error_t err; + mc_PIPELINE *pl; + mc_PACKET *pkt; + err = mcreq_basic_packet(&q, &basecmd, &hdr, 0, &pkt, &pl, 0); + ASSERT_EQ(LCB_SUCCESS, err); + pkt->flags |= MCREQ_F_REQEXT; + pkt->u_rdata.exdata = &ec.base; + mcreq_sched_add(pl, pkt); + ec.remaining++; + } + mcreq_sched_fail(&q); + ASSERT_EQ(0, ec.remaining); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/mc/t_context.cc b/couchbase-sys/libcouchbase-2.7.0/tests/mc/t_context.cc new file mode 100644 index 00000000..c4b43f65 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/mc/t_context.cc @@ -0,0 +1,100 @@ +#include "mctest.h" +#include "mc/mcreq-flush-inl.h" + +class McContext : public ::testing::Test {}; + +struct CtxCookie { + int ncalled; + size_t plLength; + CtxCookie() : ncalled(0), plLength(0) {} +}; + +extern "C" { +static void failcb(mc_PIPELINE *, mc_PACKET *pkt, lcb_error_t, void *) +{ + CtxCookie *cookie = (CtxCookie *)MCREQ_PKT_COOKIE(pkt); + cookie->ncalled++; + cookie->plLength += mcreq_get_size(pkt); +} +} + +TEST_F(McContext, testBasicContext) +{ + CQWrap cq; + CtxCookie cookie; + + mcreq_sched_enter(&cq); + + for (int ii = 0; ii < 20; ii++) { + PacketWrap pw; + char kbuf[128]; + sprintf(kbuf, "key_%d", ii); + pw.setCopyKey(kbuf); + + ASSERT_TRUE(pw.reservePacket(&cq)); + + pw.setHeaderSize(); + pw.copyHeader(); + pw.setCookie(&cookie); + + mcreq_sched_add(pw.pipeline, pw.pkt); + ASSERT_FALSE(SLLIST_IS_EMPTY(&pw.pipeline->requests) == 0); + ASSERT_TRUE(SLLIST_IS_EMPTY(&pw.pipeline->ctxqueued) == 0); + } + + mcreq_sched_fail(&cq); + + for (unsigned ii = 0; ii < cq.npipelines; ii++) { + unsigned nFail = 0; + mc_PIPELINE *pl = cq.pipelines[ii]; + cookie.plLength = 0; + + nFail = mcreq_pipeline_fail(pl, LCB_ERROR, failcb, NULL); + if (!nFail) { + continue; + } + + nb_IOV iov[50]; + unsigned toFlush; + toFlush = mcreq_flush_iov_fill(pl, iov, 50, NULL); + ASSERT_EQ(cookie.plLength, toFlush); + mcreq_flush_done(pl, toFlush, toFlush); + } +} + +TEST_F(McContext, testFailedContext) +{ + CQWrap cq; + CtxCookie cookie; + + mcreq_sched_enter(&cq); + + for (int ii = 0; ii < 20; ii++) { + PacketWrap pw; + char kbuf[128]; + sprintf(kbuf, "Key_%d", ii); + pw.setCopyKey(kbuf); + + ASSERT_TRUE(pw.reservePacket(&cq)); + + pw.setHeaderSize(); + pw.copyHeader(); + mcreq_sched_add(pw.pipeline, pw.pkt); + } + + mcreq_sched_fail(&cq); + + for (unsigned ii = 0; ii < cq.npipelines; ii++) { + mc_PIPELINE *pl = cq.pipelines[ii]; + if (!cq.scheds[pl->index]) { + continue; + } + + + ASSERT_TRUE(SLLIST_IS_EMPTY(&pl->requests)); + ASSERT_TRUE(SLLIST_IS_EMPTY(&pl->ctxqueued)); + + nb_IOV iov[1]; + ASSERT_EQ(0, mcreq_flush_iov_fill(pl, iov, 1, NULL)); + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/mc/t_flush.cc b/couchbase-sys/libcouchbase-2.7.0/tests/mc/t_flush.cc new file mode 100644 index 00000000..8b4973f8 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/mc/t_flush.cc @@ -0,0 +1,185 @@ +#include "mctest.h" +#include "mc/mcreq-flush-inl.h" + +class McFlush : public ::testing::Test {}; + +struct MyCookie { + int ncalled; + void *exp_kbuf; + MyCookie() : ncalled(0), exp_kbuf(NULL) {} +}; + +extern "C" { +static void +buf_free_callback(mc_PIPELINE *, const void *cookie, void *kbuf, void *vbuf) +{ + MyCookie *ck = (MyCookie *)cookie; + EXPECT_TRUE(kbuf == ck->exp_kbuf); + ck->ncalled++; +} +} + +TEST_F(McFlush, testBasicFlush) +{ + CQWrap cq; + PacketWrap pw; + + cq.setBufFreeCallback(buf_free_callback); + pw.setContigKey("1234"); + ASSERT_TRUE(pw.reservePacket(&cq)); + + MyCookie cookie; + cookie.exp_kbuf = pw.pktbuf; + + pw.setCookie(&cookie); + pw.setHeaderSize(); + pw.copyHeader(); + mcreq_enqueue_packet(pw.pipeline, pw.pkt); + mcreq_packet_handled(pw.pipeline, pw.pkt); + + nb_IOV iovs[10]; + + unsigned toFlush = mcreq_flush_iov_fill(pw.pipeline, iovs, 10, NULL); + EXPECT_EQ(28, toFlush); + mcreq_flush_done(pw.pipeline, 8, toFlush); + + toFlush = mcreq_flush_iov_fill(pw.pipeline, iovs, 10, NULL); + EXPECT_EQ(20, toFlush); + mcreq_flush_done(pw.pipeline, toFlush, toFlush); + + toFlush = mcreq_flush_iov_fill(pw.pipeline, iovs, 10, NULL); + ASSERT_EQ(0, toFlush); + ASSERT_EQ(1, cookie.ncalled); +} + +TEST_F(McFlush, testFlushedUnhandled) +{ + CQWrap cq; + PacketWrap pw; + cq.setBufFreeCallback(buf_free_callback); + pw.setContigKey("1234"); + + MyCookie cookie; + cookie.exp_kbuf = pw.pktbuf; + + ASSERT_TRUE(pw.reservePacket(&cq)); + pw.setCookie(&cookie); + pw.setHeaderSize(); + pw.copyHeader(); + + mcreq_enqueue_packet(pw.pipeline, pw.pkt); + + nb_IOV iovs[10]; + unsigned toFlush = mcreq_flush_iov_fill(pw.pipeline, iovs, 10, NULL); + ASSERT_EQ(28, toFlush); + mcreq_flush_done(pw.pipeline, toFlush, toFlush); + + ASSERT_EQ(0, cookie.ncalled); + ASSERT_NE(0, pw.pkt->flags & MCREQ_F_FLUSHED); + + ASSERT_EQ(pw.pkt, mcreq_pipeline_remove(pw.pipeline, pw.pkt->opaque)); + mcreq_packet_handled(pw.pipeline, pw.pkt); + ASSERT_EQ(1, cookie.ncalled); +} + +TEST_F(McFlush, testFlushCopy) +{ + CQWrap cq; + PacketWrap pw; + cq.setBufFreeCallback(buf_free_callback); + pw.setCopyKey("Hello"); + ASSERT_TRUE(pw.reservePacket(&cq)); + + MyCookie cookie; + pw.setHeaderSize(); + pw.copyHeader(); + pw.setCookie(&cookie); + mcreq_enqueue_packet(pw.pipeline, pw.pkt); + + nb_IOV iov[10]; + unsigned int toFlush = mcreq_flush_iov_fill(pw.pipeline, iov, 10, NULL); + mcreq_flush_done(pw.pipeline, toFlush, toFlush); + mcreq_pipeline_remove(pw.pipeline, pw.pkt->opaque); + mcreq_packet_handled(pw.pipeline, pw.pkt); + ASSERT_EQ(0, cookie.ncalled); +} + + +TEST_F(McFlush, testMultiFlush) +{ + CQWrap cq; + int counter = 0; + const int nitems = 10; + MyCookie **cookies; + + cookies = new MyCookie*[nitems]; + PacketWrap **pws = new PacketWrap*[nitems]; + cq.setBufFreeCallback(buf_free_callback); + + for (int ii = 0; ii < nitems; ii++) { + PacketWrap *pw = new PacketWrap; + pws[ii] = pw; + char curkey[128]; + sprintf(curkey, "Key_%d", ii); + pw->setContigKey(curkey); + + cookies[ii] = new MyCookie; + cookies[ii]->exp_kbuf = pw->pktbuf; + + ASSERT_TRUE(pw->reservePacket(&cq)); + + pw->setCookie(cookies[ii]); + mcreq_enqueue_packet(pw->pipeline, pw->pkt); + pw->setHeaderSize(); + pw->copyHeader(); + mcreq_packet_handled(pw->pipeline, pw->pkt); + mcreq_pipeline_remove(pw->pipeline, pw->pkt->opaque); + } + + for (unsigned ii = 0; ii < cq.npipelines; ii++) { + mc_PIPELINE *pipeline = cq.pipelines[ii]; + nb_IOV iov[10]; + unsigned toFlush = mcreq_flush_iov_fill(pipeline, iov, 10, NULL); + if (toFlush) { + mcreq_flush_done(pipeline, toFlush, toFlush); + } + } + + for (int ii = 0; ii < nitems; ii++) { + ASSERT_EQ(1, cookies[ii]->ncalled); + delete cookies[ii]; + delete pws[ii]; + } + delete[] cookies; + delete[] pws; +} + +TEST_F(McFlush, testPartialFlush) +{ + CQWrap cq; + PacketWrap pw; + MyCookie cookie; + + cq.setBufFreeCallback(buf_free_callback); + pw.setContigKey("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + ASSERT_TRUE(pw.reservePacket(&cq)); + pw.setCookie(&cookie); + cookie.exp_kbuf = pw.pktbuf; + pw.setHeaderSize(); + pw.copyHeader(); + mcreq_enqueue_packet(pw.pipeline, pw.pkt); + + nb_IOV iov[1]; + unsigned int toFlush = 0; + do { + toFlush = mcreq_flush_iov_fill(pw.pipeline, iov, 1, NULL); + if (toFlush) { + mcreq_flush_done(pw.pipeline, 1, toFlush); + } + } while (toFlush > 0); + + ASSERT_NE(0, pw.pkt->flags & MCREQ_F_FLUSHED); + mcreq_pipeline_remove(pw.pipeline, pw.pkt->opaque); + mcreq_packet_handled(pw.pipeline, pw.pkt); + ASSERT_EQ(1, cookie.ncalled); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/mc/t_forward.cc b/couchbase-sys/libcouchbase-2.7.0/tests/mc/t_forward.cc new file mode 100644 index 00000000..057189a7 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/mc/t_forward.cc @@ -0,0 +1,239 @@ +#include "mctest.h" +#include "mc/mcreq-flush-inl.h" +#include "mc/forward.h" +#include "pktmaker.h" + +using namespace PacketMaker; +using std::vector; +using std::string; + +struct Vars { + mc_PACKET *pkt; + mc_PIPELINE *pl; + nb_IOV iovs[10]; + mc_IOVINFO ioi; + vector reqbuf; + + Vars() { + pkt = NULL; + pl = NULL; + memset(&ioi, 0, sizeof(ioi)); + memset(&iovs, 0, sizeof(iovs)); + } + + lcb_error_t requestPacket(mc_CMDQUEUE *cq) { + return mc_forward_packet(cq, &ioi, &pkt, &pl, 0); + } + + void initInfo() { + mc_iovinfo_init(&ioi, iovs, 10); + } +}; + +class McFwd : public ::testing::Test {}; + +static void +setupRequestBuf(vector& out, size_t nkey, size_t nval) +{ + string k(nkey, 'K'); + string v(nval, 'V'); + StorageRequest sr(k, v); + out.clear(); + sr.serialize(out); + EXPECT_EQ(nkey + nval + 24, out.size()); +} + +TEST_F(McFwd, testForwardSingle) +{ + CQWrap cq; + StorageRequest sr(string("fookey"), string("foovalue")); + + mc_IOVINFO iovinfo; + + nb_IOV iovs[10]; + // Enqueue first packet inside entire body. + vector reqbody; + sr.serialize(reqbody); + + memset(iovs, 0, sizeof(iovs)); + memset(&iovinfo, 0, sizeof(iovinfo)); + mc_iovinfo_init(&iovinfo, iovs, 10); + ASSERT_EQ(10, iovinfo.c.niov); + ASSERT_EQ(&iovs[0], iovinfo.c.iov); + ASSERT_NE(0, reqbody.size()); + + iovs->iov_base = &reqbody[0]; + iovs->iov_len = reqbody.size(); + iovinfo.total = reqbody.size(); + + mc_PACKET *pkt = NULL; + mc_PIPELINE *pl = NULL; + lcb_error_t rc = mc_forward_packet(&cq, &iovinfo, &pkt, &pl, 0); + ASSERT_EQ(LCB_SUCCESS, rc); + ASSERT_EQ(0, iovinfo.wanted); + ASSERT_EQ(reqbody.size(), iovinfo.consumed); + ASSERT_EQ(9, iovinfo.c.niov); + ASSERT_EQ(0, iovinfo.c.offset); + mcreq_sched_fail(&cq); +} + +TEST_F(McFwd, testFragmentedBasic) +{ + CQWrap cq; + nb_IOV iovs[10]; + vector reqbuf; + + memset(iovs, 0, sizeof(iovs)); + setupRequestBuf(reqbuf, 10, 10); + + iovs[0].iov_base = &reqbuf[0]; + iovs[0].iov_len = 34; + + iovs[1].iov_base = &reqbuf[34]; + iovs[1].iov_len = 10; + + mc_IOVINFO ioi; + memset(&ioi, 0, sizeof(ioi)); + mc_iovinfo_init(&ioi, iovs, 10); + lcb_error_t rc; + mc_PACKET *pkt; + mc_PIPELINE *pl; + + rc = mc_forward_packet(&cq, &ioi, &pkt, &pl, 0); + ASSERT_EQ(LCB_SUCCESS, rc); + ASSERT_EQ(0, ioi.wanted); + ASSERT_EQ(44, ioi.consumed); + ASSERT_EQ(0, ioi.c.offset); + ASSERT_EQ(8, ioi.c.niov); + ASSERT_EQ(0, ioi.c.iov[0].iov_len); + mcreq_sched_fail(&cq); +} + +TEST_F(McFwd, testFragmentedHeader) { + CQWrap cq; + Vars vars; + + setupRequestBuf(vars.reqbuf, 100, 100); + vars.iovs[0].iov_base = &vars.reqbuf[0]; + vars.iovs[0].iov_len = 10; + + vars.iovs[1].iov_base = &vars.reqbuf[10]; + vars.iovs[1].iov_len = 10; + + vars.iovs[2].iov_base = &vars.reqbuf[20]; + vars.iovs[2].iov_len = vars.reqbuf.size() - 20; + vars.initInfo(); + ASSERT_EQ(vars.reqbuf.size(), vars.ioi.total); + + lcb_error_t rc = vars.requestPacket(&cq); + ASSERT_EQ(LCB_SUCCESS, rc); + ASSERT_EQ(0, vars.pkt->flags & MCREQ_F_KEY_NOCOPY); + ASSERT_EQ(0, vars.ioi.total); + ASSERT_EQ(0, vars.ioi.c.offset); + ASSERT_EQ(vars.reqbuf.size(), vars.ioi.consumed); + ASSERT_EQ(0, vars.ioi.c.iov[0].iov_len); + ASSERT_EQ(7, vars.ioi.c.niov); + + mcreq_sched_fail(&cq); +} + +TEST_F(McFwd, testInsufficientHeader) +{ + CQWrap cq; + Vars vars; + lcb_error_t rc; + + setupRequestBuf(vars.reqbuf, 100, 100); + + // Test with no data + vars.iovs[0].iov_base = NULL; + vars.iovs[0].iov_len = 0; + vars.initInfo(); + rc = vars.requestPacket(&cq); + ASSERT_EQ(LCB_INCOMPLETE_PACKET, rc); + ASSERT_EQ(24, vars.ioi.wanted); + + // Test with partial (but incomplete header) + vars.iovs[0].iov_base = &vars.reqbuf[0]; + vars.iovs[0].iov_len = 20; + vars.initInfo(); + rc = vars.requestPacket(&cq); + ASSERT_EQ(LCB_INCOMPLETE_PACKET, rc); + ASSERT_EQ(24, vars.ioi.wanted); + + // Test with full header but partial key + vars.iovs[0].iov_base = &vars.reqbuf[0]; + vars.iovs[0].iov_len = 30; + vars.initInfo(); + rc = vars.requestPacket(&cq); + ASSERT_EQ(rc, LCB_INCOMPLETE_PACKET); + ASSERT_EQ(vars.reqbuf.size(), vars.ioi.wanted); +} + +TEST_F(McFwd, testMultiValue) +{ + CQWrap cq; + Vars vars; + lcb_error_t rc; + setupRequestBuf(vars.reqbuf, 1, 810); + + vars.iovs[0].iov_base = &vars.reqbuf[0]; + vars.iovs[0].iov_len = 25; + + for (int ii = 1; ii < 10; ii++) { + vars.iovs[ii].iov_base = &vars.reqbuf[25 + (ii-1) * 90]; + vars.iovs[ii].iov_len = 90; + } + + vars.initInfo(); + ASSERT_EQ(835, vars.reqbuf.size()); + ASSERT_EQ(835, vars.ioi.total); + + rc = vars.requestPacket(&cq); + ASSERT_EQ(LCB_SUCCESS, rc); + ASSERT_NE(0, vars.pkt->flags & MCREQ_F_VALUE_IOV); + mcreq_sched_fail(&cq); + + // Eh, let's check these other nifty things. Why not? + ASSERT_EQ(0, vars.ioi.wanted); + ASSERT_EQ(0, vars.ioi.c.niov); +} + +TEST_F(McFwd, testNoMap) +{ + CQWrap cq; + lcb_error_t err; + protocol_binary_request_header hdr; + memset(&hdr, 0, sizeof hdr); + hdr.request.magic = PROTOCOL_BINARY_REQ; + hdr.request.opcode = 0x50; + hdr.request.extlen = 8; + hdr.request.bodylen = htonl(8); + hdr.request.vbucket = 0; + char reqbuf[32] = { 0 }; + memcpy(reqbuf, hdr.bytes, sizeof hdr.bytes); + mc_IOVINFO ioi; + nb_IOV iov; + iov.iov_base = reqbuf; + iov.iov_len = sizeof reqbuf; + mc_iovinfo_init(&ioi, &iov, 1); + + mc_PACKET *pkt_tmp; + mc_PIPELINE *pl_tmp = cq.pipelines[0]; + err = mc_forward_packet(&cq, &ioi, &pkt_tmp, &pl_tmp, MC_FWD_OPT_NOMAP); + + ASSERT_EQ(LCB_SUCCESS, err); + ASSERT_NE(0, pkt_tmp->flags & MCREQ_F_UFWD); + + // Get the key + const void *key; + lcb_SIZE nkey; + mcreq_get_key(pkt_tmp, &key, &nkey); + ASSERT_EQ(0, nkey); + + // Ensure we have no vBucket stamping + protocol_binary_request_header hdr2; + mcreq_read_hdr(pkt_tmp, &hdr2); + ASSERT_EQ(0, hdr2.request.vbucket); + mcreq_sched_fail(&cq); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/mc/t_ioflush.cc b/couchbase-sys/libcouchbase-2.7.0/tests/mc/t_ioflush.cc new file mode 100644 index 00000000..a4f114f0 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/mc/t_ioflush.cc @@ -0,0 +1,102 @@ +#include "mctest.h" +#include "mc/mcreq-flush-inl.h" +#include +#include + +using std::vector; +using std::map; + +/** + * Note that this file doesn't actually do any I/O, but simulates I/O patterns + * more realistically than t_flush would. This is basically a more advanced + * version which handles multiple I/O models and does stricter checking on + * items. + */ + +struct Context { + int ncalled; + map children; + Context() { + ncalled = 0; + } +}; + +struct IOCookie { + Context *parent; + mc_PACKET *pkt; + IOCookie(Context *p) { + parent = p; + pkt = NULL; + } +}; + +extern "C" { +static void failcb(mc_PIPELINE *, mc_PACKET *pkt, lcb_error_t, void*) +{ + IOCookie *ioc = (IOCookie *)MCREQ_PKT_COOKIE(pkt); + ioc->parent->children[ioc]++; + ioc->parent->ncalled++; + delete ioc; +} +} + +class McIOFlush : public ::testing::Test {}; + +struct FlushInfo { + mc_PIPELINE *pipeline; + mc_PACKET *pkt; + unsigned size; +}; + +// Test flushing using an IOCP pattern; with multiple items +// at the end and the beginning. +TEST_F(McIOFlush, testIOCPFlush) +{ + vector flushes; + CQWrap cq; + const int count = 20; + Context ctx; + + for (int ii = 0; ii < count; ii++) { + char buf[128]; + sprintf(buf, "Key_%d", ii); + PacketWrap pw; + pw.setCopyKey(buf); + ASSERT_TRUE(pw.reservePacket(&cq)); + pw.setHeaderSize(); + pw.copyHeader(); + + IOCookie *c = new IOCookie(&ctx); + c->pkt = pw.pkt; + + pw.setCookie(c); + mcreq_enqueue_packet(pw.pipeline, pw.pkt); + + nb_IOV iov[1]; + unsigned toFlush = mcreq_flush_iov_fill(pw.pipeline, iov, 1, NULL); + + FlushInfo fi; + fi.pipeline = pw.pipeline; + fi.size = toFlush; + fi.pkt = pw.pkt; + + flushes.push_back(fi); + } + + for (unsigned ii = 0; ii < cq.npipelines; ii++) { + mcreq_pipeline_fail(cq.pipelines[ii], LCB_ERROR, failcb, NULL); + } + + ASSERT_EQ(count, flushes.size()); + for (unsigned ii = 0; ii < count; ii++) { + FlushInfo& fi = flushes[ii]; + ASSERT_NE(0, fi.pkt->flags & MCREQ_F_INVOKED); + mcreq_flush_done(flushes[ii].pipeline, flushes[ii].size, flushes[ii].size); + } + + ASSERT_EQ(count, ctx.ncalled); + map::iterator iter = ctx.children.begin(); + for (; iter != ctx.children.end(); ++iter) { + ASSERT_EQ(1, iter->second); + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/mc/t_iovcursor.cc b/couchbase-sys/libcouchbase-2.7.0/tests/mc/t_iovcursor.cc new file mode 100644 index 00000000..2098970e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/mc/t_iovcursor.cc @@ -0,0 +1,173 @@ +#include +#include "mctest.h" +#include "mc/iovcursor-inl.h" +#include "mc/forward.h" +class McIOVC : public ::testing::Test +{ +}; + +TEST_F(McIOVC, testPeekCopy) +{ + nb_IOV iov; + iov.iov_base = (void*)"ABCDEF"; + iov.iov_len = strlen((const char*)iov.iov_base); + mc_IOVINFO cursor; + mc_IOVCURSOR *mincur = &cursor.c; + mc_iovinfo_init(&cursor, &iov, 1); + + // basic sanity + ASSERT_EQ(0, mincur->offset); + ASSERT_EQ(6, cursor.total); + ASSERT_EQ(1, mincur->niov); + ASSERT_EQ(&iov, mincur->iov); + + char buf[256] = { 0 }; + // test basic peek + iovcursor_peek(mincur, buf, 3, 0); + ASSERT_STREQ("ABC", buf); + + memset(buf, 0, sizeof buf); + iovcursor_peek(mincur, buf, 3, 3); + ASSERT_STREQ("DEF", buf); + + memset(buf, 0, sizeof buf); + iovcursor_peek(mincur, buf, 1, 5); + ASSERT_STREQ("F", buf); +} + +TEST_F(McIOVC, testPeekEx) +{ + nb_IOV iov[3]; + iov[0].iov_base = (void*)"ABC"; + iov[0].iov_len = 3; + iov[1].iov_base = (void*)"DEF"; + iov[1].iov_len = 3; + iov[2].iov_base = (void*)"GHI"; + iov[2].iov_len = 3; + + iovcursor_STATUS status; + mc_IOVINFO cursor; + mc_IOVCURSOR *mincur = &cursor.c; + mc_iovinfo_init(&cursor, iov, 3); + + const char *contigptr; + char cptgt[256] = { 0 }; + + // Simple case + status = iovcursor_peek_ex(mincur, cptgt, NULL, 9, 0); + ASSERT_EQ(IOVCURSOR_STATUS_BUFCOPY_OK, status); + ASSERT_STREQ("ABCDEFGHI", cptgt); + + memset(cptgt, 0, sizeof cptgt); + status = iovcursor_peek_ex(mincur, cptgt, &contigptr, 9, 0); + ASSERT_EQ(IOVCURSOR_STATUS_BUFCOPY_OK, status); + ASSERT_STREQ("ABCDEFGHI", cptgt); + ASSERT_TRUE(contigptr == NULL); + + status = iovcursor_peek_ex(mincur, NULL, &contigptr, 9, 0); + ASSERT_EQ(IOVCURSOR_STATUS_FRAGMENTED, status); + ASSERT_TRUE(contigptr == NULL); + + // Contiguous pointer + memset(cptgt, 0, sizeof cptgt); + status = iovcursor_peek_ex(mincur, NULL, &contigptr, 3, 0); + ASSERT_EQ(IOVCURSOR_STATUS_CONTIGPTR_OK, status); + ASSERT_EQ('\0', cptgt[0]); + memcpy(cptgt, contigptr, 3); + ASSERT_STREQ("ABC", cptgt); + + // With offsets: CDE + contigptr = (char *)0x1; + memset(cptgt, 0, sizeof cptgt); + status = iovcursor_peek_ex(mincur, cptgt, &contigptr, 3, 2); + ASSERT_EQ(IOVCURSOR_STATUS_BUFCOPY_OK, status); + ASSERT_STREQ("CDE", cptgt); + ASSERT_TRUE(contigptr == NULL); +} + +TEST_F(McIOVC, testAdvCopy) +{ + nb_IOV iov[3]; + iov[0].iov_base = (void*)"ABC"; + iov[0].iov_len = 3; + iov[1].iov_base = (void*)"DEF"; + iov[1].iov_len = 3; + iov[2].iov_base = (void*)"GHI"; + iov[2].iov_len = 3; + mc_IOVINFO cursor; + mc_IOVCURSOR *mincur = &cursor.c; + + mc_iovinfo_init(&cursor, iov, 3); + char tgt[256]; + + iovcursor_adv_copy(mincur, tgt, 1); + ASSERT_EQ('A', tgt[0]); + ASSERT_EQ(1, mincur->offset); + memset(tgt, 0, sizeof tgt); + + iovcursor_adv_copy(mincur, tgt, 2); + ASSERT_STREQ("BC", tgt); + ASSERT_EQ(0, mincur->offset); + ASSERT_EQ(2, mincur->niov); + + // Reset the iovs + memset(tgt, 0, sizeof tgt); + mc_iovinfo_init(&cursor, iov, 3); + iovcursor_adv_copy(mincur, tgt, 4); + ASSERT_STREQ("ABCD", tgt); + ASSERT_EQ(2, mincur->niov); + ASSERT_EQ(1, mincur->offset); + + // Read the rest? + memset(tgt, 0, sizeof tgt); + iovcursor_adv_copy(mincur, tgt, 5); + ASSERT_STREQ("EFGHI", tgt); + ASSERT_EQ(0, mincur->niov); + ASSERT_EQ(0, mincur->offset); +} + +TEST_F(McIOVC, testAdvIovalloc) +{ + nb_IOV *iov_p; + unsigned niov; + + nb_IOV iov[5]; + iov[0].iov_base = (void*)"ABC"; + iov[0].iov_len = 3; + + iov[1].iov_base = (void*)"DEF"; + iov[1].iov_len = 3; + + iov[2].iov_base = (void*)"GHI"; + iov[2].iov_len = 3; + + iov[3].iov_base = (void *)"JKL"; + iov[3].iov_len = 3; + + iov[4].iov_base = (void *)"MNO"; + iov[4].iov_len = 3; + + mc_IOVINFO cursor; + mc_IOVCURSOR *mincur = &cursor.c; + mc_iovinfo_init(&cursor, iov, 5); + + char tgt[256] = { 0 }; + // Copy the first 4 fragments + iovcursor_adv_copy(mincur, tgt, 4); + ASSERT_STREQ("ABCD", tgt); + + // Now the IOVs payload: { EF, GHI, JK } + iovcursor_adv_iovalloc(mincur, 7, &iov_p, &niov); + ASSERT_FALSE(iov_p == NULL); + ASSERT_EQ(3, niov); + + // make a small cursor based on the current one, why not? + mc_IOVCURSOR mini; + mini.offset = 0; + mini.iov = iov_p; + mini.niov = niov; + memset(tgt, 0, sizeof tgt); + iovcursor_adv_copy(&mini, tgt, 7); + ASSERT_STREQ("EFGHIJK", tgt); + free(iov_p); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/procutil.c b/couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/procutil.c new file mode 100644 index 00000000..824a44ea --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/procutil.c @@ -0,0 +1,305 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "procutil.h" +#include +#include + +#ifndef _WIN32 +#define _XOPEN_SOURCE +#include +#include +#include +#include /* O_* */ +#include /* usleep */ +#include /* kill */ +#include +#include /* ESRCH */ +#include /* waitpid */ + +static char **clisplit(const char *s) +{ + wordexp_t p; + int rv; + char **ret; + unsigned int ii; + + memset(&p, 0, sizeof(p)); + rv = wordexp(s, &p, WRDE_NOCMD | WRDE_SHOWERR); + if (rv != 0) { + return NULL; + } + ret = malloc(sizeof(char *) * (p.we_wordc + 1)); + if (!ret) { + return NULL; + } + for (ii = 0; ii < p.we_wordc; ii++) { + ret[ii] = strdup(p.we_wordv[ii]); + } + ret[ii] = NULL; + return ret; +} + +static int spawn_process_impl(child_process_t *proc) +{ + int rv; + char **argv; + + proc->pid = fork(); + if (proc->pid < 0) { + return -1; + } + if (proc->pid > 0) { + return 0; + } + + /** In Child */ + argv = clisplit(proc->name); + if (!argv) { + fprintf(stderr, "Couldn't split arguments\n"); + exit(EXIT_FAILURE); + } + if (proc->redirect) { + int fd = open(proc->redirect, O_RDWR | O_CREAT | O_APPEND, 0644); + if (fd < 0) { + perror(proc->redirect); + exit(EXIT_FAILURE); + } + if (dup2(fd, fileno(stderr)) < 0 || dup2(fd, fileno(stdout)) < 0) { + perror("dup2"); + exit(EXIT_FAILURE); + } + setvbuf(stderr, NULL, _IOLBF, 0); + } + rv = execvp(argv[0], argv); + if (rv < 0) { + perror(argv[0]); + exit(EXIT_FAILURE); + } + abort(); + return 0; /* make things happy */ +} + +void kill_process(child_process_t *process, int force) +{ + int signum = SIGTERM; + if (-1 == kill(process->pid, signum)) { + if (errno != ESRCH && force) { + kill(process->pid, SIGKILL); + } + } +} + +int wait_process(child_process_t *process, int tmosec) +{ + int ec, flags = 0; + time_t now, tmostamp; + + if (process->exited) { + return 0; + } + if (tmosec < 0 || tmosec > 0) { + flags |= WNOHANG; + } + now = time(NULL); + + /** Probably better logic for this */ + if (tmosec <= 0) { + tmostamp = 0; + } else { + tmostamp = now + tmosec; + } + + do { + pid_t pidrv = waitpid(process->pid, &ec, flags); + + if (pidrv > 0) { + if (WIFEXITED(ec)) { + process->status = WEXITSTATUS(ec); + process->exited = 1; + + + } else if (WIFSIGNALED(ec)) { + process->status = WTERMSIG(ec); + process->exited = 1; + + + } else if (WIFSTOPPED(ec) || WIFCONTINUED(ec)) { + continue; + + } else { + fprintf(stderr, + "Waitpid returned pid with neither EXITED or " + "SIGNALLED true. Assuming something else (0x%x)\n", + ec); + process->status = -1; + process->exited = 1; + } + + } else if (pidrv == -1 && errno == ESRCH) { + fprintf(stderr, + "Process has already terminated. waitpid(%d) == ESRCH\n", + process->pid); + + process->exited = 1; + } + + if (process->exited) { + return 0; + } + + if (!tmostamp) { + break; + } + usleep(500); + now = time(NULL); + } while (now < tmostamp); + + return -1; +} + +void cleanup_process(child_process_t *proc) +{ + /* nothing */ + (void)proc; +} + +#else +/** Windows */ +static int spawn_process_impl(child_process_t *proc) +{ + BOOL success; + + if (proc->redirect) { + HANDLE out = NULL; + HANDLE err = NULL; + SECURITY_ATTRIBUTES attrs; + + memset(&attrs, 0, sizeof(attrs)); + attrs.nLength = sizeof(attrs); + attrs.bInheritHandle = TRUE; + out = CreateFile(proc->redirect, + FILE_APPEND_DATA, + FILE_SHARE_WRITE | FILE_SHARE_READ, + &attrs, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (out == INVALID_HANDLE_VALUE) { + fprintf(stderr, "Couldn't open '%s'. %d\n", + proc->redirect, (int)GetLastError()); + return -1; + } + if (!DuplicateHandle(GetCurrentProcess(), out, + GetCurrentProcess(), &err, + 0, TRUE, DUPLICATE_SAME_ACCESS)) { + fprintf(stderr, "Couldn't DuplicateHandle. %d\n", + (int)GetLastError()); + return -1; + } + proc->si.cb = sizeof(proc->si); + proc->si.hStdError = err; + proc->si.hStdOutput = out; + proc->si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + proc->si.dwFlags = STARTF_USESTDHANDLES; + } + success = CreateProcess(NULL, /* name */ + (char *)proc->name, /* commandline */ + NULL, /* process attributes */ + NULL, /* security attributes */ + TRUE, /* inherit handles */ + 0, /* creation flags */ + NULL, /* environment */ + NULL, /* current directory */ + &proc->si, /* STARTUPINFO */ + &proc->pi /* PROCESS_INFORMATION */); + + if (!success) { + fprintf(stderr, "Couldn't spawn '%s'. [%d]\n", + proc->name, (int)GetLastError()); + return -1; + } + + return 0; +} + +void kill_process(child_process_t *process, int force) +{ + if (!force) { + return; /* nothing we can do here */ + } + TerminateProcess(process->pi.hProcess, 0); +} + +int wait_process(child_process_t *process, int tmosec) +{ + DWORD millis, result; + + if (process->exited) { + return 0; + } + if (tmosec < 0) { + millis = 0; + } else if (tmosec == 0) { + millis = INFINITE; + } else { + millis = tmosec * 1000; + } + result = WaitForSingleObject(process->pi.hProcess, millis); + if (result != WAIT_OBJECT_0) { + if (result == WAIT_FAILED) { + fprintf(stderr, "Wait failed with code [%d]\n", + (int)GetLastError()); + } + return -1; + } + process->exited = 1; + if (!GetExitCodeProcess(process->pi.hProcess, &result)) { + fprintf(stderr, "GetExitCodeProcess: %d\n", (int)GetLastError()); + + } else { + process->status = result; + } + return 0; +} + +void cleanup_process(child_process_t *process) +{ + CloseHandle(process->pi.hProcess); + CloseHandle(process->pi.hThread); + if (process->redirect) { + CloseHandle(process->si.hStdOutput); + CloseHandle(process->si.hStdError); + } +} + +#endif + +int create_process(child_process_t *proc) +{ + int rv; + + if (proc->interactive) { + rv = system(proc->name); + proc->status = rv; + proc->exited = 1; + return 0; + } + proc->status = -1; + return spawn_process_impl(proc); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/procutil.h b/couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/procutil.h new file mode 100644 index 00000000..938a9a44 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/procutil.h @@ -0,0 +1,89 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2013 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LCB_PROCUTIL_H +#define LCB_PROCUTIL_H + +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct { + /** Full command line to launch */ + const char *name; + + /** Name of file to which output should be redirected. Optional */ + const char *redirect; + + /** Whether this should be a foreground process (uses 'system') */ + int interactive; + + /** Exit status */ + int status; + + /** Whether the application exited*/ + int exited; + + /** PLATFORM-SPECIFIC */ +#ifdef _WIN32 + STARTUPINFO si; + PROCESS_INFORMATION pi; +#else + pid_t pid; +#endif + } child_process_t; + + + /** + * Create a new process. + * Returns 0 on success and nonzero on failure. + */ + int create_process(child_process_t *process); + + /** + * Try to kill the process. If 'force' is specified, the process is killed + * using more "forceful" action + */ + void kill_process(child_process_t *process, int force); + + /** + * Wait until a process has terminated. + * If tmosec is negative, it polls without blocking, + * if it is 0, it waits forever. If it is + * positive, it will wait for that many seconds, polling intermittently. + * + * Returns 0 if the process exited. nonzero on timeout + */ + int wait_process(child_process_t *process, int tmosec); + + /** + * Cleans up any resources opened while creating the process. + */ + void cleanup_process(child_process_t *process); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/server.c b/couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/server.c new file mode 100644 index 00000000..c16df6d7 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/server.c @@ -0,0 +1,391 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2011-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include "server.h" +#include +#include +#include +#include + +#ifndef _WIN32 +#define DIRSEP "/" +#include +#include +#include +#include +#include +#include +#ifdef linux +#undef ntohs +#undef ntohl +#undef htons +#undef htonl +#endif +#else +#define DIRSEP "\\" +#include /* for access() */ +#endif /* ! _WIN32 */ + +static int create_monitor(struct test_server_info *info) +{ + struct addrinfo hints, *next, *ai; + int error; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + info->sock = -1; + error = getaddrinfo(NULL, "0", &hints, &ai); + if (error != 0) { +#ifdef _WIN32 + if (0) { +#else + if (error != EAI_SYSTEM) { +#endif + fprintf(stderr, "getaddrinfo failed: %s\n", + gai_strerror(error)); + } else { + perror("getaddrinfo failed:"); + } + return 0; + } + + for (next = ai; next; next = next->ai_next) { + int flags = 1; + socklen_t len; + + if ((info->sock = socket(next->ai_family, + next->ai_socktype, + next->ai_protocol)) == -1) { + continue; + } + + setsockopt(info->sock, SOL_SOCKET, SO_REUSEADDR, + (void *)&flags, sizeof(flags)); + + if (bind(info->sock, next->ai_addr, next->ai_addrlen) == -1) { + closesocket(info->sock); + info->sock = -1; + continue; + } else if (listen(info->sock, 10) == -1) { + closesocket(info->sock); + info->sock = -1; + continue; + } + + /* Ok, I've got a working socket :) */ + len = sizeof(info->storage); + if (getsockname(info->sock, (struct sockaddr *)&info->storage, &len) == -1) { + closesocket(info->sock); + info->sock = -1; + continue; + } + if (next->ai_addr->sa_family == AF_INET) { + info->port = ntohs((*(struct sockaddr_in *)&info->storage).sin_port); + } else { + info->port = ntohs((*(struct sockaddr_in6 *)&info->storage).sin6_port); + } + } + + freeaddrinfo(ai); + return info->sock != -1; +} + +static void wait_for_server(const char *port) +{ + struct addrinfo hints, *next, *ai; + int sock = -1; + int error; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + error = getaddrinfo("localhost", port, &hints, &ai); + if (error != 0) { +#ifndef _WIN32 + if (error != EAI_SYSTEM) { +#else + if (0) { +#endif + fprintf(stderr, "getaddrinfo failed: %s\n", + gai_strerror(error)); + } else { + perror("getaddrinfo failed:"); + } + abort(); + } + + while (1) { + for (next = ai; next; next = next->ai_next) { + if ((sock = socket(next->ai_family, + next->ai_socktype, + next->ai_protocol)) == -1) { + continue; + } + + if (connect(sock, next->ai_addr, next->ai_addrlen) == 0) { + closesocket(sock); + freeaddrinfo(ai); + return; + } + + closesocket(sock); + } + usleep(250); + } +} + +/** + * Parse server parameters from environment; + * format is host,bucket,username,password + */ +static int parse_server_conf(struct test_server_info *info, const char *param) +{ + char *strings[10] = { NULL }; + int curix = 0; + char *param_copy = strdup(param); + param = param_copy; + + while (*param && curix < 10) { + const char *curfld; + char *curval; + size_t diff; + + for (curfld = param; *param && *param != ','; param++); + diff = (param - curfld); + curval = calloc(1, diff + 1); + curval[diff] = '\0'; + memcpy(curval, curfld, diff); + strings[curix++] = curval; + if (*param == ',') { + param++; + } + } + + info->http = strings[0]; + info->bucket = strings[1]; + info->username = strings[2]; + info->password = strings[3]; + + free(param_copy); + + if (!info->http) { + fprintf(stderr, "Must have node entry point for real cluster test\n"); + return 0; + } + return 1; +} + +static int start_mock_process(struct test_server_info *info, char **argv) +{ + char argbuf[4096] = { 0 }; + char **arg; + for (arg = argv; *arg; arg++) { + strcat(argbuf, *arg); + strcat(argbuf, " "); + } + + memset(&info->process, 0, sizeof(info->process)); + info->process.name = argbuf; + + return create_process(&info->process); +} + +static void kill_mock_process(struct test_server_info *info) +{ + kill_process(&info->process, 1); + wait_process(&info->process, 1); + cleanup_process(&info->process); +} + +#ifndef _WIN32 +#define WRAPPER_BASE "start_mock.sh" +#else +#define WRAPPER_BASE "start_mock.bat" +#endif + +static void negotiate_mock_connection(struct test_server_info *info) +{ + char buffer[1024]; + lcb_ssize_t offset; + lcb_ssize_t nr; + int ii; + + /* wait until the server connects */ + for (ii = 0; ii < 10; ii++) { + info->client = accept(info->sock, NULL, NULL); + if (info->client == -1) { + /* running this in gdb on OS X, I got an EINTR a few times */ + if (errno == EINTR) { + fprintf(stderr, "start_mock_server: Sleeping 1 second on EINTR\n"); + sleep(1); + } else { + perror("start_mock_server"); + abort(); + } + } else { + break; + } + } + + assert(info->client != -1); + /* Get the port number of the http server */ + offset = snprintf(buffer, sizeof(buffer), "localhost:"); + nr = recv(info->client, buffer + offset, + sizeof(buffer) - (size_t)offset - 1, 0); + assert(nr > 0); + buffer[nr + offset] = '\0'; + info->http = strdup(buffer); + wait_for_server(buffer + offset); +} + +static int start_mock_server(struct test_server_info *info, char **cmdline) +{ + + char wrapper[1024]; + char monitor[1024]; + char *argv[1024]; +#ifdef _WIN32 + int access_mode = 00; +#else + int access_mode = X_OK; +#endif + const char *srcdir = getenv("srcdir"); + + + if (srcdir == NULL) { + srcdir = "."; + } + + snprintf(wrapper, sizeof(wrapper), + "%s" DIRSEP "tests" DIRSEP WRAPPER_BASE, + srcdir); + + if (access(wrapper, access_mode) == -1) { + fprintf(stderr, "Failed to locate \"%s\": %s\n", + wrapper, + strerror(errno)); + return 0; + } + + + if (!create_monitor(info)) { + return 0; + } + + { + int arg = 0; + argv[arg++] = (char *)wrapper; + sprintf(monitor, "--harakiri-monitor=localhost:%d", info->port); + argv[arg++] = monitor; + + if (cmdline != NULL) { + int ii = 0; + while (cmdline[ii] != NULL && arg < 1022) { + argv[arg++] = cmdline[ii++]; + } + } + argv[arg++] = NULL; + + } + + start_mock_process(info, argv); + negotiate_mock_connection(info); + sleep(1); /* give it a bit time to initialize itself */ + return 1; +} + +const void *start_test_server(char **cmdline) +{ + const char *clconf = getenv(LCB_TEST_REALCLUSTER_ENV); + int server_ok = 0; + struct test_server_info *info = calloc(1, sizeof(*info)); + +#ifdef _WIN32 + /** Winsock boilerplate */ + { + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) { + fprintf(stderr, "WSAStartup failed. Abort\n"); + abort(); + } + } +#endif + + if (info == NULL) { + return NULL; + } + + if (clconf) { + server_ok = parse_server_conf(info, clconf); + info->is_mock = 0; + } else { + server_ok = start_mock_server(info, cmdline); + info->is_mock = 1; + } + + if (!server_ok) { + fprintf(stderr, "Couldn't setup server!\n"); + abort(); + } + + return info; +} + +void shutdown_mock_server(const void *handle) +{ + struct test_server_info *info = (void *)handle; + if (info != NULL) { + free(info->http); + free(info->bucket); + free(info->username); + free(info->password); + if (info->is_mock) { + closesocket(info->client); + closesocket(info->sock); + kill_mock_process(info); + } + free((void *)handle); + } +} + +const char *get_mock_http_server(const void *handle) +{ + struct test_server_info *info = (void *)handle; + return info->http; +} + +void get_mock_std_creds(const void *handle, const char **userp, const char **passp) +{ + const struct test_server_info *info = handle; + if (info->is_mock) { + *userp = NULL; + *passp = NULL; + } else { + *userp = info->username; + *passp = info->password; + } +} + +int is_using_real_cluster(void) +{ + return getenv(LCB_TEST_REALCLUSTER_ENV) != NULL; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/server.h b/couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/server.h new file mode 100644 index 00000000..a1c8eecb --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/server.h @@ -0,0 +1,72 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LIBCOUCHBASE_TEST_SERVER_H +#define LIBCOUCHBASE_TEST_SERVER_H 1 +#define LCB_TEST_REALCLUSTER_ENV "LCB_TEST_CLUSTER_CONF" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +#define in_port_t USHORT +#ifndef usleep +#define usleep(us) Sleep((us) / 1000) +#endif +#ifndef sleep +#define sleep(s) Sleep( (s) * 1000) +#endif + +#include +#else +#define closesocket close +#include +#include +#include +#endif +#include +#include "procutil.h" + + struct test_server_info { + child_process_t process; + char *http; + char *bucket; + char *username; + char *password; + in_port_t port; + struct sockaddr_storage storage; + int sock; + int client; + int is_mock; + }; + + + const void *start_test_server(char **cmdline); + const char *get_mock_http_server(const void *); + void get_mock_std_creds(const void *handle, const char **userp, const char **passp); + int is_using_real_cluster(void); + + void shutdown_mock_server(const void *); + + struct lcb_io_opt_st *get_test_io_opts(void); + void setup_test_timeout_handler(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/timeout.c b/couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/timeout.c new file mode 100644 index 00000000..0f46fa9b --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/mocksupport/timeout.c @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include +/* + * The current test suite should not take more than 5 minutes to run. + * If you're testing on a really slow system you may set the + * environment variable LCB_MAX_TEST_DURATION to the maximum number of + * seconds you'd like the tests to take. + */ +const int max_duration = 300; + +#ifdef _WIN32 +static HANDLE hTimer; +void CALLBACK test_timed_out(PVOID lpUnused, BOOLEAN bUnused) +{ + (void)lpUnused; + (void)bUnused; + fprintf(stderr, "Tests are taking too long to run. Aborting..\n"); + abort(); +} +#endif + +void setup_test_timeout_handler(void) +{ + char *ptr = getenv("LCB_MAX_TEST_DURATION"); + int duration = 0; + if (ptr != NULL) { + duration = atoi(ptr); + } + if (duration == 0) { + duration = max_duration; + } + +#ifdef HAVE_SETITIMER + struct itimerval timer = { .it_value = { .tv_sec = duration } }; + setitimer(ITIMER_REAL, &timer, NULL); +#elif defined(HAVE_ALARM) + alarm(duration); +#elif defined(_WIN32) + CreateTimerQueueTimer( + &hTimer, + NULL, + test_timed_out, + NULL, + duration * 1000, + 0, + 0); +#else + /* print an error message so that we're using the duration variable + * and not generate a warning about unused variables ;) */ + fprintf(stderr, "Tests may run longer than %d due to lack of an alarm\n", + duration); +#endif +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/nonio_tests.cc b/couchbase-sys/libcouchbase-2.7.0/tests/nonio_tests.cc new file mode 100644 index 00000000..c5682af2 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/nonio_tests.cc @@ -0,0 +1,23 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/rdb/rdbtest.h b/couchbase-sys/libcouchbase-2.7.0/tests/rdb/rdbtest.h new file mode 100644 index 00000000..8242a7aa --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/rdb/rdbtest.h @@ -0,0 +1,133 @@ +#define NOMINMAX // for win32, use std::min rather than min +#include +#include +#include + +class RdbAllocator +{ +public: + rdb_ALLOCATOR *_inner; + rdb_ROPESEG *alloc(size_t n) { + return _inner->s_alloc(_inner, n); + } + rdb_ROPESEG *realloc(rdb_ROPESEG *prev, size_t n) { + return _inner->s_realloc(_inner, prev, n); + } + void reserve(rdb_ROPEBUF *buf, size_t cap) { + _inner->r_reserve(_inner, buf, cap); + } + void free(rdb_ROPESEG *seg) { + _inner->s_release(_inner, seg); + } + void release() { + _inner->a_release(_inner); + } + + RdbAllocator(rdb_ALLOCATOR *inner) { + _inner = inner; + } +}; + +struct IORope : public rdb_IOROPE { + IORope(rdb_ALLOCATOR *allocator) { + rdb_init(this, allocator); + rdsize = 256; + } + + IORope() { + rdb_init(this, rdb_bigalloc_new()); + rdsize = 256; + } + + ~IORope() { + rdb_cleanup(this); + } + + IORope(const IORope&); + + std::string stlstr(size_t n) { + char *buf = new char[n]; + rdb_copyread(this, buf, n); + std::string rv(buf, n); + delete[] buf; + return rv; + } + + size_t usedSize() const { + return recvd.nused; + } + + void feed(const std::string &s) { + size_t n_fed = 0; + nb_IOV iov[32]; + + while (n_fed < s.size()) { + unsigned niov = rdb_rdstart(this, iov, 32); + unsigned cur_nfed = 0; + + for (unsigned ii = 0; ii < niov && n_fed < s.size(); ii++) { + const char *frag = s.data() + n_fed; + nb_IOV *curiov = iov + ii; + // on win32 iov_len is not a size_t + unsigned to_copy = std::min(s.size() - n_fed, (size_t)curiov->iov_len); + memcpy(curiov->iov_base, frag, to_copy); + n_fed += to_copy; + cur_nfed += to_copy; + } + rdb_rdend(this, cur_nfed); + } + } + + void feed(const char *s) { + feed(std::string(s)); + } +}; + +struct ReadPacket { + std::vector segments; + std::vector iovs; + + ReadPacket(nb_IOV *iov, rdb_ROPESEG **segs, unsigned n) { + segments.reserve(n); + iovs.reserve(n); + + // Insert them. + segments.insert(segments.begin(), segs, segs + n); + iovs.insert(iovs.begin(), iov, iov + n); + } + + ReadPacket(rdb_IOROPE *ior, unsigned nb) { + segments.resize(2); + iovs.resize(2); + unsigned niov; + + while (true) { + niov = rdb_refread_ex(ior, &iovs[0], &segments[0], iovs.size(), nb); + if (niov != (unsigned)-1) { + iovs.resize(niov); + segments.resize(niov); + break; + } + + iovs.resize(iovs.size() * 2); + segments.resize(segments.size() * 2); + } + } + + void refSegment(unsigned ix) { + rdb_seg_ref(segments[ix]); + } + + std::string asString() { + std::string s; + for (size_t ii = 0; ii < iovs.size(); ii++) { + nb_IOV *cur = &iovs[ii]; + s.append((const char *)cur->iov_base, cur->iov_len); + } + return s; + } + + void unrefSegment(unsigned ix) { + rdb_seg_unref(segments[ix]); + } +}; diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/rdb/t_basic.cc b/couchbase-sys/libcouchbase-2.7.0/tests/rdb/t_basic.cc new file mode 100644 index 00000000..fb36f7ce --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/rdb/t_basic.cc @@ -0,0 +1,128 @@ +#include "rdbtest.h" + +class RopeTest : public ::testing::Test {}; +using std::string; + +TEST_F(RopeTest, testBasic) +{ + IORope rope; + nb_IOV iovs; + unsigned niov = rdb_rdstart(&rope, &iovs, 1); + ASSERT_EQ(1, niov); + ASSERT_FALSE(iovs.iov_base == NULL); + ASSERT_GT(iovs.iov_len, 0U); + + memset(iovs.iov_base, 0x66, iovs.iov_len); + rdb_rdend(&rope, iovs.iov_len); + ASSERT_EQ(iovs.iov_len, rope.usedSize()); + + for (unsigned ii = 0; ii < iovs.iov_len; ii++) { + unsigned char tmp; + rdb_copyread(&rope, &tmp, 1); + ASSERT_EQ(0x66, tmp); + rdb_consumed(&rope, 1); + } +} + +TEST_F(RopeTest, testFragmented) +{ + IORope rope(rdb_chunkalloc_new(1)); + nb_IOV iovs[32]; + unsigned niov; + niov = rdb_rdstart(&rope, iovs, 32); + ASSERT_EQ(32, niov); + + string hello("Hello World!"); + for (unsigned ii = 0; ii < hello.size(); ii++) { + memcpy(iovs[ii].iov_base, &hello[ii], 1); + } + + rdb_rdend(&rope, hello.size()); + ASSERT_EQ(hello.size(), rope.usedSize()); + + // Now we should be able to extract it properly + char tmpbuf[32] = { 0 }; + rdb_copyread(&rope, tmpbuf, hello.size()); + ASSERT_STREQ(tmpbuf, hello.c_str()); + + /** Can we read contiguously? */ + nb_IOV iovs2[32]; + rdb_ROPESEG *backs[32]; + int nitems; + + nitems = rdb_refread_ex(&rope, iovs2, backs, 32, hello.size()); + ASSERT_EQ(hello.size(), nitems); + for (unsigned ii = 0; ii < hello.size(); ii++) { + nb_IOV *cur = iovs2 + ii; + ASSERT_EQ(*(char *)cur->iov_base, hello.at(ii)); + ASSERT_EQ(1, cur->iov_len); + } + + // Solidify them? + rdb_consolidate(&rope, 5); + memset(tmpbuf, 0, sizeof(tmpbuf)); + ASSERT_EQ(hello.size(), rope.usedSize()); + rdb_copyread(&rope, tmpbuf, hello.size()); + ASSERT_STREQ(tmpbuf, hello.c_str()); + + nitems = rdb_refread_ex(&rope, iovs2, backs, 32, hello.size()); + ASSERT_EQ(hello.size()-4, nitems); +} + +// This tests the functionality where we want _subsequent_ reads to be +// consolidated into a single buffer. +TEST_F(RopeTest, testConsolidatedReadAhead) +{ + IORope ior(rdb_chunkalloc_new(1)); + ior.rdsize = 256; + + nb_IOV iovs[32]; + rdb_ROPESEG *segs[32]; + unsigned niov; + + // Feed 4 bytes into the buffer + ior.feed("1234"); + + // Make the next 6 bytes consolidated + rdb_consolidate(&ior, 6); + ior.feed("5678"); + niov = rdb_refread_ex(&ior, iovs, segs, 3, 8); + + ASSERT_EQ(3, niov); + ASSERT_EQ(6, iovs[0].iov_len); + ASSERT_EQ(0, memcmp(iovs[0].iov_base, "123456", 6)); + ASSERT_EQ(*(char *)iovs[1].iov_base, '7'); + ASSERT_EQ(*(char *)iovs[2].iov_base, '8'); +} + + +// When I was integrating this into LCBIO, I realized this scenario. Trying to +// figure out what the intended outcome is. +// Apparently this cannot work because we can't consume a buffer which is also +// available for reading as this may result in the currently-being-read-into +// buffer being released. +TEST_F(RopeTest, DISABLED_testInterleavedReadConsume) +{ + IORope ior(rdb_bigalloc_new()); + ior.rdsize = 256; + nb_IOV iov; + unsigned niov; + + niov = rdb_rdstart(&ior, &iov, 1); + ASSERT_EQ(1, niov); + memset(iov.iov_base, '1', 29); + + rdb_rdend(&ior, 29); + rdb_consumed(&ior, 24); + ASSERT_EQ(5, rdb_get_nused(&ior)); + + nb_IOV iov2; + niov = rdb_rdstart(&ior, &iov2, 1); + ASSERT_EQ(1, niov); + ASSERT_EQ(5, rdb_get_nused(&ior)); + + rdb_consumed(&ior, 5); + ASSERT_EQ(0, rdb_get_nused(&ior)); + + rdb_rdend(&ior, 100); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/rdb/t_bigalloc.cc b/couchbase-sys/libcouchbase-2.7.0/tests/rdb/t_bigalloc.cc new file mode 100644 index 00000000..31721f72 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/rdb/t_bigalloc.cc @@ -0,0 +1,93 @@ +#include "rdbtest.h" +#include +class BigallocTest : public ::testing::Test {}; + +TEST_F(BigallocTest, testBasic) +{ + RdbAllocator a(rdb_bigalloc_new()); + rdb_BIGALLOC *ba = (rdb_BIGALLOC *)a._inner; + + ASSERT_EQ(0, LCB_CLIST_SIZE(&ba->bufs)); + ASSERT_EQ(0, ba->n_requests); + ASSERT_EQ(0, ba->n_toobig); + ASSERT_EQ(0, ba->n_toosmall); + ASSERT_EQ(RDB_BIGALLOC_ALLOCSZ_MAX, ba->max_blk_alloc); + ASSERT_EQ(RDB_BIGALLOC_ALLOCSZ_MIN, ba->min_blk_alloc); + ASSERT_EQ(RDB_BIGALLOC_BLKCNT_MAX, ba->max_blk_count); + a.release(); +} + +TEST_F(BigallocTest, testTooSmall) +{ + RdbAllocator a(rdb_bigalloc_new()); + rdb_BIGALLOC *ba = (rdb_BIGALLOC *)a._inner; + + size_t first_size = RDB_BIGALLOC_ALLOCSZ_MIN * 2; + rdb_ROPESEG *seg = a.alloc(first_size); + a.free(seg); + + ASSERT_EQ(1, LCB_CLIST_SIZE(&ba->bufs)); + ASSERT_EQ(1, ba->n_requests); + ASSERT_EQ(0, ba->n_toobig); + ASSERT_EQ(0, ba->n_toosmall); + + rdb_ROPESEG *newseg = a.alloc(first_size); + ASSERT_EQ(seg, newseg); + a.free(newseg); + + size_t smallsize = RDB_BIGALLOC_ALLOCSZ_MIN / 2; + while (ba->n_requests < RDB_BIGALLOC_RECHECK_RATE-1) { + newseg = a.alloc(smallsize); + a.free(newseg); + ASSERT_EQ(seg, newseg); // same pooled one + ASSERT_EQ(0, seg->nused); + ASSERT_EQ(0, seg->start); + memset(seg->root, '*', seg->nalloc); + seg->start = seg->nalloc; + } + + size_t oldmin = ba->min_blk_alloc, oldmax = ba->max_blk_alloc; + // Get one more: + newseg = a.alloc(smallsize); + ASSERT_EQ(oldmin/2, ba->min_blk_alloc); + ASSERT_EQ(oldmax/2, ba->max_blk_alloc); + a.free(newseg); + + rdb_bigalloc_dump(ba, stdout); + a.release(); +} + +TEST_F(BigallocTest, testPooled) +{ + RdbAllocator a(rdb_bigalloc_new()); + rdb_BIGALLOC *ba = (rdb_BIGALLOC *)a._inner; + std::vector segs; + size_t allocsize = 1; + + for (unsigned ii = 0; ii < (ba->max_blk_count * 2); ii++) { + rdb_ROPESEG *seg = a.alloc(allocsize); + segs.push_back(seg); + } + rdb_bigalloc_dump(ba, stdout); + + for (unsigned ii = 0; ii < segs.size(); ii++) { + a.free(segs[ii]); + } + + segs.clear(); + rdb_bigalloc_dump(ba, stdout); + ASSERT_EQ(RDB_BIGALLOC_BLKCNT_MAX, LCB_CLIST_SIZE(&ba->bufs)); + a.release(); +} + +TEST_F(BigallocTest, testRealloc) +{ + RdbAllocator a(rdb_bigalloc_new()); + rdb_ROPESEG *seg = a.alloc(5); + // well, we'll see what we have here + size_t cursize = seg->nalloc; + seg = a.realloc(seg, cursize+1); + ASSERT_GT(seg->nalloc, cursize); + a.free(seg); + a.release(); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/rdb/t_refs.cc b/couchbase-sys/libcouchbase-2.7.0/tests/rdb/t_refs.cc new file mode 100644 index 00000000..11a7e6dc --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/rdb/t_refs.cc @@ -0,0 +1,112 @@ +#include "rdbtest.h" +#include "list.h" + +class RefTest : public ::testing::Test {}; + +TEST_F(RefTest, testLifecycle) +{ + IORope *ior = new IORope(rdb_chunkalloc_new(4)); + ior->feed("12345678"); + ASSERT_EQ(8, ior->usedSize()); + + nb_IOV iovs[32]; + rdb_ROPESEG *segs[32]; + + unsigned nseg = rdb_refread_ex(ior, iovs, segs, 32, 8); + ASSERT_EQ(2, nseg); + + for (unsigned ii = 0; ii < nseg; ii++) { + rdb_seg_ref(segs[ii]); + ASSERT_NE(0, segs[ii]->shflags & RDB_ROPESEG_F_USER); + ASSERT_NE(0, segs[ii]->shflags & RDB_ROPESEG_F_LIB); + ASSERT_EQ(1, segs[ii]->refcnt); + } + + // Ensure the memory allocated is removed. + delete ior; + + ASSERT_EQ(0, memcmp(iovs[0].iov_base, "1234", 4)); + ASSERT_EQ(0, memcmp(iovs[1].iov_base, "5678", 4)); + ASSERT_NE(0, segs[0]->shflags & RDB_ROPESEG_F_USER); + ASSERT_EQ(0, segs[0]->shflags & RDB_ROPESEG_F_LIB); + rdb_seg_unref(segs[0]); + rdb_seg_unref(segs[1]); +} + +// Some more blahblah +TEST_F(RefTest, testCycle2) +{ + IORope *ior = new IORope(rdb_chunkalloc_new(10)); + + // Read first 3 bytes + ior->feed("1234567890A"); + ReadPacket rp(ior, 3); + ASSERT_EQ(1, rp.segments.size()); + rp.refSegment(0); + rdb_consumed(ior, 3); + ASSERT_EQ(rp.asString(), "123"); + + // Read another 3 bytes + ReadPacket rp2(ior, 3); + rp2.refSegment(0); + rdb_consumed(ior, 3); + ASSERT_EQ(rp2.asString(), "456"); + ASSERT_EQ(1, rp2.segments.size()); + + ASSERT_EQ(rp.segments[0], rp2.segments[0]); + + // Allocate a third one, consuming the rest + ReadPacket rp3(ior, 5); + ASSERT_EQ(2, rp3.segments.size()); + ASSERT_EQ(rp.segments[0], rp3.segments[0]); + ASSERT_EQ(rp3.asString(), "7890A"); + rp3.refSegment(1); + rdb_consumed(ior, 5); + + delete ior; + + rp.unrefSegment(0); + rp2.unrefSegment(0); + rp3.unrefSegment(1); +} + + +// See what happens when we try to consolidate buffers as part of an already +// referenced segment. +TEST_F(RefTest, testRefConsolidate) +{ + IORope *ior = new IORope(rdb_chunkalloc_new(3)); + ior->feed("123456789"); + ReadPacket rp(ior, 3); + rp.refSegment(0); + rdb_consumed(ior, 3); + ASSERT_EQ(RDB_ROPESEG_F_USER, rp.segments[0]->shflags); + + rdb_consolidate(ior, 6); + ASSERT_NE(RDB_SEG_FIRST(&ior->recvd), rp.segments[0]); + ASSERT_EQ(6, ior->recvd.nused); + rdb_consumed(ior, 6); + rp.unrefSegment(0); + delete ior; + + // however we should be using the same segment otherwise.. + ior = new IORope(rdb_chunkalloc_new(3)); + ior->feed("123456789"); + ReadPacket rp2(ior, 3); + char *p = rdb_get_consolidated(ior, 6); + ASSERT_EQ(0, memcmp(p, "123456", 6)); + ASSERT_EQ(RDB_SEG_FIRST(&ior->recvd), rp2.segments[0]); + ASSERT_EQ(9, ior->recvd.nused); + delete ior; + + ior = new IORope(rdb_chunkalloc_new(6)); + ior->feed("123456789"); + ReadPacket rp3(ior, 6); + rp3.refSegment(0); + + p = rdb_get_consolidated(ior, 9); + ASSERT_EQ(0, memcmp(p, "123456789", 9)); + ASSERT_EQ(9, ior->recvd.nused); + rp3.unrefSegment(0); + delete ior; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/socktests/socktest.cc b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/socktest.cc new file mode 100644 index 00000000..f316c631 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/socktest.cc @@ -0,0 +1,347 @@ +#undef NDEBUG +#include "socktest.h" +#include +using std::list; + +extern "C" { +static void +ctx_error(lcbio_CTX *ctx, lcb_error_t err) +{ + ESocket *s = (ESocket *) lcbio_ctx_data(ctx); + s->lasterr = err; + s->actions->onError(s); + s->parent->stop(); +} + +static void +ctx_read(lcbio_CTX *ctx, unsigned nr) +{ + ESocket *s = (ESocket *) lcbio_ctx_data(ctx); + s->actions->onRead(s, nr); +} + +static void +conn_cb(lcbio_SOCKET *sock, void *data, lcb_error_t err, lcbio_OSERR oserr) +{ + ESocket *mysock = (ESocket *)data; + mysock->assign(sock, err); + mysock->parent->stop(); + mysock->syserr = oserr; + mysock->callCount++; +} + +static void +ctx_flush_ready(lcbio_CTX *ctx) +{ + ESocket *s = (ESocket *) lcbio_ctx_data(ctx); + s->actions->onFlushReady(s); +} + +static void +ctx_flush_done(lcbio_CTX *ctx, unsigned expected, unsigned nr) +{ + ESocket *s = (ESocket *) lcbio_ctx_data(ctx); + s->actions->onFlushDone(s, expected, nr); +} +} + +void +IOActions::onError(ESocket *) +{ + // noop for now +} + +void +IOActions::onRead(ESocket *s, size_t nr) +{ + lcbio_CTXRDITER iter; + LCBIO_CTX_ITERFOR(s->ctx, &iter, nr) { + char *curbuf = (char *)lcbio_ctx_ribuf(&iter); + unsigned nbuf = lcbio_ctx_risize(&iter); + s->readbuf.insert(s->readbuf.end(), curbuf, curbuf + nbuf); + } +} + +IOActions ESocket::defaultActions; + +void +ESocket::assign(lcbio_SOCKET *s, lcb_error_t err) +{ + creq.u.cs = NULL; + if (s == NULL) { + lasterr = err; + return; + } + + err = lcbio_sslify_if_needed(s, s->settings); + if (err != LCB_SUCCESS) { + lasterr = err; + return; + } + + lcbio_CTXPROCS procs; + procs.cb_err = ctx_error; + procs.cb_read = ctx_read; + procs.cb_flush_done = ctx_flush_done; + procs.cb_flush_ready = ctx_flush_ready; + sock = s; + ctx = lcbio_ctx_new(s, this, &procs); +} + +extern "C" { +static void +close_cb(lcbio_SOCKET *s, int reusable, void *arg) +{ + (void)arg; + if (reusable) { + lcbio_ref(s); + lcbio_mgr_put(s); + } +} +} + +void +ESocket::close() +{ + if (!ctx) { + return; + } + + if (creq.type == LCBIO_CONNREQ_POOLED) { + lcbio_ctx_close(ctx, close_cb, NULL); + } else { + lcbio_ctx_close(ctx, NULL, NULL); + } + + sock = NULL; + ctx = NULL; +} + +class BreakTimer : public Timer { +public: + BreakTimer(Loop *l) : Timer(l->iot) { + this->loop = l; + } + + void expired() { + if (!loop->bcond) { + return; + } + if (loop->bcond->shouldBreak()) { + loop->stop(); + } else { + loop->scheduleBreak(); + } + } + virtual ~BreakTimer() { } +private: + Loop *loop; +}; + +Loop::Loop() +{ + io = NULL; + + lcb_error_t rc = lcb_create_io_ops(&io, NULL); + assert(rc == LCB_SUCCESS); + assert(io != NULL); + iot = lcbio_table_new(io); + settings = lcb_settings_new(); + settings->logger = lcb_init_console_logger(); + server = new TestServer(); + breakTimer = new BreakTimer(this); + bcond = NULL; + sockpool = lcbio_mgr_create(settings, iot); +} + +Loop::~Loop() +{ + delete breakTimer; + delete server; + lcbio_mgr_destroy(sockpool); + lcbio_table_unref(iot); + lcb_destroy_io_ops(io); + lcb_settings_unref(settings); +} + +void +Loop::scheduleBreak() +{ + breakTimer->schedule(2); // 2ms +} + +void +Loop::cancelBreak() +{ + breakTimer->cancel(); +} + +void +Loop::start() +{ + if (bcond) { + scheduleBreak(); + } + IOT_START(iot); + cancelBreak(); + bcond = NULL; +} + +void +Loop::stop() +{ + cancelBreak(); + IOT_STOP(iot); +} + +void +Loop::initSockCommon(ESocket *sock) +{ + // Find the peer.. + struct sockaddr_in *addr = (struct sockaddr_in *)&sock->sock->info->sa_local; + uint16_t port = ntohs(addr->sin_port); + sock->conn = server->findConnection(port); +} + +void +Loop::connectPooled(ESocket *sock, lcb_host_t *host, unsigned mstmo) +{ + lcb_host_t tmphost; + sock->parent = this; + if (!host) { + populateHost(&tmphost); + host = &tmphost; + } + sock->creq.type = LCBIO_CONNREQ_POOLED; + sock->creq.u.preq = lcbio_mgr_get( + sockpool, host, LCB_MS2US(mstmo), conn_cb, sock); + start(); + if (sock->sock) { + initSockCommon(sock); + } +} + +void +Loop::connect(ESocket *sock, lcb_host_t *host, unsigned mstmo) +{ + lcb_host_t tmphost; + + if (host == NULL) { + populateHost(&tmphost); + host = &tmphost; + } + + sock->parent = this; + sock->creq.type = LCBIO_CONNREQ_RAW; + sock->creq.u.cs = lcbio_connect( + iot, settings, host, LCB_MS2US(mstmo), conn_cb, sock); + + start(); + + if (sock->sock) { + initSockCommon(sock); + } +} + +void +Loop::populateHost(lcb_host_t *host) +{ + strcpy(host->host, server->getHostString().c_str()); + strcpy(host->port, server->getPortString().c_str()); +} + +extern "C" { +static void timerCallback(void *arg) +{ + Timer *tm = (Timer *)arg; + tm->expired(); +} +} + +Timer::Timer(lcbio_TABLE *iot) +{ + timer = lcbio_timer_new(iot, this, timerCallback); +} + +Timer::~Timer() +{ + destroy(); +} + +void +Timer::destroy() +{ + lcbio_timer_destroy(timer); + timer = NULL; +} + +void +Timer::cancel() +{ + lcbio_timer_disarm(timer); +} + +void +Timer::schedule(unsigned ms) +{ + lcbio_timer_rearm(timer, LCB_MS2US(ms)); +} + +void +Timer::signal() +{ + schedule(0); +} + +// Conditions +BreakCondition::BreakCondition() +{ + broke = false; +} + +bool +BreakCondition::shouldBreak() +{ + if (shouldBreakImpl()) { + broke = true; + return true; + } + return false; +} + +bool +FlushedBreakCondition::shouldBreakImpl() +{ + lcbio_CTX *ctx = sock->ctx; + if (ctx->npending) { + return false; + } + + if (ctx->output && ctx->output->rb.nbytes == 0) { + return true; + } + return false; +} + +bool +ReadBreakCondition::shouldBreakImpl() +{ + lcbio_CTX *ctx = sock->ctx; + unsigned unread = rdb_get_nused(&ctx->ior); + return unread + sock->getReceived().size() >= expected; +} + +extern "C" { +static void +dtor_cb(lcbio_CTX *ctx) +{ + CtxCloseBreakCondition *bc = (CtxCloseBreakCondition *)lcbio_ctx_data(ctx); + bc->gotDtor(); +} +} +void +CtxCloseBreakCondition::closeCtx() +{ + lcbio_ctx_close_ex(s->ctx, NULL, NULL, dtor_cb, this); + s->ctx = NULL; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/socktests/socktest.h b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/socktest.h new file mode 100644 index 00000000..690e7d19 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/socktest.h @@ -0,0 +1,448 @@ +/** + * @file + * Core routines and classes for @ref LCBIO tests. + */ +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#include +#include +#include "settings.h" +#include "logging.h" +#include +#include "internal.h" +#ifndef _WIN32 +#include +#endif + +using namespace LCBTest; + +static inline void +hostFromSockFD(SockFD *sfd, lcb_host_t *tgt) +{ + strcpy(tgt->host, sfd->getLocalHost().c_str()); + sprintf(tgt->port, "%d", sfd->getLocalPort()); +} + +class Loop; +struct ESocket; + +/** Wrapper class for @ref lcbio_CTXPROCS */ +class IOActions { +public: + virtual void onRead(ESocket *s, size_t nr); + virtual void onError(ESocket *s); + virtual void onFlushDone(ESocket *s, size_t, size_t n) {} + virtual void onFlushReady(ESocket *s) {} + virtual ~IOActions() {} +}; + +/** + * This class represents a connecting socket. It is used to wrap the + * functionality of the @ref lcbio_SOCKET structure. + * + * To actually connect the socket, use one of the `connect` methods. + * + * Once the ESocket has been connected, it will also contain a pointer to the + * _server's_ perspective of the connection (see #conn). This can then be + * used to synchronize actions between the client (i.e. this object) and the + * server (the @ref LCBTest::TestConnection object). + * + * To perform I/O on this object, you must coordinate manually between the + * client (i.e. the ESocket object) and the server (i.e. the #conn member): + * + * Writing to the Server: + * + * 1. Add data to send using the put() method + * 2. Schedule the data to be sent, using the schedule() method + * 3. Create a @ref LCBTest::RecvFuture object, passing the number of bytes you expect + * the server to receive. + * 4. Set the server's current future object, via LCBTest::TestConnection::setRecv + * 5. Optionally set a @ref FutureBreakCondition on the @ref Loop object, so + * that the event loop will exit once the operation has been completed by + * the server + * 6. Start the loop via Loop::start() + * + * + * Reading from the Server: + * + * 1. Create a @ref LCBTest::SendFuture object which will contain the data the server + * should send to the client + * 2. Set the @ref LCBTest::TestConnection (#conn)'s current future via + * TestConnection::setSend() method + * 3. Indicate the minimum number of bytes the client expects to receive (this + * shoud be less or equal to the number of bytes passed to the + * @ref LCBTest::SendFuture object). + * 4. Schedule the read via #schedule() + * 5. Create either a @ref ReadBreakCondition, passing the number of bytes you + * want to read (at minimum) from the constructor + * 6. Start the loop via Loop::start() + * 7. The received data should be available via getReceived() + * + * @see lcbio_SOCKET + * @see Loop + * @see Loop::connect() + */ +struct ESocket { + /** This contains the pending request. See the underlying C object for details */ + lcbio_CONNREQ creq; + /** The underlying @ref lcbio_SOCKET */ + lcbio_SOCKET *sock; + /** The current context */ + lcbio_CTX *ctx; + + /** If the initial Loop::connect() failed, this contains the last + * `errno` (or equivalent) which was received on the connection attempt */ + lcbio_OSERR syserr; + + /** Contains the captured library error codes, if applicable */ + lcb_error_t lasterr; + Loop *parent; + + /** Actions implementation. The various callbacks in @ref lcbio_CTXPROCS + * dispatch to this table */ + IOActions *actions; + + /** The *Server* state for this connection */ + TestConnection *conn; + + /** This is used internally by various tests, typically to check that a + * callback was invoked a certain number of times (or at all) */ + int callCount; + + static IOActions defaultActions; + + ESocket() { + sock = NULL; + ctx = NULL; + parent = NULL; + lasterr = LCB_SUCCESS; + syserr = 0; + callCount = 0; + conn = NULL; + creq.u.preq = NULL; + creq.type = LCBIO_CONNREQ_RAW; + actions = &defaultActions; + } + + /** Closes the underlying @ref lcbio_SOCKET object */ + void close(); + + /** Unsets the underlying @ref lcbio_SOCKET object */ + void clear() { ctx = NULL; } + + ~ESocket() { close(); } + + /** + * Add data to be sent. Call the schedule() method to schedule the operation. + * @param b the buffer to send + * @param n the number of bytes to send + * + * @see schedule() + */ + void put(const void *b, size_t n) { + lcbio_ctx_put(ctx, b, n); + } + + void put(const std::string& s) { + put(s.c_str(), s.size()); + } + + /**Request that a given number of bytes should be read from the remote. When + * the data is received, it is placed into an internal buffer (#readbuf). + * + * @param n The minimum number of bytes to be read + */ + void reqrd(size_t n) { + lcbio_ctx_rwant(ctx, n); + } + + /** Wraps lcbio_ctx_schedule() */ + void schedule() { + lcbio_ctx_schedule(ctx); + } + + /**Get the contents of the internal read buffer. The contents are not + * removed. + * + * @return A copy of the contents. + */ + std::string getReceived() { + return std::string(readbuf.begin(), readbuf.end()); + } + + + size_t getUnreadSize() { + return rdb_get_nused(&ctx->ior); + } + + void setActions(IOActions *ioa) { + actions = ioa; + } + + /** Internal method used to associate the socket (if any) with this object */ + void assign(lcbio_SOCKET *sock, lcb_error_t err); + + /** Internal buffer to store read data */ + std::vector readbuf; +}; + +/** + * Base class for timers. + * + * The expired() method should be implemented by subclasses, and will be + * called once the timer expires. + */ +class Timer { +public: + /**Create a new timer + * @param iot The parent IO Table */ + Timer(lcbio_TABLE *iot); + + virtual ~Timer(); + + /**To be implemented by subclasses. Called whenever the timer expires; + * or the signal() method is called. + * + * Note that this method is called from within the event loop (and never + * directly). + */ + virtual void expired() = 0; + void destroy(); + + /** Cancel this timer. The expired() membered function will not be invoked */ + void cancel(); + + /**Set the expiration delay for this timer + * @param ms The expiration delay, in milliseconds */ + void schedule(unsigned ms); + + /** Asynchronously call the #expired() method. */ + void signal(); + +private: + lcbio_pTIMER timer; +}; + + +/** + * This class checks if the loop should break or not. If it should, then + * shouldBreak() returns true. This is required because some event loops are + * in 'always run' mode and don't particularly break once no I/O handles + * remain active. + */ +class BreakCondition { +public: + BreakCondition(); + virtual ~BreakCondition() {} + + /** Called by the loop to determine if it should break. */ + bool shouldBreak(); + + /** Call this to determine if the loop actually exited because the condition + * was satisfied. The loop may also exit after a period of general inactivity + * depending on the implementation. + * + * @return true if the loop was forcefully broken because shouldBreak() returned + * true. */ + bool didBreak() { return broke; } + +protected: + + /**Subclasses should implement this. + * @return true if the loop should break. + * @note This function is only called as long as #broke is false + */ + virtual bool shouldBreakImpl() = 0; + + /** Set by the shouldBreak() wrapper */ + bool broke; +}; + +/** + * This @ref BreakCondition implementation checks to see if the contained + * @ref LCBTest::Future object is complete. If the underlying future is complete, + * it will break the loop. + */ +class FutureBreakCondition : public BreakCondition { +public: + /** @param ft The future to poll */ + FutureBreakCondition(Future *ft) : BreakCondition() { f = ft; } + + virtual ~FutureBreakCondition() {} +protected: + bool shouldBreakImpl() { return f->checkDone(); } + Future *f; +}; + +/** + * This @ref BreakCondition implementation checks to see if the pending write + * buffer from the underying @ref ESocket / @ref lcbio_SOCKET has been completely + * flushed (and thus no longer needed by the underlying socket implementation). + */ +class FlushedBreakCondition : public BreakCondition { +public: + /** @param s The socket to check */ + FlushedBreakCondition(ESocket *s) { sock = s; } + virtual ~FlushedBreakCondition() {} +protected: + bool shouldBreakImpl(); +private: + ESocket *sock; +}; + +/** + * This @ref BreakCondition implementation checks to see if the given socket + * has read at least a certain amount of data + */ +class ReadBreakCondition : public BreakCondition { +public: + /** + * @param s The socket + * @param nr The minimum number of bytes to read before breaking + */ + ReadBreakCondition(ESocket *s, unsigned nr) { sock = s; expected = nr; } + virtual ~ReadBreakCondition() {} +protected: + bool shouldBreakImpl(); +private: + unsigned expected; + ESocket *sock; +}; + +/** + * This @ref BreakCondition implementation checks to see if the given socket + * has an error. + * + * This is generally useful to use in tests where an error is expected, but where + * the loop may terminate before the error is received. + */ +class ErrorBreakCondition : public BreakCondition { +public: + /** @param s Socket to monitor for errors. */ + ErrorBreakCondition(ESocket *s) : BreakCondition() { sock = s; } +protected: + ESocket *sock; + bool shouldBreakImpl() { return sock->lasterr != LCB_SUCCESS; } +}; + +/** TODO: Is this really a break condition? This seems to be used for testing + * the invocation of lcbio_ctx_close_ex()'s callback argument. */ +class CtxCloseBreakCondition : public BreakCondition { +public: + CtxCloseBreakCondition(ESocket *sock) : BreakCondition() { + s = sock; + destroyed = false; + } + + void gotDtor() { + destroyed = true; + } + + void closeCtx(); + +protected: + ESocket *s; + bool destroyed; + bool shouldBreakImpl() { + return destroyed; + } +}; + +/** This implementation will always break the loop. */ +class NullBreakCondition : public BreakCondition { +public: + bool shouldBreakImpl() { return true; } +}; + +/**Internal class used to allow the loop to 'poll' a @ref BreakCondition. + * This will by default check every 2 milliseconds to see if the condition + * has been satisfied or not. */ +class BreakTimer; + +/** Class representing the underlying libcouchbase event loop. */ +class Loop { +public: + Loop(); + ~Loop(); + + /** Runs the loop */ + void start(); + + /** Stops the loop */ + void stop(); + + /** + * This method will connect a newly created @ref ESocket object + * + * This method blocks until the socket is connected, or an error is + * received. See the ESocket::syserr and ESocket::lasterr fields to + * indicate status. + * + * @param sock The socket to connect + * @param host the endpoint to connect to. If this is null, the value of + * populateHost() will be used. + * @param mstmo the timeout (in milliseconds) for the connection to succeed. + */ + void connect(ESocket *sock, lcb_host_t *host = NULL, unsigned mstmo = 1000); + + /** + * This method will connect a newly created @ref ESocket object using the + * connection pool (i.e. @ref lcbio_MGR). The semantics should normally + * be the same as the connect() method, except that if a previously created + * (and available) connection in the socket pool exists, it will be used + * rather than creating a new one. + * + * @param sock The socket to connect to + * @param host The host to connect to. If NULL, the value of populateHost() will be used + * @param mstmo The timeout (in milliseconds) for the connection to succeed + */ + void connectPooled(ESocket *sock, lcb_host_t *host = NULL, unsigned mstmo = 1000); + + /** + * Populate the host object with the host and port pointing to the current + * @ref LCBTest::TestServer (#server) + * @param host The host to populate + */ + void populateHost(lcb_host_t *host); + + /**Sets the condition upon which the loop will terminate + * @param bc the condition */ + void setBreakCondition(BreakCondition *bc) { bcond = bc; } + + /* These members are not really public, but are available for inspection */ + lcbio_MGR *sockpool; + TestServer *server; /**< Underlying server object */ + lcb_settings *settings; + lcb_io_opt_t io; + lcbio_pTABLE iot; + +private: + void scheduleBreak(); + void cancelBreak(); + void initSockCommon(ESocket *s); + + friend class BreakTimer; + + BreakTimer *breakTimer; + std::list pending; + BreakCondition *bcond; +}; + +class SockTest : public ::testing::Test { +protected: + Loop *loop; + void SetUp() { + lcb_initialize_socket_subsystem(); +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif + loop = new Loop(); + } + + void TearDown() { + delete loop; + } +}; diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_basic.cc b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_basic.cc new file mode 100644 index 00000000..fa0119a4 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_basic.cc @@ -0,0 +1,143 @@ +#include "socktest.h" +using namespace LCBTest; +using std::string; +using std::vector; + +class SockConnTest : public SockTest {}; + +TEST_F(SockConnTest, testBasic) +{ + ESocket sock; + + // We can connect + loop->connect(&sock); + ASSERT_FALSE(sock.sock == NULL); + ASSERT_TRUE(sock.creq.u.cs == NULL); + ASSERT_EQ(1, sock.sock->refcount); + + // We can send data + string sendStr("Hello World"); + RecvFuture rf(sendStr.size()); + FutureBreakCondition wbc(&rf); + + sock.conn->setRecv(&rf); + sock.put(sendStr); + sock.schedule(); + loop->setBreakCondition(&wbc); + loop->start(); + rf.wait(); + ASSERT_TRUE(rf.isOk()); + ASSERT_EQ(rf.getString(), sendStr); + + // We can receive data + string recvStr("Goodbye World!"); + SendFuture sf(recvStr); + ReadBreakCondition rbc(&sock, recvStr.size()); + sock.conn->setSend(&sf); + sock.reqrd(recvStr.size()); + sock.schedule(); + loop->setBreakCondition(&rbc); + loop->start(); + sf.wait(); + ASSERT_TRUE(sf.isOk()); + ASSERT_EQ(sock.getReceived(), recvStr); + + // Clean it all up + sock.close(); +} + +static bool isRefused(lcbio_OSERR err) +{ + if (err == ECONNREFUSED || err == ECONNABORTED) { + return true; + } +#ifdef _WIN32 + if (err == WSAECONNREFUSED) { + return true; + } +#endif + return false; +} +// Test a connect without an accept +TEST_F(SockConnTest, testRefused) +{ + ESocket sock; + lcb_host_t host; + strcpy(host.host, "localhost"); + strcpy(host.port, "1"); + loop->connect(&sock, &host, 100000); + ASSERT_TRUE(sock.sock == NULL); + ASSERT_TRUE(isRefused(sock.syserr)); +} + +TEST_F(SockConnTest, testBadDomain) +{ + ESocket sock; + lcb_host_t host; + strcpy(host.host, "domain-should-not-work.nonexist.com"); + strcpy(host.port, "123"); + loop->connect(&sock, &host, 1000); + ASSERT_TRUE(sock.sock == NULL); +} + +TEST_F(SockConnTest, testInvalidPort) +{ + ESocket sock; + lcb_host_t host; + strcpy(host.host, "localhost"); + strcpy(host.port, "111111111"); + loop->connect(&sock, &host, 1000); + ASSERT_TRUE(sock.sock == NULL); +} + +TEST_F(SockTest, testEmptyHost) +{ + ESocket sock; + lcb_host_t host; + host.host[0] = '\0'; + host.port[0] = '\0'; + loop->connect(&sock, &host, 1000); + ASSERT_TRUE(sock.sock == NULL); +} + +TEST_F(SockConnTest, testCancellation) +{ + ESocket sock; + lcb_host_t host; + loop->populateHost(&host); + sock.creq.u.cs = lcbio_connect( + loop->iot, loop->settings, &host, 100000, NULL, NULL); + ASSERT_FALSE(sock.creq.u.cs == NULL); + lcbio_connreq_cancel(&sock.creq); + + NullBreakCondition nbc; + loop->setBreakCondition(&nbc); + loop->start(); +} + +extern "C" { +static void +conncb_1(lcbio_SOCKET *sock, void *arg, lcb_error_t err, lcbio_OSERR syserr) +{ + ESocket *es = (ESocket *)arg; + es->creq.u.cs = NULL; + es->callCount++; + es->ctx = NULL; + es->lasterr = err; + es->parent->stop(); + + // unref is implicit +} +} +TEST_F(SockConnTest, testImmediateUnref) +{ + ESocket sock; + lcb_host_t host; + sock.parent = loop; + loop->populateHost(&host); + sock.creq.u.cs = lcbio_connect( + loop->iot, loop->settings, &host, 1000000, conncb_1, &sock); + loop->start(); + ASSERT_EQ(1, sock.callCount); + ASSERT_TRUE(sock.sock == NULL); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_ctx.cc b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_ctx.cc new file mode 100644 index 00000000..117e6ae3 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_ctx.cc @@ -0,0 +1,73 @@ +#include "socktest.h" +using namespace LCBTest; +using std::string; +using std::vector; +class SockCtxTest : public SockTest {}; + +/** + * While some of the previous tests also used the 'Easy' context implicitly, + * this will try to test some of the more advanced free/destroy functionality. + * + * This mainly relates to freeing the context and inspecting whether any + * callbacks are invoked afterwards. + */ + +TEST_F(SockCtxTest, testClose) +{ + ESocket sock; + + loop->connect(&sock); + for (unsigned ii = 0; ii < 100; ++ii) { + sock.put("Hi"); + sock.schedule(); + } + + CtxCloseBreakCondition cbc(&sock); + cbc.closeCtx(); + loop->setBreakCondition(&cbc); + loop->start(); +} + +struct ReleaseInfo { + lcbio_SOCKET *sock; + bool reusable; + ReleaseInfo() { + sock = NULL; + reusable = false; + } + void reset() { sock = NULL; reusable = false; } +}; + +extern "C" { +static void +release_cb(lcbio_SOCKET *s, int reusable, void *arg) +{ + ReleaseInfo *info = (ReleaseInfo *)arg; + if (reusable) { + info->sock = s; + lcbio_ref(s); + } + info->reusable = !!reusable; +} +} + +TEST_F(SockCtxTest, testReleasable) +{ + ESocket sock; + loop->connect(&sock); + ReleaseInfo ri; + // Release the socket + lcbio_ctx_close(sock.ctx, release_cb, &ri); + sock.clear(); + ASSERT_TRUE(ri.reusable); + + // Schedule some events on it. It should not be releaseable + sock.assign(ri.sock, LCB_SUCCESS); + sock.put("Hi!"); + sock.schedule(); + ri.reset(); + lcbio_ctx_close(sock.ctx, release_cb, &ri); + lcbio_unref(sock.sock); + sock.clear(); + ASSERT_FALSE(ri.reusable); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_manager.cc b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_manager.cc new file mode 100644 index 00000000..744361d9 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_manager.cc @@ -0,0 +1,179 @@ +#include "socktest.h" +#include +using namespace LCBTest; +using std::string; +using std::vector; +class SockMgrTest : public SockTest { + void SetUp() { + SockTest::SetUp(); + loop->sockpool->maxidle = 2; + loop->sockpool->tmoidle = LCB_MS2US(2000); + } +}; + +TEST_F(SockMgrTest, testBasic) +{ + ESocket *sock1 = new ESocket(); + loop->connectPooled(sock1); + lcbio_SOCKET *rawsock = sock1->sock; + delete sock1; + ESocket *sock2 = new ESocket(); + loop->connectPooled(sock2); + ASSERT_EQ(rawsock, sock2->sock); + + ESocket *sock3 = new ESocket(); + loop->connectPooled(sock3); + ASSERT_NE(rawsock, sock3->sock); + delete sock3; + delete sock2; +} + +TEST_F(SockMgrTest, testCancellation) +{ + lcb_host_t host; + loop->populateHost(&host); + lcbio_MGRREQ *req = lcbio_mgr_get(loop->sockpool, + &host, LCB_MS2US(1000), NULL, NULL); + ASSERT_FALSE(req == NULL); + lcbio_mgr_cancel(req); + loop->sockpool->tmoidle = LCB_MS2US(2); + loop->start(); +} + +// See if a connection closed when it was idle is returned or not! +TEST_F(SockMgrTest, testIdleClosed) +{ + struct lcb_cntl_iops_info_st info = { 0 }; + lcb_error_t err = lcb_cntl(NULL, + LCB_CNTL_GET, LCB_CNTL_IOPS_DEFAULT_TYPES, &info); + + ASSERT_EQ(LCB_SUCCESS, err); + switch (info.v.v0.effective) { + case LCB_IO_OPS_SELECT: + case LCB_IO_OPS_LIBEV: + case LCB_IO_OPS_LIBEVENT: + case LCB_IO_OPS_WINIOCP: + break; + + case LCB_IO_OPS_LIBUV: + default: + fprintf(stderr, "SockMgrTest::testIdleClosed: IOPS Type=0x%x does not have a known check_closed. Skipping\n", info.v.v0.effective); + return; + } + + + lcb_socket_t llfd; + ESocket *sock1 = new ESocket(); + loop->connectPooled(sock1); + TestConnection *tc = sock1->conn; + + if (loop->iot->model == LCB_IOMODEL_EVENT) { + llfd = sock1->ctx->sock->u.fd; + } else { + llfd = sock1->ctx->sock->u.sd->socket; + } + + // Wait unitl it's closed by the server + CloseFuture cf(CloseFuture::BEFORE_IO); + tc->setClose(&cf); + cf.wait(); + + delete sock1; + // Since shutdown() is TCP level, while send/recv are socket level, we + // might need to loop a bit until we get something! + int rv = -1, attempts = 0; + do { + char buf = 0; + rv = recv(llfd, &buf, 1, 0); + } while (rv != 0 && ++attempts); + + if (attempts) { + fprintf(stderr, "Needed to loop more than once! (%d times)\n", attempts); + } + + ESocket *sock2 = new ESocket(); + loop->connectPooled(sock2); + + // We should be able to perform IO on this socket. Write a few bytes + string msg("Hello World!"); + RecvFuture rf(msg.size()); + FutureBreakCondition fbc(&rf); + + sock2->conn->setRecv(&rf); + sock2->put(msg); + sock2->schedule(); + + loop->setBreakCondition(&fbc); + loop->start(); + rf.wait(); + ASSERT_TRUE(rf.isOk()); + delete sock2; +} + +struct PCtxDummy : lcbio_PROTOCTX { + int *cVar; + bool invoked; + bool shouldDelete; +}; +extern "C" { +static void protoctx_dtor(lcbio_PROTOCTX *ctx) { + PCtxDummy *d = (PCtxDummy *)ctx; + if (d->shouldDelete) { + delete d; + } else { + *d->cVar += 1; + d->invoked = true; + } +} +} + +TEST_F(SockMgrTest, testMaxIdle) +{ + // Assuming maxidle=2 as defined in the beginning + int destroyCount = 0; + ESocket *socks[4]; + PCtxDummy *ctxs[4]; + + for (int ii = 0; ii < 4; ii++) { + ESocket *s = new ESocket(); + PCtxDummy *pctx = new PCtxDummy(); + + pctx->id = LCBIO_PROTOCTX_MAX; + pctx->dtor = protoctx_dtor; + pctx->cVar = &destroyCount; + pctx->invoked = false; + + loop->connectPooled(s); + lcbio_protoctx_add(s->sock, pctx); + + socks[ii] = s; + ctxs[ii] = pctx; + } + + // Assume they're all alive. Now delete them + for (int ii = 0; ii < 4; ii++) { + delete socks[ii]; + } + + // We need + ASSERT_EQ(2, destroyCount); + for (int ii = 0; ii < 4; ii++) { + if (ctxs[ii]->invoked) { + delete ctxs[ii]; + } else { + ctxs[ii]->shouldDelete = true; + } + } + + // Ensure subsequent connections can also work! + ESocket *otherSocks[8]; + for (int ii = 0; ii < 8; ii++) { + ESocket *s = new ESocket(); + loop->connectPooled(s); + otherSocks[ii] = s; + ASSERT_TRUE(s->sock != NULL); + } + for (int ii = 0; ii < 8; ii++) { + delete otherSocks[ii]; + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_putex.cc b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_putex.cc new file mode 100644 index 00000000..195f77c0 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_putex.cc @@ -0,0 +1,256 @@ +#include "socktest.h" +#include +#include +using namespace LCBTest; +using std::string; +using std::vector; +using std::list; + +/** + * These tests cover the various lcbio_PutEx() routines. + */ + +struct WriteBuffer { + sllist_node ll; + char *buf; + const size_t length; + bool flushed; + + WriteBuffer(const std::string& s) : length(s.size()){ + buf = new char[length]; + memcpy(buf, s.data(), length); + flushed = false; + } + + ~WriteBuffer() { + delete[] buf; + } +}; + +extern "C" { +static nb_SIZE +pdu_callback(void *pdu, nb_SIZE remaining, void *arg) +{ + WriteBuffer *wb = (WriteBuffer *) pdu; + + *(size_t *)arg += 1; + if (wb->length <= remaining) { + wb->flushed = true; + } + return wb->length; +} +} + +struct BufList { + nb_MGR mgr; + BufList() { + netbuf_init(&mgr, NULL); + } + + ~BufList() { + netbuf_cleanup(&mgr); + } + + vector getIOV(size_t *nbytes) { + nb_IOV iovs[32]; + vector ret; + int niov = 0; + *nbytes = netbuf_start_flush(&mgr, iovs, 32, &niov); + for (int ii = 0; ii < niov; ii++) { + lcb_IOV cur; + cur.iov_base = iovs[ii].iov_base; + cur.iov_len = iovs[ii].iov_len; + ret.push_back(cur); + } + return ret; + } + + void updateFlushed(size_t expected, unsigned n) { + size_t pduCount = 0; + netbuf_end_flush2(&mgr, n, pdu_callback, 0, &pduCount); + size_t nremoved = 0; + while (!bufs.empty()) { + WriteBuffer *curBuf = bufs.front(); + if (curBuf->flushed) { + bufs.pop_front(); + delete curBuf; + nremoved++; + } else { + break; + } + } + + if (expected != n) { + netbuf_reset_flush(&mgr); + } + } + + void append(const std::string &s) { + WriteBuffer *wb = new WriteBuffer(s); + nb_SPAN span; + CREATE_STANDALONE_SPAN(&span, wb->buf, wb->length); + netbuf_enqueue_span(&mgr, &span); + netbuf_pdu_enqueue(&mgr, wb, 0); + bufs.push_back(wb); + } + + list bufs; +}; + +class BufActions : public IOActions { +public: + unsigned totalFlushed; + BufActions(){ totalFlushed = 0; } + BufList buflist; + void onFlushReady(ESocket *s) { + int ready = 0; + size_t nbytes; + + do { + vector iovs = buflist.getIOV(&nbytes); + if (!nbytes) { + break; // nothing left to flush + } + ready = lcbio_ctx_put_ex(s->ctx, &iovs[0], iovs.size(), nbytes); + } while (ready); + + if (nbytes) { + lcbio_ctx_wwant(s->ctx); + s->schedule(); + } + } + + void onFlushDone(ESocket *s, size_t expected, size_t nr) { + totalFlushed += nr; + buflist.updateFlushed(expected, nr); + } +}; + +class MyBreakCondition : public BreakCondition { +public: + BufList *bl; + RecvFuture *rf; + MyBreakCondition(BufList *buflist, RecvFuture *ft) { + bl = buflist; + rf = ft; + } +protected: + /** break only after we're fully flushed and all the data's been received */ + bool shouldBreakImpl() { + return rf->checkDone() && bl->bufs.empty(); + } +}; + +class SockPutexTest : public SockTest { +public: + ESocket sock; + BufActions bufActions; + BufList *buflist; + void SetUp() { + SockTest::SetUp(); + sock.setActions(&bufActions); + loop->connect(&sock); + buflist = &bufActions.buflist; + } + void TearDown() { + sock.close(); + SockTest::TearDown(); + } +}; + +TEST_F(SockPutexTest, testBasic) +{ + RecvFuture rf(100); + for (int ii = 0; ii < 100; ii++) { + buflist->append("@"); + } + + sock.conn->setRecv(&rf); + lcbio_ctx_wwant(sock.ctx); + sock.schedule(); + MyBreakCondition mbc(buflist, &rf); + loop->setBreakCondition(&mbc); + loop->start(); + rf.wait(); + string expected(100, '@'); + ASSERT_EQ(rf.getString(), expected); +} + + +TEST_F(SockPutexTest, testBig) +{ + const size_t rchunk = 1000, niters = 1000, expected = rchunk * niters; + + // fill up the write buffers + for (int ii = 0; ii < niters; ii++) { + buflist->append(string(rchunk, '#')); + } + + size_t nconsumed = 0; + size_t nbufsOrig = buflist->bufs.size(); + + /** + * Iterate until all the buffers have been flushed and returned to the + * caller, and until they have all been received + */ + while (buflist->bufs.empty() == false || nconsumed != expected) { + RecvFuture rf(100); + FutureBreakCondition fbc(&rf); + + if (nconsumed != expected) { + rf.reinit(std::min(rchunk, expected - nconsumed)); + sock.conn->setRecv(&rf); + loop->setBreakCondition(&fbc); + } + + if (bufActions.totalFlushed != expected) { + // Make the tests run quicker and only send data when it's not + // flushed + lcbio_ctx_wwant(sock.ctx); + sock.schedule(); + loop->start(); + } + + if (nconsumed != expected) { + rf.wait(); + ASSERT_TRUE(rf.isOk()); + loop->setBreakCondition(NULL); + nconsumed += rf.getBuf().size(); + } + if (bufActions.totalFlushed == expected) { + assert(buflist->bufs.empty()); + } + } + + ASSERT_TRUE(buflist->bufs.empty()); +} + +class TClosedBreakCondition : public BreakCondition { +public: + ESocket *s; + BufList *bl; + TClosedBreakCondition(ESocket *sock, BufList *buflist) { + s = sock; + bl = buflist; + } +protected: + bool shouldBreakImpl() { + return bl->bufs.empty(); + } + +}; +TEST_F(SockPutexTest, testClosed) +{ + sock.conn->close(); + TClosedBreakCondition tcb(&sock, buflist); + + while (sock.lasterr == LCB_SUCCESS) { + buflist->append(string(100, '$')); + lcbio_ctx_wwant(sock.ctx); + sock.schedule(); + loop->setBreakCondition(&tcb); + loop->start(); + } + + ASSERT_TRUE(buflist->bufs.empty()); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_read.cc b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_read.cc new file mode 100644 index 00000000..17c3dc14 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_read.cc @@ -0,0 +1,187 @@ +#include "socktest.h" +using namespace LCBTest; +using std::string; +using std::vector; +class SockReadTest : public SockTest {}; +namespace { + +/** + * Set a specific 'rdwant' value. Send data smaller than the want, and then + * send some more data. + */ +TEST_F(SockReadTest, testWant) +{ + ESocket sock; + loop->connect(&sock); + string expected("Hello World!"); + SendFuture sf(expected); + + ReadBreakCondition cond(&sock, expected.size()); + + sock.reqrd(expected.size() * 2); + sock.schedule(); + sock.conn->setSend(&sf); + loop->setBreakCondition(&cond); + loop->start(); + sf.wait(); + ASSERT_TRUE(cond.didBreak()); + + // If everything is right, we should have nothing inside the buffer + ASSERT_TRUE(sock.getReceived().empty()); + + SendFuture sf2(expected); + sock.conn->setSend(&sf2); + cond = ReadBreakCondition(&sock, expected.size() * 2); + loop->setBreakCondition(&cond); + loop->start(); + sf2.wait(); + expected += expected; + ASSERT_FALSE(sock.getReceived().empty()); + ASSERT_EQ(expected, sock.getReceived()); +} + +// Ensure the 'rdwant' flag is reset when we invoke the callback +TEST_F(SockReadTest, testWantReset) +{ + ESocket sock; + loop->connect(&sock); + string expected("Hi!!!"); + sock.reqrd(expected.size()); + SendFuture sf(expected); + ReadBreakCondition rbc(&sock, expected.size()); + loop->setBreakCondition(&rbc); + ASSERT_EQ(expected.size(), sock.ctx->rdwant); + sock.schedule(); + sock.conn->setSend(&sf); + loop->start(); + ASSERT_EQ(0, sock.ctx->rdwant); +} + +/** + * We should get an error if the socket is closed before we have data + */ +TEST_F(SockReadTest, testBrokenRead) +{ + ESocket sock; + loop->connect(&sock); + CloseFuture cf(CloseFuture::BEFORE_IO); + sock.conn->setClose(&cf); + sock.reqrd(5000); + sock.schedule(); + ErrorBreakCondition ebc(&sock); + loop->setBreakCondition(&ebc); + loop->start(); + cf.wait(); + ASSERT_TRUE(sock.lasterr == LCB_NETWORK_ERROR || + sock.lasterr == LCB_ESOCKSHUTDOWN); +} + +TEST_F(SockReadTest, testReadAhead) +{ + ESocket sock; + loop->connect(&sock); + string sendStr(200, '$'); + unsigned wantSize = sendStr.size() / 2; + SendFuture sf(sendStr); + ReadBreakCondition rbc(&sock, wantSize); + + sock.reqrd(wantSize); + sock.conn->setSend(&sf); + sock.schedule(); + + loop->setBreakCondition(&rbc); + loop->start(); + sf.wait(); + + ASSERT_TRUE(sock.getReceived().size() >= wantSize); + if (sock.getReceived().size() == wantSize) { + fprintf(stderr, "!!! received exactly wantsize. Slow network?\n"); + } +} + +/** + * Test the behavior of an orderly close where all the required data is + * consumed. + */ +TEST_F(SockReadTest, testOrderlyClose) +{ + ESocket sock; + loop->connect(&sock); + string expected(200, '$'); + + SendFuture sf(expected); + ReadBreakCondition rbc(&sock, expected.size()); + CloseFuture cf(CloseFuture::AFTER_IO); + + sock.conn->setSend(&sf); + sock.conn->setClose(&cf); + loop->setBreakCondition(&rbc); + sock.reqrd(expected.size()); + sock.schedule(); + loop->start(); + + cf.wait(); + sf.wait(); + + ASSERT_EQ(expected, sock.getReceived()); +} + +class ChunkReadActions : public IOActions { +public: + int numChunks; + vector buffer; + void onRead(ESocket *s, size_t nr) { + lcbio_CTXRDITER iter; + LCBIO_CTX_ITERFOR(s->ctx, &iter, nr) { + unsigned nbuf = lcbio_ctx_risize(&iter); + void *buf = lcbio_ctx_ribuf(&iter); + numChunks++; + buffer.insert(buffer.end(), (char *)buf, (char *)buf + nbuf); + } + } + + ChunkReadActions() : IOActions() { + numChunks = 0; + } +}; + +class CRABreakCondition : public BreakCondition { +public: + ChunkReadActions *cra; + int expected; + CRABreakCondition(ChunkReadActions *actions, int exp) { + cra = actions; + expected = exp; + } + + bool shouldBreakImpl() { + return cra->numChunks >= expected; + } + +}; +/** Tests the iterator chunking mechanism */ +TEST_F(SockReadTest, testChunkedIter) +{ + // Set the chunked allocator + ESocket sock; + loop->connect(&sock); + rdb_challoc(&sock.ctx->ior, rdb_chunkalloc_new(1)); + + string toSend(20, '+'); + SendFuture sf(toSend); + sock.conn->setSend(&sf); + ChunkReadActions cra; + CRABreakCondition bc(&cra, 20); + + sock.setActions(&cra); + sock.reqrd(toSend.size()); + sock.schedule(); + loop->setBreakCondition(&bc); + loop->start(); + + ASSERT_EQ(toSend.size(), cra.numChunks); + ASSERT_EQ(toSend.size(), cra.buffer.size()); + ASSERT_EQ(toSend, string(cra.buffer.begin(), cra.buffer.end())); + sf.wait(); +} +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_reentrant.cc b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_reentrant.cc new file mode 100644 index 00000000..366de2b0 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_reentrant.cc @@ -0,0 +1,143 @@ +#include "socktest.h" +using namespace LCBTest; +using std::string; +using std::vector; + +class SockReentrantTest : public SockTest {}; + +/** + * This file tests various reentrant actions within the socket handlers. + */ +class ReadAgainAction : public IOActions { +public: + ReadAgainAction() { + callCount = 0; + sf = NULL; + } + + virtual ~ReadAgainAction(){} + + int callCount; + SendFuture *sf; + void onRead(ESocket *s, size_t nr) { + if (callCount++) { + s->parent->stop(); + return; + } + + s->reqrd(nr * 2); + sf = new SendFuture(string(nr, '$')); + s->conn->setSend(sf); + s->schedule(); + } + void onError(ESocket *) { + // do nothing + } +}; + +class CallCountBreakCondition : public BreakCondition { +public: + ReadAgainAction *raa; + CallCountBreakCondition(ReadAgainAction *action) { + raa = action; + } +protected: + bool shouldBreakImpl() { return raa->callCount >= 2; } +}; + +TEST_F(SockReentrantTest, testReadAgain) +{ + ESocket sock; + loop->connect(&sock); + SendFuture sf1(string(100, '#')); + sock.conn->setSend(&sf1); + sock.reqrd(100); + sock.schedule(); + + ReadAgainAction raa; + CallCountBreakCondition bc(&raa); + sock.setActions(&raa); + loop->setBreakCondition(&bc); + loop->start(); + ASSERT_TRUE(raa.callCount == 2); + ASSERT_TRUE(sock.getUnreadSize() >= 200); + ASSERT_TRUE(raa.sf != NULL); + raa.sf->wait(); + delete raa.sf; +} + + +class CloseReadAction : public IOActions { +public: + CloseReadAction() { wasCalled = false; } + virtual ~CloseReadAction() {} + void onRead(ESocket *s, size_t) { + EXPECT_FALSE(wasCalled); + wasCalled = true; + s->parent->stop(); + s->close(); + } + void onError(ESocket *) {} + bool wasCalled; +}; + +class CRABreakCondition : public BreakCondition { +public: + CloseReadAction *cra; + CRABreakCondition(CloseReadAction *action) { cra = action; } +protected: + bool shouldBreakImpl() { return cra->wasCalled; } +}; + +TEST_F(SockReentrantTest, testCloseOnRead) +{ + ESocket sock; + loop->connect(&sock); + SendFuture sf(string(100, '#')); + sock.conn->setSend(&sf); + sock.reqrd(1); + sock.schedule(); + CloseReadAction cra; + CRABreakCondition bc(&cra); + sock.setActions(&cra); + loop->setBreakCondition(&bc); + loop->start(); + sf.wait(); + ASSERT_TRUE(cra.wasCalled); +} + +class CloseWriteAction : public IOActions { +public: + bool wasCalled; + CloseWriteAction() { wasCalled = false; } + virtual ~CloseWriteAction() { } + void onRead(ESocket *s, size_t) { + EXPECT_FALSE(wasCalled); + wasCalled = true; + for (int ii = 0; ii < 100; ii++) { + s->put("Hello!"); + s->schedule(); + } + s->close(); + s->parent->stop(); + } + void onError(ESocket *){} +}; +TEST_F(SockReentrantTest, testCloseOnWrite) +{ + ESocket sock; + loop->connect(&sock); + SendFuture sf(string(100, '#')); + sock.conn->setSend(&sf); + sock.reqrd(1); + sock.schedule(); + CloseWriteAction cwa; + sock.setActions(&cwa); + + RecvFuture rf(1); + FutureBreakCondition fbc(&rf); + loop->setBreakCondition(&fbc); + loop->start(); + sf.wait(); + ASSERT_TRUE(cwa.wasCalled); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_ssl.cc b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_ssl.cc new file mode 100644 index 00000000..707a8fbe --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_ssl.cc @@ -0,0 +1,80 @@ +#include "socktest.h" + +#ifndef LCB_NO_SSL + +#include +using namespace LCBTest; +using std::string; +using std::vector; + +class SSLTest : public SockTest { +protected: + void SetUp() { + lcbio_ssl_global_init(); + lcb_error_t errp = LCB_SUCCESS; + // Initialize the SSL stuff + + SockTest::SetUp(); + loop->settings->sslopts = LCB_SSL_ENABLED|LCB_SSL_NOVERIFY; + loop->settings->ssl_ctx = lcbio_ssl_new(NULL, 1, &errp, loop->settings); + loop->server->factory = TestServer::sslSocketFactory; + EXPECT_FALSE(loop->settings->ssl_ctx == NULL) << lcb_strerror(NULL, errp); + } + + void TearDown() { + lcbio_ssl_free(loop->settings->ssl_ctx); + loop->settings->ssl_ctx = NULL; + SockTest::TearDown(); + } +}; + +TEST_F(SSLTest, testBasic) +{ + // Copy/pasted from SockConnTest::testBasic + + ESocket sock; + + // We can connect + loop->connect(&sock); + ASSERT_FALSE(sock.sock == NULL); + ASSERT_TRUE(sock.creq.u.cs == NULL); + ASSERT_EQ(1, sock.sock->refcount); + + // We can send data + string sendStr("Hello World"); + RecvFuture rf(sendStr.size()); + FutureBreakCondition wbc(&rf); + + sock.conn->setRecv(&rf); + sock.put(sendStr); + sock.schedule(); + loop->setBreakCondition(&wbc); + loop->start(); + rf.wait(); + ASSERT_TRUE(rf.isOk()); + ASSERT_EQ(rf.getString(), sendStr); + + // We can receive data + string recvStr("Goodbye World!"); + SendFuture sf(recvStr); + ReadBreakCondition rbc(&sock, recvStr.size()); + sock.conn->setSend(&sf); + sock.reqrd(recvStr.size()); + sock.schedule(); + loop->setBreakCondition(&rbc); + loop->start(); + sf.wait(); + ASSERT_TRUE(sf.isOk()); + ASSERT_EQ(sock.getReceived(), recvStr); + + // Clean it all up + sock.close(); +} + +#else +class SSLTest : public ::testing::Test {}; +TEST_F(SSLTest, DISABLED_testBasic) +{ + EXPECT_FALSE(true); +} +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_write.cc b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_write.cc new file mode 100644 index 00000000..56068a03 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/socktests/t_write.cc @@ -0,0 +1,95 @@ +#include "socktest.h" +using namespace LCBTest; +using std::string; +using std::vector; +class SockWriteTest : public SockTest {}; + +/** + * This file tests the ability of the sockets to write various pieces + * of data. + */ +TEST_F(SockWriteTest, testMultiWrite) +{ + ESocket sock; + loop->connect(&sock); + + string expected("Hello World!"); + RecvFuture rf(expected.size()); + FutureBreakCondition wbc(&rf); + + sock.put("Hello "); sock.schedule(); + sock.put("World"); sock.schedule(); + sock.put("!"); sock.schedule(); + sock.schedule(); + + sock.conn->setRecv(&rf); + + loop->setBreakCondition(&wbc); + loop->start(); + rf.wait(); + ASSERT_TRUE(rf.isOk()); + ASSERT_EQ(expected, rf.getString()); +} + + +/** + * Test with a very big write + */ +TEST_F(SockWriteTest, testBigWrite) +{ + ESocket sock; + loop->connect(&sock); + string expected(1024*1024*2, '*'); + RecvFuture rf(expected.size()); + sock.conn->setRecv(&rf); + sock.put(expected); + sock.schedule(); + + FutureBreakCondition wbc(&rf); + loop->setBreakCondition(&wbc); + loop->start(); + rf.wait(); + ASSERT_TRUE(rf.isOk()); + ASSERT_EQ(expected, rf.getString()); +} + +/** + * Write to a broken socket. Because close is not synchronous on both ends + * of the connection (even though in this case they are on the same host) + * we need to loop until we get an error. + */ +TEST_F(SockWriteTest, testBrokenFirstWrite) +{ + ESocket sock; + loop->connect(&sock); + + while (sock.lasterr == LCB_SUCCESS) { + CloseFuture cf(CloseFuture::BEFORE_IO); + FlushedBreakCondition fbc(&sock); + sock.conn->setClose(&cf); + sock.put("This should fail"); + sock.schedule(); + loop->setBreakCondition(&fbc); + loop->start(); + cf.wait(); + } +} + +TEST_F(SockWriteTest, testBrokenMultiWrites) +{ + ESocket sock; + loop->connect(&sock); + while (sock.lasterr == LCB_SUCCESS) { + CloseFuture cf(CloseFuture::BEFORE_IO); + FlushedBreakCondition fbc(&sock); + sock.conn->setClose(&cf); + + for (int ii = 0; ii < 100; ii++) { + sock.put("This message should fail"); + sock.schedule(); + } + loop->setBreakCondition(&fbc); + loop->start(); + cf.wait(); + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/start_mock.bat b/couchbase-sys/libcouchbase-2.7.0/tests/start_mock.bat new file mode 100644 index 00000000..2d7f79cf --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/start_mock.bat @@ -0,0 +1,15 @@ +@ECHO OFF +SET lcbdir=%srcdir% +IF "%lcbdir%"=="" ( + SET lcbdir=. +) + +SET MOCKPATH=%lcbdir%\tests\CouchbaseMock.jar + +java ^ + -client^ + -jar "%MOCKPATH%"^ + --nodes=4^ + --host=localhost^ + --port=0^ + %* diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/start_mock.sh b/couchbase-sys/libcouchbase-2.7.0/tests/start_mock.sh new file mode 100755 index 00000000..3bb3cced --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/start_mock.sh @@ -0,0 +1,42 @@ +#! /bin/sh +# +# Copyright 2011 Couchbase, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# We don't want to run memory debugging on java ;) +unset LD_PRELOAD +unset MALLOC_DEBUG +unset UMEM_DEBUG + +# This is a wrapper script to start the Couchbase Mock server. +# We could have started it directly from the C code, but by using +# a script it's a bit easier to test it manually ;) +if [ -z "$srcdir" ]; then + srcdir="." +fi + +for p in "$srcdir/tests" "$srcdir" "tests" "."; do + if [ -f "$p/CouchbaseMock.jar" ]; then + COUCHBASEMOCK="$p/CouchbaseMock.jar" + fi +done + +exec java \ + -client \ + -jar "$COUCHBASEMOCK" \ + --nodes=4 \ + --host=localhost \ + --port=0 \ + "$@" diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/unit_tests.cc b/couchbase-sys/libcouchbase-2.7.0/tests/unit_tests.cc new file mode 100644 index 00000000..176fc328 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/unit_tests.cc @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2010-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include + +#ifdef NO_COUCHBASEMOCK +class MockTestsAreDisabled : public ::testing::Test {}; +TEST_F(MockTestsAreDisabled, MockTestsAreDisabled) { + fprintf(stderr, "*** WARNING\n"); + fprintf(stderr, "*** libcouchbase Java Mock tests are disabled\n"); + fprintf(stderr, "*** Basic memcached functionality (get, set, etc.) \n"); + fprintf(stderr, "*** will NOT be tested\n"); +} +#define SETUP_MOCK_ENV() +#define setup_test_timeout_handler() +#else +#include "iotests/iotests.h" +#define SETUP_MOCK_ENV() ::testing::AddGlobalTestEnvironment(MockEnvironment::getInstance()) +#endif + +int main(int argc, char **argv) +{ + setvbuf(stdout, NULL, _IOLBF, 2048); + setvbuf(stderr, NULL, _IOLBF, 2048); + SETUP_MOCK_ENV(); + ::testing::InitGoogleTest(&argc, argv); + setup_test_timeout_handler(); + return RUN_ALL_TESTS(); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/bad.json b/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/bad.json new file mode 100644 index 00000000..37629db1 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/bad.json @@ -0,0 +1,101 @@ +{ + "rev": 5, + "name": "bucket-1", + "uri": "/pools/default/buckets/bucket-1?bucket_uuid=37e1c1dfc17d74c80c5e3cbbacf57069", + "streamingUri": "/pools/default/bucketsStreaming/bucket-1?bucket_uuid=37e1c1dfc17d74c80c5e3cbbacf57069", + "nodes": [ + { + "couchApiBase": "http://10.0.0.233:8092/bucket-1%2B37e1c1dfc17d74c80c5e3cbbacf57069", + "hostname": "10.0.0.233:8091", + "ports": { + "proxy": 11211, + "direct": 11210 + } + }, + { + "couchApiBase": "http://10.0.0.234:8092/bucket-1%2B37e1c1dfc17d74c80c5e3cbbacf57069", + "hostname": "10.0.0.234:8091", + "ports": { + "proxy": 11211, + "direct": 11210 + } + }, + { + "couchApiBase": "http://10.0.0.250:8092/bucket-1%2B37e1c1dfc17d74c80c5e3cbbacf57069", + "hostname": "10.0.0.250:8091", + "ports": { + "proxy": 11211, + "direct": 11210 + } + } + ], + "nodesExt": [ + { + "services": { + "mgmt": 8091, + "capi": 8092, + "meta": 10000, + "capiSSL": 18092, + "mgmtSSL": 18091, + "kv": 11210, + "projector": 9999, + "kvSSL": 11207, + "moxi": 11211 + }, + "hostname": "10.0.0.233" + }, + { + "services": { + "mgmt": 8091, + "capi": 8092, + "meta": 10000, + "capiSSL": 18092, + "mgmtSSL": 18091, + "kv": 11210, + "projector": 9999, + "kvSSL": 11207, + "moxi": 11211 + }, + "hostname": "10.0.0.234" + }, + { + "services": { + "mgmt": 8091, + "capi": 8092, + "meta": 10000, + "capiSSL": 18092, + "mgmtSSL": 18091, + "kv": 11210, + "projector": 9999, + "kvSSL": 11207, + "moxi": 11211 + }, + "thisNode": true, + "hostname": "10.0.0.250" + } + ], + "nodeLocator": "vbucket", + "uuid": "37e1c1dfc17d74c80c5e3cbbacf57069", + "ddocs": { + "uri": "/pools/default/buckets/bucket-1/ddocs" + }, + "vBucketServerMap": { + "hashAlgorithm": "CRC", + "numReplicas": 1, + "serverList": [ + "10.0.0.233:11210", + "10.0.0.234:11210", + "10.0.0.250:11210" + ], + "vBucketMap": [] + }, + "bucketCapabilitiesVer": "", + "bucketCapabilities": [ + "cbhello", + "touch", + "couchapi", + "cccp", + "xdcrCheckpointing", + "nodesExt" + ] +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/full_25.json b/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/full_25.json new file mode 100644 index 00000000..548e82db --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/full_25.json @@ -0,0 +1,363 @@ +{ + "quota" : { + "rawRAM" : 524288000, + "ram" : 524288000 + }, + "localRandomKeyUri" : "/pools/default/buckets/default/localRandomKey", + "bucketCapabilitiesVer" : "", + "authType" : "sasl", + "uuid" : "cc4b033b19282a4bef76925a11755cc3", + "replicaNumber" : 1, + "vBucketServerMap" : { + "vBucketMap" : [ + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ] + ], + "numReplicas" : 1, + "hashAlgorithm" : "CRC", + "serverList" : [ + "10.0.0.120:11210" + ] + }, + "saslPassword" : "", + "basicStats" : { + "opsPerSec" : 0, + "diskUsed" : 1386470, + "quotaPercentUsed" : 0.420147705078125, + "diskFetches" : 0, + "memUsed" : 2202784, + "dataUsed" : 1301646, + "itemCount" : 6 + }, + "fastWarmupSettings" : false, + "replicaIndex" : false, + "controllers" : { + "startRecovery" : "/pools/default/buckets/default/controller/startRecovery", + "flush" : "/pools/default/buckets/default/controller/doFlush", + "compactDB" : "/pools/default/buckets/default/controller/compactDatabases", + "purgeDeletes" : "/pools/default/buckets/default/controller/unsafePurgeBucket", + "compactAll" : "/pools/default/buckets/default/controller/compactBucket" + }, + "autoCompactionSettings" : false, + "nodeLocator" : "vbucket", + "threadsNumber" : 2, + "bucketCapabilities" : [ + "touch", + "couchapi" + ], + "name" : "default", + "stats" : { + "directoryURI" : "/pools/default/buckets/default/statsDirectory", + "nodeStatsListURI" : "/pools/default/buckets/default/nodes", + "uri" : "/pools/default/buckets/default/stats" + }, + "ddocs" : { + "uri" : "/pools/default/buckets/default/ddocs" + }, + "uri" : "/pools/default/buckets/default?bucket_uuid=cc4b033b19282a4bef76925a11755cc3", + "streamingUri" : "/pools/default/bucketsStreaming/default?bucket_uuid=cc4b033b19282a4bef76925a11755cc3", + "proxyPort" : 0, + "nodes" : [ + { + "status" : "healthy", + "couchApiBaseHTTPS" : "https://10.0.0.120:18092/default", + "thisNode" : true, + "hostname" : "10.0.0.120:8091", + "memoryFree" : 10630004736, + "os" : "i386-apple-darwin11.4.2", + "otpNode" : "ns_1@127.0.0.1", + "mcdMemoryReserved" : 13107, + "mcdMemoryAllocated" : 13107, + "uptime" : "613933", + "clusterCompatibility" : 131077, + "memoryTotal" : 17179869184, + "replication" : 0, + "couchApiBase" : "http://10.0.0.120:8092/default", + "version" : "2.5.1-1083-rel-enterprise", + "ports" : { + "direct" : 11210, + "sslProxy" : 11214, + "httpsMgmt" : 18091, + "proxy" : 11211, + "httpsCAPI" : 18092 + }, + "clusterMembership" : "active", + "interestingStats" : { + "cmd_get" : 0, + "mem_used" : 10186600, + "ep_bg_fetched" : 0, + "ops" : 0, + "get_hits" : 0, + "couch_docs_data_size" : 12353230, + "couch_views_actual_disk_size" : 1509002, + "couch_docs_actual_disk_size" : 13198057, + "curr_items" : 7309, + "vb_replica_curr_items" : 0, + "couch_views_data_size" : 1494638, + "curr_items_tot" : 7309 + }, + "systemStats" : { + "swap_used" : 3255107584, + "cpu_utilization_rate" : 14.8936170212766, + "mem_total" : 17179869184, + "mem_free" : 10630004736, + "swap_total" : 4294967296 + } + } + ], + "bucketType" : "membase" +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/memd_25.json b/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/memd_25.json new file mode 100644 index 00000000..931dc8de --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/memd_25.json @@ -0,0 +1,90 @@ +{ + "quota" : { + "rawRAM" : 209715200, + "ram" : 209715200 + }, + "localRandomKeyUri" : "/pools/default/buckets/memd/localRandomKey", + "bucketCapabilitiesVer" : "", + "authType" : "sasl", + "uuid" : "047928a75783a542eb8382ce8f679aa1", + "replicaNumber" : 0, + "saslPassword" : "", + "basicStats" : { + "opsPerSec" : 0, + "quotaPercentUsed" : 1.06668472290039, + "memUsed" : 2237000, + "itemCount" : 0, + "hitRatio" : 0 + }, + "fastWarmupSettings" : false, + "replicaIndex" : true, + "controllers" : { + "startRecovery" : "/pools/default/buckets/memd/controller/startRecovery", + "compactDB" : "/pools/default/buckets/default/controller/compactDatabases", + "purgeDeletes" : "/pools/default/buckets/memd/controller/unsafePurgeBucket", + "compactAll" : "/pools/default/buckets/memd/controller/compactBucket" + }, + "autoCompactionSettings" : false, + "nodeLocator" : "ketama", + "threadsNumber" : 3, + "bucketCapabilities" : [], + "name" : "memd", + "stats" : { + "directoryURI" : "/pools/default/buckets/memd/statsDirectory", + "nodeStatsListURI" : "/pools/default/buckets/memd/nodes", + "uri" : "/pools/default/buckets/memd/stats" + }, + "ddocs" : { + "uri" : "/pools/default/buckets/memd/ddocs" + }, + "uri" : "/pools/default/buckets/memd?bucket_uuid=047928a75783a542eb8382ce8f679aa1", + "streamingUri" : "/pools/default/bucketsStreaming/memd?bucket_uuid=047928a75783a542eb8382ce8f679aa1", + "proxyPort" : 0, + "nodes" : [ + { + "status" : "healthy", + "thisNode" : true, + "hostname" : "10.0.0.120:8091", + "memoryFree" : 10491817984, + "os" : "i386-apple-darwin11.4.2", + "otpNode" : "ns_1@127.0.0.1", + "mcdMemoryReserved" : 13107, + "mcdMemoryAllocated" : 13107, + "uptime" : "615129", + "clusterCompatibility" : 131077, + "memoryTotal" : 17179869184, + "replication" : 0, + "version" : "2.5.1-1083-rel-enterprise", + "ports" : { + "direct" : 11210, + "sslProxy" : 11214, + "httpsMgmt" : 18091, + "proxy" : 11211, + "httpsCAPI" : 18092 + }, + "clusterMembership" : "active", + "interestingStats" : { + "cmd_get" : 0, + "mem_used" : 12423600, + "ep_bg_fetched" : 0, + "ops" : 0, + "get_hits" : 0, + "couch_docs_data_size" : 13404750, + "couch_views_actual_disk_size" : 1509002, + "couch_docs_actual_disk_size" : 14256490, + "curr_items" : 7309, + "vb_replica_curr_items" : 0, + "couch_views_data_size" : 1494638, + "curr_items_tot" : 7309 + }, + "systemStats" : { + "swap_used" : 3252314112, + "cpu_utilization_rate" : 13.5572139303483, + "mem_total" : 17179869184, + "mem_free" : 10491817984, + "swap_total" : 4294967296 + } + } + ], + "bucketType" : "memcached" +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/memd_30.json b/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/memd_30.json new file mode 100644 index 00000000..7b09329f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/memd_30.json @@ -0,0 +1 @@ +{"rev":17,"name":"memd","uri":"/pools/default/buckets/memd?bucket_uuid=15b8be38964d2acd7b2079988b505ac4","streamingUri":"/pools/default/bucketsStreaming/memd?bucket_uuid=15b8be38964d2acd7b2079988b505ac4","nodes":[{"couchApiBase":"http://$HOST:8092/memd","hostname":"$HOST:8091","ports":{"proxy":11211,"direct":11210}}],"nodesExt":[{"services":{"mgmt":8091,"capi":8092,"moxi":11211,"kv":11210,"kvSSL":11207,"capiSSL":18092,"mgmtSSL":18091}}],"nodeLocator":"ketama","uuid":"15b8be38964d2acd7b2079988b505ac4","bucketCapabilitiesVer":"","bucketCapabilities":["cbhello"]} \ No newline at end of file diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/memd_45.json b/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/memd_45.json new file mode 100644 index 00000000..db8b037e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/memd_45.json @@ -0,0 +1 @@ +{"rev":49,"name":"memd","uri":"/pools/default/buckets/memd?bucket_uuid=5f0bea1472aab794b613ff6f0b9fc936","streamingUri":"/pools/default/bucketsStreaming/memd?bucket_uuid=5f0bea1472aab794b613ff6f0b9fc936","nodes":[{"couchApiBase":"http://10.0.0.195:9500/memd%2B5f0bea1472aab794b613ff6f0b9fc936","hostname":"10.0.0.195:9000","ports":{"proxy":12001,"direct":12000}},{"couchApiBase":"http://$HOST:9501/memd%2B5f0bea1472aab794b613ff6f0b9fc936","hostname":"$HOST:9001","ports":{"proxy":12003,"direct":12002}},{"couchApiBase":"http://$HOST:9502/memd%2B5f0bea1472aab794b613ff6f0b9fc936","hostname":"$HOST:9002","ports":{"proxy":12005,"direct":12004}},{"couchApiBase":"http://$HOST:9503/memd%2B5f0bea1472aab794b613ff6f0b9fc936","hostname":"$HOST:9003","ports":{"proxy":12007,"direct":12006}}],"nodesExt":[{"services":{"mgmt":9000,"mgmtSSL":19000,"fts":9200,"indexAdmin":9100,"indexScan":9101,"indexHttp":9102,"indexStreamInit":9103,"indexStreamCatchup":9104,"indexStreamMaint":9105,"capiSSL":19500,"capi":9500,"kvSSL":11996,"projector":10000,"kv":12000,"moxi":12001,"n1ql":9499,"n1qlSSL":19499},"thisNode":true,"hostname":"10.0.0.195"},{"services":{"mgmt":9001,"mgmtSSL":19001,"fts":9201,"indexAdmin":9106,"indexScan":9107,"indexHttp":9108,"indexStreamInit":9109,"indexStreamCatchup":9110,"indexStreamMaint":9111,"capiSSL":19501,"capi":9501,"kvSSL":11992,"projector":10001,"kv":12002,"moxi":12003,"n1ql":9498,"n1qlSSL":19498}},{"services":{"mgmt":9002,"mgmtSSL":19002,"fts":9202,"indexAdmin":9112,"indexScan":9113,"indexHttp":9114,"indexStreamInit":9115,"indexStreamCatchup":9116,"indexStreamMaint":9117,"capiSSL":19502,"capi":9502,"kvSSL":11988,"projector":10002,"kv":12004,"moxi":12005,"n1ql":9497,"n1qlSSL":19497}},{"services":{"mgmt":9003,"mgmtSSL":19003,"fts":9203,"indexAdmin":9118,"indexScan":9119,"indexHttp":9120,"indexStreamInit":9121,"indexStreamCatchup":9122,"indexStreamMaint":9123,"capiSSL":19503,"capi":9503,"kvSSL":11984,"projector":10003,"kv":12006,"moxi":12007,"n1ql":9496,"n1qlSSL":19496}}],"nodeLocator":"ketama","uuid":"5f0bea1472aab794b613ff6f0b9fc936","bucketCapabilitiesVer":"","bucketCapabilities":["cbhello","nodesExt"]} \ No newline at end of file diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/terse_25.json b/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/terse_25.json new file mode 100644 index 00000000..1a4189bc --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/terse_25.json @@ -0,0 +1,291 @@ +{ + "bucketCapabilitiesVer" : "", + "nodeLocator" : "vbucket", + "bucketCapabilities" : [ + "touch", + "couchapi" + ], + "name" : "default", + "ddocs" : { + "uri" : "/pools/default/buckets/default/ddocs" + }, + "uuid" : "cc4b033b19282a4bef76925a11755cc3", + "uri" : "/pools/default/buckets/default?bucket_uuid=cc4b033b19282a4bef76925a11755cc3", + "vBucketServerMap" : { + "vBucketMap" : [ + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ], + [ + 0, + -1 + ] + ], + "numReplicas" : 1, + "hashAlgorithm" : "CRC", + "serverList" : [ + "$HOST:11210" + ] + }, + "streamingUri" : "/pools/default/bucketsStreaming/default?bucket_uuid=cc4b033b19282a4bef76925a11755cc3", + "rev" : 19, + "nodes" : [ + { + "ports" : { + "direct" : 11210, + "proxy" : 11211 + }, + "couchApiBase" : "http://$HOST:8092/default", + "hostname" : "$HOST:8091" + } + ] +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/terse_30.json b/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/terse_30.json new file mode 100644 index 00000000..16da984e --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/confdata/terse_30.json @@ -0,0 +1 @@ +{"rev":17,"name":"default","uri":"/pools/default/buckets/default?bucket_uuid=373bce23a8f8db41ed6e42a60f866252","streamingUri":"/pools/default/bucketsStreaming/default?bucket_uuid=373bce23a8f8db41ed6e42a60f866252","nodes":[{"couchApiBase":"http://$HOST:8092/default","hostname":"$HOST:8091","ports":{"proxy":11211,"direct":11210}}],"nodesExt":[{"services":{"mgmt":8091,"capi":8092,"moxi":11211,"kv":11210,"kvSSL":11207,"capiSSL":18092,"mgmtSSL":18091}}],"nodeLocator":"vbucket","uuid":"373bce23a8f8db41ed6e42a60f866252","ddocs":{"uri":"/pools/default/buckets/default/ddocs"},"vBucketServerMap":{"hashAlgorithm":"CRC","numReplicas":1,"serverList":["$HOST:11210"],"vBucketMap":[[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1],[0,-1]]},"bucketCapabilitiesVer":"","bucketCapabilities":["cbhello","datatype","touch","couchapi","cccp"]} \ No newline at end of file diff --git a/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/t_config.cc b/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/t_config.cc new file mode 100644 index 00000000..1fb36f20 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tests/vbucket/t_config.cc @@ -0,0 +1,341 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "contrib/lcb-jsoncpp/lcb-jsoncpp.h" + +using std::string; +using std::vector; +using std::map; + +static string getConfigFile(const char *fname) +{ + // Determine where the file is located? + string base = ""; + const char *prefix; + + // Locate source directory + if ((prefix = getenv("CMAKE_CURRENT_SOURCE_DIR"))) { + base = prefix; + } else if ((prefix = getenv("srcdir"))) { + base = prefix; + } else { + base = "./../"; + } + base += "/tests/vbucket/confdata/"; + base += fname; + + // Open the file + std::ifstream ifs; + ifs.open(base.c_str()); + if (!ifs.is_open()) { + std::cerr << "Couldn't open " << base << std::endl; + } + EXPECT_TRUE(ifs.is_open()); + std::stringstream ss; + ss << ifs.rdbuf(); + return ss.str(); +} + +class ConfigTest : public ::testing::Test { +protected: + void testConfig(const char *fname, bool checkNew = false); +}; + + +void +ConfigTest::testConfig(const char *fname, bool checkNew) +{ + string testData = getConfigFile(fname); + lcbvb_CONFIG *vbc = lcbvb_create(); + ASSERT_TRUE(vbc != NULL); + int rv = lcbvb_load_json(vbc, testData.c_str()); + ASSERT_EQ(0, rv); + ASSERT_GT(vbc->nsrv, 0); + + if (vbc->dtype == LCBVB_DIST_VBUCKET) { + ASSERT_GT(vbc->nvb, 0); + + for (unsigned ii = 0; ii < vbc->nvb; ii++) { + lcbvb_vbmaster(vbc, ii); + for (unsigned jj = 0; jj < vbc->nrepl; jj++) { + lcbvb_vbreplica(vbc, ii, jj); + } + } + } + + for (unsigned ii = 0; ii < vbc->nsrv; ++ii) { + lcbvb_SERVER *srv = LCBVB_GET_SERVER(vbc, ii); + ASSERT_TRUE(srv->authority != NULL); + ASSERT_TRUE(srv->hostname != NULL); + ASSERT_GT(srv->svc.data, 0); + ASSERT_GT(srv->svc.mgmt, 0); + if (vbc->dtype == LCBVB_DIST_VBUCKET) { + ASSERT_GT(srv->svc.views, 0); + if (checkNew) { + ASSERT_GT(srv->svc_ssl.views, 0); + } + } + if (checkNew) { + ASSERT_GT(srv->svc_ssl.data, 0); + ASSERT_GT(srv->svc_ssl.mgmt, 0); + } + } + if (checkNew) { + ASSERT_FALSE(NULL == vbc->buuid); + ASSERT_GT(vbc->revid, -1); + } + + const char *k = "Hello"; + size_t nk = strlen(k); + // map the key + int srvix, vbid; + if (vbc->dtype == LCBVB_DIST_KETAMA) { + if (testData.find("$HOST") != string::npos) { + ASSERT_TRUE(vbc->continuum == NULL); + ASSERT_EQ(0, vbc->ncontinuum); + lcbvb_replace_host(vbc, "localhost"); + } + + ASSERT_TRUE(vbc->continuum != NULL); + ASSERT_EQ(160 * vbc->nsrv, vbc->ncontinuum); + lcbvb_map_key(vbc, k, nk, &vbid, &srvix); + ASSERT_EQ(0, vbid); + } else { + lcbvb_map_key(vbc, k, nk, &vbid, &srvix); + ASSERT_NE(0, vbid); + } + lcbvb_destroy(vbc); +} + +TEST_F(ConfigTest, testBasicConfigs) +{ + testConfig("full_25.json"); + testConfig("terse_25.json"); + testConfig("memd_25.json"); + testConfig("terse_30.json", true); + testConfig("memd_30.json", true); +} + +TEST_F(ConfigTest, testGeneration) +{ + lcbvb_CONFIG *cfg = lcbvb_create(); + lcbvb_genconfig(cfg, 4, 1, 1024); + char *js = lcbvb_save_json(cfg); + lcbvb_destroy(cfg); + + cfg = lcbvb_create(); + int rv = lcbvb_load_json(cfg, js); + ASSERT_EQ(0, rv); + ASSERT_EQ(4, cfg->nsrv); + ASSERT_EQ(1, cfg->nrepl); + ASSERT_EQ(LCBVB_DIST_VBUCKET, cfg->dtype); + ASSERT_EQ(1024, cfg->nvb); + lcbvb_destroy(cfg); + free(js); +} + +TEST_F(ConfigTest, testAltMap) +{ + lcbvb_CONFIG *cfg = lcbvb_create(); + lcbvb_genconfig(cfg, 4, 1, 64); + lcbvb_genffmap(cfg); + + string key("Dummy Key"); + int vbix = lcbvb_k2vb(cfg, key.c_str(), key.size()); + int master = lcbvb_vbmaster(cfg, vbix); + int oldmaster = master; + + int altix = lcbvb_nmv_remap(cfg, vbix, master); + ASSERT_GT(altix, -1) << "Alternative index > -1"; + ASSERT_NE(altix, master) << "NMV Remap works with correct master"; + + master = altix; + altix = lcbvb_nmv_remap(cfg, vbix, oldmaster); + ASSERT_EQ(master, altix) << "NMV Remap doesn't do anything with old master"; + lcbvb_destroy(cfg); +} + +TEST_F(ConfigTest, testGetReplicaNode) +{ + lcbvb_CONFIG *cfg = lcbvb_create(); + lcbvb_genconfig(cfg, 4, 1, 2); + + // Select a random vbucket + int srvix = cfg->vbuckets[0].servers[0]; + ASSERT_NE(-1, srvix); + int rv = lcbvb_vbmaster(cfg, 0); + ASSERT_EQ(srvix, rv); + + srvix = cfg->vbuckets[0].servers[1]; + ASSERT_NE(-1, srvix); + rv = lcbvb_vbreplica(cfg, 0, 0); + ASSERT_EQ(srvix, rv); + + rv = lcbvb_vbreplica(cfg, 0, 1); + ASSERT_EQ(-1, rv); + + rv = lcbvb_vbreplica(cfg, 0, 9999); + ASSERT_EQ(-1, rv); + lcbvb_destroy(cfg); + + cfg = lcbvb_create(); + lcbvb_genconfig(cfg, 1, 0, 2); + rv = lcbvb_vbmaster(cfg, 0); + ASSERT_NE(-1, rv); + rv = lcbvb_vbreplica(cfg, 0, 0); + ASSERT_EQ(-1, rv); + lcbvb_destroy(cfg); + +} + +TEST_F(ConfigTest, testBadInput) +{ + lcbvb_CONFIG *cfg = lcbvb_create(); + int rc = lcbvb_load_json(cfg, "{}"); + ASSERT_EQ(-1, rc); + lcbvb_destroy(cfg); + + cfg = lcbvb_create(); + rc = lcbvb_load_json(cfg, "INVALIDJSON"); + ASSERT_EQ(-1, rc); + lcbvb_destroy(cfg); + + cfg = lcbvb_create(); + rc = lcbvb_load_json(cfg, ""); + ASSERT_EQ(-1, rc); + lcbvb_destroy(cfg); + +} + +TEST_F(ConfigTest, testEmptyMap) +{ + string emptyTxt = getConfigFile("bad.json"); + lcbvb_CONFIG *cfg = lcbvb_create(); + int rc = lcbvb_load_json(cfg, emptyTxt.c_str()); + ASSERT_EQ(-1, rc); + lcbvb_destroy(cfg); +} + +TEST_F(ConfigTest, testNondataNodes) +{ + // Tests the handling of nodes which don't have any data in them + const size_t nservers = 6; + const size_t ndatasrv = 3; + const size_t nreplica = ndatasrv - 1; + + vector servers; + servers.resize(nservers); + + + size_t ii; + for (ii = 0; ii < nservers-ndatasrv; ++ii) { + lcbvb_SERVER& server = servers[ii]; + memset(&server, 0, sizeof server); + server.svc.data = 1000 + ii; + server.svc.views = 2000 + ii; + server.hostname = const_cast("dummy.host.ru"); + } + + for (; ii < nservers; ii++) { + lcbvb_SERVER& server = servers[ii]; + memset(&server, 0, sizeof server); + server.svc.n1ql = 3000 + ii; + server.hostname = const_cast("query.host.biz"); + } + + lcbvb_CONFIG *cfg_ex = lcbvb_create(); + int rv = lcbvb_genconfig_ex(cfg_ex, "default", NULL, + &servers[0], + servers.size(), // include non-data servers + nreplica, + 1024); + ASSERT_EQ(0, rv); + lcbvb_genffmap(cfg_ex); + + lcbvb_CONFIG *cfg_old = lcbvb_create(); + rv = lcbvb_genconfig_ex(cfg_old, "default", NULL, + &servers[0], ndatasrv, nreplica, 1024); + ASSERT_EQ(0, rv); + lcbvb_genffmap(cfg_old); + + ASSERT_EQ(ndatasrv, cfg_ex->ndatasrv); + ASSERT_EQ(nservers, cfg_ex->nsrv); + + ASSERT_EQ(ndatasrv, cfg_old->ndatasrv); + ASSERT_EQ(ndatasrv, cfg_old->nsrv); + + // So far, so good. + vector keys; + for (ii = 0; ii < 1024; ii++) { + std::stringstream ss; + ss << "Key_" << ii; + keys.push_back(ss.str()); + } + + int vbid, ix_exp, ix_cur; + // Ensure vBucket mapping, etc. is the same + for (ii = 0; ii < keys.size(); ii++) { + const string& s = keys[ii]; + + lcbvb_map_key(cfg_old, s.c_str(), s.size(), &vbid, &ix_exp); + lcbvb_map_key(cfg_ex, s.c_str(), s.size(), &vbid, &ix_cur); + ASSERT_TRUE(ix_exp > -1 && ix_exp < cfg_ex->ndatasrv); + ASSERT_EQ(ix_exp, ix_cur); + } + + // On the new config, ensure that remap never maps to a non-data node. + for (ii = 0; ii < keys.size(); ii++) { + const string& s = keys[ii]; + for (size_t jj = 0; jj < cfg_ex->nsrv * 2; jj++) { + int ix; + lcbvb_map_key(cfg_ex, s.c_str(), s.size(), &vbid, &ix); + int newix = lcbvb_nmv_remap(cfg_ex, vbid, ix); + if (newix == -1) { + continue; + } else { + ASSERT_TRUE(newix < cfg_ex->ndatasrv); + } + } + } + + // Test with ketama + lcbvb_make_ketama(cfg_ex); + lcbvb_make_ketama(cfg_old); + for (ii = 0; ii < keys.size(); ii++) { + const string& s = keys[ii]; + lcbvb_map_key(cfg_old, s.c_str(), s.size(), &vbid, &ix_exp); + lcbvb_map_key(cfg_ex, s.c_str(), s.size(), &vbid, &ix_cur); + ASSERT_TRUE(ix_exp > -1 && ix_exp < cfg_old->ndatasrv); + ASSERT_EQ(ix_exp, ix_cur); + } + + // destroy 'em + lcbvb_destroy(cfg_ex); + lcbvb_destroy(cfg_old); + +} + +TEST_F(ConfigTest, testKetamaUniformity) +{ + string txt = getConfigFile("memd_45.json"); + lcbvb_CONFIG *vbc = lcbvb_parse_json(txt.c_str()); + ASSERT_TRUE(vbc != NULL); + ASSERT_EQ(4, vbc->nsrv); + ASSERT_EQ(LCBVB_DIST_KETAMA, vbc->dtype); + + // Ensure the continuum stuff is NULL! + ASSERT_TRUE(vbc->continuum == NULL); + ASSERT_EQ(0, vbc->ncontinuum); + lcbvb_replace_host(vbc, "localhost"); + + ASSERT_STREQ("10.0.0.195:12000", vbc->servers[0].authority); + ASSERT_STREQ("localhost:12002", vbc->servers[1].authority); + ASSERT_STREQ("localhost:12004", vbc->servers[2].authority); + ASSERT_STREQ("localhost:12006", vbc->servers[3].authority); + lcbvb_destroy(vbc); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tools/CMakeLists.txt b/couchbase-sys/libcouchbase-2.7.0/tools/CMakeLists.txt new file mode 100644 index 00000000..b90e668f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tools/CMakeLists.txt @@ -0,0 +1,51 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +INCLUDE_DIRECTORIES(${SOURCE_ROOT}/contrib/cliopts) +FILE(GLOB T_COMMONSRC common/*.cc) +ADD_LIBRARY(lcbtools OBJECT ${T_COMMONSRC}) + +ADD_EXECUTABLE(cbc cbc.cc + $ $) +TARGET_LINK_LIBRARIES(cbc couchbase) + +ADD_EXECUTABLE(cbc-pillowfight cbc-pillowfight.cc + $ $ $) + +TARGET_LINK_LIBRARIES(cbc-pillowfight couchbase) + +ADD_EXECUTABLE(cbc-n1qlback cbc-n1qlback.cc + $ $) +TARGET_LINK_LIBRARIES(cbc-n1qlback couchbase) + +INSTALL(TARGETS cbc cbc-pillowfight cbc-n1qlback + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +# Set this before INSTALL_PDBS in order to set the correct output name +IF(MSVC) + SET_TARGET_PROPERTIES(cbc PROPERTIES DEBUG_OUTPUT_NAME cbc_d) + SET_TARGET_PROPERTIES(cbc-pillowfight PROPERTIES DEBUG_OUTPUT_NAME cbc-pillowfight_d) + SET_TARGET_PROPERTIES(cbc-n1qlback PROPERTIES DEBUG_OUTPUT_NAME cbc-n1qlback_d) +ENDIF() + +INSTALL_PDBS(cbc) +INSTALL_PDBS(cbc-pillowfight) +INSTALL_PDBS(cbc-n1qlback) + +SET_TARGET_PROPERTIES(lcbtools PROPERTIES COMPILE_FLAGS "${LCB_CORE_CXXFLAGS}") +SET_SOURCE_FILES_PROPERTIES(cbc.cc cbc-pillowfight.cc cbc-n1qlback.cc PROPERTIES COMPILE_FLAGS "${LCB_CORE_CXXFLAGS}") + +IF(NOT WIN32) + LIST(APPEND CBC_SUBCOMMANDS + cat create observe observe-seqno incr decr mcflush hash lock + unlock rm stats version verbosity view n1ql admin + bucket-create bucket-delete bucket-flush connstr write-config strerror + touch) + + FOREACH(subcmd IN ITEMS ${CBC_SUBCOMMANDS}) + ADD_CUSTOM_COMMAND(TARGET cbc POST_BUILD + COMMAND ln -sf cbc "cbc-${subcmd}" + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + COMMENT "Linking cbc -> cbc-${subcmd}") + INSTALL(FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/cbc-${subcmd} + DESTINATION ${CMAKE_INSTALL_BINDIR}) + ENDFOREACH() +ENDIF() diff --git a/couchbase-sys/libcouchbase-2.7.0/tools/cbc-handlers.h b/couchbase-sys/libcouchbase-2.7.0/tools/cbc-handlers.h new file mode 100644 index 00000000..59fcffad --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tools/cbc-handlers.h @@ -0,0 +1,462 @@ +#ifndef CBC_HANDLERS_H +#define CBC_HANDLERS_H +#include "config.h" +#include "common/options.h" +#include "common/histogram.h" + +namespace cbc { +#define HANDLER_DESCRIPTION(s) const char* description() const { return s; } +#define HANDLER_USAGE(s) const char* usagestr() const { return s; } +class Handler { +public: + Handler(const char *name); + virtual ~Handler(); + virtual const char * description() const = 0; + virtual const char * usagestr() const { return NULL; } + void execute(int argc, char **argv); + +protected: + virtual const std::string& getLoneArg(bool required = false); + virtual const std::string& getRequiredArg() { return getLoneArg(true); } + virtual void addOptions(); + virtual void run(); + cliopts::Parser parser; + ConnParams params; + lcb_t instance; + Histogram hg; + std::string cmdname; +}; + + +class GetHandler : public Handler { +public: + GetHandler(const char *name = "get") : + Handler(name), o_replica("replica"), o_exptime("expiry") {} + + const char* description() const { + if (isLock()) { + return "Lock keys and retrieve them from the cluster"; + } else { + return "Retrieve items from the cluster"; + } + } + +protected: + void addOptions(); + void run(); + +private: + cliopts::StringOption o_replica; + cliopts::UIntOption o_exptime; + bool isLock() const { return cmdname == "lock"; } +}; + +class TouchHandler : public Handler { +public: + TouchHandler(const char *name = "touch") : + Handler(name), o_exptime("expiry") { + o_exptime.abbrev('e').mandatory(true); + } + HANDLER_DESCRIPTION("Updated expiry times for documents") +protected: + void addOptions(); + void run(); +private: + cliopts::UIntOption o_exptime; +}; + +class SetHandler : public Handler { +public: + SetHandler(const char *name = "create") : Handler(name), + o_flags("flags"), o_exp("expiry"), o_add("add"), o_persist("persist-to"), + o_replicate("replicate-to"), o_value("value"), o_json("json"), + o_mode("mode") { + + o_flags.abbrev('f').description("Flags for item"); + o_exp.abbrev('e').description("Expiry for item"); + o_add.abbrev('a').description("Fail if item exists").hide(); + o_persist.abbrev('p').description("Wait until item is persisted to this number of nodes"); + o_replicate.abbrev('r').description("Wait until item is replicated to this number of nodes"); + o_value.abbrev('V').description("Value to use. If unspecified, read from standard input"); + o_json.abbrev('J').description("Indicate to the server that this item is JSON"); + o_mode.abbrev('M').description("Mode to use when storing"); + o_mode.argdesc("upsert|insert|replace"); + o_mode.setDefault("upsert"); + } + + const char* description() const { + if (hasFileList()) { + return "Store files to the server"; + } else { + return "Store item to the server"; + } + } + + const char* usagestr() const { + if (hasFileList()) { + return "[OPTIONS...] FILE ..."; + } else { + return "[OPTIONS...] KEY -V VALUE"; + } + } + + bool hasFileList() const { return cmdname == "cp"; } + + virtual lcb_storage_t mode(); + +protected: + void run(); + void addOptions(); + void storeItem(const std::string& key, const char *value, size_t nvalue); + void storeItem(const std::string& key, FILE *input); + +private: + cliopts::UIntOption o_flags; + cliopts::UIntOption o_exp; + cliopts::BoolOption o_add; + cliopts::IntOption o_persist; + cliopts::IntOption o_replicate; + cliopts::StringOption o_value; + cliopts::BoolOption o_json; + cliopts::StringOption o_mode; + std::map items; +}; + +class HashHandler : public Handler { +public: + HANDLER_DESCRIPTION("Get mapping information for keys") + HANDLER_USAGE("KEY ... [OPTIONS ...]") + HashHandler() : Handler("hash") {} +protected: + void run(); +}; + +class ObserveHandler : public Handler { +public: + ObserveHandler() : Handler("observe") { } + HANDLER_DESCRIPTION("Obtain persistence and replication status for keys") + HANDLER_USAGE("KEY ... ") +protected: + void run(); +}; + +class ObserveSeqnoHandler : public Handler { +public: + ObserveSeqnoHandler() : Handler("observe-seqno") {} + + HANDLER_DESCRIPTION("Request information about a particular vBucket UUID") + HANDLER_USAGE("UUID") + +protected: + void run(); +}; + +class UnlockHandler : public Handler { +public: + HANDLER_DESCRIPTION("Unlock keys") + HANDLER_USAGE("KEY CAS [OPTIONS ...]") + UnlockHandler() : Handler("unlock") {} +protected: + void run(); +}; + +class VersionHandler : public Handler { +public: + HANDLER_DESCRIPTION("Display information about libcouchbase") + VersionHandler() : Handler("version") {} + void run(); + void addOptions() {} +}; + +class RemoveHandler : public Handler { +public: + HANDLER_DESCRIPTION("Remove items from the cluster") + HANDLER_USAGE("KEY ... [OPTIONS ...]") + RemoveHandler() : Handler("rm") {} +protected: + void run(); +}; + +class StatsHandler : public Handler { +public: + HANDLER_DESCRIPTION("Retrieve cluster statistics") + HANDLER_USAGE("[STATS_KEY ...] [OPTIONS ...]") + StatsHandler() : Handler("stats"), o_keystats("keystats") { + o_keystats.description("Keys are document IDs. retrieve information about them"); + } +protected: + void run(); + void addOptions() { + Handler::addOptions(); + parser.addOption(o_keystats); + } +private: + cliopts::BoolOption o_keystats; +}; + +class VerbosityHandler : public Handler { +public: + HANDLER_DESCRIPTION("Modify the memcached logging level") + HANDLER_USAGE(" [OPTIONS ...]") + VerbosityHandler() : Handler("verbosity") {} +protected: + void run(); +}; + +class McFlushHandler : public Handler { +public: + HANDLER_DESCRIPTION("Flush a memcached bucket") + McFlushHandler() : Handler("mcflush") {} +protected: + void run(); +}; + +class ArithmeticHandler : public Handler { +public: + HANDLER_USAGE("KEY ... [OPTIONS ...]") + + ArithmeticHandler(const char *name) : Handler(name), + o_initial("initial"), o_delta("delta"), o_expiry("expiry") { + + o_initial.description("Initial value if item does not exist"); + o_delta.setDefault(1); + o_expiry.abbrev('e').description("Expiration time for key"); + } +protected: + cliopts::ULongLongOption o_initial; + cliopts::ULongLongOption o_delta; + cliopts::UIntOption o_expiry; + void run(); + virtual bool shouldInvert() const = 0; + void addOptions() { + Handler::addOptions(); + parser.addOption(o_initial); + parser.addOption(o_delta); + parser.addOption(o_expiry); + } +}; + +class IncrHandler : public ArithmeticHandler { +public: + HANDLER_DESCRIPTION("Increment a counter") + IncrHandler() : ArithmeticHandler("incr") { + o_delta.description("Amount to increment by"); + } +protected: + bool shouldInvert() const { return false; } +}; + +class DecrHandler : public ArithmeticHandler { +public: + HANDLER_DESCRIPTION("Decrement a counter") + DecrHandler() : ArithmeticHandler("decr") { + o_delta.description("Amount to decrement by"); + } +protected: + bool shouldInvert() const { return true; } +}; + +class ViewsHandler : public Handler { +public: + ViewsHandler() : Handler("view"), + o_spatial("spatial"), o_incdocs("with-docs"), o_params("params") {} + + HANDLER_DESCRIPTION("Query a view") + HANDLER_USAGE("DESIGN/VIEW") + +protected: + void run(); + void addOptions() { + Handler::addOptions(); + parser.addOption(o_spatial); + parser.addOption(o_incdocs); + parser.addOption(o_params); + } +private: + cliopts::BoolOption o_spatial; + cliopts::BoolOption o_incdocs; + cliopts::StringOption o_params; +}; + +class N1qlHandler : public Handler { +public: + N1qlHandler() : Handler("view"), o_args("qarg"), o_opts("qopt"), + o_prepare("prepare") {} + HANDLER_DESCRIPTION("Execute a N1QL Query") + HANDLER_USAGE("QUERY [--qarg PARAM1=VALUE1 --qopt PARAM2=VALUE2]") + +protected: + void run(); + + void addOptions() { + Handler::addOptions(); + o_args.description( + "Specify values for placeholders (can be specified multiple times"); + o_args.abbrev('A'); + o_args.argdesc("PLACEHOLDER_PARAM=PLACEHOLDER_VALUE"); + + o_opts.description("Additional query options"); + o_opts.abbrev('Q'); + + o_prepare.description("Prepare query before issuing"); + + parser.addOption(o_args); + parser.addOption(o_opts); + parser.addOption(o_prepare); + } +private: + cliopts::ListOption o_args; + cliopts::ListOption o_opts; + cliopts::BoolOption o_prepare; +}; + +class HttpReceiver { +public: + HttpReceiver() : statusInvoked(false) {} + virtual ~HttpReceiver() {} + void maybeInvokeStatus(const lcb_RESPHTTP*); + void install(lcb_t); + virtual void handleStatus(lcb_error_t, int) {} + virtual void onDone() {} + virtual void onChunk(const char *data, size_t size) { + resbuf.append(data, size); + } + bool statusInvoked; + std::string resbuf; + std::map headers; +}; + +class HttpBaseHandler : public Handler, public HttpReceiver { +public: + HttpBaseHandler(const char *name) : Handler(name) , + o_method("method") { + + o_method.setDefault("GET").abbrev('X').description("HTTP Method to use"); + } + +protected: + void run(); + virtual std::string getURI() = 0; + virtual const std::string& getBody(); + virtual std::string getContentType() { return ""; } + virtual bool isAdmin() const { return false; } + virtual lcb_http_method_t getMethod(); + virtual void handleStatus(lcb_error_t err, int code); + virtual void addOptions() { + if (isAdmin()) { + params.setAdminMode(); + } + Handler::addOptions(); + parser.addOption(o_method); + } + cliopts::StringOption o_method; + +private: + std::string body_cached; +}; + +class AdminHandler : public HttpBaseHandler { +public: + HANDLER_DESCRIPTION("Invoke an administrative REST API") + HANDLER_USAGE("PATH ... [OPTIONS ...]") + AdminHandler(const char *name = "admin") : HttpBaseHandler(name) {} +protected: + virtual void run(); + virtual std::string getURI(); + virtual bool isAdmin() const { return true; } + +}; + +class BucketCreateHandler : public AdminHandler { +public: + HANDLER_DESCRIPTION("Create a bucket") + HANDLER_USAGE("NAME [OPTIONS ...]") + BucketCreateHandler() : AdminHandler("bucket-create"), + o_btype("bucket-type"), + o_ramquota("ram-quota"), + o_bpass("bucket-password"), + o_replicas("num-replicas"), + o_proxyport("moxi-port"), + isMemcached(false) + { + o_btype.description("Bucket type {couchbase,memcached}").setDefault("couchbase"); + o_ramquota.description("RAM Quota for bucket (MB)").setDefault(100); + o_bpass.description("Bucket password"); + o_replicas.description("Number of replicas for bucket").setDefault(1); + o_proxyport.description("[Compatibility] memcached listening port"); + + } + +protected: + virtual void run(); + virtual void addOptions() { + AdminHandler::addOptions(); + parser.addOption(o_btype); + parser.addOption(o_ramquota); + parser.addOption(o_bpass); + parser.addOption(o_replicas); + parser.addOption(o_proxyport); + } + + std::string getURI() { return "/pools/default/buckets"; } + const std::string& getBody() { return body_s; } + std::string getContentType() { return "application/x-www-form-urlencoded"; } + lcb_http_method_t getMethod() { return LCB_HTTP_METHOD_POST; } + +private: + cliopts::StringOption o_btype; + cliopts::UIntOption o_ramquota; + cliopts::StringOption o_bpass; + cliopts::UIntOption o_replicas; + cliopts::UIntOption o_proxyport; + std::string body_s; + bool isMemcached; +}; + +class BucketDeleteHandler : public AdminHandler { +public: + HANDLER_DESCRIPTION("Delete a bucket") + HANDLER_USAGE("NAME [OPTIONS ...]") + BucketDeleteHandler() : AdminHandler("bucket-delete") {} + +protected: + void run() { + bname = getRequiredArg(); + AdminHandler::run(); + } + std::string getURI() { return std::string("/pools/default/buckets/") + bname; } + lcb_http_method_t getMethod() { return LCB_HTTP_METHOD_DELETE; } + const std::string& getBody() { static std::string e; return e; } +private: + std::string bname; +}; + +class BucketFlushHandler : public Handler { +public: + HANDLER_DESCRIPTION("Flush a bucket") + HANDLER_USAGE("[COMMON OPTIONS ...]") + BucketFlushHandler() : Handler("bucket-flush") {} +protected: + void run(); +}; + +class ConnstrHandler : public Handler { +public: + HANDLER_DESCRIPTION("Parse a connection string and provide info on its components") + HANDLER_USAGE("CONNSTR") + ConnstrHandler() : Handler("connstr") {} +protected: + void handleOptions() { } + void run(); +}; + +class WriteConfigHandler : public Handler { +public: + HANDLER_DESCRIPTION("Write the configuration file based on arguments passed") + WriteConfigHandler() : Handler("write-config") {} +protected: + void run(); +}; + +} +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/tools/cbc-n1qlback.cc b/couchbase-sys/libcouchbase-2.7.0/tools/cbc-n1qlback.cc new file mode 100644 index 00000000..421f604d --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tools/cbc-n1qlback.cc @@ -0,0 +1,439 @@ +/* + * Copyright 2015 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LCB_NO_DEPR_CXX_CTORS + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // random_shuffle +#include +#include +#ifndef WIN32 +#include +#include // isatty() +#endif +#include "common/options.h" +#include "common/histogram.h" + +using namespace cbc; +using namespace cliopts; +using std::vector; +using std::string; +using std::cerr; +using std::endl; + +#ifndef _WIN32 +static bool use_ansi_codes = true; +#else +static bool use_ansi_codes = false; +#endif + +static void do_or_die(lcb_error_t rc) +{ + if (rc != LCB_SUCCESS) { + std::stringstream ss; + ss << "[" << std::hex << rc << "] " << lcb_strerror(NULL, rc); + throw std::runtime_error(ss.str()); + } +} + +class Metrics { +public: + Metrics() + : n_rows(0), n_queries(0), n_errors(0), last_update(time(NULL)), hg(NULL) + { + #ifndef _WIN32 + if (pthread_mutex_init(&m_lock, NULL) != 0) { + abort(); + } + #endif + start_time = last_update; + } + + void update_row(size_t n = 1) { n_rows += n; update_display(); } + void update_done(size_t n = 1) { n_queries += n; update_display(); } + void update_error(size_t n = 1) { n_errors += n; update_display(); } + + void update_timings(lcb_U64 duration) { + if (hg != NULL) { + hg->record(duration); + } + } + +#ifndef _WIN32 + bool is_tty() const { return isatty(STDOUT_FILENO); } + void lock() { pthread_mutex_lock(&m_lock); } + void unlock() { pthread_mutex_unlock(&m_lock); } +#else + void lock(){} + void unlock(){} + bool is_tty() const { return false; } +#endif + void prepare_screen() + { + if (is_tty() && use_ansi_codes) { + printf("\n\n\n"); + } + } + + void prepare_timings() + { + if (hg == NULL) { + hg = new Histogram(); + hg->installStandalone(stdout); + } + } + +private: + void update_display() + { + time_t now = time(NULL); + time_t duration = now - last_update; + + if (!duration) { + return; + } + + last_update = now; + + const char *prefix = use_ansi_codes ? "\x1B[K" : ""; + const char *final_suffix; + + // Only use "ticker" style updates if we're a TTY and we have no + // following timings. + if (use_ansi_codes && is_tty() && hg == NULL) { + // Move up 3 cursors + printf("\x1B[2A"); + prefix = "\x1B[K"; + final_suffix = "\r"; + } else { + // Determine the total number of time + unsigned total_duration = now - start_time; + printf("\n"); // Clear line.. + printf("+%us\n", total_duration); + prefix = ""; + final_suffix = "\n"; + } + + printf("%sQUERIES/SEC: %lu\n", prefix, n_queries / duration); + printf("%sROWS/SEC: %lu\n", prefix, n_rows / duration); + printf("%sERRORS: %lu%s", prefix, n_errors, final_suffix); + + if (hg != NULL) { + hg->write(); + } + fflush(stdout); + + n_queries = 0; + n_rows = 0; + } + + size_t n_rows; + size_t n_queries; + size_t n_errors; + time_t last_update; + time_t start_time; + cbc::Histogram *hg; +#ifndef _WIN32 + pthread_mutex_t m_lock; +#endif +}; + +Metrics GlobalMetrics; + +class Configuration +{ +public: + Configuration() : o_file("queryfile"), o_threads("num-threads") { + o_file.mandatory(true); + o_file.description( + "Path to a file containing all the queries to execute. " + "Each line should contain the full query body"); + o_file.abbrev('f'); + + o_threads.description("Number of threads to run"); + o_threads.abbrev('t'); + o_threads.setDefault(1); + } + + void addToParser(Parser& parser) + { + parser.addOption(o_file); + parser.addOption(o_threads); + m_params.addToParser(parser); + } + + void processOptions() + { + std::ifstream ifs(o_file.const_result().c_str()); + if (!ifs.is_open()) { + int ec_save = errno; + string errstr(o_file.const_result()); + errstr += ": "; + errstr += strerror(ec_save); + throw std::runtime_error(errstr); + } + + string curline; + while (std::getline(ifs, curline).good() && !ifs.eof()) { + m_queries.push_back(curline); + } + if (m_params.useTimings()) { + GlobalMetrics.prepare_timings(); + } + } + + void set_cropts(lcb_create_st &opts) { m_params.fillCropts(opts); } + const vector& queries() const { return m_queries; } + size_t nthreads() { return o_threads.result(); } + +private: + vector m_queries; + StringOption o_file; + UIntOption o_threads; + ConnParams m_params; +}; + +extern "C" { static void n1qlcb(lcb_t, int, const lcb_RESPN1QL *resp); } +extern "C" { static void* pthrfunc(void*); } + +class ThreadContext; +struct QueryContext { + lcb_U64 begin; + bool received; // whether any row was received + ThreadContext *ctx; // Parent + + QueryContext(ThreadContext *tctx) + : begin(lcb_nstime()), received(false), ctx(tctx) {} +}; + +class ThreadContext { +public: + void run() + { + while (!m_cancelled) { + vector::const_iterator ii = m_queries.begin(); + for (; ii != m_queries.end(); ++ii) { + run_one_query(*ii); + } + } + } + +#ifndef _WIN32 + void start() + { + assert(m_thr == NULL); + m_thr = new pthread_t; + int rc = pthread_create(m_thr, NULL, pthrfunc, this); + if (rc != 0) { + throw std::runtime_error(strerror(rc)); + } + } + + void join() + { + assert(m_thr != NULL); + void *arg = NULL; + pthread_join(*m_thr, &arg); + } + + ~ThreadContext() + { + if (m_thr != NULL) { + join(); + delete m_thr; + m_thr = NULL; + } + } +#else + void start() { run(); } + void join() {} +#endif + + void handle_response(const lcb_RESPN1QL *resp, QueryContext *ctx) + { + if (!ctx->received) { + lcb_U64 duration = lcb_nstime() - ctx->begin; + m_metrics->lock(); + m_metrics->update_timings(duration); + m_metrics->unlock(); + + ctx->received = true; + } + + if (resp->rflags & LCB_RESP_F_FINAL) { + if (resp->rc != LCB_SUCCESS) { + log_error(resp->rc); + } + } else { + last_nrow++; + } + } + + ThreadContext(lcb_t instance, const vector& initial_queries) + : m_instance(instance), last_nerr(0), last_nrow(0), + m_metrics(&GlobalMetrics), m_cancelled(false), m_thr(NULL) + { + memset(&m_cmd, 0, sizeof m_cmd); + m_cmd.content_type = "application/json"; + m_cmd.callback = n1qlcb; + + // Shuffle the list + m_queries = initial_queries; + std::random_shuffle(m_queries.begin(), m_queries.end()); + } + +private: + + void log_error(lcb_error_t) + { + m_metrics->lock(); + m_metrics->update_error(); + m_metrics->unlock(); + } + + void run_one_query(const string& txt) + { + // Reset counters + last_nrow = 0; + last_nerr = 0; + + m_cmd.query = txt.c_str(); + m_cmd.nquery = txt.size(); + + // Set up our context + QueryContext qctx(this); + + lcb_error_t rc = lcb_n1ql_query(m_instance, &qctx, &m_cmd); + if (rc != LCB_SUCCESS) { + log_error(rc); + } else { + lcb_wait(m_instance); + m_metrics->lock(); + m_metrics->update_row(last_nrow); + m_metrics->update_done(1); + m_metrics->unlock(); + } + } + + lcb_t m_instance; + vector m_queries; + size_t last_nerr; + size_t last_nrow; + lcb_CMDN1QL m_cmd; + Metrics *m_metrics; + volatile bool m_cancelled; + #ifndef _WIN32 + pthread_t *m_thr; + #else + void *m_thr; + #endif +}; + +static void n1qlcb(lcb_t, int, const lcb_RESPN1QL *resp) +{ + QueryContext *qctx = reinterpret_cast(resp->cookie); + qctx->ctx->handle_response(resp, qctx); +} + +static void* pthrfunc(void *arg) +{ + reinterpret_cast(arg)->run(); + return NULL; +} + +static bool +instance_has_n1ql(lcb_t instance) { + // Check that the instance supports N1QL + lcbvb_CONFIG *vbc; + do_or_die(lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_VBCONFIG, &vbc)); + + int sslmode = 0; + lcbvb_SVCMODE svcmode = LCBVB_SVCMODE_PLAIN; + + do_or_die(lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_SSL_MODE, &sslmode)); + + if (sslmode & LCB_SSL_ENABLED) { + svcmode = LCBVB_SVCMODE_SSL; + } + + int hix = lcbvb_get_randhost(vbc, LCBVB_SVCTYPE_N1QL, svcmode); + return hix > -1; +} + +static void real_main(int argc, char **argv) { + Configuration config; + Parser parser; + config.addToParser(parser); + parser.parse(argc, argv); + + vector threads; + vector instances; + + lcb_create_st cropts = { 0 }; + config.set_cropts(cropts); + config.processOptions(); + + for (size_t ii = 0; ii < config.nthreads(); ii++) { + lcb_t instance; + do_or_die(lcb_create(&instance, &cropts)); + do_or_die(lcb_connect(instance)); + lcb_wait(instance); + do_or_die(lcb_get_bootstrap_status(instance)); + + if (ii == 0 && !instance_has_n1ql(instance)) { + throw std::runtime_error("Cluster does not support N1QL!"); + } + + ThreadContext* cx = new ThreadContext(instance, config.queries()); + threads.push_back(cx); + instances.push_back(instance); + } + + GlobalMetrics.prepare_screen(); + + for (size_t ii = 0; ii < threads.size(); ++ii) { + threads[ii]->start(); + } + for (size_t ii = 0; ii < threads.size(); ++ii) { + threads[ii]->join(); + } + for (size_t ii = 0; ii < instances.size(); ++ii) { + lcb_destroy(instances[ii]); + } +} + +int main(int argc, char **argv) +{ + try { + real_main(argc, argv); + return 0; + } catch (std::exception& exc) { + cerr << exc.what() << endl; + exit(EXIT_FAILURE); + } +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tools/cbc-pillowfight.cc b/couchbase-sys/libcouchbase-2.7.0/tools/cbc-pillowfight.cc new file mode 100644 index 00000000..2904daaa --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tools/cbc-pillowfight.cc @@ -0,0 +1,822 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2011-2012 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define NOMINMAX // Because MS' CRT headers #define min and max :( +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#else +#define usleep(n) Sleep(n/1000) +#endif +#include +#include +#include +#include "common/options.h" +#include "common/histogram.h" +#include "contrib/lcb-jsoncpp/lcb-jsoncpp.h" + +#include "docgen/seqgen.h" +#include "docgen/docgen.h" + +using namespace std; +using namespace cbc; +using namespace cliopts; +using namespace Pillowfight; +using std::vector; +using std::string; + +// Deprecated options which still exist from previous versions. +struct DeprecatedOptions { + UIntOption iterations; + UIntOption instances; + BoolOption loop; + + DeprecatedOptions() : + iterations("iterations"), instances("num-instances"), loop("loop") + { + iterations.abbrev('i').hide().setDefault(1000); + instances.abbrev('Q').hide().setDefault(1); + loop.abbrev('l').hide().setDefault(false); + } + + void addOptions(Parser &p) { + p.addOption(instances); + p.addOption(loop); + p.addOption(iterations); + } +}; + +static TemplateSpec +parseTemplateSpec(const string& input) +{ + TemplateSpec spec; + // Just need to find the path + size_t endpos = input.find(','); + if (endpos == string::npos) { + throw std::runtime_error("invalid template spec: need field,min,max"); + } + spec.term = input.substr(0, endpos); + unsigned is_sequential = 0; + int rv = sscanf(input.c_str() + endpos + 1, "%u,%u,%u", + &spec.minval, &spec.maxval, &is_sequential); + if (rv < 2) { + throw std::runtime_error("invalid template spec: need field,min,max"); + } + spec.sequential = is_sequential; + if (spec.minval > spec.maxval) { + throw std::runtime_error("min cannot be higher than max"); + } + return spec; +} + +class Configuration +{ +public: + Configuration() : + o_multiSize("batch-size"), + o_numItems("num-items"), + o_keyPrefix("key-prefix"), + o_numThreads("num-threads"), + o_randSeed("random-seed"), + o_setPercent("set-pct"), + o_minSize("min-size"), + o_maxSize("max-size"), + o_noPopulate("no-population"), + o_pauseAtEnd("pause-at-end"), + o_numCycles("num-cycles"), + o_sequential("sequential"), + o_startAt("start-at"), + o_rateLimit("rate-limit"), + o_userdocs("docs"), + o_writeJson("json"), + o_templatePairs("template"), + o_subdoc("subdoc"), + o_sdPathCount("pathcount"), + o_populateOnly("populate-only"), + o_exptime("expiry") + { + o_multiSize.setDefault(100).abbrev('B').description("Number of operations to batch"); + o_numItems.setDefault(1000).abbrev('I').description("Number of items to operate on"); + o_keyPrefix.abbrev('p').description("key prefix to use"); + o_numThreads.setDefault(1).abbrev('t').description("The number of threads to use"); + o_randSeed.setDefault(0).abbrev('s').description("Specify random seed").hide(); + o_setPercent.setDefault(33).abbrev('r').description("The percentage of operations which should be mutations"); + o_minSize.setDefault(50).abbrev('m').description("Set minimum payload size"); + o_maxSize.setDefault(5120).abbrev('M').description("Set maximum payload size"); + o_noPopulate.setDefault(false).abbrev('n').description("Skip population"); + o_pauseAtEnd.setDefault(false).abbrev('E').description("Pause at end of run (holding connections open) until user input"); + o_numCycles.setDefault(-1).abbrev('c').description("Number of cycles to be run until exiting. Set to -1 to loop infinitely"); + o_sequential.setDefault(false).description("Use sequential access (instead of random)"); + o_startAt.setDefault(0).description("For sequential access, set the first item"); + o_rateLimit.setDefault(0).description("Set operations per second limit (per thread)"); + o_userdocs.description("User documents to load (overrides --min-size and --max-size"); + o_writeJson.description("Enable writing JSON values (rather than bytes)"); + o_templatePairs.description("Values for templates to be inserted into user documents"); + o_templatePairs.argdesc("FIELD,MIN,MAX[,SEQUENTIAL]").hide(); + o_subdoc.description("Use subdoc instead of fulldoc operations"); + o_sdPathCount.description("Number of subdoc paths per command").setDefault(1); + o_populateOnly.description("Exit after documents have been populated"); + o_exptime.description("Set TTL for items").abbrev('e'); + } + + void processOptions() { + opsPerCycle = o_multiSize.result(); + prefix = o_keyPrefix.result(); + setprc = o_setPercent.result(); + shouldPopulate = !o_noPopulate.result(); + + if (depr.loop.passed()) { + fprintf(stderr, "The --loop/-l option is deprecated. Use --num-cycles\n"); + maxCycles = -1; + } else { + maxCycles = o_numCycles.result(); + } + + if (o_populateOnly.passed()) { + // Determine how many iterations are required. + if (o_numCycles.passed()) { + throw std::runtime_error("--num-cycles incompatible with --populate-only"); + } + size_t est = (o_numItems / o_numThreads) / o_multiSize; + while (est * o_numThreads * o_multiSize < o_numItems) { + est++; + } + maxCycles = est; + o_sequential.setDefault(true); + fprintf(stderr, "Populating using %lu cycles\n", maxCycles); + } + + if (depr.iterations.passed()) { + fprintf(stderr, "The --num-iterations/-I option is deprecated. Use --batch-size\n"); + opsPerCycle = depr.iterations.result(); + } + + vector specs; + vector userdocs; + + if (o_templatePairs.passed()) { + vector specs_str = o_templatePairs.result(); + for (size_t ii = 0; ii < specs_str.size(); ii++) { + specs.push_back(parseTemplateSpec(specs_str[ii])); + } + } + + // Set the document sizes.. + if (o_userdocs.passed()) { + if (o_minSize.passed() || o_maxSize.passed()) { + fprintf(stderr, "--min-size/--max-size invalid with userdocs\n"); + } + + vector filenames = o_userdocs.result(); + for (size_t ii = 0; ii < filenames.size(); ii++) { + std::stringstream ss; + std::ifstream ifs(filenames[ii].c_str()); + if (!ifs.is_open()) { + perror(filenames[ii].c_str()); + exit(EXIT_FAILURE); + } + ss << ifs.rdbuf(); + userdocs.push_back(ss.str()); + } + } + + if (specs.empty()) { + if (o_writeJson.result()) { + docgen = new JsonDocGenerator(o_minSize.result(), o_maxSize.result()); + } else if (!userdocs.empty()) { + docgen = new PresetDocGenerator(userdocs); + } else { + docgen = new RawDocGenerator(o_minSize.result(), o_maxSize.result()); + } + } else { + if (o_writeJson.result()) { + if (userdocs.empty()) { + docgen = new PlaceholderJsonGenerator( + o_minSize.result(), o_maxSize.result(), specs); + } else { + docgen = new PlaceholderJsonGenerator(userdocs, specs); + } + } else { + if (userdocs.empty()) { + throw std::runtime_error("Must provide documents with placeholders!"); + } + docgen = new PlaceholderDocGenerator(userdocs, specs); + } + } + + sdOpsPerCmd = o_sdPathCount.result(); + if (o_sdPathCount.passed()) { + o_subdoc.setDefault(true); + } + } + + void addOptions(Parser& parser) { + parser.addOption(o_multiSize); + parser.addOption(o_numItems); + parser.addOption(o_keyPrefix); + parser.addOption(o_numThreads); + parser.addOption(o_randSeed); + parser.addOption(o_setPercent); + parser.addOption(o_noPopulate); + parser.addOption(o_minSize); + parser.addOption(o_maxSize); + parser.addOption(o_pauseAtEnd); + parser.addOption(o_numCycles); + parser.addOption(o_sequential); + parser.addOption(o_startAt); + parser.addOption(o_rateLimit); + parser.addOption(o_userdocs); + parser.addOption(o_writeJson); + parser.addOption(o_templatePairs); + parser.addOption(o_subdoc); + parser.addOption(o_sdPathCount); + parser.addOption(o_populateOnly); + parser.addOption(o_exptime); + params.addToParser(parser); + depr.addOptions(parser); + } + + bool isTimings(void) { return params.useTimings(); } + + bool isLoopDone(size_t niter) { + if (maxCycles == -1) { + return false; + } + return niter >= (size_t)maxCycles; + } + + uint32_t getRandomSeed() { return o_randSeed; } + uint32_t getNumThreads() { return o_numThreads; } + string& getKeyPrefix() { return prefix; } + bool shouldPauseAtEnd() { return o_pauseAtEnd; } + bool sequentialAccess() { return o_sequential; } + bool isSubdoc() { return o_subdoc; } + unsigned firstKeyOffset() { return o_startAt; } + uint32_t getNumItems() { return o_numItems; } + uint32_t getRateLimit() { return o_rateLimit; } + unsigned getExptime() { return o_exptime; } + + uint32_t opsPerCycle; + uint32_t sdOpsPerCmd; + unsigned setprc; + string prefix; + volatile int maxCycles; + bool shouldPopulate; + bool hasTemplates; + ConnParams params; + const DocGeneratorBase *docgen; + +private: + UIntOption o_multiSize; + UIntOption o_numItems; + StringOption o_keyPrefix; + UIntOption o_numThreads; + UIntOption o_randSeed; + UIntOption o_setPercent; + UIntOption o_minSize; + UIntOption o_maxSize; + BoolOption o_noPopulate; + BoolOption o_pauseAtEnd; // Should pillowfight pause execution (with + // connections open) before exiting? + IntOption o_numCycles; + BoolOption o_sequential; + UIntOption o_startAt; + UIntOption o_rateLimit; + + // List of paths to user documents to load.. They should all be valid JSON + ListOption o_userdocs; + + // Whether generated values should be JSON + BoolOption o_writeJson; + + // List of template ranges for value generation + ListOption o_templatePairs; + BoolOption o_subdoc; + UIntOption o_sdPathCount; + + // Compound option + BoolOption o_populateOnly; + + UIntOption o_exptime; + + DeprecatedOptions depr; +} config; + +void log(const char *format, ...) +{ + char buffer[512]; + va_list args; + + va_start(args, format); + vsprintf(buffer, format, args); + if (config.isTimings()) { + std::cerr << "[" << std::fixed << lcb_nstime() / 1000000000.0 << "] "; + } + std::cerr << buffer << std::endl; + va_end(args); +} + + + +extern "C" { +static void operationCallback(lcb_t, int, const lcb_RESPBASE*); +} + +class InstanceCookie { +public: + InstanceCookie(lcb_t instance) { + lcb_set_cookie(instance, this); + lastPrint = 0; + if (config.isTimings()) { + hg.install(instance, stdout); + } + } + + static InstanceCookie* get(lcb_t instance) { + return (InstanceCookie *)lcb_get_cookie(instance); + } + + + static void dumpTimings(lcb_t instance, const char *header, bool force=false) { + time_t now = time(NULL); + InstanceCookie *ic = get(instance); + + if (now - ic->lastPrint > 0) { + ic->lastPrint = now; + } else if (!force) { + return; + } + + Histogram &h = ic->hg; + printf("[%f %s]\n", lcb_nstime() / 1000000000.0, header); + printf(" +---------+---------+---------+---------+\n"); + h.write(); + printf(" +----------------------------------------\n"); + } + +private: + time_t lastPrint; + Histogram hg; +}; + +struct NextOp { + NextOp() : m_seqno(0), m_mode(GET) {} + + string m_key; + uint32_t m_seqno; + vector m_valuefrags; + vector m_specs; + // The mode here is for future use with subdoc + enum Mode { STORE, GET, SDSTORE, SDGET }; + Mode m_mode; +}; + +/** Stateful, per-thread generator */ +class KeyGenerator { +public: + KeyGenerator(int ix) + : m_gencount(0), m_force_sequential(false), + m_in_population(config.shouldPopulate) +{ + srand(config.getRandomSeed()); + + m_genrandom = new SeqGenerator( + config.firstKeyOffset(), + config.getNumItems() + config.firstKeyOffset()); + + m_gensequence = new SeqGenerator( + config.firstKeyOffset(), + config.getNumItems() + config.firstKeyOffset(), + config.getNumThreads(), + ix); + + if (m_in_population) { + m_force_sequential = true; + } else { + m_force_sequential = config.sequentialAccess(); + } + + m_id = ix; + m_local_genstate = config.docgen->createState(config.getNumThreads(), ix); + if (config.isSubdoc()) { + m_mode_read = NextOp::SDGET; + m_mode_write = NextOp::SDSTORE; + m_sdgenstate = config.docgen->createSubdocState(config.getNumThreads(), ix); + if (!m_sdgenstate) { + std::cerr << "Current generator does not support subdoc. Did you try --json?" << std::endl; + exit(EXIT_FAILURE); + } + } else { + m_mode_read = NextOp::GET; + m_mode_write = NextOp::STORE; + } + } + + void setNextOp(NextOp& op) { + bool store_override = false; + + if (m_in_population) { + if (m_gencount++ < m_gensequence->maxItems()) { + store_override = true; + } else { + printf("Thread %d has finished populating.\n", m_id); + m_in_population = false; + m_force_sequential = config.sequentialAccess(); + } + } + + if (m_force_sequential) { + op.m_seqno = m_gensequence->next(); + } else { + op.m_seqno = m_genrandom->next(); + } + + if (store_override) { + // Populate + op.m_mode = NextOp::STORE; + m_local_genstate->populateIov(op.m_seqno, op.m_valuefrags); + + } else if (shouldStore(op.m_seqno)) { + op.m_mode = m_mode_write; + if (op.m_mode == NextOp::STORE) { + m_local_genstate->populateIov(op.m_seqno, op.m_valuefrags); + } else if (op.m_mode == NextOp::SDSTORE) { + op.m_specs.resize(config.sdOpsPerCmd); + m_sdgenstate->populateMutate(op.m_seqno, op.m_specs); + } else { + fprintf(stderr, "Invalid mode for op: %d\n", op.m_mode); + abort(); + } + } else { + op.m_mode = m_mode_read; + if (op.m_mode == NextOp::SDGET) { + op.m_specs.resize(config.sdOpsPerCmd); + m_sdgenstate->populateLookup(op.m_seqno, op.m_specs); + } + } + + generateKey(op); + } + + bool shouldStore(uint32_t seqno) { + if (config.setprc == 0) { + return false; + } + + float seqno_f = seqno % 100; + float pct_f = seqno_f / config.setprc; + return pct_f < 1; + } + + void generateKey(NextOp& op) { + uint32_t seqno = op.m_seqno; + char buffer[21]; + snprintf(buffer, sizeof(buffer), "%020d", seqno); + op.m_key.assign(config.getKeyPrefix() + buffer); + } + + const char *getStageString() const { + if (m_in_population) { + return "Populate"; + } else { + return "Run"; + } + } + +private: + SeqGenerator *m_genrandom; + SeqGenerator *m_gensequence; + size_t m_gencount; + int m_id; + + bool m_force_sequential; + bool m_in_population; + NextOp::Mode m_mode_read; + NextOp::Mode m_mode_write; + GeneratorState *m_local_genstate; + SubdocGeneratorState *m_sdgenstate; +}; + +class ThreadContext +{ +public: + ThreadContext(lcb_t handle, int ix) : kgen(ix), niter(0), instance(handle) { + + } + + void singleLoop() { + bool hasItems = false; + lcb_sched_enter(instance); + NextOp opinfo; + unsigned exptime = config.getExptime(); + + for (size_t ii = 0; ii < config.opsPerCycle; ++ii) { + kgen.setNextOp(opinfo); + + switch (opinfo.m_mode) { + case NextOp::STORE: { + lcb_CMDSTORE scmd = { 0 }; + scmd.operation = LCB_SET; + scmd.exptime = exptime; + LCB_CMD_SET_KEY(&scmd, opinfo.m_key.c_str(), opinfo.m_key.size()); + LCB_CMD_SET_VALUEIOV(&scmd, &opinfo.m_valuefrags[0], opinfo.m_valuefrags.size()); + error = lcb_store3(instance, this, &scmd); + break; + } + case NextOp::GET: { + lcb_CMDGET gcmd = { 0 }; + LCB_CMD_SET_KEY(&gcmd, opinfo.m_key.c_str(), opinfo.m_key.size()); + gcmd.exptime = exptime; + error = lcb_get3(instance, this, &gcmd); + break; + } + case NextOp::SDSTORE: + case NextOp::SDGET: { + lcb_CMDSUBDOC sdcmd = { 0 }; + if (opinfo.m_mode == NextOp::SDSTORE) { + sdcmd.exptime = exptime; + } + LCB_CMD_SET_KEY(&sdcmd, opinfo.m_key.c_str(), opinfo.m_key.size()); + sdcmd.specs = &opinfo.m_specs[0]; + sdcmd.nspecs = opinfo.m_specs.size(); + error = lcb_subdoc3(instance, this, &sdcmd); + break; + } + } + + if (error != LCB_SUCCESS) { + hasItems = false; + log("Failed to schedule operation: [0x%x] %s", error, lcb_strerror(instance, error)); + } else { + hasItems = true; + } + } + if (hasItems) { + lcb_sched_leave(instance); + lcb_wait(instance); + if (error != LCB_SUCCESS) { + log("Operation(s) failed: [0x%x] %s", error, lcb_strerror(instance, error)); + } + } else { + lcb_sched_fail(instance); + } + } + + bool run() { + do { + singleLoop(); + + if (config.isTimings()) { + InstanceCookie::dumpTimings(instance, kgen.getStageString()); + } + if (config.params.shouldDump()) { + lcb_dump(instance, stderr, LCB_DUMP_ALL); + } + if (config.getRateLimit() > 0) { + rateLimitThrottle(); + } + + } while (!config.isLoopDone(++niter)); + + if (config.isTimings()) { + InstanceCookie::dumpTimings(instance, kgen.getStageString(), true); + } + return true; + } + +#ifndef WIN32 + pthread_t thr; +#endif + +protected: + // the callback methods needs to be able to set the error handler.. + friend void operationCallback(lcb_t, int, const lcb_RESPBASE*); + Histogram histogram; + + void setError(lcb_error_t e) { error = e; } + +private: + + void rateLimitThrottle() { + lcb_U64 now = lcb_nstime(); + static lcb_U64 previous_time = now; + + const lcb_U64 elapsed_ns = now - previous_time; + const lcb_U64 wanted_duration_ns = + config.opsPerCycle * 1e9 / config.getRateLimit(); + // On first invocation no previous_time, so skip attempting to sleep. + if (elapsed_ns > 0 && elapsed_ns < wanted_duration_ns) { + // Dampen the sleep time by averaging with the previous + // sleep time. + static lcb_U64 last_sleep_ns = 0; + const lcb_U64 sleep_ns = + (last_sleep_ns + wanted_duration_ns - elapsed_ns) / 2; + usleep(sleep_ns / 1000); + now += sleep_ns; + last_sleep_ns = sleep_ns; + } + previous_time = now; + } + + KeyGenerator kgen; + size_t niter; + lcb_error_t error; + lcb_t instance; +}; + +static void operationCallback(lcb_t, int, const lcb_RESPBASE *resp) +{ + ThreadContext *tc; + + tc = const_cast(reinterpret_cast(resp->cookie)); + tc->setError(resp->rc); + +#ifndef WIN32 + static volatile unsigned long nops = 1; + static time_t start_time = time(NULL); + static int is_tty = isatty(STDOUT_FILENO); + if (is_tty) { + if (++nops % 1000 == 0) { + time_t now = time(NULL); + time_t nsecs = now - start_time; + if (!nsecs) { nsecs = 1; } + unsigned long ops_sec = nops / nsecs; + printf("OPS/SEC: %10lu\r", ops_sec); + fflush(stdout); + } + } +#endif +} + + +std::list contexts; + +extern "C" { + typedef void (*handler_t)(int); +} + +#ifndef WIN32 +static void sigint_handler(int) +{ + static int ncalled = 0; + ncalled++; + + if (ncalled < 2) { + log("Termination requested. Waiting threads to finish. Ctrl-C to force termination."); + signal(SIGINT, sigint_handler); // Reinstall + config.maxCycles = 0; + return; + } + + std::list::iterator it; + for (it = contexts.begin(); it != contexts.end(); ++it) { + delete *it; + } + contexts.clear(); + exit(EXIT_FAILURE); +} + +static void setup_sigint_handler() +{ + struct sigaction action; + sigemptyset(&action.sa_mask); + action.sa_handler = sigint_handler; + action.sa_flags = 0; + sigaction(SIGINT, &action, NULL); +} + +extern "C" { +static void* thread_worker(void*); +} + +static void start_worker(ThreadContext *ctx) +{ + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + int rc = pthread_create(&ctx->thr, &attr, thread_worker, ctx); + if (rc != 0) { + log("Couldn't create thread: (%d)", errno); + exit(EXIT_FAILURE); + } +} +static void join_worker(ThreadContext *ctx) +{ + void *arg = NULL; + int rc = pthread_join(ctx->thr, &arg); + if (rc != 0) { + log("Couldn't join thread (%d)", errno); + exit(EXIT_FAILURE); + } +} + +#else +static void setup_sigint_handler() {} +static void start_worker(ThreadContext *ctx) { ctx->run(); } +static void join_worker(ThreadContext *ctx) { (void)ctx; } +#endif + +extern "C" { +static void *thread_worker(void *arg) +{ + ThreadContext *ctx = static_cast(arg); + ctx->run(); + return NULL; +} +} + +int main(int argc, char **argv) +{ + int exit_code = EXIT_SUCCESS; + setup_sigint_handler(); + + Parser parser("cbc-pillowfight"); + config.addOptions(parser); + try { + parser.parse(argc, argv, false); + config.processOptions(); + } catch (std::string& e) { + std::cerr << e << std::endl; + exit(EXIT_FAILURE); + } catch (std::exception& e) { + std::cerr << e.what() << std::endl; + exit(EXIT_FAILURE); + } + size_t nthreads = config.getNumThreads(); + log("Running. Press Ctrl-C to terminate..."); + +#ifdef WIN32 + if (nthreads > 1) { + log("WARNING: More than a single thread on Windows not supported. Forcing 1"); + nthreads = 1; + } +#endif + + struct lcb_create_st options; + ConnParams& cp = config.params; + lcb_error_t error; + + for (uint32_t ii = 0; ii < nthreads; ++ii) { + cp.fillCropts(options); + lcb_t instance = NULL; + error = lcb_create(&instance, &options); + if (error != LCB_SUCCESS) { + log("Failed to create instance: %s", lcb_strerror(NULL, error)); + exit(EXIT_FAILURE); + } + lcb_install_callback3(instance, LCB_CALLBACK_STORE, operationCallback); + lcb_install_callback3(instance, LCB_CALLBACK_GET, operationCallback); + lcb_install_callback3(instance, LCB_CALLBACK_SDMUTATE, operationCallback); + lcb_install_callback3(instance, LCB_CALLBACK_SDLOOKUP, operationCallback); + cp.doCtls(instance); + + new InstanceCookie(instance); + + lcb_connect(instance); + lcb_wait(instance); + error = lcb_get_bootstrap_status(instance); + + if (error != LCB_SUCCESS) { + std::cout << std::endl; + log("Failed to connect: %s", lcb_strerror(instance, error)); + exit(EXIT_FAILURE); + } + + ThreadContext *ctx = new ThreadContext(instance, ii); + contexts.push_back(ctx); + start_worker(ctx); + } + + for (std::list::iterator it = contexts.begin(); + it != contexts.end(); ++it) { + join_worker(*it); + } + return exit_code; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tools/cbc.cc b/couchbase-sys/libcouchbase-2.7.0/tools/cbc.cc new file mode 100644 index 00000000..53a2dfff --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tools/cbc.cc @@ -0,0 +1,1550 @@ +#define NOMINMAX +#include "common/my_inttypes.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common/options.h" +#include "common/histogram.h" +#include "cbc-handlers.h" +#include "connspec.h" +using namespace cbc; + +using std::string; +using std::map; +using std::vector; +using std::stringstream; + +string getRespKey(const lcb_RESPBASE* resp) +{ + if (!resp->nkey) { + return ""; + } + + return string((const char *)resp->key, resp->nkey); +} + +static void +printKeyError(string& key, lcb_error_t err, const char *additional = NULL) +{ + fprintf(stderr, "%-20s %s (0x%x)\n", key.c_str(), lcb_strerror(NULL, err), err); + if (additional) { + fprintf(stderr, "%-20s%s\n", "", additional); + } +} + +static void +printKeyCasStatus(string& key, int cbtype, const lcb_RESPBASE *resp, + const char *message = NULL) +{ + fprintf(stderr, "%-20s", key.c_str()); + if (message != NULL) { + fprintf(stderr, "%s ", message); + } + fprintf(stderr, "CAS=0x%" PRIx64 "\n", resp->cas); + const lcb_MUTATION_TOKEN *st = lcb_resp_get_mutation_token(cbtype, resp); + if (st != NULL) { + fprintf(stderr, "%-20sSYNCTOKEN=%u,%" PRIu64 ",%" PRIu64 "\n", + "", st->vbid_, st->uuid_, st->seqno_); + } +} + +extern "C" { +static void +get_callback(lcb_t, lcb_CALLBACKTYPE, const lcb_RESPGET *resp) +{ + string key = getRespKey((const lcb_RESPBASE *)resp); + if (resp->rc == LCB_SUCCESS) { + fprintf(stderr, "%-20s CAS=0x%" PRIx64 ", Flags=0x%x. Size=%lu\n", + key.c_str(), resp->cas, resp->itmflags, (unsigned long)resp->nvalue); + fflush(stderr); + fwrite(resp->value, 1, resp->nvalue, stdout); + fflush(stdout); + fprintf(stderr, "\n"); + } else { + printKeyError(key, resp->rc); + } +} + +static void +store_callback(lcb_t, lcb_CALLBACKTYPE cbtype, const lcb_RESPBASE *resp) +{ + string key = getRespKey((const lcb_RESPBASE*)resp); + + if (cbtype == LCB_CALLBACK_STOREDUR) { + // Storage with durability + const lcb_RESPSTOREDUR *dresp = + reinterpret_cast(resp); + char buf[4096]; + if (resp->rc == LCB_SUCCESS) { + sprintf(buf, "Stored. Persisted(%u). Replicated(%u)", + dresp->dur_resp->npersisted, dresp->dur_resp->nreplicated); + printKeyCasStatus(key, cbtype, resp, buf); + } else { + if (dresp->store_ok) { + sprintf(buf, "Store OK, but durability failed. Persisted(%u). Replicated(%u)", + dresp->dur_resp->npersisted, dresp->dur_resp->nreplicated); + } else { + sprintf(buf, "%s", "Store failed"); + } + printKeyError(key, resp->rc, buf); + } + } else { + if (resp->rc == LCB_SUCCESS) { + printKeyCasStatus(key, cbtype, resp, "Stored."); + } else { + printKeyError(key, resp->rc); + } + } +} + +static void +common_callback(lcb_t, int type, const lcb_RESPBASE *resp) +{ + string key = getRespKey(resp); + if (resp->rc != LCB_SUCCESS) { + printKeyError(key, resp->rc); + return; + } + switch (type) { + case LCB_CALLBACK_UNLOCK: + fprintf(stderr, "%-20s Unlocked\n", key.c_str()); + break; + case LCB_CALLBACK_REMOVE: + printKeyCasStatus(key, type, resp, "Deleted."); + break; + case LCB_CALLBACK_TOUCH: + printKeyCasStatus(key, type, resp, "Touched."); + break; + default: + abort(); // didn't request it + } +} + +static void +observe_callback(lcb_t, lcb_CALLBACKTYPE, const lcb_RESPOBSERVE *resp) +{ + if (resp->nkey == 0) { + return; + } + + string key = getRespKey((const lcb_RESPBASE *)resp); + if (resp->rc == LCB_SUCCESS) { + fprintf(stderr, + "%-20s [%s] Status=0x%x, CAS=0x%" PRIx64 "\n", key.c_str(), + resp->ismaster ? "Master" : "Replica", + resp->status, resp->cas); + } else { + printKeyError(key, resp->rc); + } +} + +static void +obseqno_callback(lcb_t, lcb_CALLBACKTYPE, const lcb_RESPOBSEQNO *resp) +{ + int ix = resp->server_index; + if (resp->rc != LCB_SUCCESS) { + fprintf(stderr, + "[%d] ERROR 0x%X (%s)\n", ix, resp->rc, lcb_strerror(NULL, resp->rc)); + return; + } + lcb_U64 uuid, seq_disk, seq_mem; + if (resp->old_uuid) { + seq_disk = seq_mem = resp->old_seqno; + uuid = resp->old_uuid; + } else { + uuid = resp->cur_uuid; + seq_disk = resp->persisted_seqno; + seq_mem = resp->mem_seqno; + } + fprintf(stderr, "[%d] UUID=0x%" PRIx64 ", Cache=%" PRIu64 ", Disk=%" PRIu64, + ix, uuid, seq_mem, seq_disk); + if (resp->old_uuid) { + fprintf(stderr, "\n"); + fprintf(stderr, " FAILOVER. New: UUID=%" PRIx64 ", Cache=%" PRIu64 ", Disk=%" PRIu64, + resp->cur_uuid, resp->mem_seqno, resp->persisted_seqno); + } + fprintf(stderr, "\n"); +} + +static void +stats_callback(lcb_t, lcb_CALLBACKTYPE, const lcb_RESPSTATS *resp) +{ + if (resp->rc != LCB_SUCCESS) { + fprintf(stderr, "Got error %s (%d) in stats\n", lcb_strerror(NULL, resp->rc), resp->rc); + return; + } + if (resp->server == NULL || resp->key == NULL) { + return; + } + + string server = resp->server; + string key = getRespKey((const lcb_RESPBASE *)resp); + string value; + if (resp->nvalue > 0) { + value.assign((const char *)resp->value, resp->nvalue); + } + fprintf(stdout, "%s\t%s", server.c_str(), key.c_str()); + if (!value.empty()) { + if (*static_cast(resp->cookie) && key == "key_flags") { + // Is keystats + // Flip the bits so the display formats correctly + unsigned flags_u = 0; + sscanf(value.c_str(), "%u", &flags_u); + flags_u = htonl(flags_u); + fprintf(stdout, "\t%u (cbc: converted via htonl)", flags_u); + } else { + fprintf(stdout, "\t%s", value.c_str()); + } + } + fprintf(stdout, "\n"); +} + +static void +common_server_callback(lcb_t, int cbtype, const lcb_RESPSERVERBASE *sbase) +{ + const char *msg; + if (cbtype == LCB_CALLBACK_VERBOSITY) { + msg = "Set verbosity"; + } else if (cbtype == LCB_CALLBACK_FLUSH) { + msg = "Flush"; + } else { + msg = ""; + } + if (!sbase->server) { + return; + } + if (sbase->rc != LCB_SUCCESS) { + fprintf(stderr, "%s failed for server %s: %s\n", msg, sbase->server, + lcb_strerror(NULL, sbase->rc)); + } else { + fprintf(stderr, "%s: %s\n", msg, sbase->server); + } +} + +static void +arithmetic_callback(lcb_t, lcb_CALLBACKTYPE type, const lcb_RESPCOUNTER *resp) +{ + string key = getRespKey((const lcb_RESPBASE *)resp); + if (resp->rc != LCB_SUCCESS) { + printKeyError(key, resp->rc); + } else { + char buf[4096] = { 0 }; + sprintf(buf, "Current value is %" PRIu64 ".", resp->value); + printKeyCasStatus(key, type, (const lcb_RESPBASE *)resp, buf); + } +} + +static void +http_callback(lcb_t, int, const lcb_RESPHTTP *resp) +{ + HttpReceiver *ctx = (HttpReceiver *)resp->cookie; + ctx->maybeInvokeStatus(resp); + if (resp->nbody) { + ctx->onChunk((const char*)resp->body, resp->nbody); + } + if (resp->rflags & LCB_RESP_F_FINAL) { + ctx->onDone(); + } +} + +static void +view_callback(lcb_t, int, const lcb_RESPVIEWQUERY *resp) +{ + if (resp->rflags & LCB_RESP_F_FINAL) { + fprintf(stderr, "View query complete!\n"); + } + + if (resp->rc != LCB_SUCCESS) { + fprintf(stderr, "View query failed: 0x%x (%s)\n", + resp->rc, lcb_strerror(NULL, resp->rc)); + + if (resp->rc == LCB_HTTP_ERROR) { + if (resp->htresp != NULL) { + HttpReceiver ctx; + ctx.maybeInvokeStatus(resp->htresp); + if (resp->htresp->nbody) { + fprintf(stderr, "%.*s", (int)resp->htresp->nbody, + static_cast(resp->htresp->body)); + } + } + } + } + + if (resp->rflags & LCB_RESP_F_FINAL) { + if (resp->value) { + fprintf(stderr, "Non-row data: %.*s\n", + (int)resp->nvalue, resp->value); + } + return; + } + + printf("KEY: %.*s\n", (int)resp->nkey, static_cast(resp->key)); + printf(" VALUE: %.*s\n", (int)resp->nvalue, resp->value); + printf(" DOCID: %.*s\n", (int)resp->ndocid, resp->docid); + if (resp->docresp) { + get_callback(NULL, LCB_CALLBACK_GET, resp->docresp); + } + if (resp->geometry) { + printf(" GEO: %.*s\n", (int)resp->ngeometry, resp->geometry); + } +} +} + + +Handler::Handler(const char *name) : parser(name), instance(NULL) +{ + if (name != NULL) { + cmdname = name; + } +} + +Handler::~Handler() +{ + if (params.shouldDump()) { + lcb_dump(instance, stderr, LCB_DUMP_ALL); + } + if (instance) { + lcb_destroy(instance); + } +} + +void +Handler::execute(int argc, char **argv) +{ + addOptions(); + parser.default_settings.argstring = usagestr(); + parser.default_settings.shortdesc = description(); + parser.parse(argc, argv, true); + run(); + if (instance != NULL && params.useTimings()) { + fprintf(stderr, "Output command timings as requested (--timings)\n"); + hg.write(); + } +} + +void +Handler::addOptions() +{ + params.addToParser(parser); +} + +void +Handler::run() +{ + lcb_create_st cropts; + memset(&cropts, 0, sizeof cropts); + params.fillCropts(cropts); + lcb_error_t err; + err = lcb_create(&instance, &cropts); + if (err != LCB_SUCCESS) { + throw LcbError(err); + } + params.doCtls(instance); + err = lcb_connect(instance); + if (err != LCB_SUCCESS) { + throw LcbError(err); + } + lcb_wait(instance); + err = lcb_get_bootstrap_status(instance); + if (err != LCB_SUCCESS) { + throw LcbError(err); + } + + if (params.useTimings()) { + hg.install(instance, stdout); + } +} + +const string& +Handler::getLoneArg(bool required) +{ + static string empty(""); + + const vector& args = parser.getRestArgs(); + if (args.empty() || args.size() != 1) { + if (required) { + throw std::runtime_error("Command requires single argument"); + } + return empty; + } + return args[0]; +} + +void +GetHandler::addOptions() +{ + Handler::addOptions(); + o_exptime.abbrev('e'); + if (isLock()) { + o_exptime.description("Time the lock should be held for"); + } else { + o_exptime.description("Update the expiration time for the item"); + o_replica.abbrev('r'); + o_replica.description("Read from replica. Possible values are 'first': read from first available replica. 'all': read from all replicas, and , where 0 < N < nreplicas"); + parser.addOption(o_replica); + } + parser.addOption(o_exptime); +} + +void +GetHandler::run() +{ + Handler::run(); + lcb_install_callback3(instance, LCB_CALLBACK_GET, (lcb_RESPCALLBACK)get_callback); + lcb_install_callback3(instance, LCB_CALLBACK_GETREPLICA, (lcb_RESPCALLBACK)get_callback); + const vector& keys = parser.getRestArgs(); + lcb_error_t err; + + lcb_sched_enter(instance); + for (size_t ii = 0; ii < keys.size(); ++ii) { + lcb_CMDGET cmd = { 0 }; + const string& key = keys[ii]; + LCB_KREQ_SIMPLE(&cmd.key, key.c_str(), key.size()); + if (o_exptime.passed()) { + cmd.exptime = o_exptime.result(); + } + if (isLock()) { + cmd.lock = 1; + } + + err = lcb_get3(instance, this, &cmd); + if (err != LCB_SUCCESS) { + throw LcbError(err); + } + } + lcb_sched_leave(instance); + lcb_wait(instance); +} + +void +TouchHandler::addOptions() +{ + Handler::addOptions(); + parser.addOption(o_exptime); +} + +void +TouchHandler::run() +{ + Handler::run(); + lcb_install_callback3(instance, LCB_CALLBACK_TOUCH, (lcb_RESPCALLBACK)common_callback); + const vector& keys = parser.getRestArgs(); + lcb_error_t err; + lcb_sched_enter(instance); + for (size_t ii = 0; ii < keys.size(); ++ii) { + lcb_CMDTOUCH cmd = { 0 }; + const string& key = keys[ii]; + LCB_CMD_SET_KEY(&cmd, key.c_str(), key.size()); + cmd.exptime = o_exptime.result(); + err = lcb_touch3(instance, this, &cmd); + if (err != LCB_SUCCESS) { + throw LcbError(err); + } + } + lcb_sched_leave(instance); + lcb_wait(instance); +} + +void +SetHandler::addOptions() +{ + Handler::addOptions(); + parser.addOption(o_mode); + parser.addOption(o_flags); + parser.addOption(o_exp); + parser.addOption(o_add); + parser.addOption(o_persist); + parser.addOption(o_replicate); + if (!hasFileList()) { + parser.addOption(o_value); + } + // This may be enabled again if datatype support is re-added + // parser.addOption(o_json); +} + +lcb_storage_t +SetHandler::mode() +{ + if (o_add.passed()) { + return LCB_ADD; + } + + string s = o_mode.const_result(); + std::transform(s.begin(), s.end(), s.begin(), ::tolower); + if (s == "upsert") { + return LCB_SET; + } else if (s == "replace") { + return LCB_REPLACE; + } else if (s == "insert") { + return LCB_ADD; + } else if (s == "append") { + return LCB_APPEND; + } else if (s == "prepend") { + return LCB_PREPEND; + } else { + throw BadArg(string("Mode must be one of upsert, insert, replace. Got ") + s); + return LCB_SET; + } +} + +void +SetHandler::storeItem(const string& key, const char *value, size_t nvalue) +{ + lcb_error_t err; + lcb_CMDSTOREDUR cmd = { 0 }; + LCB_CMD_SET_KEY(&cmd, key.c_str(), key.size()); + cmd.value.vtype = LCB_KV_COPY; + cmd.value.u_buf.contig.bytes = value; + cmd.value.u_buf.contig.nbytes = nvalue; + cmd.operation = mode(); + + if (o_json.result()) { + cmd.datatype = LCB_VALUE_F_JSON; + } + if (o_exp.passed()) { + cmd.exptime = o_exp.result(); + } + if (o_flags.passed()) { + cmd.flags = o_flags.result(); + } + if (o_persist.passed() || o_replicate.passed()) { + cmd.persist_to = o_persist.result(); + cmd.replicate_to = o_replicate.result(); + err = lcb_storedur3(instance, NULL, &cmd); + } else { + err = lcb_store3(instance, NULL, reinterpret_cast(&cmd)); + } + if (err != LCB_SUCCESS) { + throw LcbError(err); + } +} + +void +SetHandler::storeItem(const string& key, FILE *input) +{ + char tmpbuf[4096]; + vector vbuf; + size_t nr; + while ((nr = fread(tmpbuf, 1, sizeof tmpbuf, input))) { + vbuf.insert(vbuf.end(), tmpbuf, &tmpbuf[nr]); + } + storeItem(key, &vbuf[0], vbuf.size()); +} + +void +SetHandler::run() +{ + Handler::run(); + lcb_install_callback3(instance, LCB_CALLBACK_STORE, (lcb_RESPCALLBACK)store_callback); + lcb_install_callback3(instance, LCB_CALLBACK_STOREDUR, (lcb_RESPCALLBACK)store_callback); + const vector& keys = parser.getRestArgs(); + + lcb_sched_enter(instance); + + if (hasFileList()) { + for (size_t ii = 0; ii < keys.size(); ii++) { + const string& key = keys[ii]; + FILE *fp = fopen(key.c_str(), "rb"); + if (fp == NULL) { + perror(key.c_str()); + continue; + } + storeItem(key, fp); + fclose(fp); + } + } else if (keys.size() > 1 || keys.empty()) { + throw BadArg("create must be passed a single key"); + } else { + const string& key = keys[0]; + if (o_value.passed()) { + const string& value = o_value.const_result(); + storeItem(key, value.c_str(), value.size()); + } else { + storeItem(key, stdin); + } + } + + lcb_sched_leave(instance); + lcb_wait(instance); +} + +void +HashHandler::run() +{ + Handler::run(); + + lcbvb_CONFIG *vbc; + lcb_error_t err; + err = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_VBCONFIG, &vbc); + if (err != LCB_SUCCESS) { + throw LcbError(err); + } + + const vector& args = parser.getRestArgs(); + for (size_t ii = 0; ii < args.size(); ii++) { + const string& key = args[ii]; + const void *vkey = (const void *)key.c_str(); + int vbid, srvix; + lcbvb_map_key(vbc, vkey, key.size(), &vbid, &srvix); + fprintf(stderr, "%s: [vBucket=%d, Index=%d]", key.c_str(), vbid, srvix); + if (srvix != -1) { + fprintf(stderr, " Server: %s", + lcbvb_get_hostport(vbc, srvix, LCBVB_SVCTYPE_DATA, LCBVB_SVCMODE_PLAIN)); + const char *vapi = lcbvb_get_capibase(vbc, srvix, LCBVB_SVCMODE_PLAIN); + if (vapi) { + fprintf(stderr, ", CouchAPI: %s", vapi); + } + } + fprintf(stderr, "\n"); + + for (size_t jj = 0; jj < lcbvb_get_nreplicas(vbc); jj++) { + int rix = lcbvb_vbreplica(vbc, vbid, jj); + const char *rname = NULL; + if (rix >= 0) { + rname = lcbvb_get_hostport(vbc, rix, LCBVB_SVCTYPE_DATA, LCBVB_SVCMODE_PLAIN); + } + if (rname == NULL) { + rname = "N/A"; + } + fprintf(stderr, "Replica #%d: Index=%d, Host=%s\n", (int)jj, rix, rname); + } + } +} + +void +ObserveHandler::run() +{ + Handler::run(); + lcb_install_callback3(instance, LCB_CALLBACK_OBSERVE, (lcb_RESPCALLBACK)observe_callback); + const vector& keys = parser.getRestArgs(); + lcb_MULTICMD_CTX *mctx = lcb_observe3_ctxnew(instance); + if (mctx == NULL) { + throw std::bad_alloc(); + } + + lcb_error_t err; + for (size_t ii = 0; ii < keys.size(); ii++) { + lcb_CMDOBSERVE cmd = { 0 }; + LCB_KREQ_SIMPLE(&cmd.key, keys[ii].c_str(), keys[ii].size()); + err = mctx->addcmd(mctx, (lcb_CMDBASE*)&cmd); + if (err != LCB_SUCCESS) { + throw LcbError(err); + } + } + + lcb_sched_enter(instance); + err = mctx->done(mctx, NULL); + if (err == LCB_SUCCESS) { + lcb_sched_leave(instance); + lcb_wait(instance); + } else { + lcb_sched_fail(instance); + throw LcbError(err); + } +} + +void +ObserveSeqnoHandler::run() +{ + Handler::run(); + lcb_install_callback3(instance, LCB_CALLBACK_OBSEQNO, (lcb_RESPCALLBACK)obseqno_callback); + const vector& infos = parser.getRestArgs(); + lcb_CMDOBSEQNO cmd = { 0 }; + lcbvb_CONFIG *vbc; + lcb_error_t rc; + + rc = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_VBCONFIG, &vbc); + if (rc != LCB_SUCCESS) { + throw LcbError(rc); + } + + lcb_sched_enter(instance); + + for (size_t ii = 0; ii < infos.size(); ++ii) { + const string& cur = infos[ii]; + unsigned vbid; + unsigned long long uuid; + int rv = sscanf(cur.c_str(), "%u,%llu", &vbid, &uuid); + if (rv != 2) { + throw BadArg("Must pass sequences of base10 vbid and base16 uuids"); + } + cmd.uuid = uuid; + cmd.vbid = vbid; + for (size_t jj = 0; jj < lcbvb_get_nreplicas(vbc) + 1; ++jj) { + int ix = lcbvb_vbserver(vbc, vbid, jj); + if (ix < 0) { + fprintf(stderr, "Server %d unavailable (skipping)\n", ix); + } + cmd.server_index = ix; + rc = lcb_observe_seqno3(instance, NULL, &cmd); + if (rc != LCB_SUCCESS) { + throw LcbError(rc); + } + } + } + lcb_sched_leave(instance); + lcb_wait(instance); +} + +void +UnlockHandler::run() +{ + Handler::run(); + lcb_install_callback3(instance, LCB_CALLBACK_UNLOCK, common_callback); + const vector& args = parser.getRestArgs(); + + if (args.size() % 2) { + throw BadArg("Expect key-cas pairs. Argument list must be even"); + } + + lcb_sched_enter(instance); + for (size_t ii = 0; ii < args.size(); ii += 2) { + const string& key = args[ii]; + lcb_CAS cas; + int rv; + rv = sscanf(args[ii+1].c_str(), "0x%" PRIx64, &cas); + if (rv != 1) { + throw BadArg("CAS must be formatted as a hex string beginning with '0x'"); + } + + lcb_CMDUNLOCK cmd; + memset(&cmd, 0, sizeof cmd); + LCB_KREQ_SIMPLE(&cmd.key, key.c_str(), key.size()); + cmd.cas = cas; + lcb_error_t err = lcb_unlock3(instance, NULL, &cmd); + if (err != LCB_SUCCESS) { + throw LcbError(err); + } + } + lcb_sched_leave(instance); + lcb_wait(instance); +} + +static const char * +iops_to_string(lcb_io_ops_type_t type) +{ + switch (type) { + case LCB_IO_OPS_LIBEV: return "libev"; + case LCB_IO_OPS_LIBEVENT: return "libevent"; + case LCB_IO_OPS_LIBUV: return "libuv"; + case LCB_IO_OPS_SELECT: return "select"; + case LCB_IO_OPS_WINIOCP: return "iocp"; + case LCB_IO_OPS_INVALID: return "user-defined"; + default: return "invalid"; + } +} + +void +VersionHandler::run() +{ + const char *changeset; + lcb_error_t err; + err = lcb_cntl(NULL, LCB_CNTL_GET, LCB_CNTL_CHANGESET, (void*)&changeset); + if (err != LCB_SUCCESS) { + changeset = "UNKNOWN"; + } + fprintf(stderr, "cbc:\n"); + fprintf(stderr, " Runtime: Version=%s, Changeset=%s\n", + lcb_get_version(NULL), changeset); + fprintf(stderr, " Headers: Version=%s, Changeset=%s\n", + LCB_VERSION_STRING, LCB_VERSION_CHANGESET); + + struct lcb_cntl_iops_info_st info; + memset(&info, 0, sizeof info); + err = lcb_cntl(NULL, LCB_CNTL_GET, LCB_CNTL_IOPS_DEFAULT_TYPES, &info); + if (err == LCB_SUCCESS) { + fprintf(stderr, " IO: Default=%s, Current=%s\n", + iops_to_string(info.v.v0.os_default), iops_to_string(info.v.v0.effective)); + } + printf(" SSL: .. %s\n", + lcb_supports_feature(LCB_SUPPORTS_SSL) ? "SUPPORTED" : "NOT SUPPORTED"); +} + +void +RemoveHandler::run() +{ + Handler::run(); + const vector &keys = parser.getRestArgs(); + lcb_sched_enter(instance); + lcb_install_callback3(instance, LCB_CALLBACK_REMOVE, common_callback); + for (size_t ii = 0; ii < keys.size(); ++ii) { + lcb_CMDREMOVE cmd; + const string& key = keys[ii]; + memset(&cmd, 0, sizeof cmd); + LCB_KREQ_SIMPLE(&cmd.key, key.c_str(), key.size()); + lcb_error_t err = lcb_remove3(instance, NULL, &cmd); + if (err != LCB_SUCCESS) { + throw LcbError(err); + } + } + lcb_sched_leave(instance); + lcb_wait(instance); +} + +void +StatsHandler::run() +{ + Handler::run(); + lcb_install_callback3(instance, LCB_CALLBACK_STATS, (lcb_RESPCALLBACK)stats_callback); + vector keys = parser.getRestArgs(); + if (keys.empty()) { + keys.push_back(""); + } + lcb_sched_enter(instance); + for (size_t ii = 0; ii < keys.size(); ii++) { + lcb_CMDSTATS cmd = { 0 }; + const string& key = keys[ii]; + if (!key.empty()) { + LCB_KREQ_SIMPLE(&cmd.key, key.c_str(), key.size()); + if (o_keystats.result()) { + cmd.cmdflags = LCB_CMDSTATS_F_KV; + } + } + bool is_keystats = o_keystats.result(); + lcb_error_t err = lcb_stats3(instance, &is_keystats, &cmd); + if (err != LCB_SUCCESS) { + throw LcbError(err); + } + } + lcb_sched_leave(instance); + lcb_wait(instance); +} + +void +VerbosityHandler::run() +{ + Handler::run(); + + const string& slevel = getRequiredArg(); + lcb_verbosity_level_t level; + if (slevel == "detail") { + level = LCB_VERBOSITY_DETAIL; + } else if (slevel == "debug") { + level = LCB_VERBOSITY_DEBUG; + } else if (slevel == "info") { + level = LCB_VERBOSITY_INFO; + } else if (slevel == "warning") { + level = LCB_VERBOSITY_WARNING; + } else { + throw BadArg("Verbosity level must be {detail,debug,info,warning}"); + } + + lcb_install_callback3(instance, LCB_CALLBACK_VERBOSITY, (lcb_RESPCALLBACK)common_server_callback); + lcb_CMDVERBOSITY cmd = { 0 }; + cmd.level = level; + lcb_error_t err; + lcb_sched_enter(instance); + err = lcb_server_verbosity3(instance, NULL, &cmd); + if (err != LCB_SUCCESS) { + throw LcbError(err); + } + lcb_sched_leave(instance); + lcb_wait(instance); +} + +void +McFlushHandler::run() +{ + Handler::run(); + + lcb_CMDFLUSH cmd = { 0 }; + lcb_error_t err; + lcb_install_callback3(instance, LCB_CALLBACK_FLUSH, (lcb_RESPCALLBACK)common_server_callback); + lcb_sched_enter(instance); + err = lcb_flush3(instance, NULL, &cmd); + if (err != LCB_SUCCESS) { + throw LcbError(err); + } + lcb_sched_leave(instance); + lcb_wait(instance); +} + + +extern "C" { +static void cbFlushCb(lcb_t, int, const lcb_RESPBASE *resp) +{ + if (resp->rc == LCB_SUCCESS) { + fprintf(stderr, "Flush OK\n"); + } else { + fprintf(stderr, "Flush failed: %s (0x%x)\n", + lcb_strerror(NULL, resp->rc), resp->rc); + } +} +} +void +BucketFlushHandler::run() +{ + Handler::run(); + lcb_CMDCBFLUSH cmd = { 0 }; + lcb_error_t err; + lcb_install_callback3(instance, LCB_CALLBACK_CBFLUSH, cbFlushCb); + err = lcb_cbflush3(instance, NULL, &cmd); + if (err != LCB_SUCCESS) { + throw LcbError(err); + } else { + lcb_wait(instance); + } +} + +void +ArithmeticHandler::run() +{ + Handler::run(); + + const vector& keys = parser.getRestArgs(); + lcb_install_callback3(instance, LCB_CALLBACK_COUNTER, (lcb_RESPCALLBACK)arithmetic_callback); + lcb_sched_enter(instance); + for (size_t ii = 0; ii < keys.size(); ++ii) { + const string& key = keys[ii]; + lcb_CMDCOUNTER cmd = { 0 }; + LCB_KREQ_SIMPLE(&cmd.key, key.c_str(), key.size()); + if (o_initial.passed()) { + cmd.create = 1; + cmd.initial = o_initial.result(); + } + uint64_t delta = o_delta.result(); + if (delta > std::numeric_limits::max()) { + throw BadArg("Delta too big"); + } + cmd.delta = static_cast(delta); + if (shouldInvert()) { + cmd.delta *= -1; + } + cmd.exptime = o_expiry.result(); + lcb_error_t err = lcb_counter3(instance, NULL, &cmd); + if (err != LCB_SUCCESS) { + throw LcbError(err); + } + } + lcb_sched_leave(instance); + lcb_wait(instance); +} + +void +ViewsHandler::run() +{ + Handler::run(); + + const string& s = getRequiredArg(); + size_t pos = s.find('/'); + if (pos == string::npos) { + throw BadArg("View must be in the format of design/view"); + } + + string ddoc = s.substr(0, pos); + string view = s.substr(pos+1); + string opts = o_params.result(); + + lcb_CMDVIEWQUERY cmd = { 0 }; + lcb_view_query_initcmd(&cmd, + ddoc.c_str(), view.c_str(), opts.c_str(), view_callback); + if (o_spatial) { + cmd.cmdflags |= LCB_CMDVIEWQUERY_F_SPATIAL; + } + if (o_incdocs) { + cmd.cmdflags |= LCB_CMDVIEWQUERY_F_INCLUDE_DOCS; + } + + lcb_error_t rc; + rc = lcb_view_query(instance, NULL, &cmd); + if (rc != LCB_SUCCESS) { + throw LcbError(rc); + } + lcb_wait(instance); +} + +static void +splitKvParam(const string& src, string& key, string& value) +{ + size_t pp = src.find('='); + if (pp == string::npos) { + throw BadArg("Param must be in the form of key=value"); + } + + key = src.substr(0, pp); + value = src.substr(pp+1); +} + +extern "C" { +static void n1qlCallback(lcb_t, int, const lcb_RESPN1QL *resp) +{ + if (resp->rflags & LCB_RESP_F_FINAL) { + fprintf(stderr, "** N1QL Response finished\n"); + if (resp->rc != LCB_SUCCESS) { + fprintf(stderr, "N1QL query failed with library code 0x%x\n", resp->rc); + if (resp->htresp) { + fprintf(stderr, "Inner HTTP request failed with library code 0x%x and HTTP status %d\n", + resp->htresp->rc, resp->htresp->htstatus); + } + } + if (resp->row) { + printf("%.*s\n", (int)resp->nrow, resp->row); + } + } else { + printf("%.*s,\n", (int)resp->nrow, resp->row); + } +} +} + +void +N1qlHandler::run() +{ + Handler::run(); + const string& qstr = getRequiredArg(); + + lcb_N1QLPARAMS *nparams = lcb_n1p_new(); + lcb_error_t rc; + + rc = lcb_n1p_setquery(nparams, qstr.c_str(), -1, LCB_N1P_QUERY_STATEMENT); + if (rc != LCB_SUCCESS) { + throw LcbError(rc); + } + + const vector& vv_args = o_args.const_result(); + for (size_t ii = 0; ii < vv_args.size(); ii++) { + string key, value; + splitKvParam(vv_args[ii], key, value); + string ktmp = "$" + key; + rc = lcb_n1p_namedparamz(nparams, ktmp.c_str(), value.c_str()); + if (rc != LCB_SUCCESS) { + throw LcbError(rc); + } + } + + const vector& vv_opts = o_opts.const_result(); + for (size_t ii = 0; ii < vv_opts.size(); ii++) { + string key, value; + splitKvParam(vv_opts[ii], key, value); + rc = lcb_n1p_setoptz(nparams, key.c_str(), value.c_str()); + if (rc != LCB_SUCCESS) { + throw LcbError(rc); + } + } + + lcb_CMDN1QL cmd = { 0 }; + rc = lcb_n1p_mkcmd(nparams, &cmd); + if (rc != LCB_SUCCESS) { + throw LcbError(rc); + } + if (o_prepare.passed()) { + cmd.cmdflags |= LCB_CMDN1QL_F_PREPCACHE; + } + fprintf(stderr, "Encoded query: %.*s\n", (int)cmd.nquery, cmd.query); + cmd.callback = n1qlCallback; + rc = lcb_n1ql_query(instance, NULL, &cmd); + if (rc != LCB_SUCCESS) { + throw LcbError(rc); + } + lcb_n1p_free(nparams); + lcb_wait(instance); +} + +void +HttpReceiver::install(lcb_t instance) +{ + lcb_install_callback3(instance, LCB_CALLBACK_HTTP, + (lcb_RESPCALLBACK)http_callback); +} + +void +HttpReceiver::maybeInvokeStatus(const lcb_RESPHTTP *resp) +{ + if (statusInvoked) { + return; + } + + statusInvoked = true; + if (resp->headers) { + for (const char * const *cur = resp->headers; *cur; cur += 2) { + string key = cur[0]; + string value = cur[1]; + headers[key] = value; + } + } + handleStatus(resp->rc, resp->htstatus); +} + +void +HttpBaseHandler::run() +{ + Handler::run(); + install(instance); + lcb_http_cmd_st cmd; + memset(&cmd, 0, sizeof cmd); + string uri = getURI(); + const string& body = getBody(); + + cmd.v.v0.method = getMethod(); + cmd.v.v0.chunked = 1; + cmd.v.v0.path = uri.c_str(); + cmd.v.v0.npath = uri.size(); + if (!body.empty()) { + cmd.v.v0.body = body.c_str(); + cmd.v.v0.nbody = body.size(); + } + string ctype = getContentType(); + if (!ctype.empty()) { + cmd.v.v0.content_type = ctype.c_str(); + } + + lcb_http_request_t dummy; + lcb_error_t err; + err = lcb_make_http_request(instance, (HttpReceiver*)this, + isAdmin() ? LCB_HTTP_TYPE_MANAGEMENT : LCB_HTTP_TYPE_VIEW, + &cmd, &dummy); + if (err != LCB_SUCCESS) { + throw LcbError(err); + } + + lcb_wait(instance); +} + +lcb_http_method_t +HttpBaseHandler::getMethod() +{ + string smeth = o_method.result(); + if (smeth == "GET") { + return LCB_HTTP_METHOD_GET; + } else if (smeth == "POST") { + return LCB_HTTP_METHOD_POST; + } else if (smeth == "DELETE") { + return LCB_HTTP_METHOD_DELETE; + } else if (smeth == "PUT") { + return LCB_HTTP_METHOD_PUT; + } else { + throw BadArg("Unrecognized method string"); + } +} + +const string& +HttpBaseHandler::getBody() +{ + if (!body_cached.empty()) { + return body_cached; + } + lcb_http_method_t meth = getMethod(); + if (meth == LCB_HTTP_METHOD_GET || meth == LCB_HTTP_METHOD_DELETE) { + return body_cached; // empty + } + + char buf[4096]; + size_t nr; + while ( (nr = fread(buf, 1, sizeof buf, stdin)) != 0) { + body_cached.append(buf, nr); + } + return body_cached; +} + +void +HttpBaseHandler::handleStatus(lcb_error_t err, int code) +{ + if (err != LCB_SUCCESS) { + fprintf(stderr, "ERROR=0x%x (%s) ", err, lcb_strerror(NULL, err)); + } + fprintf(stderr, "%d\n", code); + map::const_iterator ii = headers.begin(); + for (; ii != headers.end(); ii++) { + fprintf(stderr, " %s: %s\n", ii->first.c_str(), ii->second.c_str()); + } +} + +string +AdminHandler::getURI() +{ + return getRequiredArg(); +} + +void +AdminHandler::run() +{ + fprintf(stderr, "Requesting %s\n", getURI().c_str()); + HttpBaseHandler::run(); + printf("%s", resbuf.c_str()); +} + +void +BucketCreateHandler::run() +{ + const string& name = getRequiredArg(); + const string& btype = o_btype.const_result(); + stringstream ss; + + if (btype == "couchbase" || btype == "membase") { + isMemcached = false; + } else if (btype == "memcached") { + isMemcached = true; + } else { + throw BadArg("Unrecognized bucket type"); + } + if (o_proxyport.passed() && o_bpass.passed()) { + throw BadArg("Custom ASCII port is only available for auth-less buckets"); + } + + ss << "name=" << name; + ss << "&bucketType=" << btype; + ss << "&ramQuotaMB=" << o_ramquota.result(); + if (o_proxyport.passed()) { + ss << "&authType=none&proxyPort=" << o_proxyport.result(); + } else { + ss << "&authType=sasl&saslPassword=" << o_bpass.result(); + } + + ss << "&replicaNumber=" << o_replicas.result(); + body_s = ss.str(); + + AdminHandler::run(); +} + +struct HostEnt { + string protostr; + string hostname; + HostEnt(const std::string& host, const char *proto) { + protostr = proto; + hostname = host; + } + HostEnt(const std::string& host, const char* proto, int port) { + protostr = proto; + hostname = host; + stringstream ss; + ss << std::dec << port; + hostname += ":"; + hostname += ss.str(); + } +}; + +void +ConnstrHandler::run() +{ + const string& connstr_s = getRequiredArg(); + lcb_error_t err; + const char *errmsg; + lcb::Connspec spec; + memset(&spec, 0, sizeof spec); + err = spec.parse(connstr_s.c_str(), &errmsg); + if (err != LCB_SUCCESS) { + throw BadArg(errmsg); + } + + printf("Bucket: %s\n", spec.bucket().c_str()); + printf("Implicit port: %d\n", spec.default_port()); + string sslOpts; + if (spec.sslopts() & LCB_SSL_ENABLED) { + sslOpts = "ENABLED"; + if (spec.sslopts() & LCB_SSL_NOVERIFY) { + sslOpts += "|NOVERIFY"; + } + } + printf("SSL: %s\n", sslOpts.c_str()); + + printf("Boostrap Protocols: "); + string bsStr; + if (spec.is_bs_cccp()) { + bsStr += "CCCP, "; + } + if (spec.is_bs_http()) { + bsStr += "HTTP, "; + } + if (bsStr.empty()) { + bsStr = "CCCP,HTTP"; + } else { + bsStr.erase(bsStr.size()-1, 1); + } + printf("%s\n", bsStr.c_str()); + printf("Hosts:\n"); + vector hosts; + + for (size_t ii = 0; ii < spec.hosts().size(); ++ii) { + const lcb::Spechost *dh = &spec.hosts()[ii]; + lcb_U16 port = dh->port; + if (!port) { + port = spec.default_port(); + } + + if (dh->type == LCB_CONFIG_MCD_PORT) { + hosts.push_back(HostEnt(dh->hostname, "memcached", port)); + } else if (dh->type == LCB_CONFIG_MCD_SSL_PORT) { + hosts.push_back(HostEnt(dh->hostname, "memcached+ssl", port)); + } else if (dh->type == LCB_CONFIG_HTTP_PORT) { + hosts.push_back(HostEnt(dh->hostname, "restapi", port)); + } else if (dh->type == LCB_CONFIG_HTTP_SSL_PORT) { + hosts.push_back(HostEnt(dh->hostname, "restapi+ssl", port)); + } else { + if (spec.sslopts()) { + hosts.push_back(HostEnt(dh->hostname, "memcached+ssl", LCB_CONFIG_MCD_SSL_PORT)); + hosts.push_back(HostEnt(dh->hostname, "restapi+ssl", LCB_CONFIG_HTTP_SSL_PORT)); + } else { + hosts.push_back(HostEnt(dh->hostname, "memcached", LCB_CONFIG_MCD_PORT)); + hosts.push_back(HostEnt(dh->hostname, "restapi", LCB_CONFIG_HTTP_PORT)); + } + } + } + for (size_t ii = 0; ii < hosts.size(); ii++) { + HostEnt& ent = hosts[ii]; + string protostr = "[" + ent.protostr + "]"; + printf(" %-20s%s\n", protostr.c_str(), ent.hostname.c_str()); + } + + printf("Options: \n"); + lcb::Connspec::Options::const_iterator it = spec.options().begin(); + for (; it != spec.options().end(); ++it) { + printf(" %s=%s\n", it->first.c_str(), it->second.c_str()); + } +} + +void +WriteConfigHandler::run() +{ + lcb_create_st cropts; + params.fillCropts(cropts); + string outname = getLoneArg(); + if (outname.empty()) { + outname = ConnParams::getConfigfileName(); + } + // Generate the config + params.writeConfig(outname); +} + +static map handlers; +static map handlers_s; +static const char* optionsOrder[] = { + "help", + "cat", + "create", + "touch", + "observe", + "observe-seqno", + "incr", + "decr", + "mcflush", + "hash", + "lock", + "unlock", + "rm", + "stats", + // "verify, + "version", + "verbosity", + "view", + "n1ql", + "admin", + "bucket-create", + "bucket-delete", + "bucket-flush", + "connstr", + "write-config", + "strerror", + NULL +}; + +class HelpHandler : public Handler { +public: + HelpHandler() : Handler("help") {} + HANDLER_DESCRIPTION("Show help") +protected: + void run() { + fprintf(stderr, "Usage: cbc [options]\n"); + fprintf(stderr, "command may be:\n"); + for (const char ** cur = optionsOrder; *cur; cur++) { + const Handler *handler = handlers[*cur]; + fprintf(stderr, " %-20s", *cur); + fprintf(stderr, "%s\n", handler->description()); + } + } +}; + +class StrErrorHandler : public Handler { +public: + StrErrorHandler() : Handler("strerror") {} + HANDLER_DESCRIPTION("Decode library error code") + HANDLER_USAGE("HEX OR DECIMAL CODE") +protected: + void handleOptions() { } + void run() { + std::string nn = getRequiredArg(); + // Try to parse it as a hexadecimal number + unsigned errcode; + int rv = sscanf(nn.c_str(), "0x%x", &errcode); + if (rv != 1) { + rv = sscanf(nn.c_str(), "%u", &errcode); + if (rv != 1) { + throw BadArg("Need decimal or hex code!"); + } + } + + #define X(cname, code, cat, desc) \ + if (code == errcode) { \ + fprintf(stderr, "%s\n", #cname); \ + fprintf(stderr, " Type: 0x%x\n", cat); \ + fprintf(stderr, " Description: %s\n", desc); \ + return; \ + } \ + + LCB_XERR(X) + #undef X + + fprintf(stderr, "-- Error code not found in header. Trying runtime..\n"); + fprintf(stderr, "0x%x: %s\n", errcode, lcb_strerror(NULL, (lcb_error_t)errcode)); + } +}; + +static void +setupHandlers() +{ + handlers_s["get"] = new GetHandler(); + handlers_s["create"] = new SetHandler(); + handlers_s["hash"] = new HashHandler(); + handlers_s["help"] = new HelpHandler(); + handlers_s["lock"] = new GetHandler("lock"); + handlers_s["observe"] = new ObserveHandler(); + handlers_s["unlock"] = new UnlockHandler(); + handlers_s["version"] = new VersionHandler(); + handlers_s["rm"] = new RemoveHandler(); + handlers_s["cp"] = new SetHandler("cp"); + handlers_s["stats"] = new StatsHandler(); + handlers_s["verbosity"] = new VerbosityHandler(); + handlers_s["mcflush"] = new McFlushHandler(); + handlers_s["incr"] = new IncrHandler(); + handlers_s["decr"] = new DecrHandler(); + handlers_s["admin"] = new AdminHandler(); + handlers_s["bucket-create"] = new BucketCreateHandler(); + handlers_s["bucket-delete"] = new BucketDeleteHandler(); + handlers_s["bucket-flush"] = new BucketFlushHandler(); + handlers_s["view"] = new ViewsHandler(); + handlers_s["n1ql"] = new N1qlHandler(); + handlers_s["connstr"] = new ConnstrHandler(); + handlers_s["write-config"] = new WriteConfigHandler(); + handlers_s["strerror"] = new StrErrorHandler(); + handlers_s["observe-seqno"] = new ObserveSeqnoHandler(); + handlers_s["touch"] = new TouchHandler(); + + + + map::iterator ii; + for (ii = handlers_s.begin(); ii != handlers_s.end(); ++ii) { + handlers[ii->first] = ii->second; + } + + handlers["cat"] = handlers["get"]; +} + +#if _POSIX_VERSION >= 200112L +#include +#define HAVE_BASENAME +#endif + +static void +parseCommandname(string& cmdname, int&, char**& argv) +{ +#ifdef HAVE_BASENAME + cmdname = basename(argv[0]); + size_t dashpos; + + if (cmdname.find("cbc") != 0) { + cmdname.clear(); + // Doesn't start with cbc + return; + } + + if ((dashpos = cmdname.find('-')) != string::npos && + cmdname.find("cbc") != string::npos && + dashpos+1 < cmdname.size()) { + + // Get the actual command name + cmdname = cmdname.substr(dashpos+1); + return; + } +#else + (void)argv; +#endif + cmdname.clear(); +} + +static void +wrapExternalBinary(int argc, char **argv, const std::string& name) +{ +#ifdef _POSIX_VERSION + vector args; + string exePath(argv[0]); + size_t cbc_pos = exePath.find("cbc"); + + if (cbc_pos == string::npos) { + throw BadArg("Couldn't invoke " + name); + } + + exePath.replace(cbc_pos, 3, name); + args.push_back((char*)exePath.c_str()); + + // { "cbc", "name" } + argv += 2; argc -= 2; + for (int ii = 0; ii < argc; ii++) { + args.push_back(argv[ii]); + } + args.push_back((char*)NULL); + execvp(exePath.c_str(), &args[0]); + perror(exePath.c_str()); + throw BadArg("Couldn't execute " + name + " !"); +#else + throw BadArg("Can't wrap around " + name + " on non-POSIX environments"); +#endif +} + +int main(int argc, char **argv) +{ + + // Wrap external binaries immediately + if (argc >= 2) { + if (strcmp(argv[1], "pillowfight") == 0) { + wrapExternalBinary(argc, argv, "cbc-pillowfight"); + } else if (strcmp(argv[1], "n1qlback") == 0) { + wrapExternalBinary(argc, argv, "cbc-n1qlback"); + } + } + + setupHandlers(); + string cmdname; + parseCommandname(cmdname, argc, argv); + + if (cmdname.empty()) { + if (argc < 2) { + fprintf(stderr, "Must provide an option name\n"); + try { + HelpHandler().execute(argc, argv); + } catch (std::exception& exc) { + std::cerr << exc.what() << std::endl; + } + exit(EXIT_FAILURE); + } else { + cmdname = argv[1]; + argv++; + argc--; + } + } + + Handler *handler = handlers[cmdname]; + if (handler == NULL) { + fprintf(stderr, "Unknown command %s\n", cmdname.c_str()); + HelpHandler().execute(argc, argv); + exit(EXIT_FAILURE); + } + + try { + handler->execute(argc, argv); + + } catch (std::exception& err) { + fprintf(stderr, "%s\n", err.what()); + exit(EXIT_FAILURE); + } + + map::iterator iter = handlers_s.begin(); + for (; iter != handlers_s.end(); iter++) { + delete iter->second; + } + exit(EXIT_SUCCESS); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tools/common/histogram.cc b/couchbase-sys/libcouchbase-2.7.0/tools/common/histogram.cc new file mode 100644 index 00000000..94353286 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tools/common/histogram.cc @@ -0,0 +1,43 @@ +#include "histogram.h" +#include +using namespace cbc; +using std::string; + +void +Histogram::install(lcb_t inst, FILE *out) +{ + lcb_error_t rc; + output = out; + lcb_enable_timings(inst); + rc = lcb_cntl(inst, LCB_CNTL_GET, LCB_CNTL_KVTIMINGS, &hg); + assert(rc == LCB_SUCCESS); + assert(hg != NULL); +} + +void +Histogram::installStandalone(FILE *out) +{ + if (hg != NULL) { + return; + } + hg = lcb_histogram_create(); + output = out; +} + +void +Histogram::write() +{ + if (hg == NULL) { + return; + } + lcb_histogram_print(hg, output); +} + +void +Histogram::record(lcb_U64 duration) +{ + if (hg == NULL) { + return; + } + lcb_histogram_record(hg, duration); +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tools/common/histogram.h b/couchbase-sys/libcouchbase-2.7.0/tools/common/histogram.h new file mode 100644 index 00000000..558c0e48 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tools/common/histogram.h @@ -0,0 +1,23 @@ +#ifndef CBC_HISTOGRAM_H +#define CBC_HISTOGRAM_H +#include +#include + +namespace cbc { + +class Histogram { +public: + Histogram() { hg = NULL; output = NULL; } + void install(lcb_t, FILE *out = stderr); + void installStandalone(FILE *out = stderr); + void record(lcb_U64 duration); + void write(); + FILE *getOutput() const { return output; } +private: + lcb_HISTOGRAM *hg; + FILE *output; +}; + +} + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/tools/common/my_inttypes.h b/couchbase-sys/libcouchbase-2.7.0/tools/common/my_inttypes.h new file mode 100644 index 00000000..9d49bce1 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tools/common/my_inttypes.h @@ -0,0 +1,22 @@ +#ifndef LCB_MY_INTTYPES_H +#define LCB_MY_INTTYPES_H +#ifdef __cplusplus +#define __STDC_FORMAT_MACROS +#endif + +#if __STDC_VERSION__ >= 199901L || __GNUC__ +#include +#elif _MSC_VER +#ifndef PRIx64 +#define PRIx64 "I64x" +#endif +#ifndef PRIX64 +#define PRIX64 "I64x" +#endif + +#ifndef PRIu64 +#define PRIu64 "I64u" +#endif + +#endif /* _MSC_VER */ +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/tools/common/options.cc b/couchbase-sys/libcouchbase-2.7.0/tools/common/options.cc new file mode 100644 index 00000000..e4ef3bc0 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tools/common/options.cc @@ -0,0 +1,420 @@ +#include "options.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace cbc; +using namespace cliopts; +using std::string; +using std::ifstream; +using std::ofstream; +using std::endl; + +#ifndef _WIN32 +#include +#include +#endif + +static string promptPassword(const char *prompt) +{ +#ifndef _WIN32 + termios oldattr, newattr; + tcgetattr(STDIN_FILENO, &oldattr); + newattr = oldattr; + newattr.c_lflag &= ~ECHO; + tcsetattr(STDIN_FILENO, TCSANOW, &newattr); +#endif + + fprintf(stderr, "%s", prompt); + fflush(stderr); + // Use cin here. A bit more convenient + string ret; + std::cin >> ret; +#ifndef _WIN32 + fprintf(stderr, "\n"); + tcsetattr(STDIN_FILENO, TCSANOW, &oldattr); +#endif + return ret; +} + +static void +makeLowerCase(string &s) +{ + for (size_t ii = 0; ii < s.size(); ++ii) { + s[ii] = tolower(s[ii]); + } +} + +#define X(tpname, varname, longname, shortname) o_##varname(longname), +ConnParams::ConnParams() : + X_OPTIONS(X) + isAdmin(false) +{ + // Configure the options + #undef X + #define X(tpname, varname, longname, shortname) \ + o_##varname.abbrev(shortname); + X_OPTIONS(X) + #undef X + + o_host.description("Hostname to connect to").setDefault("localhost"); + o_host.hide(); + + o_bucket.description("Bucket to use").setDefault("default"); + o_bucket.hide(); + + o_connstr.description("Connection string").setDefault("couchbase://localhost/default"); + o_user.description("Username"); + o_passwd.description("Bucket password"); + o_saslmech.description("Force SASL mechanism").argdesc("PLAIN|CRAM_MD5"); + o_timings.description("Enable command timings"); + o_timeout.description("Operation timeout"); + o_timeout.hide(); + o_transport.description("Bootstrap protocol").argdesc("HTTP|CCCP|ALL").setDefault("ALL"); + o_configcache.description("Path to cached configuration"); + o_ssl.description("Enable SSL settings").argdesc("ON|OFF|NOVERIFY").setDefault("off"); + o_certpath.description("Path to server certificate"); + o_verbose.description("Set debugging output (specify multiple times for greater verbosity"); + o_dump.description("Dump verbose internal state after operations are done"); + + o_cparams.description("Additional options for connection. " + "Use -Dtimeout=SECONDS for KV operation timeout"); + o_cparams.argdesc("OPTION=VALUE"); + + // Hide some more exotic options + o_saslmech.hide(); + o_transport.hide(); + o_ssl.hide(); +} + +void +ConnParams::setAdminMode() +{ + o_user.description("Administrative username").setDefault("Administrator"); + o_passwd.description("Administrative password"); + isAdmin = true; +} + +void +ConnParams::addToParser(Parser& parser) +{ + string errmsg; + try { + loadFileDefaults(); + } catch (string& exc) { + errmsg = exc; + } catch (const char *& exc) { + errmsg = exc; + } + if (!errmsg.empty()) { + string newmsg = "Error processing `"; + newmsg += getConfigfileName(); + newmsg += "`. "; + newmsg += errmsg; + throw BadArg(newmsg); + } + + #define X(tp, varname, longname, shortname) parser.addOption(o_##varname); + X_OPTIONS(X) + #undef X +} + +string +ConnParams::getConfigfileName() +{ + const char *override = getenv("CBC_CONFIG"); + if (override && *override) { + return override; + } + + string ret; +#if _WIN32 + const char *v = getenv("APPDATA"); + if (v) { + ret = v; + ret += "\\"; + ret += CBC_WIN32_APPDIR; + ret += "\\"; + ret += CBC_CONFIG_FILENAME; + } +#else + const char *home = getenv("HOME"); + if (home) { + ret = home; + ret += "/"; + } + ret += CBC_CONFIG_FILENAME; +#endif + return ret; +} + +static void +stripWhitespacePadding(string& s) +{ + while (s.empty() == false && isspace( (int) s[0])) { + s.erase(0, 1); + } + while (s.empty() == false && isspace( (int) s[s.size()-1])) { + s.erase(s.size()-1, 1); + } +} + +bool +ConnParams::loadFileDefaults() +{ + // Figure out the home directory + ifstream f(getConfigfileName().c_str()); + if (!f.good()) { + return false; + } + + string curline; + while ((std::getline(f, curline).good()) && !f.eof()) { + string key, value; + size_t pos; + + stripWhitespacePadding(curline); + if (curline.empty() || curline[0] == '#') { + continue; + } + + pos = curline.find('='); + if (pos == string::npos || pos == curline.size()-1) { + throw BadArg("Configuration file must be formatted as key-value pairs"); + } + + key = curline.substr(0, pos); + value = curline.substr(pos+1); + stripWhitespacePadding(key); + stripWhitespacePadding(value); + if (key.empty() || value.empty()) { + throw BadArg("Key and value cannot be empty"); + } + + if (key == "uri") { + // URI isn't really supported anymore, but try to be compatible + o_host.setDefault(value).setPassed(); + } else if (key == "user") { + o_user.setDefault(value).setPassed(); + } else if (key == "password") { + o_passwd.setDefault(value).setPassed(); + } else if (key == "bucket") { + o_bucket.setDefault(value).setPassed(); + } else if (key == "timeout") { + unsigned ival = 0; + if (!sscanf(value.c_str(), "%u", &ival)) { + throw BadArg("Invalid formatting for timeout"); + } + o_timeout.setDefault(ival).setPassed(); + } else if (key == "connstr") { + o_connstr.setDefault(value).setPassed(); + } else if (key == "certpath") { + o_certpath.setDefault(value).setPassed(); + } else if (key == "ssl") { + o_ssl.setDefault(value).setPassed(); + } else { + throw BadArg(string("Unrecognized key: ") + key); + } + } + return true; +} + +static void +writeOption(ofstream& f, StringOption& opt, const string& key) +{ + if (!opt.passed()) { + return; + } + f << key << '=' << opt.const_result() << endl; +} + +void +ConnParams::writeConfig(const string& s) +{ + // Figure out the intermediate directories + ofstream f; + try { + f.exceptions(std::ios::failbit|std::ios::badbit); + f.open(s.c_str()); + } catch (std::exception& ex) { + throw std::runtime_error("Couldn't open " + s + " " + ex.what()); + } + + time_t now = time(NULL); + const char *timestr = ctime(&now); + f << "# Generated by cbc at " << string(timestr) << endl; + + if (!connstr.empty()) { + // Contains bucket, user + f << "connstr=" << connstr << endl; + } + writeOption(f, o_user, "user"); + writeOption(f, o_passwd, "password"); + writeOption(f, o_ssl, "ssl"); + writeOption(f, o_certpath, "certpath"); + + if (o_timeout.passed()) { + f << "timeout=" << std::dec << o_timeout.result() << endl; + } + + f.flush(); + f.close(); +} + +void +ConnParams::fillCropts(lcb_create_st& cropts) +{ + passwd = o_passwd.result(); + if (passwd == "-") { + passwd = promptPassword("Bucket password: "); + } + + if (o_connstr.passed()) { + if (o_host.passed() || o_bucket.passed()) { + throw BadArg("Use of the deprecated " + "-h/--host or -b/--bucket options with -U is " + "not allowed!"); + } + connstr = o_connstr.const_result(); + if (connstr.find('?') == string::npos) { + connstr += '?'; + } else if (connstr[connstr.size()-1] != '&') { + connstr += '&'; + } + } else { + string host = o_host.result(); + string bucket = o_bucket.result(); + + for (size_t ii = 0; ii < host.size(); ++ii) { + if (host[ii] == ';') { + host[ii] = ','; + } + } + + if (o_host.passed() || o_bucket.passed()) { + fprintf(stderr, "CBC: WARNING\n"); + fprintf(stderr, " The -h/--host and -b/--bucket options are deprecated. Use connection string instead\n"); + fprintf(stderr, " e.g. -U couchbase://%s/%s\n", host.c_str(), o_bucket.const_result().c_str()); + } + + connstr = "http://"; + connstr += host; + connstr += "/"; + connstr += bucket; + connstr += "?"; + } + + if (connstr.find("8091") != string::npos) { + fprintf(stderr, "CBC: WARNING\n"); + fprintf(stderr, " Specifying the default port (8091) has no effect\n"); + } + + if (o_certpath.passed()) { + connstr += "certpath="; + connstr += o_certpath.result(); + connstr += '&'; + } + if (o_ssl.passed()) { + connstr += "ssl="; + connstr += o_ssl.result(); + connstr += '&'; + } + if (o_transport.passed()) { + connstr += "bootstrap_on="; + string tmp = o_transport.result(); + makeLowerCase(tmp); + connstr += tmp; + connstr += '&'; + } + if (o_timeout.passed()) { + std::cerr << "Warning: --timeout option is deprecated. Use -Dtimeout=SECONDS" << std::endl; + std::cerr << " --timeout will be interpreted as SECONDS" << std::endl; + connstr += "operation_timeout="; + std::stringstream ss; + ss << std::dec << o_timeout.result(); + connstr += ss.str(); + connstr += '&'; + } + if (o_configcache.passed()) { + connstr += "config_cache="; + connstr += o_configcache.result(); + connstr += '&'; + } + if (o_user.passed()) { + connstr += "username="; + connstr += o_user.const_result(); + connstr += '&'; + } + + const std::vector& extras = o_cparams.const_result(); + for (size_t ii = 0; ii < extras.size(); ii++) { + connstr += extras[ii]; + connstr += '&'; + } + + int vLevel = 1; + if (o_verbose.passed()) { + vLevel += o_verbose.numSpecified(); + std::stringstream ss; + ss << std::dec << vLevel; + connstr += "console_log_level="; + connstr += ss.str(); + connstr += '&'; + } + + cropts.version = 3; + cropts.v.v3.passwd = passwd.c_str(); + cropts.v.v3.connstr = connstr.c_str(); + if (isAdmin) { + cropts.v.v3.type = LCB_TYPE_CLUSTER; + } else { + cropts.v.v3.type = LCB_TYPE_BUCKET; + } +} + + + +template +void doPctl(lcb_t instance, int cmd, T arg) +{ + lcb_error_t err; + err = lcb_cntl(instance, LCB_CNTL_SET, cmd, (void*)arg); + if (err != LCB_SUCCESS) { + throw LcbError(err); + } +} + +template +void doSctl(lcb_t instance, int cmd, T arg) +{ + doPctl(instance, cmd, &arg); +} + +void doStringCtl(lcb_t instance, const char *s, const char *val) +{ + lcb_error_t err; + err = lcb_cntl_string(instance, s, val); + if (err != LCB_SUCCESS) { + throw LcbError(err); + } +} + +lcb_error_t +ConnParams::doCtls(lcb_t instance) +{ + try { + if (o_saslmech.passed()) { + doPctl(instance,LCB_CNTL_FORCE_SASL_MECH, o_saslmech.result().c_str()); + } + + // Set the detailed error codes option + doSctl(instance, LCB_CNTL_DETAILED_ERRCODES, 1); + } catch (lcb_error_t &err) { + return err; + } + return LCB_SUCCESS; +} diff --git a/couchbase-sys/libcouchbase-2.7.0/tools/common/options.h b/couchbase-sys/libcouchbase-2.7.0/tools/common/options.h new file mode 100644 index 00000000..a13fb48f --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tools/common/options.h @@ -0,0 +1,81 @@ +#ifndef CBC_OPTIONS_H +#define CBC_OPTIONS_H + +#define CLIOPTS_ENABLE_CXX 1 +#include +#include +#include +#include +#include +#include "contrib/cliopts/cliopts.h" + +#define CBC_CONFIG_FILENAME ".cbcrc" +#define CBC_WIN32_APPDIR "Couchbase CBC Utility" + +namespace cbc { + +#define X_OPTIONS(X) \ + X(String, host, "host", 'h') \ + X(String, bucket, "bucket", 'b') \ + X(String, passwd, "password", 'P') \ + X(String, user, "username", 'u') \ + X(String, transport, "bootstrap-protocol", 'C') \ + X(String, configcache, "config-cache", 'Z') \ + X(String, saslmech, "force-sasl-mech", 'S') \ + X(String, connstr, "spec", 'U') \ + X(String, ssl, "ssl", '\0') \ + X(String, certpath, "certpath", '\0') \ + X(UInt, timeout, "timeout", '\0') \ + X(Bool, timings, "timings", 'T') \ + X(Bool, verbose, "verbose", 'v') \ + X(Bool, dump, "dump", '\0') \ + X(List, cparams, "cparam", 'D') + + +class LcbError : public std::runtime_error { +private: + static std::string format_err(lcb_error_t err) { + std::stringstream ss; + ss << "libcouchbase error: " << lcb_strerror(NULL, err); + ss << " (0x" << std::hex << err << ")"; + return ss.str(); + } + +public: + lcb_error_t rc; + LcbError(lcb_error_t code) : std::runtime_error(format_err(code)) {} +}; + +class BadArg : public std::runtime_error { +public: + BadArg(std::string w) : std::runtime_error(w) {} +}; + +class ConnParams { +public: + ConnParams(); + void fillCropts(lcb_create_st&); + void addToParser(cliopts::Parser& parser); + lcb_error_t doCtls(lcb_t instance); + bool useTimings() { return o_timings.result(); } + void setAdminMode(); + bool shouldDump() { return o_dump.result(); } + void writeConfig(const std::string& dest = getConfigfileName()); + static std::string getConfigfileName(); + +private: + +#define X(tp, varname, longdesc, shortdesc) \ + cliopts::tp##Option o_##varname; + + X_OPTIONS(X) +#undef X + std::string connstr; + std::string passwd; + bool isAdmin; + bool loadFileDefaults(); +}; + +} + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/tools/docgen/docgen.h b/couchbase-sys/libcouchbase-2.7.0/tools/docgen/docgen.h new file mode 100644 index 00000000..e0d68e09 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tools/docgen/docgen.h @@ -0,0 +1,469 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2016 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "contrib/lcb-jsoncpp/lcb-jsoncpp.h" +#include "placeholders.h" +#include +#include + +namespace Pillowfight { + +#define JSON_VALUE_SIZE 16 + +/** + * Per-thread mutable state to generate the document. The populateIov() + * method is called to populate IOVs suitable for passing to the storage + * functions. + */ +class GeneratorState { +public: + /** + * Populate an IOV array + * @param seq The sequence number (used as a 'selector') + * @param[out] iov IOV containing pointers to buffers + * + * The buffers pointed to in the IOV array remain valid so long as + * the populateIov() function is not called again on this state + */ + virtual void populateIov(uint32_t seq, std::vector& iov) = 0; + virtual ~GeneratorState() {} +}; + +class SubdocGeneratorState { +public: + /** + * Populates subdocument command specifications + * @param seq the sequence number of the current command + * @param[in,out] specs container to hold the actual spec array. + * The spec array must have already been properly pre-sized. + */ + virtual void populateLookup(uint32_t seq, std::vector& specs) = 0; + virtual void populateMutate(uint32_t seq, std::vector& specs) = 0; + virtual ~SubdocGeneratorState() {} +}; + +class DocGeneratorBase { +public: + /** + * Create the per-thread state for generating documents + * @param total_gens Number of total generator threads + * @param cur_gen The index of the current generator thread + * @return An opaque state object. This should be deleted by the caller + */ + virtual GeneratorState *createState(int total_gens, int cur_gen) const = 0; + virtual SubdocGeneratorState *createSubdocState(int, int) const { return NULL; } + virtual ~DocGeneratorBase() {} +}; + +/** + * Generator class for raw objects. This contains a fixed buffer and will + * simply vary in how 'long' the buffer is + */ +class RawDocGenerator : public DocGeneratorBase { +public: + /** + * Generate graded sizes based on a min,max specification. This allows us + * to be more efficient by cutting corners on how 'random' the sizes + * actually are. Rather than generating a 'random' size each time we need + * a document, we split the range into a set of potential sizes (which are + * also evenly distributed) and cycle between them. + * + * @param minsz Smallest desired size + * @param maxsz Largest desired size + * @param granularity How many grades to produce + * @return A vector of sizes which fall between the range + */ + static std::vector + gen_graded_sizes(uint32_t minsz, uint32_t maxsz, int grades=10) { + std::vector ret; + + size_t diff = maxsz - minsz; + size_t factor = diff / grades; + if (factor == 0 || minsz == maxsz) { + ret.push_back(maxsz); + } else { + for (int ii = 0; ii < grades+1; ii++) { + size_t size = minsz + (factor * ii); + ret.push_back(size); + } + } + return ret; + } + + RawDocGenerator(uint32_t minsz, uint32_t maxsz) + : m_sizes(gen_graded_sizes(minsz, maxsz)) { + // Populate the buffer to its capacity + m_buf.insert(0, maxsz, '#'); + } + + + class MyState : public GeneratorState { + public: + MyState(const RawDocGenerator *parent) : m_parent(parent) { + } + + const RawDocGenerator *m_parent; + void populateIov(uint32_t seq, std::vector& iov_out) { + m_parent->populateIov(seq, iov_out); + } + }; + + GeneratorState * createState(int, int) const { + return new MyState(this); + } + +private: + void populateIov(uint32_t seq, std::vector& iov_out) const { + iov_out.resize(1); + size_t cursz = m_sizes[seq % m_sizes.size()]; + iov_out[0].iov_base = const_cast(m_buf.c_str()); + iov_out[0].iov_len = cursz; + } + std::string m_buf; + std::vector m_sizes; +}; + +/** + * This 'generator' ignores sizes and generates documents as they are received + * from premade buffers + */ +class PresetDocGenerator : public DocGeneratorBase { +public: + /** + * @param inputs List of fixed inputs to use + */ + PresetDocGenerator(const std::vector& inputs) : m_bufs(inputs) { + } + + class MyState : public GeneratorState { + public: + MyState(const PresetDocGenerator *parent) : m_parent(parent) { + } + + void populateIov(uint32_t seq, std::vector& iov_out) { + m_parent->populateIov(seq, iov_out); + } + + private: + const PresetDocGenerator *m_parent; + }; + + GeneratorState *createState(int, int) const { + return new MyState(this); + } + +protected: + PresetDocGenerator() {} + + void populateIov(uint32_t seq, std::vector& iov_out) const { + iov_out.resize(1); + const std::string& s = m_bufs[seq % m_bufs.size()]; + iov_out[0].iov_base = const_cast(s.c_str()); + iov_out[0].iov_len = s.size(); + } + std::vector m_bufs; +}; + +// This is the same as the normal document generator, except we generate +// the JSON first +class JsonDocGenerator : public PresetDocGenerator { +private: + struct Doc { + std::string m_doc; + class Field { + public: + Field(const std::string& n, std::string& v) : m_name(n), m_value(v) {} + const std::string& name() const { return m_name; } + const std::string& value() const { return m_value; } + private: + std::string m_name; + std::string m_value; + }; + std::vector m_fields; + }; + std::vector m_docs; + +public: + /** + * @param minsz Minimum JSON document size + * @param maxsz Maximum JSON document size + */ + JsonDocGenerator(uint32_t minsz, uint32_t maxsz) + { + genDocuments(minsz, maxsz, m_docs); + for (size_t ii = 0; ii < m_docs.size(); ++ii) { + m_bufs.push_back(m_docs[ii].m_doc); + } + } + + static void genDocuments(uint32_t minsz, uint32_t maxsz, std::vector& out) + { + std::vector docs; + genDocuments(minsz, maxsz, docs); + for (size_t ii = 0; ii < docs.size(); ++ii) { + out.push_back(docs[ii].m_doc); + } + } + +private: + /** + * Helper method to decrease the original size by a given amount. + * + * This also ensures the number never reaches below 0. + * + * @param[out] orig Pointer to original size + * @param toDecr number by which to decrement + */ + static void decrSize(int *orig, size_t toDecr) + { + *orig = std::max(0, *orig - static_cast(toDecr)); + } + + static void + genDocuments(uint32_t minsz, uint32_t maxsz, std::vector& out) + { + std::vector sizes = RawDocGenerator::gen_graded_sizes(minsz, maxsz); + for (std::vector::iterator ii = sizes.begin(); + ii != sizes.end(); ++ii) { + out.push_back(generate(*ii)); + } + } + + /** + * Generates a "JSON" document of a given size. In order to remain + * more or less in-tune with common document sizes, field names will be + * "Field_$incr" and values will be evenly distributed as fixed 16 byte + * strings. (See JSON_VALUE_SIZE) + */ + static Doc generate(int docsize) + { + int counter = 0; + char keybuf[128] = { 0 }; + Json::Value root(Json::objectValue); + Json::FastWriter writer; + Doc ret; + + while (docsize > 0) { + decrSize(&docsize, sprintf(keybuf, "Field_%d", ++counter) + 3); + size_t valsize = std::min(JSON_VALUE_SIZE, docsize); + if (!valsize) { + valsize = 1; + } + std::string value(valsize, '*'); + decrSize(&docsize, valsize + 3); + root[keybuf] = value; + value = '"' + value; + value += '"'; + ret.m_fields.push_back(Doc::Field(keybuf, value)); + } + ret.m_doc = writer.write(root); + return ret; + } + + class SDGenstate: public SubdocGeneratorState { + public: + SDGenstate(const std::vector& docs) : m_pathix(0), m_docs(docs) { + } + + void populateLookup(uint32_t seq, std::vector& specs) { + populate(seq, specs, false); + } + void populateMutate(uint32_t seq, std::vector& specs) { + populate(seq, specs, true); + } + + private: + void populate(uint32_t seq, std::vector& specs, bool mutate) { + const Doc& d = doc(seq); + specs.resize(std::min(d.m_fields.size(), specs.size())); + for (size_t ii = 0; ii < d.m_fields.size() && ii < specs.size(); ++ii) { + const Doc::Field& f = d.m_fields[m_pathix++ % d.m_fields.size()]; + lcb_SDSPEC& cur_spec = specs[ii]; + LCB_SDSPEC_SET_PATH(&cur_spec, f.name().c_str(), f.name().size()); + if (mutate) { + LCB_SDSPEC_SET_VALUE(&cur_spec, f.value().c_str(), f.value().size()); + specs[ii].sdcmd = LCB_SDCMD_DICT_UPSERT; + } else { + specs[ii].sdcmd = LCB_SDCMD_GET; + } + } + } + + const Doc& doc(uint32_t seq) const { return m_docs[seq % m_docs.size()]; } + size_t m_pathix; + const std::vector& m_docs; + }; +public: + virtual SubdocGeneratorState *createSubdocState(int, int) const { + return new SDGenstate(m_docs); + } +}; + +struct TemplateSpec { + std::string term; + unsigned minval; + unsigned maxval; + bool sequential; +}; + +/** + * Generate documents based on placeholder values. Each document (JSON or not) + * may have one or more special replacement texts which can be substituted + * with a random number + */ +class PlaceholderDocGenerator : public DocGeneratorBase { +public: + /** + * @param specs Placeholder specifications + * @param inputs Documents to use for replacements + */ + PlaceholderDocGenerator( + const std::vector& inputs, + const std::vector& specs) { + + initMatches(specs, inputs); + } + +protected: + PlaceholderDocGenerator() {} + + /** + * Really belongs in constructor, but is decoupled for subclasses + */ + void initMatches( + const std::vector& specs, + const std::vector& inputs) { + + using namespace Placeholders; + + for (std::vector::const_iterator ii = specs.begin(); + ii != specs.end(); ++ii) { + Placeholders::Spec cur(ii->term, ii->minval, ii->maxval, ii->sequential); + pl_specs.push_back(cur); + } + + for (std::vector::const_iterator ii = inputs.begin(); + ii != inputs.end(); ++ii) { + matches.push_back(new DocumentMatches(*ii, pl_specs)); + } + } + +public: + GeneratorState *createState(int total, int cur) const { + return new MyState(this, total, cur); + } + +private: + class MyState : public GeneratorState { + public: + MyState(const PlaceholderDocGenerator *parent, int total, int cur) + : m_parent(parent) { + using namespace Placeholders; + for (size_t ii = 0; ii < parent->matches.size(); ii++) { + m_substs.push_back( + Substitutions(parent->matches[ii], total, cur)); + } + m_bufs.resize(m_substs.size()); + } + + void populateIov(uint32_t seq, std::vector& iov_out) { + using namespace Placeholders; + size_t ix = seq % m_substs.size(); + Substitutions& subst = m_substs[ix]; + Substitutions::Backbuffer& buf = m_bufs[ix]; + subst.makeIovs(iov_out, buf); + } + + std::vector m_bufs; + std::vector m_substs; + const PlaceholderDocGenerator *m_parent; + }; + + // Match object for each doc + std::vector matches; + std::vector pl_specs; +}; + +/** + * Generate documents based on JSON fields. This adds on top of the normal + * document generator in that JSON paths are used and explicit placeholders + * are not required + */ +class PlaceholderJsonGenerator : public PlaceholderDocGenerator { +public: + /** + * @param specs List of specs. The term in the spec refers to the field + * which is to be replaced, not the placeholder text + * @param documents The documents to operate on + */ + PlaceholderJsonGenerator( + const std::vector& documents, + const std::vector& specs) { + + initJsonPlaceholders(specs, documents); + } + + /** + * Generate the documents, and then generate specs for them + * @param specs The specs to use + * @param minsz Minimum document size + * @param maxsz Maximum document size + */ + PlaceholderJsonGenerator(uint32_t minsz, uint32_t maxsz, + const std::vector& specs) { + + std::vector jsondocs; + JsonDocGenerator::genDocuments(minsz, maxsz, jsondocs); + initJsonPlaceholders(specs, jsondocs); + } + +private: + void initJsonPlaceholders( + const std::vector& specs, + const std::vector& documents) { + + int serial = 0; + Json::Reader reader; + Json::FastWriter writer; + std::vector new_specs; + std::vector new_docs; + + for (size_t ii = 0; ii < documents.size(); ii++) { + Json::Value root; + if (!reader.parse(documents[ii], root)) { + throw std::runtime_error("Couldn't parse one or more documents!"); + } + + for (size_t jj = 0; jj < specs.size(); ++jj) { + char buf[64]; + const TemplateSpec& spec = specs[jj]; + sprintf(buf, "$__pillowfight_%d", serial++); + root[spec.term] = buf; + TemplateSpec new_spec = spec; + + std::string replace_term(buf); + replace_term = '"' + replace_term + '"'; + new_spec.term = replace_term; + new_specs.push_back(new_spec); + } + new_docs.push_back(writer.write(root)); + } + initMatches(new_specs, new_docs); + } +}; + +} // namespace diff --git a/couchbase-sys/libcouchbase-2.7.0/tools/docgen/loc.h b/couchbase-sys/libcouchbase-2.7.0/tools/docgen/loc.h new file mode 100644 index 00000000..e30f7f52 --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tools/docgen/loc.h @@ -0,0 +1,210 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2016 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CBC_PILLOWFIGHT_LOC_H +#define CBC_PILLOWFIGHT_LOC_H + +namespace Pillowfight { + +// This class copy/pasted from Subdoc (which I also wrote) +class Loc { +public: + const char *at; + size_t length; + + Loc() : at(NULL), length(0) { + } + + Loc(const lcb_IOV& iov) : + at(reinterpret_cast(iov.iov_base)), length(iov.iov_len) { + } + + Loc(const char *s, size_t n) { + assign(s, n); + } + + lcb_IOV to_iov() const { + lcb_IOV ret; + ret.iov_base = const_cast(at); + ret.iov_len = length; + return ret; + } + + enum OverlapMode { + NO_OVERLAP = 0, + OVERLAP = 1 + }; + + void assign(const char *s, size_t n) { at = s; length = n; } + + /** + * Modifies the object so that it ends where `until` begins. + * + * The object will have a starting position of the base buffer, and will + * span until the `until` buffer. + * + * Example: + * @code + * BASE = ABCDEFGHIJ + * UNTIL = FGH + * THIS = ABCDE + * @endcode + * + * @param base The common buffer + * @param until position at where this buffer should end + * @param overlap Whether the end should overlap with the first byte of `until` + */ + void end_at_begin(const Loc& base, const Loc& until, OverlapMode overlap) { + at = base.at; + length = until.at - base.at; + if (overlap == OVERLAP) { + length++; + } + } + + /** + * Modifies the object so that it begins where `until` ends. + * + * The buffer will have an end position matching the end of the base buffer, + * and will end where `from` ends. + * + * Example: + * @code + * BASE = ABCDEFGHIJ + * FROM = CDE + * THIS = FGHIJ + * @endcode + * + * @param base The common buffer + * @param from A buffer whose end should be used as the beginning of the + * current buffer + * @param overlap Whether the current buffer should overlap `until`'s last + * byte + */ + void begin_at_end(const Loc& base, const Loc& from, OverlapMode overlap) { + at = from.at + from.length; + length = base.length - (at - base.at); + if (overlap == OVERLAP) { + at--; + length++; + } + } + + /** + * Modifies the object so that it begins where `from` begins. + * + * The buffer will have an end position matching the end of the base buffer + * and will begin where `from` begins + * + * Example: + * @code + * BASE = ABCDEFGHIJ + * FROM = DEF + * THIS = DEFGHIJ + * @endcode + * + * @param base Common buffer + * @param from The begin position + */ + void begin_at_begin(const Loc& base, const Loc& from) { + at = from.at; + length = base.length - (from.at - base.at); + } + + /** + * Modifies the object so that it ends where `until` ends. + * + * The buffer will have a start position of `base` and an end position of + * `until. + * + * Example + * @code + * BASE = ABCDEFGHIJ + * UNTIL = EFG + * THIS = ABCDEFG + * @endcode + * + * @param base + * @param until + * @param overlap + */ + void end_at_end(const Loc& base, const Loc& until, OverlapMode overlap) { + at = base.at; + length = (until.at + until.length) - base.at; + if (overlap == NO_OVERLAP) { + length--; + } + } + + bool empty() const { return length == 0; } + + std::string to_string() const { + if (!empty()) { + return std::string(at, length); + } else { + return std::string(); + } + } + + // Move buffer start ahead n bytes + void ltrim(size_t n) { + at += n; + length -= n; + } + + // Move buffer end back n bytes + void rtrim(size_t n) { + length -= n; + } + + // Added for pillowfight + // + // Set buffer to end where 'loc' begins, while not touching the beginning + // of the buffer + void rtrim_to(const Loc& loc) { + assert(loc.at > at); + size_t diff = loc.at - at; + length = diff; + } + + // Added for pillowfight + bool contains(const Loc& sub) const { + return sub.at >= at && // Begins at or after our beginning + sub.at < at + length && // begins before or at the end + sub.at + sub.length <= at + length; + } + + static void + dumpIovs(const std::vector& vecs) { + for (size_t ii = 0; ii < vecs.size(); ii++) { + const lcb_IOV& iov = vecs[ii]; + printf("IOV[%lu]. Buf=%p. Len=%lu. Content=%.*s\n", + ii, iov.iov_base, iov.iov_len, + (int)iov.iov_len, (const char*)iov.iov_base); + } + } + static void + dumpIovs(const std::vector& vecs) { + for (size_t ii = 0; ii < vecs.size(); ii++) { + const Loc& loc = vecs[ii]; + std::string s = loc.to_string(); + printf("Loc[%lu]. Buf=%p. Len=%lu. Content=%s\n", + ii, loc.at, loc.length, s.c_str()); + } + } +}; +} +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/tools/docgen/placeholders.h b/couchbase-sys/libcouchbase-2.7.0/tools/docgen/placeholders.h new file mode 100644 index 00000000..767ef42c --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tools/docgen/placeholders.h @@ -0,0 +1,211 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2015 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CBC_PILLOWFIGHT_DOCGEN_H +#define CBC_PILLOWFIGHT_DOCGEN_H + +#include +#include +#include +#include "seqgen.h" +#include "loc.h" + +namespace Pillowfight { +namespace Placeholders { + +/** + * Placeholder specification. + * This contains information about a single placeholder's input specification. + */ +class Spec { +public: + /** + * @param s placeholder string + * @param minval_ minimum value for replacement + * @param maxval_ maxmimum value for replacement + * @param sequential_ if replacement should be done sequentially + */ + Spec(const std::string &s, unsigned minval, unsigned maxval, unsigned sequential) + : m_term(s), m_minval(minval), m_maxval(maxval), m_sequential(sequential) { + } + + SeqGenerator *createSeqgen(int total, int cur) const { + if (m_sequential) { + return new SeqGenerator(m_minval, m_maxval, total, cur); + } else { + return new SeqGenerator(m_minval, m_maxval); + } + } + + const std::string& term() const { return m_term; } + +private: + std::string m_term; // Placeholder string to search for + unsigned m_minval; + unsigned m_maxval; + bool m_sequential; +}; + +/** + * Mapping of a placeholder specification as it relates to a given template + * document. + * + * This class can be copied since we don't have pointer references within the + * match itself. + */ +class Match { +public: + // Size of the placeholder (derived from spec) + size_t size() const { return m_placeholder->term().size(); } + size_t offset() const { return m_offset; } + const Spec& spec() const { return *m_placeholder; } + + template static void + find(const std::string& base, const Tin& specs, Tres& results) { + + for (size_t ii = 0; ii < specs.size(); ii++) { + const Spec& pl = specs[ii]; + size_t findpos = base.find(pl.term()); + if (findpos != std::string::npos) { + results.push_back(Match(&pl, findpos)); + } + } + std::sort(results.begin(), results.end(), compare); + } + +private: + // Actual placeholder containing the information + const Spec *m_placeholder; + + // Offset into the document in which the placeholder text begins + size_t m_offset; + Match(const Spec *spec_, size_t off) + : m_placeholder(spec_), m_offset(off) { + } + + static bool compare(const Match& a, const Match& b) { + return a.m_offset < b.m_offset; + } +}; + +/** + * A document with its relevant placeholders. This contains the (constant) + * document string along with its placeholder information. + * + * The document is split into fragments, where some fragments are constant + * and some are empty and have DocPlaceholder objects mapped to them. + */ +class DocumentMatches { +public: + DocumentMatches(const std::string& original, + const std::vector& placeholders) : m_base(original) { + + Match::find(m_base, placeholders, m_matches); + + const Loc baseloc(m_base.c_str(), m_base.size()); + m_fragments.push_back(baseloc); + + for (size_t ii = 0; ii < m_matches.size(); ii++) { + Match& dph = m_matches[ii]; + + // Location of text to cut out + Loc dph_loc(m_base.c_str() + dph.offset(), dph.size()); + + // Make the last fragment end at the beginning of the current + // placeholder + m_fragments.back().rtrim_to(dph_loc); + + // Add the replacement index (current index) + m_matchix_to_fragix.push_back(m_fragments.size()); + + // Add the empty fragment as a Loc to represent the placeholder + m_fragments.push_back(Loc()); + + // Make the next segment contain the rest of the document. + // If there are more placeholders, then this fragment is truncated + Loc next_seg; + next_seg.begin_at_end(baseloc, dph_loc, Loc::NO_OVERLAP); + m_fragments.push_back(next_seg); + } + } + + const std::vector& matches() const { return m_matches; } + +private: + DocumentMatches(const DocumentMatches& other); + + friend class Substitutions; + std::string m_base; // Base document text + std::vector m_fragments; // Fragments of the document + std::vector m_matches; + std::vector m_matchix_to_fragix; // Mapping of matches to IOV indexes +}; + +class Substitutions { +public: + // Backing buffer for data + typedef std::vector Backbuffer; + + Substitutions(const DocumentMatches *matches, int total, int cur) + : m_matches(matches) { + for (size_t ii = 0; ii < m_matches->m_matches.size(); ii++) { + const Match& m = m_matches->m_matches[ii]; + SeqGenerator *gen = m.spec().createSeqgen(total, cur); + m_generators.resize(ii + 1); + m_generators[ii] = gen; + } + for (size_t ii = 0; ii < m_matches->m_fragments.size(); ii++) { + m_iovs.push_back(m_matches->m_fragments[ii].to_iov()); + } + } + + /** + * Create the IOVs necessary for assembling the document + * @param[out] iovs Vector to contain the output IOVs + * @param[out] backbuf backing buffers for substitutions + */ + void makeIovs(std::vector& iovs, Backbuffer& backbuf) { + iovs = m_iovs; + backbuf.resize(m_matches->matches().size()); + + for (size_t ii = 0; ii < m_matches->matches().size(); ii++) { + char buf[64]; + std::string& output_str = backbuf[ii]; + lcb_IOV& output_iov = iovs[m_matches->m_matchix_to_fragix[ii]]; + SeqGenerator *gen = m_generators[ii]; + + // Get the number + uint32_t cur = gen->next(); + + sprintf(buf, "%u", cur); + output_str.assign(buf); + output_iov.iov_base = const_cast(output_str.c_str()); + output_iov.iov_len = output_str.size(); + } + } + +private: + const DocumentMatches *m_matches; + // Array of generators, one for each Match index + std::vector m_generators; + std::vector m_iovs; +}; + +} // namespace +} // namespace + +#endif diff --git a/couchbase-sys/libcouchbase-2.7.0/tools/docgen/seqgen.h b/couchbase-sys/libcouchbase-2.7.0/tools/docgen/seqgen.h new file mode 100644 index 00000000..da58c0ea --- /dev/null +++ b/couchbase-sys/libcouchbase-2.7.0/tools/docgen/seqgen.h @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2016 Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CBC_PILLOWFIGHT_SEQGEN_H +#define CBC_PILLOWFIGHT_SEQGEN_H + +namespace Pillowfight { + +/** + * Stateful sequence generator, divides sequences based on number of threads. + * + * On input, it takes a total range amount as well as a number of threads + * to serve the range, and finally, the index of the current thread. + * + * There is one generator per thread, as it is stateful + */ +class SeqGenerator { +public: + /** Constructor for sequential key generators */ + SeqGenerator(uint32_t start, uint32_t end, int num_workers, int cur_worker) { + offset = start; + uint32_t total = end - start; + total_self = total / num_workers; + offset += total_self * cur_worker; + rnum = 0; + sequential = true; + curr_seqno = 0; + } + + /** Initialize as a random range */ + SeqGenerator(uint32_t start, uint32_t end) { + total_self = end - start; + offset = start; + rnum = 0; + curr_seqno = 0; + sequential = false; + + for (int ii = 0; ii < 8192; ii++) { + seqpool.push_back(rand()); + } + } + + /** + * Get the next sequence in range + * @return A number appropriate for the current sequence + */ + uint32_t next() { + if (sequential) { + rnum++; + rnum %= total_self; + rnum += offset; + return rnum; + + } else { + rnum += seqpool[curr_seqno]; + curr_seqno++; + if (curr_seqno >= seqpool.size()) { + curr_seqno = 0; + } + uint32_t seq = rnum; + seq %= total_self; + seq += offset; + return seq; + } + } + + uint32_t maxItems() const { + return total_self; + } + +private: + bool sequential; + std::vector seqpool; + uint32_t rnum; // internal iterator + uint32_t offset; // beginning numerical offset + uint32_t total_self; // maximum value of iterator + uint32_t curr_seqno; +}; +} +#endif diff --git a/couchbase-sys/src/lib.rs b/couchbase-sys/src/lib.rs new file mode 100644 index 00000000..30bbe3c2 --- /dev/null +++ b/couchbase-sys/src/lib.rs @@ -0,0 +1,6 @@ +#![allow(non_camel_case_types)] +#![allow(non_upper_case_globals)] +#![allow(non_snake_case)] +#![allow(improper_ctypes)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/couchbase/Cargo.toml b/couchbase/Cargo.toml new file mode 100644 index 00000000..4f9b2271 --- /dev/null +++ b/couchbase/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "couchbase" +version = "0.1.0" +authors = ["Michael Nitschinger "] + +[dependencies] +couchbase-sys = { version = "0.1.0", path = "../couchbase-sys" } diff --git a/couchbase/src/lib.rs b/couchbase/src/lib.rs new file mode 100644 index 00000000..569bab66 --- /dev/null +++ b/couchbase/src/lib.rs @@ -0,0 +1 @@ +extern crate couchbase_sys;