diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/cmd_formatter.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/cmd_formatter.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/cmd_formatter.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/cmd_formatter.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/command.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/command.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/command.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/command.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/command_args.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/command_args.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/command_args.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/command_args.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/command_options.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/command_options.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/command_options.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/command_options.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/connection.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/connection.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/connection.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/connection.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/connection_pool.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/connection_pool.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/connection_pool.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/connection_pool.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/cxx_utils.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/cxx_utils.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/cxx_utils.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/cxx_utils.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/errors.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/errors.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/errors.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/errors.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/pipeline.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/pipeline.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/pipeline.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/pipeline.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/queued_redis.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/queued_redis.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/queued_redis.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/queued_redis.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/queued_redis.hpp b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/queued_redis.hpp similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/queued_redis.hpp rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/queued_redis.hpp diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis++.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/redis++.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis++.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/redis++.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/redis.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/redis.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis.hpp b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/redis.hpp similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis.hpp rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/redis.hpp diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis_cluster.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/redis_cluster.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis_cluster.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/redis_cluster.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis_cluster.hpp b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/redis_cluster.hpp similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/redis_cluster.hpp rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/redis_cluster.hpp diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/reply.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/reply.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/reply.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/reply.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/sentinel.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/sentinel.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/sentinel.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/sentinel.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/shards.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/shards.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/shards.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/shards.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/shards_pool.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/shards_pool.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/shards_pool.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/shards_pool.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/subscriber.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/subscriber.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/subscriber.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/subscriber.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/tls.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/tls.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/tls.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/tls.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/transaction.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/transaction.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/transaction.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/transaction.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/utils.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/utils.h similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/include/sw/redis++/utils.h rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/include/sw/redis++/utils.h diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/lib/pkgconfig/redis++.pc b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/lib/pkgconfig/redis++.pc similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/lib/pkgconfig/redis++.pc rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/lib/pkgconfig/redis++.pc diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-config-version.cmake b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/share/cmake/redis++/redis++-config-version.cmake similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-config-version.cmake rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/share/cmake/redis++/redis++-config-version.cmake diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-config.cmake b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/share/cmake/redis++/redis++-config.cmake similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-config.cmake rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/share/cmake/redis++/redis++-config.cmake diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-targets-release.cmake b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/share/cmake/redis++/redis++-targets-release.cmake similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-targets-release.cmake rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/share/cmake/redis++/redis++-targets-release.cmake diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-targets.cmake b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/share/cmake/redis++/redis++-targets.cmake similarity index 100% rename from ext/redis-plus-plus-1.3.3/install/ubuntu22.04/share/cmake/redis++/redis++-targets.cmake rename to ext/redis-plus-plus-1.3.3/install/ubuntu22.04/amd64/share/cmake/redis++/redis++-targets.cmake diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/cmd_formatter.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/cmd_formatter.h new file mode 100644 index 000000000..85efdf347 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/cmd_formatter.h @@ -0,0 +1,775 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + 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 SEWENEW_REDISPLUSPLUS_CMD_FORMATTER_H +#define SEWENEW_REDISPLUSPLUS_CMD_FORMATTER_H + +#include +#include "command_options.h" +#include "command_args.h" +#include "command.h" +#include "errors.h" + +namespace sw { + +namespace redis { + +class FormattedCommand { +public: + FormattedCommand(char *data, int len) : _data(data), _size(len) { + if (data == nullptr || len < 0) { + throw Error("failed to format command"); + } + } + + FormattedCommand(const FormattedCommand &) = delete; + FormattedCommand& operator=(const FormattedCommand &) = delete; + + FormattedCommand(FormattedCommand &&that) noexcept { + _move(std::move(that)); + } + + FormattedCommand& operator=(FormattedCommand &&that) noexcept { + if (this != &that) { + _move(std::move(that)); + } + + return *this; + } + + ~FormattedCommand() noexcept { + if (_data != nullptr) { + redisFreeCommand(_data); + } + } + + const char* data() const noexcept { + return _data; + } + + int size() const noexcept { + return _size; + } + +private: + void _move(FormattedCommand &&that) noexcept { + _data = that._data; + _size = that._size; + that._data = nullptr; + that._size = 0; + } + + char *_data = nullptr; + int _size = 0; +}; + +namespace fmt { + +template +FormattedCommand format_cmd(const char *format, Args &&...args) { + char *data = nullptr; + auto len = redisFormatCommand(&data, format, std::forward(args)...); + + return FormattedCommand(data, len); +} + +inline FormattedCommand format_cmd(int argc, const char **argv, const std::size_t *argv_len) { + char *data = nullptr; + auto len = redisFormatCommandArgv(&data, argc, argv, argv_len); + + return FormattedCommand(data, len); +} + +inline FormattedCommand format_cmd(CmdArgs &args) { + char *data = nullptr; + auto len = redisFormatCommandArgv(&data, args.size(), args.argv(), args.argv_len()); + + return FormattedCommand(data, len); +} + +struct SetResultParser { + bool operator()(redisReply &reply) const { + sw::redis::reply::rewrite_set_reply(reply); + return sw::redis::reply::parse(reply); + } +}; + +// CONNECTION commands. + +inline FormattedCommand echo(const StringView &msg) { + return format_cmd("ECHO %b", msg.data(), msg.size()); +} + +inline FormattedCommand ping() { + return format_cmd("PING"); +} + +inline FormattedCommand ping(const StringView &msg) { + return format_cmd("PING %b", msg.data(), msg.size()); +} + +inline FormattedCommand del(const StringView &key) { + return format_cmd("DEL %b", key.data(), key.size()); +} + +template +FormattedCommand del_range(Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "DEL" << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand exists(const StringView &key) { + return format_cmd("EXISTS %b", key.data(), key.size()); +} + +template +FormattedCommand exists_range(Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "EXISTS" << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand expire(const StringView &key, const std::chrono::seconds &timeout) { + return format_cmd("EXPIRE %b %lld", key.data(), key.size(), timeout.count()); +} + +inline FormattedCommand expireat(const StringView &key, + const std::chrono::time_point &tp) { + return format_cmd("EXPIREAT %b %lld", key.data(), key.size(), tp.time_since_epoch().count()); +} + +inline FormattedCommand pexpire(const StringView &key, + const std::chrono::milliseconds &timeout) { + return format_cmd("PEXPIRE %b %lld", key.data(), key.size(), timeout.count()); +} + +inline FormattedCommand pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return format_cmd("PEXPIREAT %b %lld", key.data(), key.size(), tp.time_since_epoch().count()); +} + +inline FormattedCommand pttl(const StringView &key) { + return format_cmd("PTTL %b", key.data(), key.size()); +} + +inline FormattedCommand rename(const StringView &key, const StringView &newkey) { + return format_cmd("RENAME %b %b", key.data(), key.size(), newkey.data(), newkey.size()); +} + +inline FormattedCommand renamenx(const StringView &key, const StringView &newkey) { + return format_cmd("RENAMENX %b %b", key.data(), key.size(), newkey.data(), newkey.size()); +} + +inline FormattedCommand ttl(const StringView &key) { + return format_cmd("TTL %b", key.data(), key.size()); +} + +inline FormattedCommand unlink(const StringView &key) { + return format_cmd("UNLINK %b", key.data(), key.size()); +} + +template +FormattedCommand unlink_range(Input first, Input last) { + CmdArgs args; + args << "UNLINK" << std::make_pair(first, last); + + return format_cmd(args); +} + +// STRING commands. + +inline FormattedCommand get(const StringView &key) { + return format_cmd("GET %b", key.data(), key.size()); +} + +inline FormattedCommand incr(const StringView &key) { + return format_cmd("INCR %b", key.data(), key.size()); +} + +inline FormattedCommand incrby(const StringView &key, long long increment) { + return format_cmd("INCRBY %b %lld", key.data(), key.size(), increment); +} + +inline FormattedCommand incrbyfloat(const StringView &key, double increment) { + return format_cmd("INCRBYFLOAT %b %f", key.data(), key.size(), increment); +} + +template +FormattedCommand mget(Input first, Input last) { + CmdArgs args; + args << "MGET" << std::make_pair(first, last); + + return format_cmd(args); +} + +template +FormattedCommand mset(Input first, Input last) { + CmdArgs args; + args << "MSET" << std::make_pair(first, last); + + return format_cmd(args); +} + +template +FormattedCommand msetnx(Input first, Input last) { + CmdArgs args; + args << "MSETNX" << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl, + UpdateType type) { + CmdArgs args; + args << "SET" << key << val; + + if (ttl > std::chrono::milliseconds(0)) { + args << "PX" << ttl.count(); + } + + cmd::detail::set_update_type(args, type); + + return format_cmd(args); +} + +inline FormattedCommand strlen(const StringView &key) { + return format_cmd("STRLEN %b", key.data(), key.size()); +} + +inline FormattedCommand blpop(const StringView &key, const std::chrono::seconds &timeout) { + return format_cmd("BLPOP %b %lld", key.data(), key.size(), timeout.count()); +} + +template +FormattedCommand blpop_range(Input first, Input last, const std::chrono::seconds &timeout) { + assert(first != last); + + CmdArgs args; + args << "BLPOP" << std::make_pair(first, last) << timeout.count(); + + return format_cmd(args); +} + +inline FormattedCommand brpop(const StringView &key, const std::chrono::seconds &timeout) { + return format_cmd("BRPOP %b %lld", key.data(), key.size(), timeout.count()); +} + + +template +FormattedCommand brpop_range(Input first, Input last, const std::chrono::seconds &timeout) { + assert(first != last); + + CmdArgs args; + args << "BRPOP" << std::make_pair(first, last) << timeout.count(); + + return format_cmd(args); +} + +inline FormattedCommand brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout) { + return format_cmd("BRPOPLPUSH %b %b %lld", + source.data(), source.size(), + destination.data(), destination.size(), + timeout.count()); +} + +inline FormattedCommand llen(const StringView &key) { + return format_cmd("LLEN %b", key.data(), key.size()); +} + +inline FormattedCommand lpop(const StringView &key) { + return format_cmd("LPOP %b", key.data(), key.size()); +} + +inline FormattedCommand lpush(const StringView &key, const StringView &val) { + return format_cmd("LPUSH %b %b", key.data(), key.size(), val.data(), val.size()); +} + +template +FormattedCommand lpush_range(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "LPUSH" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand lrange(const StringView &key, long long start, long long stop) { + return format_cmd("LRANGE %b %lld %lld", key.data(), key.size(), start, stop); +} + +inline FormattedCommand lrem(const StringView &key, long long count, const StringView &val) { + return format_cmd("LREM %b %lld %b", key.data(), key.size(), count, val.data(), val.size()); +} + +inline FormattedCommand ltrim(const StringView &key, long long start, long long stop) { + return format_cmd("LTRIM %b %lld %lld", key.data(), key.size(), start, stop); +} + +inline FormattedCommand rpop(const StringView &key) { + return format_cmd("RPOP %b", key.data(), key.size()); +} + +inline FormattedCommand rpoplpush(const StringView &source, const StringView &destination) { + return format_cmd("RPOPLPUSH %b %b", + source.data(), source.size(), + destination.data(), destination.size()); +} + +inline FormattedCommand rpush(const StringView &key, const StringView &val) { + return format_cmd("RPUSH %b %b", key.data(), key.size(), val.data(), val.size()); +} + +template +FormattedCommand rpush_range(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "RPUSH" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +// HASH commands. + +inline FormattedCommand hdel(const StringView &key, const StringView &field) { + return format_cmd("HDEL %b %b", key.data(), key.size(), field.data(), field.size()); +} + +template +FormattedCommand hdel_range(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "HDEL" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand hexists(const StringView &key, const StringView &field) { + return format_cmd("HEXISTS %b %b", key.data(), key.size(), field.data(), field.size()); +} + +inline FormattedCommand hget(const StringView &key, const StringView &field) { + return format_cmd("HGET %b %b", key.data(), key.size(), field.data(), field.size()); +} + +inline FormattedCommand hgetall(const StringView &key) { + return format_cmd("HGETALL %b", key.data(), key.size()); +} + +inline FormattedCommand hincrby(const StringView &key, + const StringView &field, + long long increment) { + return format_cmd("HINCRBY %b %b %lld", + key.data(), key.size(), + field.data(), field.size(), + increment); +} + +inline FormattedCommand hincrbyfloat(const StringView &key, + const StringView &field, + double increment) { + return format_cmd("HINCRBYFLOAT %b %b %f", + key.data(), key.size(), + field.data(), field.size(), + increment); +} + +inline FormattedCommand hkeys(const StringView &key) { + return format_cmd("HKEYS %b", key.data(), key.size()); +} + +inline FormattedCommand hlen(const StringView &key) { + return format_cmd("HLEN %b", key.data(), key.size()); +} + +template +FormattedCommand hmget(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "HMGET" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +template +FormattedCommand hmset(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "HMSET" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand hset(const StringView &key, + const StringView &field, + const StringView &val) { + return format_cmd("HSET %b %b %b", + key.data(), key.size(), + field.data(), field.size(), + val.data(), val.size()); +} + +template +auto hset_range(const StringView &key, + Input first, + Input last) + -> typename std::enable_if::value, + FormattedCommand>::type { + assert(first != last); + + CmdArgs args; + args << "HSET" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand hvals(const StringView &key) { + return format_cmd("HVALS %b", key.data(), key.size()); +} + +// SET commands. + +inline FormattedCommand sadd(const StringView &key, const StringView &member) { + return format_cmd("SADD %b %b", key.data(), key.size(), member.data(), member.size()); +} + +template +FormattedCommand sadd_range(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SADD" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +inline FormattedCommand scard(const StringView &key) { + return format_cmd("SCARD %b", key.data(), key.size()); +} + +inline FormattedCommand sismember(const StringView &key, const StringView &member) { + return format_cmd("SISMEMBER %b %b", key.data(), key.size(), member.data(), member.size()); +} + +inline FormattedCommand smembers(const StringView &key) { + return format_cmd("SMEMBERS %b", key.data(), key.size()); +} + +inline FormattedCommand spop(const StringView &key) { + return format_cmd("SPOP %b", key.data(), key.size()); +} + +inline FormattedCommand spop(const StringView &key, long long count) { + return format_cmd("SPOP %b %lld", key.data(), key.size(), count); +} + +inline FormattedCommand srem(const StringView &key, const StringView &member) { + return format_cmd("SREM %b %b", key.data(), key.size(), member.data(), member.size()); +} + +template +FormattedCommand srem_range(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SREM" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +// SORTED SET commands. + +inline FormattedCommand bzpopmax(const StringView &key, const std::chrono::seconds &timeout) { + return format_cmd("BZPOPMAX %b %lld", key.data(), key.size(), timeout.count()); +} + +template +FormattedCommand bzpopmax_range(Input first, + Input last, + const std::chrono::seconds &timeout) { + assert(first != last); + + CmdArgs args; + args << "BZPOPMAX" << std::make_pair(first, last) << timeout.count(); + + return format_cmd(args); +} + +inline FormattedCommand bzpopmin(const StringView &key, const std::chrono::seconds &timeout) { + return format_cmd("BZPOPMIN %b %lld", key.data(), key.size(), timeout.count()); +} + +template +FormattedCommand bzpopmin_range(Input first, Input last, const std::chrono::seconds &timeout) { + assert(first != last); + + CmdArgs args; + args << "BZPOPMIN" << std::make_pair(first, last) << timeout.count(); + + return format_cmd(args); +} + +inline FormattedCommand zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type, + bool changed) { + CmdArgs args; + args << "ZADD" << key; + + cmd::detail::set_update_type(args, type); + + if (changed) { + args << "CH"; + } + + args << score << member; + + return format_cmd(args); +} + +template +FormattedCommand zadd_range(const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed) { + CmdArgs args; + args << "ZADD" << key; + + cmd::detail::set_update_type(args, type); + + if (changed) { + args << "CH"; + } + + while (first != last) { + // Swap the pair to pair. + args << first->second << first->first; + ++first; + } + + return format_cmd(args); +} + +inline FormattedCommand zcard(const StringView &key) { + return format_cmd("ZCARD %b", key.data(), key.size()); +} + +template +FormattedCommand zcount(const StringView &key, const Interval &interval) { + return format_cmd("ZCOUNT %b %s %s", + key.data(), key.size(), + interval.min().c_str(), + interval.max().c_str()); +} + +inline FormattedCommand zincrby(const StringView &key, + double increment, + const StringView &member) { + return format_cmd("ZINCRBY %b %f %b", + key.data(), key.size(), + increment, + member.data(), member.size()); +} + +template +FormattedCommand zlexcount(const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + return format_cmd("ZLEXCOUNT %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline FormattedCommand zpopmax(const StringView &key) { + return format_cmd("ZPOPMAX %b", key.data(), key.size()); +} + +inline FormattedCommand zpopmax(const StringView &key, long long count) { + return format_cmd("ZPOPMAX %b %lld", key.data(), key.size(), count); +} + +inline FormattedCommand zpopmin(const StringView &key) { + return format_cmd("ZPOPMIN %b", key.data(), key.size()); +} + +inline FormattedCommand zpopmin_count(const StringView &key, long long count) { + return format_cmd("ZPOPMIN %b %lld", key.data(), key.size(), count); +} + +inline FormattedCommand zrange(const StringView &key, long long start, long long stop) { + return format_cmd("ZRANGE %b %lld %lld", key.data(), key.size(), start, stop); +} + +template +FormattedCommand zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + return format_cmd("ZRANGEBYLEX %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); +} + +template +FormattedCommand zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + return format_cmd("ZRANGEBYSCORE %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); +} + +inline FormattedCommand zrank(const StringView &key, const StringView &member) { + return format_cmd("ZRANK %b %b", key.data(), key.size(), member.data(), member.size()); +} + +inline FormattedCommand zrem(const StringView &key, const StringView &member) { + return format_cmd("ZREM %b %b", key.data(), key.size(), member.data(), member.size()); +} + +template +FormattedCommand zrem_range(const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "ZREM" << key << std::make_pair(first, last); + + return format_cmd(args); +} + +template +FormattedCommand zremrangebylex(const StringView &key, const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + return format_cmd("ZREMRANGEBYLEX %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline FormattedCommand zremrangebyrank(const StringView &key, long long start, long long stop) { + return format_cmd("ZREMRANGEBYRANK %b %lld %lld", key.data(), key.size(), start, stop); +} + +template +FormattedCommand zremrangebyscore(const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + return format_cmd("ZREMRANGEBYSCORE %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +template +FormattedCommand zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + return format_cmd("ZREVRANGEBYLEX %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + max.data(), max.size(), + min.data(), min.size(), + opts.offset, + opts.count); +} + +inline FormattedCommand zrevrank(const StringView &key, const StringView &member) { + return format_cmd("ZREVRANK %b %b", key.data(), key.size(), member.data(), member.size()); +} + +inline FormattedCommand zscore(const StringView &key, const StringView &member) { + return format_cmd("ZSCORE %b %b", key.data(), key.size(), member.data(), member.size()); +} + +// SCRIPTING commands. +template +FormattedCommand eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + CmdArgs args; + auto keys_num = std::distance(keys_first, keys_last); + + args << "EVAL" << script << keys_num + << std::make_pair(keys_first, keys_last) + << std::make_pair(args_first, args_last); + + return format_cmd(args); +} + +template +FormattedCommand evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + CmdArgs args; + auto keys_num = std::distance(keys_first, keys_last); + + args << "EVALSHA" << script << keys_num + << std::make_pair(keys_first, keys_last) + << std::make_pair(args_first, args_last); + + return format_cmd(args); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_CMD_FORMATTER_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/command.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/command.h new file mode 100644 index 000000000..81857dd9b --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/command.h @@ -0,0 +1,2271 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_COMMAND_H +#define SEWENEW_REDISPLUSPLUS_COMMAND_H + +#include +#include +#include +#include +#include "connection.h" +#include "command_options.h" +#include "command_args.h" +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace cmd { + +// CONNECTION command. +inline void auth(Connection &connection, const StringView &password) { + connection.send("AUTH %b", password.data(), password.size()); +} + +inline void auth(Connection &connection, const StringView &user, const StringView &password) { + connection.send("AUTH %b %b", + user.data(), user.size(), + password.data(), password.size()); +} + +inline void echo(Connection &connection, const StringView &msg) { + connection.send("ECHO %b", msg.data(), msg.size()); +} + +inline void ping(Connection &connection) { + connection.send("PING"); +} + +inline void quit(Connection &connection) { + connection.send("QUIT"); +} + +inline void ping(Connection &connection, const StringView &msg) { + // If *msg* is empty, Redis returns am empty reply of REDIS_REPLY_STRING type. + connection.send("PING %b", msg.data(), msg.size()); +} + +inline void select(Connection &connection, long long idx) { + connection.send("SELECT %lld", idx); +} + +inline void swapdb(Connection &connection, long long idx1, long long idx2) { + connection.send("SWAPDB %lld %lld", idx1, idx2); +} + +// SERVER commands. + +inline void bgrewriteaof(Connection &connection) { + connection.send("BGREWRITEAOF"); +} + +inline void bgsave(Connection &connection) { + connection.send("BGSAVE"); +} + +inline void dbsize(Connection &connection) { + connection.send("DBSIZE"); +} + +inline void flushall(Connection &connection, bool async) { + if (async) { + connection.send("FLUSHALL ASYNC"); + } else { + connection.send("FLUSHALL"); + } +} + +inline void flushdb(Connection &connection, bool async) { + if (async) { + connection.send("FLUSHDB ASYNC"); + } else { + connection.send("FLUSHDB"); + } +} + +inline void info(Connection &connection) { + connection.send("INFO"); +} + +inline void info(Connection &connection, const StringView §ion) { + connection.send("INFO %b", section.data(), section.size()); +} + +inline void lastsave(Connection &connection) { + connection.send("LASTSAVE"); +} + +inline void save(Connection &connection) { + connection.send("SAVE"); +} + +// KEY commands. + +inline void del(Connection &connection, const StringView &key) { + connection.send("DEL %b", key.data(), key.size()); +} + +template +inline void del_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "DEL" << std::make_pair(first, last); + + connection.send(args); +} + +inline void dump(Connection &connection, const StringView &key) { + connection.send("DUMP %b", key.data(), key.size()); +} + +inline void exists(Connection &connection, const StringView &key) { + connection.send("EXISTS %b", key.data(), key.size()); +} + +template +inline void exists_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "EXISTS" << std::make_pair(first, last); + + connection.send(args); +} + +inline void expire(Connection &connection, + const StringView &key, + long long timeout) { + connection.send("EXPIRE %b %lld", + key.data(), key.size(), + timeout); +} + +inline void expireat(Connection &connection, + const StringView &key, + long long timestamp) { + connection.send("EXPIREAT %b %lld", + key.data(), key.size(), + timestamp); +} + +inline void keys(Connection &connection, const StringView &pattern) { + connection.send("KEYS %b", pattern.data(), pattern.size()); +} + +inline void move(Connection &connection, const StringView &key, long long db) { + connection.send("MOVE %b %lld", + key.data(), key.size(), + db); +} + +inline void persist(Connection &connection, const StringView &key) { + connection.send("PERSIST %b", key.data(), key.size()); +} + +inline void pexpire(Connection &connection, + const StringView &key, + long long timeout) { + connection.send("PEXPIRE %b %lld", + key.data(), key.size(), + timeout); +} + +inline void pexpireat(Connection &connection, + const StringView &key, + long long timestamp) { + connection.send("PEXPIREAT %b %lld", + key.data(), key.size(), + timestamp); +} + +inline void pttl(Connection &connection, const StringView &key) { + connection.send("PTTL %b", key.data(), key.size()); +} + +inline void randomkey(Connection &connection) { + connection.send("RANDOMKEY"); +} + +inline void rename(Connection &connection, + const StringView &key, + const StringView &newkey) { + connection.send("RENAME %b %b", + key.data(), key.size(), + newkey.data(), newkey.size()); +} + +inline void renamenx(Connection &connection, + const StringView &key, + const StringView &newkey) { + connection.send("RENAMENX %b %b", + key.data(), key.size(), + newkey.data(), newkey.size()); +} + +void restore(Connection &connection, + const StringView &key, + const StringView &val, + long long ttl, + bool replace); + +inline void scan(Connection &connection, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("SCAN %lld MATCH %b COUNT %lld", + cursor, + pattern.data(), pattern.size(), + count); +} + +inline void touch(Connection &connection, const StringView &key) { + connection.send("TOUCH %b", key.data(), key.size()); +} + +template +inline void touch_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "TOUCH" << std::make_pair(first, last); + + connection.send(args); +} + +inline void ttl(Connection &connection, const StringView &key) { + connection.send("TTL %b", key.data(), key.size()); +} + +inline void type(Connection &connection, const StringView &key) { + connection.send("TYPE %b", key.data(), key.size()); +} + +inline void unlink(Connection &connection, const StringView &key) { + connection.send("UNLINK %b", key.data(), key.size()); +} + +template +inline void unlink_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "UNLINK" << std::make_pair(first, last); + + connection.send(args); +} + +inline void wait(Connection &connection, long long numslave, long long timeout) { + connection.send("WAIT %lld %lld", numslave, timeout); +} + +// STRING commands. + +inline void append(Connection &connection, const StringView &key, const StringView &str) { + connection.send("APPEND %b %b", + key.data(), key.size(), + str.data(), str.size()); +} + +inline void bitcount(Connection &connection, + const StringView &key, + long long start, + long long end) { + connection.send("BITCOUNT %b %lld %lld", + key.data(), key.size(), + start, end); +} + +void bitop(Connection &connection, + BitOp op, + const StringView &destination, + const StringView &key); + +template +void bitop_range(Connection &connection, + BitOp op, + const StringView &destination, + Input first, + Input last); + +inline void bitpos(Connection &connection, + const StringView &key, + long long bit, + long long start, + long long end) { + connection.send("BITPOS %b %lld %lld %lld", + key.data(), key.size(), + bit, + start, + end); +} + +inline void decr(Connection &connection, const StringView &key) { + connection.send("DECR %b", key.data(), key.size()); +} + +inline void decrby(Connection &connection, const StringView &key, long long decrement) { + connection.send("DECRBY %b %lld", + key.data(), key.size(), + decrement); +} + +inline void get(Connection &connection, const StringView &key) { + connection.send("GET %b", + key.data(), key.size()); +} + +inline void getbit(Connection &connection, const StringView &key, long long offset) { + connection.send("GETBIT %b %lld", + key.data(), key.size(), + offset); +} + +inline void getrange(Connection &connection, + const StringView &key, + long long start, + long long end) { + connection.send("GETRANGE %b %lld %lld", + key.data(), key.size(), + start, + end); +} + +inline void getset(Connection &connection, + const StringView &key, + const StringView &val) { + connection.send("GETSET %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +inline void incr(Connection &connection, const StringView &key) { + connection.send("INCR %b", key.data(), key.size()); +} + +inline void incrby(Connection &connection, const StringView &key, long long increment) { + connection.send("INCRBY %b %lld", + key.data(), key.size(), + increment); +} + +inline void incrbyfloat(Connection &connection, const StringView &key, double increment) { + connection.send("INCRBYFLOAT %b %f", + key.data(), key.size(), + increment); +} + +template +inline void mget(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "MGET" << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void mset(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "MSET" << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void msetnx(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "MSETNX" << std::make_pair(first, last); + + connection.send(args); +} + +inline void psetex(Connection &connection, + const StringView &key, + long long ttl, + const StringView &val) { + connection.send("PSETEX %b %lld %b", + key.data(), key.size(), + ttl, + val.data(), val.size()); +} + +void set(Connection &connection, + const StringView &key, + const StringView &val, + long long ttl, + UpdateType type); + +inline void setex(Connection &connection, + const StringView &key, + long long ttl, + const StringView &val) { + connection.send("SETEX %b %lld %b", + key.data(), key.size(), + ttl, + val.data(), val.size()); +} + +inline void setnx(Connection &connection, + const StringView &key, + const StringView &val) { + connection.send("SETNX %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +inline void setrange(Connection &connection, + const StringView &key, + long long offset, + const StringView &val) { + connection.send("SETRANGE %b %lld %b", + key.data(), key.size(), + offset, + val.data(), val.size()); +} + +inline void strlen(Connection &connection, const StringView &key) { + connection.send("STRLEN %b", key.data(), key.size()); +} + +// LIST commands. + +inline void blpop(Connection &connection, const StringView &key, long long timeout) { + connection.send("BLPOP %b %lld", + key.data(), key.size(), + timeout); +} + +template +inline void blpop_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BLPOP" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +inline void brpop(Connection &connection, const StringView &key, long long timeout) { + connection.send("BRPOP %b %lld", + key.data(), key.size(), + timeout); +} + +template +inline void brpop_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BRPOP" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +inline void brpoplpush(Connection &connection, + const StringView &source, + const StringView &destination, + long long timeout) { + connection.send("BRPOPLPUSH %b %b %lld", + source.data(), source.size(), + destination.data(), destination.size(), + timeout); +} + +inline void lindex(Connection &connection, const StringView &key, long long index) { + connection.send("LINDEX %b %lld", + key.data(), key.size(), + index); +} + +void linsert(Connection &connection, + const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val); + +inline void llen(Connection &connection, + const StringView &key) { + connection.send("LLEN %b", key.data(), key.size()); +} + +inline void lpop(Connection &connection, const StringView &key) { + connection.send("LPOP %b", + key.data(), key.size()); +} + +inline void lpush(Connection &connection, const StringView &key, const StringView &val) { + connection.send("LPUSH %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +template +inline void lpush_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "LPUSH" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void lpushx(Connection &connection, const StringView &key, const StringView &val) { + connection.send("LPUSHX %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +inline void lrange(Connection &connection, + const StringView &key, + long long start, + long long stop) { + connection.send("LRANGE %b %lld %lld", + key.data(), key.size(), + start, + stop); +} + +inline void lrem(Connection &connection, + const StringView &key, + long long count, + const StringView &val) { + connection.send("LREM %b %lld %b", + key.data(), key.size(), + count, + val.data(), val.size()); +} + +inline void lset(Connection &connection, + const StringView &key, + long long index, + const StringView &val) { + connection.send("LSET %b %lld %b", + key.data(), key.size(), + index, + val.data(), val.size()); +} + +inline void ltrim(Connection &connection, + const StringView &key, + long long start, + long long stop) { + connection.send("LTRIM %b %lld %lld", + key.data(), key.size(), + start, + stop); +} + +inline void rpop(Connection &connection, const StringView &key) { + connection.send("RPOP %b", key.data(), key.size()); +} + +inline void rpoplpush(Connection &connection, + const StringView &source, + const StringView &destination) { + connection.send("RPOPLPUSH %b %b", + source.data(), source.size(), + destination.data(), destination.size()); +} + +inline void rpush(Connection &connection, const StringView &key, const StringView &val) { + connection.send("RPUSH %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +template +inline void rpush_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "RPUSH" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void rpushx(Connection &connection, const StringView &key, const StringView &val) { + connection.send("RPUSHX %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +// HASH commands. + +inline void hdel(Connection &connection, const StringView &key, const StringView &field) { + connection.send("HDEL %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +template +inline void hdel_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "HDEL" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void hexists(Connection &connection, const StringView &key, const StringView &field) { + connection.send("HEXISTS %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +inline void hget(Connection &connection, const StringView &key, const StringView &field) { + connection.send("HGET %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +inline void hgetall(Connection &connection, const StringView &key) { + connection.send("HGETALL %b", key.data(), key.size()); +} + +inline void hincrby(Connection &connection, + const StringView &key, + const StringView &field, + long long increment) { + connection.send("HINCRBY %b %b %lld", + key.data(), key.size(), + field.data(), field.size(), + increment); +} + +inline void hincrbyfloat(Connection &connection, + const StringView &key, + const StringView &field, + double increment) { + connection.send("HINCRBYFLOAT %b %b %f", + key.data(), key.size(), + field.data(), field.size(), + increment); +} + +inline void hkeys(Connection &connection, const StringView &key) { + connection.send("HKEYS %b", key.data(), key.size()); +} + +inline void hlen(Connection &connection, const StringView &key) { + connection.send("HLEN %b", key.data(), key.size()); +} + +template +inline void hmget(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "HMGET" << key << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void hmset(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "HMSET" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void hscan(Connection &connection, + const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("HSCAN %b %lld MATCH %b COUNT %lld", + key.data(), key.size(), + cursor, + pattern.data(), pattern.size(), + count); +} + +inline void hset(Connection &connection, + const StringView &key, + const StringView &field, + const StringView &val) { + connection.send("HSET %b %b %b", + key.data(), key.size(), + field.data(), field.size(), + val.data(), val.size()); +} + +template +void hset_range(Connection &connection, const StringView &key, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "HSET" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void hsetnx(Connection &connection, + const StringView &key, + const StringView &field, + const StringView &val) { + connection.send("HSETNX %b %b %b", + key.data(), key.size(), + field.data(), field.size(), + val.data(), val.size()); +} + +inline void hstrlen(Connection &connection, + const StringView &key, + const StringView &field) { + connection.send("HSTRLEN %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +inline void hvals(Connection &connection, const StringView &key) { + connection.send("HVALS %b", key.data(), key.size()); +} + +// SET commands + +inline void sadd(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("SADD %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void sadd_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SADD" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void scard(Connection &connection, const StringView &key) { + connection.send("SCARD %b", key.data(), key.size()); +} + +template +inline void sdiff(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SDIFF" << std::make_pair(first, last); + + connection.send(args); +} + +inline void sdiffstore(Connection &connection, + const StringView &destination, + const StringView &key) { + connection.send("SDIFFSTORE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void sdiffstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SDIFFSTORE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void sinter(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SINTER" << std::make_pair(first, last); + + connection.send(args); +} + +inline void sinterstore(Connection &connection, + const StringView &destination, + const StringView &key) { + connection.send("SINTERSTORE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void sinterstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SINTERSTORE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +inline void sismember(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("SISMEMBER %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void smembers(Connection &connection, const StringView &key) { + connection.send("SMEMBERS %b", key.data(), key.size()); +} + +inline void smove(Connection &connection, + const StringView &source, + const StringView &destination, + const StringView &member) { + connection.send("SMOVE %b %b %b", + source.data(), source.size(), + destination.data(), destination.size(), + member.data(), member.size()); +} + +inline void spop(Connection &connection, const StringView &key) { + connection.send("SPOP %b", key.data(), key.size()); +} + +inline void spop_range(Connection &connection, const StringView &key, long long count) { + connection.send("SPOP %b %lld", + key.data(), key.size(), + count); +} + +inline void srandmember(Connection &connection, const StringView &key) { + connection.send("SRANDMEMBER %b", key.data(), key.size()); +} + +inline void srandmember_range(Connection &connection, + const StringView &key, + long long count) { + connection.send("SRANDMEMBER %b %lld", + key.data(), key.size(), + count); +} + +inline void srem(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("SREM %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void srem_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SREM" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void sscan(Connection &connection, + const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("SSCAN %b %lld MATCH %b COUNT %lld", + key.data(), key.size(), + cursor, + pattern.data(), pattern.size(), + count); +} + +template +inline void sunion(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SUNION" << std::make_pair(first, last); + + connection.send(args); +} + +inline void sunionstore(Connection &connection, + const StringView &destination, + const StringView &key) { + connection.send("SUNIONSTORE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void sunionstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SUNIONSTORE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +// Sorted Set commands. + +inline void bzpopmax(Connection &connection, const StringView &key, long long timeout) { + connection.send("BZPOPMAX %b %lld", key.data(), key.size(), timeout); +} + +template +void bzpopmax_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BZPOPMAX" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +inline void bzpopmin(Connection &connection, const StringView &key, long long timeout) { + connection.send("BZPOPMIN %b %lld", key.data(), key.size(), timeout); +} + +template +void bzpopmin_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BZPOPMIN" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +template +void zadd_range(Connection &connection, + const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed); + +inline void zadd(Connection &connection, + const StringView &key, + const StringView &member, + double score, + UpdateType type, + bool changed) { + auto tmp = {std::make_pair(member, score)}; + + zadd_range(connection, key, tmp.begin(), tmp.end(), type, changed); +} + +inline void zcard(Connection &connection, const StringView &key) { + connection.send("ZCARD %b", key.data(), key.size()); +} + +template +inline void zcount(Connection &connection, + const StringView &key, + const Interval &interval) { + connection.send("ZCOUNT %b %s %s", + key.data(), key.size(), + interval.min().c_str(), + interval.max().c_str()); +} + +inline void zincrby(Connection &connection, + const StringView &key, + double increment, + const StringView &member) { + connection.send("ZINCRBY %b %f %b", + key.data(), key.size(), + increment, + member.data(), member.size()); +} + +inline void zinterstore(Connection &connection, + const StringView &destination, + const StringView &key, + double weight) { + connection.send("ZINTERSTORE %b 1 %b WEIGHTS %f", + destination.data(), destination.size(), + key.data(), key.size(), + weight); +} + +template +void zinterstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr); + +template +inline void zlexcount(Connection &connection, + const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZLEXCOUNT %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline void zpopmax(Connection &connection, const StringView &key, long long count) { + connection.send("ZPOPMAX %b %lld", + key.data(), key.size(), + count); +} + +inline void zpopmin(Connection &connection, const StringView &key, long long count) { + connection.send("ZPOPMIN %b %lld", + key.data(), key.size(), + count); +} + +inline void zrange(Connection &connection, + const StringView &key, + long long start, + long long stop, + bool with_scores) { + if (with_scores) { + connection.send("ZRANGE %b %lld %lld WITHSCORES", + key.data(), key.size(), + start, + stop); + } else { + connection.send("ZRANGE %b %lld %lld", + key.data(), key.size(), + start, + stop); + } +} + +template +inline void zrangebylex(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZRANGEBYLEX %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); +} + +template +void zrangebyscore(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + if (with_scores) { + connection.send("ZRANGEBYSCORE %b %b %b WITHSCORES LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); + } else { + connection.send("ZRANGEBYSCORE %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); + } +} + +inline void zrank(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZRANK %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void zrem(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZREM %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void zrem_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "ZREM" << key << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void zremrangebylex(Connection &connection, + const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZREMRANGEBYLEX %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline void zremrangebyrank(Connection &connection, + const StringView &key, + long long start, + long long stop) { + connection.send("zremrangebyrank %b %lld %lld", + key.data(), key.size(), + start, + stop); +} + +template +inline void zremrangebyscore(Connection &connection, + const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZREMRANGEBYSCORE %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline void zrevrange(Connection &connection, + const StringView &key, + long long start, + long long stop, + bool with_scores) { + if (with_scores) { + connection.send("ZREVRANGE %b %lld %lld WITHSCORES", + key.data(), key.size(), + start, + stop); + } else { + connection.send("ZREVRANGE %b %lld %lld", + key.data(), key.size(), + start, + stop); + } +} + +template +inline void zrevrangebylex(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZREVRANGEBYLEX %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + max.data(), max.size(), + min.data(), min.size(), + opts.offset, + opts.count); +} + +template +void zrevrangebyscore(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + if (with_scores) { + connection.send("ZREVRANGEBYSCORE %b %b %b WITHSCORES LIMIT %lld %lld", + key.data(), key.size(), + max.data(), max.size(), + min.data(), min.size(), + opts.offset, + opts.count); + } else { + connection.send("ZREVRANGEBYSCORE %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + max.data(), max.size(), + min.data(), min.size(), + opts.offset, + opts.count); + } +} + +inline void zrevrank(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZREVRANK %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void zscan(Connection &connection, + const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("ZSCAN %b %lld MATCH %b COUNT %lld", + key.data(), key.size(), + cursor, + pattern.data(), pattern.size(), + count); +} + +inline void zscore(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZSCORE %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void zunionstore(Connection &connection, + const StringView &destination, + const StringView &key, + double weight) { + connection.send("ZUNIONSTORE %b 1 %b WEIGHTS %f", + destination.data(), destination.size(), + key.data(), key.size(), + weight); +} + +template +void zunionstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr); + +// HYPERLOGLOG commands. + +inline void pfadd(Connection &connection, + const StringView &key, + const StringView &element) { + connection.send("PFADD %b %b", + key.data(), key.size(), + element.data(), element.size()); +} + +template +inline void pfadd_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "PFADD" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void pfcount(Connection &connection, const StringView &key) { + connection.send("PFCOUNT %b", key.data(), key.size()); +} + +template +inline void pfcount_range(Connection &connection, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "PFCOUNT" << std::make_pair(first, last); + + connection.send(args); +} + +inline void pfmerge(Connection &connection, const StringView &destination, const StringView &key) { + connection.send("PFMERGE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void pfmerge_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "PFMERGE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +// GEO commands. + +inline void geoadd(Connection &connection, + const StringView &key, + const std::tuple &member) { + const auto &mem = std::get<0>(member); + + connection.send("GEOADD %b %f %f %b", + key.data(), key.size(), + std::get<1>(member), + std::get<2>(member), + mem.data(), mem.size()); +} + +template +inline void geoadd_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "GEOADD" << key; + + while (first != last) { + const auto &member = *first; + args << std::get<1>(member) << std::get<2>(member) << std::get<0>(member); + ++first; + } + + connection.send(args); +} + +void geodist(Connection &connection, + const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit); + +inline void geohash(Connection &connection, const StringView &key, const StringView &member) { + connection.send("GEOHASH %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void geohash_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "GEOHASH" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void geopos(Connection &connection, const StringView &key, const StringView &member) { + connection.send("GEOPOS %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void geopos_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "GEOPOS" << key << std::make_pair(first, last); + + connection.send(args); +} + +void georadius(Connection &connection, + const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash); + +void georadius_store(Connection &connection, + const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + +void georadiusbymember(Connection &connection, + const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash); + +void georadiusbymember_store(Connection &connection, + const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + +// SCRIPTING commands. + +template +inline void eval(Connection &connection, + const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + CmdArgs cmd_args; + + auto keys_num = std::distance(keys_first, keys_last); + + cmd_args << "EVAL" << script << keys_num + << std::make_pair(keys_first, keys_last) + << std::make_pair(args_first, args_last); + + connection.send(cmd_args); +} + +template +inline void evalsha(Connection &connection, + const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + CmdArgs cmd_args; + + auto keys_num = std::distance(keys_first, keys_last); + + cmd_args << "EVALSHA" << script << keys_num + << std::make_pair(keys_first, keys_last) + << std::make_pair(args_first, args_last); + + connection.send(cmd_args); +} + +inline void script_exists(Connection &connection, const StringView &sha) { + connection.send("SCRIPT EXISTS %b", sha.data(), sha.size()); +} + +template +inline void script_exists_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SCRIPT" << "EXISTS" << std::make_pair(first, last); + + connection.send(args); +} + +inline void script_flush(Connection &connection) { + connection.send("SCRIPT FLUSH"); +} + +inline void script_kill(Connection &connection) { + connection.send("SCRIPT KILL"); +} + +inline void script_load(Connection &connection, const StringView &script) { + connection.send("SCRIPT LOAD %b", script.data(), script.size()); +} + +// PUBSUB commands. + +inline void psubscribe(Connection &connection, const StringView &pattern) { + connection.send("PSUBSCRIBE %b", pattern.data(), pattern.size()); +} + +template +inline void psubscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("PSUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "PSUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +inline void publish(Connection &connection, + const StringView &channel, + const StringView &message) { + connection.send("PUBLISH %b %b", + channel.data(), channel.size(), + message.data(), message.size()); +} + +inline void punsubscribe(Connection &connection) { + connection.send("PUNSUBSCRIBE"); +} + +inline void punsubscribe(Connection &connection, const StringView &pattern) { + connection.send("PUNSUBSCRIBE %b", pattern.data(), pattern.size()); +} + +template +inline void punsubscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("PUNSUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "PUNSUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +inline void subscribe(Connection &connection, const StringView &channel) { + connection.send("SUBSCRIBE %b", channel.data(), channel.size()); +} + +template +inline void subscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("SUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "SUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +inline void unsubscribe(Connection &connection) { + connection.send("UNSUBSCRIBE"); +} + +inline void unsubscribe(Connection &connection, const StringView &channel) { + connection.send("UNSUBSCRIBE %b", channel.data(), channel.size()); +} + +template +inline void unsubscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("UNSUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "UNSUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +// Transaction commands. + +inline void discard(Connection &connection) { + connection.send("DISCARD"); +} + +inline void exec(Connection &connection) { + connection.send("EXEC"); +} + +inline void multi(Connection &connection) { + connection.send("MULTI"); +} + +inline void unwatch(Connection &connection) { + connection.send("UNWATCH"); +} + +template +inline void unwatch_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("UNWATCH: no key specified"); + } + + CmdArgs args; + args << "UNWATCH" << std::make_pair(first, last); + + connection.send(args); +} + +inline void watch(Connection &connection, const StringView &key) { + connection.send("WATCH %b", key.data(), key.size()); +} + +template +inline void watch_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("WATCH: no key specified"); + } + + CmdArgs args; + args << "WATCH" << std::make_pair(first, last); + + connection.send(args); +} + +// Stream commands. + +inline void xack(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &id) { + connection.send("XACK %b %b %b", + key.data(), key.size(), + group.data(), group.size(), + id.data(), id.size()); +} + +template +void xack_range(Connection &connection, + const StringView &key, + const StringView &group, + Input first, + Input last) { + CmdArgs args; + args << "XACK" << key << group << std::make_pair(first, last); + + connection.send(args); +} + +template +void xadd_range(Connection &connection, + const StringView &key, + const StringView &id, + Input first, + Input last) { + CmdArgs args; + args << "XADD" << key << id << std::make_pair(first, last); + + connection.send(args); +} + +template +void xadd_maxlen_range(Connection &connection, + const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx) { + CmdArgs args; + args << "XADD" << key << "MAXLEN"; + + if (approx) { + args << "~"; + } + + args << count << id << std::make_pair(first, last); + + connection.send(args); +} + +inline void xclaim(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &consumer, + long long min_idle_time, + const StringView &id) { + connection.send("XCLAIM %b %b %b %lld %b", + key.data(), key.size(), + group.data(), group.size(), + consumer.data(), consumer.size(), + min_idle_time, + id.data(), id.size()); +} + +template +void xclaim_range(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &consumer, + long long min_idle_time, + Input first, + Input last) { + CmdArgs args; + args << "XCLAIM" << key << group << consumer << min_idle_time << std::make_pair(first, last); + + connection.send(args); +} + +inline void xdel(Connection &connection, const StringView &key, const StringView &id) { + connection.send("XDEL %b %b", key.data(), key.size(), id.data(), id.size()); +} + +template +void xdel_range(Connection &connection, const StringView &key, Input first, Input last) { + CmdArgs args; + args << "XDEL" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void xgroup_create(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream) { + CmdArgs args; + args << "XGROUP" << "CREATE" << key << group << id; + + if (mkstream) { + args << "MKSTREAM"; + } + + connection.send(args); +} + +inline void xgroup_setid(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &id) { + connection.send("XGROUP SETID %b %b %b", + key.data(), key.size(), + group.data(), group.size(), + id.data(), id.size()); +} + +inline void xgroup_destroy(Connection &connection, + const StringView &key, + const StringView &group) { + connection.send("XGROUP DESTROY %b %b", + key.data(), key.size(), + group.data(), group.size()); +} + +inline void xgroup_delconsumer(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &consumer) { + connection.send("XGROUP DELCONSUMER %b %b %b", + key.data(), key.size(), + group.data(), group.size(), + consumer.data(), consumer.size()); +} + +inline void xlen(Connection &connection, const StringView &key) { + connection.send("XLEN %b", key.data(), key.size()); +} + +inline void xpending(Connection &connection, const StringView &key, const StringView &group) { + connection.send("XPENDING %b %b", + key.data(), key.size(), + group.data(), group.size()); +} + +inline void xpending_detail(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count) { + connection.send("XPENDING %b %b %b %b %lld", + key.data(), key.size(), + group.data(), group.size(), + start.data(), start.size(), + end.data(), end.size(), + count); +} + +inline void xpending_per_consumer(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer) { + connection.send("XPENDING %b %b %b %b %lld %b", + key.data(), key.size(), + group.data(), group.size(), + start.data(), start.size(), + end.data(), end.size(), + count, + consumer.data(), consumer.size()); +} + +inline void xrange(Connection &connection, + const StringView &key, + const StringView &start, + const StringView &end) { + connection.send("XRANGE %b %b %b", + key.data(), key.size(), + start.data(), start.size(), + end.data(), end.size()); +} + +inline void xrange_count(Connection &connection, + const StringView &key, + const StringView &start, + const StringView &end, + long long count) { + connection.send("XRANGE %b %b %b COUNT %lld", + key.data(), key.size(), + start.data(), start.size(), + end.data(), end.size(), + count); +} + +inline void xread(Connection &connection, + const StringView &key, + const StringView &id, + long long count) { + connection.send("XREAD COUNT %lld STREAMS %b %b", + count, + key.data(), key.size(), + id.data(), id.size()); +} + +template +void xread_range(Connection &connection, Input first, Input last, long long count) { + CmdArgs args; + args << "XREAD" << "COUNT" << count << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xread_block(Connection &connection, + const StringView &key, + const StringView &id, + long long timeout, + long long count) { + connection.send("XREAD COUNT %lld BLOCK %lld STREAMS %b %b", + count, + timeout, + key.data(), key.size(), + id.data(), id.size()); +} + +template +void xread_block_range(Connection &connection, + Input first, + Input last, + long long timeout, + long long count) { + CmdArgs args; + args << "XREAD" << "COUNT" << count << "BLOCK" << timeout << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xreadgroup(Connection &connection, + const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer << "COUNT" << count; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS" << key << id; + + connection.send(args); +} + +template +void xreadgroup_range(Connection &connection, + const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer << "COUNT" << count; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xreadgroup_block(Connection &connection, + const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long timeout, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer + << "COUNT" << count << "BLOCK" << timeout; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS" << key << id; + + connection.send(args); +} + +template +void xreadgroup_block_range(Connection &connection, + const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long timeout, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer + << "COUNT" << count << "BLOCK" << timeout; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xrevrange(Connection &connection, + const StringView &key, + const StringView &end, + const StringView &start) { + connection.send("XREVRANGE %b %b %b", + key.data(), key.size(), + end.data(), end.size(), + start.data(), start.size()); +} + +inline void xrevrange_count(Connection &connection, + const StringView &key, + const StringView &end, + const StringView &start, + long long count) { + connection.send("XREVRANGE %b %b %b COUNT %lld", + key.data(), key.size(), + end.data(), end.size(), + start.data(), start.size(), + count); +} + +void xtrim(Connection &connection, const StringView &key, long long count, bool approx); + +namespace detail { + +void set_bitop(CmdArgs &args, BitOp op); + +void set_update_type(CmdArgs &args, UpdateType type); + +void set_aggregation_type(CmdArgs &args, Aggregation type); + +template +void zinterstore(std::false_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZINTERSTORE" << destination << std::distance(first, last) + << std::make_pair(first, last); + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +template +void zinterstore(std::true_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZINTERSTORE" << destination << std::distance(first, last); + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + args << "WEIGHTS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +template +void zunionstore(std::false_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZUNIONSTORE" << destination << std::distance(first, last) + << std::make_pair(first, last); + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +template +void zunionstore(std::true_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZUNIONSTORE" << destination << std::distance(first, last); + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + args << "WEIGHTS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +void set_geo_unit(CmdArgs &args, GeoUnit unit); + +void set_georadius_store_parameters(CmdArgs &args, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + +void set_georadius_parameters(CmdArgs &args, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash); + +} + +} + +} + +} + +namespace sw { + +namespace redis { + +namespace cmd { + +template +void bitop_range(Connection &connection, + BitOp op, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + + detail::set_bitop(args, op); + + args << destination << std::make_pair(first, last); + + connection.send(args); +} + +template +void zadd_range(Connection &connection, + const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed) { + assert(first != last); + + CmdArgs args; + + args << "ZADD" << key; + + detail::set_update_type(args, type); + + if (changed) { + args << "CH"; + } + + while (first != last) { + // Swap the pair to pair. + args << first->second << first->first; + ++first; + } + + connection.send(args); +} + +template +void zinterstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + assert(first != last); + + detail::zinterstore(typename IsKvPairIter::type(), + connection, + destination, + first, + last, + aggr); +} + +template +void zunionstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + assert(first != last); + + detail::zunionstore(typename IsKvPairIter::type(), + connection, + destination, + first, + last, + aggr); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/command_args.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/command_args.h new file mode 100644 index 000000000..0beb71e5c --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/command_args.h @@ -0,0 +1,180 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H +#define SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H + +#include +#include +#include +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +class CmdArgs { +public: + template + CmdArgs& append(Arg &&arg); + + template + CmdArgs& append(Arg &&arg, Args &&...args); + + // All overloads of operator<< are for internal use only. + CmdArgs& operator<<(const StringView &arg); + + template ::type>::value, + int>::type = 0> + CmdArgs& operator<<(T &&arg); + + template + CmdArgs& operator<<(const std::pair &range); + + template + auto operator<<(const std::tuple &) -> + typename std::enable_if::type { + return *this; + } + + template + auto operator<<(const std::tuple &arg) -> + typename std::enable_if::type; + + const char** argv() { + return _argv.data(); + } + + const std::size_t* argv_len() { + return _argv_len.data(); + } + + std::size_t size() const { + return _argv.size(); + } + +private: + // Deep copy. + CmdArgs& _append(std::string arg); + + // Shallow copy. + CmdArgs& _append(const StringView &arg); + + // Shallow copy. + CmdArgs& _append(const char *arg); + + template ::type>::value, + int>::type = 0> + CmdArgs& _append(T &&arg) { + return operator<<(std::forward(arg)); + } + + template + CmdArgs& _append(std::true_type, const std::pair &range); + + template + CmdArgs& _append(std::false_type, const std::pair &range); + + std::vector _argv; + std::vector _argv_len; + + std::list _args; +}; + +template +inline CmdArgs& CmdArgs::append(Arg &&arg) { + return _append(std::forward(arg)); +} + +template +inline CmdArgs& CmdArgs::append(Arg &&arg, Args &&...args) { + _append(std::forward(arg)); + + return append(std::forward(args)...); +} + +inline CmdArgs& CmdArgs::operator<<(const StringView &arg) { + _argv.push_back(arg.data()); + _argv_len.push_back(arg.size()); + + return *this; +} + +template +inline CmdArgs& CmdArgs::operator<<(const std::pair &range) { + return _append(IsKvPair())>::type>(), range); +} + +template ::type>::value, + int>::type> +inline CmdArgs& CmdArgs::operator<<(T &&arg) { + return _append(std::to_string(std::forward(arg))); +} + +template +auto CmdArgs::operator<<(const std::tuple &arg) -> + typename std::enable_if::type { + operator<<(std::get(arg)); + + return operator<<(arg); +} + +inline CmdArgs& CmdArgs::_append(std::string arg) { + _args.push_back(std::move(arg)); + return operator<<(_args.back()); +} + +inline CmdArgs& CmdArgs::_append(const StringView &arg) { + return operator<<(arg); +} + +inline CmdArgs& CmdArgs::_append(const char *arg) { + return operator<<(arg); +} + +template +CmdArgs& CmdArgs::_append(std::false_type, const std::pair &range) { + auto first = range.first; + auto last = range.second; + while (first != last) { + *this << *first; + ++first; + } + + return *this; +} + +template +CmdArgs& CmdArgs::_append(std::true_type, const std::pair &range) { + auto first = range.first; + auto last = range.second; + while (first != last) { + *this << first->first << first->second; + ++first; + } + + return *this; +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/command_options.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/command_options.h new file mode 100644 index 000000000..ca766c086 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/command_options.h @@ -0,0 +1,211 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H +#define SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H + +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +enum class UpdateType { + EXIST, + NOT_EXIST, + ALWAYS +}; + +enum class InsertPosition { + BEFORE, + AFTER +}; + +enum class BoundType { + CLOSED, + OPEN, + LEFT_OPEN, + RIGHT_OPEN +}; + +// (-inf, +inf) +template +class UnboundedInterval; + +// [min, max], (min, max), (min, max], [min, max) +template +class BoundedInterval; + +// [min, +inf), (min, +inf) +template +class LeftBoundedInterval; + +// (-inf, max], (-inf, max) +template +class RightBoundedInterval; + +template <> +class UnboundedInterval { +public: + const std::string& min() const; + + const std::string& max() const; +}; + +template <> +class BoundedInterval { +public: + BoundedInterval(double min, double max, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const { + return _max; + } + +private: + std::string _min; + std::string _max; +}; + +template <> +class LeftBoundedInterval { +public: + LeftBoundedInterval(double min, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const; + +private: + std::string _min; +}; + +template <> +class RightBoundedInterval { +public: + RightBoundedInterval(double max, BoundType type); + + const std::string& min() const; + + const std::string& max() const { + return _max; + } + +private: + std::string _max; +}; + +template <> +class UnboundedInterval { +public: + const std::string& min() const; + + const std::string& max() const; +}; + +template <> +class BoundedInterval { +public: + BoundedInterval(const std::string &min, const std::string &max, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const { + return _max; + } + +private: + std::string _min; + std::string _max; +}; + +template <> +class LeftBoundedInterval { +public: + LeftBoundedInterval(const std::string &min, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const; + +private: + std::string _min; +}; + +template <> +class RightBoundedInterval { +public: + RightBoundedInterval(const std::string &max, BoundType type); + + const std::string& min() const; + + const std::string& max() const { + return _max; + } + +private: + std::string _max; +}; + +struct LimitOptions { + long long offset = 0; + long long count = -1; +}; + +enum class Aggregation { + SUM, + MIN, + MAX +}; + +enum class BitOp { + AND, + OR, + XOR, + NOT +}; + +enum class GeoUnit { + M, + KM, + MI, + FT +}; + +template +struct WithCoord : TupleWithType, T> {}; + +template +struct WithDist : TupleWithType {}; + +template +struct WithHash : TupleWithType {}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/connection.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/connection.h new file mode 100644 index 000000000..340375561 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/connection.h @@ -0,0 +1,237 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_CONNECTION_H +#define SEWENEW_REDISPLUSPLUS_CONNECTION_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "errors.h" +#include "reply.h" +#include "utils.h" +#include "tls.h" + +namespace sw { + +namespace redis { + +enum class ConnectionType { + TCP = 0, + UNIX +}; + +struct ConnectionOptions { +public: + ConnectionOptions() = default; + + explicit ConnectionOptions(const std::string &uri); + + ConnectionOptions(const ConnectionOptions &) = default; + ConnectionOptions& operator=(const ConnectionOptions &) = default; + + ConnectionOptions(ConnectionOptions &&) = default; + ConnectionOptions& operator=(ConnectionOptions &&) = default; + + ~ConnectionOptions() = default; + + ConnectionType type = ConnectionType::TCP; + + std::string host; + + int port = 6379; + + std::string path; + + std::string user = "default"; + + std::string password; + + int db = 0; + + bool keep_alive = false; + + std::chrono::milliseconds connect_timeout{0}; + + std::chrono::milliseconds socket_timeout{0}; + + tls::TlsOptions tls; + + // `readonly` is only used for reading from a slave node in Redis Cluster mode. + // Client code should never manually set/get it. This member might be removed in the future. + bool readonly = false; + +private: + ConnectionOptions _parse_uri(const std::string &uri) const; + + auto _split_uri(const std::string &uri) const + -> std::tuple; + + auto _split_path(const std::string &path) const + -> std::tuple; + + void _parse_parameters(const std::string ¶meter_string, + ConnectionOptions &opts) const; + + void _set_option(const std::string &key, const std::string &val, ConnectionOptions &opts) const; + + bool _parse_bool_option(const std::string &str) const; + + std::chrono::milliseconds _parse_timeout_option(const std::string &str) const; + + std::vector _split(const std::string &str, const std::string &delimiter) const; + + void _set_auth_opts(const std::string &auth, ConnectionOptions &opts) const; + + void _set_tcp_opts(const std::string &path, ConnectionOptions &opts) const; + + void _set_unix_opts(const std::string &path, ConnectionOptions &opts) const; +}; + +class CmdArgs; + +class Connection { +public: + explicit Connection(const ConnectionOptions &opts); + + Connection(const Connection &) = delete; + Connection& operator=(const Connection &) = delete; + + Connection(Connection &&) = default; + Connection& operator=(Connection &&) = default; + + ~Connection() = default; + + // Check if the connection is broken. Client needs to do this check + // before sending some command to the connection. If it's broken, + // client needs to reconnect it. + bool broken() const noexcept { + return _ctx->err != REDIS_OK; + } + + void reset() noexcept { + _ctx->err = 0; + } + + void invalidate() noexcept { + _ctx->err = REDIS_ERR; + } + + void reconnect(); + + auto create_time() const + -> std::chrono::time_point { + return _create_time; + } + + auto last_active() const + -> std::chrono::time_point { + return _last_active; + } + + template + void send(const char *format, Args &&...args); + + void send(int argc, const char **argv, const std::size_t *argv_len); + + void send(CmdArgs &args); + + ReplyUPtr recv(bool handle_error_reply = true); + + const ConnectionOptions& options() const { + return _opts; + } + + friend void swap(Connection &lhs, Connection &rhs) noexcept; + +private: + class Connector; + + struct ContextDeleter { + void operator()(redisContext *context) const { + if (context != nullptr) { + redisFree(context); + } + }; + }; + + using ContextUPtr = std::unique_ptr; + + void _set_options(); + + void _auth(); + + void _select_db(); + + void _enable_readonly(); + + redisContext* _context(); + + ContextUPtr _ctx; + + // The time that the connection is created. + std::chrono::time_point _create_time{}; + + // The time that the connection is created or the time that + // the connection is recently used, i.e. `_context()` is called. + std::chrono::time_point _last_active{}; + + ConnectionOptions _opts; + + // TODO: define _tls_ctx before _ctx + tls::TlsContextUPtr _tls_ctx; +}; + +using ConnectionSPtr = std::shared_ptr; + +enum class Role { + MASTER, + SLAVE +}; + +// Inline implementaions. + +template +inline void Connection::send(const char *format, Args &&...args) { + auto ctx = _context(); + + assert(ctx != nullptr); + + if (redisAppendCommand(ctx, + format, + std::forward(args)...) != REDIS_OK) { + throw_error(*ctx, "Failed to send command"); + } + + assert(!broken()); +} + +inline redisContext* Connection::_context() { + _last_active = std::chrono::steady_clock::now(); + + return _ctx.get(); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_CONNECTION_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/connection_pool.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/connection_pool.h new file mode 100644 index 000000000..30ba5daa2 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/connection_pool.h @@ -0,0 +1,182 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H +#define SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H + +#include +#include +#include +#include +#include +#include +#include "connection.h" +#include "sentinel.h" + +namespace sw { + +namespace redis { + +struct ConnectionPoolOptions { + // Max number of connections, including both in-use and idle ones. + std::size_t size = 1; + + // Max time to wait for a connection. 0ms means client waits forever. + std::chrono::milliseconds wait_timeout{0}; + + // Max lifetime of a connection. 0ms means we never expire the connection. + std::chrono::milliseconds connection_lifetime{0}; + + // Max idle time of a connection. 0ms means we never expire the connection. + std::chrono::milliseconds connection_idle_time{0}; +}; + +class ConnectionPool { +public: + ConnectionPool(const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts); + + ConnectionPool(SimpleSentinel sentinel, + const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts); + + ConnectionPool() = default; + + ConnectionPool(ConnectionPool &&that); + ConnectionPool& operator=(ConnectionPool &&that); + + ConnectionPool(const ConnectionPool &) = delete; + ConnectionPool& operator=(const ConnectionPool &) = delete; + + ~ConnectionPool() = default; + + // Fetch a connection from pool. + Connection fetch(); + + ConnectionOptions connection_options(); + + void release(Connection connection); + + // Create a new connection. + Connection create(); + + ConnectionPool clone(); + +private: + void _move(ConnectionPool &&that); + + // NOT thread-safe + Connection _create(); + + Connection _create(SimpleSentinel &sentinel, const ConnectionOptions &opts, bool locked); + + Connection _fetch(); + + void _wait_for_connection(std::unique_lock &lock); + + bool _need_reconnect(const Connection &connection, + const std::chrono::milliseconds &connection_lifetime, + const std::chrono::milliseconds &connection_idle_time) const; + + void _update_connection_opts(const std::string &host, int port) { + _opts.host = host; + _opts.port = port; + } + + bool _role_changed(const ConnectionOptions &opts) const { + return opts.port != _opts.port || opts.host != _opts.host; + } + + ConnectionOptions _opts; + + ConnectionPoolOptions _pool_opts; + + std::deque _pool; + + std::size_t _used_connections = 0; + + std::mutex _mutex; + + std::condition_variable _cv; + + SimpleSentinel _sentinel; +}; + +using ConnectionPoolSPtr = std::shared_ptr; + +class SafeConnection { +public: + explicit SafeConnection(ConnectionPool &pool) : _pool(pool), _connection(_pool.fetch()) { + assert(!_connection.broken()); + } + + SafeConnection(const SafeConnection &) = delete; + SafeConnection& operator=(const SafeConnection &) = delete; + + SafeConnection(SafeConnection &&) = delete; + SafeConnection& operator=(SafeConnection &&) = delete; + + ~SafeConnection() { + _pool.release(std::move(_connection)); + } + + Connection& connection() { + return _connection; + } + +private: + ConnectionPool &_pool; + Connection _connection; +}; + +// NOTE: This class is similar to `SafeConnection`. +// The difference is that `SafeConnection` tries to avoid copying a std::shared_ptr. +class GuardedConnection { +public: + explicit GuardedConnection(const ConnectionPoolSPtr &pool) : _pool(pool), + _connection(_pool->fetch()) { + assert(!_connection.broken()); + } + + GuardedConnection(const GuardedConnection &) = delete; + GuardedConnection& operator=(const GuardedConnection &) = delete; + + GuardedConnection(GuardedConnection &&) = default; + GuardedConnection& operator=(GuardedConnection &&) = default; + + ~GuardedConnection() { + // If `GuardedConnection` has been moved, `_pool` will be nullptr. + if (_pool) { + _pool->release(std::move(_connection)); + } + } + + Connection& connection() { + return _connection; + } + +private: + ConnectionPoolSPtr _pool; + Connection _connection; +}; + +using GuardedConnectionSPtr = std::shared_ptr; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/cxx_utils.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/cxx_utils.h new file mode 100644 index 000000000..dce51abe9 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/cxx_utils.h @@ -0,0 +1,46 @@ +/************************************************************************** + Copyright (c) 2021 sewenew + + 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 SEWENEW_REDISPLUSPLUS_CXX_UTILS_H +#define SEWENEW_REDISPLUSPLUS_CXX_UTILS_H + +#include +#include +#include + +#define REDIS_PLUS_PLUS_HAS_OPTIONAL + +#define REDIS_PLUS_PLUS_HAS_VARIANT + +namespace sw { + +namespace redis { + +using StringView = std::string_view; + +template +using Optional = std::optional; + +template +using Variant = std::variant; + +using Monostate = std::monostate; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_CXX_UTILS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/errors.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/errors.h new file mode 100644 index 000000000..7cc19d2f1 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/errors.h @@ -0,0 +1,166 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_ERRORS_H +#define SEWENEW_REDISPLUSPLUS_ERRORS_H + +#include +#include +#include + +namespace sw { + +namespace redis { + +enum ReplyErrorType { + ERR, + MOVED, + ASK +}; + +class Error : public std::exception { +public: + explicit Error(const std::string &msg) : _msg(msg) {} + + Error(const Error &) = default; + Error& operator=(const Error &) = default; + + Error(Error &&) = default; + Error& operator=(Error &&) = default; + + virtual ~Error() override = default; + + virtual const char* what() const noexcept override { + return _msg.data(); + } + +private: + std::string _msg; +}; + +class IoError : public Error { +public: + explicit IoError(const std::string &msg) : Error(msg) {} + + IoError(const IoError &) = default; + IoError& operator=(const IoError &) = default; + + IoError(IoError &&) = default; + IoError& operator=(IoError &&) = default; + + virtual ~IoError() override = default; +}; + +class TimeoutError : public IoError { +public: + explicit TimeoutError(const std::string &msg) : IoError(msg) {} + + TimeoutError(const TimeoutError &) = default; + TimeoutError& operator=(const TimeoutError &) = default; + + TimeoutError(TimeoutError &&) = default; + TimeoutError& operator=(TimeoutError &&) = default; + + virtual ~TimeoutError() override = default; +}; + +class ClosedError : public Error { +public: + explicit ClosedError(const std::string &msg) : Error(msg) {} + + ClosedError(const ClosedError &) = default; + ClosedError& operator=(const ClosedError &) = default; + + ClosedError(ClosedError &&) = default; + ClosedError& operator=(ClosedError &&) = default; + + virtual ~ClosedError() override = default; +}; + +class ProtoError : public Error { +public: + explicit ProtoError(const std::string &msg) : Error(msg) {} + + ProtoError(const ProtoError &) = default; + ProtoError& operator=(const ProtoError &) = default; + + ProtoError(ProtoError &&) = default; + ProtoError& operator=(ProtoError &&) = default; + + virtual ~ProtoError() override = default; +}; + +class OomError : public Error { +public: + explicit OomError(const std::string &msg) : Error(msg) {} + + OomError(const OomError &) = default; + OomError& operator=(const OomError &) = default; + + OomError(OomError &&) = default; + OomError& operator=(OomError &&) = default; + + virtual ~OomError() override = default; +}; + +class ReplyError : public Error { +public: + explicit ReplyError(const std::string &msg) : Error(msg) {} + + ReplyError(const ReplyError &) = default; + ReplyError& operator=(const ReplyError &) = default; + + ReplyError(ReplyError &&) = default; + ReplyError& operator=(ReplyError &&) = default; + + virtual ~ReplyError() override = default; +}; + +class WatchError : public Error { +public: + explicit WatchError() : Error("Watched key has been modified") {} + + WatchError(const WatchError &) = default; + WatchError& operator=(const WatchError &) = default; + + WatchError(WatchError &&) = default; + WatchError& operator=(WatchError &&) = default; + + virtual ~WatchError() override = default; +}; + + +// MovedError and AskError are defined in shards.h +class MovedError; + +class AskError; + +void throw_error(const redisContext &context, const std::string &err_info); + +void throw_error(const redisReply &reply); + +template +inline void range_check(const char *cmd, Input first, Input last) { + if (first == last) { + throw Error(std::string(cmd) + ": no key specified"); + } +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_ERRORS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/pipeline.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/pipeline.h new file mode 100644 index 000000000..52b01253f --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/pipeline.h @@ -0,0 +1,49 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_PIPELINE_H +#define SEWENEW_REDISPLUSPLUS_PIPELINE_H + +#include +#include +#include "connection.h" + +namespace sw { + +namespace redis { + +class PipelineImpl { +public: + template + void command(Connection &connection, Cmd cmd, Args &&...args) { + assert(!connection.broken()); + + cmd(connection, std::forward(args)...); + } + + std::vector exec(Connection &connection, std::size_t cmd_num); + + void discard(Connection &connection, std::size_t /*cmd_num*/) { + // Reconnect to Redis to discard all commands. + connection.reconnect(); + } +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_PIPELINE_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/queued_redis.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/queued_redis.h new file mode 100644 index 000000000..e663c7423 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/queued_redis.h @@ -0,0 +1,2013 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_H +#define SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_H + +#include +#include +#include +#include +#include +#include "connection.h" +#include "connection_pool.h" +#include "utils.h" +#include "reply.h" +#include "command.h" +#include "redis.h" +#include "errors.h" + +namespace sw { + +namespace redis { + +class QueuedReplies; + +// If any command throws, QueuedRedis resets the connection, and becomes invalid. +// In this case, the only thing we can do is to destory the QueuedRedis object. +template +class QueuedRedis { +public: + QueuedRedis(QueuedRedis &&) = default; + QueuedRedis& operator=(QueuedRedis &&) = default; + + // When it destructs, the underlying *Connection* will be closed or return to pool, + // and any command that has NOT been executed will be ignored. + ~QueuedRedis(); + + Redis redis(); + + template + auto command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, + QueuedRedis&>::type; + + template + QueuedRedis& command(const StringView &cmd_name, Args &&...args); + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, QueuedRedis&>::type; + + QueuedReplies exec(); + + void discard(); + + // CONNECTION commands. + + QueuedRedis& auth(const StringView &password) { + return command(cmd::auth, password); + } + + QueuedRedis& auth(const StringView &user, const StringView &password) { + return command( + cmd::auth, user, password); + } + + QueuedRedis& echo(const StringView &msg) { + return command(cmd::echo, msg); + } + + QueuedRedis& ping() { + return command(cmd::ping); + } + + QueuedRedis& ping(const StringView &msg) { + return command(cmd::ping, msg); + } + + // We DO NOT support the QUIT command. See *Redis::quit* doc for details. + // + // QueuedRedis& quit(); + + QueuedRedis& select(long long idx) { + return command(cmd::select, idx); + } + + QueuedRedis& swapdb(long long idx1, long long idx2) { + return command(cmd::swapdb, idx1, idx2); + } + + // SERVER commands. + + QueuedRedis& bgrewriteaof() { + return command(cmd::bgrewriteaof); + } + + QueuedRedis& bgsave() { + return command(cmd::bgsave); + } + + QueuedRedis& dbsize() { + return command(cmd::dbsize); + } + + QueuedRedis& flushall(bool async = false) { + return command(cmd::flushall, async); + } + + QueuedRedis& flushdb(bool async = false) { + return command(cmd::flushdb, async); + } + + QueuedRedis& info() { + return command(cmd::info); + } + + QueuedRedis& info(const StringView §ion) { + return command(cmd::info, section); + } + + QueuedRedis& lastsave() { + return command(cmd::lastsave); + } + + QueuedRedis& save() { + return command(cmd::save); + } + + // KEY commands. + + QueuedRedis& del(const StringView &key) { + return command(cmd::del, key); + } + + template + QueuedRedis& del(Input first, Input last) { + range_check("DEL", first, last); + + return command(cmd::del_range, first, last); + } + + template + QueuedRedis& del(std::initializer_list il) { + return del(il.begin(), il.end()); + } + + QueuedRedis& dump(const StringView &key) { + return command(cmd::dump, key); + } + + QueuedRedis& exists(const StringView &key) { + return command(cmd::exists, key); + } + + template + QueuedRedis& exists(Input first, Input last) { + range_check("EXISTS", first, last); + + return command(cmd::exists_range, first, last); + } + + template + QueuedRedis& exists(std::initializer_list il) { + return exists(il.begin(), il.end()); + } + + QueuedRedis& expire(const StringView &key, long long timeout) { + return command(cmd::expire, key, timeout); + } + + QueuedRedis& expire(const StringView &key, + const std::chrono::seconds &timeout) { + return expire(key, timeout.count()); + } + + QueuedRedis& expireat(const StringView &key, long long timestamp) { + return command(cmd::expireat, key, timestamp); + } + + QueuedRedis& expireat(const StringView &key, + const std::chrono::time_point &tp) { + return expireat(key, tp.time_since_epoch().count()); + } + + QueuedRedis& keys(const StringView &pattern) { + return command(cmd::keys, pattern); + } + + QueuedRedis& move(const StringView &key, long long db) { + return command(cmd::move, key, db); + } + + QueuedRedis& persist(const StringView &key) { + return command(cmd::persist, key); + } + + QueuedRedis& pexpire(const StringView &key, long long timeout) { + return command(cmd::pexpire, key, timeout); + } + + QueuedRedis& pexpire(const StringView &key, + const std::chrono::milliseconds &timeout) { + return pexpire(key, timeout.count()); + } + + QueuedRedis& pexpireat(const StringView &key, long long timestamp) { + return command(cmd::pexpireat, key, timestamp); + } + + QueuedRedis& pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return pexpireat(key, tp.time_since_epoch().count()); + } + + QueuedRedis& pttl(const StringView &key) { + return command(cmd::pttl, key); + } + + QueuedRedis& randomkey() { + return command(cmd::randomkey); + } + + QueuedRedis& rename(const StringView &key, const StringView &newkey) { + return command(cmd::rename, key, newkey); + } + + QueuedRedis& renamenx(const StringView &key, const StringView &newkey) { + return command(cmd::renamenx, key, newkey); + } + + QueuedRedis& restore(const StringView &key, + const StringView &val, + long long ttl, + bool replace = false) { + return command(cmd::restore, key, val, ttl, replace); + } + + QueuedRedis& restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds{0}, + bool replace = false) { + return restore(key, val, ttl.count(), replace); + } + + // TODO: sort + + QueuedRedis& scan(long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::scan, cursor, pattern, count); + } + + QueuedRedis& scan(long long cursor) { + return scan(cursor, "*", 10); + } + + QueuedRedis& scan(long long cursor, + const StringView &pattern) { + return scan(cursor, pattern, 10); + } + + QueuedRedis& scan(long long cursor, + long long count) { + return scan(cursor, "*", count); + } + + QueuedRedis& touch(const StringView &key) { + return command(cmd::touch, key); + } + + template + QueuedRedis& touch(Input first, Input last) { + range_check("TOUCH", first, last); + + return command(cmd::touch_range, first, last); + } + + template + QueuedRedis& touch(std::initializer_list il) { + return touch(il.begin(), il.end()); + } + + QueuedRedis& ttl(const StringView &key) { + return command(cmd::ttl, key); + } + + QueuedRedis& type(const StringView &key) { + return command(cmd::type, key); + } + + QueuedRedis& unlink(const StringView &key) { + return command(cmd::unlink, key); + } + + template + QueuedRedis& unlink(Input first, Input last) { + range_check("UNLINK", first, last); + + return command(cmd::unlink_range, first, last); + } + + template + QueuedRedis& unlink(std::initializer_list il) { + return unlink(il.begin(), il.end()); + } + + QueuedRedis& wait(long long numslaves, long long timeout) { + return command(cmd::wait, numslaves, timeout); + } + + QueuedRedis& wait(long long numslaves, const std::chrono::milliseconds &timeout) { + return wait(numslaves, timeout.count()); + } + + // STRING commands. + + QueuedRedis& append(const StringView &key, const StringView &str) { + return command(cmd::append, key, str); + } + + QueuedRedis& bitcount(const StringView &key, + long long start = 0, + long long end = -1) { + return command(cmd::bitcount, key, start, end); + } + + QueuedRedis& bitop(BitOp op, + const StringView &destination, + const StringView &key) { + return command(cmd::bitop, op, destination, key); + } + + template + QueuedRedis& bitop(BitOp op, + const StringView &destination, + Input first, + Input last) { + range_check("BITOP", first, last); + + return command(cmd::bitop_range, op, destination, first, last); + } + + template + QueuedRedis& bitop(BitOp op, + const StringView &destination, + std::initializer_list il) { + return bitop(op, destination, il.begin(), il.end()); + } + + QueuedRedis& bitpos(const StringView &key, + long long bit, + long long start = 0, + long long end = -1) { + return command(cmd::bitpos, key, bit, start, end); + } + + QueuedRedis& decr(const StringView &key) { + return command(cmd::decr, key); + } + + QueuedRedis& decrby(const StringView &key, long long decrement) { + return command(cmd::decrby, key, decrement); + } + + QueuedRedis& get(const StringView &key) { + return command(cmd::get, key); + } + + QueuedRedis& getbit(const StringView &key, long long offset) { + return command(cmd::getbit, key, offset); + } + + QueuedRedis& getrange(const StringView &key, long long start, long long end) { + return command(cmd::getrange, key, start, end); + } + + QueuedRedis& getset(const StringView &key, const StringView &val) { + return command(cmd::getset, key, val); + } + + QueuedRedis& incr(const StringView &key) { + return command(cmd::incr, key); + } + + QueuedRedis& incrby(const StringView &key, long long increment) { + return command(cmd::incrby, key, increment); + } + + QueuedRedis& incrbyfloat(const StringView &key, double increment) { + return command(cmd::incrbyfloat, key, increment); + } + + template + QueuedRedis& mget(Input first, Input last) { + range_check("MGET", first, last); + + return command(cmd::mget, first, last); + } + + template + QueuedRedis& mget(std::initializer_list il) { + return mget(il.begin(), il.end()); + } + + template + QueuedRedis& mset(Input first, Input last) { + range_check("MSET", first, last); + + return command(cmd::mset, first, last); + } + + template + QueuedRedis& mset(std::initializer_list il) { + return mset(il.begin(), il.end()); + } + + template + QueuedRedis& msetnx(Input first, Input last) { + range_check("MSETNX", first, last); + + return command(cmd::msetnx, first, last); + } + + template + QueuedRedis& msetnx(std::initializer_list il) { + return msetnx(il.begin(), il.end()); + } + + QueuedRedis& psetex(const StringView &key, + long long ttl, + const StringView &val) { + return command(cmd::psetex, key, ttl, val); + } + + QueuedRedis& psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val) { + return psetex(key, ttl.count(), val); + } + + QueuedRedis& set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0), + UpdateType type = UpdateType::ALWAYS) { + _set_cmd_indexes.push_back(_cmd_num); + + return command(cmd::set, key, val, ttl.count(), type); + } + + QueuedRedis& setex(const StringView &key, + long long ttl, + const StringView &val) { + return command(cmd::setex, key, ttl, val); + } + + QueuedRedis& setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val) { + return setex(key, ttl.count(), val); + } + + QueuedRedis& setnx(const StringView &key, const StringView &val) { + return command(cmd::setnx, key, val); + } + + QueuedRedis& setrange(const StringView &key, + long long offset, + const StringView &val) { + return command(cmd::setrange, key, offset, val); + } + + QueuedRedis& strlen(const StringView &key) { + return command(cmd::strlen, key); + } + + // LIST commands. + + QueuedRedis& blpop(const StringView &key, long long timeout) { + return command(cmd::blpop, key, timeout); + } + + QueuedRedis& blpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(key, timeout.count()); + } + + template + QueuedRedis& blpop(Input first, Input last, long long timeout) { + range_check("BLPOP", first, last); + + return command(cmd::blpop_range, first, last, timeout); + } + + template + QueuedRedis& blpop(std::initializer_list il, long long timeout) { + return blpop(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& blpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(first, last, timeout.count()); + } + + template + QueuedRedis& blpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(il.begin(), il.end(), timeout); + } + + QueuedRedis& brpop(const StringView &key, long long timeout) { + return command(cmd::brpop, key, timeout); + } + + QueuedRedis& brpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(key, timeout.count()); + } + + template + QueuedRedis& brpop(Input first, Input last, long long timeout) { + range_check("BRPOP", first, last); + + return command(cmd::brpop_range, first, last, timeout); + } + + template + QueuedRedis& brpop(std::initializer_list il, long long timeout) { + return brpop(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& brpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(first, last, timeout.count()); + } + + template + QueuedRedis& brpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(il.begin(), il.end(), timeout); + } + + QueuedRedis& brpoplpush(const StringView &source, + const StringView &destination, + long long timeout) { + return command(cmd::brpoplpush, source, destination, timeout); + } + + QueuedRedis& brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpoplpush(source, destination, timeout.count()); + } + + QueuedRedis& lindex(const StringView &key, long long index) { + return command(cmd::lindex, key, index); + } + + QueuedRedis& linsert(const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val) { + return command(cmd::linsert, key, position, pivot, val); + } + + QueuedRedis& llen(const StringView &key) { + return command(cmd::llen, key); + } + + QueuedRedis& lpop(const StringView &key) { + return command(cmd::lpop, key); + } + + QueuedRedis& lpush(const StringView &key, const StringView &val) { + return command(cmd::lpush, key, val); + } + + template + QueuedRedis& lpush(const StringView &key, Input first, Input last) { + range_check("LPUSH", first, last); + + return command(cmd::lpush_range, key, first, last); + } + + template + QueuedRedis& lpush(const StringView &key, std::initializer_list il) { + return lpush(key, il.begin(), il.end()); + } + + QueuedRedis& lpushx(const StringView &key, const StringView &val) { + return command(cmd::lpushx, key, val); + } + + QueuedRedis& lrange(const StringView &key, + long long start, + long long stop) { + return command(cmd::lrange, key, start, stop); + } + + QueuedRedis& lrem(const StringView &key, long long count, const StringView &val) { + return command(cmd::lrem, key, count, val); + } + + QueuedRedis& lset(const StringView &key, long long index, const StringView &val) { + return command(cmd::lset, key, index, val); + } + + QueuedRedis& ltrim(const StringView &key, long long start, long long stop) { + return command(cmd::ltrim, key, start, stop); + } + + QueuedRedis& rpop(const StringView &key) { + return command(cmd::rpop, key); + } + + QueuedRedis& rpoplpush(const StringView &source, const StringView &destination) { + return command(cmd::rpoplpush, source, destination); + } + + QueuedRedis& rpush(const StringView &key, const StringView &val) { + return command(cmd::rpush, key, val); + } + + template + QueuedRedis& rpush(const StringView &key, Input first, Input last) { + range_check("RPUSH", first, last); + + return command(cmd::rpush_range, key, first, last); + } + + template + QueuedRedis& rpush(const StringView &key, std::initializer_list il) { + return rpush(key, il.begin(), il.end()); + } + + QueuedRedis& rpushx(const StringView &key, const StringView &val) { + return command(cmd::rpushx, key, val); + } + + // HASH commands. + + QueuedRedis& hdel(const StringView &key, const StringView &field) { + return command(cmd::hdel, key, field); + } + + template + QueuedRedis& hdel(const StringView &key, Input first, Input last) { + range_check("HDEL", first, last); + + return command(cmd::hdel_range, key, first, last); + } + + template + QueuedRedis& hdel(const StringView &key, std::initializer_list il) { + return hdel(key, il.begin(), il.end()); + } + + QueuedRedis& hexists(const StringView &key, const StringView &field) { + return command(cmd::hexists, key, field); + } + + QueuedRedis& hget(const StringView &key, const StringView &field) { + return command(cmd::hget, key, field); + } + + QueuedRedis& hgetall(const StringView &key) { + return command(cmd::hgetall, key); + } + + QueuedRedis& hincrby(const StringView &key, + const StringView &field, + long long increment) { + return command(cmd::hincrby, key, field, increment); + } + + QueuedRedis& hincrbyfloat(const StringView &key, + const StringView &field, + double increment) { + return command(cmd::hincrbyfloat, key, field, increment); + } + + QueuedRedis& hkeys(const StringView &key) { + return command(cmd::hkeys, key); + } + + QueuedRedis& hlen(const StringView &key) { + return command(cmd::hlen, key); + } + + template + QueuedRedis& hmget(const StringView &key, Input first, Input last) { + range_check("HMGET", first, last); + + return command(cmd::hmget, key, first, last); + } + + template + QueuedRedis& hmget(const StringView &key, std::initializer_list il) { + return hmget(key, il.begin(), il.end()); + } + + template + QueuedRedis& hmset(const StringView &key, Input first, Input last) { + range_check("HMSET", first, last); + + return command(cmd::hmset, key, first, last); + } + + template + QueuedRedis& hmset(const StringView &key, std::initializer_list il) { + return hmset(key, il.begin(), il.end()); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::hscan, key, cursor, pattern, count); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor, + const StringView &pattern) { + return hscan(key, cursor, pattern, 10); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor, + long long count) { + return hscan(key, cursor, "*", count); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor) { + return hscan(key, cursor, "*", 10); + } + + QueuedRedis& hset(const StringView &key, const StringView &field, const StringView &val) { + return command(cmd::hset, key, field, val); + } + + QueuedRedis& hset(const StringView &key, const std::pair &item) { + return hset(key, item.first, item.second); + } + + template + auto hset(const StringView &key, Input first, Input last) + -> typename std::enable_if::value, + QueuedRedis&>::type { + range_check("HSET", first, last); + + return command(cmd::hset_range, key, first, last); + } + + template + QueuedRedis& hset(const StringView &key, std::initializer_list il) { + return hset(key, il.begin(), il.end()); + } + + QueuedRedis& hsetnx(const StringView &key, const StringView &field, const StringView &val) { + return command(cmd::hsetnx, key, field, val); + } + + QueuedRedis& hsetnx(const StringView &key, const std::pair &item) { + return hsetnx(key, item.first, item.second); + } + + QueuedRedis& hstrlen(const StringView &key, const StringView &field) { + return command(cmd::hstrlen, key, field); + } + + QueuedRedis& hvals(const StringView &key) { + return command(cmd::hvals, key); + } + + // SET commands. + + QueuedRedis& sadd(const StringView &key, const StringView &member) { + return command(cmd::sadd, key, member); + } + + template + QueuedRedis& sadd(const StringView &key, Input first, Input last) { + range_check("SADD", first, last); + + return command(cmd::sadd_range, key, first, last); + } + + template + QueuedRedis& sadd(const StringView &key, std::initializer_list il) { + return sadd(key, il.begin(), il.end()); + } + + QueuedRedis& scard(const StringView &key) { + return command(cmd::scard, key); + } + + template + QueuedRedis& sdiff(Input first, Input last) { + range_check("SDIFF", first, last); + + return command(cmd::sdiff, first, last); + } + + template + QueuedRedis& sdiff(std::initializer_list il) { + return sdiff(il.begin(), il.end()); + } + + QueuedRedis& sdiffstore(const StringView &destination, const StringView &key) { + return command(cmd::sdiffstore, destination, key); + } + + template + QueuedRedis& sdiffstore(const StringView &destination, + Input first, + Input last) { + range_check("SDIFFSTORE", first, last); + + return command(cmd::sdiffstore_range, destination, first, last); + } + + template + QueuedRedis& sdiffstore(const StringView &destination, std::initializer_list il) { + return sdiffstore(destination, il.begin(), il.end()); + } + + template + QueuedRedis& sinter(Input first, Input last) { + range_check("SINTER", first, last); + + return command(cmd::sinter, first, last); + } + + template + QueuedRedis& sinter(std::initializer_list il) { + return sinter(il.begin(), il.end()); + } + + QueuedRedis& sinterstore(const StringView &destination, const StringView &key) { + return command(cmd::sinterstore, destination, key); + } + + template + QueuedRedis& sinterstore(const StringView &destination, + Input first, + Input last) { + range_check("SINTERSTORE", first, last); + + return command(cmd::sinterstore_range, destination, first, last); + } + + template + QueuedRedis& sinterstore(const StringView &destination, std::initializer_list il) { + return sinterstore(destination, il.begin(), il.end()); + } + + QueuedRedis& sismember(const StringView &key, const StringView &member) { + return command(cmd::sismember, key, member); + } + + QueuedRedis& smembers(const StringView &key) { + return command(cmd::smembers, key); + } + + QueuedRedis& smove(const StringView &source, + const StringView &destination, + const StringView &member) { + return command(cmd::smove, source, destination, member); + } + + QueuedRedis& spop(const StringView &key) { + return command(cmd::spop, key); + } + + QueuedRedis& spop(const StringView &key, long long count) { + return command(cmd::spop_range, key, count); + } + + QueuedRedis& srandmember(const StringView &key) { + return command(cmd::srandmember, key); + } + + QueuedRedis& srandmember(const StringView &key, long long count) { + return command(cmd::srandmember_range, key, count); + } + + QueuedRedis& srem(const StringView &key, const StringView &member) { + return command(cmd::srem, key, member); + } + + template + QueuedRedis& srem(const StringView &key, Input first, Input last) { + range_check("SREM", first, last); + + return command(cmd::srem_range, key, first, last); + } + + template + QueuedRedis& srem(const StringView &key, std::initializer_list il) { + return srem(key, il.begin(), il.end()); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::sscan, key, cursor, pattern, count); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor, + const StringView &pattern) { + return sscan(key, cursor, pattern, 10); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor, + long long count) { + return sscan(key, cursor, "*", count); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor) { + return sscan(key, cursor, "*", 10); + } + + template + QueuedRedis& sunion(Input first, Input last) { + range_check("SUNION", first, last); + + return command(cmd::sunion, first, last); + } + + template + QueuedRedis& sunion(std::initializer_list il) { + return sunion(il.begin(), il.end()); + } + + QueuedRedis& sunionstore(const StringView &destination, const StringView &key) { + return command(cmd::sunionstore, destination, key); + } + + template + QueuedRedis& sunionstore(const StringView &destination, Input first, Input last) { + range_check("SUNIONSTORE", first, last); + + return command(cmd::sunionstore_range, destination, first, last); + } + + template + QueuedRedis& sunionstore(const StringView &destination, std::initializer_list il) { + return sunionstore(destination, il.begin(), il.end()); + } + + // SORTED SET commands. + + QueuedRedis& bzpopmax(const StringView &key, long long timeout) { + return command(cmd::bzpopmax, key, timeout); + } + + QueuedRedis& bzpopmax(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmax(key, timeout.count()); + } + + template + QueuedRedis& bzpopmax(Input first, Input last, long long timeout) { + range_check("BZPOPMAX", first, last); + + return command(cmd::bzpopmax_range, first, last, timeout); + } + + template + QueuedRedis& bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmax(first, last, timeout.count()); + } + + template + QueuedRedis& bzpopmax(std::initializer_list il, long long timeout) { + return bzpopmax(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& bzpopmax(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmax(il.begin(), il.end(), timeout); + } + + QueuedRedis& bzpopmin(const StringView &key, long long timeout) { + return command(cmd::bzpopmin, key, timeout); + } + + QueuedRedis& bzpopmin(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmin(key, timeout.count()); + } + + template + QueuedRedis& bzpopmin(Input first, Input last, long long timeout) { + range_check("BZPOPMIN", first, last); + + return command(cmd::bzpopmin_range, first, last, timeout); + } + + template + QueuedRedis& bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmin(first, last, timeout.count()); + } + + template + QueuedRedis& bzpopmin(std::initializer_list il, long long timeout) { + return bzpopmin(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& bzpopmin(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmin(il.begin(), il.end(), timeout); + } + + // We don't support the INCR option, since you can always use ZINCRBY instead. + QueuedRedis& zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return command(cmd::zadd, key, member, score, type, changed); + } + + template + QueuedRedis& zadd(const StringView &key, + Input first, + Input last, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + range_check("ZADD", first, last); + + return command(cmd::zadd_range, key, first, last, type, changed); + } + + QueuedRedis& zcard(const StringView &key) { + return command(cmd::zcard, key); + } + + template + QueuedRedis& zcount(const StringView &key, const Interval &interval) { + return command(cmd::zcount, key, interval); + } + + QueuedRedis& zincrby(const StringView &key, double increment, const StringView &member) { + return command(cmd::zincrby, key, increment, member); + } + + QueuedRedis& zinterstore(const StringView &destination, + const StringView &key, + double weight) { + return command(cmd::zinterstore, destination, key, weight); + } + + template + QueuedRedis& zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM) { + range_check("ZINTERSTORE", first, last); + + return command(cmd::zinterstore_range, destination, first, last, type); + } + + template + QueuedRedis& zinterstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zinterstore(destination, il.begin(), il.end(), type); + } + + template + QueuedRedis& zlexcount(const StringView &key, const Interval &interval) { + return command(cmd::zlexcount, key, interval); + } + + QueuedRedis& zpopmax(const StringView &key) { + _empty_array_cmd_indexes.push_back(_cmd_num); + + return command(cmd::zpopmax, key, 1); + } + + QueuedRedis& zpopmax(const StringView &key, long long count) { + return command(cmd::zpopmax, key, count); + } + + QueuedRedis& zpopmin(const StringView &key) { + _empty_array_cmd_indexes.push_back(_cmd_num); + + return command(cmd::zpopmin, key, 1); + } + + QueuedRedis& zpopmin(const StringView &key, long long count) { + return command(cmd::zpopmin, key, count); + } + + // NOTE: *QueuedRedis::zrange*'s parameters are different from *Redis::zrange*. + // *Redis::zrange* is overloaded by the output iterator, however, there's no such + // iterator in *QueuedRedis::zrange*. So we have to use an extra parameter: *with_scores*, + // to decide whether we should send *WITHSCORES* option to Redis. This also applies to + // other commands with the *WITHSCORES* option, e.g. *ZRANGEBYSCORE*, *ZREVRANGE*, + // *ZREVRANGEBYSCORE*. + QueuedRedis& zrange(const StringView &key, + long long start, + long long stop, + bool with_scores = false) { + return command(cmd::zrange, key, start, stop, with_scores); + } + + template + QueuedRedis& zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + return command(cmd::zrangebylex, key, interval, opts); + } + + template + QueuedRedis& zrangebylex(const StringView &key, const Interval &interval) { + return zrangebylex(key, interval, {}); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores = false) { + return command(cmd::zrangebyscore, key, interval, opts, with_scores); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrangebyscore(const StringView &key, + const Interval &interval, + bool with_scores = false) { + return zrangebyscore(key, interval, {}, with_scores); + } + + QueuedRedis& zrank(const StringView &key, const StringView &member) { + return command(cmd::zrank, key, member); + } + + QueuedRedis& zrem(const StringView &key, const StringView &member) { + return command(cmd::zrem, key, member); + } + + template + QueuedRedis& zrem(const StringView &key, Input first, Input last) { + range_check("ZREM", first, last); + + return command(cmd::zrem_range, key, first, last); + } + + template + QueuedRedis& zrem(const StringView &key, std::initializer_list il) { + return zrem(key, il.begin(), il.end()); + } + + template + QueuedRedis& zremrangebylex(const StringView &key, const Interval &interval) { + return command(cmd::zremrangebylex, key, interval); + } + + QueuedRedis& zremrangebyrank(const StringView &key, long long start, long long stop) { + return command(cmd::zremrangebyrank, key, start, stop); + } + + template + QueuedRedis& zremrangebyscore(const StringView &key, const Interval &interval) { + return command(cmd::zremrangebyscore, key, interval); + } + + // See comments on *ZRANGE*. + QueuedRedis& zrevrange(const StringView &key, + long long start, + long long stop, + bool with_scores = false) { + return command(cmd::zrevrange, key, start, stop, with_scores); + } + + template + QueuedRedis& zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + return command(cmd::zrevrangebylex, key, interval, opts); + } + + template + QueuedRedis& zrevrangebylex(const StringView &key, const Interval &interval) { + return zrevrangebylex(key, interval, {}); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores = false) { + return command(cmd::zrevrangebyscore, key, interval, opts, with_scores); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrevrangebyscore(const StringView &key, + const Interval &interval, + bool with_scores = false) { + return zrevrangebyscore(key, interval, {}, with_scores); + } + + QueuedRedis& zrevrank(const StringView &key, const StringView &member) { + return command(cmd::zrevrank, key, member); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::zscan, key, cursor, pattern, count); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor, + const StringView &pattern) { + return zscan(key, cursor, pattern, 10); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor, + long long count) { + return zscan(key, cursor, "*", count); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor) { + return zscan(key, cursor, "*", 10); + } + + QueuedRedis& zscore(const StringView &key, const StringView &member) { + return command(cmd::zscore, key, member); + } + + QueuedRedis& zunionstore(const StringView &destination, + const StringView &key, + double weight) { + return command(cmd::zunionstore, destination, key, weight); + } + + template + QueuedRedis& zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM) { + range_check("ZUNIONSTORE", first, last); + + return command(cmd::zunionstore_range, destination, first, last, type); + } + + template + QueuedRedis& zunionstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zunionstore(destination, il.begin(), il.end(), type); + } + + // HYPERLOGLOG commands. + + QueuedRedis& pfadd(const StringView &key, const StringView &element) { + return command(cmd::pfadd, key, element); + } + + template + QueuedRedis& pfadd(const StringView &key, Input first, Input last) { + range_check("PFADD", first, last); + + return command(cmd::pfadd_range, key, first, last); + } + + template + QueuedRedis& pfadd(const StringView &key, std::initializer_list il) { + return pfadd(key, il.begin(), il.end()); + } + + QueuedRedis& pfcount(const StringView &key) { + return command(cmd::pfcount, key); + } + + template + QueuedRedis& pfcount(Input first, Input last) { + range_check("PFCOUNT", first, last); + + return command(cmd::pfcount_range, first, last); + } + + template + QueuedRedis& pfcount(std::initializer_list il) { + return pfcount(il.begin(), il.end()); + } + + QueuedRedis& pfmerge(const StringView &destination, const StringView &key) { + return command(cmd::pfmerge, destination, key); + } + + template + QueuedRedis& pfmerge(const StringView &destination, Input first, Input last) { + range_check("PFMERGE", first, last); + + return command(cmd::pfmerge_range, destination, first, last); + } + + template + QueuedRedis& pfmerge(const StringView &destination, std::initializer_list il) { + return pfmerge(destination, il.begin(), il.end()); + } + + // GEO commands. + + QueuedRedis& geoadd(const StringView &key, + const std::tuple &member) { + return command(cmd::geoadd, key, member); + } + + template + QueuedRedis& geoadd(const StringView &key, + Input first, + Input last) { + range_check("GEOADD", first, last); + + return command(cmd::geoadd_range, key, first, last); + } + + template + QueuedRedis& geoadd(const StringView &key, std::initializer_list il) { + return geoadd(key, il.begin(), il.end()); + } + + QueuedRedis& geodist(const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit = GeoUnit::M) { + return command(cmd::geodist, key, member1, member2, unit); + } + + // Call reply::parse_leniently to parse the reply. + QueuedRedis& geohash(const StringView &key, const StringView &member) { + return command(cmd::geohash, key, member); + } + + template + QueuedRedis& geohash(const StringView &key, Input first, Input last) { + range_check("GEOHASH", first, last); + + return command(cmd::geohash_range, key, first, last); + } + + template + QueuedRedis& geohash(const StringView &key, std::initializer_list il) { + return geohash(key, il.begin(), il.end()); + } + + // Call reply::parse_leniently to parse the reply. + QueuedRedis& geopos(const StringView &key, const StringView &member) { + return command(cmd::geopos, key, member); + } + + template + QueuedRedis& geopos(const StringView &key, Input first, Input last) { + range_check("GEOPOS", first, last); + + return command(cmd::geopos_range, key, first, last); + } + + template + QueuedRedis& geopos(const StringView &key, std::initializer_list il) { + return geopos(key, il.begin(), il.end()); + } + + // TODO: + // 1. since we have different overloads for georadius and georadius-store, + // we might use the GEORADIUS_RO command in the future. + // 2. there're too many parameters for this method, we might refactor it. + QueuedRedis& georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count) { + _empty_array_cmd_indexes.push_back(_cmd_num); + + return command(cmd::georadius_store, + key, + loc, + radius, + unit, + destination, + store_dist, + count); + } + + // NOTE: *QueuedRedis::georadius*'s parameters are different from *Redis::georadius*. + // *Redis::georadius* is overloaded by the output iterator, however, there's no such + // iterator in *QueuedRedis::georadius*. So we have to use extra parameters to decide + // whether we should send options to Redis. This also applies to *GEORADIUSBYMEMBER*. + QueuedRedis& georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash) { + return command(cmd::georadius, + key, + loc, + radius, + unit, + count, + asc, + with_coord, + with_dist, + with_hash); + } + + QueuedRedis& georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count) { + _empty_array_cmd_indexes.push_back(_cmd_num); + + return command(cmd::georadiusbymember, + key, + member, + radius, + unit, + destination, + store_dist, + count); + } + + // See the comments on *GEORADIUS*. + QueuedRedis& georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash) { + return command(cmd::georadiusbymember, + key, + member, + radius, + unit, + count, + asc, + with_coord, + with_dist, + with_hash); + } + + // SCRIPTING commands. + + template + QueuedRedis& eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + return command(cmd::eval, script, keys_first, keys_last, args_first, args_last); + } + + QueuedRedis& eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return eval(script, keys.begin(), keys.end(), args.begin(), args.end()); + } + + template + QueuedRedis& evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + return command(cmd::evalsha, script, keys_first, keys_last, args_first, args_last); + } + + QueuedRedis& evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return evalsha(script, keys.begin(), keys.end(), args.begin(), args.end()); + } + + // Call reply::parse_leniently to parse the reply. + QueuedRedis& script_exists(const StringView &sha1) { + return command(cmd::script_exists, sha1); + } + + template + QueuedRedis& script_exists(Input first, Input last) { + range_check("SCRIPT EXISTS", first, last); + + return command(cmd::script_exists_range, first, last); + } + + template + QueuedRedis& script_exists(std::initializer_list il) { + return script_exists(il.begin(), il.end()); + } + + QueuedRedis& script_flush() { + return command(cmd::script_flush); + } + + QueuedRedis& script_kill() { + return command(cmd::script_kill); + } + + QueuedRedis& script_load(const StringView &script) { + return command(cmd::script_load, script); + } + + // PUBSUB commands. + + QueuedRedis& publish(const StringView &channel, const StringView &message) { + return command(cmd::publish, channel, message); + } + + // Stream commands. + + QueuedRedis& xack(const StringView &key, const StringView &group, const StringView &id) { + return command(cmd::xack, key, group, id); + } + + template + QueuedRedis& xack(const StringView &key, const StringView &group, Input first, Input last) { + range_check("XACK", first, last); + + return command(cmd::xack_range, key, group, first, last); + } + + template + QueuedRedis& xack(const StringView &key, const StringView &group, std::initializer_list il) { + return xack(key, group, il.begin(), il.end()); + } + + template + QueuedRedis& xadd(const StringView &key, const StringView &id, Input first, Input last) { + range_check("XADD", first, last); + + return command(cmd::xadd_range, key, id, first, last); + } + + template + QueuedRedis& xadd(const StringView &key, const StringView &id, std::initializer_list il) { + return xadd(key, id, il.begin(), il.end()); + } + + template + QueuedRedis& xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx = true) { + range_check("XADD", first, last); + + return command(cmd::xadd_maxlen_range, key, id, first, last, count, approx); + } + + template + QueuedRedis& xadd(const StringView &key, + const StringView &id, + std::initializer_list il, + long long count, + bool approx = true) { + return xadd(key, id, il.begin(), il.end(), count, approx); + } + + QueuedRedis& xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id) { + return command(cmd::xclaim, key, group, consumer, min_idle_time.count(), id); + } + + template + QueuedRedis& xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last) { + range_check("XCLAIM", first, last); + + return command(cmd::xclaim_range, + key, + group, + consumer, + min_idle_time.count(), + first, + last); + } + + template + QueuedRedis& xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + std::initializer_list il) { + return xclaim(key, group, consumer, min_idle_time, il.begin(), il.end()); + } + + QueuedRedis& xdel(const StringView &key, const StringView &id) { + return command(cmd::xdel, key, id); + } + + template + QueuedRedis& xdel(const StringView &key, Input first, Input last) { + range_check("XDEL", first, last); + + return command(cmd::xdel_range, key, first, last); + } + + template + QueuedRedis& xdel(const StringView &key, std::initializer_list il) { + return xdel(key, il.begin(), il.end()); + } + + QueuedRedis& xgroup_create(const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream = false) { + return command(cmd::xgroup_create, key, group, id, mkstream); + } + + QueuedRedis& xgroup_setid(const StringView &key, + const StringView &group, + const StringView &id) { + return command(cmd::xgroup_setid, key, group, id); + } + + QueuedRedis& xgroup_destroy(const StringView &key, const StringView &group) { + return command(cmd::xgroup_destroy, key, group); + } + + QueuedRedis& xgroup_delconsumer(const StringView &key, + const StringView &group, + const StringView &consumer) { + return command(cmd::xgroup_delconsumer, key, group, consumer); + } + + QueuedRedis& xlen(const StringView &key) { + return command(cmd::xlen, key); + } + + QueuedRedis& xpending(const StringView &key, const StringView &group) { + return command(cmd::xpending, key, group); + } + + QueuedRedis& xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count) { + return command(cmd::xpending_detail, key, group, start, end, count); + } + + QueuedRedis& xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer) { + return command(cmd::xpending_per_consumer, key, group, start, end, count, consumer); + } + + QueuedRedis& xrange(const StringView &key, + const StringView &start, + const StringView &end) { + return command(cmd::xrange, key, start, end); + } + + QueuedRedis& xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count) { + return command(cmd::xrange_count, key, start, end, count); + } + + QueuedRedis& xread(const StringView &key, const StringView &id, long long count) { + return command(cmd::xread, key, id, count); + } + + QueuedRedis& xread(const StringView &key, const StringView &id) { + return xread(key, id, 0); + } + + template + auto xread(Input first, Input last, long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + range_check("XREAD", first, last); + + return command(cmd::xread_range, first, last, count); + } + + template + auto xread(Input first, Input last) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xread(first, last, 0); + } + + QueuedRedis& xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count) { + return command(cmd::xread_block, key, id, timeout.count(), count); + } + + QueuedRedis& xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout) { + return xread(key, id, timeout, 0); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + range_check("XREAD", first, last); + + return command(cmd::xread_block_range, first, last, timeout.count(), count); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xread(first, last, timeout, 0); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack) { + return command(cmd::xreadgroup, group, consumer, key, id, count, noack); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count) { + return xreadgroup(group, consumer, key, id, count, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack) + -> typename std::enable_if::value, + QueuedRedis&>::type { + range_check("XREADGROUP", first, last); + + return command(cmd::xreadgroup_range, group, consumer, first, last, count, noack); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first ,last, count, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first ,last, 0, false); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack) { + return command(cmd::xreadgroup_block, + group, + consumer, + key, + id, + timeout.count(), + count, + noack); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count) { + return xreadgroup(group, consumer, key, id, timeout, count, false); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout) { + return xreadgroup(group, consumer, key, id, timeout, 0, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack) + -> typename std::enable_if::value, + QueuedRedis&>::type { + range_check("XREADGROUP", first, last); + + return command(cmd::xreadgroup_block_range, + group, + consumer, + first, + last, + timeout.count(), + count, + noack); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first, last, timeout, count, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first, last, timeout, 0, false); + } + + QueuedRedis& xrevrange(const StringView &key, + const StringView &end, + const StringView &start) { + return command(cmd::xrevrange, key, end, start); + } + + QueuedRedis& xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count) { + return command(cmd::xrevrange_count, key, end, start, count); + } + + QueuedRedis& xtrim(const StringView &key, long long count, bool approx = true) { + return command(cmd::xtrim, key, count, approx); + } + +private: + friend class Redis; + + friend class RedisCluster; + + template + QueuedRedis(const ConnectionPoolSPtr &pool, bool new_connection, Args &&...args); + + Connection& _connection(); + + void _sanity_check(); + + void _reset(bool reset_connection = true); + + void _return_connection(); + + void _invalidate(); + + void _clean_up(); + + void _rewrite_replies(std::vector &replies) const; + + template + void _rewrite_replies(const std::vector &indexes, + Func rewriter, + std::vector &replies) const; + + GuardedConnectionSPtr _guarded_connection; + + ConnectionPoolSPtr _connection_pool; + + bool _new_connection = true; + + Impl _impl; + + std::size_t _cmd_num = 0; + + std::vector _set_cmd_indexes; + + std::vector _empty_array_cmd_indexes; + + bool _valid = true; +}; + +class QueuedReplies { +public: + QueuedReplies() = default; + + QueuedReplies(const QueuedReplies &) = delete; + QueuedReplies& operator=(const QueuedReplies &) = delete; + + QueuedReplies(QueuedReplies &&) = default; + QueuedReplies& operator=(QueuedReplies &&) = default; + + ~QueuedReplies() = default; + + std::size_t size() const; + + redisReply& get(std::size_t idx); + + template + Result get(std::size_t idx); + + template + void get(std::size_t idx, Output output); + +private: + template + friend class QueuedRedis; + + explicit QueuedReplies(std::vector replies) : _replies(std::move(replies)) {} + + void _index_check(std::size_t idx) const; + + std::vector _replies; +}; + +} + +} + +#include "queued_redis.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/queued_redis.hpp b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/queued_redis.hpp new file mode 100644 index 000000000..d2865e46e --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/queued_redis.hpp @@ -0,0 +1,275 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP +#define SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP + +namespace sw { + +namespace redis { + +template +template +QueuedRedis::QueuedRedis(const ConnectionPoolSPtr &pool, + bool new_connection, + Args &&...args) : + _new_connection(new_connection), + _impl(std::forward(args)...) { + assert(pool); + + if (_new_connection) { + _connection_pool = std::make_shared(pool->clone()); + } else { + // Create a connection from the origin pool. + _connection_pool = pool; + } +} + +template +QueuedRedis::~QueuedRedis() { + try { + _clean_up(); + } catch (const Error &e) { + // Ensure the destructor does not throw + } +} + +template +Redis QueuedRedis::redis() { + _sanity_check(); + + assert(_guarded_connection); + + return Redis(_guarded_connection); +} + +template +template +auto QueuedRedis::command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, + QueuedRedis&>::type { + try { + _sanity_check(); + + _impl.command(_connection(), cmd, std::forward(args)...); + + ++_cmd_num; + } catch (const Error &e) { + _invalidate(); + throw; + } + + return *this; +} + +template +template +QueuedRedis& QueuedRedis::command(const StringView &cmd_name, Args &&...args) { + auto cmd = [](Connection &connection, const StringView &cmd_name, Args &&...args) { + CmdArgs cmd_args; + cmd_args.append(cmd_name, std::forward(args)...); + connection.send(cmd_args); + }; + + return command(cmd, cmd_name, std::forward(args)...); +} + +template +template +auto QueuedRedis::command(Input first, Input last) + -> typename std::enable_if::value, QueuedRedis&>::type { + if (first == last) { + throw Error("command: empty range"); + } + + auto cmd = [](Connection &connection, Input first, Input last) { + CmdArgs cmd_args; + while (first != last) { + cmd_args.append(*first); + ++first; + } + connection.send(cmd_args); + }; + + return command(cmd, first, last); +} + +template +QueuedReplies QueuedRedis::exec() { + try { + _sanity_check(); + + auto replies = _impl.exec(_connection(), _cmd_num); + + _rewrite_replies(replies); + + _reset(); + + return QueuedReplies(std::move(replies)); + } catch (const WatchError &e) { + // In this case, we only clear some states and keep the connection, + // so that user can retry the transaction. + _reset(false); + throw; + } catch (const Error &e) { + _invalidate(); + throw; + } +} + +template +void QueuedRedis::discard() { + try { + _sanity_check(); + + _impl.discard(_connection(), _cmd_num); + + _reset(); + } catch (const Error &e) { + _invalidate(); + throw; + } +} + +template +Connection& QueuedRedis::_connection() { + assert(_valid); + + if (!_guarded_connection) { + _guarded_connection = std::make_shared(_connection_pool); + } + + return _guarded_connection->connection(); +} + +template +void QueuedRedis::_sanity_check() { + if (!_valid) { + throw Error("Not in valid state"); + } + + if (_connection().broken()) { + throw Error("Connection is broken"); + } +} + +template +inline void QueuedRedis::_reset(bool reset_connection) { + if (reset_connection && !_new_connection) { + _return_connection(); + } + + _cmd_num = 0; + + _set_cmd_indexes.clear(); + + _empty_array_cmd_indexes.clear(); +} + +template +inline void QueuedRedis::_return_connection() { + if (_guarded_connection.use_count() == 1) { + // If no one else holding the connection, return it back to pool. + // Instead, if some other `Redis` object holds the connection, + // e.g. `auto redis = transaction.redis();`, we cannot return the connection. + _guarded_connection.reset(); + } +} + +template +void QueuedRedis::_invalidate() { + _valid = false; + + _clean_up(); + + _reset(); +} + +template +void QueuedRedis::_clean_up() { + if (_guarded_connection && !_new_connection) { + // Something bad happened, we need to close the current connection + // before returning it back to pool. + _guarded_connection->connection().invalidate(); + } +} + +template +void QueuedRedis::_rewrite_replies(std::vector &replies) const { + _rewrite_replies(_set_cmd_indexes, reply::rewrite_set_reply, replies); + + _rewrite_replies(_empty_array_cmd_indexes, reply::rewrite_empty_array_reply, replies); +} + +template +template +void QueuedRedis::_rewrite_replies(const std::vector &indexes, + Func rewriter, + std::vector &replies) const { + for (auto idx : indexes) { + assert(idx < replies.size()); + + auto &reply = replies[idx]; + + assert(reply); + + rewriter(*reply); + } +} + +inline std::size_t QueuedReplies::size() const { + return _replies.size(); +} + +inline redisReply& QueuedReplies::get(std::size_t idx) { + _index_check(idx); + + auto &reply = _replies[idx]; + + assert(reply); + + if (reply::is_error(*reply)) { + throw_error(*reply); + } + + return *reply; +} + +template +inline Result QueuedReplies::get(std::size_t idx) { + auto &reply = get(idx); + + return reply::parse(reply); +} + +template +inline void QueuedReplies::get(std::size_t idx, Output output) { + auto &reply = get(idx); + + reply::to_array(reply, output); +} + +inline void QueuedReplies::_index_check(std::size_t idx) const { + if (idx >= size()) { + throw Error("Out of range"); + } +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/redis++.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/redis++.h new file mode 100644 index 000000000..0da0ebb16 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/redis++.h @@ -0,0 +1,25 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H +#define SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H + +#include "redis.h" +#include "redis_cluster.h" +#include "queued_redis.h" +#include "sentinel.h" + +#endif // end SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/redis.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/redis.h new file mode 100644 index 000000000..1333e6b68 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/redis.h @@ -0,0 +1,3631 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_REDIS_H +#define SEWENEW_REDISPLUSPLUS_REDIS_H + +#include +#include +#include +#include +#include +#include "connection_pool.h" +#include "reply.h" +#include "command_options.h" +#include "utils.h" +#include "subscriber.h" +#include "pipeline.h" +#include "transaction.h" +#include "sentinel.h" + +namespace sw { + +namespace redis { + +template +class QueuedRedis; + +using Transaction = QueuedRedis; + +using Pipeline = QueuedRedis; + +class Redis { +public: + /// @brief Construct `Redis` instance with connection options and connection pool options. + /// @param connection_opts Connection options. + /// @param pool_opts Connection pool options. + /// @see `ConnectionOptions` + /// @see `ConnectionPoolOptions` + /// @see https://github.com/sewenew/redis-plus-plus#connection + explicit Redis(const ConnectionOptions &connection_opts, + const ConnectionPoolOptions &pool_opts = {}) : + _pool(std::make_shared(pool_opts, connection_opts)) {} + + /// @brief Construct `Redis` instance with URI. + /// @param uri URI, e.g. 'tcp://127.0.0.1', 'tcp://127.0.0.1:6379', or 'unix://path/to/socket'. + /// Full URI scheme: 'tcp://[[username:]password@]host[:port][/db]' or + /// unix://[[username:]password@]path-to-unix-domain-socket[/db] + /// @see https://github.com/sewenew/redis-plus-plus/issues/37 + /// @see https://github.com/sewenew/redis-plus-plus#connection + explicit Redis(const std::string &uri); + + /// @brief Construct `Redis` instance with Redis sentinel, i.e. get node info from sentinel. + /// @param sentinel `Sentinel` instance. + /// @param master_name Name of master node. + /// @param role Connect to master node or slave node. + /// - Role::MASTER: Connect to master node. + /// - Role::SLAVE: Connect to slave node. + /// @param connection_opts Connection options. + /// @param pool_opts Connection pool options. + /// @see `Sentinel` + /// @see `Role` + /// @see https://github.com/sewenew/redis-plus-plus#redis-sentinel + Redis(const std::shared_ptr &sentinel, + const std::string &master_name, + Role role, + const ConnectionOptions &connection_opts, + const ConnectionPoolOptions &pool_opts = {}) : + _pool(std::make_shared(SimpleSentinel(sentinel, master_name, role), + pool_opts, + connection_opts)) {} + + /// @brief `Redis` is not copyable. + Redis(const Redis &) = delete; + + /// @brief `Redis` is not copyable. + Redis& operator=(const Redis &) = delete; + + /// @brief `Redis` is movable. + Redis(Redis &&) = default; + + /// @brief `Redis` is movable. + Redis& operator=(Redis &&) = default; + + /// @brief Create a pipeline. + /// @param new_connection Whether creating a `Pipeline` object in a new connection. + /// @return The created pipeline. + /// @note Instead of picking a connection from the underlying connection pool, + /// this method will create a new connection to Redis. So it's not a cheap operation, + /// and you'd better reuse the returned object as much as possible. + /// @see https://github.com/sewenew/redis-plus-plus#pipeline + Pipeline pipeline(bool new_connection = true); + + /// @brief Create a transaction. + /// @param piped Whether commands in a transaction should be sent in a pipeline to reduce RTT. + /// @param new_connection Whether creating a `Pipeline` object in a new connection. + /// @return The created transaction. + /// @note Instead of picking a connection from the underlying connection pool, + /// this method will create a new connection to Redis. So it's not a cheap operation, + /// and you'd better reuse the returned object as much as possible. + /// @see https://github.com/sewenew/redis-plus-plus#transaction + Transaction transaction(bool piped = false, bool new_connection = true); + + /// @brief Create a subscriber. + /// @return The created subscriber. + /// @note Instead of picking a connection from the underlying connection pool, + /// this method will create a new connection to Redis. So it's not a cheap operation, + /// and you'd better reuse the returned object as much as possible. + /// @see https://github.com/sewenew/redis-plus-plus#publishsubscribe + Subscriber subscriber(); + + template + auto command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, + ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, void>::type; + + template + Result command(const StringView &cmd_name, Args &&...args); + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, Result>::type; + + template + auto command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type; + + // CONNECTION commands. + + /// @brief Send password to Redis. + /// @param password Password. + /// @note Normally, you should not call this method. + /// Instead, you should set password with `ConnectionOptions` or URI. + /// @see https://redis.io/commands/auth + void auth(const StringView &password); + + /// @brief Send user and password to Redis. + /// @param user User name. + /// @param password Password. + /// @note Normally, you should not call this method. + /// Instead, you should set password with `ConnectionOptions` or URI. + /// Also this overload only works with Redis 6.0 or later. + /// @see https://redis.io/commands/auth + void auth(const StringView &user, const StringView &password); + + /// @brief Ask Redis to return the given message. + /// @param msg Message to be sent. + /// @return Return the given message. + /// @see https://redis.io/commands/echo + std::string echo(const StringView &msg); + + /// @brief Test if the connection is alive. + /// @return Always return *PONG*. + /// @see https://redis.io/commands/ping + std::string ping(); + + /// @brief Test if the connection is alive. + /// @param msg Message sent to Redis. + /// @return Return the given message. + /// @see https://redis.io/commands/ping + std::string ping(const StringView &msg); + + // After sending QUIT, only the current connection will be close, while + // other connections in the pool is still open. This is a strange behavior. + // So we DO NOT support the QUIT command. If you want to quit connection to + // server, just destroy the Redis object. + // + // void quit(); + + // We get a connection from the pool, and send the SELECT command to switch + // to a specified DB. However, when we try to send other commands to the + // given DB, we might get a different connection from the pool, and these + // commands, in fact, work on other DB. e.g. + // + // redis.select(1); // get a connection from the pool and switch to the 1th DB + // redis.get("key"); // might get another connection from the pool, + // // and try to get 'key' on the default DB + // + // Obviously, this is NOT what we expect. So we DO NOT support SELECT command. + // In order to select a DB, we can specify the DB index with the ConnectionOptions. + // + // However, since Pipeline and Transaction always send multiple commands on a + // single connection, these two classes have a *select* method. + // + // void select(long long idx); + + /// @brief Swap two Redis databases. + /// @param idx1 The index of the first database. + /// @param idx2 The index of the second database. + /// @see https://redis.io/commands/swapdb + void swapdb(long long idx1, long long idx2); + + // SERVER commands. + + /// @brief Rewrite AOF in the background. + /// @see https://redis.io/commands/bgrewriteaof + void bgrewriteaof(); + + /// @brief Save database in the background. + /// @see https://redis.io/commands/bgsave + void bgsave(); + + /// @brief Get the size of the currently selected database. + /// @return Number of keys in currently selected database. + /// @see https://redis.io/commands/dbsize + long long dbsize(); + + /// @brief Remove keys of all databases. + /// @param async Whether flushing databases asynchronously, i.e. without blocking the server. + /// @see https://redis.io/commands/flushall + void flushall(bool async = false); + + /// @brief Remove keys of current databases. + /// @param async Whether flushing databases asynchronously, i.e. without blocking the server. + /// @see https://redis.io/commands/flushdb + void flushdb(bool async = false); + + /// @brief Get the info about the server. + /// @return Server info. + /// @see https://redis.io/commands/info + std::string info(); + + /// @brief Get the info about the server on the given section. + /// @param section Section. + /// @return Server info. + /// @see https://redis.io/commands/info + std::string info(const StringView §ion); + + /// @brief Get the UNIX timestamp in seconds, at which the database was saved successfully. + /// @return The last saving time. + /// @see https://redis.io/commands/lastsave + long long lastsave(); + + /// @brief Save databases into RDB file **synchronously**, i.e. block the server during saving. + /// @see https://redis.io/commands/save + void save(); + + // KEY commands. + + /// @brief Delete the given key. + /// @param key Key. + /// @return Number of keys removed. + /// @retval 1 If the key exists, and has been removed. + /// @retval 0 If the key does not exist. + /// @see https://redis.io/commands/del + long long del(const StringView &key); + + /// @brief Delete the given list of keys. + /// @param first Iterator to the first key in the range. + /// @param last Off-the-end iterator to the given range. + /// @return Number of keys removed. + /// @see https://redis.io/commands/del + template + long long del(Input first, Input last); + + /// @brief Delete the given list of keys. + /// @param il Initializer list of keys to be deleted. + /// @return Number of keys removed. + /// @see https://redis.io/commands/del + template + long long del(std::initializer_list il) { + return del(il.begin(), il.end()); + } + + /// @brief Get the serialized valued stored at key. + /// @param key Key. + /// @return The serialized value. + /// @note If key does not exist, `dump` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/dump + OptionalString dump(const StringView &key); + + /// @brief Check if the given key exists. + /// @param key Key. + /// @return Whether the given key exists. + /// @retval 1 If key exists. + /// @retval 0 If key does not exist. + /// @see https://redis.io/commands/exists + long long exists(const StringView &key); + + /// @brief Check if the given keys exist. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the given range. + /// @return Number of keys existing. + /// @see https://redis.io/commands/exists + template + long long exists(Input first, Input last); + + /// @brief Check if the given keys exist. + /// @param il Initializer list of keys to be checked. + /// @return Number of keys existing. + /// @see https://redis.io/commands/exists + template + long long exists(std::initializer_list il) { + return exists(il.begin(), il.end()); + } + + /// @brief Set a timeout on key. + /// @param key Key. + /// @param timeout Timeout in seconds. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/expire + bool expire(const StringView &key, long long timeout); + + /// @brief Set a timeout on key. + /// @param key Key. + /// @param timeout Timeout in seconds. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/expire + bool expire(const StringView &key, const std::chrono::seconds &timeout); + + /// @brief Set a timeout on key, i.e. expire the key at a future time point. + /// @param key Key. + /// @param timestamp Time in seconds since UNIX epoch. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/expireat + bool expireat(const StringView &key, long long timestamp); + + /// @brief Set a timeout on key, i.e. expire the key at a future time point. + /// @param key Key. + /// @param timestamp Time in seconds since UNIX epoch. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/expireat + bool expireat(const StringView &key, + const std::chrono::time_point &tp); + + /// @brief Get keys matching the given pattern. + /// @param pattern Pattern. + /// @param output Output iterator to the destination where the returned keys are stored. + /// @note It's always a bad idea to call `keys`, since it might block Redis for a long time, + /// especially when the data set is very big. + /// @see `Redis::scan` + /// @see https://redis.io/commands/keys + template + void keys(const StringView &pattern, Output output); + + /// @brief Move a key to the given database. + /// @param key Key. + /// @param db The destination database. + /// @return Whether key has been moved. + /// @retval true If key has been moved. + /// @retval false If key was not moved. + /// @see https://redis.io/commands/move + bool move(const StringView &key, long long db); + + /// @brief Remove timeout on key. + /// @param key Key. + /// @return Whether timeout has been removed. + /// @retval true If timeout has been removed. + /// @retval false If key does not exist, or does not have an associated timeout. + /// @see https://redis.io/commands/persist + bool persist(const StringView &key); + + /// @brief Set a timeout on key. + /// @param key Key. + /// @param timeout Timeout in milliseconds. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/pexpire + bool pexpire(const StringView &key, long long timeout); + + /// @brief Set a timeout on key. + /// @param key Key. + /// @param timeout Timeout in milliseconds. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/pexpire + bool pexpire(const StringView &key, const std::chrono::milliseconds &timeout); + + /// @brief Set a timeout on key, i.e. expire the key at a future time point. + /// @param key Key. + /// @param timestamp Time in milliseconds since UNIX epoch. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/pexpireat + bool pexpireat(const StringView &key, long long timestamp); + + /// @brief Set a timeout on key, i.e. expire the key at a future time point. + /// @param key Key. + /// @param timestamp Time in milliseconds since UNIX epoch. + /// @return Whether timeout has been set. + /// @retval true If timeout has been set. + /// @retval false If key does not exist. + /// @see https://redis.io/commands/pexpireat + bool pexpireat(const StringView &key, + const std::chrono::time_point &tp); + + /// @brief Get the TTL of a key in milliseconds. + /// @param key Key. + /// @return TTL of the key in milliseconds. + /// @see https://redis.io/commands/pttl + long long pttl(const StringView &key); + + /// @brief Get a random key from current database. + /// @return A random key. + /// @note If the database is empty, `randomkey` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/randomkey + OptionalString randomkey(); + + /// @brief Rename `key` to `newkey`. + /// @param key Key to be renamed. + /// @param newkey The new name of the key. + /// @see https://redis.io/commands/rename + void rename(const StringView &key, const StringView &newkey); + + /// @brief Rename `key` to `newkey` if `newkey` does not exist. + /// @param key Key to be renamed. + /// @param newkey The new name of the key. + /// @return Whether key has been renamed. + /// @retval true If key has been renamed. + /// @retval false If newkey already exists. + /// @see https://redis.io/commands/renamenx + bool renamenx(const StringView &key, const StringView &newkey); + + /// @brief Create a key with the value obtained by `Redis::dump`. + /// @param key Key. + /// @param val Value obtained by `Redis::dump`. + /// @param ttl Timeout of the created key in milliseconds. If `ttl` is 0, set no timeout. + /// @param replace Whether to overwrite an existing key. + /// If `replace` is `true` and key already exists, throw an exception. + /// @see https://redis.io/commands/restore + void restore(const StringView &key, + const StringView &val, + long long ttl, + bool replace = false); + + /// @brief Create a key with the value obtained by `Redis::dump`. + /// @param key Key. + /// @param val Value obtained by `Redis::dump`. + /// @param ttl Timeout of the created key in milliseconds. If `ttl` is 0, set no timeout. + /// @param replace Whether to overwrite an existing key. + /// If `replace` is `true` and key already exists, throw an exception. + /// @see https://redis.io/commands/restore + void restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds{0}, + bool replace = false); + + // TODO: sort + + /// @brief Scan keys of the database matching the given pattern. + /// + /// Example: + /// @code{.cpp} + /// auto cursor = 0LL; + /// std::unordered_set keys; + /// while (true) { + /// cursor = redis.scan(cursor, "pattern:*", 10, std::inserter(keys, keys.begin())); + /// if (cursor == 0) { + /// break; + /// } + /// } + /// @endcode + /// @param cursor Cursor. + /// @param pattern Pattern of the keys to be scanned. + /// @param count A hint for how many keys to be scanned. + /// @param output Output iterator to the destination where the returned keys are stored. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/scan + /// TODO: support the TYPE option for Redis 6.0. + template + long long scan(long long cursor, + const StringView &pattern, + long long count, + Output output); + + /// @brief Scan all keys of the database. + /// @param cursor Cursor. + /// @param output Output iterator to the destination where the returned keys are stored. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/scan + template + long long scan(long long cursor, + Output output); + + /// @brief Scan keys of the database matching the given pattern. + /// @param cursor Cursor. + /// @param pattern Pattern of the keys to be scanned. + /// @param output Output iterator to the destination where the returned keys are stored. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/scan + template + long long scan(long long cursor, + const StringView &pattern, + Output output); + + /// @brief Scan keys of the database matching the given pattern. + /// @param cursor Cursor. + /// @param count A hint for how many keys to be scanned. + /// @param output Output iterator to the destination where the returned keys are stored. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/scan + template + long long scan(long long cursor, + long long count, + Output output); + + /// @brief Update the last access time of the given key. + /// @param key Key. + /// @return Whether last access time of the key has been updated. + /// @retval 1 If key exists, and last access time has been updated. + /// @retval 0 If key does not exist. + /// @see https://redis.io/commands/touch + long long touch(const StringView &key); + + /// @brief Update the last access time of the given key. + /// @param first Iterator to the first key to be touched. + /// @param last Off-the-end iterator to the given range. + /// @return Whether last access time of the key has been updated. + /// @retval 1 If key exists, and last access time has been updated. + /// @retval 0 If key does not exist. + /// @see https://redis.io/commands/touch + template + long long touch(Input first, Input last); + + /// @brief Update the last access time of the given key. + /// @param il Initializer list of keys to be touched. + /// @return Whether last access time of the key has been updated. + /// @retval 1 If key exists, and last access time has been updated. + /// @retval 0 If key does not exist. + /// @see https://redis.io/commands/touch + template + long long touch(std::initializer_list il) { + return touch(il.begin(), il.end()); + } + + /// @brief Get the remaining Time-To-Live of a key. + /// @param key Key. + /// @return TTL in seconds. + /// @retval TTL If the key has a timeout. + /// @retval -1 If the key exists but does not have a timeout. + /// @retval -2 If the key does not exist. + /// @note In Redis 2.6 or older, `ttl` returns -1 if the key does not exist, + /// or if the key exists but does not have a timeout. + /// @see https://redis.io/commands/ttl + long long ttl(const StringView &key); + + /// @brief Get the type of the value stored at key. + /// @param key Key. + /// @return The type of the value. + /// @see https://redis.io/commands/type + std::string type(const StringView &key); + + /// @brief Remove the given key asynchronously, i.e. without blocking Redis. + /// @param key Key. + /// @return Whether the key has been removed. + /// @retval 1 If key exists, and has been removed. + /// @retval 0 If key does not exist. + /// @see https://redis.io/commands/unlink + long long unlink(const StringView &key); + + /// @brief Remove the given keys asynchronously, i.e. without blocking Redis. + /// @param first Iterator to the first key in the given range. + /// @param last Off-the-end iterator to the given range. + /// @return Number of keys that have been removed. + /// @see https://redis.io/commands/unlink + template + long long unlink(Input first, Input last); + + /// @brief Remove the given keys asynchronously, i.e. without blocking Redis. + /// @param il Initializer list of keys to be unlinked. + /// @return Number of keys that have been removed. + /// @see https://redis.io/commands/unlink + template + long long unlink(std::initializer_list il) { + return unlink(il.begin(), il.end()); + } + + /// @brief Wait until previous write commands are successfully replicated to at + /// least the specified number of replicas or the given timeout has been reached. + /// @param numslaves Number of replicas. + /// @param timeout Timeout in milliseconds. If timeout is 0ms, wait forever. + /// @return Number of replicas that have been successfully replicated these write commands. + /// @note The return value might be less than `numslaves`, because timeout has been reached. + /// @see https://redis.io/commands/wait + long long wait(long long numslaves, long long timeout); + + /// @brief Wait until previous write commands are successfully replicated to at + /// least the specified number of replicas or the given timeout has been reached. + /// @param numslaves Number of replicas. + /// @param timeout Timeout in milliseconds. If timeout is 0ms, wait forever. + /// @return Number of replicas that have been successfully replicated these write commands. + /// @note The return value might be less than `numslaves`, because timeout has been reached. + /// @see https://redis.io/commands/wait + /// TODO: Support default parameter for `timeout` to wait forever. + long long wait(long long numslaves, const std::chrono::milliseconds &timeout); + + // STRING commands. + + /// @brief Append the given string to the string stored at key. + /// @param key Key. + /// @param str String to be appended. + /// @return The length of the string after the append operation. + /// @see https://redis.io/commands/append + long long append(const StringView &key, const StringView &str); + + /// @brief Get the number of bits that have been set for the given range of the string. + /// @param key Key. + /// @param start Start index (inclusive) of the range. 0 means the beginning of the string. + /// @param end End index (inclusive) of the range. -1 means the end of the string. + /// @return Number of bits that have been set. + /// @note The index can be negative to index from the end of the string. + /// @see https://redis.io/commands/bitcount + long long bitcount(const StringView &key, long long start = 0, long long end = -1); + + /// @brief Do bit operation on the string stored at `key`, and save the result to `destination`. + /// @param op Bit operations. + /// @param destination The destination key where the result is saved. + /// @param key The key where the string to be operated is stored. + /// @return The length of the string saved at `destination`. + /// @see https://redis.io/commands/bitop + /// @see `BitOp` + long long bitop(BitOp op, const StringView &destination, const StringView &key); + + /// @brief Do bit operations on the strings stored at the given keys, + /// and save the result to `destination`. + /// @param op Bit operations. + /// @param destination The destination key where the result is saved. + /// @param first Iterator to the first key where the string to be operated is stored. + /// @param last Off-the-end iterator to the given range of keys. + /// @return The length of the string saved at `destination`. + /// @see https://redis.io/commands/bitop + /// @see `BitOp` + template + long long bitop(BitOp op, const StringView &destination, Input first, Input last); + + /// @brief Do bit operations on the strings stored at the given keys, + /// and save the result to `destination`. + /// @param op Bit operations. + /// @param destination The destination key where the result is saved. + /// @param il Initializer list of keys where the strings are operated. + /// @return The length of the string saved at `destination`. + /// @see https://redis.io/commands/bitop + /// @see `BitOp` + template + long long bitop(BitOp op, const StringView &destination, std::initializer_list il) { + return bitop(op, destination, il.begin(), il.end()); + } + + /// @brief Get the position of the first bit set to 0 or 1 in the given range of the string. + /// @param key Key. + /// @param bit 0 or 1. + /// @param start Start index (inclusive) of the range. 0 means the beginning of the string. + /// @param end End index (inclusive) of the range. -1 means the end of the string. + /// @return The position of the first bit set to 0 or 1. + /// @see https://redis.io/commands/bitpos + long long bitpos(const StringView &key, + long long bit, + long long start = 0, + long long end = -1); + + /// @brief Decrement the integer stored at key by 1. + /// @param key Key. + /// @return The value after the decrement. + /// @see https://redis.io/commands/decr + long long decr(const StringView &key); + + /// @brief Decrement the integer stored at key by `decrement`. + /// @param key Key. + /// @param decrement Decrement. + /// @return The value after the decrement. + /// @see https://redis.io/commands/decrby + long long decrby(const StringView &key, long long decrement); + + /// @brief Get the string value stored at key. + /// + /// Example: + /// @code{.cpp} + /// auto val = redis.get("key"); + /// if (val) + /// std::cout << *val << std::endl; + /// else + /// std::cout << "key not exist" << std::endl; + /// @endcode + /// @param key Key. + /// @return The value stored at key. + /// @note If key does not exist, `get` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/get + OptionalString get(const StringView &key); + + /// @brief Get the bit value at offset in the string. + /// @param key Key. + /// @param offset Offset. + /// @return The bit value. + /// @see https://redis.io/commands/getbit + long long getbit(const StringView &key, long long offset); + + /// @brief Get the substring of the string stored at key. + /// @param key Key. + /// @param start Start index (inclusive) of the range. 0 means the beginning of the string. + /// @param end End index (inclusive) of the range. -1 means the end of the string. + /// @return The substring in range [start, end]. If key does not exist, return an empty string. + /// @see https://redis.io/commands/getrange + std::string getrange(const StringView &key, long long start, long long end); + + /// @brief Atomically set the string stored at `key` to `val`, and return the old value. + /// @param key Key. + /// @param val Value to be set. + /// @return The old value stored at key. + /// @note If key does not exist, `getset` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/getset + /// @see `OptionalString` + OptionalString getset(const StringView &key, const StringView &val); + + /// @brief Increment the integer stored at key by 1. + /// @param key Key. + /// @return The value after the increment. + /// @see https://redis.io/commands/incr + long long incr(const StringView &key); + + /// @brief Increment the integer stored at key by `increment`. + /// @param key Key. + /// @param increment Increment. + /// @return The value after the increment. + /// @see https://redis.io/commands/incrby + long long incrby(const StringView &key, long long increment); + + /// @brief Increment the floating point number stored at key by `increment`. + /// @param key Key. + /// @param increment Increment. + /// @return The value after the increment. + /// @see https://redis.io/commands/incrbyfloat + double incrbyfloat(const StringView &key, double increment); + + /// @brief Get the values of multiple keys atomically. + /// + /// Example: + /// @code{.cpp} + /// std::vector keys = {"k1", "k2", "k3"}; + /// std::vector vals; + /// redis.mget(keys.begin(), keys.end(), std::back_inserter(vals)); + /// for (const auto &val : vals) { + /// if (val) + /// std::cout << *val << std::endl; + /// else + /// std::cout << "key does not exist" << std::endl; + /// } + /// @endcode + /// @param first Iterator to the first key of the given range. + /// @param last Off-the-end iterator to the given range. + /// @param output Output iterator to the destination where the values are stored. + /// @note The destination should be a container of `OptionalString` type, + /// since the given key might not exist (in this case, the value of the corresponding + /// key is `OptionalString{}` (`std::nullopt`)). + /// @see https://redis.io/commands/mget + template + void mget(Input first, Input last, Output output); + + /// @brief Get the values of multiple keys atomically. + /// + /// Example: + /// @code{.cpp} + /// std::vector vals; + /// redis.mget({"k1", "k2", "k3"}, std::back_inserter(vals)); + /// for (const auto &val : vals) { + /// if (val) + /// std::cout << *val << std::endl; + /// else + /// std::cout << "key does not exist" << std::endl; + /// } + /// @endcode + /// @param il Initializer list of keys. + /// @param output Output iterator to the destination where the values are stored. + /// @note The destination should be a container of `OptionalString` type, + /// since the given key might not exist (in this case, the value of the corresponding + /// key is `OptionalString{}` (`std::nullopt`)). + /// @see https://redis.io/commands/mget + template + void mget(std::initializer_list il, Output output) { + mget(il.begin(), il.end(), output); + } + + /// @brief Set multiple key-value pairs. + /// + /// Example: + /// @code{.cpp} + /// std::vector> kvs1 = {{"k1", "v1"}, {"k2", "v2"}}; + /// redis.mset(kvs1.begin(), kvs1.end()); + /// std::unordered_map kvs2 = {{"k3", "v3"}, {"k4", "v4"}}; + /// redis.mset(kvs2.begin(), kvs2.end()); + /// @endcode + /// @param first Iterator to the first key-value pair. + /// @param last Off-the-end iterator to the given range. + /// @see https://redis.io/commands/mset + template + void mset(Input first, Input last); + + /// @brief Set multiple key-value pairs. + /// + /// Example: + /// @code{.cpp} + /// redis.mset({std::make_pair("k1", "v1"), std::make_pair("k2", "v2")}); + /// @endcode + /// @param il Initializer list of key-value pairs. + /// @see https://redis.io/commands/mset + template + void mset(std::initializer_list il) { + mset(il.begin(), il.end()); + } + + /// @brief Set the given key-value pairs if all specified keys do not exist. + /// + /// Example: + /// @code{.cpp} + /// std::vector> kvs1; + /// redis.msetnx(kvs1.begin(), kvs1.end()); + /// std::unordered_map kvs2; + /// redis.msetnx(kvs2.begin(), kvs2.end()); + /// @endcode + /// @param first Iterator to the first key-value pair. + /// @param last Off-the-end iterator of the given range. + /// @return Whether all keys have been set. + /// @retval true If all keys have been set. + /// @retval false If no key was set, i.e. at least one key already exist. + /// @see https://redis.io/commands/msetnx + template + bool msetnx(Input first, Input last); + + /// @brief Set the given key-value pairs if all specified keys do not exist. + /// + /// Example: + /// @code{.cpp} + /// redis.msetnx({make_pair("k1", "v1"), make_pair("k2", "v2")}); + /// @endcode + /// @param il Initializer list of key-value pairs. + /// @return Whether all keys have been set. + /// @retval true If all keys have been set. + /// @retval false If no key was set, i.e. at least one key already exist. + /// @see https://redis.io/commands/msetnx + template + bool msetnx(std::initializer_list il) { + return msetnx(il.begin(), il.end()); + } + + /// @brief Set key-value pair with the given timeout in milliseconds. + /// @param key Key. + /// @param ttl Time-To-Live in milliseconds. + /// @param val Value. + /// @see https://redis.io/commands/psetex + void psetex(const StringView &key, + long long ttl, + const StringView &val); + + /// @brief Set key-value pair with the given timeout in milliseconds. + /// @param key Key. + /// @param ttl Time-To-Live in milliseconds. + /// @param val Value. + /// @see https://redis.io/commands/psetex + void psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val); + + /// @brief Set a key-value pair. + /// + /// Example: + /// @code{.cpp} + /// // Set a key-value pair. + /// redis.set("key", "value"); + /// // Set a key-value pair, and expire it after 10 seconds. + /// redis.set("key", "value", std::chrono::seconds(10)); + /// // Set a key-value pair with a timeout, only if the key already exists. + /// if (redis.set("key", "value", std::chrono::seconds(10), UpdateType::EXIST)) + /// std::cout << "OK" << std::endl; + /// else + /// std::cout << "key does not exist" << std::endl; + /// @endcode + /// @param key Key. + /// @param val Value. + /// @param ttl Timeout on the key. If `ttl` is 0ms, do not set timeout. + /// @param type Options for set command: + /// - UpdateType::EXIST: Set the key only if it already exists. + /// - UpdateType::NOT_EXIST: Set the key only if it does not exist. + /// - UpdateType::ALWAYS: Always set the key no matter whether it exists. + /// @return Whether the key has been set. + /// @retval true If the key has been set. + /// @retval false If the key was not set, because of the given option. + /// @see https://redis.io/commands/set + // TODO: Support KEEPTTL option for Redis 6.0 + bool set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0), + UpdateType type = UpdateType::ALWAYS); + + // TODO: add SETBIT command. + + /// @brief Set key-value pair with the given timeout in seconds. + /// @param key Key. + /// @param ttl Time-To-Live in seconds. + /// @param val Value. + /// @see https://redis.io/commands/setex + void setex(const StringView &key, + long long ttl, + const StringView &val); + + /// @brief Set key-value pair with the given timeout in seconds. + /// @param key Key. + /// @param ttl Time-To-Live in seconds. + /// @param val Value. + /// @see https://redis.io/commands/setex + void setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val); + + /// @brief Set the key if it does not exist. + /// @param key Key. + /// @param val Value. + /// @return Whether the key has been set. + /// @retval true If the key has been set. + /// @retval false If the key was not set, i.e. the key already exists. + /// @see https://redis.io/commands/setnx + bool setnx(const StringView &key, const StringView &val); + + /// @brief Set the substring starting from `offset` to the given value. + /// @param key Key. + /// @param offset Offset. + /// @param val Value. + /// @return The length of the string after this operation. + /// @see https://redis.io/commands/setrange + long long setrange(const StringView &key, long long offset, const StringView &val); + + /// @brief Get the length of the string stored at key. + /// @param key Key. + /// @return The length of the string. + /// @note If key does not exist, `strlen` returns 0. + /// @see https://redis.io/commands/strlen + long long strlen(const StringView &key); + + // LIST commands. + + /// @brief Pop the first element of the list in a blocking way. + /// @param key Key where the list is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If list is empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::lpop` + /// @see https://redis.io/commands/blpop + OptionalStringPair blpop(const StringView &key, long long timeout); + + /// @brief Pop the first element of the list in a blocking way. + /// @param key Key where the list is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If list is empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::lpop` + /// @see https://redis.io/commands/blpop + OptionalStringPair blpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + /// @brief Pop the first element of multiple lists in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::lpop` + /// @see https://redis.io/commands/blpop + template + OptionalStringPair blpop(Input first, Input last, long long timeout); + + /// @brief Pop the first element of multiple lists in a blocking way. + /// @param il Initializer list of keys. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::lpop` + /// @see https://redis.io/commands/blpop + template + OptionalStringPair blpop(std::initializer_list il, long long timeout) { + return blpop(il.begin(), il.end(), timeout); + } + + /// @brief Pop the first element of multiple lists in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::lpop` + /// @see https://redis.io/commands/blpop + template + OptionalStringPair blpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + /// @brief Pop the first element of multiple lists in a blocking way. + /// @param il Initializer list of keys. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::lpop` + /// @see https://redis.io/commands/blpop + template + OptionalStringPair blpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(il.begin(), il.end(), timeout); + } + + /// @brief Pop the last element of the list in a blocking way. + /// @param key Key where the list is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If list is empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::rpop` + /// @see https://redis.io/commands/brpop + OptionalStringPair brpop(const StringView &key, long long timeout); + + /// @brief Pop the last element of the list in a blocking way. + /// @param key Key where the list is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If list is empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::rpop` + /// @see https://redis.io/commands/brpop + OptionalStringPair brpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + /// @brief Pop the last element of multiple lists in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::rpop` + /// @see https://redis.io/commands/brpop + template + OptionalStringPair brpop(Input first, Input last, long long timeout); + + /// @brief Pop the last element of multiple lists in a blocking way. + /// @param il Initializer list of lists. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::rpop` + /// @see https://redis.io/commands/brpop + template + OptionalStringPair brpop(std::initializer_list il, long long timeout) { + return brpop(il.begin(), il.end(), timeout); + } + + /// @brief Pop the last element of multiple lists in a blocking way. + /// @param first Iterator to the first list. + /// @param last Off-the-end iterator to the list range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::rpop` + /// @see https://redis.io/commands/brpop + template + OptionalStringPair brpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + /// @brief Pop the last element of multiple lists in a blocking way. + /// @param il Initializer list of list keys. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-element pair. + /// @note If all lists are empty and timeout reaches, return `OptionalStringPair{}` (`std::nullopt`). + /// @see `Redis::rpop` + /// @see https://redis.io/commands/brpop + template + OptionalStringPair brpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(il.begin(), il.end(), timeout); + } + + /// @brief Pop last element of one list and push it to the left of another list in blocking way. + /// @param source Key of the source list. + /// @param destination Key of the destination list. + /// @param timeout Timeout. 0 means block forever. + /// @return The popped element. + /// @note If the source list does not exist, `brpoplpush` returns `OptionalString{}` (`std::nullopt`). + /// @see `Redis::rpoplpush` + /// @see https://redis.io/commands/brpoplpush + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + long long timeout); + + /// @brief Pop last element of one list and push it to the left of another list in blocking way. + /// @param source Key of the source list. + /// @param destination Key of the destination list. + /// @param timeout Timeout. 0 means block forever. + /// @return The popped element. + /// @note If the source list does not exist, `brpoplpush` returns `OptionalString{}` (`std::nullopt`). + /// @see `Redis::rpoplpush` + /// @see https://redis.io/commands/brpoplpush + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + /// @brief Get the element at the given index of the list. + /// @param key Key where the list is stored. + /// @param index Zero-base index, and -1 means the last element. + /// @return The element at the given index. + /// @see https://redis.io/commands/lindex + OptionalString lindex(const StringView &key, long long index); + + /// @brief Insert an element to a list before or after the pivot element. + /// + /// Example: + /// @code{.cpp} + /// // Insert 'hello' before 'world' + /// auto len = redis.linsert("list", InsertPosition::BEFORE, "world", "hello"); + /// if (len == -1) + /// std::cout << "there's no 'world' in the list" << std::endl; + /// else + /// std::cout << "after the operation, the length of the list is " << len << std::endl; + /// @endcode + /// @param key Key where the list is stored. + /// @param position Before or after the pivot element. + /// @param pivot The pivot value. The `pivot` is the value of the element, not the index. + /// @param val Element to be inserted. + /// @return The length of the list after the operation. + /// @note If the pivot value is not found, `linsert` returns -1. + /// @see `InsertPosition` + /// @see https://redis.io/commands/linsert + long long linsert(const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val); + + /// @brief Get the length of the list. + /// @param key Key where the list is stored. + /// @return The length of the list. + /// @see https://redis.io/commands/llen + long long llen(const StringView &key); + + /// @brief Pop the first element of the list. + /// + /// Example: + /// @code{.cpp} + /// auto element = redis.lpop("list"); + /// if (element) + /// std::cout << *element << std::endl; + /// else + /// std::cout << "list is empty, i.e. list does not exist" << std::endl; + /// @endcode + /// @param key Key where the list is stored. + /// @return The popped element. + /// @note If list is empty, i.e. key does not exist, return `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/lpop + OptionalString lpop(const StringView &key); + + /// @brief Push an element to the beginning of the list. + /// @param key Key where the list is stored. + /// @param val Element to be pushed. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/lpush + long long lpush(const StringView &key, const StringView &val); + + /// @brief Push multiple elements to the beginning of the list. + /// + /// Example: + /// @code{.cpp} + /// std::vector elements = {"e1", "e2", "e3"}; + /// redis.lpush("list", elements.begin(), elements.end()); + /// @endcode + /// @param key Key where the list is stored. + /// @param first Iterator to the first element to be pushed. + /// @param last Off-the-end iterator to the given element range. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/lpush + template + long long lpush(const StringView &key, Input first, Input last); + + /// @brief Push multiple elements to the beginning of the list. + /// + /// Example: + /// @code{.cpp} + /// redis.lpush("list", {"e1", "e2", "e3"}); + /// @endcode + /// @param key Key where the list is stored. + /// @param il Initializer list of elements. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/lpush + template + long long lpush(const StringView &key, std::initializer_list il) { + return lpush(key, il.begin(), il.end()); + } + + /// @brief Push an element to the beginning of the list, only if the list already exists. + /// @param key Key where the list is stored. + /// @param val Element to be pushed. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/lpushx + // TODO: add a multiple elements overload. + long long lpushx(const StringView &key, const StringView &val); + + /// @brief Get elements in the given range of the given list. + /// + /// Example: + /// @code{.cpp} + /// std::vector elements; + /// // Save all elements of a Redis list to a vector of string. + /// redis.lrange("list", 0, -1, std::back_inserter(elements)); + /// @endcode + /// @param key Key where the list is stored. + /// @param start Start index of the range. Index can be negative, which mean index from the end. + /// @param stop End index of the range. + /// @param output Output iterator to the destination where the results are saved. + /// @see https://redis.io/commands/lrange + template + void lrange(const StringView &key, long long start, long long stop, Output output); + + /// @brief Remove the first `count` occurrences of elements equal to `val`. + /// @param key Key where the list is stored. + /// @param count Number of occurrences to be removed. + /// @param val Value. + /// @return Number of elements removed. + /// @note `count` can be positive, negative and 0. Check the reference for detail. + /// @see https://redis.io/commands/lrem + long long lrem(const StringView &key, long long count, const StringView &val); + + /// @brief Set the element at the given index to the specified value. + /// @param key Key where the list is stored. + /// @param index Index of the element to be set. + /// @param val Value. + /// @see https://redis.io/commands/lset + void lset(const StringView &key, long long index, const StringView &val); + + /// @brief Trim a list to keep only element in the given range. + /// @param key Key where the key is stored. + /// @param start Start of the index. + /// @param stop End of the index. + /// @see https://redis.io/commands/ltrim + void ltrim(const StringView &key, long long start, long long stop); + + /// @brief Pop the last element of a list. + /// @param key Key where the list is stored. + /// @return The popped element. + /// @note If the list is empty, i.e. key does not exist, `rpop` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/rpop + OptionalString rpop(const StringView &key); + + /// @brief Pop last element of one list and push it to the left of another list. + /// @param source Key of the source list. + /// @param destination Key of the destination list. + /// @return The popped element. + /// @note If the source list does not exist, `rpoplpush` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/brpoplpush + OptionalString rpoplpush(const StringView &source, const StringView &destination); + + /// @brief Push an element to the end of the list. + /// @param key Key where the list is stored. + /// @param val Element to be pushed. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/rpush + long long rpush(const StringView &key, const StringView &val); + + /// @brief Push multiple elements to the end of the list. + /// @param key Key where the list is stored. + /// @param first Iterator to the first element to be pushed. + /// @param last Off-the-end iterator to the given element range. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/rpush + template + long long rpush(const StringView &key, Input first, Input last); + + /// @brief Push multiple elements to the end of the list. + /// @param key Key where the list is stored. + /// @param il Initializer list of elements to be pushed. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/rpush + template + long long rpush(const StringView &key, std::initializer_list il) { + return rpush(key, il.begin(), il.end()); + } + + /// @brief Push an element to the end of the list, only if the list already exists. + /// @param key Key where the list is stored. + /// @param val Element to be pushed. + /// @return The length of the list after the operation. + /// @see https://redis.io/commands/rpushx + long long rpushx(const StringView &key, const StringView &val); + + // HASH commands. + + /// @brief Remove the given field from hash. + /// @param key Key where the hash is stored. + /// @param field Field to be removed. + /// @return Whether the field has been removed. + /// @retval 1 If the field exists, and has been removed. + /// @retval 0 If the field does not exist. + /// @see https://redis.io/commands/hdel + long long hdel(const StringView &key, const StringView &field); + + /// @brief Remove multiple fields from hash. + /// @param key Key where the hash is stored. + /// @param first Iterator to the first field to be removed. + /// @param last Off-the-end iterator to the given field range. + /// @return Number of fields that has been removed. + /// @see https://redis.io/commands/hdel + template + long long hdel(const StringView &key, Input first, Input last); + + /// @brief Remove multiple fields from hash. + /// @param key Key where the hash is stored. + /// @param il Initializer list of fields. + /// @return Number of fields that has been removed. + /// @see https://redis.io/commands/hdel + template + long long hdel(const StringView &key, std::initializer_list il) { + return hdel(key, il.begin(), il.end()); + } + + /// @brief Check if the given field exists in hash. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @return Whether the field exists. + /// @retval true If the field exists in hash. + /// @retval false If the field does not exist. + /// @see https://redis.io/commands/hexists + bool hexists(const StringView &key, const StringView &field); + + /// @brief Get the value of the given field. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @return Value of the given field. + /// @note If field does not exist, `hget` returns `OptionalString{}` (`std::nullopt`). + /// @see https://redis.io/commands/hget + OptionalString hget(const StringView &key, const StringView &field); + + /// @brief Get all field-value pairs of the given hash. + /// + /// Example: + /// @code{.cpp} + /// std::unordered_map results; + /// // Save all field-value pairs of a Redis hash to an unordered_map. + /// redis.hgetall("hash", std::inserter(results, results.begin())); + /// @endcode + /// @param key Key where the hash is stored. + /// @param output Output iterator to the destination where the result is saved. + /// @note It's always a bad idea to call `hgetall` on a large hash, since it will block Redis. + /// @see `Redis::hscan` + /// @see https://redis.io/commands/hgetall + template + void hgetall(const StringView &key, Output output); + + /// @brief Increment the integer stored at the given field. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @param increment Increment. + /// @return The value of the field after the increment. + /// @see https://redis.io/commands/hincrby + long long hincrby(const StringView &key, const StringView &field, long long increment); + + /// @brief Increment the floating point number stored at the given field. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @param increment Increment. + /// @return The value of the field after the increment. + /// @see https://redis.io/commands/hincrbyfloat + double hincrbyfloat(const StringView &key, const StringView &field, double increment); + + /// @brief Get all fields of the given hash. + /// @param key Key where the hash is stored. + /// @param output Output iterator to the destination where the result is saved. + /// @note It's always a bad idea to call `hkeys` on a large hash, since it will block Redis. + /// @see `Redis::hscan` + /// @see https://redis.io/commands/hkeys + template + void hkeys(const StringView &key, Output output); + + /// @brief Get the number of fields of the given hash. + /// @param key Key where the hash is stored. + /// @return Number of fields. + /// @see https://redis.io/commands/hlen + long long hlen(const StringView &key); + + /// @brief Get values of multiple fields. + /// + /// Example: + /// @code{.cpp} + /// std::vector fields = {"f1", "f2"}; + /// std::vector vals; + /// redis.hmget("hash", fields.begin(), fields.end(), std::back_inserter(vals)); + /// for (const auto &val : vals) { + /// if (val) + /// std::cout << *val << std::endl; + /// else + /// std::cout << "field not exist" << std::endl; + /// } + /// @endcode + /// @param key Key where the hash is stored. + /// @param first Iterator to the first field. + /// @param last Off-the-end iterator to the given field range. + /// @param output Output iterator to the destination where the result is saved. + /// @note The destination should be a container of `OptionalString` type, + /// since the given field might not exist (in this case, the value of the corresponding + /// field is `OptionalString{}` (`std::nullopt`)). + /// @see https://redis.io/commands/hmget + template + void hmget(const StringView &key, Input first, Input last, Output output); + + /// @brief Get values of multiple fields. + /// + /// Example: + /// @code{.cpp} + /// std::vector vals; + /// redis.hmget("hash", {"f1", "f2"}, std::back_inserter(vals)); + /// for (const auto &val : vals) { + /// if (val) + /// std::cout << *val << std::endl; + /// else + /// std::cout << "field not exist" << std::endl; + /// } + /// @endcode + /// @param key Key where the hash is stored. + /// @param il Initializer list of fields. + /// @param output Output iterator to the destination where the result is saved. + /// @note The destination should be a container of `OptionalString` type, + /// since the given field might not exist (in this case, the value of the corresponding + /// field is `OptionalString{}` (`std::nullopt`)). + /// @see https://redis.io/commands/hmget + template + void hmget(const StringView &key, std::initializer_list il, Output output) { + hmget(key, il.begin(), il.end(), output); + } + + /// @brief Set multiple field-value pairs of the given hash. + /// + /// Example: + /// @code{.cpp} + /// std::unordered_map m = {{"f1", "v1"}, {"f2", "v2"}}; + /// redis.hmset("hash", m.begin(), m.end()); + /// @endcode + /// @param key Key where the hash is stored. + /// @param first Iterator to the first field-value pair. + /// @param last Off-the-end iterator to the range. + /// @see https://redis.io/commands/hmset + template + void hmset(const StringView &key, Input first, Input last); + + /// @brief Set multiple field-value pairs of the given hash. + /// + /// Example: + /// @code{.cpp} + /// redis.hmset("hash", {std::make_pair("f1", "v1"), std::make_pair("f2", "v2")}); + /// @endcode + /// @param key Key where the hash is stored. + /// @param il Initializer list of field-value pairs. + /// @see https://redis.io/commands/hmset + template + void hmset(const StringView &key, std::initializer_list il) { + hmset(key, il.begin(), il.end()); + } + + /// @brief Scan fields of the given hash matching the given pattern. + /// + /// Example: + /// @code{.cpp} + /// auto cursor = 0LL; + /// std::unordered_map kvs; + /// while (true) { + /// cursor = redis.hscan("hash", cursor, "pattern:*", 10, std::inserter(kvs, kvs.begin())); + /// if (cursor == 0) { + /// break; + /// } + /// } + /// @endcode + /// @param key Key where the hash is stored. + /// @param cursor Cursor. + /// @param pattern Pattern of fields to be scanned. + /// @param count A hint for how many fields to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/hscan + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + /// @brief Scan fields of the given hash matching the given pattern. + /// @param key Key where the hash is stored. + /// @param cursor Cursor. + /// @param pattern Pattern of fields to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/hscan + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + /// @brief Scan all fields of the given hash. + /// @param key Key where the hash is stored. + /// @param cursor Cursor. + /// @param count A hint for how many fields to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/hscan + template + long long hscan(const StringView &key, + long long cursor, + long long count, + Output output); + + /// @brief Scan all fields of the given hash. + /// @param key Key where the hash is stored. + /// @param cursor Cursor. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/hscan + template + long long hscan(const StringView &key, + long long cursor, + Output output); + + /// @brief Set hash field to value. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @param val Value. + /// @return Whether the given field is a new field. + /// @retval true If the given field didn't exist, and a new field has been added. + /// @retval false If the given field already exists, and its value has been overwritten. + /// @note When `hset` returns false, it does not mean that the method failed to set the field. + /// Instead, it means that the field already exists, and we've overwritten its value. + /// If `hset` fails, it will throw an exception of `Exception` type. + /// @see https://github.com/sewenew/redis-plus-plus/issues/9 + /// @see https://redis.io/commands/hset + bool hset(const StringView &key, const StringView &field, const StringView &val); + + /// @brief Set hash field to value. + /// @param key Key where the hash is stored. + /// @param item The field-value pair to be set. + /// @return Whether the given field is a new field. + /// @retval true If the given field didn't exist, and a new field has been added. + /// @retval false If the given field already exists, and its value has been overwritten. + /// @note When `hset` returns false, it does not mean that the method failed to set the field. + /// Instead, it means that the field already exists, and we've overwritten its value. + /// If `hset` fails, it will throw an exception of `Exception` type. + /// @see https://github.com/sewenew/redis-plus-plus/issues/9 + /// @see https://redis.io/commands/hset + bool hset(const StringView &key, const std::pair &item); + + /// @brief Set multiple fields of the given hash. + /// + /// Example: + /// @code{.cpp} + /// std::unordered_map m = {{"f1", "v1"}, {"f2", "v2"}}; + /// redis.hset("hash", m.begin(), m.end()); + /// @endcode + /// @param key Key where the hash is stored. + /// @param first Iterator to the first field to be set. + /// @param last Off-the-end iterator to the given range. + /// @return Number of fields that have been added, i.e. fields that not existed before. + /// @see https://redis.io/commands/hset + template + auto hset(const StringView &key, Input first, Input last) + -> typename std::enable_if::value, long long>::type; + + /// @brief Set multiple fields of the given hash. + /// + /// Example: + /// @code{.cpp} + /// redis.hset("hash", {std::make_pair("f1", "v1"), std::make_pair("f2", "v2")}); + /// @endcode + /// @param key Key where the hash is stored. + /// @param il Initializer list of field-value pairs. + /// @return Number of fields that have been added, i.e. fields that not existed before. + /// @see https://redis.io/commands/hset + template + long long hset(const StringView &key, std::initializer_list il) { + return hset(key, il.begin(), il.end()); + } + + /// @brief Set hash field to value, only if the given field does not exist. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @param val Value. + /// @return Whether the field has been set. + /// @retval true If the field has been set. + /// @retval false If failed to set the field, i.e. the field already exists. + /// @see https://redis.io/commands/hsetnx + bool hsetnx(const StringView &key, const StringView &field, const StringView &val); + + /// @brief Set hash field to value, only if the given field does not exist. + /// @param key Key where the hash is stored. + /// @param item The field-value pair to be set. + /// @return Whether the field has been set. + /// @retval true If the field has been set. + /// @retval false If failed to set the field, i.e. the field already exists. + /// @see https://redis.io/commands/hsetnx + bool hsetnx(const StringView &key, const std::pair &item); + + /// @brief Get the length of the string stored at the given field. + /// @param key Key where the hash is stored. + /// @param field Field. + /// @return Length of the string. + /// @see https://redis.io/commands/hstrlen + long long hstrlen(const StringView &key, const StringView &field); + + /// @brief Get values of all fields stored at the given hash. + /// @param key Key where the hash is stored. + /// @param output Output iterator to the destination where the result is saved. + /// @note It's always a bad idea to call `hvals` on a large hash, since it might block Redis. + /// @see `Redis::hscan` + /// @see https://redis.io/commands/hvals + template + void hvals(const StringView &key, Output output); + + // SET commands. + + /// @brief Add a member to the given set. + /// @param key Key where the set is stored. + /// @param member Member to be added. + /// @return Whether the given member is a new member. + /// @retval 1 The member did not exist before, and it has been added now. + /// @retval 0 The member already exists before this operation. + /// @see https://redis.io/commands/sadd + long long sadd(const StringView &key, const StringView &member); + + /// @brief Add multiple members to the given set. + /// @param key Key where the set is stored. + /// @param first Iterator to the first member to be added. + /// @param last Off-the-end iterator to the member range. + /// @return Number of new members that have been added, i.e. members did not exist before. + /// @see https://redis.io/commands/sadd + template + long long sadd(const StringView &key, Input first, Input last); + + /// @brief Add multiple members to the given set. + /// @param key Key where the set is stored. + /// @param il Initializer list of members to be added. + /// @return Number of new members that have been added, i.e. members did not exist before. + /// @see https://redis.io/commands/sadd + template + long long sadd(const StringView &key, std::initializer_list il) { + return sadd(key, il.begin(), il.end()); + } + + /// @brief Get the number of members in the set. + /// @param key Key where the set is stored. + /// @return Number of members. + /// @see https://redis.io/commands/scard + long long scard(const StringView &key); + + /// @brief Get the difference between the first set and all successive sets. + /// @param first Iterator to the first set. + /// @param last Off-the-end iterator to the range. + /// @param output Output iterator to the destination where the result is saved. + /// @see https://redis.io/commands/sdiff + // TODO: `void sdiff(const StringView &key, Input first, Input last, Output output)` is better. + template + void sdiff(Input first, Input last, Output output); + + /// @brief Get the difference between the first set and all successive sets. + /// @param il Initializer list of sets. + /// @param output Output iterator to the destination where the result is saved. + /// @see https://redis.io/commands/sdiff + template + void sdiff(std::initializer_list il, Output output) { + sdiff(il.begin(), il.end(), output); + } + + /// @brief Copy set stored at `key` to `destination`. + /// @param destination Key of the destination set. + /// @param key Key of the source set. + /// @return Number of members of the set. + /// @see https://redis.io/commands/sdiffstore + long long sdiffstore(const StringView &destination, const StringView &key); + + /// @brief Same as `sdiff`, except that it stores the result to another set. + /// @param destination Key of the destination set. + /// @param first Iterator to the first set. + /// @param last Off-the-end iterator to set range. + /// @return Number of members in the resulting set. + /// @see https://redis.io/commands/sdiffstore + template + long long sdiffstore(const StringView &destination, + Input first, + Input last); + + /// @brief Same as `sdiff`, except that it stores the result to another set. + /// @param destination Key of the destination set. + /// @param il Initializer list of sets. + /// @return Number of members in the resulting set. + /// @see https://redis.io/commands/sdiffstore + template + long long sdiffstore(const StringView &destination, + std::initializer_list il) { + return sdiffstore(destination, il.begin(), il.end()); + } + + /// @brief Get the intersection between the first set and all successive sets. + /// @param first Iterator to the first set. + /// @param last Off-the-end iterator to the range. + /// @param output Output iterator to the destination where the result is saved. + /// @see https://redis.io/commands/sinter + // TODO: `void sinter(const StringView &key, Input first, Input last, Output output)` is better. + template + void sinter(Input first, Input last, Output output); + + /// @brief Get the intersection between the first set and all successive sets. + /// @param il Initializer list of sets. + /// @param output Output iterator to the destination where the result is saved. + /// @see https://redis.io/commands/sinter + template + void sinter(std::initializer_list il, Output output) { + sinter(il.begin(), il.end(), output); + } + + /// @brief Copy set stored at `key` to `destination`. + /// @param destination Key of the destination set. + /// @param key Key of the source set. + /// @return Number of members of the set. + /// @see https://redis.io/commands/sinter + long long sinterstore(const StringView &destination, const StringView &key); + + /// @brief Same as `sinter`, except that it stores the result to another set. + /// @param destination Key of the destination set. + /// @param first Iterator to the first set. + /// @param last Off-the-end iterator to set range. + /// @return Number of members in the resulting set. + /// @see https://redis.io/commands/sinter + template + long long sinterstore(const StringView &destination, + Input first, + Input last); + + /// @brief Same as `sinter`, except that it stores the result to another set. + /// @param destination Key of the destination set. + /// @param il Initializer list of sets. + /// @return Number of members in the resulting set. + /// @see https://redis.io/commands/sinter + template + long long sinterstore(const StringView &destination, + std::initializer_list il) { + return sinterstore(destination, il.begin(), il.end()); + } + + /// @brief Test if `member` exists in the set stored at key. + /// @param key Key where the set is stored. + /// @param member Member to be checked. + /// @return Whether `member` exists in the set. + /// @retval true If it exists in the set. + /// @retval false If it does not exist in the set, or the given key does not exist. + /// @see https://redis.io/commands/sismember + bool sismember(const StringView &key, const StringView &member); + + /// @brief Get all members in the given set. + /// + /// Example: + /// @code{.cpp} + /// std::unordered_set members1; + /// redis.smembers("set", std::inserter(members1, members1.begin())); + /// std::vector members2; + /// redis.smembers("set", std::back_inserter(members2)); + /// @endcode + /// @param key Key where the set is stored. + /// @param output Iterator to the destination where the result is saved. + /// @see https://redis.io/commands/smembers + template + void smembers(const StringView &key, Output output); + + /// @brief Move `member` from one set to another. + /// @param source Key of the set in which the member currently exists. + /// @param destination Key of the destination set. + /// @return Whether the member has been moved. + /// @retval true If the member has been moved. + /// @retval false If `member` does not exist in `source`. + /// @see https://redis.io/commands/smove + bool smove(const StringView &source, + const StringView &destination, + const StringView &member); + + /// @brief Remove a random member from the set. + /// @param key Key where the set is stored. + /// @return The popped member. + /// @note If the set is empty, `spop` returns `OptionalString{}` (`std::nullopt`). + /// @see `Redis::srandmember` + /// @see https://redis.io/commands/spop + OptionalString spop(const StringView &key); + + /// @brief Remove multiple random members from the set. + /// + /// Example: + /// @code{.cpp} + /// std::vector members; + /// redis.spop("set", 10, std::back_inserter(members)); + /// @endcode + /// @param key Key where the set is stored. + /// @param count Number of members to be popped. + /// @param output Output iterator to the destination where the result is saved. + /// @note The number of popped members might be less than `count`. + /// @see `Redis::srandmember` + /// @see https://redis.io/commands/spop + template + void spop(const StringView &key, long long count, Output output); + + /// @brief Get a random member of the given set. + /// @param key Key where the set is stored. + /// @return A random member. + /// @note If the set is empty, `srandmember` returns `OptionalString{}` (`std::nullopt`). + /// @note This method won't remove the member from the set. + /// @see `Redis::spop` + /// @see https://redis.io/commands/srandmember + OptionalString srandmember(const StringView &key); + + /// @brief Get multiple random members of the given set. + /// @param key Key where the set is stored. + /// @param count Number of members to be returned. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method won't remove members from the set. + /// @see `Redis::spop` + /// @see https://redis.io/commands/srandmember + template + void srandmember(const StringView &key, long long count, Output output); + + /// @brief Remove a member from set. + /// @param key Key where the set is stored. + /// @param member Member to be removed. + /// @return Whether the member has been removed. + /// @retval 1 If the given member exists, and has been removed. + /// @retval 0 If the given member does not exist. + /// @see https://redis.io/commands/srem + long long srem(const StringView &key, const StringView &member); + + /// @brief Remove multiple members from set. + /// @param key Key where the set is stored. + /// @param first Iterator to the first member to be removed. + /// @param last Off-the-end iterator to the range. + /// @return Number of members that have been removed. + /// @see https://redis.io/commands/srem + template + long long srem(const StringView &key, Input first, Input last); + + /// @brief Remove multiple members from set. + /// @param key Key where the set is stored. + /// @param il Initializer list of members to be removed. + /// @return Number of members that have been removed. + /// @see https://redis.io/commands/srem + template + long long srem(const StringView &key, std::initializer_list il) { + return srem(key, il.begin(), il.end()); + } + + /// @brief Scan members of the set matching the given pattern. + /// + /// Example: + /// @code{.cpp} + /// auto cursor = 0LL; + /// std::unordered_set members; + /// while (true) { + /// cursor = redis.sscan("set", cursor, "pattern:*", + /// 10, std::inserter(members, members.begin())); + /// if (cursor == 0) { + /// break; + /// } + /// } + /// @endcode + /// @param key Key where the set is stored. + /// @param cursor Cursor. + /// @param pattern Pattern of fields to be scanned. + /// @param count A hint for how many fields to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/sscan + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + /// @brief Scan members of the set matching the given pattern. + /// @param key Key where the set is stored. + /// @param cursor Cursor. + /// @param pattern Pattern of fields to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/sscan + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + /// @brief Scan all members of the given set. + /// @param key Key where the set is stored. + /// @param cursor Cursor. + /// @param count A hint for how many fields to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/sscan + template + long long sscan(const StringView &key, + long long cursor, + long long count, + Output output); + + /// @brief Scan all members of the given set. + /// @param key Key where the set is stored. + /// @param cursor Cursor. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/sscan + template + long long sscan(const StringView &key, + long long cursor, + Output output); + + /// @brief Get the union between the first set and all successive sets. + /// @param first Iterator to the first set. + /// @param last Off-the-end iterator to the range. + /// @param output Output iterator to the destination where the result is saved. + /// @see https://redis.io/commands/sunion + // TODO: `void sunion(const StringView &key, Input first, Input last, Output output)` is better. + template + void sunion(Input first, Input last, Output output); + + /// @brief Get the union between the first set and all successive sets. + /// @param il Initializer list of sets. + /// @param output Output iterator to the destination where the result is saved. + /// @see https://redis.io/commands/sunion + template + void sunion(std::initializer_list il, Output output) { + sunion(il.begin(), il.end(), output); + } + + /// @brief Copy set stored at `key` to `destination`. + /// @param destination Key of the destination set. + /// @param key Key of the source set. + /// @return Number of members of the set. + /// @see https://redis.io/commands/sunionstore + long long sunionstore(const StringView &destination, const StringView &key); + + /// @brief Same as `sunion`, except that it stores the result to another set. + /// @param destination Key of the destination set. + /// @param first Iterator to the first set. + /// @param last Off-the-end iterator to set range. + /// @return Number of members in the resulting set. + /// @see https://redis.io/commands/sunionstore + template + long long sunionstore(const StringView &destination, Input first, Input last); + + /// @brief Same as `sunion`, except that it stores the result to another set. + /// @param destination Key of the destination set. + /// @param il Initializer list of sets. + /// @return Number of members in the resulting set. + /// @see https://redis.io/commands/sunionstore + template + long long sunionstore(const StringView &destination, std::initializer_list il) { + return sunionstore(destination, il.begin(), il.end()); + } + + // SORTED SET commands. + + /// @brief Pop the member with highest score from sorted set in a blocking way. + /// @param key Key where the sorted set is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the highest score. + /// @note If sorted set is empty and timeout reaches, `bzpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmax` + /// @see https://redis.io/commands/bzpopmax + auto bzpopmax(const StringView &key, long long timeout) + -> Optional>; + + /// @brief Pop the member with highest score from sorted set in a blocking way. + /// @param key Key where the sorted set is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the highest score. + /// @note If sorted set is empty and timeout reaches, `bzpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmax` + /// @see https://redis.io/commands/bzpopmax + auto bzpopmax(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + /// @brief Pop the member with highest score from multiple sorted set in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the higest score. + /// @note If all lists are empty and timeout reaches, `bzpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmax` + /// @see https://redis.io/commands/bzpopmax + template + auto bzpopmax(Input first, Input last, long long timeout) + -> Optional>; + + /// @brief Pop the member with highest score from multiple sorted set in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the higest score. + /// @note If all lists are empty and timeout reaches, `bzpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmax` + /// @see https://redis.io/commands/bzpopmax + template + auto bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + /// @brief Pop the member with highest score from multiple sorted set in a blocking way. + /// @param il Initializer list of sorted sets. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the higest score. + /// @note If all lists are empty and timeout reaches, `bzpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmax` + /// @see https://redis.io/commands/bzpopmax + template + auto bzpopmax(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + /// @brief Pop the member with highest score from multiple sorted set in a blocking way. + /// @param il Initializer list of sorted sets. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the higest score. + /// @note If all lists are empty and timeout reaches, `bzpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmax` + /// @see https://redis.io/commands/bzpopmax + template + auto bzpopmax(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + /// @brief Pop the member with lowest score from sorted set in a blocking way. + /// @param key Key where the sorted set is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the lowest score. + /// @note If sorted set is empty and timeout reaches, `bzpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmin` + /// @see https://redis.io/commands/bzpopmin + auto bzpopmin(const StringView &key, long long timeout) + -> Optional>; + + /// @brief Pop the member with lowest score from sorted set in a blocking way. + /// @param key Key where the sorted set is stored. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the lowest score. + /// @note If sorted set is empty and timeout reaches, `bzpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmin` + /// @see https://redis.io/commands/bzpopmin + auto bzpopmin(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + /// @brief Pop the member with lowest score from multiple sorted set in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the lowest score. + /// @note If all lists are empty and timeout reaches, `bzpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmin` + /// @see https://redis.io/commands/bzpopmin + template + auto bzpopmin(Input first, Input last, long long timeout) + -> Optional>; + + /// @brief Pop the member with lowest score from multiple sorted set in a blocking way. + /// @param first Iterator to the first key. + /// @param last Off-the-end iterator to the key range. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the lowest score. + /// @note If all lists are empty and timeout reaches, `bzpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmin` + /// @see https://redis.io/commands/bzpopmin + template + auto bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + /// @brief Pop the member with lowest score from multiple sorted set in a blocking way. + /// @param il Initializer list of sorted sets. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the lowest score. + /// @note If all lists are empty and timeout reaches, `bzpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmin` + /// @see https://redis.io/commands/bzpopmin + template + auto bzpopmin(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + /// @brief Pop the member with lowest score from multiple sorted set in a blocking way. + /// @param il Initializer list of sorted sets. + /// @param timeout Timeout in seconds. 0 means block forever. + /// @return Key-member-score tuple with the lowest score. + /// @note If all lists are empty and timeout reaches, `bzpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::zpopmin` + /// @see https://redis.io/commands/bzpopmin + template + auto bzpopmin(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + /// @brief Add or update a member with score to sorted set. + /// @param key Key where the sorted set is stored. + /// @param member Member to be added. + /// @param score Score of the member. + /// @param type Options for zadd command: + /// - UpdateType::EXIST: Add the member only if it already exists. + /// - UpdateType::NOT_EXIST: Add the member only if it does not exist. + /// - UpdateType::ALWAYS: Always add the member no matter whether it exists. + /// @param changed Whether change the return value from number of newly added member to + /// number of members changed (i.e. added and updated). + /// @return Number of added members or number of added and updated members depends on `changed`. + /// @note We don't support the INCR option, because in this case, the return value of zadd + /// command is NOT of type long long. However, you can always use the generic interface + /// to send zadd command with INCR option: + /// `auto score = redis.command("ZADD", "key", "XX", "INCR", 10, "mem");` + /// @see `UpdateType` + /// @see https://redis.io/commands/zadd + long long zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + /// @brief Add or update multiple members with score to sorted set. + /// + /// Example: + /// @code{.cpp} + /// std::unordered_map m = {{"m1", 1.2}, {"m2", 2.3}}; + /// redis.zadd("zset", m.begin(), m.end()); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param first Iterator to the first member-score pair. + /// @param last Off-the-end iterator to the member-score pairs range. + /// @param type Options for zadd command: + /// - UpdateType::EXIST: Add the member only if it already exists. + /// - UpdateType::NOT_EXIST: Add the member only if it does not exist. + /// - UpdateType::ALWAYS: Always add the member no matter whether it exists. + /// @param changed Whether change the return value from number of newly added member to + /// number of members changed (i.e. added and updated). + /// @return Number of added members or number of added and updated members depends on `changed`. + /// @note We don't support the INCR option, because in this case, the return value of zadd + /// command is NOT of type long long. However, you can always use the generic interface + /// to send zadd command with INCR option: + /// `auto score = redis.command("ZADD", "key", "XX", "INCR", 10, "mem");` + /// @see `UpdateType` + /// @see https://redis.io/commands/zadd + template + long long zadd(const StringView &key, + Input first, + Input last, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + /// @brief Add or update multiple members with score to sorted set. + /// + /// Example: + /// @code{.cpp} + /// redis.zadd("zset", {std::make_pair("m1", 1.4), std::make_pair("m2", 2.3)}); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param first Iterator to the first member-score pair. + /// @param last Off-the-end iterator to the member-score pairs range. + /// @param type Options for zadd command: + /// - UpdateType::EXIST: Add the member only if it already exists. + /// - UpdateType::NOT_EXIST: Add the member only if it does not exist. + /// - UpdateType::ALWAYS: Always add the member no matter whether it exists. + /// @param changed Whether change the return value from number of newly added member to + /// number of members changed (i.e. added and updated). + /// @return Number of added members or number of added and updated members depends on `changed`. + /// @note We don't support the INCR option, because in this case, the return value of zadd + /// command is NOT of type long long. However, you can always use the generic interface + /// to send zadd command with INCR option: + /// `auto score = redis.command("ZADD", "key", "XX", "INCR", 10, "mem");` + /// @see `UpdateType` + /// @see https://redis.io/commands/zadd + template + long long zadd(const StringView &key, + std::initializer_list il, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return zadd(key, il.begin(), il.end(), type, changed); + } + + /// @brief Get the number of members in the sorted set. + /// @param key Key where the sorted set is stored. + /// @return Number of members in the sorted set. + /// @see https://redis.io/commands/zcard + long long zcard(const StringView &key); + + /// @brief Get the number of members with score between a min-max score range. + /// + /// Example: + /// @code{.cpp} + /// // Count members with score between (2.3, 5] + /// redis.zcount("zset", BoundedInterval(2.3, 5, BoundType::LEFT_OPEN)); + /// // Count members with score between [2.3, 5) + /// redis.zcount("zset", BoundedInterval(2.3, 5, BoundType::RIGHT_OPEN)); + /// // Count members with score between (2.3, 5) + /// redis.zcount("zset", BoundedInterval(2.3, 5, BoundType::OPEN)); + /// // Count members with score between [2.3, 5] + /// redis.zcount("zset", BoundedInterval(2.3, 5, BoundType::CLOSED)); + /// // Count members with score between [2.3, +inf) + /// redis.zcount("zset", LeftBoundedInterval(2.3, BoundType::RIGHT_OPEN)); + /// // Count members with score between (2.3, +inf) + /// redis.zcount("zset", LeftBoundedInterval(2.3, BoundType::OPEN)); + /// // Count members with score between (-inf, 5] + /// redis.zcount("zset", RightBoundedInterval(5, BoundType::LEFT_OPEN)); + /// // Count members with score between (-inf, 5) + /// redis.zcount("zset", RightBoundedInterval(5, BoundType::OPEN)); + /// // Count members with score between (-inf, +inf) + /// redis.zcount("zset", UnboundedInterval{}); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval The min-max score range. + /// @return Number of members with score between a min-max score range. + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see https://redis.io/commands/zcount + // TODO: add a string version of Interval: zcount("key", "(2.3", "5"). + template + long long zcount(const StringView &key, const Interval &interval); + + /// @brief Increment the score of given member. + /// @param key Key where the sorted set is stored. + /// @param increment Increment. + /// @param member Member. + /// @return The score of the member after the operation. + /// @see https://redis.io/commands/zincrby + double zincrby(const StringView &key, double increment, const StringView &member); + + /// @brief Copy a sorted set to another one with the scores being multiplied by a factor. + /// @param destination Key of the destination sorted set. + /// @param key Key of the source sorted set. + /// @param weight Weight to be multiplied to the score of each member. + /// @return The number of members in the sorted set. + /// @note There's no aggregation type parameter for single key overload, since these 3 types + /// have the same effect. + /// @see `Redis::zunionstore` + /// @see https://redis.io/commands/zinterstore + long long zinterstore(const StringView &destination, const StringView &key, double weight); + + /// @brief Get intersection of multiple sorted sets, and store the result to another one. + /// + /// Example: + /// @code{.cpp} + /// // Use the default weight, i.e. 1, + /// // and use the sum of the all scores as the score of the result: + /// std::vector keys = {"k1", "k2", "k3"}; + /// redis.zinterstore("destination", keys.begin(), keys.end()); + /// // Each sorted set has a different weight, + /// // and the score of the result is the min of all scores. + /// std::vector> keys_with_weights = {{"k1", 1}, {"k2", 2}}; + /// redis.zinterstore("destination", keys_with_weights.begin(), + /// keys_with_weights.end(), Aggregation::MIN); + /// // NOTE: `keys_with_weights` can also be of type `std::unordered_map`. + /// // However, it will be slower than std::vector>, since we use + /// // `std::distance(first, last)` to calculate the *numkeys* parameter. + /// @endcode + /// @param destination Key of the destination sorted set. + /// @param first Iterator to the first sorted set (might with weight). + /// @param last Off-the-end iterator to the sorted set range. + /// @param type How the scores are aggregated. + /// - Aggregation::SUM: Score of a member is the sum of all scores. + /// - Aggregation::MIN: Score of a member is the min of all scores. + /// - Aggregation::MAX: Score of a member is the max of all scores. + /// @return The number of members in the resulting sorted set. + /// @note The score of each member can be multiplied by a factor, i.e. weight. If `Input` is an + /// iterator to a container of `std::string`, we use the default weight, i.e. 1, and send + /// *ZINTERSTORE dest numkeys key [key ...] [AGGREGATE SUM|MIN|MAX]* command. + /// If `Input` is an iterator to a container of `std::pair`, + /// i.e. key-weight pair, we send the command with the given weights: + /// *ZINTERSTORE dest numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]*. + /// See the *Example* part for examples on how to use this command. + /// @see `Redis::zunionstore` + /// @see https://redis.io/commands/zinterstore + template + long long zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + /// @brief Get intersection of multiple sorted sets, and store the result to another one. + /// + /// Example: + /// @code{.cpp} + /// // Use the default weight, i.e. 1, + /// // and use the sum of the all scores as the score of the result: + /// redis.zinterstore("destination", {"k1", "k2"}); + /// // Each sorted set has a different weight, + /// // and the score of the result is the min of all scores. + /// redis.zinterstore("destination", + /// {std::make_pair("k1", 1), std::make_pair("k2", 2)}, Aggregation::MIN); + /// @endcode + /// @param destination Key of the destination sorted set. + /// @param il Initializer list of sorted set. + /// @param type How the scores are aggregated. + /// - Aggregation::SUM: Score of a member is the sum of all scores. + /// - Aggregation::MIN: Score of a member is the min of all scores. + /// - Aggregation::MAX: Score of a member is the max of all scores. + /// @return The number of members in the resulting sorted set. + /// @note The score of each member can be multiplied by a factor, i.e. weight. If `T` is + /// of type `std::string`, we use the default weight, i.e. 1, and send + /// *ZINTERSTORE dest numkeys key [key ...] [AGGREGATE SUM|MIN|MAX]* command. + /// If `T` is of type `std::pair`, i.e. key-weight pair, + /// we send the command with the given weights: + /// *ZINTERSTORE dest numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]*. + /// See the *Example* part for examples on how to use this command. + /// @see `Redis::zunionstore` + /// @see https://redis.io/commands/zinterstore + template + long long zinterstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zinterstore(destination, il.begin(), il.end(), type); + } + + /// @brief Get the number of members between a min-max range in lexicographical order. + /// + /// Example: + /// @code{.cpp} + /// // Count members between (abc, abd] + /// redis.zlexcount("zset", BoundedInterval("abc", "abd", BoundType::LEFT_OPEN)); + /// // Count members between [abc, abd) + /// redis.zlexcount("zset", BoundedInterval("abc", "abd", BoundType::RIGHT_OPEN)); + /// // Count members between (abc, abd) + /// redis.zlexcount("zset", BoundedInterval("abc", "abd", BoundType::OPEN)); + /// // Count members between [abc, abd] + /// redis.zlexcount("zset", BoundedInterval("abc", "abd", BoundType::CLOSED)); + /// // Count members between [abc, +inf) + /// redis.zlexcount("zset", LeftBoundedInterval("abc", BoundType::RIGHT_OPEN)); + /// // Count members between (abc, +inf) + /// redis.zlexcount("zset", LeftBoundedInterval("abc", BoundType::OPEN)); + /// // Count members between (-inf, "abd"] + /// redis.zlexcount("zset", RightBoundedInterval("abd", BoundType::LEFT_OPEN)); + /// // Count members between (-inf, "abd") + /// redis.zlexcount("zset", RightBoundedInterval("abd", BoundType::OPEN)); + /// // Count members between (-inf, +inf) + /// redis.zlexcount("zset", UnboundedInterval{}); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval The min-max range in lexicographical order. + /// @return Number of members between a min-max range in lexicographical order. + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see https://redis.io/commands/zlexcount + // TODO: add a string version of Interval: zlexcount("key", "(abc", "abd"). + template + long long zlexcount(const StringView &key, const Interval &interval); + + /// @brief Pop the member with highest score from sorted set. + /// @param key Key where the sorted set is stored. + /// @return Member-score pair with the highest score. + /// @note If sorted set is empty `zpopmax` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::bzpopmax` + /// @see https://redis.io/commands/zpopmax + Optional> zpopmax(const StringView &key); + + /// @brief Pop multiple members with highest score from sorted set. + /// @param key Key where the sorted set is stored. + /// @param count Number of members to be popped. + /// @param output Output iterator to the destination where the result is saved. + /// @note The number of returned members might be less than `count`. + /// @see `Redis::bzpopmax` + /// @see https://redis.io/commands/zpopmax + template + void zpopmax(const StringView &key, long long count, Output output); + + /// @brief Pop the member with lowest score from sorted set. + /// @param key Key where the sorted set is stored. + /// @return Member-score pair with the lowest score. + /// @note If sorted set is empty `zpopmin` returns + /// `Optional>{}` (`std::nullopt`). + /// @see `Redis::bzpopmin` + /// @see https://redis.io/commands/zpopmin + Optional> zpopmin(const StringView &key); + + /// @brief Pop multiple members with lowest score from sorted set. + /// @param key Key where the sorted set is stored. + /// @param count Number of members to be popped. + /// @param output Output iterator to the destination where the result is saved. + /// @note The number of returned members might be less than `count`. + /// @see `Redis::bzpopmin` + /// @see https://redis.io/commands/zpopmin + template + void zpopmin(const StringView &key, long long count, Output output); + + /// @brief Get a range of members by rank (ordered from lowest to highest). + /// + /// Example: + /// @code{.cpp} + /// // send *ZRANGE* command without the *WITHSCORES* option: + /// std::vector result; + /// redis.zrange("zset", 0, -1, std::back_inserter(result)); + /// // send command with *WITHSCORES* option: + /// std::vector> with_score; + /// redis.zrange("zset", 0, -1, std::back_inserter(with_score)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param start Start rank. Inclusive and can be negative. + /// @param stop Stop rank. Inclusive and can be negative. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method can also return the score of each member. If `output` is an iterator + /// to a container of `std::string`, we send *ZRANGE key start stop* command. + /// If it's an iterator to a container of `std::pair`, + /// we send *ZRANGE key start stop WITHSCORES* command. See the *Example* part on + /// how to use this method. + /// @see `Redis::zrevrange` + /// @see https://redis.io/commands/zrange + template + void zrange(const StringView &key, long long start, long long stop, Output output); + + /// @brief Get a range of members by lexicographical order (from lowest to highest). + /// + /// Example: + /// @code{.cpp} + /// std::vector result; + /// // Get members between [abc, abd]. + /// redis.zrangebylex("zset", BoundedInterval("abc", "abd", BoundType::CLOSED), + /// std::back_inserter(result)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by lexicographical order. + /// @param output Output iterator to the destination where the result is saved. + /// @note See `Redis::zlexcount`'s *Example* part for how to set `interval` parameter. + /// @see `Redis::zlexcount` + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see `Redis::zrevrangebylex` + /// @see https://redis.io/commands/zrangebylex + template + void zrangebylex(const StringView &key, const Interval &interval, Output output); + + /// @brief Get a range of members by lexicographical order (from lowest to highest). + /// + /// Example: + /// @code{.cpp} + /// std::vector result; + /// // Limit the result to at most 5 members starting from 10. + /// LimitOptions opts; + /// opts.offset = 10; + /// opts.count = 5; + /// // Get members between [abc, abd]. + /// redis.zrangebylex("zset", BoundedInterval("abc", "abd", BoundType::CLOSED), + /// opts, std::back_inserter(result)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by lexicographical order. + /// @param opts Options to do pagination, i.e. *LIMIT offset count*. + /// @param output Output iterator to the destination where the result is saved. + /// @note See `Redis::zlexcount`'s *Example* part for how to set `interval` parameter. + /// @see `Redis::zlexcount` + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see `LimitOptions` + /// @see `Redis::zrevrangebylex` + /// @see https://redis.io/commands/zrangebylex + template + void zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + /// @brief Get a range of members by score (ordered from lowest to highest). + /// + /// Example: + /// @code{.cpp} + /// // Send *ZRANGEBYSCORE* command without the *WITHSCORES* option: + /// std::vector result; + /// // Get members whose score between (3, 6). + /// redis.zrangebyscore("zset", BoundedInterval(3, 6, BoundType::OPEN), + /// std::back_inserter(result)); + /// // Send command with *WITHSCORES* option: + /// std::vector> with_score; + /// // Get members whose score between [3, +inf). + /// redis.zrangebyscore("zset", LeftBoundedInterval(3, BoundType::RIGHT_OPEN), + /// std::back_inserter(with_score)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by score. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method can also return the score of each member. If `output` is an iterator + /// to a container of `std::string`, we send *ZRANGEBYSCORE key min max* command. + /// If it's an iterator to a container of `std::pair`, + /// we send *ZRANGEBYSCORE key min max WITHSCORES* command. See the *Example* part on + /// how to use this method. + /// @note See `Redis::zcount`'s *Example* part for how to set the `interval` parameter. + /// @see `Redis::zrevrangebyscore` + /// @see https://redis.io/commands/zrangebyscore + template + void zrangebyscore(const StringView &key, const Interval &interval, Output output); + + /// @brief Get a range of members by score (ordered from lowest to highest). + /// + /// Example: + /// @code{.cpp} + /// // Send *ZRANGEBYSCORE* command without the *WITHSCORES* option: + /// std::vector result; + /// // Only return at most 5 members starting from 10. + /// LimitOptions opts; + /// opts.offset = 10; + /// opts.count = 5; + /// // Get members whose score between (3, 6). + /// redis.zrangebyscore("zset", BoundedInterval(3, 6, BoundType::OPEN), + /// opts, std::back_inserter(result)); + /// // Send command with *WITHSCORES* option: + /// std::vector> with_score; + /// // Get members whose score between [3, +inf). + /// redis.zrangebyscore("zset", LeftBoundedInterval(3, BoundType::RIGHT_OPEN), + /// opts, std::back_inserter(with_score)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by score. + /// @param opts Options to do pagination, i.e. *LIMIT offset count*. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method can also return the score of each member. If `output` is an iterator + /// to a container of `std::string`, we send *ZRANGEBYSCORE key min max* command. + /// If it's an iterator to a container of `std::pair`, + /// we send *ZRANGEBYSCORE key min max WITHSCORES* command. See the *Example* part on + /// how to use this method. + /// @note See `Redis::zcount`'s *Example* part for how to set the `interval` parameter. + /// @see `Redis::zrevrangebyscore` + /// @see https://redis.io/commands/zrangebyscore + template + void zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + /// @brief Get the rank (from low to high) of the given member in the sorted set. + /// @param key Key where the sorted set is stored. + /// @param member Member. + /// @return The rank of the given member. + /// @note If the member does not exist, `zrank` returns `OptionalLongLong{}` (`std::nullopt`). + /// @see https://redis.io/commands/zrank + OptionalLongLong zrank(const StringView &key, const StringView &member); + + /// @brief Remove the given member from sorted set. + /// @param key Key where the sorted set is stored. + /// @param member Member to be removed. + /// @return Whether the member has been removed. + /// @retval 1 If the member exists, and has been removed. + /// @retval 0 If the member does not exist. + /// @see https://redis.io/commands/zrem + long long zrem(const StringView &key, const StringView &member); + + /// @brief Remove multiple members from sorted set. + /// @param key Key where the sorted set is stored. + /// @param first Iterator to the first member. + /// @param last Off-the-end iterator to the given range. + /// @return Number of members that have been removed. + /// @see https://redis.io/commands/zrem + template + long long zrem(const StringView &key, Input first, Input last); + + /// @brief Remove multiple members from sorted set. + /// @param key Key where the sorted set is stored. + /// @param il Initializer list of members to be removed. + /// @return Number of members that have been removed. + /// @see https://redis.io/commands/zrem + template + long long zrem(const StringView &key, std::initializer_list il) { + return zrem(key, il.begin(), il.end()); + } + + /// @brief Remove members in the given range of lexicographical order. + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by lexicographical order. + /// @note See `Redis::zlexcount`'s *Example* part for how to set `interval` parameter. + /// @return Number of members removed. + /// @see `Redis::zlexcount` + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see https://redis.io/commands/zremrangebylex + template + long long zremrangebylex(const StringView &key, const Interval &interval); + + /// @brief Remove members in the given range ordered by rank. + /// @param key Key where the sorted set is stored. + /// @param start Start rank. + /// @param stop Stop rank. + /// @return Number of members removed. + /// @see https://redis.io/commands/zremrangebyrank + long long zremrangebyrank(const StringView &key, long long start, long long stop); + + /// @brief Remove members in the given range ordered by score. + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by score. + /// @return Number of members removed. + /// @note See `Redis::zcount`'s *Example* part for how to set the `interval` parameter. + /// @see https://redis.io/commands/zremrangebyscore + template + long long zremrangebyscore(const StringView &key, const Interval &interval); + + /// @brief Get a range of members by rank (ordered from highest to lowest). + /// + /// Example: + /// @code{.cpp} + /// // send *ZREVRANGE* command without the *WITHSCORES* option: + /// std::vector result; + /// redis.zrevrange("key", 0, -1, std::back_inserter(result)); + /// // send command with *WITHSCORES* option: + /// std::vector> with_score; + /// redis.zrevrange("key", 0, -1, std::back_inserter(with_score)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param start Start rank. Inclusive and can be negative. + /// @param stop Stop rank. Inclusive and can be negative. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method can also return the score of each member. If `output` is an iterator + /// to a container of `std::string`, we send *ZREVRANGE key start stop* command. + /// If it's an iterator to a container of `std::pair`, + /// we send *ZREVRANGE key start stop WITHSCORES* command. See the *Example* part on + /// how to use this method. + /// @see `Redis::zrange` + /// @see https://redis.io/commands/zrevrange + template + void zrevrange(const StringView &key, long long start, long long stop, Output output); + + /// @brief Get a range of members by lexicographical order (from highest to lowest). + /// + /// Example: + /// @code{.cpp} + /// std::vector result; + /// // Get members between [abc, abd] in reverse order. + /// redis.zrevrangebylex("zset", BoundedInterval("abc", "abd", BoundType::CLOSED), + /// std::back_inserter(result)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by lexicographical order. + /// @param output Output iterator to the destination where the result is saved. + /// @note See `Redis::zlexcount`'s *Example* part for how to set `interval` parameter. + /// @see `Redis::zlexcount` + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see `Redis::zrangebylex` + /// @see https://redis.io/commands/zrevrangebylex + template + void zrevrangebylex(const StringView &key, const Interval &interval, Output output); + + /// @brief Get a range of members by lexicographical order (from highest to lowest). + /// + /// Example: + /// @code{.cpp} + /// std::vector result; + /// // Limit the result to at most 5 members starting from 10. + /// LimitOptions opts; + /// opts.offset = 10; + /// opts.count = 5; + /// // Get members between [abc, abd] in reverse order. + /// redis.zrevrangebylex("zset", BoundedInterval("abc", "abd", BoundType::CLOSED), + /// opts, std::back_inserter(result)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by lexicographical order. + /// @param opts Options to do pagination, i.e. *LIMIT offset count*. + /// @param output Output iterator to the destination where the result is saved. + /// @note See `Redis::zlexcount`'s *Example* part for how to set `interval` parameter. + /// @see `Redis::zlexcount` + /// @see `BoundedInterval` + /// @see `LeftBoundedInterval` + /// @see `RightBoundedInterval` + /// @see `UnboundedInterval` + /// @see `BoundType` + /// @see `LimitOptions` + /// @see `Redis::zrangebylex` + /// @see https://redis.io/commands/zrevrangebylex + template + void zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + /// @brief Get a range of members by score (ordered from highest to lowest). + /// + /// Example: + /// @code{.cpp} + /// // Send *ZREVRANGEBYSCORE* command without the *WITHSCORES* option: + /// std::vector result; + /// // Get members whose score between (3, 6) in reverse order. + /// redis.zrevrangebyscore("zset", BoundedInterval(3, 6, BoundType::OPEN), + /// std::back_inserter(result)); + /// // Send command with *WITHSCORES* option: + /// std::vector> with_score; + /// // Get members whose score between [3, +inf) in reverse order. + /// redis.zrevrangebyscore("zset", LeftBoundedInterval(3, BoundType::RIGHT_OPEN), + /// std::back_inserter(with_score)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by score. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method can also return the score of each member. If `output` is an iterator + /// to a container of `std::string`, we send *ZREVRANGEBYSCORE key min max* command. + /// If it's an iterator to a container of `std::pair`, + /// we send *ZREVRANGEBYSCORE key min max WITHSCORES* command. See the *Example* part on + /// how to use this method. + /// @note See `Redis::zcount`'s *Example* part for how to set the `interval` parameter. + /// @see `Redis::zrangebyscore` + /// @see https://redis.io/commands/zrevrangebyscore + template + void zrevrangebyscore(const StringView &key, const Interval &interval, Output output); + + /// @brief Get a range of members by score (ordered from highest to lowest). + /// + /// Example: + /// @code{.cpp} + /// // Send *ZREVRANGEBYSCORE* command without the *WITHSCORES* option: + /// std::vector result; + /// // Only return at most 5 members starting from 10. + /// LimitOptions opts; + /// opts.offset = 10; + /// opts.count = 5; + /// // Get members whose score between (3, 6) in reverse order. + /// redis.zrevrangebyscore("zset", BoundedInterval(3, 6, BoundType::OPEN), + /// opts, std::back_inserter(result)); + /// // Send command with *WITHSCORES* option: + /// std::vector> with_score; + /// // Get members whose score between [3, +inf) in reverse order. + /// redis.zrevrangebyscore("zset", LeftBoundedInterval(3, BoundType::RIGHT_OPEN), + /// opts, std::back_inserter(with_score)); + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param interval the min-max range by score. + /// @param opts Options to do pagination, i.e. *LIMIT offset count*. + /// @param output Output iterator to the destination where the result is saved. + /// @note This method can also return the score of each member. If `output` is an iterator + /// to a container of `std::string`, we send *ZREVRANGEBYSCORE key min max* command. + /// If it's an iterator to a container of `std::pair`, + /// we send *ZREVRANGEBYSCORE key min max WITHSCORES* command. See the *Example* part on + /// how to use this method. + /// @note See `Redis::zcount`'s *Example* part for how to set the `interval` parameter. + /// @see `Redis::zrangebyscore` + /// @see https://redis.io/commands/zrevrangebyscore + template + void zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + /// @brief Get the rank (from high to low) of the given member in the sorted set. + /// @param key Key where the sorted set is stored. + /// @param member Member. + /// @return The rank of the given member. + /// @note If the member does not exist, `zrevrank` returns `OptionalLongLong{}` (`std::nullopt`). + /// @see https://redis.io/commands/zrevrank + OptionalLongLong zrevrank(const StringView &key, const StringView &member); + + /// @brief Scan members of the given sorted set matching the given pattern. + /// + /// Example: + /// @code{.cpp} + /// auto cursor = 0LL; + /// std::vector> members; + /// while (true) { + /// cursor = redis.zscan("zset", cursor, "pattern:*", + /// 10, std::back_inserter(members)); + /// if (cursor == 0) { + /// break; + /// } + /// } + /// @endcode + /// @param key Key where the sorted set is stored. + /// @param cursor Cursor. + /// @param pattern Pattern of members to be scanned. + /// @param count A hint for how many members to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/zscan + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + /// @brief Scan members of the given sorted set matching the given pattern. + /// @param key Key where the sorted set is stored. + /// @param cursor Cursor. + /// @param pattern Pattern of members to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/zscan + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + /// @brief Scan all members of the given sorted set. + /// @param key Key where the sorted set is stored. + /// @param cursor Cursor. + /// @param count A hint for how many members to be scanned. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/zscan + template + long long zscan(const StringView &key, + long long cursor, + long long count, + Output output); + + /// @brief Scan all members of the given sorted set. + /// @param key Key where the sorted set is stored. + /// @param cursor Cursor. + /// @param output Output iterator to the destination where the result is saved. + /// @return The cursor to be used for the next scan operation. + /// @see https://redis.io/commands/zscan + template + long long zscan(const StringView &key, + long long cursor, + Output output); + + /// @brief Get the score of the given member. + /// @param key Key where the sorted set is stored. + /// @param member Member. + /// @return The score of the member. + /// @note If member does not exist, `zscore` returns `OptionalDouble{}` (`std::nullopt`). + /// @see https://redis.io/commands/zscore + OptionalDouble zscore(const StringView &key, const StringView &member); + + /// @brief Copy a sorted set to another one with the scores being multiplied by a factor. + /// @param destination Key of the destination sorted set. + /// @param key Key of the source sorted set. + /// @param weight Weight to be multiplied to the score of each member. + /// @return The number of members in the sorted set. + /// @note There's no aggregation type parameter for single key overload, since these 3 types + /// have the same effect. + /// @see `Redis::zinterstore` + /// @see https://redis.io/commands/zinterstore + long long zunionstore(const StringView &destination, const StringView &key, double weight); + + /// @brief Get union of multiple sorted sets, and store the result to another one. + /// + /// Example: + /// @code{.cpp} + /// // Use the default weight, i.e. 1, + /// // and use the sum of the all scores as the score of the result: + /// std::vector keys = {"k1", "k2", "k3"}; + /// redis.zunionstore("destination", keys.begin(), keys.end()); + /// // Each sorted set has a different weight, + /// // and the score of the result is the min of all scores. + /// std::vector> keys_with_weights = {{"k1", 1}, {"k2", 2}}; + /// redis.zunionstore("destination", keys_with_weights.begin(), + /// keys_with_weights.end(), Aggregation::MIN); + /// // NOTE: `keys_with_weights` can also be of type `std::unordered_map`. + /// // However, it will be slower than std::vector>, since we use + /// // `std::distance(first, last)` to calculate the *numkeys* parameter. + /// @endcode + /// @param destination Key of the destination sorted set. + /// @param first Iterator to the first sorted set (might with weight). + /// @param last Off-the-end iterator to the sorted set range. + /// @param type How the scores are aggregated. + /// - Aggregation::SUM: Score of a member is the sum of all scores. + /// - Aggregation::MIN: Score of a member is the min of all scores. + /// - Aggregation::MAX: Score of a member is the max of all scores. + /// @return The number of members in the resulting sorted set. + /// @note The score of each member can be multiplied by a factor, i.e. weight. If `Input` is an + /// iterator to a container of `std::string`, we use the default weight, i.e. 1, and send + /// *ZUNIONSTORE dest numkeys key [key ...] [AGGREGATE SUM|MIN|MAX]* command. + /// If `Input` is an iterator to a container of `std::pair`, + /// i.e. key-weight pair, we send the command with the given weights: + /// *ZUNIONSTORE dest numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]*. + /// See the *Example* part for examples on how to use this command. + /// @see `Redis::zinterstore` + /// @see https://redis.io/commands/zunionstore + template + long long zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + /// @brief Get union of multiple sorted sets, and store the result to another one. + /// + /// Example: + /// @code{.cpp} + /// // Use the default weight, i.e. 1, + /// // and use the sum of the all scores as the score of the result: + /// redis.zunionstore("destination", {"k1", "k2"}); + /// // Each sorted set has a different weight, + /// // and the score of the result is the min of all scores. + /// redis.zunionstore("destination", + /// {std::make_pair("k1", 1), std::make_pair("k2", 2)}, Aggregation::MIN); + /// @endcode + /// @param destination Key of the destination sorted set. + /// @param il Initializer list of sorted set. + /// @param type How the scores are aggregated. + /// - Aggregation::SUM: Score of a member is the sum of all scores. + /// - Aggregation::MIN: Score of a member is the min of all scores. + /// - Aggregation::MAX: Score of a member is the max of all scores. + /// @return The number of members in the resulting sorted set. + /// @note The score of each member can be multiplied by a factor, i.e. weight. If `T` is + /// of type `std::string`, we use the default weight, i.e. 1, and send + /// *ZUNIONSTORE dest numkeys key [key ...] [AGGREGATE SUM|MIN|MAX]* command. + /// If `T` is of type `std::pair`, i.e. key-weight pair, + /// we send the command with the given weights: + /// *ZUNIONSTORE dest numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]*. + /// See the *Example* part for examples on how to use this command. + /// @see `Redis::zinterstore` + /// @see https://redis.io/commands/zunionstore + template + long long zunionstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zunionstore(destination, il.begin(), il.end(), type); + } + + // HYPERLOGLOG commands. + + /// @brief Add the given element to a hyperloglog. + /// @param key Key of the hyperloglog. + /// @param element Element to be added. + /// @return Whether any of hyperloglog's internal register has been altered. + /// @retval true If at least one internal register has been altered. + /// @retval false If none of internal registers has been altered. + /// @note When `pfadd` returns false, it does not mean that this method failed to add + /// an element to the hyperloglog. Instead it means that the internal registers + /// were not altered. If `pfadd` fails, it will throw an exception of `Exception` type. + /// @see https://redis.io/commands/pfadd + bool pfadd(const StringView &key, const StringView &element); + + /// @brief Add the given elements to a hyperloglog. + /// @param key Key of the hyperloglog. + /// @param first Iterator to the first element. + /// @param last Off-the-end iterator to the given range. + /// @return Whether any of hyperloglog's internal register has been altered. + /// @retval true If at least one internal register has been altered. + /// @retval false If none of internal registers has been altered. + /// @note When `pfadd` returns false, it does not mean that this method failed to add + /// an element to the hyperloglog. Instead it means that the internal registers + /// were not altered. If `pfadd` fails, it will throw an exception of `Exception` type. + /// @see https://redis.io/commands/pfadd + template + bool pfadd(const StringView &key, Input first, Input last); + + /// @brief Add the given elements to a hyperloglog. + /// @param key Key of the hyperloglog. + /// @param il Initializer list of elements to be added. + /// @return Whether any of hyperloglog's internal register has been altered. + /// @retval true If at least one internal register has been altered. + /// @retval false If none of internal registers has been altered. + /// @note When `pfadd` returns false, it does not mean that this method failed to add + /// an element to the hyperloglog. Instead it means that the internal registers + /// were not altered. If `pfadd` fails, it will throw an exception of `Exception` type. + /// @see https://redis.io/commands/pfadd + template + bool pfadd(const StringView &key, std::initializer_list il) { + return pfadd(key, il.begin(), il.end()); + } + + long long pfcount(const StringView &key); + + template + long long pfcount(Input first, Input last); + + template + long long pfcount(std::initializer_list il) { + return pfcount(il.begin(), il.end()); + } + + void pfmerge(const StringView &destination, const StringView &key); + + template + void pfmerge(const StringView &destination, Input first, Input last); + + template + void pfmerge(const StringView &destination, std::initializer_list il) { + pfmerge(destination, il.begin(), il.end()); + } + + // GEO commands. + + long long geoadd(const StringView &key, + const std::tuple &member); + + template + long long geoadd(const StringView &key, + Input first, + Input last); + + template + long long geoadd(const StringView &key, + std::initializer_list il) { + return geoadd(key, il.begin(), il.end()); + } + + OptionalDouble geodist(const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit = GeoUnit::M); + + OptionalString geohash(const StringView &key, const StringView &member); + + template + void geohash(const StringView &key, Input first, Input last, Output output); + + template + void geohash(const StringView &key, std::initializer_list il, Output output) { + geohash(key, il.begin(), il.end(), output); + } + + Optional> geopos(const StringView &key, const StringView &member); + + template + void geopos(const StringView &key, Input first, Input last, Output output); + + template + void geopos(const StringView &key, std::initializer_list il, Output output) { + geopos(key, il.begin(), il.end(), output); + } + + // TODO: + // 1. since we have different overloads for georadius and georadius-store, + // we might use the GEORADIUS_RO command in the future. + // 2. there're too many parameters for this method, we might refactor it. + + /// @brief Get members in geo range, i.e. a circle, and store them in a sorted set. + /// @param key Key of the GEO set. + /// @param loc Location encoded with pair. + /// @param radius Radius of the range. + /// @param unit Radius unit. + /// @param destination Key of the destination sorted set. + /// @param store_dist Whether store distance info instead of geo info to destination. + /// @param count Limit the first N members. + /// @return Number of members stored in destination. + /// @note Before Redis 6.2.6, if key does not exist, returns `OptionalLongLong{}` (`std::nullopt`). + /// Since Redis 6.2.6, if key does not exist, returns 0. + /// @see `GeoUnit` + /// @see `Redis::georadiusbymember` + /// @see https://redis.io/commands/georadius + OptionalLongLong georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // If *output* is an iterator of a container of string, we send *GEORADIUS* command + // without any options and only get the members in the specified geo range. + // If *output* is an iterator of a container of a tuple, the type of the tuple decides + // options we send with the *GEORADIUS* command. If the tuple has an element of type + // double, we send the *WITHDIST* option. If it has an element of type string, we send + // the *WITHHASH* option. If it has an element of type pair, we send + // the *WITHCOORD* option. For example: + // + // The following code only gets the members in range, i.e. without any option. + // + // vector members; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(members)) + // + // The following code sends the command with *WITHDIST* option. + // + // vector> with_dist; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist)) + // + // The following code sends the command with *WITHDIST* and *WITHHASH* options. + // + // vector> with_dist_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_hash)) + // + // The following code sends the command with *WITHDIST*, *WITHCOORD* and *WITHHASH* options. + // + // vector, string>> with_dist_coord_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_coord_hash)) + // + // This also applies to *GEORADIUSBYMEMBER*. + template + void georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + /// @brief Get members in geo range, i.e. a circle, and store them in a sorted set. + /// @param key Key of the GEO set. + /// @param member Member which is the center of the circle. + /// @param radius Radius of the range. + /// @param unit Radius unit. + /// @param destination Key of the destination sorted set. + /// @param store_dist Whether store distance info instead of geo info to destination. + /// @param count Limit the first N members. + /// @return Number of members stored in destination. + /// @note Before Redis 6.2.6, if key does not exist, returns `OptionalLongLong{}` (`std::nullopt`). + /// Since Redis 6.2.6, if key does not exist, returns 0. + /// @note If member does not exist, throw an `ReplyError`. + /// @see `GeoUnit` + /// @see `Redis::georadius` + /// @see https://redis.io/commands/georadiusbymember + OptionalLongLong georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // See comments on *GEORADIUS*. + template + void georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + // SCRIPTING commands. + + template + Result eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last); + + template + Result eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output); + + template + void eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + template + Result evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last); + + template + Result evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output); + + template + void evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + /// @brief Check if the given script exists. + /// @param sha1 SHA1 digest of the script. + /// @return Whether the script exists. + /// @retval true If the script exists. + /// @retval false If the script does not exist. + /// @see https://redis.io/commands/script-exists + bool script_exists(const StringView &sha1); + + template + void script_exists(Input first, Input last, Output output); + + template + void script_exists(std::initializer_list il, Output output) { + script_exists(il.begin(), il.end(), output); + } + + void script_flush(); + + void script_kill(); + + std::string script_load(const StringView &script); + + // PUBSUB commands. + + long long publish(const StringView &channel, const StringView &message); + + // Transaction commands. + void watch(const StringView &key); + + template + void watch(Input first, Input last); + + template + void watch(std::initializer_list il) { + watch(il.begin(), il.end()); + } + + void unwatch(); + + // Stream commands. + + long long xack(const StringView &key, const StringView &group, const StringView &id); + + template + long long xack(const StringView &key, const StringView &group, Input first, Input last); + + template + long long xack(const StringView &key, const StringView &group, std::initializer_list il) { + return xack(key, group, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, const StringView &id, Input first, Input last); + + template + std::string xadd(const StringView &key, const StringView &id, std::initializer_list il) { + return xadd(key, id, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx = true); + + template + std::string xadd(const StringView &key, + const StringView &id, + std::initializer_list il, + long long count, + bool approx = true) { + return xadd(key, id, il.begin(), il.end(), count, approx); + } + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + std::initializer_list il, + Output output) { + xclaim(key, group, consumer, min_idle_time, il.begin(), il.end(), output); + } + + long long xdel(const StringView &key, const StringView &id); + + template + long long xdel(const StringView &key, Input first, Input last); + + template + long long xdel(const StringView &key, std::initializer_list il) { + return xdel(key, il.begin(), il.end()); + } + + void xgroup_create(const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream = false); + + void xgroup_setid(const StringView &key, const StringView &group, const StringView &id); + + long long xgroup_destroy(const StringView &key, const StringView &group); + + long long xgroup_delconsumer(const StringView &key, + const StringView &group, + const StringView &consumer); + + long long xlen(const StringView &key); + + template + auto xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple; + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + Output output) { + xread(key, id, 0, output); + } + + template + auto xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, Input last, Output output) + -> typename std::enable_if::value>::type { + xread(first ,last, 0, output); + } + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + xread(key, id, timeout, 0, output); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xread(first, last, timeout, 0, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + Output output) { + xreadgroup(group, consumer, key, id, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + Output output) { + xreadgroup(group, consumer, key, id, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, 0, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + xreadgroup(group, consumer, key, id, timeout, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + xreadgroup(group, consumer, key, id, timeout, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, 0, false, output); + } + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output); + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output); + + long long xtrim(const StringView &key, long long count, bool approx = true); + +private: + template + friend class QueuedRedis; + + friend class RedisCluster; + + // For internal use. + explicit Redis(const GuardedConnectionSPtr &connection); + + template + ReplyUPtr _command(const StringView &cmd_name, const IndexSequence &, Args &&...args) { + return command(cmd_name, NthValue(std::forward(args)...)...); + } + + template + ReplyUPtr _command(Connection &connection, Cmd cmd, Args &&...args); + + template + ReplyUPtr _score_command(std::true_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(std::false_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(Cmd cmd, Args &&... args); + + // Pool Mode. + // Public constructors create a *Redis* instance with a pool. + // In this case, *_connection* is a null pointer, and is never used. + ConnectionPoolSPtr _pool; + + // Single Connection Mode. + // Private constructor creates a *Redis* instance with a single connection. + // This is used when we create Transaction, Pipeline and Subscriber. + // In this case, *_pool* is empty, and is never used. + GuardedConnectionSPtr _connection; +}; + +} + +} + +#include "redis.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/redis.hpp b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/redis.hpp new file mode 100644 index 000000000..c560ce111 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/redis.hpp @@ -0,0 +1,1342 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_REDIS_HPP +#define SEWENEW_REDISPLUSPLUS_REDIS_HPP + +#include "command.h" +#include "reply.h" +#include "utils.h" +#include "errors.h" + +namespace sw { + +namespace redis { + +template +auto Redis::command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type { + if (_connection) { + // Single Connection Mode. + // TODO: In this case, should we reconnect? + auto &connection = _connection->connection(); + if (connection.broken()) { + throw Error("Connection is broken"); + } + + return _command(connection, cmd, std::forward(args)...); + } else { + assert(_pool); + + // Pool Mode, i.e. get connection from pool. + SafeConnection connection(*_pool); + + return _command(connection.connection(), cmd, std::forward(args)...); + } +} + +template +auto Redis::command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, ReplyUPtr>::type { + auto cmd = [](Connection &connection, const StringView &cmd_name, Args &&...args) { + CmdArgs cmd_args; + cmd_args.append(cmd_name, std::forward(args)...); + connection.send(cmd_args); + }; + + return command(cmd, cmd_name, std::forward(args)...); +} + +template +auto Redis::command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type { + range_check("command", first, last); + + auto cmd = [](Connection &connection, Input first, Input last) { + CmdArgs cmd_args; + while (first != last) { + cmd_args.append(*first); + ++first; + } + connection.send(cmd_args); + }; + + return command(cmd, first, last); +} + +template +Result Redis::command(const StringView &cmd_name, Args &&...args) { + auto r = command(cmd_name, std::forward(args)...); + + assert(r); + + return reply::parse(*r); +} + +template +auto Redis::command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, void>::type { + auto r = _command(cmd_name, + MakeIndexSequence(), + std::forward(args)...); + + assert(r); + + reply::to_array(*r, LastValue(std::forward(args)...)); +} + +template +auto Redis::command(Input first, Input last) + -> typename std::enable_if::value, Result>::type { + auto r = command(first, last); + + assert(r); + + return reply::parse(*r); +} + +template +auto Redis::command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type { + auto r = command(first, last); + + assert(r); + + reply::to_array(*r, output); +} + +// KEY commands. + +template +long long Redis::del(Input first, Input last) { + range_check("DEL", first, last); + + auto reply = command(cmd::del_range, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::exists(Input first, Input last) { + range_check("EXISTS", first, last); + + auto reply = command(cmd::exists_range, first, last); + + return reply::parse(*reply); +} + +inline bool Redis::expire(const StringView &key, const std::chrono::seconds &timeout) { + return expire(key, timeout.count()); +} + +inline bool Redis::expireat(const StringView &key, + const std::chrono::time_point &tp) { + return expireat(key, tp.time_since_epoch().count()); +} + +template +void Redis::keys(const StringView &pattern, Output output) { + auto reply = command(cmd::keys, pattern); + + reply::to_array(*reply, output); +} + +inline bool Redis::pexpire(const StringView &key, const std::chrono::milliseconds &timeout) { + return pexpire(key, timeout.count()); +} + +inline bool Redis::pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return pexpireat(key, tp.time_since_epoch().count()); +} + +inline void Redis::restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl, + bool replace) { + return restore(key, val, ttl.count(), replace); +} + +template +long long Redis::scan(long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::scan, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::scan(long long cursor, + const StringView &pattern, + Output output) { + return scan(cursor, pattern, 10, output); +} + +template +inline long long Redis::scan(long long cursor, + long long count, + Output output) { + return scan(cursor, "*", count, output); +} + +template +inline long long Redis::scan(long long cursor, + Output output) { + return scan(cursor, "*", 10, output); +} + +template +long long Redis::touch(Input first, Input last) { + range_check("TOUCH", first, last); + + auto reply = command(cmd::touch_range, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::unlink(Input first, Input last) { + range_check("UNLINK", first, last); + + auto reply = command(cmd::unlink_range, first, last); + + return reply::parse(*reply); +} + +inline long long Redis::wait(long long numslaves, const std::chrono::milliseconds &timeout) { + return wait(numslaves, timeout.count()); +} + +// STRING commands. + +template +long long Redis::bitop(BitOp op, const StringView &destination, Input first, Input last) { + range_check("BITOP", first, last); + + auto reply = command(cmd::bitop_range, op, destination, first, last); + + return reply::parse(*reply); +} + +template +void Redis::mget(Input first, Input last, Output output) { + range_check("MGET", first, last); + + auto reply = command(cmd::mget, first, last); + + reply::to_array(*reply, output); +} + +template +void Redis::mset(Input first, Input last) { + range_check("MSET", first, last); + + auto reply = command(cmd::mset, first, last); + + reply::parse(*reply); +} + +template +bool Redis::msetnx(Input first, Input last) { + range_check("MSETNX", first, last); + + auto reply = command(cmd::msetnx, first, last); + + return reply::parse(*reply); +} + +inline void Redis::psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val) { + return psetex(key, ttl.count(), val); +} + +inline void Redis::setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val) { + setex(key, ttl.count(), val); +} + +// LIST commands. + +template +OptionalStringPair Redis::blpop(Input first, Input last, long long timeout) { + range_check("BLPOP", first, last); + + auto reply = command(cmd::blpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair Redis::blpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return blpop(first, last, timeout.count()); +} + +template +OptionalStringPair Redis::brpop(Input first, Input last, long long timeout) { + range_check("BRPOP", first, last); + + auto reply = command(cmd::brpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair Redis::brpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return brpop(first, last, timeout.count()); +} + +inline OptionalString Redis::brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout) { + return brpoplpush(source, destination, timeout.count()); +} + +template +inline long long Redis::lpush(const StringView &key, Input first, Input last) { + range_check("LPUSH", first, last); + + auto reply = command(cmd::lpush_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void Redis::lrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = command(cmd::lrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline long long Redis::rpush(const StringView &key, Input first, Input last) { + range_check("RPUSH", first, last); + + auto reply = command(cmd::rpush_range, key, first, last); + + return reply::parse(*reply); +} + +// HASH commands. + +template +inline long long Redis::hdel(const StringView &key, Input first, Input last) { + range_check("HDEL", first, last); + + auto reply = command(cmd::hdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void Redis::hgetall(const StringView &key, Output output) { + auto reply = command(cmd::hgetall, key); + + reply::to_array(*reply, output); +} + +template +inline void Redis::hkeys(const StringView &key, Output output) { + auto reply = command(cmd::hkeys, key); + + reply::to_array(*reply, output); +} + +template +inline void Redis::hmget(const StringView &key, Input first, Input last, Output output) { + range_check("HMGET", first, last); + + auto reply = command(cmd::hmget, key, first, last); + + reply::to_array(*reply, output); +} + +template +inline void Redis::hmset(const StringView &key, Input first, Input last) { + range_check("HMSET", first, last); + + auto reply = command(cmd::hmset, key, first, last); + + reply::parse(*reply); +} + +template +long long Redis::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::hscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return hscan(key, cursor, pattern, 10, output); +} + +template +inline long long Redis::hscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return hscan(key, cursor, "*", count, output); +} + +template +inline long long Redis::hscan(const StringView &key, + long long cursor, + Output output) { + return hscan(key, cursor, "*", 10, output); +} + +template +auto Redis::hset(const StringView &key, Input first, Input last) + -> typename std::enable_if::value, + long long>::type { + range_check("HSET", first, last); + + auto reply = command(cmd::hset_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void Redis::hvals(const StringView &key, Output output) { + auto reply = command(cmd::hvals, key); + + reply::to_array(*reply, output); +} + +// SET commands. + +template +long long Redis::sadd(const StringView &key, Input first, Input last) { + range_check("SADD", first, last); + + auto reply = command(cmd::sadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void Redis::sdiff(Input first, Input last, Output output) { + range_check("SDIFF", first, last); + + auto reply = command(cmd::sdiff, first, last); + + reply::to_array(*reply, output); +} + +template +long long Redis::sdiffstore(const StringView &destination, + Input first, + Input last) { + range_check("SDIFFSTORE", first, last); + + auto reply = command(cmd::sdiffstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void Redis::sinter(Input first, Input last, Output output) { + range_check("SINTER", first, last); + + auto reply = command(cmd::sinter, first, last); + + reply::to_array(*reply, output); +} + +template +long long Redis::sinterstore(const StringView &destination, + Input first, + Input last) { + range_check("SINTERSTORE", first, last); + + auto reply = command(cmd::sinterstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void Redis::smembers(const StringView &key, Output output) { + auto reply = command(cmd::smembers, key); + + reply::to_array(*reply, output); +} + +template +void Redis::spop(const StringView &key, long long count, Output output) { + auto reply = command(cmd::spop_range, key, count); + + reply::to_array(*reply, output); +} + +template +void Redis::srandmember(const StringView &key, long long count, Output output) { + auto reply = command(cmd::srandmember_range, key, count); + + reply::to_array(*reply, output); +} + +template +long long Redis::srem(const StringView &key, Input first, Input last) { + range_check("SREM", first, last); + + auto reply = command(cmd::srem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::sscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return sscan(key, cursor, pattern, 10, output); +} + +template +inline long long Redis::sscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return sscan(key, cursor, "*", count, output); +} + +template +inline long long Redis::sscan(const StringView &key, + long long cursor, + Output output) { + return sscan(key, cursor, "*", 10, output); +} + +template +void Redis::sunion(Input first, Input last, Output output) { + range_check("SUNION", first, last); + + auto reply = command(cmd::sunion, first, last); + + reply::to_array(*reply, output); +} + +template +long long Redis::sunionstore(const StringView &destination, Input first, Input last) { + range_check("SUNIONSTORE", first, last); + + auto reply = command(cmd::sunionstore_range, destination, first, last); + + return reply::parse(*reply); +} + +// SORTED SET commands. + +inline auto Redis::bzpopmax(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(key, timeout.count()); +} + +template +auto Redis::bzpopmax(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmax_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto Redis::bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(first, last, timeout.count()); +} + +inline auto Redis::bzpopmin(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(key, timeout.count()); +} + +template +auto Redis::bzpopmin(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmin_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto Redis::bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(first, last, timeout.count()); +} + +template +long long Redis::zadd(const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed) { + range_check("ZADD", first, last); + + auto reply = command(cmd::zadd_range, key, first, last, type, changed); + + return reply::parse(*reply); +} + +template +long long Redis::zcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zcount, key, interval); + + return reply::parse(*reply); +} + +template +long long Redis::zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + range_check("ZINTERSTORE", first, last); + + auto reply = command(cmd::zinterstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +template +long long Redis::zlexcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zlexcount, key, interval); + + return reply::parse(*reply); +} + +template +void Redis::zpopmax(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmax, key, count); + + reply::to_array(*reply, output); +} + +template +void Redis::zpopmin(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmin, key, count); + + reply::to_array(*reply, output); +} + +template +void Redis::zrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +void Redis::zrangebylex(const StringView &key, const Interval &interval, Output output) { + zrangebylex(key, interval, {}, output); +} + +template +void Redis::zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void Redis::zrangebyscore(const StringView &key, + const Interval &interval, + Output output) { + zrangebyscore(key, interval, {}, output); +} + +template +void Redis::zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrangebyscore, + key, + interval, + opts); + + reply::to_array(*reply, output); +} + +template +long long Redis::zrem(const StringView &key, Input first, Input last) { + range_check("ZREM", first, last); + + auto reply = command(cmd::zrem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::zremrangebylex(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebylex, key, interval); + + return reply::parse(*reply); +} + +template +long long Redis::zremrangebyscore(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebyscore, key, interval); + + return reply::parse(*reply); +} + +template +void Redis::zrevrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrevrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline void Redis::zrevrangebylex(const StringView &key, + const Interval &interval, + Output output) { + zrevrangebylex(key, interval, {}, output); +} + +template +void Redis::zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrevrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void Redis::zrevrangebyscore(const StringView &key, const Interval &interval, Output output) { + zrevrangebyscore(key, interval, {}, output); +} + +template +void Redis::zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrevrangebyscore, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +long long Redis::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::zscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return zscan(key, cursor, pattern, 10, output); +} + +template +inline long long Redis::zscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return zscan(key, cursor, "*", count, output); +} + +template +inline long long Redis::zscan(const StringView &key, + long long cursor, + Output output) { + return zscan(key, cursor, "*", 10, output); +} + +template +long long Redis::zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + range_check("ZUNIONSTORE", first, last); + + auto reply = command(cmd::zunionstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +// HYPERLOGLOG commands. + +template +bool Redis::pfadd(const StringView &key, Input first, Input last) { + range_check("PFADD", first, last); + + auto reply = command(cmd::pfadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::pfcount(Input first, Input last) { + range_check("PFCOUNT", first, last); + + auto reply = command(cmd::pfcount_range, first, last); + + return reply::parse(*reply); +} + +template +void Redis::pfmerge(const StringView &destination, + Input first, + Input last) { + range_check("PFMERGE", first, last); + + auto reply = command(cmd::pfmerge_range, destination, first, last); + + reply::parse(*reply); +} + +// GEO commands. + +template +inline long long Redis::geoadd(const StringView &key, + Input first, + Input last) { + range_check("GEOADD", first, last); + + auto reply = command(cmd::geoadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void Redis::geohash(const StringView &key, Input first, Input last, Output output) { + range_check("GEOHASH", first, last); + + auto reply = command(cmd::geohash_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void Redis::geopos(const StringView &key, Input first, Input last, Output output) { + range_check("GEOPOS", first, last); + + auto reply = command(cmd::geopos_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void Redis::georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadius, + key, + loc, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +template +void Redis::georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadiusbymember, + key, + member, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +// SCRIPTING commands. + +template +Result Redis::eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + auto reply = command(cmd::eval, script, keys_first, keys_last, args_first, args_last); + + return reply::parse(*reply); +} + +template +Result Redis::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return eval(script, keys.begin(), keys.end(), args.begin(), args.end()); +} + +template +void Redis::eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output) { + auto reply = command(cmd::eval, + script, + keys_first, keys_last, + args_first, args_last); + + reply::to_array(*reply, output); +} + +template +void Redis::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + eval(script, keys.begin(), keys.end(), args.begin(), args.end(), output); +} + +template +Result Redis::evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + auto reply = command(cmd::evalsha, script, + keys_first, keys_last, args_first, args_last); + + return reply::parse(*reply); +} + +template +Result Redis::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return evalsha(script, keys.begin(), keys.end(), args.begin(), args.end()); +} + +template +void Redis::evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output) { + auto reply = command(cmd::evalsha, + script, + keys_first, keys_last, + args_first, args_last); + + reply::to_array(*reply, output); +} + +template +void Redis::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + evalsha(script, keys.begin(), keys.end(), args.begin(), args.end(), output); +} + +template +void Redis::script_exists(Input first, Input last, Output output) { + range_check("SCRIPT EXISTS", first, last); + + auto reply = command(cmd::script_exists_range, first, last); + + reply::to_array(*reply, output); +} + +// Transaction commands. + +template +void Redis::watch(Input first, Input last) { + auto reply = command(cmd::watch_range, first, last); + + reply::parse(*reply); +} + +// Stream commands. + +template +long long Redis::xack(const StringView &key, const StringView &group, Input first, Input last) { + auto reply = command(cmd::xack_range, key, group, first, last); + + return reply::parse(*reply); +} + +template +std::string Redis::xadd(const StringView &key, const StringView &id, Input first, Input last) { + auto reply = command(cmd::xadd_range, key, id, first, last); + + return reply::parse(*reply); +} + +template +std::string Redis::xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx) { + auto reply = command(cmd::xadd_maxlen_range, key, id, first, last, count, approx); + + return reply::parse(*reply); +} + +template +void Redis::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output) { + auto reply = command(cmd::xclaim, key, group, consumer, min_idle_time.count(), id); + + reply::to_array(*reply, output); +} + +template +void Redis::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output) { + auto reply = command(cmd::xclaim_range, + key, + group, + consumer, + min_idle_time.count(), + first, + last); + + reply::to_array(*reply, output); +} + +template +long long Redis::xdel(const StringView &key, Input first, Input last) { + auto reply = command(cmd::xdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +auto Redis::xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple { + auto reply = command(cmd::xpending, key, group); + + return reply::parse_xpending_reply(*reply, output); +} + +template +void Redis::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xpending_detail, key, group, start, end, count); + + reply::to_array(*reply, output); +} + +template +void Redis::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output) { + auto reply = command(cmd::xpending_per_consumer, key, group, start, end, count, consumer); + + reply::to_array(*reply, output); +} + +template +void Redis::xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output) { + auto reply = command(cmd::xrange, key, start, end); + + reply::to_array(*reply, output); +} + +template +void Redis::xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xrange_count, key, start, end, count); + + reply::to_array(*reply, output); +} + +template +void Redis::xread(const StringView &key, + const StringView &id, + long long count, + Output output) { + auto reply = command(cmd::xread, key, id, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type { + range_check("XREAD", first, last); + + auto reply = command(cmd::xread_range, first, last, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + auto reply = command(cmd::xread_block, key, id, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + range_check("XREAD", first, last); + + auto reply = command(cmd::xread_block_range, first, last, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output) { + auto reply = command(cmd::xreadgroup, group, consumer, key, id, count, noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + range_check("XREADGROUP", first, last); + + auto reply = command(cmd::xreadgroup_range, group, consumer, first, last, count, noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) { + auto reply = command(cmd::xreadgroup_block, + group, + consumer, + key, + id, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + range_check("XREADGROUP", first, last); + + auto reply = command(cmd::xreadgroup_block_range, + group, + consumer, + first, + last, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output) { + auto reply = command(cmd::xrevrange, key, end, start); + + reply::to_array(*reply, output); +} + +template +void Redis::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output) { + auto reply = command(cmd::xrevrange_count, key, end, start, count); + + reply::to_array(*reply, output); +} + +template +ReplyUPtr Redis::_command(Connection &connection, Cmd cmd, Args &&...args) { + assert(!connection.broken()); + + cmd(connection, std::forward(args)...); + + auto reply = connection.recv(); + + return reply; +} + +template +inline ReplyUPtr Redis::_score_command(std::true_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., true); +} + +template +inline ReplyUPtr Redis::_score_command(std::false_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., false); +} + +template +inline ReplyUPtr Redis::_score_command(Cmd cmd, Args &&... args) { + return _score_command(typename IsKvPairIter::type(), + cmd, + std::forward(args)...); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_HPP diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/redis_cluster.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/redis_cluster.h new file mode 100644 index 000000000..63c6131c1 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/redis_cluster.h @@ -0,0 +1,1439 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_H +#define SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_H + +#include +#include +#include +#include +#include "shards_pool.h" +#include "reply.h" +#include "command_options.h" +#include "utils.h" +#include "subscriber.h" +#include "pipeline.h" +#include "transaction.h" +#include "redis.h" +#include "connection.h" + +namespace sw { + +namespace redis { + +template +class QueuedRedis; + +using Transaction = QueuedRedis; + +using Pipeline = QueuedRedis; + +class RedisCluster { +public: + RedisCluster(const ConnectionOptions &connection_opts, + const ConnectionPoolOptions &pool_opts = {}, + Role role = Role::MASTER) : _pool(pool_opts, connection_opts, role) {} + + // Construct RedisCluster with URI: + // "tcp://127.0.0.1" or "tcp://127.0.0.1:6379" + // Only need to specify one URI. + explicit RedisCluster(const std::string &uri); + + RedisCluster(const RedisCluster &) = delete; + RedisCluster& operator=(const RedisCluster &) = delete; + + RedisCluster(RedisCluster &&) = default; + RedisCluster& operator=(RedisCluster &&) = default; + + Redis redis(const StringView &hash_tag, bool new_connection = true); + + Pipeline pipeline(const StringView &hash_tag, bool new_connection = true); + + Transaction transaction(const StringView &hash_tag, bool piped = false, bool new_connection = true); + + Subscriber subscriber(); + + template + auto command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && !IsIter::type>::value, ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && IsIter::type>::value, void>::type; + + template + auto command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if::value + || std::is_arithmetic::type>::value, Result>::type; + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, Result>::type; + + template + auto command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type; + + // KEY commands. + + long long del(const StringView &key); + + template + long long del(Input first, Input last); + + template + long long del(std::initializer_list il) { + return del(il.begin(), il.end()); + } + + OptionalString dump(const StringView &key); + + long long exists(const StringView &key); + + template + long long exists(Input first, Input last); + + template + long long exists(std::initializer_list il) { + return exists(il.begin(), il.end()); + } + + bool expire(const StringView &key, long long timeout); + + bool expire(const StringView &key, const std::chrono::seconds &timeout); + + bool expireat(const StringView &key, long long timestamp); + + bool expireat(const StringView &key, + const std::chrono::time_point &tp); + + bool persist(const StringView &key); + + bool pexpire(const StringView &key, long long timeout); + + bool pexpire(const StringView &key, const std::chrono::milliseconds &timeout); + + bool pexpireat(const StringView &key, long long timestamp); + + bool pexpireat(const StringView &key, + const std::chrono::time_point &tp); + + long long pttl(const StringView &key); + + void rename(const StringView &key, const StringView &newkey); + + bool renamenx(const StringView &key, const StringView &newkey); + + void restore(const StringView &key, + const StringView &val, + long long ttl, + bool replace = false); + + void restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds{0}, + bool replace = false); + + // TODO: sort + + long long touch(const StringView &key); + + template + long long touch(Input first, Input last); + + template + long long touch(std::initializer_list il) { + return touch(il.begin(), il.end()); + } + + long long ttl(const StringView &key); + + std::string type(const StringView &key); + + long long unlink(const StringView &key); + + template + long long unlink(Input first, Input last); + + template + long long unlink(std::initializer_list il) { + return unlink(il.begin(), il.end()); + } + + // STRING commands. + + long long append(const StringView &key, const StringView &str); + + long long bitcount(const StringView &key, long long start = 0, long long end = -1); + + long long bitop(BitOp op, const StringView &destination, const StringView &key); + + template + long long bitop(BitOp op, const StringView &destination, Input first, Input last); + + template + long long bitop(BitOp op, const StringView &destination, std::initializer_list il) { + return bitop(op, destination, il.begin(), il.end()); + } + + long long bitpos(const StringView &key, + long long bit, + long long start = 0, + long long end = -1); + + long long decr(const StringView &key); + + long long decrby(const StringView &key, long long decrement); + + OptionalString get(const StringView &key); + + long long getbit(const StringView &key, long long offset); + + std::string getrange(const StringView &key, long long start, long long end); + + OptionalString getset(const StringView &key, const StringView &val); + + long long incr(const StringView &key); + + long long incrby(const StringView &key, long long increment); + + double incrbyfloat(const StringView &key, double increment); + + template + void mget(Input first, Input last, Output output); + + template + void mget(std::initializer_list il, Output output) { + mget(il.begin(), il.end(), output); + } + + template + void mset(Input first, Input last); + + template + void mset(std::initializer_list il) { + mset(il.begin(), il.end()); + } + + template + bool msetnx(Input first, Input last); + + template + bool msetnx(std::initializer_list il) { + return msetnx(il.begin(), il.end()); + } + + void psetex(const StringView &key, + long long ttl, + const StringView &val); + + void psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val); + + bool set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0), + UpdateType type = UpdateType::ALWAYS); + + void setex(const StringView &key, + long long ttl, + const StringView &val); + + void setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val); + + bool setnx(const StringView &key, const StringView &val); + + long long setrange(const StringView &key, long long offset, const StringView &val); + + long long strlen(const StringView &key); + + // LIST commands. + + OptionalStringPair blpop(const StringView &key, long long timeout); + + OptionalStringPair blpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair blpop(Input first, Input last, long long timeout); + + template + OptionalStringPair blpop(std::initializer_list il, long long timeout) { + return blpop(il.begin(), il.end(), timeout); + } + + template + OptionalStringPair blpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair blpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(il.begin(), il.end(), timeout); + } + + OptionalStringPair brpop(const StringView &key, long long timeout); + + OptionalStringPair brpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair brpop(Input first, Input last, long long timeout); + + template + OptionalStringPair brpop(std::initializer_list il, long long timeout) { + return brpop(il.begin(), il.end(), timeout); + } + + template + OptionalStringPair brpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair brpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(il.begin(), il.end(), timeout); + } + + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + long long timeout); + + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + OptionalString lindex(const StringView &key, long long index); + + long long linsert(const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val); + + long long llen(const StringView &key); + + OptionalString lpop(const StringView &key); + + long long lpush(const StringView &key, const StringView &val); + + template + long long lpush(const StringView &key, Input first, Input last); + + template + long long lpush(const StringView &key, std::initializer_list il) { + return lpush(key, il.begin(), il.end()); + } + + long long lpushx(const StringView &key, const StringView &val); + + template + void lrange(const StringView &key, long long start, long long stop, Output output); + + long long lrem(const StringView &key, long long count, const StringView &val); + + void lset(const StringView &key, long long index, const StringView &val); + + void ltrim(const StringView &key, long long start, long long stop); + + OptionalString rpop(const StringView &key); + + OptionalString rpoplpush(const StringView &source, const StringView &destination); + + long long rpush(const StringView &key, const StringView &val); + + template + long long rpush(const StringView &key, Input first, Input last); + + template + long long rpush(const StringView &key, std::initializer_list il) { + return rpush(key, il.begin(), il.end()); + } + + long long rpushx(const StringView &key, const StringView &val); + + // HASH commands. + + long long hdel(const StringView &key, const StringView &field); + + template + long long hdel(const StringView &key, Input first, Input last); + + template + long long hdel(const StringView &key, std::initializer_list il) { + return hdel(key, il.begin(), il.end()); + } + + bool hexists(const StringView &key, const StringView &field); + + OptionalString hget(const StringView &key, const StringView &field); + + template + void hgetall(const StringView &key, Output output); + + long long hincrby(const StringView &key, const StringView &field, long long increment); + + double hincrbyfloat(const StringView &key, const StringView &field, double increment); + + template + void hkeys(const StringView &key, Output output); + + long long hlen(const StringView &key); + + template + void hmget(const StringView &key, Input first, Input last, Output output); + + template + void hmget(const StringView &key, std::initializer_list il, Output output) { + hmget(key, il.begin(), il.end(), output); + } + + template + void hmset(const StringView &key, Input first, Input last); + + template + void hmset(const StringView &key, std::initializer_list il) { + hmset(key, il.begin(), il.end()); + } + + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + Output output); + + bool hset(const StringView &key, const StringView &field, const StringView &val); + + bool hset(const StringView &key, const std::pair &item); + + template + auto hset(const StringView &key, Input first, Input last) + -> typename std::enable_if::value, long long>::type; + + template + long long hset(const StringView &key, std::initializer_list il) { + return hset(key, il.begin(), il.end()); + } + + bool hsetnx(const StringView &key, const StringView &field, const StringView &val); + + bool hsetnx(const StringView &key, const std::pair &item); + + long long hstrlen(const StringView &key, const StringView &field); + + template + void hvals(const StringView &key, Output output); + + // SET commands. + + long long sadd(const StringView &key, const StringView &member); + + template + long long sadd(const StringView &key, Input first, Input last); + + template + long long sadd(const StringView &key, std::initializer_list il) { + return sadd(key, il.begin(), il.end()); + } + + long long scard(const StringView &key); + + template + void sdiff(Input first, Input last, Output output); + + template + void sdiff(std::initializer_list il, Output output) { + sdiff(il.begin(), il.end(), output); + } + + long long sdiffstore(const StringView &destination, const StringView &key); + + template + long long sdiffstore(const StringView &destination, + Input first, + Input last); + + template + long long sdiffstore(const StringView &destination, + std::initializer_list il) { + return sdiffstore(destination, il.begin(), il.end()); + } + + template + void sinter(Input first, Input last, Output output); + + template + void sinter(std::initializer_list il, Output output) { + sinter(il.begin(), il.end(), output); + } + + long long sinterstore(const StringView &destination, const StringView &key); + + template + long long sinterstore(const StringView &destination, + Input first, + Input last); + + template + long long sinterstore(const StringView &destination, + std::initializer_list il) { + return sinterstore(destination, il.begin(), il.end()); + } + + bool sismember(const StringView &key, const StringView &member); + + template + void smembers(const StringView &key, Output output); + + bool smove(const StringView &source, + const StringView &destination, + const StringView &member); + + OptionalString spop(const StringView &key); + + template + void spop(const StringView &key, long long count, Output output); + + OptionalString srandmember(const StringView &key); + + template + void srandmember(const StringView &key, long long count, Output output); + + long long srem(const StringView &key, const StringView &member); + + template + long long srem(const StringView &key, Input first, Input last); + + template + long long srem(const StringView &key, std::initializer_list il) { + return srem(key, il.begin(), il.end()); + } + + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + Output output); + + template + void sunion(Input first, Input last, Output output); + + template + void sunion(std::initializer_list il, Output output) { + sunion(il.begin(), il.end(), output); + } + + long long sunionstore(const StringView &destination, const StringView &key); + + template + long long sunionstore(const StringView &destination, Input first, Input last); + + template + long long sunionstore(const StringView &destination, std::initializer_list il) { + return sunionstore(destination, il.begin(), il.end()); + } + + // SORTED SET commands. + + auto bzpopmax(const StringView &key, long long timeout) + -> Optional>; + + auto bzpopmax(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmax(Input first, Input last, long long timeout) + -> Optional>; + + template + auto bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmax(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + template + auto bzpopmax(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + auto bzpopmin(const StringView &key, long long timeout) + -> Optional>; + + auto bzpopmin(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmin(Input first, Input last, long long timeout) + -> Optional>; + + template + auto bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmin(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + template + auto bzpopmin(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + // We don't support the INCR option, since you can always use ZINCRBY instead. + long long zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + template + long long zadd(const StringView &key, + Input first, + Input last, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + template + long long zadd(const StringView &key, + std::initializer_list il, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return zadd(key, il.begin(), il.end(), type, changed); + } + + long long zcard(const StringView &key); + + template + long long zcount(const StringView &key, const Interval &interval); + + double zincrby(const StringView &key, double increment, const StringView &member); + + long long zinterstore(const StringView &destination, const StringView &key, double weight); + + template + long long zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + template + long long zinterstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zinterstore(destination, il.begin(), il.end(), type); + } + + template + long long zlexcount(const StringView &key, const Interval &interval); + + Optional> zpopmax(const StringView &key); + + template + void zpopmax(const StringView &key, long long count, Output output); + + Optional> zpopmin(const StringView &key); + + template + void zpopmin(const StringView &key, long long count, Output output); + + // If *output* is an iterator of a container of string, + // we send *ZRANGE key start stop* command. + // If it's an iterator of a container of pair, + // we send *ZRANGE key start stop WITHSCORES* command. + // + // The following code sends *ZRANGE* without the *WITHSCORES* option: + // + // vector result; + // redis.zrange("key", 0, -1, back_inserter(result)); + // + // On the other hand, the following code sends command with *WITHSCORES* option: + // + // unordered_map with_score; + // redis.zrange("key", 0, -1, inserter(with_score, with_score.end())); + // + // This also applies to other commands with the *WITHSCORES* option, + // e.g. *ZRANGEBYSCORE*, *ZREVRANGE*, *ZREVRANGEBYSCORE*. + template + void zrange(const StringView &key, long long start, long long stop, Output output); + + template + void zrangebylex(const StringView &key, const Interval &interval, Output output); + + template + void zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrangebyscore(const StringView &key, const Interval &interval, Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + OptionalLongLong zrank(const StringView &key, const StringView &member); + + long long zrem(const StringView &key, const StringView &member); + + template + long long zrem(const StringView &key, Input first, Input last); + + template + long long zrem(const StringView &key, std::initializer_list il) { + return zrem(key, il.begin(), il.end()); + } + + template + long long zremrangebylex(const StringView &key, const Interval &interval); + + long long zremrangebyrank(const StringView &key, long long start, long long stop); + + template + long long zremrangebyscore(const StringView &key, const Interval &interval); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrange(const StringView &key, long long start, long long stop, Output output); + + template + void zrevrangebylex(const StringView &key, const Interval &interval, Output output); + + template + void zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrangebyscore(const StringView &key, const Interval &interval, Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + OptionalLongLong zrevrank(const StringView &key, const StringView &member); + + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + Output output); + + OptionalDouble zscore(const StringView &key, const StringView &member); + + long long zunionstore(const StringView &destination, const StringView &key, double weight); + + template + long long zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + template + long long zunionstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zunionstore(destination, il.begin(), il.end(), type); + } + + // HYPERLOGLOG commands. + + bool pfadd(const StringView &key, const StringView &element); + + template + bool pfadd(const StringView &key, Input first, Input last); + + template + bool pfadd(const StringView &key, std::initializer_list il) { + return pfadd(key, il.begin(), il.end()); + } + + long long pfcount(const StringView &key); + + template + long long pfcount(Input first, Input last); + + template + long long pfcount(std::initializer_list il) { + return pfcount(il.begin(), il.end()); + } + + void pfmerge(const StringView &destination, const StringView &key); + + template + void pfmerge(const StringView &destination, Input first, Input last); + + template + void pfmerge(const StringView &destination, std::initializer_list il) { + pfmerge(destination, il.begin(), il.end()); + } + + // GEO commands. + + long long geoadd(const StringView &key, + const std::tuple &member); + + template + long long geoadd(const StringView &key, + Input first, + Input last); + + template + long long geoadd(const StringView &key, + std::initializer_list il) { + return geoadd(key, il.begin(), il.end()); + } + + OptionalDouble geodist(const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit = GeoUnit::M); + + OptionalString geohash(const StringView &key, const StringView &member); + + template + void geohash(const StringView &key, Input first, Input last, Output output); + + template + void geohash(const StringView &key, std::initializer_list il, Output output) { + geohash(key, il.begin(), il.end(), output); + } + + Optional> geopos(const StringView &key, const StringView &member); + + template + void geopos(const StringView &key, Input first, Input last, Output output); + + template + void geopos(const StringView &key, std::initializer_list il, Output output) { + geopos(key, il.begin(), il.end(), output); + } + + // TODO: + // 1. since we have different overloads for georadius and georadius-store, + // we might use the GEORADIUS_RO command in the future. + // 2. there're too many parameters for this method, we might refactor it. + OptionalLongLong georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // If *output* is an iterator of a container of string, we send *GEORADIUS* command + // without any options and only get the members in the specified geo range. + // If *output* is an iterator of a container of a tuple, the type of the tuple decides + // options we send with the *GEORADIUS* command. If the tuple has an element of type + // double, we send the *WITHDIST* option. If it has an element of type string, we send + // the *WITHHASH* option. If it has an element of type pair, we send + // the *WITHCOORD* option. For example: + // + // The following code only gets the members in range, i.e. without any option. + // + // vector members; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(members)) + // + // The following code sends the command with *WITHDIST* option. + // + // vector> with_dist; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist)) + // + // The following code sends the command with *WITHDIST* and *WITHHASH* options. + // + // vector> with_dist_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_hash)) + // + // The following code sends the command with *WITHDIST*, *WITHCOORD* and *WITHHASH* options. + // + // vector, string>> with_dist_coord_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_coord_hash)) + // + // This also applies to *GEORADIUSBYMEMBER*. + template + void georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + OptionalLongLong georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // See comments on *GEORADIUS*. + template + void georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + // SCRIPTING commands. + + template + Result eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last); + + template + Result eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output); + + template + void eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + template + Result evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last); + + template + Result evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output); + + template + void evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + // PUBSUB commands. + + long long publish(const StringView &channel, const StringView &message); + + // Stream commands. + + long long xack(const StringView &key, const StringView &group, const StringView &id); + + template + long long xack(const StringView &key, const StringView &group, Input first, Input last); + + template + long long xack(const StringView &key, const StringView &group, std::initializer_list il) { + return xack(key, group, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, const StringView &id, Input first, Input last); + + template + std::string xadd(const StringView &key, const StringView &id, std::initializer_list il) { + return xadd(key, id, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx = true); + + template + std::string xadd(const StringView &key, + const StringView &id, + std::initializer_list il, + long long count, + bool approx = true) { + return xadd(key, id, il.begin(), il.end(), count, approx); + } + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + std::initializer_list il, + Output output) { + xclaim(key, group, consumer, min_idle_time, il.begin(), il.end(), output); + } + + long long xdel(const StringView &key, const StringView &id); + + template + long long xdel(const StringView &key, Input first, Input last); + + template + long long xdel(const StringView &key, std::initializer_list il) { + return xdel(key, il.begin(), il.end()); + } + + void xgroup_create(const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream = false); + + void xgroup_setid(const StringView &key, const StringView &group, const StringView &id); + + long long xgroup_destroy(const StringView &key, const StringView &group); + + long long xgroup_delconsumer(const StringView &key, + const StringView &group, + const StringView &consumer); + + long long xlen(const StringView &key); + + template + auto xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple; + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + Output output) { + xread(key, id, 0, output); + } + + template + auto xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, Input last, Output output) + -> typename std::enable_if::value>::type { + xread(first, last, 0, output); + } + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + xread(key, id, timeout, 0, output); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xread(first, last, timeout, 0, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + Output output) { + xreadgroup(group, consumer, key, id, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + Output output) { + xreadgroup(group, consumer, key, id, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, 0, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + return xreadgroup(group, consumer, key, id, timeout, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + return xreadgroup(group, consumer, key, id, timeout, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, 0, false, output); + } + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output); + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output); + + long long xtrim(const StringView &key, long long count, bool approx = true); + +private: + class Command { + public: + explicit Command(const StringView &cmd_name) : _cmd_name(cmd_name) {} + + template + void operator()(Connection &connection, Args &&...args) const { + CmdArgs cmd_args; + cmd_args.append(_cmd_name, std::forward(args)...); + connection.send(cmd_args); + } + + private: + StringView _cmd_name; + }; + + template + auto _generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, + ReplyUPtr>::type; + + template + auto _generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::type>::value, + ReplyUPtr>::type; + + template + ReplyUPtr _command(Cmd cmd, Connection &connection, Args &&...args); + + template + ReplyUPtr _command(Cmd cmd, const StringView &key, Args &&...args); + + template + ReplyUPtr _command(Cmd cmd, std::true_type, const StringView &key, Args &&...args); + + template + ReplyUPtr _command(Cmd cmd, std::false_type, Input &&first, Args &&...args); + + template + ReplyUPtr _command(const StringView &cmd_name, const IndexSequence &, Args &&...args) { + return command(cmd_name, NthValue(std::forward(args)...)...); + } + + template + ReplyUPtr _range_command(Cmd cmd, std::true_type, Input input, Args &&...args); + + template + ReplyUPtr _range_command(Cmd cmd, std::false_type, Input input, Args &&...args); + + void _asking(Connection &connection); + + template + ReplyUPtr _score_command(std::true_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(std::false_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(Cmd cmd, Args &&... args); + + ShardsPool _pool; +}; + +} + +} + +#include "redis_cluster.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/redis_cluster.hpp b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/redis_cluster.hpp new file mode 100644 index 000000000..2153d181b --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/redis_cluster.hpp @@ -0,0 +1,1403 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_HPP +#define SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_HPP + +#include +#include "command.h" +#include "reply.h" +#include "utils.h" +#include "errors.h" +#include "shards_pool.h" + +namespace sw { + +namespace redis { + +template +auto RedisCluster::command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type { + return _command(cmd, + std::is_convertible::type, StringView>(), + std::forward(key), + std::forward(args)...); +} + +template +auto RedisCluster::command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && !IsIter::type>::value, ReplyUPtr>::type { + auto cmd = Command(cmd_name); + + return _generic_command(cmd, std::forward(key), std::forward(args)...); +} + +template +auto RedisCluster::command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if::value + || std::is_arithmetic::type>::value, Result>::type { + auto r = command(cmd_name, std::forward(key), std::forward(args)...); + + assert(r); + + return reply::parse(*r); +} + +template +auto RedisCluster::command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && IsIter::type>::value, void>::type { + auto r = _command(cmd_name, + MakeIndexSequence(), + std::forward(key), + std::forward(args)...); + + assert(r); + + reply::to_array(*r, LastValue(std::forward(args)...)); +} + +template +auto RedisCluster::command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type { + if (first == last || std::next(first) == last) { + throw Error("command: invalid range"); + } + + const auto &key = *first; + ++first; + + auto cmd = [&key](Connection &connection, Input first, Input last) { + CmdArgs cmd_args; + cmd_args.append(key); + while (first != last) { + cmd_args.append(*first); + ++first; + } + connection.send(cmd_args); + }; + + return command(cmd, first, last); +} + +template +auto RedisCluster::command(Input first, Input last) + -> typename std::enable_if::value, Result>::type { + auto r = command(first, last); + + assert(r); + + return reply::parse(*r); +} + +template +auto RedisCluster::command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type { + auto r = command(first, last); + + assert(r); + + reply::to_array(*r, output); +} + +// KEY commands. + +template +long long RedisCluster::del(Input first, Input last) { + range_check("DEL", first, last); + + auto reply = command(cmd::del_range, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::exists(Input first, Input last) { + range_check("EXISTS", first, last); + + auto reply = command(cmd::exists_range, first, last); + + return reply::parse(*reply); +} + +inline bool RedisCluster::expire(const StringView &key, const std::chrono::seconds &timeout) { + return expire(key, timeout.count()); +} + +inline bool RedisCluster::expireat(const StringView &key, + const std::chrono::time_point &tp) { + return expireat(key, tp.time_since_epoch().count()); +} + +inline bool RedisCluster::pexpire(const StringView &key, const std::chrono::milliseconds &timeout) { + return pexpire(key, timeout.count()); +} + +inline bool RedisCluster::pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return pexpireat(key, tp.time_since_epoch().count()); +} + +inline void RedisCluster::restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl, + bool replace) { + return restore(key, val, ttl.count(), replace); +} + +template +long long RedisCluster::touch(Input first, Input last) { + range_check("TOUCH", first, last); + + auto reply = command(cmd::touch_range, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::unlink(Input first, Input last) { + range_check("UNLINK", first, last); + + auto reply = command(cmd::unlink_range, first, last); + + return reply::parse(*reply); +} + +// STRING commands. + +template +long long RedisCluster::bitop(BitOp op, const StringView &destination, Input first, Input last) { + range_check("BITOP", first, last); + + auto reply = _command(cmd::bitop_range, destination, op, destination, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::mget(Input first, Input last, Output output) { + range_check("MGET", first, last); + + auto reply = command(cmd::mget, first, last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::mset(Input first, Input last) { + range_check("MSET", first, last); + + auto reply = command(cmd::mset, first, last); + + reply::parse(*reply); +} + +template +bool RedisCluster::msetnx(Input first, Input last) { + range_check("MSETNX", first, last); + + auto reply = command(cmd::msetnx, first, last); + + return reply::parse(*reply); +} + +inline void RedisCluster::psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val) { + return psetex(key, ttl.count(), val); +} + +inline void RedisCluster::setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val) { + setex(key, ttl.count(), val); +} + +// LIST commands. + +template +OptionalStringPair RedisCluster::blpop(Input first, Input last, long long timeout) { + range_check("BLPOP", first, last); + + auto reply = command(cmd::blpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair RedisCluster::blpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return blpop(first, last, timeout.count()); +} + +template +OptionalStringPair RedisCluster::brpop(Input first, Input last, long long timeout) { + range_check("BRPOP", first, last); + + auto reply = command(cmd::brpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair RedisCluster::brpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return brpop(first, last, timeout.count()); +} + +inline OptionalString RedisCluster::brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout) { + return brpoplpush(source, destination, timeout.count()); +} + +template +inline long long RedisCluster::lpush(const StringView &key, Input first, Input last) { + range_check("LPUSH", first, last); + + auto reply = command(cmd::lpush_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void RedisCluster::lrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = command(cmd::lrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline long long RedisCluster::rpush(const StringView &key, Input first, Input last) { + range_check("RPUSH", first, last); + + auto reply = command(cmd::rpush_range, key, first, last); + + return reply::parse(*reply); +} + +// HASH commands. + +template +inline long long RedisCluster::hdel(const StringView &key, Input first, Input last) { + range_check("HDEL", first, last); + + auto reply = command(cmd::hdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void RedisCluster::hgetall(const StringView &key, Output output) { + auto reply = command(cmd::hgetall, key); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::hkeys(const StringView &key, Output output) { + auto reply = command(cmd::hkeys, key); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::hmget(const StringView &key, Input first, Input last, Output output) { + range_check("HMGET", first, last); + + auto reply = command(cmd::hmget, key, first, last); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::hmset(const StringView &key, Input first, Input last) { + range_check("HMSET", first, last); + + auto reply = command(cmd::hmset, key, first, last); + + reply::parse(*reply); +} + +template +long long RedisCluster::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::hscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long RedisCluster::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return hscan(key, cursor, pattern, 10, output); +} + +template +inline long long RedisCluster::hscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return hscan(key, cursor, "*", count, output); +} + +template +inline long long RedisCluster::hscan(const StringView &key, + long long cursor, + Output output) { + return hscan(key, cursor, "*", 10, output); +} + +template +auto RedisCluster::hset(const StringView &key, Input first, Input last) + -> typename std::enable_if::value, + long long>::type { + range_check("HSET", first, last); + + auto reply = command(cmd::hset_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void RedisCluster::hvals(const StringView &key, Output output) { + auto reply = command(cmd::hvals, key); + + reply::to_array(*reply, output); +} + +// SET commands. + +template +long long RedisCluster::sadd(const StringView &key, Input first, Input last) { + range_check("SADD", first, last); + + auto reply = command(cmd::sadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::sdiff(Input first, Input last, Output output) { + range_check("SDIFF", first, last); + + auto reply = command(cmd::sdiff, first, last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::sdiffstore(const StringView &destination, + Input first, + Input last) { + range_check("SDIFFSTORE", first, last); + + auto reply = command(cmd::sdiffstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::sinter(Input first, Input last, Output output) { + range_check("SINTER", first, last); + + auto reply = command(cmd::sinter, first, last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::sinterstore(const StringView &destination, + Input first, + Input last) { + range_check("SINTERSTORE", first, last); + + auto reply = command(cmd::sinterstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::smembers(const StringView &key, Output output) { + auto reply = command(cmd::smembers, key); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::spop(const StringView &key, long long count, Output output) { + auto reply = command(cmd::spop_range, key, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::srandmember(const StringView &key, long long count, Output output) { + auto reply = command(cmd::srandmember_range, key, count); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::srem(const StringView &key, Input first, Input last) { + range_check("SREM", first, last); + + auto reply = command(cmd::srem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::sscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long RedisCluster::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return sscan(key, cursor, pattern, 10, output); +} + +template +inline long long RedisCluster::sscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return sscan(key, cursor, "*", count, output); +} + +template +inline long long RedisCluster::sscan(const StringView &key, + long long cursor, + Output output) { + return sscan(key, cursor, "*", 10, output); +} + +template +void RedisCluster::sunion(Input first, Input last, Output output) { + range_check("SUNION", first, last); + + auto reply = command(cmd::sunion, first, last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::sunionstore(const StringView &destination, Input first, Input last) { + range_check("SUNIONSTORE", first, last); + + auto reply = command(cmd::sunionstore_range, destination, first, last); + + return reply::parse(*reply); +} + +// SORTED SET commands. + +inline auto RedisCluster::bzpopmax(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(key, timeout.count()); +} + +template +auto RedisCluster::bzpopmax(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmax_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto RedisCluster::bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(first, last, timeout.count()); +} + +inline auto RedisCluster::bzpopmin(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(key, timeout.count()); +} + +template +auto RedisCluster::bzpopmin(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmin_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto RedisCluster::bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(first, last, timeout.count()); +} + +template +long long RedisCluster::zadd(const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed) { + range_check("ZADD", first, last); + + auto reply = command(cmd::zadd_range, key, first, last, type, changed); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zcount, key, interval); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + range_check("ZINTERSTORE", first, last); + + auto reply = command(cmd::zinterstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zlexcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zlexcount, key, interval); + + return reply::parse(*reply); +} + +template +void RedisCluster::zpopmax(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmax, key, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zpopmin(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmin, key, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrangebylex(const StringView &key, const Interval &interval, Output output) { + zrangebylex(key, interval, {}, output); +} + +template +void RedisCluster::zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrangebyscore(const StringView &key, + const Interval &interval, + Output output) { + zrangebyscore(key, interval, {}, output); +} + +template +void RedisCluster::zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrangebyscore, + key, + interval, + opts); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::zrem(const StringView &key, Input first, Input last) { + range_check("ZREM", first, last); + + auto reply = command(cmd::zrem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zremrangebylex(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebylex, key, interval); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zremrangebyscore(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebyscore, key, interval); + + return reply::parse(*reply); +} + +template +void RedisCluster::zrevrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrevrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::zrevrangebylex(const StringView &key, + const Interval &interval, + Output output) { + zrevrangebylex(key, interval, {}, output); +} + +template +void RedisCluster::zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrevrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrevrangebyscore(const StringView &key, const Interval &interval, Output output) { + zrevrangebyscore(key, interval, {}, output); +} + +template +void RedisCluster::zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrevrangebyscore, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::zscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long RedisCluster::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return zscan(key, cursor, pattern, 10, output); +} + +template +inline long long RedisCluster::zscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return zscan(key, cursor, "*", count, output); +} + +template +inline long long RedisCluster::zscan(const StringView &key, + long long cursor, + Output output) { + return zscan(key, cursor, "*", 10, output); +} + +template +long long RedisCluster::zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + range_check("ZUNIONSTORE", first, last); + + auto reply = command(cmd::zunionstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +// HYPERLOGLOG commands. + +template +bool RedisCluster::pfadd(const StringView &key, Input first, Input last) { + range_check("PFADD", first, last); + + auto reply = command(cmd::pfadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::pfcount(Input first, Input last) { + range_check("PFCOUNT", first, last); + + auto reply = command(cmd::pfcount_range, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::pfmerge(const StringView &destination, + Input first, + Input last) { + range_check("PFMERGE", first, last); + + auto reply = command(cmd::pfmerge_range, destination, first, last); + + reply::parse(*reply); +} + +// GEO commands. + +template +inline long long RedisCluster::geoadd(const StringView &key, + Input first, + Input last) { + range_check("GEOADD", first, last); + + auto reply = command(cmd::geoadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::geohash(const StringView &key, Input first, Input last, Output output) { + range_check("GEOHASH", first, last); + + auto reply = command(cmd::geohash_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::geopos(const StringView &key, Input first, Input last, Output output) { + range_check("GEOPOS", first, last); + + auto reply = command(cmd::geopos_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadius, + key, + loc, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadiusbymember, + key, + member, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +// SCRIPTING commands. + +template +Result RedisCluster::eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + if (keys_first == keys_last) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = _command(cmd::eval, *keys_first, script, keys_first, keys_last, args_first, args_last); + + return reply::parse(*reply); +} + +template +Result RedisCluster::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return eval(script, keys.begin(), keys.end(), args.begin(), args.end()); +} + +template +void RedisCluster::eval(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output) { + if (keys_first == keys_last) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = _command(cmd::eval, + *keys_first, + script, + keys_first, keys_last, + args_first, args_last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + eval(script, keys.begin(), keys.end(), args.begin(), args.end(), output); +} + +template +Result RedisCluster::evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last) { + if (keys_first == keys_last) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = _command(cmd::evalsha, *keys_first, script, + keys_first, keys_last, args_first, args_last); + + return reply::parse(*reply); +} + +template +Result RedisCluster::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return evalsha(script, keys.begin(), keys.end(), args.begin(), args.end()); +} + +template +void RedisCluster::evalsha(const StringView &script, + Keys keys_first, + Keys keys_last, + Args args_first, + Args args_last, + Output output) { + if (keys_first == keys_last) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = _command(cmd::evalsha, + *keys_first, + script, + keys_first, keys_last, + args_first, args_last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + evalsha(script, keys.begin(), keys.end(), args.begin(), args.end(), output); +} + +// Stream commands. + +template +long long RedisCluster::xack(const StringView &key, + const StringView &group, + Input first, + Input last) { + auto reply = command(cmd::xack_range, key, group, first, last); + + return reply::parse(*reply); +} + +template +std::string RedisCluster::xadd(const StringView &key, + const StringView &id, + Input first, + Input last) { + auto reply = command(cmd::xadd_range, key, id, first, last); + + return reply::parse(*reply); +} + +template +std::string RedisCluster::xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx) { + auto reply = command(cmd::xadd_maxlen_range, key, id, first, last, count, approx); + + return reply::parse(*reply); +} + +template +void RedisCluster::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output) { + auto reply = command(cmd::xclaim, key, group, consumer, min_idle_time.count(), id); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output) { + auto reply = command(cmd::xclaim_range, + key, + group, + consumer, + min_idle_time.count(), + first, + last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::xdel(const StringView &key, Input first, Input last) { + auto reply = command(cmd::xdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +auto RedisCluster::xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple { + auto reply = command(cmd::xpending, key, group); + + return reply::parse_xpending_reply(*reply, output); +} + +template +void RedisCluster::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xpending_detail, key, group, start, end, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output) { + auto reply = command(cmd::xpending_per_consumer, key, group, start, end, count, consumer); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output) { + auto reply = command(cmd::xrange, key, start, end); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xrange_count, key, start, end, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xread(const StringView &key, + const StringView &id, + long long count, + Output output) { + auto reply = command(cmd::xread, key, id, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type { + range_check("XREAD", first, last); + + auto reply = command(cmd::xread_range, first, last, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + auto reply = command(cmd::xread_block, key, id, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + range_check("XREAD", first, last); + + auto reply = command(cmd::xread_block_range, first, last, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output) { + auto reply = _command(cmd::xreadgroup, key, group, consumer, key, id, count, noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + range_check("XREADGROUP", first, last); + + auto reply = _command(cmd::xreadgroup_range, + first->first, + group, + consumer, + first, + last, + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) { + auto reply = _command(cmd::xreadgroup_block, + key, + group, + consumer, + key, + id, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + range_check("XREADGROUP", first, last); + + auto reply = _command(cmd::xreadgroup_block_range, + first->first, + group, + consumer, + first, + last, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output) { + auto reply = command(cmd::xrevrange, key, end, start); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output) { + auto reply = command(cmd::xrevrange_count, key, end, start, count); + + reply::to_array(*reply, output); +} + +template +auto RedisCluster::_generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, + ReplyUPtr>::type { + return command(cmd, std::forward(key), std::forward(args)...); +} + +template +auto RedisCluster::_generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::type>::value, + ReplyUPtr>::type { + auto k = std::to_string(std::forward(key)); + return command(cmd, k, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, std::true_type, const StringView &key, Args &&...args) { + return _command(cmd, key, key, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, std::false_type, Input &&first, Args &&...args) { + return _range_command(cmd, + std::is_convertible< + typename std::decay< + decltype(*std::declval())>::type, StringView>(), + std::forward(first), + std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_range_command(Cmd cmd, std::true_type, Input input, Args &&...args) { + return _command(cmd, *input, input, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_range_command(Cmd cmd, std::false_type, Input input, Args &&...args) { + return _command(cmd, std::get<0>(*input), input, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, Connection &connection, Args &&...args) { + assert(!connection.broken()); + + cmd(connection, std::forward(args)...); + + return connection.recv(); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, const StringView &key, Args &&...args) { + for (auto idx = 0; idx < 2; ++idx) { + try { + auto pool = _pool.fetch(key); + assert(pool); + SafeConnection safe_connection(*pool); + + return _command(cmd, safe_connection.connection(), std::forward(args)...); + } catch (const IoError &err) { + // When master is down, one of its replicas will be promoted to be the new master. + // If we try to send command to the old master, we'll get an *IoError*. + // In this case, we need to update the slots mapping. + _pool.update(); + } catch (const ClosedError &err) { + // Node might be removed. + // 1. Get up-to-date slot mapping to check if the node still exists. + _pool.update(); + + // TODO: + // 2. If it's NOT exist, update slot mapping, and retry. + // 3. If it's still exist, that means the node is down, NOT removed, throw exception. + } catch (const MovedError &err) { + // Slot mapping has been changed, update it and try again. + _pool.update(); + } catch (const AskError &err) { + auto pool = _pool.fetch(err.node()); + assert(pool); + SafeConnection safe_connection(*pool); + auto &connection = safe_connection.connection(); + + // 1. send ASKING command. + _asking(connection); + + // 2. resend last command. + try { + return _command(cmd, connection, std::forward(args)...); + } catch (const MovedError &err) { + throw Error("Slot migrating... ASKING node hasn't been set to IMPORTING state"); + } + } // For other exceptions, just throw it. + } + + // Possible failures: + // 1. Source node has already run 'CLUSTER SETSLOT xxx NODE xxx', + // while the destination node has NOT run it. + // In this case, client will be redirected by both nodes with MovedError. + // 2. Other failures... + throw Error("Failed to send command with key: " + std::string(key.data(), key.size())); +} + +template +inline ReplyUPtr RedisCluster::_score_command(std::true_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., true); +} + +template +inline ReplyUPtr RedisCluster::_score_command(std::false_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., false); +} + +template +inline ReplyUPtr RedisCluster::_score_command(Cmd cmd, Args &&... args) { + return _score_command(typename IsKvPairIter::type(), + cmd, + std::forward(args)...); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_HPP diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/reply.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/reply.h new file mode 100644 index 000000000..d89174329 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/reply.h @@ -0,0 +1,435 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_REPLY_H +#define SEWENEW_REDISPLUSPLUS_REPLY_H + +#include +#include +#include +#include +#include +#include +#include +#include "errors.h" +#include "utils.h" + +namespace sw { + +namespace redis { + +struct ReplyDeleter { + void operator()(redisReply *reply) const { + if (reply != nullptr) { + freeReplyObject(reply); + } + } +}; + +using ReplyUPtr = std::unique_ptr; + +namespace reply { + +template +struct ParseTag {}; + +template +inline T parse(redisReply &reply) { + return parse(ParseTag(), reply); +} + +template +T parse_leniently(redisReply &reply); + +void parse(ParseTag, redisReply &reply); + +std::string parse(ParseTag, redisReply &reply); + +long long parse(ParseTag, redisReply &reply); + +double parse(ParseTag, redisReply &reply); + +bool parse(ParseTag, redisReply &reply); + +template +Optional parse(ParseTag>, redisReply &reply); + +template +std::pair parse(ParseTag>, redisReply &reply); + +template +std::tuple parse(ParseTag>, redisReply &reply); + +#ifdef REDIS_PLUS_PLUS_HAS_VARIANT + +inline Monostate parse(ParseTag, redisReply &) { + // Just ignore the reply + return {}; +} + +template +Variant parse(ParseTag>, redisReply &reply); + +#endif + +template ::value, int>::type = 0> +T parse(ParseTag, redisReply &reply); + +template ::value, int>::type = 0> +T parse(ParseTag, redisReply &reply); + +template +long long parse_scan_reply(redisReply &reply, Output output); + +inline bool is_error(redisReply &reply) { + return reply.type == REDIS_REPLY_ERROR; +} + +inline bool is_nil(redisReply &reply) { + return reply.type == REDIS_REPLY_NIL; +} + +inline bool is_string(redisReply &reply) { + return reply.type == REDIS_REPLY_STRING; +} + +inline bool is_status(redisReply &reply) { + return reply.type == REDIS_REPLY_STATUS; +} + +inline bool is_integer(redisReply &reply) { + return reply.type == REDIS_REPLY_INTEGER; +} + +inline bool is_array(redisReply &reply) { + return reply.type == REDIS_REPLY_ARRAY; +} + +std::string to_status(redisReply &reply); + +template +void to_array(redisReply &reply, Output output); + +// Rewrite set reply to bool type +void rewrite_set_reply(redisReply &reply); + +// Some command might return an empty array reply as a nil reply, +// e.g. georadius, zpopmin, zpopmax. In this case, we rewrite the +// reply to a nil reply. +void rewrite_empty_array_reply(redisReply &reply); + +template +auto parse_xpending_reply(redisReply &reply, Output output) + -> std::tuple; + +} + +// Inline implementations. + +namespace reply { + +namespace detail { + +template +void to_array(redisReply &reply, Output output) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + if (reply.element == nullptr) { + // Empty array. + return; + } + + for (std::size_t idx = 0; idx != reply.elements; ++idx) { + auto *sub_reply = reply.element[idx]; + if (sub_reply == nullptr) { + throw ProtoError("Null array element reply"); + } + + *output = parse::type>(*sub_reply); + + ++output; + } +} + +bool is_flat_array(redisReply &reply); + +template +void to_flat_array(redisReply &reply, Output output) { + if (reply.element == nullptr) { + // Empty array. + return; + } + + if (reply.elements % 2 != 0) { + throw ProtoError("Not string pair array reply"); + } + + for (std::size_t idx = 0; idx != reply.elements; idx += 2) { + auto *key_reply = reply.element[idx]; + auto *val_reply = reply.element[idx + 1]; + if (key_reply == nullptr || val_reply == nullptr) { + throw ProtoError("Null string array reply"); + } + + using Pair = typename IterType::type; + using FirstType = typename std::decay::type; + using SecondType = typename std::decay::type; + *output = std::make_pair(parse(*key_reply), + parse(*val_reply)); + + ++output; + } +} + +template +void to_array(std::true_type, redisReply &reply, Output output) { + if (is_flat_array(reply)) { + to_flat_array(reply, output); + } else { + to_array(reply, output); + } +} + +template +void to_array(std::false_type, redisReply &reply, Output output) { + to_array(reply, output); +} + +template +std::tuple parse_tuple(redisReply **reply, std::size_t idx) { + assert(reply != nullptr); + + auto *sub_reply = reply[idx]; + if (sub_reply == nullptr) { + throw ProtoError("Null reply"); + } + + return std::make_tuple(parse(*sub_reply)); +} + +template +auto parse_tuple(redisReply **reply, std::size_t idx) -> + typename std::enable_if>::type { + assert(reply != nullptr); + + return std::tuple_cat(parse_tuple(reply, idx), + parse_tuple(reply, idx + 1)); +} + +#ifdef REDIS_PLUS_PLUS_HAS_VARIANT + +template +Variant parse_variant(redisReply &reply) { + return parse(reply); +} + +template +auto parse_variant(redisReply &reply) -> + typename std::enable_if>::type { + auto return_var = [](auto &&arg) { + return Variant(std::move(arg)); + }; + + try { + return std::visit(return_var, parse_variant(reply)); + } catch (const ProtoError &) { + return std::visit(return_var, parse_variant(reply)); + } +} + +#endif + +} + +template +T parse_leniently(redisReply &reply) { + if (is_array(reply) && reply.elements == 1) { + if (reply.element == nullptr) { + throw ProtoError("null array reply"); + } + + auto *ele = reply.element[0]; + if (ele != nullptr) { + return parse(*ele); + } // else fall through + } + + return parse(reply); +} + +template +Optional parse(ParseTag>, redisReply &reply) { + if (reply::is_nil(reply)) { + // Because of a GCC bug, we cannot return {} for -std=c++17 + // Refer to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86465 +#if defined REDIS_PLUS_PLUS_HAS_OPTIONAL + return std::nullopt; +#else + return {}; +#endif + } + + return Optional(parse(reply)); +} + +template +std::pair parse(ParseTag>, redisReply &reply) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + if (reply.elements != 2) { + throw ProtoError("NOT key-value PAIR reply"); + } + + if (reply.element == nullptr) { + throw ProtoError("Null PAIR reply"); + } + + auto *first = reply.element[0]; + auto *second = reply.element[1]; + if (first == nullptr || second == nullptr) { + throw ProtoError("Null pair reply"); + } + + return std::make_pair(parse::type>(*first), + parse::type>(*second)); +} + +template +std::tuple parse(ParseTag>, redisReply &reply) { + constexpr auto size = sizeof...(Args); + + static_assert(size > 0, "DO NOT support parsing tuple with 0 element"); + + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + if (reply.elements != size) { + throw ProtoError("Expect tuple reply with " + std::to_string(size) + "elements"); + } + + if (reply.element == nullptr) { + throw ProtoError("Null TUPLE reply"); + } + + return detail::parse_tuple(reply.element, 0); +} + +#ifdef REDIS_PLUS_PLUS_HAS_VARIANT + +template +Variant parse(ParseTag>, redisReply &reply) { + return detail::parse_variant(reply); +} + +#endif + +template ::value, int>::type> +T parse(ParseTag, redisReply &reply) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + T container; + + to_array(reply, std::back_inserter(container)); + + return container; +} + +template ::value, int>::type> +T parse(ParseTag, redisReply &reply) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + T container; + + to_array(reply, std::inserter(container, container.end())); + + return container; +} + +template +long long parse_scan_reply(redisReply &reply, Output output) { + if (reply.elements != 2 || reply.element == nullptr) { + throw ProtoError("Invalid scan reply"); + } + + auto *cursor_reply = reply.element[0]; + auto *data_reply = reply.element[1]; + if (cursor_reply == nullptr || data_reply == nullptr) { + throw ProtoError("Invalid cursor reply or data reply"); + } + + auto cursor_str = reply::parse(*cursor_reply); + long long new_cursor = 0; + try { + new_cursor = std::stoll(cursor_str); + } catch (const std::exception &e) { + throw ProtoError("Invalid cursor reply: " + cursor_str); + } + + reply::to_array(*data_reply, output); + + return new_cursor; +} + +template +void to_array(redisReply &reply, Output output) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + detail::to_array(typename IsKvPairIter::type(), reply, output); +} + +template +auto parse_xpending_reply(redisReply &reply, Output output) + -> std::tuple { + if (!is_array(reply) || reply.elements != 4) { + throw ProtoError("expect array reply with 4 elements"); + } + + for (std::size_t idx = 0; idx != reply.elements; ++idx) { + if (reply.element[idx] == nullptr) { + throw ProtoError("null array reply"); + } + } + + auto num = parse(*(reply.element[0])); + auto start = parse(*(reply.element[1])); + auto end = parse(*(reply.element[2])); + + auto &entry_reply = *(reply.element[3]); + if (!is_nil(entry_reply)) { + to_array(entry_reply, output); + } + + return std::make_tuple(num, std::move(start), std::move(end)); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_REPLY_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/sentinel.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/sentinel.h new file mode 100644 index 000000000..6f998791e --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/sentinel.h @@ -0,0 +1,141 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_SENTINEL_H +#define SEWENEW_REDISPLUSPLUS_SENTINEL_H + +#include +#include +#include +#include +#include +#include "connection.h" +#include "shards.h" +#include "reply.h" +#include "tls.h" + +namespace sw { + +namespace redis { + +struct SentinelOptions { + std::vector> nodes; + + std::string password; + + bool keep_alive = true; + + std::chrono::milliseconds connect_timeout{100}; + + std::chrono::milliseconds socket_timeout{100}; + + std::chrono::milliseconds retry_interval{100}; + + std::size_t max_retry = 2; + + tls::TlsOptions tls; +}; + +class Sentinel { +public: + explicit Sentinel(const SentinelOptions &sentinel_opts); + + Sentinel(const Sentinel &) = delete; + Sentinel& operator=(const Sentinel &) = delete; + + Sentinel(Sentinel &&) = delete; + Sentinel& operator=(Sentinel &&) = delete; + + ~Sentinel() = default; + +private: + Connection master(const std::string &master_name, const ConnectionOptions &opts); + + Connection slave(const std::string &master_name, const ConnectionOptions &opts); + + class Iterator; + + friend class SimpleSentinel; + + std::list _parse_options(const SentinelOptions &opts) const; + + Optional _get_master_addr_by_name(Connection &connection, const StringView &name); + + std::vector _get_slave_addr_by_name(Connection &connection, const StringView &name); + + Connection _connect_redis(const Node &node, ConnectionOptions opts); + + Role _get_role(Connection &connection); + + std::vector _parse_slave_info(redisReply &reply) const; + + std::list _healthy_sentinels; + + std::list _broken_sentinels; + + SentinelOptions _sentinel_opts; + + std::mutex _mutex; +}; + +class SimpleSentinel { +public: + SimpleSentinel(const std::shared_ptr &sentinel, + const std::string &master_name, + Role role); + + SimpleSentinel() = default; + + SimpleSentinel(const SimpleSentinel &) = default; + SimpleSentinel& operator=(const SimpleSentinel &) = default; + + SimpleSentinel(SimpleSentinel &&) = default; + SimpleSentinel& operator=(SimpleSentinel &&) = default; + + ~SimpleSentinel() = default; + + explicit operator bool() const { + return bool(_sentinel); + } + + Connection create(const ConnectionOptions &opts); + +private: + std::shared_ptr _sentinel; + + std::string _master_name; + + Role _role = Role::MASTER; +}; + +class StopIterError : public Error { +public: + StopIterError() : Error("StopIterError") {} + + StopIterError(const StopIterError &) = default; + StopIterError& operator=(const StopIterError &) = default; + + StopIterError(StopIterError &&) = default; + StopIterError& operator=(StopIterError &&) = default; + + virtual ~StopIterError() override = default; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SENTINEL_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/shards.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/shards.h new file mode 100644 index 000000000..0d4cd97b3 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/shards.h @@ -0,0 +1,115 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_SHARDS_H +#define SEWENEW_REDISPLUSPLUS_SHARDS_H + +#include +#include +#include "errors.h" + +namespace sw { + +namespace redis { + +using Slot = std::size_t; + +struct SlotRange { + Slot min; + Slot max; +}; + +inline bool operator<(const SlotRange &lhs, const SlotRange &rhs) { + return lhs.max < rhs.max; +} + +struct Node { + std::string host; + int port; +}; + +inline bool operator==(const Node &lhs, const Node &rhs) { + return lhs.host == rhs.host && lhs.port == rhs.port; +} + +struct NodeHash { + std::size_t operator()(const Node &node) const noexcept { + auto host_hash = std::hash{}(node.host); + auto port_hash = std::hash{}(node.port); + return host_hash ^ (port_hash << 1); + } +}; + +using Shards = std::map; + +class RedirectionError : public ReplyError { +public: + RedirectionError(const std::string &msg); + + RedirectionError(const RedirectionError &) = default; + RedirectionError& operator=(const RedirectionError &) = default; + + RedirectionError(RedirectionError &&) = default; + RedirectionError& operator=(RedirectionError &&) = default; + + virtual ~RedirectionError() override = default; + + Slot slot() const { + return _slot; + } + + const Node& node() const { + return _node; + } + +private: + std::pair _parse_error(const std::string &msg) const; + + Slot _slot = 0; + Node _node; +}; + +class MovedError : public RedirectionError { +public: + explicit MovedError(const std::string &msg) : RedirectionError(msg) {} + + MovedError(const MovedError &) = default; + MovedError& operator=(const MovedError &) = default; + + MovedError(MovedError &&) = default; + MovedError& operator=(MovedError &&) = default; + + virtual ~MovedError() override = default; +}; + +class AskError : public RedirectionError { +public: + explicit AskError(const std::string &msg) : RedirectionError(msg) {} + + AskError(const AskError &) = default; + AskError& operator=(const AskError &) = default; + + AskError(AskError &&) = default; + AskError& operator=(AskError &&) = default; + + virtual ~AskError() override = default; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SHARDS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/shards_pool.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/shards_pool.h new file mode 100644 index 000000000..e6e9e4a52 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/shards_pool.h @@ -0,0 +1,121 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H +#define SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H + +#include +#include +#include +#include +#include +#include "reply.h" +#include "connection_pool.h" +#include "shards.h" + +namespace sw { + +namespace redis { + +class ShardsPool { +public: + ShardsPool() = default; + + ShardsPool(const ShardsPool &that) = delete; + ShardsPool& operator=(const ShardsPool &that) = delete; + + ShardsPool(ShardsPool &&that); + ShardsPool& operator=(ShardsPool &&that); + + ~ShardsPool() = default; + + ShardsPool(const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts, + Role role); + + // Fetch a connection by key. + ConnectionPoolSPtr fetch(const StringView &key); + + // Randomly pick a connection. + ConnectionPoolSPtr fetch(); + + // Fetch a connection by node. + ConnectionPoolSPtr fetch(const Node &node); + + void update(); + + ConnectionOptions connection_options(const StringView &key); + + ConnectionOptions connection_options(); + + Shards shards(); + +private: + void _move(ShardsPool &&that); + + void _init_pool(const Shards &shards); + + Shards _cluster_slots(Connection &connection) const; + + ReplyUPtr _cluster_slots_command(Connection &connection) const; + + Shards _parse_reply(redisReply &reply) const; + + Slot _parse_slot(redisReply *reply) const; + + Node _parse_node(redisReply *reply) const; + + std::pair _parse_slot_info(redisReply &reply) const; + + // Get slot by key. + std::size_t _slot(const StringView &key) const; + + // Randomly pick a slot. + std::size_t _slot() const; + + // Get a random number between [min, max] + std::size_t _random(std::size_t min, std::size_t max) const; + + ConnectionPoolSPtr& _get_pool(Slot slot); + + ConnectionPoolSPtr _fetch(Slot slot); + + ConnectionOptions _connection_options(Slot slot); + + using NodeMap = std::unordered_map; + + NodeMap::iterator _add_node(const Node &node); + + ConnectionPoolOptions _pool_opts; + + ConnectionOptions _connection_opts; + + Shards _shards; + + NodeMap _pools; + + std::mutex _mutex; + + Role _role = Role::MASTER; + + static const std::size_t SHARDS = 16383; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/subscriber.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/subscriber.h new file mode 100644 index 000000000..8b7c5cfb4 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/subscriber.h @@ -0,0 +1,231 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H +#define SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H + +#include +#include +#include +#include "connection.h" +#include "reply.h" +#include "command.h" +#include "utils.h" + +namespace sw { + +namespace redis { + +// @NOTE: Subscriber is NOT thread-safe. +// Subscriber uses callbacks to handle messages. There are 6 kinds of messages: +// 1) MESSAGE: message sent to a channel. +// 2) PMESSAGE: message sent to channels of a given pattern. +// 3) SUBSCRIBE: meta message sent when we successfully subscribe to a channel. +// 4) UNSUBSCRIBE: meta message sent when we successfully unsubscribe to a channel. +// 5) PSUBSCRIBE: meta message sent when we successfully subscribe to a channel pattern. +// 6) PUNSUBSCRIBE: meta message sent when we successfully unsubscribe to a channel pattern. +// +// Use Subscriber::on_message(MsgCallback) to set the callback function for message of +// *MESSAGE* type, and the callback interface is: +// void (std::string channel, std::string msg) +// +// Use Subscriber::on_pmessage(PatternMsgCallback) to set the callback function for message of +// *PMESSAGE* type, and the callback interface is: +// void (std::string pattern, std::string channel, std::string msg) +// +// Messages of other types are called *META MESSAGE*, they have the same callback interface. +// Use Subscriber::on_meta(MetaCallback) to set the callback function: +// void (Subscriber::MsgType type, OptionalString channel, long long num) +// +// NOTE: If we haven't subscribe/psubscribe to any channel/pattern, and try to +// unsubscribe/punsubscribe without any parameter, i.e. unsubscribe/punsubscribe all +// channels/patterns, *channel* will be null. So the second parameter of meta callback +// is of type *OptionalString*. +// +// All these callback interfaces pass std::string by value, and you can take their ownership +// (i.e. std::move) safely. +// +// If you don't set callback for a specific kind of message, Subscriber::consume() will +// receive the message, and ignore it, i.e. no callback will be called. +class Subscriber { +public: + Subscriber(const Subscriber &) = delete; + Subscriber& operator=(const Subscriber &) = delete; + + Subscriber(Subscriber &&) = default; + Subscriber& operator=(Subscriber &&) = default; + + ~Subscriber() = default; + + enum class MsgType { + SUBSCRIBE, + UNSUBSCRIBE, + PSUBSCRIBE, + PUNSUBSCRIBE, + MESSAGE, + PMESSAGE + }; + + template + void on_message(MsgCb msg_callback); + + template + void on_pmessage(PMsgCb pmsg_callback); + + template + void on_meta(MetaCb meta_callback); + + void subscribe(const StringView &channel); + + template + void subscribe(Input first, Input last); + + template + void subscribe(std::initializer_list channels) { + subscribe(channels.begin(), channels.end()); + } + + void unsubscribe(); + + void unsubscribe(const StringView &channel); + + template + void unsubscribe(Input first, Input last); + + template + void unsubscribe(std::initializer_list channels) { + unsubscribe(channels.begin(), channels.end()); + } + + void psubscribe(const StringView &pattern); + + template + void psubscribe(Input first, Input last); + + template + void psubscribe(std::initializer_list channels) { + psubscribe(channels.begin(), channels.end()); + } + + void punsubscribe(); + + void punsubscribe(const StringView &channel); + + template + void punsubscribe(Input first, Input last); + + template + void punsubscribe(std::initializer_list channels) { + punsubscribe(channels.begin(), channels.end()); + } + + void consume(); + +private: + friend class Redis; + + friend class RedisCluster; + + explicit Subscriber(Connection connection); + + MsgType _msg_type(redisReply *reply) const; + + void _check_connection(); + + void _handle_message(redisReply &reply); + + void _handle_pmessage(redisReply &reply); + + void _handle_meta(MsgType type, redisReply &reply); + + using MsgCallback = std::function; + + using PatternMsgCallback = std::function; + + using MetaCallback = std::function; + + using TypeIndex = std::unordered_map; + static const TypeIndex _msg_type_index; + + Connection _connection; + + MsgCallback _msg_callback = nullptr; + + PatternMsgCallback _pmsg_callback = nullptr; + + MetaCallback _meta_callback = nullptr; +}; + +template +void Subscriber::on_message(MsgCb msg_callback) { + _msg_callback = msg_callback; +} + +template +void Subscriber::on_pmessage(PMsgCb pmsg_callback) { + _pmsg_callback = pmsg_callback; +} + +template +void Subscriber::on_meta(MetaCb meta_callback) { + _meta_callback = meta_callback; +} + +template +void Subscriber::subscribe(Input first, Input last) { + if (first == last) { + return; + } + + _check_connection(); + + cmd::subscribe_range(_connection, first, last); +} + +template +void Subscriber::unsubscribe(Input first, Input last) { + _check_connection(); + + cmd::unsubscribe_range(_connection, first, last); +} + +template +void Subscriber::psubscribe(Input first, Input last) { + if (first == last) { + return; + } + + _check_connection(); + + cmd::psubscribe_range(_connection, first, last); +} + +template +void Subscriber::punsubscribe(Input first, Input last) { + _check_connection(); + + cmd::punsubscribe_range(_connection, first, last); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/tls.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/tls.h new file mode 100644 index 000000000..0f2303db7 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/tls.h @@ -0,0 +1,47 @@ +/************************************************************************** + Copyright (c) 2020 sewenew + + 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 SEWENEW_REDISPLUSPLUS_NO_TLS_H +#define SEWENEW_REDISPLUSPLUS_NO_TLS_H + +#include + +namespace sw { + +namespace redis { + +namespace tls { + +struct TlsOptions {}; + +struct TlsContextUPtr {}; + +inline TlsContextUPtr secure_connection(redisContext &/*ctx*/, const TlsOptions &/*opts*/) { + // Do nothing + return {}; +} + +inline bool enabled(const TlsOptions &/*opts*/) { + return false; +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_NO_TLS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/transaction.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/transaction.h new file mode 100644 index 000000000..f19f24889 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/transaction.h @@ -0,0 +1,77 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_TRANSACTION_H +#define SEWENEW_REDISPLUSPLUS_TRANSACTION_H + +#include +#include +#include "connection.h" +#include "errors.h" + +namespace sw { + +namespace redis { + +class TransactionImpl { +public: + explicit TransactionImpl(bool piped) : _piped(piped) {} + + template + void command(Connection &connection, Cmd cmd, Args &&...args); + + std::vector exec(Connection &connection, std::size_t cmd_num); + + void discard(Connection &connection, std::size_t cmd_num); + +private: + void _open_transaction(Connection &connection); + + void _close_transaction(); + + void _get_queued_reply(Connection &connection); + + void _get_queued_replies(Connection &connection, std::size_t cmd_num); + + std::vector _exec(Connection &connection); + + void _discard(Connection &connection); + + bool _in_transaction = false; + + bool _piped; +}; + +template +void TransactionImpl::command(Connection &connection, Cmd cmd, Args &&...args) { + assert(!connection.broken()); + + if (!_in_transaction) { + _open_transaction(connection); + } + + cmd(connection, std::forward(args)...); + + if (!_piped) { + _get_queued_reply(connection); + } +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TRANSACTION_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/utils.h b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/utils.h new file mode 100644 index 000000000..f77f796d6 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/include/sw/redis++/utils.h @@ -0,0 +1,193 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + 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 SEWENEW_REDISPLUSPLUS_UTILS_H +#define SEWENEW_REDISPLUSPLUS_UTILS_H + +#include +#include +#include +#include "cxx_utils.h" + +namespace sw { + +namespace redis { + +using OptionalString = Optional; + +using OptionalLongLong = Optional; + +using OptionalDouble = Optional; + +using OptionalStringPair = Optional>; + +template +struct IsKvPair : std::false_type {}; + +template +struct IsKvPair> : std::true_type {}; + +template +using Void = void; + +template > +struct IsInserter : std::false_type {}; + +template +//struct IsInserter> : std::true_type {}; +struct IsInserter::value>::type> + : std::true_type {}; + +template > +struct IterType { + using type = typename std::iterator_traits::value_type; +}; + +template +//struct IterType> { +struct IterType::value>::type> { + typename std::enable_if::value>::type> { + using type = typename std::decay::type; +}; + +template > +struct IsIter : std::false_type {}; + +template +struct IsIter::value>::type> : std::true_type {}; + +template +//struct IsIter::iterator_category>> +struct IsIter::value_type>::value>::type> + : std::integral_constant::value> {}; + +template +struct IsKvPairIter : IsKvPair::type> {}; + +template +struct TupleWithType : std::false_type {}; + +template +struct TupleWithType> : std::false_type {}; + +template +struct TupleWithType> : TupleWithType> {}; + +template +struct TupleWithType> : std::true_type {}; + +template +struct IndexSequence {}; + +template +struct MakeIndexSequence : MakeIndexSequence {}; + +template +struct MakeIndexSequence<0, Is...> : IndexSequence {}; + +// NthType and NthValue are taken from +// https://stackoverflow.com/questions/14261183 +template +struct NthType {}; + +template +struct NthType<0, Arg, Args...> { + using type = Arg; +}; + +template +struct NthType { + using type = typename NthType::type; +}; + +template +struct LastType { + using type = typename NthType::type; +}; + +struct InvalidLastType {}; + +template <> +struct LastType<> { + using type = InvalidLastType; +}; + +template +auto NthValue(Arg &&arg, Args &&...) + -> typename std::enable_if<(I == 0), decltype(std::forward(arg))>::type { + return std::forward(arg); +} + +template +auto NthValue(Arg &&, Args &&...args) + -> typename std::enable_if<(I > 0), + decltype(std::forward::type>( + std::declval::type>()))>::type { + return std::forward::type>( + NthValue(std::forward(args)...)); +} + +template +auto LastValue(Args &&...args) + -> decltype(std::forward::type>( + std::declval::type>())) { + return std::forward::type>( + NthValue(std::forward(args)...)); +} + +template > +struct HasPushBack : std::false_type {}; + +template +struct HasPushBack().push_back(std::declval()) + )>::value>::type> : std::true_type {}; + +template > +struct HasInsert : std::false_type {}; + +template +struct HasInsert().insert(std::declval(), + std::declval())), + typename T::iterator>::value>::type> : std::true_type {}; + +template +struct IsSequenceContainer + : std::integral_constant::value + && !std::is_same::type, std::string>::value> {}; + +template +struct IsAssociativeContainer + : std::integral_constant::value && !HasPushBack::value> {}; + +uint16_t crc16(const char *buf, int len); + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_UTILS_H diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/lib/pkgconfig/redis++.pc b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/lib/pkgconfig/redis++.pc new file mode 100644 index 000000000..7bf203b35 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/lib/pkgconfig/redis++.pc @@ -0,0 +1,12 @@ +prefix=/home/grant/dev/ZeroTierOne/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64 +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: redis++ +Description: This is a Redis client, based on hiredis and written in C++11. It supports scritpting, pub/sub, pipeline, transaction, Redis Cluster, Redis Sentinel, connection pool, ACL, SSL and thread safety. +Version: 1.3.3 +URL: https://github.com/sewenew/redis-plus-plus +Requires: hiredis +Cflags: -I${includedir} +Libs: -L${libdir} -lredis++ diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/share/cmake/redis++/redis++-config-version.cmake b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/share/cmake/redis++/redis++-config-version.cmake new file mode 100644 index 000000000..ef4be219f --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/share/cmake/redis++/redis++-config-version.cmake @@ -0,0 +1,48 @@ +# This is a basic version file for the Config-mode of find_package(). +# It is used by write_basic_package_version_file() as input file for configure_file() +# to create a version-file which can be installed along a config.cmake file. +# +# The created file sets PACKAGE_VERSION_EXACT if the current version string and +# the requested version string are exactly the same and it sets +# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version. +# The variable CVF_VERSION must be set before calling configure_file(). + +set(PACKAGE_VERSION "1.3.3") + +if (PACKAGE_FIND_VERSION_RANGE) + # Package version must be in the requested version range + if ((PACKAGE_FIND_VERSION_RANGE_MIN STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_MIN) + OR ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_GREATER PACKAGE_FIND_VERSION_MAX) + OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND PACKAGE_VERSION VERSION_GREATER_EQUAL PACKAGE_FIND_VERSION_MAX))) + set(PACKAGE_VERSION_COMPATIBLE FALSE) + else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + endif() +else() + if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE FALSE) + else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + endif() + endif() +endif() + + +# if the installed project requested no architecture check, don't perform the check +if("FALSE") + return() +endif() + +# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it: +if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "8" STREQUAL "") + return() +endif() + +# check that the installed version has the same 32/64bit-ness as the one which is currently searching: +if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8") + math(EXPR installedBits "8 * 8") + set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)") + set(PACKAGE_VERSION_UNSUITABLE TRUE) +endif() diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/share/cmake/redis++/redis++-config.cmake b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/share/cmake/redis++/redis++-config.cmake new file mode 100644 index 000000000..580d98613 --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/share/cmake/redis++/redis++-config.cmake @@ -0,0 +1,36 @@ + +####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() ####### +####### Any changes to this file will be overwritten by the next CMake run #### +####### The input file was redis++-config.cmake.in ######## + +get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE) + +macro(set_and_check _var _file) + set(${_var} "${_file}") + if(NOT EXISTS "${_file}") + message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !") + endif() +endmacro() + +macro(check_required_components _NAME) + foreach(comp ${${_NAME}_FIND_COMPONENTS}) + if(NOT ${_NAME}_${comp}_FOUND) + if(${_NAME}_FIND_REQUIRED_${comp}) + set(${_NAME}_FOUND FALSE) + endif() + endif() + endforeach() +endmacro() + +#################################################################################### + +include(CMakeFindDependencyMacro) + +string(REPLACE "," ";" REDIS_PLUS_PLUS_DEPENDS_LIST hiredis) +foreach(REDIS_PLUS_PLUS_DEP ${REDIS_PLUS_PLUS_DEPENDS_LIST}) + find_dependency(${REDIS_PLUS_PLUS_DEP} REQUIRED) +endforeach() + +include("${CMAKE_CURRENT_LIST_DIR}/redis++-targets.cmake") + +check_required_components(redis++) diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/share/cmake/redis++/redis++-targets-release.cmake b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/share/cmake/redis++/redis++-targets-release.cmake new file mode 100644 index 000000000..b790b539f --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/share/cmake/redis++/redis++-targets-release.cmake @@ -0,0 +1,19 @@ +#---------------------------------------------------------------- +# Generated CMake target import file for configuration "Release". +#---------------------------------------------------------------- + +# Commands may need to know the format version. +set(CMAKE_IMPORT_FILE_VERSION 1) + +# Import target "redis++::redis++_static" for configuration "Release" +set_property(TARGET redis++::redis++_static APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) +set_target_properties(redis++::redis++_static PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "CXX" + IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libredis++.a" + ) + +list(APPEND _IMPORT_CHECK_TARGETS redis++::redis++_static ) +list(APPEND _IMPORT_CHECK_FILES_FOR_redis++::redis++_static "${_IMPORT_PREFIX}/lib/libredis++.a" ) + +# Commands beyond this point should not need to know the version. +set(CMAKE_IMPORT_FILE_VERSION) diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/share/cmake/redis++/redis++-targets.cmake b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/share/cmake/redis++/redis++-targets.cmake new file mode 100644 index 000000000..9e87d927e --- /dev/null +++ b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/arm64/share/cmake/redis++/redis++-targets.cmake @@ -0,0 +1,94 @@ +# Generated by CMake + +if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.6) + message(FATAL_ERROR "CMake >= 2.6.0 required") +endif() +cmake_policy(PUSH) +cmake_policy(VERSION 2.6...3.20) +#---------------------------------------------------------------- +# Generated CMake target import file. +#---------------------------------------------------------------- + +# Commands may need to know the format version. +set(CMAKE_IMPORT_FILE_VERSION 1) + +# Protect against multiple inclusion, which would fail when already imported targets are added once more. +set(_targetsDefined) +set(_targetsNotDefined) +set(_expectedTargets) +foreach(_expectedTarget redis++::redis++_static) + list(APPEND _expectedTargets ${_expectedTarget}) + if(NOT TARGET ${_expectedTarget}) + list(APPEND _targetsNotDefined ${_expectedTarget}) + endif() + if(TARGET ${_expectedTarget}) + list(APPEND _targetsDefined ${_expectedTarget}) + endif() +endforeach() +if("${_targetsDefined}" STREQUAL "${_expectedTargets}") + unset(_targetsDefined) + unset(_targetsNotDefined) + unset(_expectedTargets) + set(CMAKE_IMPORT_FILE_VERSION) + cmake_policy(POP) + return() +endif() +if(NOT "${_targetsDefined}" STREQUAL "") + message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_targetsDefined}\nTargets not yet defined: ${_targetsNotDefined}\n") +endif() +unset(_targetsDefined) +unset(_targetsNotDefined) +unset(_expectedTargets) + + +# Compute the installation prefix relative to this file. +get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH) +get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) +get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) +get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) +if(_IMPORT_PREFIX STREQUAL "/") + set(_IMPORT_PREFIX "") +endif() + +# Create imported target redis++::redis++_static +add_library(redis++::redis++_static STATIC IMPORTED) + +set_target_properties(redis++::redis++_static PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include;${_IMPORT_PREFIX}/include" +) + +# Load information for each installed configuration. +get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) +file(GLOB CONFIG_FILES "${_DIR}/redis++-targets-*.cmake") +foreach(f ${CONFIG_FILES}) + include(${f}) +endforeach() + +# Cleanup temporary variables. +set(_IMPORT_PREFIX) + +# Loop over all imported files and verify that they actually exist +foreach(target ${_IMPORT_CHECK_TARGETS} ) + foreach(file ${_IMPORT_CHECK_FILES_FOR_${target}} ) + if(NOT EXISTS "${file}" ) + message(FATAL_ERROR "The imported target \"${target}\" references the file + \"${file}\" +but this file does not exist. Possible reasons include: +* The file was deleted, renamed, or moved to another location. +* An install or uninstall procedure did not complete successfully. +* The installation package was faulty and contained + \"${CMAKE_CURRENT_LIST_FILE}\" +but not all the files it references. +") + endif() + endforeach() + unset(_IMPORT_CHECK_FILES_FOR_${target}) +endforeach() +unset(_IMPORT_CHECK_TARGETS) + +# This file does not depend on other imported targets which have +# been exported from the same project but in a separate export set. + +# Commands beyond this point should not need to know the version. +set(CMAKE_IMPORT_FILE_VERSION) +cmake_policy(POP) diff --git a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/lib/libredis++.a b/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/lib/libredis++.a deleted file mode 100644 index 3f398e48d..000000000 Binary files a/ext/redis-plus-plus-1.3.3/install/ubuntu22.04/lib/libredis++.a and /dev/null differ