mirror of
https://github.com/ZeroTier/ZeroTierOne
synced 2025-08-20 05:13:58 -07:00
redis++ binaries for mac
This commit is contained in:
parent
b5c661c5d5
commit
8f3a0b17ad
96 changed files with 35078 additions and 0 deletions
376
ext/redis-plus-plus-1.1.1/src/sw/redis++/command.cpp
Normal file
376
ext/redis-plus-plus-1.1.1/src/sw/redis++/command.cpp
Normal file
|
@ -0,0 +1,376 @@
|
|||
/**************************************************************************
|
||||
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.
|
||||
*************************************************************************/
|
||||
|
||||
#include "command.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace cmd {
|
||||
|
||||
// KEY commands.
|
||||
|
||||
void restore(Connection &connection,
|
||||
const StringView &key,
|
||||
const StringView &val,
|
||||
long long ttl,
|
||||
bool replace) {
|
||||
CmdArgs args;
|
||||
args << "RESTORE" << key << ttl << val;
|
||||
|
||||
if (replace) {
|
||||
args << "REPLACE";
|
||||
}
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
// STRING commands.
|
||||
|
||||
void bitop(Connection &connection,
|
||||
BitOp op,
|
||||
const StringView &destination,
|
||||
const StringView &key) {
|
||||
CmdArgs args;
|
||||
|
||||
detail::set_bitop(args, op);
|
||||
|
||||
args << destination << key;
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
void set(Connection &connection,
|
||||
const StringView &key,
|
||||
const StringView &val,
|
||||
long long ttl,
|
||||
UpdateType type) {
|
||||
CmdArgs args;
|
||||
args << "SET" << key << val;
|
||||
|
||||
if (ttl > 0) {
|
||||
args << "PX" << ttl;
|
||||
}
|
||||
|
||||
detail::set_update_type(args, type);
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
// LIST commands.
|
||||
|
||||
void linsert(Connection &connection,
|
||||
const StringView &key,
|
||||
InsertPosition position,
|
||||
const StringView &pivot,
|
||||
const StringView &val) {
|
||||
std::string pos;
|
||||
switch (position) {
|
||||
case InsertPosition::BEFORE:
|
||||
pos = "BEFORE";
|
||||
break;
|
||||
|
||||
case InsertPosition::AFTER:
|
||||
pos = "AFTER";
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
connection.send("LINSERT %b %s %b %b",
|
||||
key.data(), key.size(),
|
||||
pos.c_str(),
|
||||
pivot.data(), pivot.size(),
|
||||
val.data(), val.size());
|
||||
}
|
||||
|
||||
// GEO commands.
|
||||
|
||||
void geodist(Connection &connection,
|
||||
const StringView &key,
|
||||
const StringView &member1,
|
||||
const StringView &member2,
|
||||
GeoUnit unit) {
|
||||
CmdArgs args;
|
||||
args << "GEODIST" << key << member1 << member2;
|
||||
|
||||
detail::set_geo_unit(args, unit);
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
void georadius_store(Connection &connection,
|
||||
const StringView &key,
|
||||
const std::pair<double, double> &loc,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
CmdArgs args;
|
||||
args << "GEORADIUS" << key << loc.first << loc.second;
|
||||
|
||||
detail::set_georadius_store_parameters(args,
|
||||
radius,
|
||||
unit,
|
||||
destination,
|
||||
store_dist,
|
||||
count);
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
void georadius(Connection &connection,
|
||||
const StringView &key,
|
||||
const std::pair<double, double> &loc,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
long long count,
|
||||
bool asc,
|
||||
bool with_coord,
|
||||
bool with_dist,
|
||||
bool with_hash) {
|
||||
CmdArgs args;
|
||||
args << "GEORADIUS" << key << loc.first << loc.second;
|
||||
|
||||
detail::set_georadius_parameters(args,
|
||||
radius,
|
||||
unit,
|
||||
count,
|
||||
asc,
|
||||
with_coord,
|
||||
with_dist,
|
||||
with_hash);
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
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) {
|
||||
CmdArgs args;
|
||||
args << "GEORADIUSBYMEMBER" << key << member;
|
||||
|
||||
detail::set_georadius_parameters(args,
|
||||
radius,
|
||||
unit,
|
||||
count,
|
||||
asc,
|
||||
with_coord,
|
||||
with_dist,
|
||||
with_hash);
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
void georadiusbymember_store(Connection &connection,
|
||||
const StringView &key,
|
||||
const StringView &member,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
CmdArgs args;
|
||||
args << "GEORADIUSBYMEMBER" << key << member;
|
||||
|
||||
detail::set_georadius_store_parameters(args,
|
||||
radius,
|
||||
unit,
|
||||
destination,
|
||||
store_dist,
|
||||
count);
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
// Stream commands.
|
||||
|
||||
void xtrim(Connection &connection, const StringView &key, long long count, bool approx) {
|
||||
CmdArgs args;
|
||||
args << "XTRIM" << key << "MAXLEN";
|
||||
|
||||
if (approx) {
|
||||
args << "~";
|
||||
}
|
||||
|
||||
args << count;
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
void set_bitop(CmdArgs &args, BitOp op) {
|
||||
args << "BITOP";
|
||||
|
||||
switch (op) {
|
||||
case BitOp::AND:
|
||||
args << "AND";
|
||||
break;
|
||||
|
||||
case BitOp::OR:
|
||||
args << "OR";
|
||||
break;
|
||||
|
||||
case BitOp::XOR:
|
||||
args << "XOR";
|
||||
break;
|
||||
|
||||
case BitOp::NOT:
|
||||
args << "NOT";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unknown bit operations");
|
||||
}
|
||||
}
|
||||
|
||||
void set_update_type(CmdArgs &args, UpdateType type) {
|
||||
switch (type) {
|
||||
case UpdateType::EXIST:
|
||||
args << "XX";
|
||||
break;
|
||||
|
||||
case UpdateType::NOT_EXIST:
|
||||
args << "NX";
|
||||
break;
|
||||
|
||||
case UpdateType::ALWAYS:
|
||||
// Do nothing.
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unknown update type");
|
||||
}
|
||||
}
|
||||
|
||||
void set_aggregation_type(CmdArgs &args, Aggregation aggr) {
|
||||
args << "AGGREGATE";
|
||||
|
||||
switch (aggr) {
|
||||
case Aggregation::SUM:
|
||||
args << "SUM";
|
||||
break;
|
||||
|
||||
case Aggregation::MIN:
|
||||
args << "MIN";
|
||||
break;
|
||||
|
||||
case Aggregation::MAX:
|
||||
args << "MAX";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unknown aggregation type");
|
||||
}
|
||||
}
|
||||
|
||||
void set_geo_unit(CmdArgs &args, GeoUnit unit) {
|
||||
switch (unit) {
|
||||
case GeoUnit::M:
|
||||
args << "m";
|
||||
break;
|
||||
|
||||
case GeoUnit::KM:
|
||||
args << "km";
|
||||
break;
|
||||
|
||||
case GeoUnit::MI:
|
||||
args << "mi";
|
||||
break;
|
||||
|
||||
case GeoUnit::FT:
|
||||
args << "ft";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unknown geo unit type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void set_georadius_store_parameters(CmdArgs &args,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
args << radius;
|
||||
|
||||
detail::set_geo_unit(args, unit);
|
||||
|
||||
args << "COUNT" << count;
|
||||
|
||||
if (store_dist) {
|
||||
args << "STOREDIST";
|
||||
} else {
|
||||
args << "STORE";
|
||||
}
|
||||
|
||||
args << destination;
|
||||
}
|
||||
|
||||
void set_georadius_parameters(CmdArgs &args,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
long long count,
|
||||
bool asc,
|
||||
bool with_coord,
|
||||
bool with_dist,
|
||||
bool with_hash) {
|
||||
args << radius;
|
||||
|
||||
detail::set_geo_unit(args, unit);
|
||||
|
||||
if (with_coord) {
|
||||
args << "WITHCOORD";
|
||||
}
|
||||
|
||||
if (with_dist) {
|
||||
args << "WITHDIST";
|
||||
}
|
||||
|
||||
if (with_hash) {
|
||||
args << "WITHHASH";
|
||||
}
|
||||
|
||||
args << "COUNT" << count;
|
||||
|
||||
if (asc) {
|
||||
args << "ASC";
|
||||
} else {
|
||||
args << "DESC";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
2233
ext/redis-plus-plus-1.1.1/src/sw/redis++/command.h
Normal file
2233
ext/redis-plus-plus-1.1.1/src/sw/redis++/command.h
Normal file
File diff suppressed because it is too large
Load diff
180
ext/redis-plus-plus-1.1.1/src/sw/redis++/command_args.h
Normal file
180
ext/redis-plus-plus-1.1.1/src/sw/redis++/command_args.h
Normal file
|
@ -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 <vector>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
class CmdArgs {
|
||||
public:
|
||||
template <typename Arg>
|
||||
CmdArgs& append(Arg &&arg);
|
||||
|
||||
template <typename Arg, typename ...Args>
|
||||
CmdArgs& append(Arg &&arg, Args &&...args);
|
||||
|
||||
// All overloads of operator<< are for internal use only.
|
||||
CmdArgs& operator<<(const StringView &arg);
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_arithmetic<typename std::decay<T>::type>::value,
|
||||
int>::type = 0>
|
||||
CmdArgs& operator<<(T &&arg);
|
||||
|
||||
template <typename Iter>
|
||||
CmdArgs& operator<<(const std::pair<Iter, Iter> &range);
|
||||
|
||||
template <std::size_t N, typename ...Args>
|
||||
auto operator<<(const std::tuple<Args...> &) ->
|
||||
typename std::enable_if<N == sizeof...(Args), CmdArgs&>::type {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <std::size_t N = 0, typename ...Args>
|
||||
auto operator<<(const std::tuple<Args...> &arg) ->
|
||||
typename std::enable_if<N < sizeof...(Args), CmdArgs&>::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 <typename T,
|
||||
typename std::enable_if<std::is_arithmetic<typename std::decay<T>::type>::value,
|
||||
int>::type = 0>
|
||||
CmdArgs& _append(T &&arg) {
|
||||
return operator<<(std::forward<T>(arg));
|
||||
}
|
||||
|
||||
template <typename Iter>
|
||||
CmdArgs& _append(std::true_type, const std::pair<Iter, Iter> &range);
|
||||
|
||||
template <typename Iter>
|
||||
CmdArgs& _append(std::false_type, const std::pair<Iter, Iter> &range);
|
||||
|
||||
std::vector<const char *> _argv;
|
||||
std::vector<std::size_t> _argv_len;
|
||||
|
||||
std::list<std::string> _args;
|
||||
};
|
||||
|
||||
template <typename Arg>
|
||||
inline CmdArgs& CmdArgs::append(Arg &&arg) {
|
||||
return _append(std::forward<Arg>(arg));
|
||||
}
|
||||
|
||||
template <typename Arg, typename ...Args>
|
||||
inline CmdArgs& CmdArgs::append(Arg &&arg, Args &&...args) {
|
||||
_append(std::forward<Arg>(arg));
|
||||
|
||||
return append(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
inline CmdArgs& CmdArgs::operator<<(const StringView &arg) {
|
||||
_argv.push_back(arg.data());
|
||||
_argv_len.push_back(arg.size());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Iter>
|
||||
inline CmdArgs& CmdArgs::operator<<(const std::pair<Iter, Iter> &range) {
|
||||
return _append(IsKvPair<typename std::decay<decltype(*std::declval<Iter>())>::type>(), range);
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_arithmetic<typename std::decay<T>::type>::value,
|
||||
int>::type>
|
||||
inline CmdArgs& CmdArgs::operator<<(T &&arg) {
|
||||
return _append(std::to_string(std::forward<T>(arg)));
|
||||
}
|
||||
|
||||
template <std::size_t N, typename ...Args>
|
||||
auto CmdArgs::operator<<(const std::tuple<Args...> &arg) ->
|
||||
typename std::enable_if<N < sizeof...(Args), CmdArgs&>::type {
|
||||
operator<<(std::get<N>(arg));
|
||||
|
||||
return operator<<<N + 1, Args...>(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 <typename Iter>
|
||||
CmdArgs& CmdArgs::_append(std::false_type, const std::pair<Iter, Iter> &range) {
|
||||
auto first = range.first;
|
||||
auto last = range.second;
|
||||
while (first != last) {
|
||||
*this << *first;
|
||||
++first;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Iter>
|
||||
CmdArgs& CmdArgs::_append(std::true_type, const std::pair<Iter, Iter> &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
|
201
ext/redis-plus-plus-1.1.1/src/sw/redis++/command_options.cpp
Normal file
201
ext/redis-plus-plus-1.1.1/src/sw/redis++/command_options.cpp
Normal file
|
@ -0,0 +1,201 @@
|
|||
/**************************************************************************
|
||||
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.
|
||||
*************************************************************************/
|
||||
|
||||
#include "command_options.h"
|
||||
#include "errors.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const std::string NEGATIVE_INFINITY_NUMERIC = "-inf";
|
||||
const std::string POSITIVE_INFINITY_NUMERIC = "+inf";
|
||||
|
||||
const std::string NEGATIVE_INFINITY_STRING = "-";
|
||||
const std::string POSITIVE_INFINITY_STRING = "+";
|
||||
|
||||
std::string unbound(const std::string &bnd);
|
||||
|
||||
std::string bound(const std::string &bnd);
|
||||
|
||||
}
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
const std::string& UnboundedInterval<double>::min() const {
|
||||
return NEGATIVE_INFINITY_NUMERIC;
|
||||
}
|
||||
|
||||
const std::string& UnboundedInterval<double>::max() const {
|
||||
return POSITIVE_INFINITY_NUMERIC;
|
||||
}
|
||||
|
||||
BoundedInterval<double>::BoundedInterval(double min, double max, BoundType type) :
|
||||
_min(std::to_string(min)),
|
||||
_max(std::to_string(max)) {
|
||||
switch (type) {
|
||||
case BoundType::CLOSED:
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
case BoundType::OPEN:
|
||||
_min = unbound(_min);
|
||||
_max = unbound(_max);
|
||||
break;
|
||||
|
||||
case BoundType::LEFT_OPEN:
|
||||
_min = unbound(_min);
|
||||
break;
|
||||
|
||||
case BoundType::RIGHT_OPEN:
|
||||
_max = unbound(_max);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unknow BoundType");
|
||||
}
|
||||
}
|
||||
|
||||
LeftBoundedInterval<double>::LeftBoundedInterval(double min, BoundType type) :
|
||||
_min(std::to_string(min)) {
|
||||
switch (type) {
|
||||
case BoundType::OPEN:
|
||||
_min = unbound(_min);
|
||||
break;
|
||||
|
||||
case BoundType::RIGHT_OPEN:
|
||||
// Do nothing.
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Bound type can only be OPEN or RIGHT_OPEN");
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& LeftBoundedInterval<double>::max() const {
|
||||
return POSITIVE_INFINITY_NUMERIC;
|
||||
}
|
||||
|
||||
RightBoundedInterval<double>::RightBoundedInterval(double max, BoundType type) :
|
||||
_max(std::to_string(max)) {
|
||||
switch (type) {
|
||||
case BoundType::OPEN:
|
||||
_max = unbound(_max);
|
||||
break;
|
||||
|
||||
case BoundType::LEFT_OPEN:
|
||||
// Do nothing.
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Bound type can only be OPEN or LEFT_OPEN");
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& RightBoundedInterval<double>::min() const {
|
||||
return NEGATIVE_INFINITY_NUMERIC;
|
||||
}
|
||||
|
||||
const std::string& UnboundedInterval<std::string>::min() const {
|
||||
return NEGATIVE_INFINITY_STRING;
|
||||
}
|
||||
|
||||
const std::string& UnboundedInterval<std::string>::max() const {
|
||||
return POSITIVE_INFINITY_STRING;
|
||||
}
|
||||
|
||||
BoundedInterval<std::string>::BoundedInterval(const std::string &min,
|
||||
const std::string &max,
|
||||
BoundType type) {
|
||||
switch (type) {
|
||||
case BoundType::CLOSED:
|
||||
_min = bound(min);
|
||||
_max = bound(max);
|
||||
break;
|
||||
|
||||
case BoundType::OPEN:
|
||||
_min = unbound(min);
|
||||
_max = unbound(max);
|
||||
break;
|
||||
|
||||
case BoundType::LEFT_OPEN:
|
||||
_min = unbound(min);
|
||||
_max = bound(max);
|
||||
break;
|
||||
|
||||
case BoundType::RIGHT_OPEN:
|
||||
_min = bound(min);
|
||||
_max = unbound(max);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unknow BoundType");
|
||||
}
|
||||
}
|
||||
|
||||
LeftBoundedInterval<std::string>::LeftBoundedInterval(const std::string &min, BoundType type) {
|
||||
switch (type) {
|
||||
case BoundType::OPEN:
|
||||
_min = unbound(min);
|
||||
break;
|
||||
|
||||
case BoundType::RIGHT_OPEN:
|
||||
_min = bound(min);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Bound type can only be OPEN or RIGHT_OPEN");
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& LeftBoundedInterval<std::string>::max() const {
|
||||
return POSITIVE_INFINITY_STRING;
|
||||
}
|
||||
|
||||
RightBoundedInterval<std::string>::RightBoundedInterval(const std::string &max, BoundType type) {
|
||||
switch (type) {
|
||||
case BoundType::OPEN:
|
||||
_max = unbound(max);
|
||||
break;
|
||||
|
||||
case BoundType::LEFT_OPEN:
|
||||
_max = bound(max);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Bound type can only be OPEN or LEFT_OPEN");
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& RightBoundedInterval<std::string>::min() const {
|
||||
return NEGATIVE_INFINITY_STRING;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
std::string unbound(const std::string &bnd) {
|
||||
return "(" + bnd;
|
||||
}
|
||||
|
||||
std::string bound(const std::string &bnd) {
|
||||
return "[" + bnd;
|
||||
}
|
||||
|
||||
}
|
211
ext/redis-plus-plus-1.1.1/src/sw/redis++/command_options.h
Normal file
211
ext/redis-plus-plus-1.1.1/src/sw/redis++/command_options.h
Normal file
|
@ -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 <string>
|
||||
#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 <typename T>
|
||||
class UnboundedInterval;
|
||||
|
||||
// [min, max], (min, max), (min, max], [min, max)
|
||||
template <typename T>
|
||||
class BoundedInterval;
|
||||
|
||||
// [min, +inf), (min, +inf)
|
||||
template <typename T>
|
||||
class LeftBoundedInterval;
|
||||
|
||||
// (-inf, max], (-inf, max)
|
||||
template <typename T>
|
||||
class RightBoundedInterval;
|
||||
|
||||
template <>
|
||||
class UnboundedInterval<double> {
|
||||
public:
|
||||
const std::string& min() const;
|
||||
|
||||
const std::string& max() const;
|
||||
};
|
||||
|
||||
template <>
|
||||
class BoundedInterval<double> {
|
||||
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<double> {
|
||||
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<double> {
|
||||
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<std::string> {
|
||||
public:
|
||||
const std::string& min() const;
|
||||
|
||||
const std::string& max() const;
|
||||
};
|
||||
|
||||
template <>
|
||||
class BoundedInterval<std::string> {
|
||||
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<std::string> {
|
||||
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<std::string> {
|
||||
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 <typename T>
|
||||
struct WithCoord : TupleWithType<std::pair<double, double>, T> {};
|
||||
|
||||
template <typename T>
|
||||
struct WithDist : TupleWithType<double, T> {};
|
||||
|
||||
template <typename T>
|
||||
struct WithHash : TupleWithType<long long, T> {};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H
|
305
ext/redis-plus-plus-1.1.1/src/sw/redis++/connection.cpp
Normal file
305
ext/redis-plus-plus-1.1.1/src/sw/redis++/connection.cpp
Normal file
|
@ -0,0 +1,305 @@
|
|||
/**************************************************************************
|
||||
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.
|
||||
*************************************************************************/
|
||||
|
||||
#include "connection.h"
|
||||
#include <cassert>
|
||||
#include "reply.h"
|
||||
#include "command.h"
|
||||
#include "command_args.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
ConnectionOptions::ConnectionOptions(const std::string &uri) :
|
||||
ConnectionOptions(_parse_options(uri)) {}
|
||||
|
||||
ConnectionOptions ConnectionOptions::_parse_options(const std::string &uri) const {
|
||||
std::string type;
|
||||
std::string path;
|
||||
std::tie(type, path) = _split_string(uri, "://");
|
||||
|
||||
if (path.empty()) {
|
||||
throw Error("Invalid URI: no path");
|
||||
}
|
||||
|
||||
if (type == "tcp") {
|
||||
return _parse_tcp_options(path);
|
||||
} else if (type == "unix") {
|
||||
return _parse_unix_options(path);
|
||||
} else {
|
||||
throw Error("Invalid URI: invalid type");
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionOptions ConnectionOptions::_parse_tcp_options(const std::string &path) const {
|
||||
ConnectionOptions options;
|
||||
|
||||
options.type = ConnectionType::TCP;
|
||||
|
||||
std::string host;
|
||||
std::string port;
|
||||
std::tie(host, port) = _split_string(path, ":");
|
||||
|
||||
options.host = host;
|
||||
try {
|
||||
if (!port.empty()) {
|
||||
options.port = std::stoi(port);
|
||||
} // else use default port, i.e. 6379.
|
||||
} catch (const std::exception &) {
|
||||
throw Error("Invalid URL: invalid port");
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
ConnectionOptions ConnectionOptions::_parse_unix_options(const std::string &path) const {
|
||||
ConnectionOptions options;
|
||||
|
||||
options.type = ConnectionType::UNIX;
|
||||
options.path = path;
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
auto ConnectionOptions::_split_string(const std::string &str, const std::string &delimiter) const ->
|
||||
std::pair<std::string, std::string> {
|
||||
auto pos = str.rfind(delimiter);
|
||||
if (pos == std::string::npos) {
|
||||
return {str, ""};
|
||||
}
|
||||
|
||||
return {str.substr(0, pos), str.substr(pos + delimiter.size())};
|
||||
}
|
||||
|
||||
class Connection::Connector {
|
||||
public:
|
||||
explicit Connector(const ConnectionOptions &opts);
|
||||
|
||||
ContextUPtr connect() const;
|
||||
|
||||
private:
|
||||
ContextUPtr _connect() const;
|
||||
|
||||
redisContext* _connect_tcp() const;
|
||||
|
||||
redisContext* _connect_unix() const;
|
||||
|
||||
void _set_socket_timeout(redisContext &ctx) const;
|
||||
|
||||
void _enable_keep_alive(redisContext &ctx) const;
|
||||
|
||||
timeval _to_timeval(const std::chrono::milliseconds &dur) const;
|
||||
|
||||
const ConnectionOptions &_opts;
|
||||
};
|
||||
|
||||
Connection::Connector::Connector(const ConnectionOptions &opts) : _opts(opts) {}
|
||||
|
||||
Connection::ContextUPtr Connection::Connector::connect() const {
|
||||
auto ctx = _connect();
|
||||
|
||||
assert(ctx);
|
||||
|
||||
if (ctx->err != REDIS_OK) {
|
||||
throw_error(*ctx, "Failed to connect to Redis");
|
||||
}
|
||||
|
||||
_set_socket_timeout(*ctx);
|
||||
|
||||
_enable_keep_alive(*ctx);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
Connection::ContextUPtr Connection::Connector::_connect() const {
|
||||
redisContext *context = nullptr;
|
||||
switch (_opts.type) {
|
||||
case ConnectionType::TCP:
|
||||
context = _connect_tcp();
|
||||
break;
|
||||
|
||||
case ConnectionType::UNIX:
|
||||
context = _connect_unix();
|
||||
break;
|
||||
|
||||
default:
|
||||
// Never goes here.
|
||||
throw Error("Unkonw connection type");
|
||||
}
|
||||
|
||||
if (context == nullptr) {
|
||||
throw Error("Failed to allocate memory for connection.");
|
||||
}
|
||||
|
||||
return ContextUPtr(context);
|
||||
}
|
||||
|
||||
redisContext* Connection::Connector::_connect_tcp() const {
|
||||
if (_opts.connect_timeout > std::chrono::milliseconds(0)) {
|
||||
return redisConnectWithTimeout(_opts.host.c_str(),
|
||||
_opts.port,
|
||||
_to_timeval(_opts.connect_timeout));
|
||||
} else {
|
||||
return redisConnect(_opts.host.c_str(), _opts.port);
|
||||
}
|
||||
}
|
||||
|
||||
redisContext* Connection::Connector::_connect_unix() const {
|
||||
if (_opts.connect_timeout > std::chrono::milliseconds(0)) {
|
||||
return redisConnectUnixWithTimeout(
|
||||
_opts.path.c_str(),
|
||||
_to_timeval(_opts.connect_timeout));
|
||||
} else {
|
||||
return redisConnectUnix(_opts.path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::Connector::_set_socket_timeout(redisContext &ctx) const {
|
||||
if (_opts.socket_timeout <= std::chrono::milliseconds(0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (redisSetTimeout(&ctx, _to_timeval(_opts.socket_timeout)) != REDIS_OK) {
|
||||
throw_error(ctx, "Failed to set socket timeout");
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::Connector::_enable_keep_alive(redisContext &ctx) const {
|
||||
if (!_opts.keep_alive) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (redisEnableKeepAlive(&ctx) != REDIS_OK) {
|
||||
throw_error(ctx, "Failed to enable keep alive option");
|
||||
}
|
||||
}
|
||||
|
||||
timeval Connection::Connector::_to_timeval(const std::chrono::milliseconds &dur) const {
|
||||
auto sec = std::chrono::duration_cast<std::chrono::seconds>(dur);
|
||||
auto msec = std::chrono::duration_cast<std::chrono::microseconds>(dur - sec);
|
||||
|
||||
return {
|
||||
static_cast<std::time_t>(sec.count()),
|
||||
static_cast<suseconds_t>(msec.count())
|
||||
};
|
||||
}
|
||||
|
||||
void swap(Connection &lhs, Connection &rhs) noexcept {
|
||||
std::swap(lhs._ctx, rhs._ctx);
|
||||
std::swap(lhs._last_active, rhs._last_active);
|
||||
std::swap(lhs._opts, rhs._opts);
|
||||
}
|
||||
|
||||
Connection::Connection(const ConnectionOptions &opts) :
|
||||
_ctx(Connector(opts).connect()),
|
||||
_last_active(std::chrono::steady_clock::now()),
|
||||
_opts(opts) {
|
||||
assert(_ctx && !broken());
|
||||
|
||||
_set_options();
|
||||
}
|
||||
|
||||
void Connection::reconnect() {
|
||||
Connection connection(_opts);
|
||||
|
||||
swap(*this, connection);
|
||||
}
|
||||
|
||||
void Connection::send(int argc, const char **argv, const std::size_t *argv_len) {
|
||||
auto ctx = _context();
|
||||
|
||||
assert(ctx != nullptr);
|
||||
|
||||
if (redisAppendCommandArgv(ctx,
|
||||
argc,
|
||||
argv,
|
||||
argv_len) != REDIS_OK) {
|
||||
throw_error(*ctx, "Failed to send command");
|
||||
}
|
||||
|
||||
assert(!broken());
|
||||
}
|
||||
|
||||
void Connection::send(CmdArgs &args) {
|
||||
auto ctx = _context();
|
||||
|
||||
assert(ctx != nullptr);
|
||||
|
||||
if (redisAppendCommandArgv(ctx,
|
||||
args.size(),
|
||||
args.argv(),
|
||||
args.argv_len()) != REDIS_OK) {
|
||||
throw_error(*ctx, "Failed to send command");
|
||||
}
|
||||
|
||||
assert(!broken());
|
||||
}
|
||||
|
||||
ReplyUPtr Connection::recv() {
|
||||
auto *ctx = _context();
|
||||
|
||||
assert(ctx != nullptr);
|
||||
|
||||
void *r = nullptr;
|
||||
if (redisGetReply(ctx, &r) != REDIS_OK) {
|
||||
throw_error(*ctx, "Failed to get reply");
|
||||
}
|
||||
|
||||
assert(!broken() && r != nullptr);
|
||||
|
||||
auto reply = ReplyUPtr(static_cast<redisReply*>(r));
|
||||
|
||||
if (reply::is_error(*reply)) {
|
||||
throw_error(*reply);
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
void Connection::_set_options() {
|
||||
_auth();
|
||||
|
||||
_select_db();
|
||||
}
|
||||
|
||||
void Connection::_auth() {
|
||||
if (_opts.password.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmd::auth(*this, _opts.password);
|
||||
|
||||
auto reply = recv();
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void Connection::_select_db() {
|
||||
if (_opts.db == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmd::select(*this, _opts.db);
|
||||
|
||||
auto reply = recv();
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
194
ext/redis-plus-plus-1.1.1/src/sw/redis++/connection.h
Normal file
194
ext/redis-plus-plus-1.1.1/src/sw/redis++/connection.h
Normal file
|
@ -0,0 +1,194 @@
|
|||
/**************************************************************************
|
||||
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 <cerrno>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <chrono>
|
||||
#include <hiredis/hiredis.h>
|
||||
#include "errors.h"
|
||||
#include "reply.h"
|
||||
#include "utils.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 password;
|
||||
|
||||
int db = 0;
|
||||
|
||||
bool keep_alive = false;
|
||||
|
||||
std::chrono::milliseconds connect_timeout{0};
|
||||
|
||||
std::chrono::milliseconds socket_timeout{0};
|
||||
|
||||
private:
|
||||
ConnectionOptions _parse_options(const std::string &uri) const;
|
||||
|
||||
ConnectionOptions _parse_tcp_options(const std::string &path) const;
|
||||
|
||||
ConnectionOptions _parse_unix_options(const std::string &path) const;
|
||||
|
||||
auto _split_string(const std::string &str, const std::string &delimiter) const ->
|
||||
std::pair<std::string, std::string>;
|
||||
};
|
||||
|
||||
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 reconnect();
|
||||
|
||||
auto last_active() const
|
||||
-> std::chrono::time_point<std::chrono::steady_clock> {
|
||||
return _last_active;
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
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();
|
||||
|
||||
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<redisContext, ContextDeleter>;
|
||||
|
||||
void _set_options();
|
||||
|
||||
void _auth();
|
||||
|
||||
void _select_db();
|
||||
|
||||
redisContext* _context();
|
||||
|
||||
ContextUPtr _ctx;
|
||||
|
||||
// The time that the connection is created or the time that
|
||||
// the connection is used, i.e. *context()* is called.
|
||||
std::chrono::time_point<std::chrono::steady_clock> _last_active{};
|
||||
|
||||
ConnectionOptions _opts;
|
||||
};
|
||||
|
||||
using ConnectionSPtr = std::shared_ptr<Connection>;
|
||||
|
||||
enum class Role {
|
||||
MASTER,
|
||||
SLAVE
|
||||
};
|
||||
|
||||
// Inline implementaions.
|
||||
|
||||
template <typename ...Args>
|
||||
inline void Connection::send(const char *format, Args &&...args) {
|
||||
auto ctx = _context();
|
||||
|
||||
assert(ctx != nullptr);
|
||||
|
||||
if (redisAppendCommand(ctx,
|
||||
format,
|
||||
std::forward<Args>(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
|
249
ext/redis-plus-plus-1.1.1/src/sw/redis++/connection_pool.cpp
Normal file
249
ext/redis-plus-plus-1.1.1/src/sw/redis++/connection_pool.cpp
Normal file
|
@ -0,0 +1,249 @@
|
|||
/**************************************************************************
|
||||
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.
|
||||
*************************************************************************/
|
||||
|
||||
#include "connection_pool.h"
|
||||
#include <cassert>
|
||||
#include "errors.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
ConnectionPool::ConnectionPool(const ConnectionPoolOptions &pool_opts,
|
||||
const ConnectionOptions &connection_opts) :
|
||||
_opts(connection_opts),
|
||||
_pool_opts(pool_opts) {
|
||||
if (_pool_opts.size == 0) {
|
||||
throw Error("CANNOT create an empty pool");
|
||||
}
|
||||
|
||||
// Lazily create connections.
|
||||
}
|
||||
|
||||
ConnectionPool::ConnectionPool(SimpleSentinel sentinel,
|
||||
const ConnectionPoolOptions &pool_opts,
|
||||
const ConnectionOptions &connection_opts) :
|
||||
_opts(connection_opts),
|
||||
_pool_opts(pool_opts),
|
||||
_sentinel(std::move(sentinel)) {
|
||||
// In this case, the connection must be of TCP type.
|
||||
if (_opts.type != ConnectionType::TCP) {
|
||||
throw Error("Sentinel only supports TCP connection");
|
||||
}
|
||||
|
||||
if (_opts.connect_timeout == std::chrono::milliseconds(0)
|
||||
|| _opts.socket_timeout == std::chrono::milliseconds(0)) {
|
||||
throw Error("With sentinel, connection timeout and socket timeout cannot be 0");
|
||||
}
|
||||
|
||||
// Cleanup connection options.
|
||||
_update_connection_opts("", -1);
|
||||
|
||||
assert(_sentinel);
|
||||
}
|
||||
|
||||
ConnectionPool::ConnectionPool(ConnectionPool &&that) {
|
||||
std::lock_guard<std::mutex> lock(that._mutex);
|
||||
|
||||
_move(std::move(that));
|
||||
}
|
||||
|
||||
ConnectionPool& ConnectionPool::operator=(ConnectionPool &&that) {
|
||||
if (this != &that) {
|
||||
std::lock(_mutex, that._mutex);
|
||||
std::lock_guard<std::mutex> lock_this(_mutex, std::adopt_lock);
|
||||
std::lock_guard<std::mutex> lock_that(that._mutex, std::adopt_lock);
|
||||
|
||||
_move(std::move(that));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Connection ConnectionPool::fetch() {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
|
||||
if (_pool.empty()) {
|
||||
if (_used_connections == _pool_opts.size) {
|
||||
_wait_for_connection(lock);
|
||||
} else {
|
||||
// Lazily create a new connection.
|
||||
auto connection = _create();
|
||||
|
||||
++_used_connections;
|
||||
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
|
||||
// _pool is NOT empty.
|
||||
auto connection = _fetch();
|
||||
|
||||
auto connection_lifetime = _pool_opts.connection_lifetime;
|
||||
|
||||
if (_sentinel) {
|
||||
auto opts = _opts;
|
||||
auto role_changed = _role_changed(connection.options());
|
||||
auto sentinel = _sentinel;
|
||||
|
||||
lock.unlock();
|
||||
|
||||
if (role_changed || _need_reconnect(connection, connection_lifetime)) {
|
||||
try {
|
||||
connection = _create(sentinel, opts, false);
|
||||
} catch (const Error &e) {
|
||||
// Failed to reconnect, return it to the pool, and retry latter.
|
||||
release(std::move(connection));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
|
||||
if (_need_reconnect(connection, connection_lifetime)) {
|
||||
try {
|
||||
connection.reconnect();
|
||||
} catch (const Error &e) {
|
||||
// Failed to reconnect, return it to the pool, and retry latter.
|
||||
release(std::move(connection));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
ConnectionOptions ConnectionPool::connection_options() {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
return _opts;
|
||||
}
|
||||
|
||||
void ConnectionPool::release(Connection connection) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
_pool.push_back(std::move(connection));
|
||||
}
|
||||
|
||||
_cv.notify_one();
|
||||
}
|
||||
|
||||
Connection ConnectionPool::create() {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
|
||||
auto opts = _opts;
|
||||
|
||||
if (_sentinel) {
|
||||
auto sentinel = _sentinel;
|
||||
|
||||
lock.unlock();
|
||||
|
||||
return _create(sentinel, opts, false);
|
||||
} else {
|
||||
lock.unlock();
|
||||
|
||||
return Connection(opts);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionPool::_move(ConnectionPool &&that) {
|
||||
_opts = std::move(that._opts);
|
||||
_pool_opts = std::move(that._pool_opts);
|
||||
_pool = std::move(that._pool);
|
||||
_used_connections = that._used_connections;
|
||||
_sentinel = std::move(that._sentinel);
|
||||
}
|
||||
|
||||
Connection ConnectionPool::_create() {
|
||||
if (_sentinel) {
|
||||
// Get Redis host and port info from sentinel.
|
||||
return _create(_sentinel, _opts, true);
|
||||
}
|
||||
|
||||
return Connection(_opts);
|
||||
}
|
||||
|
||||
Connection ConnectionPool::_create(SimpleSentinel &sentinel,
|
||||
const ConnectionOptions &opts,
|
||||
bool locked) {
|
||||
try {
|
||||
auto connection = sentinel.create(opts);
|
||||
|
||||
std::unique_lock<std::mutex> lock(_mutex, std::defer_lock);
|
||||
if (!locked) {
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
const auto &connection_opts = connection.options();
|
||||
if (_role_changed(connection_opts)) {
|
||||
// Master/Slave has been changed, reconnect all connections.
|
||||
_update_connection_opts(connection_opts.host, connection_opts.port);
|
||||
}
|
||||
|
||||
return connection;
|
||||
} catch (const StopIterError &e) {
|
||||
throw Error("Failed to create connection with sentinel");
|
||||
}
|
||||
}
|
||||
|
||||
Connection ConnectionPool::_fetch() {
|
||||
assert(!_pool.empty());
|
||||
|
||||
auto connection = std::move(_pool.front());
|
||||
_pool.pop_front();
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
void ConnectionPool::_wait_for_connection(std::unique_lock<std::mutex> &lock) {
|
||||
auto timeout = _pool_opts.wait_timeout;
|
||||
if (timeout > std::chrono::milliseconds(0)) {
|
||||
// Wait until _pool is no longer empty or timeout.
|
||||
if (!_cv.wait_for(lock,
|
||||
timeout,
|
||||
[this] { return !(this->_pool).empty(); })) {
|
||||
throw Error("Failed to fetch a connection in "
|
||||
+ std::to_string(timeout.count()) + " milliseconds");
|
||||
}
|
||||
} else {
|
||||
// Wait forever.
|
||||
_cv.wait(lock, [this] { return !(this->_pool).empty(); });
|
||||
}
|
||||
}
|
||||
|
||||
bool ConnectionPool::_need_reconnect(const Connection &connection,
|
||||
const std::chrono::milliseconds &connection_lifetime) const {
|
||||
if (connection.broken()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (connection_lifetime > std::chrono::milliseconds(0)) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (now - connection.last_active() > connection_lifetime) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
115
ext/redis-plus-plus-1.1.1/src/sw/redis++/connection_pool.h
Normal file
115
ext/redis-plus-plus-1.1.1/src/sw/redis++/connection_pool.h
Normal file
|
@ -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_CONNECTION_POOL_H
|
||||
#define SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H
|
||||
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#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};
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
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<std::mutex> &lock);
|
||||
|
||||
bool _need_reconnect(const Connection &connection,
|
||||
const std::chrono::milliseconds &connection_lifetime) 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<Connection> _pool;
|
||||
|
||||
std::size_t _used_connections = 0;
|
||||
|
||||
std::mutex _mutex;
|
||||
|
||||
std::condition_variable _cv;
|
||||
|
||||
SimpleSentinel _sentinel;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H
|
96
ext/redis-plus-plus-1.1.1/src/sw/redis++/crc16.cpp
Normal file
96
ext/redis-plus-plus-1.1.1/src/sw/redis++/crc16.cpp
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright 2001-2010 Georges Menie (www.menie.org)
|
||||
* Copyright 2010-2012 Salvatore Sanfilippo (adapted to Redis coding style)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the University of California, Berkeley nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* CRC16 implementation according to CCITT standards.
|
||||
*
|
||||
* Note by @antirez: this is actually the XMODEM CRC 16 algorithm, using the
|
||||
* following parameters:
|
||||
*
|
||||
* Name : "XMODEM", also known as "ZMODEM", "CRC-16/ACORN"
|
||||
* Width : 16 bit
|
||||
* Poly : 1021 (That is actually x^16 + x^12 + x^5 + 1)
|
||||
* Initialization : 0000
|
||||
* Reflect Input byte : False
|
||||
* Reflect Output CRC : False
|
||||
* Xor constant to output CRC : 0000
|
||||
* Output for "123456789" : 31C3
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
static const uint16_t crc16tab[256]= {
|
||||
0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
|
||||
0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
|
||||
0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
|
||||
0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
|
||||
0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
|
||||
0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
|
||||
0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
|
||||
0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
|
||||
0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
|
||||
0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
|
||||
0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
|
||||
0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
|
||||
0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
|
||||
0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
|
||||
0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
|
||||
0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
|
||||
0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
|
||||
0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
|
||||
0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
|
||||
0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
|
||||
0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
|
||||
0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
|
||||
0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
|
||||
0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
|
||||
0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
|
||||
0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
|
||||
0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
|
||||
0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
|
||||
0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
|
||||
0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
|
||||
0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
|
||||
0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
|
||||
};
|
||||
|
||||
uint16_t crc16(const char *buf, int len) {
|
||||
int counter;
|
||||
uint16_t crc = 0;
|
||||
for (counter = 0; counter < len; counter++)
|
||||
crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *buf++)&0x00FF];
|
||||
return crc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
136
ext/redis-plus-plus-1.1.1/src/sw/redis++/errors.cpp
Normal file
136
ext/redis-plus-plus-1.1.1/src/sw/redis++/errors.cpp
Normal file
|
@ -0,0 +1,136 @@
|
|||
/**************************************************************************
|
||||
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.
|
||||
*************************************************************************/
|
||||
|
||||
#include "errors.h"
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <unordered_map>
|
||||
#include <tuple>
|
||||
#include "shards.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace sw::redis;
|
||||
|
||||
std::pair<ReplyErrorType, std::string> parse_error(const std::string &msg);
|
||||
|
||||
std::unordered_map<std::string, ReplyErrorType> error_map = {
|
||||
{"MOVED", ReplyErrorType::MOVED},
|
||||
{"ASK", ReplyErrorType::ASK}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
void throw_error(redisContext &context, const std::string &err_info) {
|
||||
auto err_code = context.err;
|
||||
const auto *err_str = context.errstr;
|
||||
if (err_str == nullptr) {
|
||||
throw Error(err_info + ": null error message: " + std::to_string(err_code));
|
||||
}
|
||||
|
||||
auto err_msg = err_info + ": " + err_str;
|
||||
|
||||
switch (err_code) {
|
||||
case REDIS_ERR_IO:
|
||||
if (errno == EAGAIN || errno == EINTR) {
|
||||
throw TimeoutError(err_msg);
|
||||
} else {
|
||||
throw IoError(err_msg);
|
||||
}
|
||||
break;
|
||||
|
||||
case REDIS_ERR_EOF:
|
||||
throw ClosedError(err_msg);
|
||||
break;
|
||||
|
||||
case REDIS_ERR_PROTOCOL:
|
||||
throw ProtoError(err_msg);
|
||||
break;
|
||||
|
||||
case REDIS_ERR_OOM:
|
||||
throw OomError(err_msg);
|
||||
break;
|
||||
|
||||
case REDIS_ERR_OTHER:
|
||||
throw Error(err_msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error(err_info + ": Unknown error code");
|
||||
}
|
||||
}
|
||||
|
||||
void throw_error(const redisReply &reply) {
|
||||
assert(reply.type == REDIS_REPLY_ERROR);
|
||||
|
||||
if (reply.str == nullptr) {
|
||||
throw Error("Null error reply");
|
||||
}
|
||||
|
||||
auto err_str = std::string(reply.str, reply.len);
|
||||
|
||||
auto err_type = ReplyErrorType::ERR;
|
||||
std::string err_msg;
|
||||
std::tie(err_type, err_msg) = parse_error(err_str);
|
||||
|
||||
switch (err_type) {
|
||||
case ReplyErrorType::MOVED:
|
||||
throw MovedError(err_msg);
|
||||
break;
|
||||
|
||||
case ReplyErrorType::ASK:
|
||||
throw AskError(err_msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw ReplyError(err_str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace sw::redis;
|
||||
|
||||
std::pair<ReplyErrorType, std::string> parse_error(const std::string &err) {
|
||||
// The error contains an Error Prefix, and an optional error message.
|
||||
auto idx = err.find_first_of(" \n");
|
||||
|
||||
if (idx == std::string::npos) {
|
||||
throw ProtoError("No Error Prefix: " + err);
|
||||
}
|
||||
|
||||
auto err_prefix = err.substr(0, idx);
|
||||
auto err_type = ReplyErrorType::ERR;
|
||||
|
||||
auto iter = error_map.find(err_prefix);
|
||||
if (iter != error_map.end()) {
|
||||
// Specific error.
|
||||
err_type = iter->second;
|
||||
} // else Generic error.
|
||||
|
||||
return {err_type, err.substr(idx + 1)};
|
||||
}
|
||||
|
||||
}
|
159
ext/redis-plus-plus-1.1.1/src/sw/redis++/errors.h
Normal file
159
ext/redis-plus-plus-1.1.1/src/sw/redis++/errors.h
Normal file
|
@ -0,0 +1,159 @@
|
|||
/**************************************************************************
|
||||
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 <exception>
|
||||
#include <string>
|
||||
#include <hiredis/hiredis.h>
|
||||
|
||||
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() = default;
|
||||
|
||||
virtual const char* what() const noexcept {
|
||||
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() = 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() = 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() = 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() = 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() = 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() = 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() = default;
|
||||
};
|
||||
|
||||
|
||||
// MovedError and AskError are defined in shards.h
|
||||
class MovedError;
|
||||
|
||||
class AskError;
|
||||
|
||||
void throw_error(redisContext &context, const std::string &err_info);
|
||||
|
||||
void throw_error(const redisReply &reply);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_ERRORS_H
|
35
ext/redis-plus-plus-1.1.1/src/sw/redis++/pipeline.cpp
Normal file
35
ext/redis-plus-plus-1.1.1/src/sw/redis++/pipeline.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
/**************************************************************************
|
||||
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.
|
||||
*************************************************************************/
|
||||
|
||||
#include "pipeline.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
std::vector<ReplyUPtr> PipelineImpl::exec(Connection &connection, std::size_t cmd_num) {
|
||||
std::vector<ReplyUPtr> replies;
|
||||
while (cmd_num > 0) {
|
||||
replies.push_back(connection.recv());
|
||||
--cmd_num;
|
||||
}
|
||||
|
||||
return replies;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
49
ext/redis-plus-plus-1.1.1/src/sw/redis++/pipeline.h
Normal file
49
ext/redis-plus-plus-1.1.1/src/sw/redis++/pipeline.h
Normal file
|
@ -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 <cassert>
|
||||
#include <vector>
|
||||
#include "connection.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
class PipelineImpl {
|
||||
public:
|
||||
template <typename Cmd, typename ...Args>
|
||||
void command(Connection &connection, Cmd cmd, Args &&...args) {
|
||||
assert(!connection.broken());
|
||||
|
||||
cmd(connection, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
std::vector<ReplyUPtr> 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
|
1844
ext/redis-plus-plus-1.1.1/src/sw/redis++/queued_redis.h
Normal file
1844
ext/redis-plus-plus-1.1.1/src/sw/redis++/queued_redis.h
Normal file
File diff suppressed because it is too large
Load diff
208
ext/redis-plus-plus-1.1.1/src/sw/redis++/queued_redis.hpp
Normal file
208
ext/redis-plus-plus-1.1.1/src/sw/redis++/queued_redis.hpp
Normal file
|
@ -0,0 +1,208 @@
|
|||
/**************************************************************************
|
||||
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 <typename Impl>
|
||||
template <typename ...Args>
|
||||
QueuedRedis<Impl>::QueuedRedis(const ConnectionSPtr &connection, Args &&...args) :
|
||||
_connection(connection),
|
||||
_impl(std::forward<Args>(args)...) {
|
||||
assert(_connection);
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
Redis QueuedRedis<Impl>::redis() {
|
||||
return Redis(_connection);
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
template <typename Cmd, typename ...Args>
|
||||
auto QueuedRedis<Impl>::command(Cmd cmd, Args &&...args)
|
||||
-> typename std::enable_if<!std::is_convertible<Cmd, StringView>::value,
|
||||
QueuedRedis<Impl>&>::type {
|
||||
try {
|
||||
_sanity_check();
|
||||
|
||||
_impl.command(*_connection, cmd, std::forward<Args>(args)...);
|
||||
|
||||
++_cmd_num;
|
||||
} catch (const Error &e) {
|
||||
_invalidate();
|
||||
throw;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
template <typename ...Args>
|
||||
QueuedRedis<Impl>& QueuedRedis<Impl>::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>(args)...);
|
||||
connection.send(cmd_args);
|
||||
};
|
||||
|
||||
return command(cmd, cmd_name, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
template <typename Input>
|
||||
auto QueuedRedis<Impl>::command(Input first, Input last)
|
||||
-> typename std::enable_if<IsIter<Input>::value, QueuedRedis<Impl>&>::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 <typename Impl>
|
||||
QueuedReplies QueuedRedis<Impl>::exec() {
|
||||
try {
|
||||
_sanity_check();
|
||||
|
||||
auto replies = _impl.exec(*_connection, _cmd_num);
|
||||
|
||||
_rewrite_replies(replies);
|
||||
|
||||
_reset();
|
||||
|
||||
return QueuedReplies(std::move(replies));
|
||||
} catch (const Error &e) {
|
||||
_invalidate();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
void QueuedRedis<Impl>::discard() {
|
||||
try {
|
||||
_sanity_check();
|
||||
|
||||
_impl.discard(*_connection, _cmd_num);
|
||||
|
||||
_reset();
|
||||
} catch (const Error &e) {
|
||||
_invalidate();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
void QueuedRedis<Impl>::_sanity_check() const {
|
||||
if (!_valid) {
|
||||
throw Error("Not in valid state");
|
||||
}
|
||||
|
||||
if (_connection->broken()) {
|
||||
throw Error("Connection is broken");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
inline void QueuedRedis<Impl>::_reset() {
|
||||
_cmd_num = 0;
|
||||
|
||||
_set_cmd_indexes.clear();
|
||||
|
||||
_georadius_cmd_indexes.clear();
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
void QueuedRedis<Impl>::_invalidate() {
|
||||
_valid = false;
|
||||
|
||||
_reset();
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
void QueuedRedis<Impl>::_rewrite_replies(std::vector<ReplyUPtr> &replies) const {
|
||||
_rewrite_replies(_set_cmd_indexes, reply::rewrite_set_reply, replies);
|
||||
|
||||
_rewrite_replies(_georadius_cmd_indexes, reply::rewrite_georadius_reply, replies);
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
template <typename Func>
|
||||
void QueuedRedis<Impl>::_rewrite_replies(const std::vector<std::size_t> &indexes,
|
||||
Func rewriter,
|
||||
std::vector<ReplyUPtr> &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);
|
||||
|
||||
return *reply;
|
||||
}
|
||||
|
||||
template <typename Result>
|
||||
inline Result QueuedReplies::get(std::size_t idx) {
|
||||
auto &reply = get(idx);
|
||||
|
||||
return reply::parse<Result>(reply);
|
||||
}
|
||||
|
||||
template <typename Output>
|
||||
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
|
25
ext/redis-plus-plus-1.1.1/src/sw/redis++/redis++.h
Normal file
25
ext/redis-plus-plus-1.1.1/src/sw/redis++/redis++.h
Normal file
|
@ -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
|
882
ext/redis-plus-plus-1.1.1/src/sw/redis++/redis.cpp
Normal file
882
ext/redis-plus-plus-1.1.1/src/sw/redis++/redis.cpp
Normal file
|
@ -0,0 +1,882 @@
|
|||
/**************************************************************************
|
||||
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.
|
||||
*************************************************************************/
|
||||
|
||||
#include "redis.h"
|
||||
#include <hiredis/hiredis.h>
|
||||
#include "command.h"
|
||||
#include "errors.h"
|
||||
#include "queued_redis.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
Redis::Redis(const std::string &uri) : Redis(ConnectionOptions(uri)) {}
|
||||
|
||||
Redis::Redis(const ConnectionSPtr &connection) : _connection(connection) {
|
||||
assert(_connection);
|
||||
}
|
||||
|
||||
Pipeline Redis::pipeline() {
|
||||
return Pipeline(std::make_shared<Connection>(_pool.create()));
|
||||
}
|
||||
|
||||
Transaction Redis::transaction(bool piped) {
|
||||
return Transaction(std::make_shared<Connection>(_pool.create()), piped);
|
||||
}
|
||||
|
||||
Subscriber Redis::subscriber() {
|
||||
return Subscriber(_pool.create());
|
||||
}
|
||||
|
||||
// CONNECTION commands.
|
||||
|
||||
void Redis::auth(const StringView &password) {
|
||||
auto reply = command(cmd::auth, password);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::echo(const StringView &msg) {
|
||||
auto reply = command(cmd::echo, msg);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::ping() {
|
||||
auto reply = command<void (*)(Connection &)>(cmd::ping);
|
||||
|
||||
return reply::to_status(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::ping(const StringView &msg) {
|
||||
auto reply = command<void (*)(Connection &, const StringView &)>(cmd::ping, msg);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
void Redis::swapdb(long long idx1, long long idx2) {
|
||||
auto reply = command(cmd::swapdb, idx1, idx2);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
// SERVER commands.
|
||||
|
||||
void Redis::bgrewriteaof() {
|
||||
auto reply = command(cmd::bgrewriteaof);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void Redis::bgsave() {
|
||||
auto reply = command(cmd::bgsave);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::dbsize() {
|
||||
auto reply = command(cmd::dbsize);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void Redis::flushall(bool async) {
|
||||
auto reply = command(cmd::flushall, async);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void Redis::flushdb(bool async) {
|
||||
auto reply = command(cmd::flushdb, async);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::info() {
|
||||
auto reply = command<void (*)(Connection &)>(cmd::info);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::info(const StringView §ion) {
|
||||
auto reply = command<void (*)(Connection &, const StringView &)>(cmd::info, section);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::lastsave() {
|
||||
auto reply = command(cmd::lastsave);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void Redis::save() {
|
||||
auto reply = command(cmd::save);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
// KEY commands.
|
||||
|
||||
long long Redis::del(const StringView &key) {
|
||||
auto reply = command(cmd::del, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::dump(const StringView &key) {
|
||||
auto reply = command(cmd::dump, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::exists(const StringView &key) {
|
||||
auto reply = command(cmd::exists, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::expire(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::expire, key, timeout);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::expireat(const StringView &key, long long timestamp) {
|
||||
auto reply = command(cmd::expireat, key, timestamp);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::move(const StringView &key, long long db) {
|
||||
auto reply = command(cmd::move, key, db);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::persist(const StringView &key) {
|
||||
auto reply = command(cmd::persist, key);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::pexpire(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::pexpire, key, timeout);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::pexpireat(const StringView &key, long long timestamp) {
|
||||
auto reply = command(cmd::pexpireat, key, timestamp);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::pttl(const StringView &key) {
|
||||
auto reply = command(cmd::pttl, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::randomkey() {
|
||||
auto reply = command(cmd::randomkey);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
void Redis::rename(const StringView &key, const StringView &newkey) {
|
||||
auto reply = command(cmd::rename, key, newkey);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::renamenx(const StringView &key, const StringView &newkey) {
|
||||
auto reply = command(cmd::renamenx, key, newkey);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
void Redis::restore(const StringView &key,
|
||||
const StringView &val,
|
||||
long long ttl,
|
||||
bool replace) {
|
||||
auto reply = command(cmd::restore, key, val, ttl, replace);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::touch(const StringView &key) {
|
||||
auto reply = command(cmd::touch, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::ttl(const StringView &key) {
|
||||
auto reply = command(cmd::ttl, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::type(const StringView &key) {
|
||||
auto reply = command(cmd::type, key);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::unlink(const StringView &key) {
|
||||
auto reply = command(cmd::unlink, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::wait(long long numslaves, long long timeout) {
|
||||
auto reply = command(cmd::wait, numslaves, timeout);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// STRING commands.
|
||||
|
||||
long long Redis::append(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::append, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::bitcount(const StringView &key, long long start, long long end) {
|
||||
auto reply = command(cmd::bitcount, key, start, end);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::bitop(BitOp op, const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::bitop, op, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::bitpos(const StringView &key,
|
||||
long long bit,
|
||||
long long start,
|
||||
long long end) {
|
||||
auto reply = command(cmd::bitpos, key, bit, start, end);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::decr(const StringView &key) {
|
||||
auto reply = command(cmd::decr, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::decrby(const StringView &key, long long decrement) {
|
||||
auto reply = command(cmd::decrby, key, decrement);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::get(const StringView &key) {
|
||||
auto reply = command(cmd::get, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::getbit(const StringView &key, long long offset) {
|
||||
auto reply = command(cmd::getbit, key, offset);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::getrange(const StringView &key, long long start, long long end) {
|
||||
auto reply = command(cmd::getrange, key, start, end);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::getset(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::getset, key, val);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::incr(const StringView &key) {
|
||||
auto reply = command(cmd::incr, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::incrby(const StringView &key, long long increment) {
|
||||
auto reply = command(cmd::incrby, key, increment);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
double Redis::incrbyfloat(const StringView &key, double increment) {
|
||||
auto reply = command(cmd::incrbyfloat, key, increment);
|
||||
|
||||
return reply::parse<double>(*reply);
|
||||
}
|
||||
|
||||
void Redis::psetex(const StringView &key,
|
||||
long long ttl,
|
||||
const StringView &val) {
|
||||
auto reply = command(cmd::psetex, key, ttl, val);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::set(const StringView &key,
|
||||
const StringView &val,
|
||||
const std::chrono::milliseconds &ttl,
|
||||
UpdateType type) {
|
||||
auto reply = command(cmd::set, key, val, ttl.count(), type);
|
||||
|
||||
reply::rewrite_set_reply(*reply);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
void Redis::setex(const StringView &key,
|
||||
long long ttl,
|
||||
const StringView &val) {
|
||||
auto reply = command(cmd::setex, key, ttl, val);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::setnx(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::setnx, key, val);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::setrange(const StringView &key, long long offset, const StringView &val) {
|
||||
auto reply = command(cmd::setrange, key, offset, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::strlen(const StringView &key) {
|
||||
auto reply = command(cmd::strlen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// LIST commands.
|
||||
|
||||
OptionalStringPair Redis::blpop(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::blpop, key, timeout);
|
||||
|
||||
return reply::parse<OptionalStringPair>(*reply);
|
||||
}
|
||||
|
||||
OptionalStringPair Redis::blpop(const StringView &key, const std::chrono::seconds &timeout) {
|
||||
return blpop(key, timeout.count());
|
||||
}
|
||||
|
||||
OptionalStringPair Redis::brpop(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::brpop, key, timeout);
|
||||
|
||||
return reply::parse<OptionalStringPair>(*reply);
|
||||
}
|
||||
|
||||
OptionalStringPair Redis::brpop(const StringView &key, const std::chrono::seconds &timeout) {
|
||||
return brpop(key, timeout.count());
|
||||
}
|
||||
|
||||
OptionalString Redis::brpoplpush(const StringView &source,
|
||||
const StringView &destination,
|
||||
long long timeout) {
|
||||
auto reply = command(cmd::brpoplpush, source, destination, timeout);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::lindex(const StringView &key, long long index) {
|
||||
auto reply = command(cmd::lindex, key, index);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::linsert(const StringView &key,
|
||||
InsertPosition position,
|
||||
const StringView &pivot,
|
||||
const StringView &val) {
|
||||
auto reply = command(cmd::linsert, key, position, pivot, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::llen(const StringView &key) {
|
||||
auto reply = command(cmd::llen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::lpop(const StringView &key) {
|
||||
auto reply = command(cmd::lpop, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::lpush(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::lpush, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::lpushx(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::lpushx, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::lrem(const StringView &key, long long count, const StringView &val) {
|
||||
auto reply = command(cmd::lrem, key, count, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void Redis::lset(const StringView &key, long long index, const StringView &val) {
|
||||
auto reply = command(cmd::lset, key, index, val);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void Redis::ltrim(const StringView &key, long long start, long long stop) {
|
||||
auto reply = command(cmd::ltrim, key, start, stop);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::rpop(const StringView &key) {
|
||||
auto reply = command(cmd::rpop, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::rpoplpush(const StringView &source, const StringView &destination) {
|
||||
auto reply = command(cmd::rpoplpush, source, destination);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::rpush(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::rpush, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::rpushx(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::rpushx, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::hdel(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hdel, key, field);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::hexists(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hexists, key, field);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::hget(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hget, key, field);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::hincrby(const StringView &key, const StringView &field, long long increment) {
|
||||
auto reply = command(cmd::hincrby, key, field, increment);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
double Redis::hincrbyfloat(const StringView &key, const StringView &field, double increment) {
|
||||
auto reply = command(cmd::hincrbyfloat, key, field, increment);
|
||||
|
||||
return reply::parse<double>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::hlen(const StringView &key) {
|
||||
auto reply = command(cmd::hlen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::hset(const StringView &key, const StringView &field, const StringView &val) {
|
||||
auto reply = command(cmd::hset, key, field, val);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::hset(const StringView &key, const std::pair<StringView, StringView> &item) {
|
||||
return hset(key, item.first, item.second);
|
||||
}
|
||||
|
||||
bool Redis::hsetnx(const StringView &key, const StringView &field, const StringView &val) {
|
||||
auto reply = command(cmd::hsetnx, key, field, val);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::hsetnx(const StringView &key, const std::pair<StringView, StringView> &item) {
|
||||
return hsetnx(key, item.first, item.second);
|
||||
}
|
||||
|
||||
long long Redis::hstrlen(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hstrlen, key, field);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// SET commands.
|
||||
|
||||
long long Redis::sadd(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::sadd, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::scard(const StringView &key) {
|
||||
auto reply = command(cmd::scard, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::sdiffstore(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::sdiffstore, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::sinterstore(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::sinterstore, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::sismember(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::sismember, key, member);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::smove(const StringView &source,
|
||||
const StringView &destination,
|
||||
const StringView &member) {
|
||||
auto reply = command(cmd::smove, source, destination, member);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::spop(const StringView &key) {
|
||||
auto reply = command(cmd::spop, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::srandmember(const StringView &key) {
|
||||
auto reply = command(cmd::srandmember, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::srem(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::srem, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::sunionstore(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::sunionstore, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// SORTED SET commands.
|
||||
|
||||
auto Redis::bzpopmax(const StringView &key, long long timeout)
|
||||
-> Optional<std::tuple<std::string, std::string, double>> {
|
||||
auto reply = command(cmd::bzpopmax, key, timeout);
|
||||
|
||||
return reply::parse<Optional<std::tuple<std::string, std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
auto Redis::bzpopmin(const StringView &key, long long timeout)
|
||||
-> Optional<std::tuple<std::string, std::string, double>> {
|
||||
auto reply = command(cmd::bzpopmin, key, timeout);
|
||||
|
||||
return reply::parse<Optional<std::tuple<std::string, std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::zadd(const StringView &key,
|
||||
const StringView &member,
|
||||
double score,
|
||||
UpdateType type,
|
||||
bool changed) {
|
||||
auto reply = command(cmd::zadd, key, member, score, type, changed);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::zcard(const StringView &key) {
|
||||
auto reply = command(cmd::zcard, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
double Redis::zincrby(const StringView &key, double increment, const StringView &member) {
|
||||
auto reply = command(cmd::zincrby, key, increment, member);
|
||||
|
||||
return reply::parse<double>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::zinterstore(const StringView &destination, const StringView &key, double weight) {
|
||||
auto reply = command(cmd::zinterstore, destination, key, weight);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
Optional<std::pair<std::string, double>> Redis::zpopmax(const StringView &key) {
|
||||
auto reply = command(cmd::zpopmax, key, 1);
|
||||
|
||||
return reply::parse<Optional<std::pair<std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
Optional<std::pair<std::string, double>> Redis::zpopmin(const StringView &key) {
|
||||
auto reply = command(cmd::zpopmin, key, 1);
|
||||
|
||||
return reply::parse<Optional<std::pair<std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong Redis::zrank(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zrank, key, member);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::zrem(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zrem, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::zremrangebyrank(const StringView &key, long long start, long long stop) {
|
||||
auto reply = command(cmd::zremrangebyrank, key, start, stop);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong Redis::zrevrank(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zrevrank, key, member);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
OptionalDouble Redis::zscore(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zscore, key, member);
|
||||
|
||||
return reply::parse<OptionalDouble>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::zunionstore(const StringView &destination, const StringView &key, double weight) {
|
||||
auto reply = command(cmd::zunionstore, destination, key, weight);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// HYPERLOGLOG commands.
|
||||
|
||||
bool Redis::pfadd(const StringView &key, const StringView &element) {
|
||||
auto reply = command(cmd::pfadd, key, element);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::pfcount(const StringView &key) {
|
||||
auto reply = command(cmd::pfcount, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void Redis::pfmerge(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::pfmerge, destination, key);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
// GEO commands.
|
||||
|
||||
long long Redis::geoadd(const StringView &key,
|
||||
const std::tuple<StringView, double, double> &member) {
|
||||
auto reply = command(cmd::geoadd, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalDouble Redis::geodist(const StringView &key,
|
||||
const StringView &member1,
|
||||
const StringView &member2,
|
||||
GeoUnit unit) {
|
||||
auto reply = command(cmd::geodist, key, member1, member2, unit);
|
||||
|
||||
return reply::parse<OptionalDouble>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong Redis::georadius(const StringView &key,
|
||||
const std::pair<double, double> &loc,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
auto reply = command(cmd::georadius_store,
|
||||
key,
|
||||
loc,
|
||||
radius,
|
||||
unit,
|
||||
destination,
|
||||
store_dist,
|
||||
count);
|
||||
|
||||
reply::rewrite_georadius_reply(*reply);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong Redis::georadiusbymember(const StringView &key,
|
||||
const StringView &member,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
auto reply = command(cmd::georadiusbymember_store,
|
||||
key,
|
||||
member,
|
||||
radius,
|
||||
unit,
|
||||
destination,
|
||||
store_dist,
|
||||
count);
|
||||
|
||||
reply::rewrite_georadius_reply(*reply);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
// SCRIPTING commands.
|
||||
|
||||
void Redis::script_flush() {
|
||||
auto reply = command(cmd::script_flush);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void Redis::script_kill() {
|
||||
auto reply = command(cmd::script_kill);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::script_load(const StringView &script) {
|
||||
auto reply = command(cmd::script_load, script);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
// PUBSUB commands.
|
||||
|
||||
long long Redis::publish(const StringView &channel, const StringView &message) {
|
||||
auto reply = command(cmd::publish, channel, message);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// Transaction commands.
|
||||
|
||||
void Redis::watch(const StringView &key) {
|
||||
auto reply = command(cmd::watch, key);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
// Stream commands.
|
||||
|
||||
long long Redis::xack(const StringView &key, const StringView &group, const StringView &id) {
|
||||
auto reply = command(cmd::xack, key, group, id);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::xdel(const StringView &key, const StringView &id) {
|
||||
auto reply = command(cmd::xdel, key, id);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void Redis::xgroup_create(const StringView &key,
|
||||
const StringView &group,
|
||||
const StringView &id,
|
||||
bool mkstream) {
|
||||
auto reply = command(cmd::xgroup_create, key, group, id, mkstream);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void Redis::xgroup_setid(const StringView &key, const StringView &group, const StringView &id) {
|
||||
auto reply = command(cmd::xgroup_setid, key, group, id);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::xgroup_destroy(const StringView &key, const StringView &group) {
|
||||
auto reply = command(cmd::xgroup_destroy, key, group);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::xgroup_delconsumer(const StringView &key,
|
||||
const StringView &group,
|
||||
const StringView &consumer) {
|
||||
auto reply = command(cmd::xgroup_delconsumer, key, group, consumer);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::xlen(const StringView &key) {
|
||||
auto reply = command(cmd::xlen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::xtrim(const StringView &key, long long count, bool approx) {
|
||||
auto reply = command(cmd::xtrim, key, count, approx);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
1523
ext/redis-plus-plus-1.1.1/src/sw/redis++/redis.h
Normal file
1523
ext/redis-plus-plus-1.1.1/src/sw/redis++/redis.h
Normal file
File diff suppressed because it is too large
Load diff
1365
ext/redis-plus-plus-1.1.1/src/sw/redis++/redis.hpp
Normal file
1365
ext/redis-plus-plus-1.1.1/src/sw/redis++/redis.hpp
Normal file
File diff suppressed because it is too large
Load diff
769
ext/redis-plus-plus-1.1.1/src/sw/redis++/redis_cluster.cpp
Normal file
769
ext/redis-plus-plus-1.1.1/src/sw/redis++/redis_cluster.cpp
Normal file
|
@ -0,0 +1,769 @@
|
|||
/**************************************************************************
|
||||
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.
|
||||
*************************************************************************/
|
||||
|
||||
#include "redis_cluster.h"
|
||||
#include <hiredis/hiredis.h>
|
||||
#include "command.h"
|
||||
#include "errors.h"
|
||||
#include "queued_redis.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
RedisCluster::RedisCluster(const std::string &uri) : RedisCluster(ConnectionOptions(uri)) {}
|
||||
|
||||
Redis RedisCluster::redis(const StringView &hash_tag) {
|
||||
auto opts = _pool.connection_options(hash_tag);
|
||||
return Redis(std::make_shared<Connection>(opts));
|
||||
}
|
||||
|
||||
Pipeline RedisCluster::pipeline(const StringView &hash_tag) {
|
||||
auto opts = _pool.connection_options(hash_tag);
|
||||
return Pipeline(std::make_shared<Connection>(opts));
|
||||
}
|
||||
|
||||
Transaction RedisCluster::transaction(const StringView &hash_tag, bool piped) {
|
||||
auto opts = _pool.connection_options(hash_tag);
|
||||
return Transaction(std::make_shared<Connection>(opts), piped);
|
||||
}
|
||||
|
||||
Subscriber RedisCluster::subscriber() {
|
||||
auto opts = _pool.connection_options();
|
||||
return Subscriber(Connection(opts));
|
||||
}
|
||||
|
||||
// KEY commands.
|
||||
|
||||
long long RedisCluster::del(const StringView &key) {
|
||||
auto reply = command(cmd::del, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::dump(const StringView &key) {
|
||||
auto reply = command(cmd::dump, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::exists(const StringView &key) {
|
||||
auto reply = command(cmd::exists, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::expire(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::expire, key, timeout);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::expireat(const StringView &key, long long timestamp) {
|
||||
auto reply = command(cmd::expireat, key, timestamp);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::persist(const StringView &key) {
|
||||
auto reply = command(cmd::persist, key);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::pexpire(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::pexpire, key, timeout);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::pexpireat(const StringView &key, long long timestamp) {
|
||||
auto reply = command(cmd::pexpireat, key, timestamp);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::pttl(const StringView &key) {
|
||||
auto reply = command(cmd::pttl, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::rename(const StringView &key, const StringView &newkey) {
|
||||
auto reply = command(cmd::rename, key, newkey);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::renamenx(const StringView &key, const StringView &newkey) {
|
||||
auto reply = command(cmd::renamenx, key, newkey);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::restore(const StringView &key,
|
||||
const StringView &val,
|
||||
long long ttl,
|
||||
bool replace) {
|
||||
auto reply = command(cmd::restore, key, val, ttl, replace);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::touch(const StringView &key) {
|
||||
auto reply = command(cmd::touch, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::ttl(const StringView &key) {
|
||||
auto reply = command(cmd::ttl, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
std::string RedisCluster::type(const StringView &key) {
|
||||
auto reply = command(cmd::type, key);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::unlink(const StringView &key) {
|
||||
auto reply = command(cmd::unlink, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// STRING commands.
|
||||
|
||||
long long RedisCluster::append(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::append, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::bitcount(const StringView &key, long long start, long long end) {
|
||||
auto reply = command(cmd::bitcount, key, start, end);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::bitop(BitOp op, const StringView &destination, const StringView &key) {
|
||||
auto reply = _command(cmd::bitop, destination, op, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::bitpos(const StringView &key,
|
||||
long long bit,
|
||||
long long start,
|
||||
long long end) {
|
||||
auto reply = command(cmd::bitpos, key, bit, start, end);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::decr(const StringView &key) {
|
||||
auto reply = command(cmd::decr, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::decrby(const StringView &key, long long decrement) {
|
||||
auto reply = command(cmd::decrby, key, decrement);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::get(const StringView &key) {
|
||||
auto reply = command(cmd::get, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::getbit(const StringView &key, long long offset) {
|
||||
auto reply = command(cmd::getbit, key, offset);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
std::string RedisCluster::getrange(const StringView &key, long long start, long long end) {
|
||||
auto reply = command(cmd::getrange, key, start, end);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::getset(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::getset, key, val);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::incr(const StringView &key) {
|
||||
auto reply = command(cmd::incr, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::incrby(const StringView &key, long long increment) {
|
||||
auto reply = command(cmd::incrby, key, increment);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
double RedisCluster::incrbyfloat(const StringView &key, double increment) {
|
||||
auto reply = command(cmd::incrbyfloat, key, increment);
|
||||
|
||||
return reply::parse<double>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::psetex(const StringView &key,
|
||||
long long ttl,
|
||||
const StringView &val) {
|
||||
auto reply = command(cmd::psetex, key, ttl, val);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::set(const StringView &key,
|
||||
const StringView &val,
|
||||
const std::chrono::milliseconds &ttl,
|
||||
UpdateType type) {
|
||||
auto reply = command(cmd::set, key, val, ttl.count(), type);
|
||||
|
||||
reply::rewrite_set_reply(*reply);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::setex(const StringView &key,
|
||||
long long ttl,
|
||||
const StringView &val) {
|
||||
auto reply = command(cmd::setex, key, ttl, val);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::setnx(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::setnx, key, val);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::setrange(const StringView &key, long long offset, const StringView &val) {
|
||||
auto reply = command(cmd::setrange, key, offset, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::strlen(const StringView &key) {
|
||||
auto reply = command(cmd::strlen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// LIST commands.
|
||||
|
||||
OptionalStringPair RedisCluster::blpop(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::blpop, key, timeout);
|
||||
|
||||
return reply::parse<OptionalStringPair>(*reply);
|
||||
}
|
||||
|
||||
OptionalStringPair RedisCluster::blpop(const StringView &key, const std::chrono::seconds &timeout) {
|
||||
return blpop(key, timeout.count());
|
||||
}
|
||||
|
||||
OptionalStringPair RedisCluster::brpop(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::brpop, key, timeout);
|
||||
|
||||
return reply::parse<OptionalStringPair>(*reply);
|
||||
}
|
||||
|
||||
OptionalStringPair RedisCluster::brpop(const StringView &key, const std::chrono::seconds &timeout) {
|
||||
return brpop(key, timeout.count());
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::brpoplpush(const StringView &source,
|
||||
const StringView &destination,
|
||||
long long timeout) {
|
||||
auto reply = command(cmd::brpoplpush, source, destination, timeout);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::lindex(const StringView &key, long long index) {
|
||||
auto reply = command(cmd::lindex, key, index);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::linsert(const StringView &key,
|
||||
InsertPosition position,
|
||||
const StringView &pivot,
|
||||
const StringView &val) {
|
||||
auto reply = command(cmd::linsert, key, position, pivot, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::llen(const StringView &key) {
|
||||
auto reply = command(cmd::llen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::lpop(const StringView &key) {
|
||||
auto reply = command(cmd::lpop, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::lpush(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::lpush, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::lpushx(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::lpushx, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::lrem(const StringView &key, long long count, const StringView &val) {
|
||||
auto reply = command(cmd::lrem, key, count, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::lset(const StringView &key, long long index, const StringView &val) {
|
||||
auto reply = command(cmd::lset, key, index, val);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::ltrim(const StringView &key, long long start, long long stop) {
|
||||
auto reply = command(cmd::ltrim, key, start, stop);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::rpop(const StringView &key) {
|
||||
auto reply = command(cmd::rpop, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::rpoplpush(const StringView &source, const StringView &destination) {
|
||||
auto reply = command(cmd::rpoplpush, source, destination);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::rpush(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::rpush, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::rpushx(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::rpushx, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::hdel(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hdel, key, field);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::hexists(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hexists, key, field);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::hget(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hget, key, field);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::hincrby(const StringView &key, const StringView &field, long long increment) {
|
||||
auto reply = command(cmd::hincrby, key, field, increment);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
double RedisCluster::hincrbyfloat(const StringView &key, const StringView &field, double increment) {
|
||||
auto reply = command(cmd::hincrbyfloat, key, field, increment);
|
||||
|
||||
return reply::parse<double>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::hlen(const StringView &key) {
|
||||
auto reply = command(cmd::hlen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::hset(const StringView &key, const StringView &field, const StringView &val) {
|
||||
auto reply = command(cmd::hset, key, field, val);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::hset(const StringView &key, const std::pair<StringView, StringView> &item) {
|
||||
return hset(key, item.first, item.second);
|
||||
}
|
||||
|
||||
bool RedisCluster::hsetnx(const StringView &key, const StringView &field, const StringView &val) {
|
||||
auto reply = command(cmd::hsetnx, key, field, val);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::hsetnx(const StringView &key, const std::pair<StringView, StringView> &item) {
|
||||
return hsetnx(key, item.first, item.second);
|
||||
}
|
||||
|
||||
long long RedisCluster::hstrlen(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hstrlen, key, field);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// SET commands.
|
||||
|
||||
long long RedisCluster::sadd(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::sadd, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::scard(const StringView &key) {
|
||||
auto reply = command(cmd::scard, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::sdiffstore(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::sdiffstore, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::sinterstore(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::sinterstore, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::sismember(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::sismember, key, member);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::smove(const StringView &source,
|
||||
const StringView &destination,
|
||||
const StringView &member) {
|
||||
auto reply = command(cmd::smove, source, destination, member);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::spop(const StringView &key) {
|
||||
auto reply = command(cmd::spop, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::srandmember(const StringView &key) {
|
||||
auto reply = command(cmd::srandmember, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::srem(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::srem, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::sunionstore(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::sunionstore, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// SORTED SET commands.
|
||||
|
||||
auto RedisCluster::bzpopmax(const StringView &key, long long timeout)
|
||||
-> Optional<std::tuple<std::string, std::string, double>> {
|
||||
auto reply = command(cmd::bzpopmax, key, timeout);
|
||||
|
||||
return reply::parse<Optional<std::tuple<std::string, std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
auto RedisCluster::bzpopmin(const StringView &key, long long timeout)
|
||||
-> Optional<std::tuple<std::string, std::string, double>> {
|
||||
auto reply = command(cmd::bzpopmin, key, timeout);
|
||||
|
||||
return reply::parse<Optional<std::tuple<std::string, std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::zadd(const StringView &key,
|
||||
const StringView &member,
|
||||
double score,
|
||||
UpdateType type,
|
||||
bool changed) {
|
||||
auto reply = command(cmd::zadd, key, member, score, type, changed);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::zcard(const StringView &key) {
|
||||
auto reply = command(cmd::zcard, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
double RedisCluster::zincrby(const StringView &key, double increment, const StringView &member) {
|
||||
auto reply = command(cmd::zincrby, key, increment, member);
|
||||
|
||||
return reply::parse<double>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::zinterstore(const StringView &destination,
|
||||
const StringView &key,
|
||||
double weight) {
|
||||
auto reply = command(cmd::zinterstore, destination, key, weight);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
Optional<std::pair<std::string, double>> RedisCluster::zpopmax(const StringView &key) {
|
||||
auto reply = command(cmd::zpopmax, key, 1);
|
||||
|
||||
return reply::parse<Optional<std::pair<std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
Optional<std::pair<std::string, double>> RedisCluster::zpopmin(const StringView &key) {
|
||||
auto reply = command(cmd::zpopmin, key, 1);
|
||||
|
||||
return reply::parse<Optional<std::pair<std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong RedisCluster::zrank(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zrank, key, member);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::zrem(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zrem, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::zremrangebyrank(const StringView &key, long long start, long long stop) {
|
||||
auto reply = command(cmd::zremrangebyrank, key, start, stop);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong RedisCluster::zrevrank(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zrevrank, key, member);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
OptionalDouble RedisCluster::zscore(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zscore, key, member);
|
||||
|
||||
return reply::parse<OptionalDouble>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::zunionstore(const StringView &destination,
|
||||
const StringView &key,
|
||||
double weight) {
|
||||
auto reply = command(cmd::zunionstore, destination, key, weight);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// HYPERLOGLOG commands.
|
||||
|
||||
bool RedisCluster::pfadd(const StringView &key, const StringView &element) {
|
||||
auto reply = command(cmd::pfadd, key, element);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::pfcount(const StringView &key) {
|
||||
auto reply = command(cmd::pfcount, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::pfmerge(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::pfmerge, destination, key);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
// GEO commands.
|
||||
|
||||
long long RedisCluster::geoadd(const StringView &key,
|
||||
const std::tuple<StringView, double, double> &member) {
|
||||
auto reply = command(cmd::geoadd, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalDouble RedisCluster::geodist(const StringView &key,
|
||||
const StringView &member1,
|
||||
const StringView &member2,
|
||||
GeoUnit unit) {
|
||||
auto reply = command(cmd::geodist, key, member1, member2, unit);
|
||||
|
||||
return reply::parse<OptionalDouble>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong RedisCluster::georadius(const StringView &key,
|
||||
const std::pair<double, double> &loc,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
auto reply = command(cmd::georadius_store,
|
||||
key,
|
||||
loc,
|
||||
radius,
|
||||
unit,
|
||||
destination,
|
||||
store_dist,
|
||||
count);
|
||||
|
||||
reply::rewrite_georadius_reply(*reply);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong RedisCluster::georadiusbymember(const StringView &key,
|
||||
const StringView &member,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
auto reply = command(cmd::georadiusbymember_store,
|
||||
key,
|
||||
member,
|
||||
radius,
|
||||
unit,
|
||||
destination,
|
||||
store_dist,
|
||||
count);
|
||||
|
||||
reply::rewrite_georadius_reply(*reply);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
// PUBSUB commands.
|
||||
|
||||
long long RedisCluster::publish(const StringView &channel, const StringView &message) {
|
||||
auto reply = command(cmd::publish, channel, message);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// Stream commands.
|
||||
|
||||
long long RedisCluster::xack(const StringView &key, const StringView &group, const StringView &id) {
|
||||
auto reply = command(cmd::xack, key, group, id);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::xdel(const StringView &key, const StringView &id) {
|
||||
auto reply = command(cmd::xdel, key, id);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::xgroup_create(const StringView &key,
|
||||
const StringView &group,
|
||||
const StringView &id,
|
||||
bool mkstream) {
|
||||
auto reply = command(cmd::xgroup_create, key, group, id, mkstream);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::xgroup_setid(const StringView &key,
|
||||
const StringView &group,
|
||||
const StringView &id) {
|
||||
auto reply = command(cmd::xgroup_setid, key, group, id);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::xgroup_destroy(const StringView &key, const StringView &group) {
|
||||
auto reply = command(cmd::xgroup_destroy, key, group);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::xgroup_delconsumer(const StringView &key,
|
||||
const StringView &group,
|
||||
const StringView &consumer) {
|
||||
auto reply = command(cmd::xgroup_delconsumer, key, group, consumer);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::xlen(const StringView &key) {
|
||||
auto reply = command(cmd::xlen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::xtrim(const StringView &key, long long count, bool approx) {
|
||||
auto reply = command(cmd::xtrim, key, count, approx);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::_asking(Connection &connection) {
|
||||
// Send ASKING command.
|
||||
connection.send("ASKING");
|
||||
|
||||
auto reply = connection.recv();
|
||||
|
||||
assert(reply);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
1395
ext/redis-plus-plus-1.1.1/src/sw/redis++/redis_cluster.h
Normal file
1395
ext/redis-plus-plus-1.1.1/src/sw/redis++/redis_cluster.h
Normal file
File diff suppressed because it is too large
Load diff
1415
ext/redis-plus-plus-1.1.1/src/sw/redis++/redis_cluster.hpp
Normal file
1415
ext/redis-plus-plus-1.1.1/src/sw/redis++/redis_cluster.hpp
Normal file
File diff suppressed because it is too large
Load diff
150
ext/redis-plus-plus-1.1.1/src/sw/redis++/reply.cpp
Normal file
150
ext/redis-plus-plus-1.1.1/src/sw/redis++/reply.cpp
Normal file
|
@ -0,0 +1,150 @@
|
|||
/**************************************************************************
|
||||
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.
|
||||
*************************************************************************/
|
||||
|
||||
#include "reply.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace reply {
|
||||
|
||||
std::string to_status(redisReply &reply) {
|
||||
if (!reply::is_status(reply)) {
|
||||
throw ProtoError("Expect STATUS reply");
|
||||
}
|
||||
|
||||
if (reply.str == nullptr) {
|
||||
throw ProtoError("A null status reply");
|
||||
}
|
||||
|
||||
// Old version hiredis' *redisReply::len* is of type int.
|
||||
// So we CANNOT have something like: *return {reply.str, reply.len}*.
|
||||
return std::string(reply.str, reply.len);
|
||||
}
|
||||
|
||||
std::string parse(ParseTag<std::string>, redisReply &reply) {
|
||||
if (!reply::is_string(reply) && !reply::is_status(reply)) {
|
||||
throw ProtoError("Expect STRING reply");
|
||||
}
|
||||
|
||||
if (reply.str == nullptr) {
|
||||
throw ProtoError("A null string reply");
|
||||
}
|
||||
|
||||
// Old version hiredis' *redisReply::len* is of type int.
|
||||
// So we CANNOT have something like: *return {reply.str, reply.len}*.
|
||||
return std::string(reply.str, reply.len);
|
||||
}
|
||||
|
||||
long long parse(ParseTag<long long>, redisReply &reply) {
|
||||
if (!reply::is_integer(reply)) {
|
||||
throw ProtoError("Expect INTEGER reply");
|
||||
}
|
||||
|
||||
return reply.integer;
|
||||
}
|
||||
|
||||
double parse(ParseTag<double>, redisReply &reply) {
|
||||
return std::stod(parse<std::string>(reply));
|
||||
}
|
||||
|
||||
bool parse(ParseTag<bool>, redisReply &reply) {
|
||||
auto ret = parse<long long>(reply);
|
||||
|
||||
if (ret == 1) {
|
||||
return true;
|
||||
} else if (ret == 0) {
|
||||
return false;
|
||||
} else {
|
||||
throw ProtoError("Invalid bool reply: " + std::to_string(ret));
|
||||
}
|
||||
}
|
||||
|
||||
void parse(ParseTag<void>, redisReply &reply) {
|
||||
if (!reply::is_status(reply)) {
|
||||
throw ProtoError("Expect STATUS reply");
|
||||
}
|
||||
|
||||
if (reply.str == nullptr) {
|
||||
throw ProtoError("A null status reply");
|
||||
}
|
||||
|
||||
static const std::string OK = "OK";
|
||||
|
||||
// Old version hiredis' *redisReply::len* is of type int.
|
||||
// So we have to cast it to an unsigned int.
|
||||
if (static_cast<std::size_t>(reply.len) != OK.size()
|
||||
|| OK.compare(0, OK.size(), reply.str, reply.len) != 0) {
|
||||
throw ProtoError("NOT ok status reply: " + reply::to_status(reply));
|
||||
}
|
||||
}
|
||||
|
||||
void rewrite_set_reply(redisReply &reply) {
|
||||
if (is_nil(reply)) {
|
||||
// Failed to set, and make it a FALSE reply.
|
||||
reply.type = REDIS_REPLY_INTEGER;
|
||||
reply.integer = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if it's a "OK" status reply.
|
||||
reply::parse<void>(reply);
|
||||
|
||||
assert(is_status(reply) && reply.str != nullptr);
|
||||
|
||||
free(reply.str);
|
||||
|
||||
// Make it a TRUE reply.
|
||||
reply.type = REDIS_REPLY_INTEGER;
|
||||
reply.integer = 1;
|
||||
}
|
||||
|
||||
void rewrite_georadius_reply(redisReply &reply) {
|
||||
if (is_array(reply) && reply.element == nullptr) {
|
||||
// Make it a nil reply.
|
||||
reply.type = REDIS_REPLY_NIL;
|
||||
}
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
bool is_flat_array(redisReply &reply) {
|
||||
assert(reply::is_array(reply));
|
||||
|
||||
// Empty array reply.
|
||||
if (reply.element == nullptr || reply.elements == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto *sub_reply = reply.element[0];
|
||||
|
||||
// Null element.
|
||||
if (sub_reply == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !reply::is_array(*sub_reply);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
363
ext/redis-plus-plus-1.1.1/src/sw/redis++/reply.h
Normal file
363
ext/redis-plus-plus-1.1.1/src/sw/redis++/reply.h
Normal file
|
@ -0,0 +1,363 @@
|
|||
/**************************************************************************
|
||||
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 <cassert>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <hiredis/hiredis.h>
|
||||
#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<redisReply, ReplyDeleter>;
|
||||
|
||||
namespace reply {
|
||||
|
||||
template <typename T>
|
||||
struct ParseTag {};
|
||||
|
||||
template <typename T>
|
||||
inline T parse(redisReply &reply) {
|
||||
return parse(ParseTag<T>(), reply);
|
||||
}
|
||||
|
||||
void parse(ParseTag<void>, redisReply &reply);
|
||||
|
||||
std::string parse(ParseTag<std::string>, redisReply &reply);
|
||||
|
||||
long long parse(ParseTag<long long>, redisReply &reply);
|
||||
|
||||
double parse(ParseTag<double>, redisReply &reply);
|
||||
|
||||
bool parse(ParseTag<bool>, redisReply &reply);
|
||||
|
||||
template <typename T>
|
||||
Optional<T> parse(ParseTag<Optional<T>>, redisReply &reply);
|
||||
|
||||
template <typename T, typename U>
|
||||
std::pair<T, U> parse(ParseTag<std::pair<T, U>>, redisReply &reply);
|
||||
|
||||
template <typename ...Args>
|
||||
std::tuple<Args...> parse(ParseTag<std::tuple<Args...>>, redisReply &reply);
|
||||
|
||||
template <typename T, typename std::enable_if<IsSequenceContainer<T>::value, int>::type = 0>
|
||||
T parse(ParseTag<T>, redisReply &reply);
|
||||
|
||||
template <typename T, typename std::enable_if<IsAssociativeContainer<T>::value, int>::type = 0>
|
||||
T parse(ParseTag<T>, redisReply &reply);
|
||||
|
||||
template <typename Output>
|
||||
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 <typename Output>
|
||||
void to_array(redisReply &reply, Output output);
|
||||
|
||||
// Rewrite set reply to bool type
|
||||
void rewrite_set_reply(redisReply &reply);
|
||||
|
||||
// Rewrite georadius reply to OptionalLongLong type
|
||||
void rewrite_georadius_reply(redisReply &reply);
|
||||
|
||||
template <typename Output>
|
||||
auto parse_xpending_reply(redisReply &reply, Output output)
|
||||
-> std::tuple<long long, OptionalString, OptionalString>;
|
||||
|
||||
}
|
||||
|
||||
// Inline implementations.
|
||||
|
||||
namespace reply {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename Output>
|
||||
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<typename IterType<Output>::type>(*sub_reply);
|
||||
|
||||
++output;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_flat_array(redisReply &reply);
|
||||
|
||||
template <typename Output>
|
||||
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<Output>::type;
|
||||
using FirstType = typename std::decay<typename Pair::first_type>::type;
|
||||
using SecondType = typename std::decay<typename Pair::second_type>::type;
|
||||
*output = std::make_pair(parse<FirstType>(*key_reply),
|
||||
parse<SecondType>(*val_reply));
|
||||
|
||||
++output;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Output>
|
||||
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 <typename Output>
|
||||
void to_array(std::false_type, redisReply &reply, Output output) {
|
||||
to_array(reply, output);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::tuple<T> 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<T>(*sub_reply));
|
||||
}
|
||||
|
||||
template <typename T, typename ...Args>
|
||||
auto parse_tuple(redisReply **reply, std::size_t idx) ->
|
||||
typename std::enable_if<sizeof...(Args) != 0, std::tuple<T, Args...>>::type {
|
||||
assert(reply != nullptr);
|
||||
|
||||
return std::tuple_cat(parse_tuple<T>(reply, idx),
|
||||
parse_tuple<Args...>(reply, idx + 1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Optional<T> parse(ParseTag<Optional<T>>, redisReply &reply) {
|
||||
if (reply::is_nil(reply)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return Optional<T>(parse<T>(reply));
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
std::pair<T, U> parse(ParseTag<std::pair<T, U>>, 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<typename std::decay<T>::type>(*first),
|
||||
parse<typename std::decay<U>::type>(*second));
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
std::tuple<Args...> parse(ParseTag<std::tuple<Args...>>, 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<Args...>(reply.element, 0);
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<IsSequenceContainer<T>::value, int>::type>
|
||||
T parse(ParseTag<T>, redisReply &reply) {
|
||||
if (!is_array(reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
T container;
|
||||
|
||||
to_array(reply, std::back_inserter(container));
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<IsAssociativeContainer<T>::value, int>::type>
|
||||
T parse(ParseTag<T>, redisReply &reply) {
|
||||
if (!is_array(reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
T container;
|
||||
|
||||
to_array(reply, std::inserter(container, container.end()));
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
template <typename Output>
|
||||
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<std::string>(*cursor_reply);
|
||||
auto 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 <typename Output>
|
||||
void to_array(redisReply &reply, Output output) {
|
||||
if (!is_array(reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
detail::to_array(typename IsKvPairIter<Output>::type(), reply, output);
|
||||
}
|
||||
|
||||
template <typename Output>
|
||||
auto parse_xpending_reply(redisReply &reply, Output output)
|
||||
-> std::tuple<long long, OptionalString, OptionalString> {
|
||||
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<long long>(*(reply.element[0]));
|
||||
auto start = parse<OptionalString>(*(reply.element[1]));
|
||||
auto end = parse<OptionalString>(*(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
|
361
ext/redis-plus-plus-1.1.1/src/sw/redis++/sentinel.cpp
Normal file
361
ext/redis-plus-plus-1.1.1/src/sw/redis++/sentinel.cpp
Normal file
|
@ -0,0 +1,361 @@
|
|||
/**************************************************************************
|
||||
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.
|
||||
*************************************************************************/
|
||||
|
||||
#include "sentinel.h"
|
||||
#include <cassert>
|
||||
#include <thread>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include "redis.h"
|
||||
#include "errors.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
class Sentinel::Iterator {
|
||||
public:
|
||||
Iterator(std::list<Connection> &healthy_sentinels,
|
||||
std::list<ConnectionOptions> &broken_sentinels) :
|
||||
_healthy_sentinels(healthy_sentinels),
|
||||
_broken_sentinels(broken_sentinels) {
|
||||
reset();
|
||||
}
|
||||
|
||||
Connection& next();
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
std::list<Connection> &_healthy_sentinels;
|
||||
|
||||
std::size_t _healthy_size = 0;
|
||||
|
||||
std::list<ConnectionOptions> &_broken_sentinels;
|
||||
|
||||
std::size_t _broken_size = 0;
|
||||
};
|
||||
|
||||
Connection& Sentinel::Iterator::next() {
|
||||
while (_healthy_size > 0) {
|
||||
assert(_healthy_sentinels.size() >= _healthy_size);
|
||||
|
||||
--_healthy_size;
|
||||
|
||||
auto &connection = _healthy_sentinels.front();
|
||||
if (connection.broken()) {
|
||||
_broken_sentinels.push_front(connection.options());
|
||||
++_broken_size;
|
||||
|
||||
_healthy_sentinels.pop_front();
|
||||
} else {
|
||||
_healthy_sentinels.splice(_healthy_sentinels.end(),
|
||||
_healthy_sentinels,
|
||||
_healthy_sentinels.begin());
|
||||
|
||||
return _healthy_sentinels.back();
|
||||
}
|
||||
}
|
||||
|
||||
while (_broken_size > 0) {
|
||||
assert(_broken_sentinels.size() >= _broken_size);
|
||||
|
||||
--_broken_size;
|
||||
|
||||
try {
|
||||
const auto &opt = _broken_sentinels.front();
|
||||
Connection connection(opt);
|
||||
_healthy_sentinels.push_back(std::move(connection));
|
||||
|
||||
_broken_sentinels.pop_front();
|
||||
|
||||
return _healthy_sentinels.back();
|
||||
} catch (const Error &e) {
|
||||
// Failed to connect to sentinel.
|
||||
_broken_sentinels.splice(_broken_sentinels.end(),
|
||||
_broken_sentinels,
|
||||
_broken_sentinels.begin());
|
||||
}
|
||||
}
|
||||
|
||||
throw StopIterError();
|
||||
}
|
||||
|
||||
void Sentinel::Iterator::reset() {
|
||||
_healthy_size = _healthy_sentinels.size();
|
||||
_broken_size = _broken_sentinels.size();
|
||||
}
|
||||
|
||||
Sentinel::Sentinel(const SentinelOptions &sentinel_opts) :
|
||||
_broken_sentinels(_parse_options(sentinel_opts)),
|
||||
_sentinel_opts(sentinel_opts) {
|
||||
if (_sentinel_opts.connect_timeout == std::chrono::milliseconds(0)
|
||||
|| _sentinel_opts.socket_timeout == std::chrono::milliseconds(0)) {
|
||||
throw Error("With sentinel, connection timeout and socket timeout cannot be 0");
|
||||
}
|
||||
}
|
||||
|
||||
Connection Sentinel::master(const std::string &master_name, const ConnectionOptions &opts) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
Iterator iter(_healthy_sentinels, _broken_sentinels);
|
||||
std::size_t retries = 0;
|
||||
while (true) {
|
||||
try {
|
||||
auto &sentinel = iter.next();
|
||||
|
||||
auto master = _get_master_addr_by_name(sentinel, master_name);
|
||||
if (!master) {
|
||||
// Try the next sentinel.
|
||||
continue;
|
||||
}
|
||||
|
||||
auto connection = _connect_redis(*master, opts);
|
||||
if (_get_role(connection) != Role::MASTER) {
|
||||
// Retry the whole process at most SentinelOptions::max_retry times.
|
||||
++retries;
|
||||
if (retries > _sentinel_opts.max_retry) {
|
||||
throw Error("Failed to get master from sentinel");
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(_sentinel_opts.retry_interval);
|
||||
|
||||
// Restart the iteration.
|
||||
iter.reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
return connection;
|
||||
} catch (const StopIterError &e) {
|
||||
throw;
|
||||
} catch (const Error &e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connection Sentinel::slave(const std::string &master_name, const ConnectionOptions &opts) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
Iterator iter(_healthy_sentinels, _broken_sentinels);
|
||||
std::size_t retries = 0;
|
||||
while (true) {
|
||||
try {
|
||||
auto &sentinel = iter.next();
|
||||
|
||||
auto slaves = _get_slave_addr_by_name(sentinel, master_name);
|
||||
if (slaves.empty()) {
|
||||
// Try the next sentinel.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Normally slaves list is NOT very large, so there won't be a performance problem.
|
||||
auto slave_iter = std::find(slaves.begin(),
|
||||
slaves.end(),
|
||||
Node{opts.host, opts.port});
|
||||
if (slave_iter != slaves.end() && slave_iter != slaves.begin()) {
|
||||
// The given node is still a valid slave. Try it first.
|
||||
std::swap(*(slaves.begin()), *slave_iter);
|
||||
}
|
||||
|
||||
for (const auto &slave : slaves) {
|
||||
try {
|
||||
auto connection = _connect_redis(slave, opts);
|
||||
if (_get_role(connection) != Role::SLAVE) {
|
||||
// Retry the whole process at most SentinelOptions::max_retry times.
|
||||
++retries;
|
||||
if (retries > _sentinel_opts.max_retry) {
|
||||
throw Error("Failed to get slave from sentinel");
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(_sentinel_opts.retry_interval);
|
||||
|
||||
// Restart the iteration.
|
||||
iter.reset();
|
||||
break;
|
||||
}
|
||||
|
||||
return connection;
|
||||
} catch (const Error &e) {
|
||||
// Try the next slave.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (const StopIterError &e) {
|
||||
throw;
|
||||
} catch (const Error &e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Optional<Node> Sentinel::_get_master_addr_by_name(Connection &connection, const StringView &name) {
|
||||
connection.send("SENTINEL GET-MASTER-ADDR-BY-NAME %b", name.data(), name.size());
|
||||
|
||||
auto reply = connection.recv();
|
||||
|
||||
assert(reply);
|
||||
|
||||
auto master = reply::parse<Optional<std::pair<std::string, std::string>>>(*reply);
|
||||
if (!master) {
|
||||
return {};
|
||||
}
|
||||
|
||||
int port = 0;
|
||||
try {
|
||||
port = std::stoi(master->second);
|
||||
} catch (const std::exception &) {
|
||||
throw ProtoError("Master port is invalid: " + master->second);
|
||||
}
|
||||
|
||||
return Optional<Node>{Node{master->first, port}};
|
||||
}
|
||||
|
||||
std::vector<Node> Sentinel::_get_slave_addr_by_name(Connection &connection,
|
||||
const StringView &name) {
|
||||
try {
|
||||
connection.send("SENTINEL SLAVES %b", name.data(), name.size());
|
||||
|
||||
auto reply = connection.recv();
|
||||
|
||||
assert(reply);
|
||||
|
||||
auto slaves = _parse_slave_info(*reply);
|
||||
|
||||
// Make slave list random.
|
||||
std::mt19937 gen(std::random_device{}());
|
||||
std::shuffle(slaves.begin(), slaves.end(), gen);
|
||||
|
||||
return slaves;
|
||||
} catch (const ReplyError &e) {
|
||||
// Unknown master name.
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Node> Sentinel::_parse_slave_info(redisReply &reply) const {
|
||||
using SlaveInfo = std::unordered_map<std::string, std::string>;
|
||||
|
||||
auto slaves = reply::parse<std::vector<SlaveInfo>>(reply);
|
||||
|
||||
std::vector<Node> nodes;
|
||||
for (const auto &slave : slaves) {
|
||||
auto flags_iter = slave.find("flags");
|
||||
auto ip_iter = slave.find("ip");
|
||||
auto port_iter = slave.find("port");
|
||||
if (flags_iter == slave.end() || ip_iter == slave.end() || port_iter == slave.end()) {
|
||||
throw ProtoError("Invalid slave info");
|
||||
}
|
||||
|
||||
// This slave is down, e.g. 's_down,slave,disconnected'
|
||||
if (flags_iter->second != "slave") {
|
||||
continue;
|
||||
}
|
||||
|
||||
int port = 0;
|
||||
try {
|
||||
port = std::stoi(port_iter->second);
|
||||
} catch (const std::exception &) {
|
||||
throw ProtoError("Slave port is invalid: " + port_iter->second);
|
||||
}
|
||||
|
||||
nodes.push_back(Node{ip_iter->second, port});
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Connection Sentinel::_connect_redis(const Node &node, ConnectionOptions opts) {
|
||||
opts.host = node.host;
|
||||
opts.port = node.port;
|
||||
|
||||
return Connection(opts);
|
||||
}
|
||||
|
||||
Role Sentinel::_get_role(Connection &connection) {
|
||||
connection.send("INFO REPLICATION");
|
||||
auto reply = connection.recv();
|
||||
|
||||
assert(reply);
|
||||
auto info = reply::parse<std::string>(*reply);
|
||||
|
||||
auto start = info.find("role:");
|
||||
if (start == std::string::npos) {
|
||||
throw ProtoError("Invalid INFO REPLICATION reply");
|
||||
}
|
||||
start += 5;
|
||||
auto stop = info.find("\r\n", start);
|
||||
if (stop == std::string::npos) {
|
||||
throw ProtoError("Invalid INFO REPLICATION reply");
|
||||
}
|
||||
|
||||
auto role = info.substr(start, stop - start);
|
||||
if (role == "master") {
|
||||
return Role::MASTER;
|
||||
} else if (role == "slave") {
|
||||
return Role::SLAVE;
|
||||
} else {
|
||||
throw Error("Invalid role: " + role);
|
||||
}
|
||||
}
|
||||
|
||||
std::list<ConnectionOptions> Sentinel::_parse_options(const SentinelOptions &opts) const {
|
||||
std::list<ConnectionOptions> options;
|
||||
for (const auto &node : opts.nodes) {
|
||||
ConnectionOptions opt;
|
||||
opt.host = node.first;
|
||||
opt.port = node.second;
|
||||
opt.password = opts.password;
|
||||
opt.keep_alive = opts.keep_alive;
|
||||
opt.connect_timeout = opts.connect_timeout;
|
||||
opt.socket_timeout = opts.socket_timeout;
|
||||
|
||||
options.push_back(opt);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
SimpleSentinel::SimpleSentinel(const std::shared_ptr<Sentinel> &sentinel,
|
||||
const std::string &master_name,
|
||||
Role role) :
|
||||
_sentinel(sentinel),
|
||||
_master_name(master_name),
|
||||
_role(role) {
|
||||
if (!_sentinel) {
|
||||
throw Error("Sentinel cannot be null");
|
||||
}
|
||||
|
||||
if (_role != Role::MASTER && _role != Role::SLAVE) {
|
||||
throw Error("Role must be Role::MASTER or Role::SLAVE");
|
||||
}
|
||||
}
|
||||
|
||||
Connection SimpleSentinel::create(const ConnectionOptions &opts) {
|
||||
assert(_sentinel);
|
||||
|
||||
if (_role == Role::MASTER) {
|
||||
return _sentinel->master(_master_name, opts);
|
||||
}
|
||||
|
||||
assert(_role == Role::SLAVE);
|
||||
|
||||
return _sentinel->slave(_master_name, opts);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
138
ext/redis-plus-plus-1.1.1/src/sw/redis++/sentinel.h
Normal file
138
ext/redis-plus-plus-1.1.1/src/sw/redis++/sentinel.h
Normal file
|
@ -0,0 +1,138 @@
|
|||
/**************************************************************************
|
||||
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 <string>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include "connection.h"
|
||||
#include "shards.h"
|
||||
#include "reply.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
struct SentinelOptions {
|
||||
std::vector<std::pair<std::string, int>> 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;
|
||||
};
|
||||
|
||||
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<ConnectionOptions> _parse_options(const SentinelOptions &opts) const;
|
||||
|
||||
Optional<Node> _get_master_addr_by_name(Connection &connection, const StringView &name);
|
||||
|
||||
std::vector<Node> _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<Node> _parse_slave_info(redisReply &reply) const;
|
||||
|
||||
std::list<Connection> _healthy_sentinels;
|
||||
|
||||
std::list<ConnectionOptions> _broken_sentinels;
|
||||
|
||||
SentinelOptions _sentinel_opts;
|
||||
|
||||
std::mutex _mutex;
|
||||
};
|
||||
|
||||
class SimpleSentinel {
|
||||
public:
|
||||
SimpleSentinel(const std::shared_ptr<Sentinel> &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> _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() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_SENTINEL_H
|
50
ext/redis-plus-plus-1.1.1/src/sw/redis++/shards.cpp
Normal file
50
ext/redis-plus-plus-1.1.1/src/sw/redis++/shards.cpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
/**************************************************************************
|
||||
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.
|
||||
*************************************************************************/
|
||||
|
||||
#include "shards.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
RedirectionError::RedirectionError(const std::string &msg): ReplyError(msg) {
|
||||
std::tie(_slot, _node) = _parse_error(msg);
|
||||
}
|
||||
|
||||
std::pair<Slot, Node> RedirectionError::_parse_error(const std::string &msg) const {
|
||||
// "slot ip:port"
|
||||
auto space_pos = msg.find(" ");
|
||||
auto colon_pos = msg.find(":");
|
||||
if (space_pos == std::string::npos
|
||||
|| colon_pos == std::string::npos
|
||||
|| colon_pos < space_pos) {
|
||||
throw ProtoError("Invalid ASK error message: " + msg);
|
||||
}
|
||||
|
||||
try {
|
||||
auto slot = std::stoull(msg.substr(0, space_pos));
|
||||
auto host = msg.substr(space_pos + 1, colon_pos - space_pos - 1);
|
||||
auto port = std::stoi(msg.substr(colon_pos + 1));
|
||||
|
||||
return {slot, {host, port}};
|
||||
} catch (const std::exception &e) {
|
||||
throw ProtoError("Invalid ASK error message: " + msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
115
ext/redis-plus-plus-1.1.1/src/sw/redis++/shards.h
Normal file
115
ext/redis-plus-plus-1.1.1/src/sw/redis++/shards.h
Normal file
|
@ -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 <string>
|
||||
#include <map>
|
||||
#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<std::string>{}(node.host);
|
||||
auto port_hash = std::hash<int>{}(node.port);
|
||||
return host_hash ^ (port_hash << 1);
|
||||
}
|
||||
};
|
||||
|
||||
using Shards = std::map<SlotRange, Node>;
|
||||
|
||||
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() = default;
|
||||
|
||||
Slot slot() const {
|
||||
return _slot;
|
||||
}
|
||||
|
||||
const Node& node() const {
|
||||
return _node;
|
||||
}
|
||||
|
||||
private:
|
||||
std::pair<Slot, Node> _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() = 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() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_SHARDS_H
|
319
ext/redis-plus-plus-1.1.1/src/sw/redis++/shards_pool.cpp
Normal file
319
ext/redis-plus-plus-1.1.1/src/sw/redis++/shards_pool.cpp
Normal file
|
@ -0,0 +1,319 @@
|
|||
/**************************************************************************
|
||||
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.
|
||||
*************************************************************************/
|
||||
|
||||
#include "shards_pool.h"
|
||||
#include <unordered_set>
|
||||
#include "errors.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
const std::size_t ShardsPool::SHARDS;
|
||||
|
||||
ShardsPool::ShardsPool(const ConnectionPoolOptions &pool_opts,
|
||||
const ConnectionOptions &connection_opts) :
|
||||
_pool_opts(pool_opts),
|
||||
_connection_opts(connection_opts) {
|
||||
if (_connection_opts.type != ConnectionType::TCP) {
|
||||
throw Error("Only support TCP connection for Redis Cluster");
|
||||
}
|
||||
|
||||
Connection connection(_connection_opts);
|
||||
|
||||
_shards = _cluster_slots(connection);
|
||||
|
||||
_init_pool(_shards);
|
||||
}
|
||||
|
||||
ShardsPool::ShardsPool(ShardsPool &&that) {
|
||||
std::lock_guard<std::mutex> lock(that._mutex);
|
||||
|
||||
_move(std::move(that));
|
||||
}
|
||||
|
||||
ShardsPool& ShardsPool::operator=(ShardsPool &&that) {
|
||||
if (this != &that) {
|
||||
std::lock(_mutex, that._mutex);
|
||||
std::lock_guard<std::mutex> lock_this(_mutex, std::adopt_lock);
|
||||
std::lock_guard<std::mutex> lock_that(that._mutex, std::adopt_lock);
|
||||
|
||||
_move(std::move(that));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
GuardedConnection ShardsPool::fetch(const StringView &key) {
|
||||
auto slot = _slot(key);
|
||||
|
||||
return _fetch(slot);
|
||||
}
|
||||
|
||||
GuardedConnection ShardsPool::fetch() {
|
||||
auto slot = _slot();
|
||||
|
||||
return _fetch(slot);
|
||||
}
|
||||
|
||||
GuardedConnection ShardsPool::fetch(const Node &node) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
auto iter = _pools.find(node);
|
||||
if (iter == _pools.end()) {
|
||||
// Node doesn't exist, and it should be a newly created node.
|
||||
// So add a new connection pool.
|
||||
iter = _add_node(node);
|
||||
}
|
||||
|
||||
assert(iter != _pools.end());
|
||||
|
||||
return GuardedConnection(iter->second);
|
||||
}
|
||||
|
||||
void ShardsPool::update() {
|
||||
// My might send command to a removed node.
|
||||
// Try at most 3 times.
|
||||
for (auto idx = 0; idx < 3; ++idx) {
|
||||
try {
|
||||
// Randomly pick a connection.
|
||||
auto guarded_connection = fetch();
|
||||
auto shards = _cluster_slots(guarded_connection.connection());
|
||||
|
||||
std::unordered_set<Node, NodeHash> nodes;
|
||||
for (const auto &shard : shards) {
|
||||
nodes.insert(shard.second);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
// TODO: If shards is unchanged, no need to update, and return immediately.
|
||||
|
||||
_shards = std::move(shards);
|
||||
|
||||
// Remove non-existent nodes.
|
||||
for (auto iter = _pools.begin(); iter != _pools.end(); ) {
|
||||
if (nodes.find(iter->first) == nodes.end()) {
|
||||
// Node has been removed.
|
||||
_pools.erase(iter++);
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
// Add connection pool for new nodes.
|
||||
// In fact, connections will be created lazily.
|
||||
for (const auto &node : nodes) {
|
||||
if (_pools.find(node) == _pools.end()) {
|
||||
_add_node(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Update successfully.
|
||||
return;
|
||||
} catch (const Error &) {
|
||||
// continue;
|
||||
}
|
||||
}
|
||||
|
||||
throw Error("Failed to update shards info");
|
||||
}
|
||||
|
||||
ConnectionOptions ShardsPool::connection_options(const StringView &key) {
|
||||
auto slot = _slot(key);
|
||||
|
||||
return _connection_options(slot);
|
||||
}
|
||||
|
||||
ConnectionOptions ShardsPool::connection_options() {
|
||||
auto slot = _slot();
|
||||
|
||||
return _connection_options(slot);
|
||||
}
|
||||
void ShardsPool::_move(ShardsPool &&that) {
|
||||
_pool_opts = that._pool_opts;
|
||||
_connection_opts = that._connection_opts;
|
||||
_shards = std::move(that._shards);
|
||||
_pools = std::move(that._pools);
|
||||
}
|
||||
|
||||
void ShardsPool::_init_pool(const Shards &shards) {
|
||||
for (const auto &shard : shards) {
|
||||
_add_node(shard.second);
|
||||
}
|
||||
}
|
||||
|
||||
Shards ShardsPool::_cluster_slots(Connection &connection) const {
|
||||
auto reply = _cluster_slots_command(connection);
|
||||
|
||||
assert(reply);
|
||||
|
||||
return _parse_reply(*reply);
|
||||
}
|
||||
|
||||
ReplyUPtr ShardsPool::_cluster_slots_command(Connection &connection) const {
|
||||
connection.send("CLUSTER SLOTS");
|
||||
|
||||
return connection.recv();
|
||||
}
|
||||
|
||||
Shards ShardsPool::_parse_reply(redisReply &reply) const {
|
||||
if (!reply::is_array(reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
if (reply.element == nullptr || reply.elements == 0) {
|
||||
throw Error("Empty slots");
|
||||
}
|
||||
|
||||
Shards shards;
|
||||
for (std::size_t idx = 0; idx != reply.elements; ++idx) {
|
||||
auto *sub_reply = reply.element[idx];
|
||||
if (sub_reply == nullptr) {
|
||||
throw ProtoError("Null slot info");
|
||||
}
|
||||
|
||||
shards.emplace(_parse_slot_info(*sub_reply));
|
||||
}
|
||||
|
||||
return shards;
|
||||
}
|
||||
|
||||
std::pair<SlotRange, Node> ShardsPool::_parse_slot_info(redisReply &reply) const {
|
||||
if (reply.elements < 3 || reply.element == nullptr) {
|
||||
throw ProtoError("Invalid slot info");
|
||||
}
|
||||
|
||||
// Min slot id
|
||||
auto *min_slot_reply = reply.element[0];
|
||||
if (min_slot_reply == nullptr) {
|
||||
throw ProtoError("Invalid min slot");
|
||||
}
|
||||
std::size_t min_slot = reply::parse<long long>(*min_slot_reply);
|
||||
|
||||
// Max slot id
|
||||
auto *max_slot_reply = reply.element[1];
|
||||
if (max_slot_reply == nullptr) {
|
||||
throw ProtoError("Invalid max slot");
|
||||
}
|
||||
std::size_t max_slot = reply::parse<long long>(*max_slot_reply);
|
||||
|
||||
if (min_slot > max_slot) {
|
||||
throw ProtoError("Invalid slot range");
|
||||
}
|
||||
|
||||
// Master node info
|
||||
auto *node_reply = reply.element[2];
|
||||
if (node_reply == nullptr
|
||||
|| !reply::is_array(*node_reply)
|
||||
|| node_reply->element == nullptr
|
||||
|| node_reply->elements < 2) {
|
||||
throw ProtoError("Invalid node info");
|
||||
}
|
||||
|
||||
auto master_host = reply::parse<std::string>(*(node_reply->element[0]));
|
||||
int master_port = reply::parse<long long>(*(node_reply->element[1]));
|
||||
|
||||
// By now, we ignore node id and other replicas' info.
|
||||
|
||||
return {SlotRange{min_slot, max_slot}, Node{master_host, master_port}};
|
||||
}
|
||||
|
||||
Slot ShardsPool::_slot(const StringView &key) const {
|
||||
// The following code is copied from: https://redis.io/topics/cluster-spec
|
||||
// And I did some minor changes.
|
||||
|
||||
const auto *k = key.data();
|
||||
auto keylen = key.size();
|
||||
|
||||
// start-end indexes of { and }.
|
||||
std::size_t s = 0;
|
||||
std::size_t e = 0;
|
||||
|
||||
// Search the first occurrence of '{'.
|
||||
for (s = 0; s < keylen; s++)
|
||||
if (k[s] == '{') break;
|
||||
|
||||
// No '{' ? Hash the whole key. This is the base case.
|
||||
if (s == keylen) return crc16(k, keylen) & SHARDS;
|
||||
|
||||
// '{' found? Check if we have the corresponding '}'.
|
||||
for (e = s + 1; e < keylen; e++)
|
||||
if (k[e] == '}') break;
|
||||
|
||||
// No '}' or nothing between {} ? Hash the whole key.
|
||||
if (e == keylen || e == s + 1) return crc16(k, keylen) & SHARDS;
|
||||
|
||||
// If we are here there is both a { and a } on its right. Hash
|
||||
// what is in the middle between { and }.
|
||||
return crc16(k + s + 1, e - s - 1) & SHARDS;
|
||||
}
|
||||
|
||||
Slot ShardsPool::_slot() const {
|
||||
static thread_local std::default_random_engine engine;
|
||||
|
||||
std::uniform_int_distribution<std::size_t> uniform_dist(0, SHARDS);
|
||||
|
||||
return uniform_dist(engine);
|
||||
}
|
||||
|
||||
ConnectionPoolSPtr& ShardsPool::_get_pool(Slot slot) {
|
||||
auto shards_iter = _shards.lower_bound(SlotRange{slot, slot});
|
||||
if (shards_iter == _shards.end() || slot < shards_iter->first.min) {
|
||||
throw Error("Slot is out of range: " + std::to_string(slot));
|
||||
}
|
||||
|
||||
const auto &node = shards_iter->second;
|
||||
|
||||
auto node_iter = _pools.find(node);
|
||||
if (node_iter == _pools.end()) {
|
||||
throw Error("Slot is NOT covered: " + std::to_string(slot));
|
||||
}
|
||||
|
||||
return node_iter->second;
|
||||
}
|
||||
|
||||
GuardedConnection ShardsPool::_fetch(Slot slot) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
auto &pool = _get_pool(slot);
|
||||
|
||||
assert(pool);
|
||||
|
||||
return GuardedConnection(pool);
|
||||
}
|
||||
|
||||
ConnectionOptions ShardsPool::_connection_options(Slot slot) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
auto &pool = _get_pool(slot);
|
||||
|
||||
assert(pool);
|
||||
|
||||
return pool->connection_options();
|
||||
}
|
||||
|
||||
auto ShardsPool::_add_node(const Node &node) -> NodeMap::iterator {
|
||||
auto opts = _connection_opts;
|
||||
opts.host = node.host;
|
||||
opts.port = node.port;
|
||||
|
||||
return _pools.emplace(node, std::make_shared<ConnectionPool>(_pool_opts, opts)).first;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
137
ext/redis-plus-plus-1.1.1/src/sw/redis++/shards_pool.h
Normal file
137
ext/redis-plus-plus-1.1.1/src/sw/redis++/shards_pool.h
Normal file
|
@ -0,0 +1,137 @@
|
|||
/**************************************************************************
|
||||
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 <cassert>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <random>
|
||||
#include <memory>
|
||||
#include "reply.h"
|
||||
#include "connection_pool.h"
|
||||
#include "shards.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
using ConnectionPoolSPtr = std::shared_ptr<ConnectionPool>;
|
||||
|
||||
class GuardedConnection {
|
||||
public:
|
||||
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() {
|
||||
_pool->release(std::move(_connection));
|
||||
}
|
||||
|
||||
Connection& connection() {
|
||||
return _connection;
|
||||
}
|
||||
|
||||
private:
|
||||
ConnectionPoolSPtr _pool;
|
||||
Connection _connection;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
// Fetch a connection by key.
|
||||
GuardedConnection fetch(const StringView &key);
|
||||
|
||||
// Randomly pick a connection.
|
||||
GuardedConnection fetch();
|
||||
|
||||
// Fetch a connection by node.
|
||||
GuardedConnection fetch(const Node &node);
|
||||
|
||||
void update();
|
||||
|
||||
ConnectionOptions connection_options(const StringView &key);
|
||||
|
||||
ConnectionOptions connection_options();
|
||||
|
||||
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;
|
||||
|
||||
std::pair<SlotRange, Node> _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;
|
||||
|
||||
ConnectionPoolSPtr& _get_pool(Slot slot);
|
||||
|
||||
GuardedConnection _fetch(Slot slot);
|
||||
|
||||
ConnectionOptions _connection_options(Slot slot);
|
||||
|
||||
using NodeMap = std::unordered_map<Node, ConnectionPoolSPtr, NodeHash>;
|
||||
|
||||
NodeMap::iterator _add_node(const Node &node);
|
||||
|
||||
ConnectionPoolOptions _pool_opts;
|
||||
|
||||
ConnectionOptions _connection_opts;
|
||||
|
||||
Shards _shards;
|
||||
|
||||
NodeMap _pools;
|
||||
|
||||
std::mutex _mutex;
|
||||
|
||||
static const std::size_t SHARDS = 16383;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H
|
222
ext/redis-plus-plus-1.1.1/src/sw/redis++/subscriber.cpp
Normal file
222
ext/redis-plus-plus-1.1.1/src/sw/redis++/subscriber.cpp
Normal file
|
@ -0,0 +1,222 @@
|
|||
/**************************************************************************
|
||||
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.
|
||||
*************************************************************************/
|
||||
|
||||
#include "subscriber.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
const Subscriber::TypeIndex Subscriber::_msg_type_index = {
|
||||
{"message", MsgType::MESSAGE},
|
||||
{"pmessage", MsgType::PMESSAGE},
|
||||
{"subscribe", MsgType::SUBSCRIBE},
|
||||
{"unsubscribe", MsgType::UNSUBSCRIBE},
|
||||
{"psubscribe", MsgType::PSUBSCRIBE},
|
||||
{"punsubscribe", MsgType::PUNSUBSCRIBE}
|
||||
};
|
||||
|
||||
Subscriber::Subscriber(Connection connection) : _connection(std::move(connection)) {}
|
||||
|
||||
void Subscriber::subscribe(const StringView &channel) {
|
||||
_check_connection();
|
||||
|
||||
// TODO: cmd::subscribe DOES NOT send the subscribe message to Redis.
|
||||
// In fact, it puts the command to network buffer.
|
||||
// So we need a queue to record these sub or unsub commands, and
|
||||
// ensure that before stopping the subscriber, all these commands
|
||||
// have really been sent to Redis.
|
||||
cmd::subscribe(_connection, channel);
|
||||
}
|
||||
|
||||
void Subscriber::unsubscribe() {
|
||||
_check_connection();
|
||||
|
||||
cmd::unsubscribe(_connection);
|
||||
}
|
||||
|
||||
void Subscriber::unsubscribe(const StringView &channel) {
|
||||
_check_connection();
|
||||
|
||||
cmd::unsubscribe(_connection, channel);
|
||||
}
|
||||
|
||||
void Subscriber::psubscribe(const StringView &pattern) {
|
||||
_check_connection();
|
||||
|
||||
cmd::psubscribe(_connection, pattern);
|
||||
}
|
||||
|
||||
void Subscriber::punsubscribe() {
|
||||
_check_connection();
|
||||
|
||||
cmd::punsubscribe(_connection);
|
||||
}
|
||||
|
||||
void Subscriber::punsubscribe(const StringView &pattern) {
|
||||
_check_connection();
|
||||
|
||||
cmd::punsubscribe(_connection, pattern);
|
||||
}
|
||||
|
||||
void Subscriber::consume() {
|
||||
_check_connection();
|
||||
|
||||
ReplyUPtr reply;
|
||||
try {
|
||||
reply = _connection.recv();
|
||||
} catch (const TimeoutError &) {
|
||||
_connection.reset();
|
||||
throw;
|
||||
}
|
||||
|
||||
assert(reply);
|
||||
|
||||
if (!reply::is_array(*reply) || reply->elements < 1 || reply->element == nullptr) {
|
||||
throw ProtoError("Invalid subscribe message");
|
||||
}
|
||||
|
||||
auto type = _msg_type(reply->element[0]);
|
||||
switch (type) {
|
||||
case MsgType::MESSAGE:
|
||||
_handle_message(*reply);
|
||||
break;
|
||||
|
||||
case MsgType::PMESSAGE:
|
||||
_handle_pmessage(*reply);
|
||||
break;
|
||||
|
||||
case MsgType::SUBSCRIBE:
|
||||
case MsgType::UNSUBSCRIBE:
|
||||
case MsgType::PSUBSCRIBE:
|
||||
case MsgType::PUNSUBSCRIBE:
|
||||
_handle_meta(type, *reply);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
Subscriber::MsgType Subscriber::_msg_type(redisReply *reply) const {
|
||||
if (reply == nullptr) {
|
||||
throw ProtoError("Null type reply.");
|
||||
}
|
||||
|
||||
auto type = reply::parse<std::string>(*reply);
|
||||
|
||||
auto iter = _msg_type_index.find(type);
|
||||
if (iter == _msg_type_index.end()) {
|
||||
throw ProtoError("Invalid message type.");
|
||||
}
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
void Subscriber::_check_connection() {
|
||||
if (_connection.broken()) {
|
||||
throw Error("Connection is broken");
|
||||
}
|
||||
}
|
||||
|
||||
void Subscriber::_handle_message(redisReply &reply) {
|
||||
if (_msg_callback == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply.elements != 3) {
|
||||
throw ProtoError("Expect 3 sub replies");
|
||||
}
|
||||
|
||||
assert(reply.element != nullptr);
|
||||
|
||||
auto *channel_reply = reply.element[1];
|
||||
if (channel_reply == nullptr) {
|
||||
throw ProtoError("Null channel reply");
|
||||
}
|
||||
auto channel = reply::parse<std::string>(*channel_reply);
|
||||
|
||||
auto *msg_reply = reply.element[2];
|
||||
if (msg_reply == nullptr) {
|
||||
throw ProtoError("Null message reply");
|
||||
}
|
||||
auto msg = reply::parse<std::string>(*msg_reply);
|
||||
|
||||
_msg_callback(std::move(channel), std::move(msg));
|
||||
}
|
||||
|
||||
void Subscriber::_handle_pmessage(redisReply &reply) {
|
||||
if (_pmsg_callback == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply.elements != 4) {
|
||||
throw ProtoError("Expect 4 sub replies");
|
||||
}
|
||||
|
||||
assert(reply.element != nullptr);
|
||||
|
||||
auto *pattern_reply = reply.element[1];
|
||||
if (pattern_reply == nullptr) {
|
||||
throw ProtoError("Null pattern reply");
|
||||
}
|
||||
auto pattern = reply::parse<std::string>(*pattern_reply);
|
||||
|
||||
auto *channel_reply = reply.element[2];
|
||||
if (channel_reply == nullptr) {
|
||||
throw ProtoError("Null channel reply");
|
||||
}
|
||||
auto channel = reply::parse<std::string>(*channel_reply);
|
||||
|
||||
auto *msg_reply = reply.element[3];
|
||||
if (msg_reply == nullptr) {
|
||||
throw ProtoError("Null message reply");
|
||||
}
|
||||
auto msg = reply::parse<std::string>(*msg_reply);
|
||||
|
||||
_pmsg_callback(std::move(pattern), std::move(channel), std::move(msg));
|
||||
}
|
||||
|
||||
void Subscriber::_handle_meta(MsgType type, redisReply &reply) {
|
||||
if (_meta_callback == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply.elements != 3) {
|
||||
throw ProtoError("Expect 3 sub replies");
|
||||
}
|
||||
|
||||
assert(reply.element != nullptr);
|
||||
|
||||
auto *channel_reply = reply.element[1];
|
||||
if (channel_reply == nullptr) {
|
||||
throw ProtoError("Null channel reply");
|
||||
}
|
||||
auto channel = reply::parse<OptionalString>(*channel_reply);
|
||||
|
||||
auto *num_reply = reply.element[2];
|
||||
if (num_reply == nullptr) {
|
||||
throw ProtoError("Null num reply");
|
||||
}
|
||||
auto num = reply::parse<long long>(*num_reply);
|
||||
|
||||
_meta_callback(type, std::move(channel), num);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
231
ext/redis-plus-plus-1.1.1/src/sw/redis++/subscriber.h
Normal file
231
ext/redis-plus-plus-1.1.1/src/sw/redis++/subscriber.h
Normal file
|
@ -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 <unordered_map>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#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 <typename MsgCb>
|
||||
void on_message(MsgCb msg_callback);
|
||||
|
||||
template <typename PMsgCb>
|
||||
void on_pmessage(PMsgCb pmsg_callback);
|
||||
|
||||
template <typename MetaCb>
|
||||
void on_meta(MetaCb meta_callback);
|
||||
|
||||
void subscribe(const StringView &channel);
|
||||
|
||||
template <typename Input>
|
||||
void subscribe(Input first, Input last);
|
||||
|
||||
template <typename T>
|
||||
void subscribe(std::initializer_list<T> channels) {
|
||||
subscribe(channels.begin(), channels.end());
|
||||
}
|
||||
|
||||
void unsubscribe();
|
||||
|
||||
void unsubscribe(const StringView &channel);
|
||||
|
||||
template <typename Input>
|
||||
void unsubscribe(Input first, Input last);
|
||||
|
||||
template <typename T>
|
||||
void unsubscribe(std::initializer_list<T> channels) {
|
||||
unsubscribe(channels.begin(), channels.end());
|
||||
}
|
||||
|
||||
void psubscribe(const StringView &pattern);
|
||||
|
||||
template <typename Input>
|
||||
void psubscribe(Input first, Input last);
|
||||
|
||||
template <typename T>
|
||||
void psubscribe(std::initializer_list<T> channels) {
|
||||
psubscribe(channels.begin(), channels.end());
|
||||
}
|
||||
|
||||
void punsubscribe();
|
||||
|
||||
void punsubscribe(const StringView &channel);
|
||||
|
||||
template <typename Input>
|
||||
void punsubscribe(Input first, Input last);
|
||||
|
||||
template <typename T>
|
||||
void punsubscribe(std::initializer_list<T> 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<void (std::string channel, std::string msg)>;
|
||||
|
||||
using PatternMsgCallback = std::function<void (std::string pattern,
|
||||
std::string channel,
|
||||
std::string msg)>;
|
||||
|
||||
using MetaCallback = std::function<void (MsgType type,
|
||||
OptionalString channel,
|
||||
long long num)>;
|
||||
|
||||
using TypeIndex = std::unordered_map<std::string, MsgType>;
|
||||
static const TypeIndex _msg_type_index;
|
||||
|
||||
Connection _connection;
|
||||
|
||||
MsgCallback _msg_callback = nullptr;
|
||||
|
||||
PatternMsgCallback _pmsg_callback = nullptr;
|
||||
|
||||
MetaCallback _meta_callback = nullptr;
|
||||
};
|
||||
|
||||
template <typename MsgCb>
|
||||
void Subscriber::on_message(MsgCb msg_callback) {
|
||||
_msg_callback = msg_callback;
|
||||
}
|
||||
|
||||
template <typename PMsgCb>
|
||||
void Subscriber::on_pmessage(PMsgCb pmsg_callback) {
|
||||
_pmsg_callback = pmsg_callback;
|
||||
}
|
||||
|
||||
template <typename MetaCb>
|
||||
void Subscriber::on_meta(MetaCb meta_callback) {
|
||||
_meta_callback = meta_callback;
|
||||
}
|
||||
|
||||
template <typename Input>
|
||||
void Subscriber::subscribe(Input first, Input last) {
|
||||
if (first == last) {
|
||||
return;
|
||||
}
|
||||
|
||||
_check_connection();
|
||||
|
||||
cmd::subscribe_range(_connection, first, last);
|
||||
}
|
||||
|
||||
template <typename Input>
|
||||
void Subscriber::unsubscribe(Input first, Input last) {
|
||||
_check_connection();
|
||||
|
||||
cmd::unsubscribe_range(_connection, first, last);
|
||||
}
|
||||
|
||||
template <typename Input>
|
||||
void Subscriber::psubscribe(Input first, Input last) {
|
||||
if (first == last) {
|
||||
return;
|
||||
}
|
||||
|
||||
_check_connection();
|
||||
|
||||
cmd::psubscribe_range(_connection, first, last);
|
||||
}
|
||||
|
||||
template <typename Input>
|
||||
void Subscriber::punsubscribe(Input first, Input last) {
|
||||
_check_connection();
|
||||
|
||||
cmd::punsubscribe_range(_connection, first, last);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H
|
123
ext/redis-plus-plus-1.1.1/src/sw/redis++/transaction.cpp
Normal file
123
ext/redis-plus-plus-1.1.1/src/sw/redis++/transaction.cpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
/**************************************************************************
|
||||
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.
|
||||
*************************************************************************/
|
||||
|
||||
#include "transaction.h"
|
||||
#include "command.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
std::vector<ReplyUPtr> TransactionImpl::exec(Connection &connection, std::size_t cmd_num) {
|
||||
_close_transaction();
|
||||
|
||||
_get_queued_replies(connection, cmd_num);
|
||||
|
||||
return _exec(connection);
|
||||
}
|
||||
|
||||
void TransactionImpl::discard(Connection &connection, std::size_t cmd_num) {
|
||||
_close_transaction();
|
||||
|
||||
_get_queued_replies(connection, cmd_num);
|
||||
|
||||
_discard(connection);
|
||||
}
|
||||
|
||||
void TransactionImpl::_open_transaction(Connection &connection) {
|
||||
assert(!_in_transaction);
|
||||
|
||||
cmd::multi(connection);
|
||||
auto reply = connection.recv();
|
||||
auto status = reply::to_status(*reply);
|
||||
if (status != "OK") {
|
||||
throw Error("Failed to open transaction: " + status);
|
||||
}
|
||||
|
||||
_in_transaction = true;
|
||||
}
|
||||
|
||||
void TransactionImpl::_close_transaction() {
|
||||
if (!_in_transaction) {
|
||||
throw Error("No command in transaction");
|
||||
}
|
||||
|
||||
_in_transaction = false;
|
||||
}
|
||||
|
||||
void TransactionImpl::_get_queued_reply(Connection &connection) {
|
||||
auto reply = connection.recv();
|
||||
auto status = reply::to_status(*reply);
|
||||
if (status != "QUEUED") {
|
||||
throw Error("Invalid QUEUED reply: " + status);
|
||||
}
|
||||
}
|
||||
|
||||
void TransactionImpl::_get_queued_replies(Connection &connection, std::size_t cmd_num) {
|
||||
if (_piped) {
|
||||
// Get all QUEUED reply
|
||||
while (cmd_num > 0) {
|
||||
_get_queued_reply(connection);
|
||||
|
||||
--cmd_num;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ReplyUPtr> TransactionImpl::_exec(Connection &connection) {
|
||||
cmd::exec(connection);
|
||||
|
||||
auto reply = connection.recv();
|
||||
|
||||
if (reply::is_nil(*reply)) {
|
||||
// Execution has been aborted, i.e. watched key has been modified.
|
||||
throw WatchError();
|
||||
}
|
||||
|
||||
if (!reply::is_array(*reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
if (reply->element == nullptr || reply->elements == 0) {
|
||||
// Since we don't allow EXEC without any command, this ARRAY reply
|
||||
// should NOT be null or empty.
|
||||
throw ProtoError("Null ARRAY reply");
|
||||
}
|
||||
|
||||
std::vector<ReplyUPtr> replies;
|
||||
for (std::size_t idx = 0; idx != reply->elements; ++idx) {
|
||||
auto *sub_reply = reply->element[idx];
|
||||
if (sub_reply == nullptr) {
|
||||
throw ProtoError("Null sub reply");
|
||||
}
|
||||
|
||||
auto r = ReplyUPtr(sub_reply);
|
||||
reply->element[idx] = nullptr;
|
||||
replies.push_back(std::move(r));
|
||||
}
|
||||
|
||||
return replies;
|
||||
}
|
||||
|
||||
void TransactionImpl::_discard(Connection &connection) {
|
||||
cmd::discard(connection);
|
||||
auto reply = connection.recv();
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
77
ext/redis-plus-plus-1.1.1/src/sw/redis++/transaction.h
Normal file
77
ext/redis-plus-plus-1.1.1/src/sw/redis++/transaction.h
Normal file
|
@ -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 <cassert>
|
||||
#include <vector>
|
||||
#include "connection.h"
|
||||
#include "errors.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
class TransactionImpl {
|
||||
public:
|
||||
explicit TransactionImpl(bool piped) : _piped(piped) {}
|
||||
|
||||
template <typename Cmd, typename ...Args>
|
||||
void command(Connection &connection, Cmd cmd, Args &&...args);
|
||||
|
||||
std::vector<ReplyUPtr> 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<ReplyUPtr> _exec(Connection &connection);
|
||||
|
||||
void _discard(Connection &connection);
|
||||
|
||||
bool _in_transaction = false;
|
||||
|
||||
bool _piped;
|
||||
};
|
||||
|
||||
template <typename Cmd, typename ...Args>
|
||||
void TransactionImpl::command(Connection &connection, Cmd cmd, Args &&...args) {
|
||||
assert(!connection.broken());
|
||||
|
||||
if (!_in_transaction) {
|
||||
_open_transaction(connection);
|
||||
}
|
||||
|
||||
cmd(connection, std::forward<Args>(args)...);
|
||||
|
||||
if (!_piped) {
|
||||
_get_queued_reply(connection);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TRANSACTION_H
|
269
ext/redis-plus-plus-1.1.1/src/sw/redis++/utils.h
Normal file
269
ext/redis-plus-plus-1.1.1/src/sw/redis++/utils.h
Normal file
|
@ -0,0 +1,269 @@
|
|||
/**************************************************************************
|
||||
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 <cstring>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
// By now, not all compilers support std::string_view,
|
||||
// so we make our own implementation.
|
||||
class StringView {
|
||||
public:
|
||||
constexpr StringView() noexcept = default;
|
||||
|
||||
constexpr StringView(const char *data, std::size_t size) : _data(data), _size(size) {}
|
||||
|
||||
StringView(const char *data) : _data(data), _size(std::strlen(data)) {}
|
||||
|
||||
StringView(const std::string &str) : _data(str.data()), _size(str.size()) {}
|
||||
|
||||
constexpr StringView(const StringView &) noexcept = default;
|
||||
|
||||
StringView& operator=(const StringView &) noexcept = default;
|
||||
|
||||
constexpr const char* data() const noexcept {
|
||||
return _data;
|
||||
}
|
||||
|
||||
constexpr std::size_t size() const noexcept {
|
||||
return _size;
|
||||
}
|
||||
|
||||
private:
|
||||
const char *_data = nullptr;
|
||||
std::size_t _size = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Optional {
|
||||
public:
|
||||
Optional() = default;
|
||||
|
||||
Optional(const Optional &) = default;
|
||||
Optional& operator=(const Optional &) = default;
|
||||
|
||||
Optional(Optional &&) = default;
|
||||
Optional& operator=(Optional &&) = default;
|
||||
|
||||
~Optional() = default;
|
||||
|
||||
template <typename ...Args>
|
||||
explicit Optional(Args &&...args) : _value(true, T(std::forward<Args>(args)...)) {}
|
||||
|
||||
explicit operator bool() const {
|
||||
return _value.first;
|
||||
}
|
||||
|
||||
T& value() {
|
||||
return _value.second;
|
||||
}
|
||||
|
||||
const T& value() const {
|
||||
return _value.second;
|
||||
}
|
||||
|
||||
T* operator->() {
|
||||
return &(_value.second);
|
||||
}
|
||||
|
||||
const T* operator->() const {
|
||||
return &(_value.second);
|
||||
}
|
||||
|
||||
T& operator*() {
|
||||
return _value.second;
|
||||
}
|
||||
|
||||
const T& operator*() const {
|
||||
return _value.second;
|
||||
}
|
||||
|
||||
private:
|
||||
std::pair<bool, T> _value;
|
||||
};
|
||||
|
||||
using OptionalString = Optional<std::string>;
|
||||
|
||||
using OptionalLongLong = Optional<long long>;
|
||||
|
||||
using OptionalDouble = Optional<double>;
|
||||
|
||||
using OptionalStringPair = Optional<std::pair<std::string, std::string>>;
|
||||
|
||||
template <typename ...>
|
||||
struct IsKvPair : std::false_type {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct IsKvPair<std::pair<T, U>> : std::true_type {};
|
||||
|
||||
template <typename ...>
|
||||
using Void = void;
|
||||
|
||||
template <typename T, typename U = Void<>>
|
||||
struct IsInserter : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
//struct IsInserter<T, Void<typename T::container_type>> : std::true_type {};
|
||||
struct IsInserter<T,
|
||||
typename std::enable_if<!std::is_void<typename T::container_type>::value>::type>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename Iter, typename T = Void<>>
|
||||
struct IterType {
|
||||
using type = typename std::iterator_traits<Iter>::value_type;
|
||||
};
|
||||
|
||||
template <typename Iter>
|
||||
//struct IterType<Iter, Void<typename Iter::container_type>> {
|
||||
struct IterType<Iter,
|
||||
//typename std::enable_if<std::is_void<typename Iter::value_type>::value>::type> {
|
||||
typename std::enable_if<IsInserter<Iter>::value>::type> {
|
||||
using type = typename std::decay<typename Iter::container_type::value_type>::type;
|
||||
};
|
||||
|
||||
template <typename Iter, typename T = Void<>>
|
||||
struct IsIter : std::false_type {};
|
||||
|
||||
template <typename Iter>
|
||||
struct IsIter<Iter, typename std::enable_if<IsInserter<Iter>::value>::type> : std::true_type {};
|
||||
|
||||
template <typename Iter>
|
||||
//struct IsIter<Iter, Void<typename std::iterator_traits<Iter>::iterator_category>>
|
||||
struct IsIter<Iter,
|
||||
typename std::enable_if<!std::is_void<
|
||||
typename std::iterator_traits<Iter>::value_type>::value>::type>
|
||||
: std::integral_constant<bool, !std::is_convertible<Iter, StringView>::value> {};
|
||||
|
||||
template <typename T>
|
||||
struct IsKvPairIter : IsKvPair<typename IterType<T>::type> {};
|
||||
|
||||
template <typename T, typename Tuple>
|
||||
struct TupleWithType : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct TupleWithType<T, std::tuple<>> : std::false_type {};
|
||||
|
||||
template <typename T, typename U, typename ...Args>
|
||||
struct TupleWithType<T, std::tuple<U, Args...>> : TupleWithType<T, std::tuple<Args...>> {};
|
||||
|
||||
template <typename T, typename ...Args>
|
||||
struct TupleWithType<T, std::tuple<T, Args...>> : std::true_type {};
|
||||
|
||||
template <std::size_t ...Is>
|
||||
struct IndexSequence {};
|
||||
|
||||
template <std::size_t I, std::size_t ...Is>
|
||||
struct MakeIndexSequence : MakeIndexSequence<I - 1, I - 1, Is...> {};
|
||||
|
||||
template <std::size_t ...Is>
|
||||
struct MakeIndexSequence<0, Is...> : IndexSequence<Is...> {};
|
||||
|
||||
// NthType and NthValue are taken from
|
||||
// https://stackoverflow.com/questions/14261183
|
||||
template <std::size_t I, typename ...Args>
|
||||
struct NthType {};
|
||||
|
||||
template <typename Arg, typename ...Args>
|
||||
struct NthType<0, Arg, Args...> {
|
||||
using type = Arg;
|
||||
};
|
||||
|
||||
template <std::size_t I, typename Arg, typename ...Args>
|
||||
struct NthType<I, Arg, Args...> {
|
||||
using type = typename NthType<I - 1, Args...>::type;
|
||||
};
|
||||
|
||||
template <typename ...Args>
|
||||
struct LastType {
|
||||
using type = typename NthType<sizeof...(Args) - 1, Args...>::type;
|
||||
};
|
||||
|
||||
struct InvalidLastType {};
|
||||
|
||||
template <>
|
||||
struct LastType<> {
|
||||
using type = InvalidLastType;
|
||||
};
|
||||
|
||||
template <std::size_t I, typename Arg, typename ...Args>
|
||||
auto NthValue(Arg &&arg, Args &&...)
|
||||
-> typename std::enable_if<(I == 0), decltype(std::forward<Arg>(arg))>::type {
|
||||
return std::forward<Arg>(arg);
|
||||
}
|
||||
|
||||
template <std::size_t I, typename Arg, typename ...Args>
|
||||
auto NthValue(Arg &&, Args &&...args)
|
||||
-> typename std::enable_if<(I > 0),
|
||||
decltype(std::forward<typename NthType<I, Arg, Args...>::type>(
|
||||
std::declval<typename NthType<I, Arg, Args...>::type>()))>::type {
|
||||
return std::forward<typename NthType<I, Arg, Args...>::type>(
|
||||
NthValue<I - 1>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
auto LastValue(Args &&...args)
|
||||
-> decltype(std::forward<typename LastType<Args...>::type>(
|
||||
std::declval<typename LastType<Args...>::type>())) {
|
||||
return std::forward<typename LastType<Args...>::type>(
|
||||
NthValue<sizeof...(Args) - 1>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename T, typename = Void<>>
|
||||
struct HasPushBack : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct HasPushBack<T,
|
||||
typename std::enable_if<
|
||||
std::is_void<decltype(
|
||||
std::declval<T>().push_back(std::declval<typename T::value_type>())
|
||||
)>::value>::type> : std::true_type {};
|
||||
|
||||
template <typename T, typename = Void<>>
|
||||
struct HasInsert : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct HasInsert<T,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
decltype(std::declval<T>().insert(std::declval<typename T::const_iterator>(),
|
||||
std::declval<typename T::value_type>())),
|
||||
typename T::iterator>::value>::type> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
struct IsSequenceContainer
|
||||
: std::integral_constant<bool,
|
||||
HasPushBack<T>::value
|
||||
&& !std::is_same<typename std::decay<T>::type, std::string>::value> {};
|
||||
|
||||
template <typename T>
|
||||
struct IsAssociativeContainer
|
||||
: std::integral_constant<bool,
|
||||
HasInsert<T>::value && !HasPushBack<T>::value> {};
|
||||
|
||||
uint16_t crc16(const char *buf, int len);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_UTILS_H
|
Loading…
Add table
Add a link
Reference in a new issue