199 changed files with 51072 additions and 9280 deletions
@ -0,0 +1,15 @@
|
||||
#ifndef ZT_CONTROLLER_REDIS_HPP |
||||
#define ZT_CONTROLLER_REDIS_HPP |
||||
|
||||
#include <string> |
||||
|
||||
namespace ZeroTier { |
||||
struct RedisConfig { |
||||
std::string hostname; |
||||
int port; |
||||
std::string password; |
||||
bool clusterMode; |
||||
}; |
||||
} |
||||
|
||||
#endif |
||||
@ -0,0 +1,45 @@
|
||||
language: c |
||||
sudo: false |
||||
compiler: |
||||
- gcc |
||||
- clang |
||||
|
||||
os: |
||||
- linux |
||||
- osx |
||||
|
||||
branches: |
||||
only: |
||||
- staging |
||||
- trying |
||||
- master |
||||
|
||||
before_script: |
||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew update; brew install redis; fi |
||||
|
||||
addons: |
||||
apt: |
||||
packages: |
||||
- libc6-dbg |
||||
- libc6-dev |
||||
- libc6:i386 |
||||
- libc6-dev-i386 |
||||
- libc6-dbg:i386 |
||||
- gcc-multilib |
||||
- valgrind |
||||
|
||||
env: |
||||
- CFLAGS="-Werror" |
||||
- PRE="valgrind --track-origins=yes --leak-check=full" |
||||
- TARGET="32bit" TARGET_VARS="32bit-vars" CFLAGS="-Werror" |
||||
- TARGET="32bit" TARGET_VARS="32bit-vars" PRE="valgrind --track-origins=yes --leak-check=full" |
||||
|
||||
matrix: |
||||
exclude: |
||||
- os: osx |
||||
env: PRE="valgrind --track-origins=yes --leak-check=full" |
||||
|
||||
- os: osx |
||||
env: TARGET="32bit" TARGET_VARS="32bit-vars" PRE="valgrind --track-origins=yes --leak-check=full" |
||||
|
||||
script: make $TARGET CFLAGS="$CFLAGS" && make check PRE="$PRE" && make $TARGET_VARS hiredis-example |
||||
@ -0,0 +1,190 @@
|
||||
**NOTE: BREAKING CHANGES upgrading from 0.13.x to 0.14.x **: |
||||
|
||||
* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now |
||||
protocol errors. This is consistent with the RESP specification. On 32-bit |
||||
platforms, the upper bound is lowered to `SIZE_MAX`. |
||||
|
||||
* Change `redisReply.len` to `size_t`, as it denotes the the size of a string |
||||
|
||||
User code should compare this to `size_t` values as well. If it was used to |
||||
compare to other values, casting might be necessary or can be removed, if |
||||
casting was applied before. |
||||
|
||||
### 0.14.1 (2020-03-13) |
||||
|
||||
* Adds safe allocation wrappers (CVE-2020-7105, #747, #752) (Michael Grunder) |
||||
|
||||
### 0.14.0 (2018-09-25) |
||||
|
||||
* Make string2ll static to fix conflict with Redis (Tom Lee [c3188b]) |
||||
* Use -dynamiclib instead of -shared for OSX (Ryan Schmidt [a65537]) |
||||
* Use string2ll from Redis w/added tests (Michael Grunder [7bef04, 60f622]) |
||||
* Makefile - OSX compilation fixes (Ryan Schmidt [881fcb, 0e9af8]) |
||||
* Remove redundant NULL checks (Justin Brewer [54acc8, 58e6b8]) |
||||
* Fix bulk and multi-bulk length truncation (Justin Brewer [109197]) |
||||
* Fix SIGSEGV in OpenBSD by checking for NULL before calling freeaddrinfo (Justin Brewer [546d94]) |
||||
* Several POSIX compatibility fixes (Justin Brewer [bbeab8, 49bbaa, d1c1b6]) |
||||
* Makefile - Compatibility fixes (Dimitri Vorobiev [3238cf, 12a9d1]) |
||||
* Makefile - Fix make install on FreeBSD (Zach Shipko [a2ef2b]) |
||||
* Makefile - don't assume $(INSTALL) is cp (Igor Gnatenko [725a96]) |
||||
* Separate side-effect causing function from assert and small cleanup (amallia [b46413, 3c3234]) |
||||
* Don't send negative values to `__redisAsyncCommand` (Frederik Deweerdt [706129]) |
||||
* Fix leak if setsockopt fails (Frederik Deweerdt [e21c9c]) |
||||
* Fix libevent leak (zfz [515228]) |
||||
* Clean up GCC warning (Ichito Nagata [2ec774]) |
||||
* Keep track of errno in `__redisSetErrorFromErrno()` as snprintf may use it (Jin Qing [25cd88]) |
||||
* Solaris compilation fix (Donald Whyte [41b07d]) |
||||
* Reorder linker arguments when building examples (Tustfarm-heart [06eedd]) |
||||
* Keep track of subscriptions in case of rapid subscribe/unsubscribe (Hyungjin Kim [073dc8, be76c5, d46999]) |
||||
* libuv use after free fix (Paul Scott [cbb956]) |
||||
* Properly close socket fd on reconnect attempt (WSL [64d1ec]) |
||||
* Skip valgrind in OSX tests (Jan-Erik Rediger [9deb78]) |
||||
* Various updates for Travis testing OSX (Ted Nyman [fa3774, 16a459, bc0ea5]) |
||||
* Update libevent (Chris Xin [386802]) |
||||
* Change sds.h for building in C++ projects (Ali Volkan ATLI [f5b32e]) |
||||
* Use proper format specifier in redisFormatSdsCommandArgv (Paulino Huerta, Jan-Erik Rediger [360a06, 8655a6]) |
||||
* Better handling of NULL reply in example code (Jan-Erik Rediger [1b8ed3]) |
||||
* Prevent overflow when formatting an error (Jan-Erik Rediger [0335cb]) |
||||
* Compatibility fix for strerror_r (Tom Lee [bb1747]) |
||||
* Properly detect integer parse/overflow errors (Justin Brewer [93421f]) |
||||
* Adds CI for Windows and cygwin fixes (owent, [6c53d6, 6c3e40]) |
||||
* Catch a buffer overflow when formatting the error message |
||||
* Import latest upstream sds. This breaks applications that are linked against the old hiredis v0.13 |
||||
* Fix warnings, when compiled with -Wshadow |
||||
* Make hiredis compile in Cygwin on Windows, now CI-tested |
||||
|
||||
**BREAKING CHANGES**: |
||||
|
||||
* Remove backwards compatibility macro's |
||||
|
||||
This removes the following old function aliases, use the new name now: |
||||
|
||||
| Old | New | |
||||
| --------------------------- | ---------------------- | |
||||
| redisReplyReaderCreate | redisReaderCreate | |
||||
| redisReplyReaderCreate | redisReaderCreate | |
||||
| redisReplyReaderFree | redisReaderFree | |
||||
| redisReplyReaderFeed | redisReaderFeed | |
||||
| redisReplyReaderGetReply | redisReaderGetReply | |
||||
| redisReplyReaderSetPrivdata | redisReaderSetPrivdata | |
||||
| redisReplyReaderGetObject | redisReaderGetObject | |
||||
| redisReplyReaderGetError | redisReaderGetError | |
||||
|
||||
* The `DEBUG` variable in the Makefile was renamed to `DEBUG_FLAGS` |
||||
|
||||
Previously it broke some builds for people that had `DEBUG` set to some arbitrary value, |
||||
due to debugging other software. |
||||
By renaming we avoid unintentional name clashes. |
||||
|
||||
Simply rename `DEBUG` to `DEBUG_FLAGS` in your environment to make it working again. |
||||
|
||||
### 0.13.3 (2015-09-16) |
||||
|
||||
* Revert "Clear `REDIS_CONNECTED` flag when connection is closed". |
||||
* Make tests pass on FreeBSD (Thanks, Giacomo Olgeni) |
||||
|
||||
|
||||
If the `REDIS_CONNECTED` flag is cleared, |
||||
the async onDisconnect callback function will never be called. |
||||
This causes problems as the disconnect is never reported back to the user. |
||||
|
||||
### 0.13.2 (2015-08-25) |
||||
|
||||
* Prevent crash on pending replies in async code (Thanks, @switch-st) |
||||
* Clear `REDIS_CONNECTED` flag when connection is closed (Thanks, Jerry Jacobs) |
||||
* Add MacOS X addapter (Thanks, @dizzus) |
||||
* Add Qt adapter (Thanks, Pietro Cerutti) |
||||
* Add Ivykis adapter (Thanks, Gergely Nagy) |
||||
|
||||
All adapters are provided as is and are only tested where possible. |
||||
|
||||
### 0.13.1 (2015-05-03) |
||||
|
||||
This is a bug fix release. |
||||
The new `reconnect` method introduced new struct members, which clashed with pre-defined names in pre-C99 code. |
||||
Another commit forced C99 compilation just to make it work, but of course this is not desirable for outside projects. |
||||
Other non-C99 code can now use hiredis as usual again. |
||||
Sorry for the inconvenience. |
||||
|
||||
* Fix memory leak in async reply handling (Salvatore Sanfilippo) |
||||
* Rename struct member to avoid name clash with pre-c99 code (Alex Balashov, ncopa) |
||||
|
||||
### 0.13.0 (2015-04-16) |
||||
|
||||
This release adds a minimal Windows compatibility layer. |
||||
The parser, standalone since v0.12.0, can now be compiled on Windows |
||||
(and thus used in other client libraries as well) |
||||
|
||||
* Windows compatibility layer for parser code (tzickel) |
||||
* Properly escape data printed to PKGCONF file (Dan Skorupski) |
||||
* Fix tests when assert() undefined (Keith Bennett, Matt Stancliff) |
||||
* Implement a reconnect method for the client context, this changes the structure of `redisContext` (Aaron Bedra) |
||||
|
||||
### 0.12.1 (2015-01-26) |
||||
|
||||
* Fix `make install`: DESTDIR support, install all required files, install PKGCONF in proper location |
||||
* Fix `make test` as 32 bit build on 64 bit platform |
||||
|
||||
### 0.12.0 (2015-01-22) |
||||
|
||||
* Add optional KeepAlive support |
||||
|
||||
* Try again on EINTR errors |
||||
|
||||
* Add libuv adapter |
||||
|
||||
* Add IPv6 support |
||||
|
||||
* Remove possibility of multiple close on same fd |
||||
|
||||
* Add ability to bind source address on connect |
||||
|
||||
* Add redisConnectFd() and redisFreeKeepFd() |
||||
|
||||
* Fix getaddrinfo() memory leak |
||||
|
||||
* Free string if it is unused (fixes memory leak) |
||||
|
||||
* Improve redisAppendCommandArgv performance 2.5x |
||||
|
||||
* Add support for SO_REUSEADDR |
||||
|
||||
* Fix redisvFormatCommand format parsing |
||||
|
||||
* Add GLib 2.0 adapter |
||||
|
||||
* Refactor reading code into read.c |
||||
|
||||
* Fix errno error buffers to not clobber errors |
||||
|
||||
* Generate pkgconf during build |
||||
|
||||
* Silence _BSD_SOURCE warnings |
||||
|
||||
* Improve digit counting for multibulk creation |
||||
|
||||
|
||||
### 0.11.0 |
||||
|
||||
* Increase the maximum multi-bulk reply depth to 7. |
||||
|
||||
* Increase the read buffer size from 2k to 16k. |
||||
|
||||
* Use poll(2) instead of select(2) to support large fds (>= 1024). |
||||
|
||||
### 0.10.1 |
||||
|
||||
* Makefile overhaul. Important to check out if you override one or more |
||||
variables using environment variables or via arguments to the "make" tool. |
||||
|
||||
* Issue #45: Fix potential memory leak for a multi bulk reply with 0 elements |
||||
being created by the default reply object functions. |
||||
|
||||
* Issue #43: Don't crash in an asynchronous context when Redis returns an error |
||||
reply after the connection has been made (this happens when the maximum |
||||
number of connections is reached). |
||||
|
||||
### 0.10.0 |
||||
|
||||
* See commit log. |
||||
|
||||
@ -0,0 +1,410 @@
|
||||
[](https://travis-ci.org/redis/hiredis) |
||||
|
||||
**This Readme reflects the latest changed in the master branch. See [v0.14.1](https://github.com/redis/hiredis/tree/v0.14.1) for the Readme and documentation for the latest release.** |
||||
|
||||
# HIREDIS |
||||
|
||||
Hiredis is a minimalistic C client library for the [Redis](http://redis.io/) database. |
||||
|
||||
It is minimalistic because it just adds minimal support for the protocol, but |
||||
at the same time it uses a high level printf-alike API in order to make it |
||||
much higher level than otherwise suggested by its minimal code base and the |
||||
lack of explicit bindings for every Redis command. |
||||
|
||||
Apart from supporting sending commands and receiving replies, it comes with |
||||
a reply parser that is decoupled from the I/O layer. It |
||||
is a stream parser designed for easy reusability, which can for instance be used |
||||
in higher level language bindings for efficient reply parsing. |
||||
|
||||
Hiredis only supports the binary-safe Redis protocol, so you can use it with any |
||||
Redis version >= 1.2.0. |
||||
|
||||
The library comes with multiple APIs. There is the |
||||
*synchronous API*, the *asynchronous API* and the *reply parsing API*. |
||||
|
||||
## IMPORTANT: Breaking changes when upgrading from 0.13.x -> 0.14.x |
||||
|
||||
Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now |
||||
protocol errors. This is consistent with the RESP specification. On 32-bit |
||||
platforms, the upper bound is lowered to `SIZE_MAX`. |
||||
|
||||
Change `redisReply.len` to `size_t`, as it denotes the the size of a string |
||||
|
||||
User code should compare this to `size_t` values as well. If it was used to |
||||
compare to other values, casting might be necessary or can be removed, if |
||||
casting was applied before. |
||||
|
||||
For a detailed list of changes please view our [Changelog](CHANGELOG.md). |
||||
|
||||
## Synchronous API |
||||
|
||||
To consume the synchronous API, there are only a few function calls that need to be introduced: |
||||
|
||||
```c |
||||
redisContext *redisConnect(const char *ip, int port); |
||||
void *redisCommand(redisContext *c, const char *format, ...); |
||||
void freeReplyObject(void *reply); |
||||
``` |
||||
|
||||
### Connecting |
||||
|
||||
The function `redisConnect` is used to create a so-called `redisContext`. The |
||||
context is where Hiredis holds state for a connection. The `redisContext` |
||||
struct has an integer `err` field that is non-zero when the connection is in |
||||
an error state. The field `errstr` will contain a string with a description of |
||||
the error. More information on errors can be found in the **Errors** section. |
||||
After trying to connect to Redis using `redisConnect` you should |
||||
check the `err` field to see if establishing the connection was successful: |
||||
```c |
||||
redisContext *c = redisConnect("127.0.0.1", 6379); |
||||
if (c == NULL || c->err) { |
||||
if (c) { |
||||
printf("Error: %s\n", c->errstr); |
||||
// handle error |
||||
} else { |
||||
printf("Can't allocate redis context\n"); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
*Note: A `redisContext` is not thread-safe.* |
||||
|
||||
### Sending commands |
||||
|
||||
There are several ways to issue commands to Redis. The first that will be introduced is |
||||
`redisCommand`. This function takes a format similar to printf. In the simplest form, |
||||
it is used like this: |
||||
```c |
||||
reply = redisCommand(context, "SET foo bar"); |
||||
``` |
||||
|
||||
The specifier `%s` interpolates a string in the command, and uses `strlen` to |
||||
determine the length of the string: |
||||
```c |
||||
reply = redisCommand(context, "SET foo %s", value); |
||||
``` |
||||
When you need to pass binary safe strings in a command, the `%b` specifier can be |
||||
used. Together with a pointer to the string, it requires a `size_t` length argument |
||||
of the string: |
||||
```c |
||||
reply = redisCommand(context, "SET foo %b", value, (size_t) valuelen); |
||||
``` |
||||
Internally, Hiredis splits the command in different arguments and will |
||||
convert it to the protocol used to communicate with Redis. |
||||
One or more spaces separates arguments, so you can use the specifiers |
||||
anywhere in an argument: |
||||
```c |
||||
reply = redisCommand(context, "SET key:%s %s", myid, value); |
||||
``` |
||||
|
||||
### Using replies |
||||
|
||||
The return value of `redisCommand` holds a reply when the command was |
||||
successfully executed. When an error occurs, the return value is `NULL` and |
||||
the `err` field in the context will be set (see section on **Errors**). |
||||
Once an error is returned the context cannot be reused and you should set up |
||||
a new connection. |
||||
|
||||
The standard replies that `redisCommand` are of the type `redisReply`. The |
||||
`type` field in the `redisReply` should be used to test what kind of reply |
||||
was received: |
||||
|
||||
* **`REDIS_REPLY_STATUS`**: |
||||
* The command replied with a status reply. The status string can be accessed using `reply->str`. |
||||
The length of this string can be accessed using `reply->len`. |
||||
|
||||
* **`REDIS_REPLY_ERROR`**: |
||||
* The command replied with an error. The error string can be accessed identical to `REDIS_REPLY_STATUS`. |
||||
|
||||
* **`REDIS_REPLY_INTEGER`**: |
||||
* The command replied with an integer. The integer value can be accessed using the |
||||
`reply->integer` field of type `long long`. |
||||
|
||||
* **`REDIS_REPLY_NIL`**: |
||||
* The command replied with a **nil** object. There is no data to access. |
||||
|
||||
* **`REDIS_REPLY_STRING`**: |
||||
* A bulk (string) reply. The value of the reply can be accessed using `reply->str`. |
||||
The length of this string can be accessed using `reply->len`. |
||||
|
||||
* **`REDIS_REPLY_ARRAY`**: |
||||
* A multi bulk reply. The number of elements in the multi bulk reply is stored in |
||||
`reply->elements`. Every element in the multi bulk reply is a `redisReply` object as well |
||||
and can be accessed via `reply->element[..index..]`. |
||||
Redis may reply with nested arrays but this is fully supported. |
||||
|
||||
Replies should be freed using the `freeReplyObject()` function. |
||||
Note that this function will take care of freeing sub-reply objects |
||||
contained in arrays and nested arrays, so there is no need for the user to |
||||
free the sub replies (it is actually harmful and will corrupt the memory). |
||||
|
||||
**Important:** the current version of hiredis (0.10.0) frees replies when the |
||||
asynchronous API is used. This means you should not call `freeReplyObject` when |
||||
you use this API. The reply is cleaned up by hiredis _after_ the callback |
||||
returns. This behavior will probably change in future releases, so make sure to |
||||
keep an eye on the changelog when upgrading (see issue #39). |
||||
|
||||
### Cleaning up |
||||
|
||||
To disconnect and free the context the following function can be used: |
||||
```c |
||||
void redisFree(redisContext *c); |
||||
``` |
||||
This function immediately closes the socket and then frees the allocations done in |
||||
creating the context. |
||||
|
||||
### Sending commands (cont'd) |
||||
|
||||
Together with `redisCommand`, the function `redisCommandArgv` can be used to issue commands. |
||||
It has the following prototype: |
||||
```c |
||||
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); |
||||
``` |
||||
It takes the number of arguments `argc`, an array of strings `argv` and the lengths of the |
||||
arguments `argvlen`. For convenience, `argvlen` may be set to `NULL` and the function will |
||||
use `strlen(3)` on every argument to determine its length. Obviously, when any of the arguments |
||||
need to be binary safe, the entire array of lengths `argvlen` should be provided. |
||||
|
||||
The return value has the same semantic as `redisCommand`. |
||||
|
||||
### Pipelining |
||||
|
||||
To explain how Hiredis supports pipelining in a blocking connection, there needs to be |
||||
understanding of the internal execution flow. |
||||
|
||||
When any of the functions in the `redisCommand` family is called, Hiredis first formats the |
||||
command according to the Redis protocol. The formatted command is then put in the output buffer |
||||
of the context. This output buffer is dynamic, so it can hold any number of commands. |
||||
After the command is put in the output buffer, `redisGetReply` is called. This function has the |
||||
following two execution paths: |
||||
|
||||
1. The input buffer is non-empty: |
||||
* Try to parse a single reply from the input buffer and return it |
||||
* If no reply could be parsed, continue at *2* |
||||
2. The input buffer is empty: |
||||
* Write the **entire** output buffer to the socket |
||||
* Read from the socket until a single reply could be parsed |
||||
|
||||
The function `redisGetReply` is exported as part of the Hiredis API and can be used when a reply |
||||
is expected on the socket. To pipeline commands, the only things that needs to be done is |
||||
filling up the output buffer. For this cause, two commands can be used that are identical |
||||
to the `redisCommand` family, apart from not returning a reply: |
||||
```c |
||||
void redisAppendCommand(redisContext *c, const char *format, ...); |
||||
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); |
||||
``` |
||||
After calling either function one or more times, `redisGetReply` can be used to receive the |
||||
subsequent replies. The return value for this function is either `REDIS_OK` or `REDIS_ERR`, where |
||||
the latter means an error occurred while reading a reply. Just as with the other commands, |
||||
the `err` field in the context can be used to find out what the cause of this error is. |
||||
|
||||
The following examples shows a simple pipeline (resulting in only a single call to `write(2)` and |
||||
a single call to `read(2)`): |
||||
```c |
||||
redisReply *reply; |
||||
redisAppendCommand(context,"SET foo bar"); |
||||
redisAppendCommand(context,"GET foo"); |
||||
redisGetReply(context,&reply); // reply for SET |
||||
freeReplyObject(reply); |
||||
redisGetReply(context,&reply); // reply for GET |
||||
freeReplyObject(reply); |
||||
``` |
||||
This API can also be used to implement a blocking subscriber: |
||||
```c |
||||
reply = redisCommand(context,"SUBSCRIBE foo"); |
||||
freeReplyObject(reply); |
||||
while(redisGetReply(context,&reply) == REDIS_OK) { |
||||
// consume message |
||||
freeReplyObject(reply); |
||||
} |
||||
``` |
||||
### Errors |
||||
|
||||
When a function call is not successful, depending on the function either `NULL` or `REDIS_ERR` is |
||||
returned. The `err` field inside the context will be non-zero and set to one of the |
||||
following constants: |
||||
|
||||
* **`REDIS_ERR_IO`**: |
||||
There was an I/O error while creating the connection, trying to write |
||||
to the socket or read from the socket. If you included `errno.h` in your |
||||
application, you can use the global `errno` variable to find out what is |
||||
wrong. |
||||
|
||||
* **`REDIS_ERR_EOF`**: |
||||
The server closed the connection which resulted in an empty read. |
||||
|
||||
* **`REDIS_ERR_PROTOCOL`**: |
||||
There was an error while parsing the protocol. |
||||
|
||||
* **`REDIS_ERR_OTHER`**: |
||||
Any other error. Currently, it is only used when a specified hostname to connect |
||||
to cannot be resolved. |
||||
|
||||
In every case, the `errstr` field in the context will be set to hold a string representation |
||||
of the error. |
||||
|
||||
## Asynchronous API |
||||
|
||||
Hiredis comes with an asynchronous API that works easily with any event library. |
||||
Examples are bundled that show using Hiredis with [libev](http://software.schmorp.de/pkg/libev.html) |
||||
and [libevent](http://monkey.org/~provos/libevent/). |
||||
|
||||
### Connecting |
||||
|
||||
The function `redisAsyncConnect` can be used to establish a non-blocking connection to |
||||
Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The `err` field |
||||
should be checked after creation to see if there were errors creating the connection. |
||||
Because the connection that will be created is non-blocking, the kernel is not able to |
||||
instantly return if the specified host and port is able to accept a connection. |
||||
|
||||
*Note: A `redisAsyncContext` is not thread-safe.* |
||||
|
||||
```c |
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); |
||||
if (c->err) { |
||||
printf("Error: %s\n", c->errstr); |
||||
// handle error |
||||
} |
||||
``` |
||||
|
||||
The asynchronous context can hold a disconnect callback function that is called when the |
||||
connection is disconnected (either because of an error or per user request). This function should |
||||
have the following prototype: |
||||
```c |
||||
void(const redisAsyncContext *c, int status); |
||||
``` |
||||
On a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the |
||||
user, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `err` |
||||
field in the context can be accessed to find out the cause of the error. |
||||
|
||||
The context object is always freed after the disconnect callback fired. When a reconnect is needed, |
||||
the disconnect callback is a good point to do so. |
||||
|
||||
Setting the disconnect callback can only be done once per context. For subsequent calls it will |
||||
return `REDIS_ERR`. The function to set the disconnect callback has the following prototype: |
||||
```c |
||||
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); |
||||
``` |
||||
### Sending commands and their callbacks |
||||
|
||||
In an asynchronous context, commands are automatically pipelined due to the nature of an event loop. |
||||
Therefore, unlike the synchronous API, there is only a single way to send commands. |
||||
Because commands are sent to Redis asynchronously, issuing a command requires a callback function |
||||
that is called when the reply is received. Reply callbacks should have the following prototype: |
||||
```c |
||||
void(redisAsyncContext *c, void *reply, void *privdata); |
||||
``` |
||||
The `privdata` argument can be used to curry arbitrary data to the callback from the point where |
||||
the command is initially queued for execution. |
||||
|
||||
The functions that can be used to issue commands in an asynchronous context are: |
||||
```c |
||||
int redisAsyncCommand( |
||||
redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, |
||||
const char *format, ...); |
||||
int redisAsyncCommandArgv( |
||||
redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, |
||||
int argc, const char **argv, const size_t *argvlen); |
||||
``` |
||||
Both functions work like their blocking counterparts. The return value is `REDIS_OK` when the command |
||||
was successfully added to the output buffer and `REDIS_ERR` otherwise. Example: when the connection |
||||
is being disconnected per user-request, no new commands may be added to the output buffer and `REDIS_ERR` is |
||||
returned on calls to the `redisAsyncCommand` family. |
||||
|
||||
If the reply for a command with a `NULL` callback is read, it is immediately freed. When the callback |
||||
for a command is non-`NULL`, the memory is freed immediately following the callback: the reply is only |
||||
valid for the duration of the callback. |
||||
|
||||
All pending callbacks are called with a `NULL` reply when the context encountered an error. |
||||
|
||||
### Disconnecting |
||||
|
||||
An asynchronous connection can be terminated using: |
||||
```c |
||||
void redisAsyncDisconnect(redisAsyncContext *ac); |
||||
``` |
||||
When this function is called, the connection is **not** immediately terminated. Instead, new |
||||
commands are no longer accepted and the connection is only terminated when all pending commands |
||||
have been written to the socket, their respective replies have been read and their respective |
||||
callbacks have been executed. After this, the disconnection callback is executed with the |
||||
`REDIS_OK` status and the context object is freed. |
||||
|
||||
### Hooking it up to event library *X* |
||||
|
||||
There are a few hooks that need to be set on the context object after it is created. |
||||
See the `adapters/` directory for bindings to *libev* and *libevent*. |
||||
|
||||
## Reply parsing API |
||||
|
||||
Hiredis comes with a reply parsing API that makes it easy for writing higher |
||||
level language bindings. |
||||
|
||||
The reply parsing API consists of the following functions: |
||||
```c |
||||
redisReader *redisReaderCreate(void); |
||||
void redisReaderFree(redisReader *reader); |
||||
int redisReaderFeed(redisReader *reader, const char *buf, size_t len); |
||||
int redisReaderGetReply(redisReader *reader, void **reply); |
||||
``` |
||||
The same set of functions are used internally by hiredis when creating a |
||||
normal Redis context, the above API just exposes it to the user for a direct |
||||
usage. |
||||
|
||||
### Usage |
||||
|
||||
The function `redisReaderCreate` creates a `redisReader` structure that holds a |
||||
buffer with unparsed data and state for the protocol parser. |
||||
|
||||
Incoming data -- most likely from a socket -- can be placed in the internal |
||||
buffer of the `redisReader` using `redisReaderFeed`. This function will make a |
||||
copy of the buffer pointed to by `buf` for `len` bytes. This data is parsed |
||||
when `redisReaderGetReply` is called. This function returns an integer status |
||||
and a reply object (as described above) via `void **reply`. The returned status |
||||
can be either `REDIS_OK` or `REDIS_ERR`, where the latter means something went |
||||
wrong (either a protocol error, or an out of memory error). |
||||
|
||||
The parser limits the level of nesting for multi bulk payloads to 7. If the |
||||
multi bulk nesting level is higher than this, the parser returns an error. |
||||
|
||||
### Customizing replies |
||||
|
||||
The function `redisReaderGetReply` creates `redisReply` and makes the function |
||||
argument `reply` point to the created `redisReply` variable. For instance, if |
||||
the response of type `REDIS_REPLY_STATUS` then the `str` field of `redisReply` |
||||
will hold the status as a vanilla C string. However, the functions that are |
||||
responsible for creating instances of the `redisReply` can be customized by |
||||
setting the `fn` field on the `redisReader` struct. This should be done |
||||
immediately after creating the `redisReader`. |
||||
|
||||
For example, [hiredis-rb](https://github.com/pietern/hiredis-rb/blob/master/ext/hiredis_ext/reader.c) |
||||
uses customized reply object functions to create Ruby objects. |
||||
|
||||
### Reader max buffer |
||||
|
||||
Both when using the Reader API directly or when using it indirectly via a |
||||
normal Redis context, the redisReader structure uses a buffer in order to |
||||
accumulate data from the server. |
||||
Usually this buffer is destroyed when it is empty and is larger than 16 |
||||
KiB in order to avoid wasting memory in unused buffers |
||||
|
||||
However when working with very big payloads destroying the buffer may slow |
||||
down performances considerably, so it is possible to modify the max size of |
||||
an idle buffer changing the value of the `maxbuf` field of the reader structure |
||||
to the desired value. The special value of 0 means that there is no maximum |
||||
value for an idle buffer, so the buffer will never get freed. |
||||
|
||||
For instance if you have a normal Redis context you can set the maximum idle |
||||
buffer to zero (unlimited) just with: |
||||
```c |
||||
context->reader->maxbuf = 0; |
||||
``` |
||||
This should be done only in order to maximize performances when working with |
||||
large payloads. The context should be set back to `REDIS_READER_MAX_BUF` again |
||||
as soon as possible in order to prevent allocation of useless memory. |
||||
|
||||
## AUTHORS |
||||
|
||||
Hiredis was written by Salvatore Sanfilippo (antirez at gmail) and |
||||
Pieter Noordhuis (pcnoordhuis at gmail) and is released under the BSD license. |
||||
Hiredis is currently maintained by Matt Stancliff (matt at genges dot com) and |
||||
Jan-Erik Rediger (janerik at fnordig dot com) |
||||
@ -0,0 +1,81 @@
|
||||
#ifndef __HIREDIS_IVYKIS_H__ |
||||
#define __HIREDIS_IVYKIS_H__ |
||||
#include <iv.h> |
||||
#include "../hiredis.h" |
||||
#include "../async.h" |
||||
|
||||
typedef struct redisIvykisEvents { |
||||
redisAsyncContext *context; |
||||
struct iv_fd fd; |
||||
} redisIvykisEvents; |
||||
|
||||
static void redisIvykisReadEvent(void *arg) { |
||||
redisAsyncContext *context = (redisAsyncContext *)arg; |
||||
redisAsyncHandleRead(context); |
||||
} |
||||
|
||||
static void redisIvykisWriteEvent(void *arg) { |
||||
redisAsyncContext *context = (redisAsyncContext *)arg; |
||||
redisAsyncHandleWrite(context); |
||||
} |
||||
|
||||
static void redisIvykisAddRead(void *privdata) { |
||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata; |
||||
iv_fd_set_handler_in(&e->fd, redisIvykisReadEvent); |
||||
} |
||||
|
||||
static void redisIvykisDelRead(void *privdata) { |
||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata; |
||||
iv_fd_set_handler_in(&e->fd, NULL); |
||||
} |
||||
|
||||
static void redisIvykisAddWrite(void *privdata) { |
||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata; |
||||
iv_fd_set_handler_out(&e->fd, redisIvykisWriteEvent); |
||||
} |
||||
|
||||
static void redisIvykisDelWrite(void *privdata) { |
||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata; |
||||
iv_fd_set_handler_out(&e->fd, NULL); |
||||
} |
||||
|
||||
static void redisIvykisCleanup(void *privdata) { |
||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata; |
||||
|
||||
iv_fd_unregister(&e->fd); |
||||
free(e); |
||||
} |
||||
|
||||
static int redisIvykisAttach(redisAsyncContext *ac) { |
||||
redisContext *c = &(ac->c); |
||||
redisIvykisEvents *e; |
||||
|
||||
/* Nothing should be attached when something is already attached */ |
||||
if (ac->ev.data != NULL) |
||||
return REDIS_ERR; |
||||
|
||||
/* Create container for context and r/w events */ |
||||
e = (redisIvykisEvents*)hi_malloc(sizeof(*e)); |
||||
e->context = ac; |
||||
|
||||
/* Register functions to start/stop listening for events */ |
||||
ac->ev.addRead = redisIvykisAddRead; |
||||
ac->ev.delRead = redisIvykisDelRead; |
||||
ac->ev.addWrite = redisIvykisAddWrite; |
||||
ac->ev.delWrite = redisIvykisDelWrite; |
||||
ac->ev.cleanup = redisIvykisCleanup; |
||||
ac->ev.data = e; |
||||
|
||||
/* Initialize and install read/write events */ |
||||
IV_FD_INIT(&e->fd); |
||||
e->fd.fd = c->fd; |
||||
e->fd.handler_in = redisIvykisReadEvent; |
||||
e->fd.handler_out = redisIvykisWriteEvent; |
||||
e->fd.handler_err = NULL; |
||||
e->fd.cookie = e->context; |
||||
|
||||
iv_fd_register(&e->fd); |
||||
|
||||
return REDIS_OK; |
||||
} |
||||
#endif |
||||
@ -0,0 +1,114 @@
|
||||
//
|
||||
// Created by Дмитрий Бахвалов on 13.07.15.
|
||||
// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __HIREDIS_MACOSX_H__ |
||||
#define __HIREDIS_MACOSX_H__ |
||||
|
||||
#include <CoreFoundation/CoreFoundation.h> |
||||
|
||||
#include "../hiredis.h" |
||||
#include "../async.h" |
||||
|
||||
typedef struct { |
||||
redisAsyncContext *context; |
||||
CFSocketRef socketRef; |
||||
CFRunLoopSourceRef sourceRef; |
||||
} RedisRunLoop; |
||||
|
||||
static int freeRedisRunLoop(RedisRunLoop* redisRunLoop) { |
||||
if( redisRunLoop != NULL ) { |
||||
if( redisRunLoop->sourceRef != NULL ) { |
||||
CFRunLoopSourceInvalidate(redisRunLoop->sourceRef); |
||||
CFRelease(redisRunLoop->sourceRef); |
||||
} |
||||
if( redisRunLoop->socketRef != NULL ) { |
||||
CFSocketInvalidate(redisRunLoop->socketRef); |
||||
CFRelease(redisRunLoop->socketRef); |
||||
} |
||||
free(redisRunLoop); |
||||
} |
||||
return REDIS_ERR; |
||||
} |
||||
|
||||
static void redisMacOSAddRead(void *privdata) { |
||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; |
||||
CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack); |
||||
} |
||||
|
||||
static void redisMacOSDelRead(void *privdata) { |
||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; |
||||
CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack); |
||||
} |
||||
|
||||
static void redisMacOSAddWrite(void *privdata) { |
||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; |
||||
CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack); |
||||
} |
||||
|
||||
static void redisMacOSDelWrite(void *privdata) { |
||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; |
||||
CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack); |
||||
} |
||||
|
||||
static void redisMacOSCleanup(void *privdata) { |
||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; |
||||
freeRedisRunLoop(redisRunLoop); |
||||
} |
||||
|
||||
static void redisMacOSAsyncCallback(CFSocketRef __unused s, CFSocketCallBackType callbackType, CFDataRef __unused address, const void __unused *data, void *info) { |
||||
redisAsyncContext* context = (redisAsyncContext*) info; |
||||
|
||||
switch (callbackType) { |
||||
case kCFSocketReadCallBack: |
||||
redisAsyncHandleRead(context); |
||||
break; |
||||
|
||||
case kCFSocketWriteCallBack: |
||||
redisAsyncHandleWrite(context); |
||||
break; |
||||
|
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
|
||||
static int redisMacOSAttach(redisAsyncContext *redisAsyncCtx, CFRunLoopRef runLoop) { |
||||
redisContext *redisCtx = &(redisAsyncCtx->c); |
||||
|
||||
/* Nothing should be attached when something is already attached */ |
||||
if( redisAsyncCtx->ev.data != NULL ) return REDIS_ERR; |
||||
|
||||
RedisRunLoop* redisRunLoop = (RedisRunLoop*) calloc(1, sizeof(RedisRunLoop)); |
||||
if( !redisRunLoop ) return REDIS_ERR; |
||||
|
||||
/* Setup redis stuff */ |
||||
redisRunLoop->context = redisAsyncCtx; |
||||
|
||||
redisAsyncCtx->ev.addRead = redisMacOSAddRead; |
||||
redisAsyncCtx->ev.delRead = redisMacOSDelRead; |
||||
redisAsyncCtx->ev.addWrite = redisMacOSAddWrite; |
||||
redisAsyncCtx->ev.delWrite = redisMacOSDelWrite; |
||||
redisAsyncCtx->ev.cleanup = redisMacOSCleanup; |
||||
redisAsyncCtx->ev.data = redisRunLoop; |
||||
|
||||
/* Initialize and install read/write events */ |
||||
CFSocketContext socketCtx = { 0, redisAsyncCtx, NULL, NULL, NULL }; |
||||
|
||||
redisRunLoop->socketRef = CFSocketCreateWithNative(NULL, redisCtx->fd, |
||||
kCFSocketReadCallBack | kCFSocketWriteCallBack, |
||||
redisMacOSAsyncCallback, |
||||
&socketCtx); |
||||
if( !redisRunLoop->socketRef ) return freeRedisRunLoop(redisRunLoop); |
||||
|
||||
redisRunLoop->sourceRef = CFSocketCreateRunLoopSource(NULL, redisRunLoop->socketRef, 0); |
||||
if( !redisRunLoop->sourceRef ) return freeRedisRunLoop(redisRunLoop); |
||||
|
||||
CFRunLoopAddSource(runLoop, redisRunLoop->sourceRef, kCFRunLoopDefaultMode); |
||||
|
||||
return REDIS_OK; |
||||
} |
||||
|
||||
#endif |
||||
|
||||
@ -0,0 +1,135 @@
|
||||
/*-
|
||||
* Copyright (C) 2014 Pietro Cerutti <gahr@gahr.ch> |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions |
||||
* are met: |
||||
* 1. Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* 2. Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE |
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||||
* SUCH DAMAGE. |
||||
*/ |
||||
|
||||
#ifndef __HIREDIS_QT_H__ |
||||
#define __HIREDIS_QT_H__ |
||||
#include <QSocketNotifier> |
||||
#include "../async.h" |
||||
|
||||
static void RedisQtAddRead(void *); |
||||
static void RedisQtDelRead(void *); |
||||
static void RedisQtAddWrite(void *); |
||||
static void RedisQtDelWrite(void *); |
||||
static void RedisQtCleanup(void *); |
||||
|
||||
class RedisQtAdapter : public QObject { |
||||
|
||||
Q_OBJECT |
||||
|
||||
friend |
||||
void RedisQtAddRead(void * adapter) { |
||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter); |
||||
a->addRead(); |
||||
} |
||||
|
||||
friend |
||||
void RedisQtDelRead(void * adapter) { |
||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter); |
||||
a->delRead(); |
||||
} |
||||
|
||||
friend |
||||
void RedisQtAddWrite(void * adapter) { |
||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter); |
||||
a->addWrite(); |
||||
} |
||||
|
||||
friend |
||||
void RedisQtDelWrite(void * adapter) { |
||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter); |
||||
a->delWrite(); |
||||
} |
||||
|
||||
friend |
||||
void RedisQtCleanup(void * adapter) { |
||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter); |
||||
a->cleanup(); |
||||
} |
||||
|
||||
public: |
||||
RedisQtAdapter(QObject * parent = 0) |
||||
: QObject(parent), m_ctx(0), m_read(0), m_write(0) { } |
||||
|
||||
~RedisQtAdapter() { |
||||
if (m_ctx != 0) { |
||||
m_ctx->ev.data = NULL; |
||||
} |
||||
} |
||||
|
||||
int setContext(redisAsyncContext * ac) { |
||||
if (ac->ev.data != NULL) { |
||||
return REDIS_ERR; |
||||
} |
||||
m_ctx = ac; |
||||
m_ctx->ev.data = this; |
||||
m_ctx->ev.addRead = RedisQtAddRead; |
||||
m_ctx->ev.delRead = RedisQtDelRead; |
||||
m_ctx->ev.addWrite = RedisQtAddWrite; |
||||
m_ctx->ev.delWrite = RedisQtDelWrite; |
||||
m_ctx->ev.cleanup = RedisQtCleanup; |
||||
return REDIS_OK; |
||||
} |
||||
|
||||
private: |
||||
void addRead() { |
||||
if (m_read) return; |
||||
m_read = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Read, 0); |
||||
connect(m_read, SIGNAL(activated(int)), this, SLOT(read())); |
||||
} |
||||
|
||||
void delRead() { |
||||
if (!m_read) return; |
||||
delete m_read; |
||||
m_read = 0; |
||||
} |
||||
|
||||
void addWrite() { |
||||
if (m_write) return; |
||||
m_write = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Write, 0); |
||||
connect(m_write, SIGNAL(activated(int)), this, SLOT(write())); |
||||
} |
||||
|
||||
void delWrite() { |
||||
if (!m_write) return; |
||||
delete m_write; |
||||
m_write = 0; |
||||
} |
||||
|
||||
void cleanup() { |
||||
delRead(); |
||||
delWrite(); |
||||
} |
||||
|
||||
private slots: |
||||
void read() { redisAsyncHandleRead(m_ctx); } |
||||
void write() { redisAsyncHandleWrite(m_ctx); } |
||||
|
||||
private: |
||||
redisAsyncContext * m_ctx; |
||||
QSocketNotifier * m_read; |
||||
QSocketNotifier * m_write; |
||||
}; |
||||
|
||||
#endif /* !__HIREDIS_QT_H__ */ |
||||
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com> |
||||
* |
||||
* 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 Redis nor the names of its contributors may be used |
||||
* to endorse or promote products derived from this software without |
||||
* specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
* POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
#include "fmacros.h" |
||||
#include "alloc.h" |
||||
#include <string.h> |
||||
|
||||
void *hi_malloc(size_t size) { |
||||
void *ptr = malloc(size); |
||||
if (ptr == NULL) |
||||
HIREDIS_OOM_HANDLER; |
||||
|
||||
return ptr; |
||||
} |
||||
|
||||
void *hi_calloc(size_t nmemb, size_t size) { |
||||
void *ptr = calloc(nmemb, size); |
||||
if (ptr == NULL) |
||||
HIREDIS_OOM_HANDLER; |
||||
|
||||
return ptr; |
||||
} |
||||
|
||||
void *hi_realloc(void *ptr, size_t size) { |
||||
void *newptr = realloc(ptr, size); |
||||
if (newptr == NULL) |
||||
HIREDIS_OOM_HANDLER; |
||||
|
||||
return newptr; |
||||
} |
||||
|
||||
char *hi_strdup(const char *str) { |
||||
char *newstr = strdup(str); |
||||
if (newstr == NULL) |
||||
HIREDIS_OOM_HANDLER; |
||||
|
||||
return newstr; |
||||
} |
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com> |
||||
* |
||||
* 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 Redis nor the names of its contributors may be used |
||||
* to endorse or promote products derived from this software without |
||||
* specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
* POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
#ifndef HIREDIS_ALLOC_H |
||||
#define HIREDIS_ALLOC_H |
||||
|
||||
#include <stdlib.h> /* for size_t */ |
||||
|
||||
#ifndef HIREDIS_OOM_HANDLER |
||||
#define HIREDIS_OOM_HANDLER abort() |
||||
#endif |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
void *hi_malloc(size_t size); |
||||
void *hi_calloc(size_t nmemb, size_t size); |
||||
void *hi_realloc(void *ptr, size_t size); |
||||
char *hi_strdup(const char *str); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif /* HIREDIS_ALLOC_H */ |
||||
@ -0,0 +1,23 @@
|
||||
# Appveyor configuration file for CI build of hiredis on Windows (under Cygwin) |
||||
environment: |
||||
matrix: |
||||
- CYG_BASH: C:\cygwin64\bin\bash |
||||
CC: gcc |
||||
- CYG_BASH: C:\cygwin\bin\bash |
||||
CC: gcc |
||||
TARGET: 32bit |
||||
TARGET_VARS: 32bit-vars |
||||
|
||||
clone_depth: 1 |
||||
|
||||
# Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail |
||||
init: |
||||
- git config --global core.autocrlf input |
||||
|
||||
# Install needed build dependencies |
||||
install: |
||||
- '%CYG_BASH% -lc "cygcheck -dc cygwin"' |
||||
|
||||
build_script: |
||||
- 'echo building...' |
||||
- '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0</dev/null; make LDFLAGS=$LDFLAGS CC=$CC $TARGET CFLAGS=$CFLAGS && make LDFLAGS=$LDFLAGS CC=$CC $TARGET_VARS hiredis-example"' |
||||
@ -0,0 +1,58 @@
|
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <signal.h> |
||||
|
||||
#include <hiredis.h> |
||||
#include <async.h> |
||||
#include <adapters/ivykis.h> |
||||
|
||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) { |
||||
redisReply *reply = r; |
||||
if (reply == NULL) return; |
||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str); |
||||
|
||||
/* Disconnect after receiving the reply to GET */ |
||||
redisAsyncDisconnect(c); |
||||
} |
||||
|
||||
void connectCallback(const redisAsyncContext *c, int status) { |
||||
if (status != REDIS_OK) { |
||||
printf("Error: %s\n", c->errstr); |
||||
return; |
||||
} |
||||
printf("Connected...\n"); |
||||
} |
||||
|
||||
void disconnectCallback(const redisAsyncContext *c, int status) { |
||||
if (status != REDIS_OK) { |
||||
printf("Error: %s\n", c->errstr); |
||||
return; |
||||
} |
||||
printf("Disconnected...\n"); |
||||
} |
||||
|
||||
int main (int argc, char **argv) { |
||||
signal(SIGPIPE, SIG_IGN); |
||||
|
||||
iv_init(); |
||||
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); |
||||
if (c->err) { |
||||
/* Let *c leak for now... */ |
||||
printf("Error: %s\n", c->errstr); |
||||
return 1; |
||||
} |
||||
|
||||
redisIvykisAttach(c); |
||||
redisAsyncSetConnectCallback(c,connectCallback); |
||||
redisAsyncSetDisconnectCallback(c,disconnectCallback); |
||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); |
||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); |
||||
|
||||
iv_main(); |
||||
|
||||
iv_deinit(); |
||||
|
||||
return 0; |
||||
} |
||||
@ -0,0 +1,66 @@
|
||||
//
|
||||
// Created by Дмитрий Бахвалов on 13.07.15.
|
||||
// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved.
|
||||
//
|
||||
|
||||
#include <stdio.h> |
||||
|
||||
#include <hiredis.h> |
||||
#include <async.h> |
||||
#include <adapters/macosx.h> |
||||
|
||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) { |
||||
redisReply *reply = r; |
||||
if (reply == NULL) return; |
||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str); |
||||
|
||||
/* Disconnect after receiving the reply to GET */ |
||||
redisAsyncDisconnect(c); |
||||
} |
||||
|
||||
void connectCallback(const redisAsyncContext *c, int status) { |
||||
if (status != REDIS_OK) { |
||||
printf("Error: %s\n", c->errstr); |
||||
return; |
||||
} |
||||
printf("Connected...\n"); |
||||
} |
||||
|
||||
void disconnectCallback(const redisAsyncContext *c, int status) { |
||||
if (status != REDIS_OK) { |
||||
printf("Error: %s\n", c->errstr); |
||||
return; |
||||
} |
||||
CFRunLoopStop(CFRunLoopGetCurrent()); |
||||
printf("Disconnected...\n"); |
||||
} |
||||
|
||||
int main (int argc, char **argv) { |
||||
signal(SIGPIPE, SIG_IGN); |
||||
|
||||
CFRunLoopRef loop = CFRunLoopGetCurrent(); |
||||
if( !loop ) { |
||||
printf("Error: Cannot get current run loop\n"); |
||||
return 1; |
||||
} |
||||
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); |
||||
if (c->err) { |
||||
/* Let *c leak for now... */ |
||||
printf("Error: %s\n", c->errstr); |
||||
return 1; |
||||
} |
||||
|
||||
redisMacOSAttach(c, loop); |
||||
|
||||
redisAsyncSetConnectCallback(c,connectCallback); |
||||
redisAsyncSetDisconnectCallback(c,disconnectCallback); |
||||
|
||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); |
||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); |
||||
|
||||
CFRunLoopRun(); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
@ -0,0 +1,46 @@
|
||||
#include <iostream> |
||||
using namespace std; |
||||
|
||||
#include <QCoreApplication> |
||||
#include <QTimer> |
||||
|
||||
#include "example-qt.h" |
||||
|
||||
void getCallback(redisAsyncContext *, void * r, void * privdata) { |
||||
|
||||
redisReply * reply = static_cast<redisReply *>(r); |
||||
ExampleQt * ex = static_cast<ExampleQt *>(privdata); |
||||
if (reply == nullptr || ex == nullptr) return; |
||||
|
||||
cout << "key: " << reply->str << endl; |
||||
|
||||
ex->finish(); |
||||
} |
||||
|
||||
void ExampleQt::run() { |
||||
|
||||
m_ctx = redisAsyncConnect("localhost", 6379); |
||||
|
||||
if (m_ctx->err) { |
||||
cerr << "Error: " << m_ctx->errstr << endl; |
||||
redisAsyncFree(m_ctx); |
||||
emit finished(); |
||||
} |
||||
|
||||
m_adapter.setContext(m_ctx); |
||||
|
||||
redisAsyncCommand(m_ctx, NULL, NULL, "SET key %s", m_value); |
||||
redisAsyncCommand(m_ctx, getCallback, this, "GET key"); |
||||
} |
||||
|
||||
int main (int argc, char **argv) { |
||||
|
||||
QCoreApplication app(argc, argv); |
||||
|
||||
ExampleQt example(argv[argc-1]); |
||||
|
||||
QObject::connect(&example, SIGNAL(finished()), &app, SLOT(quit())); |
||||
QTimer::singleShot(0, &example, SLOT(run())); |
||||
|
||||
return app.exec(); |
||||
} |
||||
@ -0,0 +1,32 @@
|
||||
#ifndef __HIREDIS_EXAMPLE_QT_H |
||||
#define __HIREDIS_EXAMPLE_QT_H |
||||
|
||||
#include <adapters/qt.h> |
||||
|
||||
class ExampleQt : public QObject { |
||||
|
||||
Q_OBJECT |
||||
|
||||
public: |
||||
ExampleQt(const char * value, QObject * parent = 0) |
||||
: QObject(parent), m_value(value) {} |
||||
|
||||
signals: |
||||
void finished(); |
||||
|
||||
public slots: |
||||
void run(); |
||||
|
||||
private: |
||||
void finish() { emit finished(); } |
||||
|
||||
private: |
||||
const char * m_value; |
||||
redisAsyncContext * m_ctx; |
||||
RedisQtAdapter m_adapter; |
||||
|
||||
friend |
||||
void getCallback(redisAsyncContext *, void *, void *); |
||||
}; |
||||
|
||||
#endif /* !__HIREDIS_EXAMPLE_QT_H */ |
||||
@ -0,0 +1,12 @@
|
||||
#ifndef __HIREDIS_FMACRO_H |
||||
#define __HIREDIS_FMACRO_H |
||||
|
||||
#define _XOPEN_SOURCE 600 |
||||
#define _POSIX_C_SOURCE 200112L |
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__) |
||||
/* Enable TCP_KEEPALIVE */ |
||||
#define _DARWIN_C_SOURCE |
||||
#endif |
||||
|
||||
#endif |
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com> |
||||
* |
||||
* 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 Redis nor the names of its contributors may be used |
||||
* to endorse or promote products derived from this software without |
||||
* specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
* POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
#ifndef HIREDIS_ALLOC_H |
||||
#define HIREDIS_ALLOC_H |
||||
|
||||
#include <stdlib.h> /* for size_t */ |
||||
|
||||
#ifndef HIREDIS_OOM_HANDLER |
||||
#define HIREDIS_OOM_HANDLER abort() |
||||
#endif |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
void *hi_malloc(size_t size); |
||||
void *hi_calloc(size_t nmemb, size_t size); |
||||
void *hi_realloc(void *ptr, size_t size); |
||||
char *hi_strdup(const char *str); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif /* HIREDIS_ALLOC_H */ |
||||
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com> |
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com> |
||||
* |
||||
* 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 Redis nor the names of its contributors may be used |
||||
* to endorse or promote products derived from this software without |
||||
* specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
* POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
#ifndef __HIREDIS_ASYNC_H |
||||
#define __HIREDIS_ASYNC_H |
||||
#include "hiredis.h" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
struct redisAsyncContext; /* need forward declaration of redisAsyncContext */ |
||||
struct dict; /* dictionary header is included in async.c */ |
||||
|
||||
/* Reply callback prototype and container */ |
||||
typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*); |
||||
typedef struct redisCallback { |
||||
struct redisCallback *next; /* simple singly linked list */ |
||||
redisCallbackFn *fn; |
||||
int pending_subs; |
||||
void *privdata; |
||||
} redisCallback; |
||||
|
||||
/* List of callbacks for either regular replies or pub/sub */ |
||||
typedef struct redisCallbackList { |
||||
redisCallback *head, *tail; |
||||
} redisCallbackList; |
||||
|
||||
/* Connection callback prototypes */ |
||||
typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status); |
||||
typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status); |
||||
|
||||
/* Context for an async connection to Redis */ |
||||
typedef struct redisAsyncContext { |
||||
/* Hold the regular context, so it can be realloc'ed. */ |
||||
redisContext c; |
||||
|
||||
/* Setup error flags so they can be used directly. */ |
||||
int err; |
||||
char *errstr; |
||||
|
||||
/* Not used by hiredis */ |
||||
void *data; |
||||
|
||||
/* Event library data and hooks */ |
||||
struct { |
||||
void *data; |
||||
|
||||
/* Hooks that are called when the library expects to start
|
||||
* reading/writing. These functions should be idempotent. */ |
||||
void (*addRead)(void *privdata); |
||||
void (*delRead)(void *privdata); |
||||
void (*addWrite)(void *privdata); |
||||
void (*delWrite)(void *privdata); |
||||
void (*cleanup)(void *privdata); |
||||
} ev; |
||||
|
||||
/* Called when either the connection is terminated due to an error or per
|
||||
* user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */ |
||||
redisDisconnectCallback *onDisconnect; |
||||
|
||||
/* Called when the first write event was received. */ |
||||
redisConnectCallback *onConnect; |
||||
|
||||
/* Regular command callbacks */ |
||||
redisCallbackList replies; |
||||
|
||||
/* Subscription callbacks */ |
||||
struct { |
||||
redisCallbackList invalid; |
||||
struct dict *channels; |
||||
struct dict *patterns; |
||||
} sub; |
||||
} redisAsyncContext; |
||||
|
||||
/* Functions that proxy to hiredis */ |
||||
redisAsyncContext *redisAsyncConnect(const char *ip, int port); |
||||
redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr); |
||||
redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port, |
||||
const char *source_addr); |
||||
redisAsyncContext *redisAsyncConnectUnix(const char *path); |
||||
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn); |
||||
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); |
||||
void redisAsyncDisconnect(redisAsyncContext *ac); |
||||
void redisAsyncFree(redisAsyncContext *ac); |
||||
|
||||
/* Handle read/write events */ |
||||
void redisAsyncHandleRead(redisAsyncContext *ac); |
||||
void redisAsyncHandleWrite(redisAsyncContext *ac); |
||||
|
||||
/* Command functions for an async context. Write the command to the
|
||||
* output buffer and register the provided callback. */ |
||||
int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap); |
||||
int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...); |
||||
int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen); |
||||
int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif |
||||
@ -0,0 +1,126 @@
|
||||
/* Hash table implementation.
|
||||
* |
||||
* This file implements in memory hash tables with insert/del/replace/find/ |
||||
* get-random-element operations. Hash tables will auto resize if needed |
||||
* tables of power of two in size are used, collisions are handled by |
||||
* chaining. See the source code for more information... :) |
||||
* |
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com> |
||||
* 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 Redis nor the names of its contributors may be used |
||||
* to endorse or promote products derived from this software without |
||||
* specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
* POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
#ifndef __DICT_H |
||||
#define __DICT_H |
||||
|
||||
#define DICT_OK 0 |
||||
#define DICT_ERR 1 |
||||
|
||||
/* Unused arguments generate annoying warnings... */ |
||||
#define DICT_NOTUSED(V) ((void) V) |
||||
|
||||
typedef struct dictEntry { |
||||
void *key; |
||||
void *val; |
||||
struct dictEntry *next; |
||||
} dictEntry; |
||||
|
||||
typedef struct dictType { |
||||
unsigned int (*hashFunction)(const void *key); |
||||
void *(*keyDup)(void *privdata, const void *key); |
||||
void *(*valDup)(void *privdata, const void *obj); |
||||
int (*keyCompare)(void *privdata, const void *key1, const void *key2); |
||||
void (*keyDestructor)(void *privdata, void *key); |
||||
void (*valDestructor)(void *privdata, void *obj); |
||||
} dictType; |
||||
|
||||
typedef struct dict { |
||||
dictEntry **table; |
||||
dictType *type; |
||||
unsigned long size; |
||||
unsigned long sizemask; |
||||
unsigned long used; |
||||
void *privdata; |
||||
} dict; |
||||
|
||||
typedef struct dictIterator { |
||||
dict *ht; |
||||
int index; |
||||
dictEntry *entry, *nextEntry; |
||||
} dictIterator; |
||||
|
||||
/* This is the initial size of every hash table */ |
||||
#define DICT_HT_INITIAL_SIZE 4 |
||||
|
||||
/* ------------------------------- Macros ------------------------------------*/ |
||||
#define dictFreeEntryVal(ht, entry) \ |
||||
if ((ht)->type->valDestructor) \
|
||||
(ht)->type->valDestructor((ht)->privdata, (entry)->val) |
||||
|
||||
#define dictSetHashVal(ht, entry, _val_) do { \ |
||||
if ((ht)->type->valDup) \
|
||||
entry->val = (ht)->type->valDup((ht)->privdata, _val_); \
|
||||
else \
|
||||
entry->val = (_val_); \
|
||||
} while(0) |
||||
|
||||
#define dictFreeEntryKey(ht, entry) \ |
||||
if ((ht)->type->keyDestructor) \
|
||||
(ht)->type->keyDestructor((ht)->privdata, (entry)->key) |
||||
|
||||
#define dictSetHashKey(ht, entry, _key_) do { \ |
||||
if ((ht)->type->keyDup) \
|
||||
entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \
|
||||
else \
|
||||
entry->key = (_key_); \
|
||||
} while(0) |
||||
|
||||
#define dictCompareHashKeys(ht, key1, key2) \ |
||||
(((ht)->type->keyCompare) ? \
|
||||
(ht)->type->keyCompare((ht)->privdata, key1, key2) : \
|
||||
(key1) == (key2)) |
||||
|
||||
#define dictHashKey(ht, key) (ht)->type->hashFunction(key) |
||||
|
||||
#define dictGetEntryKey(he) ((he)->key) |
||||
#define dictGetEntryVal(he) ((he)->val) |
||||
#define dictSlots(ht) ((ht)->size) |
||||
#define dictSize(ht) ((ht)->used) |
||||
|
||||
/* API */ |
||||
static unsigned int dictGenHashFunction(const unsigned char *buf, int len); |
||||
static dict *dictCreate(dictType *type, void *privDataPtr); |
||||
static int dictExpand(dict *ht, unsigned long size); |
||||
static int dictAdd(dict *ht, void *key, void *val); |
||||
static int dictReplace(dict *ht, void *key, void *val); |
||||
static int dictDelete(dict *ht, const void *key); |
||||
static void dictRelease(dict *ht); |
||||
static dictEntry * dictFind(dict *ht, const void *key); |
||||
static dictIterator *dictGetIterator(dict *ht); |
||||
static dictEntry *dictNext(dictIterator *iter); |
||||
static void dictReleaseIterator(dictIterator *iter); |
||||
|
||||
#endif /* __DICT_H */ |
||||
@ -0,0 +1,12 @@
|
||||
#ifndef __HIREDIS_FMACRO_H |
||||
#define __HIREDIS_FMACRO_H |
||||
|
||||
#define _XOPEN_SOURCE 600 |
||||
#define _POSIX_C_SOURCE 200112L |
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__) |
||||
/* Enable TCP_KEEPALIVE */ |
||||
#define _DARWIN_C_SOURCE |
||||
#endif |
||||
|
||||
#endif |
||||
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com> |
||||
* Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com> |
||||
* Copyright (c) 2015, Matt Stancliff <matt at genges dot com>, |
||||
* Jan-Erik Rediger <janerik at fnordig dot com> |
||||
* |
||||
* 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 Redis nor the names of its contributors may be used |
||||
* to endorse or promote products derived from this software without |
||||
* specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
* POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
#ifndef __HIREDIS_H |
||||
#define __HIREDIS_H |
||||
#include "read.h" |
||||
#include <stdarg.h> /* for va_list */ |
||||
#include <sys/time.h> /* for struct timeval */ |
||||
#include <stdint.h> /* uintXX_t, etc */ |
||||
#include "sds.h" /* for sds */ |
||||
#include "alloc.h" /* for allocation wrappers */ |
||||
|
||||
#define HIREDIS_MAJOR 0 |
||||
#define HIREDIS_MINOR 14 |
||||
#define HIREDIS_PATCH 1 |
||||
#define HIREDIS_SONAME 0.14 |
||||
|
||||
/* Connection type can be blocking or non-blocking and is set in the
|
||||
* least significant bit of the flags field in redisContext. */ |
||||
#define REDIS_BLOCK 0x1 |
||||
|
||||
/* Connection may be disconnected before being free'd. The second bit
|
||||
* in the flags field is set when the context is connected. */ |
||||
#define REDIS_CONNECTED 0x2 |
||||
|
||||
/* The async API might try to disconnect cleanly and flush the output
|
||||
* buffer and read all subsequent replies before disconnecting. |
||||
* This flag means no new commands can come in and the connection |
||||
* should be terminated once all replies have been read. */ |
||||
#define REDIS_DISCONNECTING 0x4 |
||||
|
||||
/* Flag specific to the async API which means that the context should be clean
|
||||
* up as soon as possible. */ |
||||
#define REDIS_FREEING 0x8 |
||||
|
||||
/* Flag that is set when an async callback is executed. */ |
||||
#define REDIS_IN_CALLBACK 0x10 |
||||
|
||||
/* Flag that is set when the async context has one or more subscriptions. */ |
||||
#define REDIS_SUBSCRIBED 0x20 |
||||
|
||||
/* Flag that is set when monitor mode is active */ |
||||
#define REDIS_MONITORING 0x40 |
||||
|
||||
/* Flag that is set when we should set SO_REUSEADDR before calling bind() */ |
||||
#define REDIS_REUSEADDR 0x80 |
||||
|
||||
#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */ |
||||
|
||||
/* number of times we retry to connect in the case of EADDRNOTAVAIL and
|
||||
* SO_REUSEADDR is being used. */ |
||||
#define REDIS_CONNECT_RETRIES 10 |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/* This is the reply object returned by redisCommand() */ |
||||
typedef struct redisReply { |
||||
int type; /* REDIS_REPLY_* */ |
||||
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */ |
||||
size_t len; /* Length of string */ |
||||
char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */ |
||||
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */ |
||||
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */ |
||||
} redisReply; |
||||
|
||||
redisReader *redisReaderCreate(void); |
||||
|
||||
/* Function to free the reply objects hiredis returns by default. */ |
||||
void freeReplyObject(void *reply); |
||||
|
||||
/* Functions to format a command according to the protocol. */ |
||||
int redisvFormatCommand(char **target, const char *format, va_list ap); |
||||
int redisFormatCommand(char **target, const char *format, ...); |
||||
int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen); |
||||
int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen); |
||||
void redisFreeCommand(char *cmd); |
||||
void redisFreeSdsCommand(sds cmd); |
||||
|
||||
enum redisConnectionType { |
||||
REDIS_CONN_TCP, |
||||
REDIS_CONN_UNIX |
||||
}; |
||||
|
||||
/* Context for a connection to Redis */ |
||||
typedef struct redisContext { |
||||
int err; /* Error flags, 0 when there is no error */ |
||||
char errstr[128]; /* String representation of error when applicable */ |
||||
int fd; |
||||
int flags; |
||||
char *obuf; /* Write buffer */ |
||||
redisReader *reader; /* Protocol reader */ |
||||
|
||||
enum redisConnectionType connection_type; |
||||
struct timeval *timeout; |
||||
|
||||
struct { |
||||
char *host; |
||||
char *source_addr; |
||||
int port; |
||||
} tcp; |
||||
|
||||
struct { |
||||
char *path; |
||||
} unix_sock; |
||||
|
||||
} redisContext; |
||||
|
||||
redisContext *redisConnect(const char *ip, int port); |
||||
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv); |
||||
redisContext *redisConnectNonBlock(const char *ip, int port); |
||||
redisContext *redisConnectBindNonBlock(const char *ip, int port, |
||||
const char *source_addr); |
||||
redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, |
||||
const char *source_addr); |
||||
redisContext *redisConnectUnix(const char *path); |
||||
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv); |
||||
redisContext *redisConnectUnixNonBlock(const char *path); |
||||
redisContext *redisConnectFd(int fd); |
||||
|
||||
/**
|
||||
* Reconnect the given context using the saved information. |
||||
* |
||||
* This re-uses the exact same connect options as in the initial connection. |
||||
* host, ip (or path), timeout and bind address are reused, |
||||
* flags are used unmodified from the existing context. |
||||
* |
||||
* Returns REDIS_OK on successful connect or REDIS_ERR otherwise. |
||||
*/ |
||||
int redisReconnect(redisContext *c); |
||||
|
||||
int redisSetTimeout(redisContext *c, const struct timeval tv); |
||||
int redisEnableKeepAlive(redisContext *c); |
||||
void redisFree(redisContext *c); |
||||
int redisFreeKeepFd(redisContext *c); |
||||
int redisBufferRead(redisContext *c); |
||||
int redisBufferWrite(redisContext *c, int *done); |
||||
|
||||
/* In a blocking context, this function first checks if there are unconsumed
|
||||
* replies to return and returns one if so. Otherwise, it flushes the output |
||||
* buffer to the socket and reads until it has a reply. In a non-blocking |
||||
* context, it will return unconsumed replies until there are no more. */ |
||||
int redisGetReply(redisContext *c, void **reply); |
||||
int redisGetReplyFromReader(redisContext *c, void **reply); |
||||
|
||||
/* Write a formatted command to the output buffer. Use these functions in blocking mode
|
||||
* to get a pipeline of commands. */ |
||||
int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len); |
||||
|
||||
/* Write a command to the output buffer. Use these functions in blocking mode
|
||||
* to get a pipeline of commands. */ |
||||
int redisvAppendCommand(redisContext *c, const char *format, va_list ap); |
||||
int redisAppendCommand(redisContext *c, const char *format, ...); |
||||
int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); |
||||
|
||||
/* Issue a command to Redis. In a blocking context, it is identical to calling
|
||||
* redisAppendCommand, followed by redisGetReply. The function will return |
||||
* NULL if there was an error in performing the request, otherwise it will |
||||
* return the reply. In a non-blocking context, it is identical to calling |
||||
* only redisAppendCommand and will always return NULL. */ |
||||
void *redisvCommand(redisContext *c, const char *format, va_list ap); |
||||
void *redisCommand(redisContext *c, const char *format, ...); |
||||
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif |
||||
@ -0,0 +1,273 @@
|
||||
/* SDSLib 2.0 -- A C dynamic strings library
|
||||
* |
||||
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com> |
||||
* Copyright (c) 2015, Oran Agra |
||||
* Copyright (c) 2015, Redis Labs, Inc |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright notice, |
||||
* this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* * Neither the name of Redis nor the names of its contributors may be used |
||||
* to endorse or promote products derived from this software without |
||||
* specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
* POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
#ifndef __SDS_H |
||||
#define __SDS_H |
||||
|
||||
#define SDS_MAX_PREALLOC (1024*1024) |
||||
|
||||
#include <sys/types.h> |
||||
#include <stdarg.h> |
||||
#include <stdint.h> |
||||
|
||||
typedef char *sds; |
||||
|
||||
/* Note: sdshdr5 is never used, we just access the flags byte directly.
|
||||
* However is here to document the layout of type 5 SDS strings. */ |
||||
struct __attribute__ ((__packed__)) sdshdr5 { |
||||
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ |
||||
char buf[]; |
||||
}; |
||||
struct __attribute__ ((__packed__)) sdshdr8 { |
||||
uint8_t len; /* used */ |
||||
uint8_t alloc; /* excluding the header and null terminator */ |
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */ |
||||
char buf[]; |
||||
}; |
||||
struct __attribute__ ((__packed__)) sdshdr16 { |
||||
uint16_t len; /* used */ |
||||
uint16_t alloc; /* excluding the header and null terminator */ |
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */ |
||||
char buf[]; |
||||
}; |
||||
struct __attribute__ ((__packed__)) sdshdr32 { |
||||
uint32_t len; /* used */ |
||||
uint32_t alloc; /* excluding the header and null terminator */ |
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */ |
||||
char buf[]; |
||||
}; |
||||
struct __attribute__ ((__packed__)) sdshdr64 { |
||||
uint64_t len; /* used */ |
||||
uint64_t alloc; /* excluding the header and null terminator */ |
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */ |
||||
char buf[]; |
||||
}; |
||||
|
||||
#define SDS_TYPE_5 0 |
||||
#define SDS_TYPE_8 1 |
||||
#define SDS_TYPE_16 2 |
||||
#define SDS_TYPE_32 3 |
||||
#define SDS_TYPE_64 4 |
||||
#define SDS_TYPE_MASK 7 |
||||
#define SDS_TYPE_BITS 3 |
||||
#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))); |
||||
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)))) |
||||
#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS) |
||||
|
||||
static inline size_t sdslen(const sds s) { |
||||
unsigned char flags = s[-1]; |
||||
switch(flags&SDS_TYPE_MASK) { |
||||
case SDS_TYPE_5: |
||||
return SDS_TYPE_5_LEN(flags); |
||||
case SDS_TYPE_8: |
||||
return SDS_HDR(8,s)->len; |
||||
case SDS_TYPE_16: |
||||
return SDS_HDR(16,s)->len; |
||||
case SDS_TYPE_32: |
||||
return SDS_HDR(32,s)->len; |
||||
case SDS_TYPE_64: |
||||
return SDS_HDR(64,s)->len; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
static inline size_t sdsavail(const sds s) { |
||||
unsigned char flags = s[-1]; |
||||
switch(flags&SDS_TYPE_MASK) { |
||||
case SDS_TYPE_5: { |
||||
return 0; |
||||
} |
||||
case SDS_TYPE_8: { |
||||
SDS_HDR_VAR(8,s); |
||||
return sh->alloc - sh->len; |
||||
} |
||||
case SDS_TYPE_16: { |
||||
SDS_HDR_VAR(16,s); |
||||
return sh->alloc - sh->len; |
||||
} |
||||
case SDS_TYPE_32: { |
||||
SDS_HDR_VAR(32,s); |
||||
return sh->alloc - sh->len; |
||||
} |
||||
case SDS_TYPE_64: { |
||||
SDS_HDR_VAR(64,s); |
||||
return sh->alloc - sh->len; |
||||
} |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
static inline void sdssetlen(sds s, size_t newlen) { |
||||
unsigned char flags = s[-1]; |
||||
switch(flags&SDS_TYPE_MASK) { |
||||
case SDS_TYPE_5: |
||||
{ |
||||
unsigned char *fp = ((unsigned char*)s)-1; |
||||
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); |
||||
} |
||||
break; |
||||
case SDS_TYPE_8: |
||||
SDS_HDR(8,s)->len = newlen; |
||||
break; |
||||
case SDS_TYPE_16: |
||||
SDS_HDR(16,s)->len = newlen; |
||||
break; |
||||
case SDS_TYPE_32: |
||||
SDS_HDR(32,s)->len = newlen; |
||||
break; |
||||
case SDS_TYPE_64: |
||||
SDS_HDR(64,s)->len = newlen; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
static inline void sdsinclen(sds s, size_t inc) { |
||||
unsigned char flags = s[-1]; |
||||
switch(flags&SDS_TYPE_MASK) { |
||||
case SDS_TYPE_5: |
||||
{ |
||||
unsigned char *fp = ((unsigned char*)s)-1; |
||||
unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc; |
||||
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); |
||||
} |
||||
break; |
||||
case SDS_TYPE_8: |
||||
SDS_HDR(8,s)->len += inc; |
||||
break; |
||||
case SDS_TYPE_16: |
||||
SDS_HDR(16,s)->len += inc; |
||||
break; |
||||
case SDS_TYPE_32: |
||||
SDS_HDR(32,s)->len += inc; |
||||
break; |
||||
case SDS_TYPE_64: |
||||
SDS_HDR(64,s)->len += inc; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
/* sdsalloc() = sdsavail() + sdslen() */ |
||||
static inline size_t sdsalloc(const sds s) { |
||||
unsigned char flags = s[-1]; |
||||
switch(flags&SDS_TYPE_MASK) { |
||||
case SDS_TYPE_5: |
||||
return SDS_TYPE_5_LEN(flags); |
||||
case SDS_TYPE_8: |
||||
return SDS_HDR(8,s)->alloc; |
||||
case SDS_TYPE_16: |
||||
return SDS_HDR(16,s)->alloc; |
||||
case SDS_TYPE_32: |
||||
return SDS_HDR(32,s)->alloc; |
||||
case SDS_TYPE_64: |
||||
return SDS_HDR(64,s)->alloc; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
static inline void sdssetalloc(sds s, size_t newlen) { |
||||
unsigned char flags = s[-1]; |
||||
switch(flags&SDS_TYPE_MASK) { |
||||
case SDS_TYPE_5: |
||||
/* Nothing to do, this type has no total allocation info. */ |
||||
break; |
||||
case SDS_TYPE_8: |
||||
SDS_HDR(8,s)->alloc = newlen; |
||||
break; |
||||
case SDS_TYPE_16: |
||||
SDS_HDR(16,s)->alloc = newlen; |
||||
break; |
||||
case SDS_TYPE_32: |
||||
SDS_HDR(32,s)->alloc = newlen; |
||||
break; |
||||
case SDS_TYPE_64: |
||||
SDS_HDR(64,s)->alloc = newlen; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
sds sdsnewlen(const void *init, size_t initlen); |
||||
sds sdsnew(const char *init); |
||||
sds sdsempty(void); |
||||
sds sdsdup(const sds s); |
||||
void sdsfree(sds s); |
||||
sds sdsgrowzero(sds s, size_t len); |
||||
sds sdscatlen(sds s, const void *t, size_t len); |
||||
sds sdscat(sds s, const char *t); |
||||
sds sdscatsds(sds s, const sds t); |
||||
sds sdscpylen(sds s, const char *t, size_t len); |
||||
sds sdscpy(sds s, const char *t); |
||||
|
||||
sds sdscatvprintf(sds s, const char *fmt, va_list ap); |
||||
#ifdef __GNUC__ |
||||
sds sdscatprintf(sds s, const char *fmt, ...) |
||||
__attribute__((format(printf, 2, 3))); |
||||
#else |
||||
sds sdscatprintf(sds s, const char *fmt, ...); |
||||
#endif |
||||
|
||||
sds sdscatfmt(sds s, char const *fmt, ...); |
||||
sds sdstrim(sds s, const char *cset); |
||||
void sdsrange(sds s, int start, int end); |
||||
void sdsupdatelen(sds s); |
||||
void sdsclear(sds s); |
||||
int sdscmp(const sds s1, const sds s2); |
||||
sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); |
||||
void sdsfreesplitres(sds *tokens, int count); |
||||
void sdstolower(sds s); |
||||
void sdstoupper(sds s); |
||||
sds sdsfromlonglong(long long value); |
||||
sds sdscatrepr(sds s, const char *p, size_t len); |
||||
sds *sdssplitargs(const char *line, int *argc); |
||||
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); |
||||
sds sdsjoin(char **argv, int argc, char *sep); |
||||
sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen); |
||||
|
||||
/* Low level functions exposed to the user API */ |
||||
sds sdsMakeRoomFor(sds s, size_t addlen); |
||||
void sdsIncrLen(sds s, int incr); |
||||
sds sdsRemoveFreeSpace(sds s); |
||||
size_t sdsAllocSize(sds s); |
||||
void *sdsAllocPtr(sds s); |
||||
|
||||
/* Export the allocator used by SDS to the program using SDS.
|
||||
* Sometimes the program SDS is linked to, may use a different set of |
||||
* allocators, but may want to allocate or free things that SDS will |
||||
* respectively free or allocate. */ |
||||
void *sds_malloc(size_t size); |
||||
void *sds_realloc(void *ptr, size_t size); |
||||
void sds_free(void *ptr); |
||||
|
||||
#ifdef REDIS_TEST |
||||
int sdsTest(int argc, char *argv[]); |
||||
#endif |
||||
|
||||
#endif |
||||
@ -0,0 +1,42 @@
|
||||
/* SDSLib 2.0 -- A C dynamic strings library
|
||||
* |
||||
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com> |
||||
* Copyright (c) 2015, Oran Agra |
||||
* Copyright (c) 2015, Redis Labs, Inc |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright notice, |
||||
* this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* * Neither the name of Redis nor the names of its contributors may be used |
||||
* to endorse or promote products derived from this software without |
||||
* specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
* POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
/* SDS allocator selection.
|
||||
* |
||||
* This file is used in order to change the SDS allocator at compile time. |
||||
* Just define the following defines to what you want to use. Also add |
||||
* the include of your alternate allocator if needed (not needed in order |
||||
* to use the default libc allocator). */ |
||||
|
||||
#define s_malloc malloc |
||||
#define s_realloc realloc |
||||
#define s_free free |
||||
Binary file not shown.
Binary file not shown.
@ -0,0 +1,49 @@
|
||||
/* Extracted from anet.c to work properly with Hiredis error reporting.
|
||||
* |
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com> |
||||
* Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com> |
||||
* Copyright (c) 2015, Matt Stancliff <matt at genges dot com>, |
||||
* Jan-Erik Rediger <janerik at fnordig dot com> |
||||
* |
||||
* 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 Redis nor the names of its contributors may be used |
||||
* to endorse or promote products derived from this software without |
||||
* specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
* POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
#ifndef __NET_H |
||||
#define __NET_H |
||||
|
||||
#include "hiredis.h" |
||||
|
||||
int redisCheckSocketError(redisContext *c); |
||||
int redisContextSetTimeout(redisContext *c, const struct timeval tv); |
||||
int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout); |
||||
int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, |
||||
const struct timeval *timeout, |
||||
const char *source_addr); |
||||
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout); |
||||
int redisKeepAlive(redisContext *c, int interval); |
||||
|
||||
#endif |
||||
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com> |
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com> |
||||
* |
||||
* 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 Redis nor the names of its contributors may be used |
||||
* to endorse or promote products derived from this software without |
||||
* specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
* POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
|
||||
#ifndef __HIREDIS_READ_H |
||||
#define __HIREDIS_READ_H |
||||
#include <stdio.h> /* for size_t */ |
||||
|
||||
#define REDIS_ERR -1 |
||||
#define REDIS_OK 0 |
||||
|
||||
/* When an error occurs, the err flag in a context is set to hold the type of
|
||||
* error that occurred. REDIS_ERR_IO means there was an I/O error and you |
||||
* should use the "errno" variable to find out what is wrong. |
||||
* For other values, the "errstr" field will hold a description. */ |
||||
#define REDIS_ERR_IO 1 /* Error in read or write */ |
||||
#define REDIS_ERR_EOF 3 /* End of file */ |
||||
#define REDIS_ERR_PROTOCOL 4 /* Protocol error */ |
||||
#define REDIS_ERR_OOM 5 /* Out of memory */ |
||||
#define REDIS_ERR_OTHER 2 /* Everything else... */ |
||||
|
||||
#define REDIS_REPLY_STRING 1 |
||||
#define REDIS_REPLY_ARRAY 2 |
||||
#define REDIS_REPLY_INTEGER 3 |
||||
#define REDIS_REPLY_NIL 4 |
||||
#define REDIS_REPLY_STATUS 5 |
||||
#define REDIS_REPLY_ERROR 6 |
||||
|
||||
#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */ |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
typedef struct redisReadTask { |
||||
int type; |
||||
int elements; /* number of elements in multibulk container */ |
||||
int idx; /* index in parent (array) object */ |
||||
void *obj; /* holds user-generated value for a read task */ |
||||
struct redisReadTask *parent; /* parent task */ |
||||
void *privdata; /* user-settable arbitrary field */ |
||||
} redisReadTask; |
||||
|
||||
typedef struct redisReplyObjectFunctions { |
||||
void *(*createString)(const redisReadTask*, char*, size_t); |
||||
void *(*createArray)(const redisReadTask*, int); |
||||
void *(*createInteger)(const redisReadTask*, long long); |
||||
void *(*createNil)(const redisReadTask*); |
||||
void (*freeObject)(void*); |
||||
} redisReplyObjectFunctions; |
||||
|
||||
typedef struct redisReader { |
||||
int err; /* Error flags, 0 when there is no error */ |
||||
char errstr[128]; /* String representation of error when applicable */ |
||||
|
||||
char *buf; /* Read buffer */ |
||||
size_t pos; /* Buffer cursor */ |
||||
size_t len; /* Buffer length */ |
||||
size_t maxbuf; /* Max length of unused buffer */ |
||||
|
||||
redisReadTask rstack[9]; |
||||
int ridx; /* Index of current read task */ |
||||
void *reply; /* Temporary reply pointer */ |
||||
|
||||
redisReplyObjectFunctions *fn; |
||||
void *privdata; |
||||
} redisReader; |
||||
|
||||
/* Public API for the protocol parser. */ |
||||
redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn); |
||||
void redisReaderFree(redisReader *r); |
||||
int redisReaderFeed(redisReader *r, const char *buf, size_t len); |
||||
int redisReaderGetReply(redisReader *r, void **reply); |
||||
|
||||
#define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p)) |
||||
#define redisReaderGetObject(_r) (((redisReader*)(_r))->reply) |
||||
#define redisReaderGetError(_r) (((redisReader*)(_r))->errstr) |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif |
||||
@ -0,0 +1,273 @@
|
||||
/* SDSLib 2.0 -- A C dynamic strings library
|
||||
* |
||||
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com> |
||||
* Copyright (c) 2015, Oran Agra |
||||
* Copyright (c) 2015, Redis Labs, Inc |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright notice, |
||||
* this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* * Neither the name of Redis nor the names of its contributors may be used |
||||
* to endorse or promote products derived from this software without |
||||
* specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
* POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
#ifndef __SDS_H |
||||
#define __SDS_H |
||||
|
||||
#define SDS_MAX_PREALLOC (1024*1024) |
||||
|
||||
#include <sys/types.h> |
||||
#include <stdarg.h> |
||||
#include <stdint.h> |
||||
|
||||
typedef char *sds; |
||||
|
||||
/* Note: sdshdr5 is never used, we just access the flags byte directly.
|
||||
* However is here to document the layout of type 5 SDS strings. */ |
||||
struct __attribute__ ((__packed__)) sdshdr5 { |
||||
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ |
||||
char buf[]; |
||||
}; |
||||
struct __attribute__ ((__packed__)) sdshdr8 { |
||||
uint8_t len; /* used */ |
||||
uint8_t alloc; /* excluding the header and null terminator */ |
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */ |
||||
char buf[]; |
||||
}; |
||||
struct __attribute__ ((__packed__)) sdshdr16 { |
||||
uint16_t len; /* used */ |
||||
uint16_t alloc; /* excluding the header and null terminator */ |
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */ |
||||
char buf[]; |
||||
}; |
||||
struct __attribute__ ((__packed__)) sdshdr32 { |
||||
uint32_t len; /* used */ |
||||
uint32_t alloc; /* excluding the header and null terminator */ |
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */ |
||||
char buf[]; |
||||
}; |
||||
struct __attribute__ ((__packed__)) sdshdr64 { |
||||
uint64_t len; /* used */ |
||||
uint64_t alloc; /* excluding the header and null terminator */ |
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */ |
||||
char buf[]; |
||||
}; |
||||
|
||||
#define SDS_TYPE_5 0 |
||||
#define SDS_TYPE_8 1 |
||||
#define SDS_TYPE_16 2 |
||||
#define SDS_TYPE_32 3 |
||||
#define SDS_TYPE_64 4 |
||||
#define SDS_TYPE_MASK 7 |
||||
#define SDS_TYPE_BITS 3 |
||||
#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))); |
||||
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)))) |
||||
#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS) |
||||
|
||||
static inline size_t sdslen(const sds s) { |
||||
unsigned char flags = s[-1]; |
||||
switch(flags&SDS_TYPE_MASK) { |
||||
case SDS_TYPE_5: |
||||
return SDS_TYPE_5_LEN(flags); |
||||
case SDS_TYPE_8: |
||||
return SDS_HDR(8,s)->len; |
||||
case SDS_TYPE_16: |
||||
return SDS_HDR(16,s)->len; |
||||
case SDS_TYPE_32: |
||||
return SDS_HDR(32,s)->len; |
||||
case SDS_TYPE_64: |
||||
return SDS_HDR(64,s)->len; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
static inline size_t sdsavail(const sds s) { |
||||
unsigned char flags = s[-1]; |
||||
switch(flags&SDS_TYPE_MASK) { |
||||
case SDS_TYPE_5: { |
||||
return 0; |
||||
} |
||||
case SDS_TYPE_8: { |
||||
SDS_HDR_VAR(8,s); |
||||
return sh->alloc - sh->len; |
||||
} |
||||
case SDS_TYPE_16: { |
||||
SDS_HDR_VAR(16,s); |
||||
return sh->alloc - sh->len; |
||||
} |
||||
case SDS_TYPE_32: { |
||||
SDS_HDR_VAR(32,s); |
||||
return sh->alloc - sh->len; |
||||
} |
||||
case SDS_TYPE_64: { |
||||
SDS_HDR_VAR(64,s); |
||||
return sh->alloc - sh->len; |
||||
} |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
static inline void sdssetlen(sds s, size_t newlen) { |
||||
unsigned char flags = s[-1]; |
||||
switch(flags&SDS_TYPE_MASK) { |
||||
case SDS_TYPE_5: |
||||
{ |
||||
unsigned char *fp = ((unsigned char*)s)-1; |
||||
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); |
||||
} |
||||
break; |
||||
case SDS_TYPE_8: |
||||
SDS_HDR(8,s)->len = newlen; |
||||
break; |
||||
case SDS_TYPE_16: |
||||
SDS_HDR(16,s)->len = newlen; |
||||
break; |
||||
case SDS_TYPE_32: |
||||
SDS_HDR(32,s)->len = newlen; |
||||
break; |
||||
case SDS_TYPE_64: |
||||
SDS_HDR(64,s)->len = newlen; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
static inline void sdsinclen(sds s, size_t inc) { |
||||
unsigned char flags = s[-1]; |
||||
switch(flags&SDS_TYPE_MASK) { |
||||
case SDS_TYPE_5: |
||||
{ |
||||
unsigned char *fp = ((unsigned char*)s)-1; |
||||
unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc; |
||||
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); |
||||
} |
||||
break; |
||||
case SDS_TYPE_8: |
||||
SDS_HDR(8,s)->len += inc; |
||||
break; |
||||
case SDS_TYPE_16: |
||||
SDS_HDR(16,s)->len += inc; |
||||
break; |
||||
case SDS_TYPE_32: |
||||
SDS_HDR(32,s)->len += inc; |
||||
break; |
||||
case SDS_TYPE_64: |
||||
SDS_HDR(64,s)->len += inc; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
/* sdsalloc() = sdsavail() + sdslen() */ |
||||
static inline size_t sdsalloc(const sds s) { |
||||
unsigned char flags = s[-1]; |
||||
switch(flags&SDS_TYPE_MASK) { |
||||
case SDS_TYPE_5: |
||||
return SDS_TYPE_5_LEN(flags); |
||||
case SDS_TYPE_8: |
||||
return SDS_HDR(8,s)->alloc; |
||||
case SDS_TYPE_16: |
||||
return SDS_HDR(16,s)->alloc; |
||||
case SDS_TYPE_32: |
||||
return SDS_HDR(32,s)->alloc; |
||||
case SDS_TYPE_64: |
||||
return SDS_HDR(64,s)->alloc; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
static inline void sdssetalloc(sds s, size_t newlen) { |
||||
unsigned char flags = s[-1]; |
||||
switch(flags&SDS_TYPE_MASK) { |
||||
case SDS_TYPE_5: |
||||
/* Nothing to do, this type has no total allocation info. */ |
||||
break; |
||||
case SDS_TYPE_8: |
||||
SDS_HDR(8,s)->alloc = newlen; |
||||
break; |
||||
case SDS_TYPE_16: |
||||
SDS_HDR(16,s)->alloc = newlen; |
||||
break; |
||||
case SDS_TYPE_32: |
||||
SDS_HDR(32,s)->alloc = newlen; |
||||
break; |
||||
case SDS_TYPE_64: |
||||
SDS_HDR(64,s)->alloc = newlen; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
sds sdsnewlen(const void *init, size_t initlen); |
||||
sds sdsnew(const char *init); |
||||
sds sdsempty(void); |
||||
sds sdsdup(const sds s); |
||||
void sdsfree(sds s); |
||||
sds sdsgrowzero(sds s, size_t len); |
||||
sds sdscatlen(sds s, const void *t, size_t len); |
||||
sds sdscat(sds s, const char *t); |
||||
sds sdscatsds(sds s, const sds t); |
||||
sds sdscpylen(sds s, const char *t, size_t len); |
||||
sds sdscpy(sds s, const char *t); |
||||
|
||||
sds sdscatvprintf(sds s, const char *fmt, va_list ap); |
||||
#ifdef __GNUC__ |
||||
sds sdscatprintf(sds s, const char *fmt, ...) |
||||
__attribute__((format(printf, 2, 3))); |
||||
#else |
||||
sds sdscatprintf(sds s, const char *fmt, ...); |
||||
#endif |
||||
|
||||
sds sdscatfmt(sds s, char const *fmt, ...); |
||||
sds sdstrim(sds s, const char *cset); |
||||
void sdsrange(sds s, int start, int end); |
||||
void sdsupdatelen(sds s); |
||||
void sdsclear(sds s); |
||||
int sdscmp(const sds s1, const sds s2); |
||||
sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); |
||||
void sdsfreesplitres(sds *tokens, int count); |
||||
void sdstolower(sds s); |
||||
void sdstoupper(sds s); |
||||
sds sdsfromlonglong(long long value); |
||||
sds sdscatrepr(sds s, const char *p, size_t len); |
||||
sds *sdssplitargs(const char *line, int *argc); |
||||
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); |
||||
sds sdsjoin(char **argv, int argc, char *sep); |
||||
sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen); |
||||
|
||||
/* Low level functions exposed to the user API */ |
||||
sds sdsMakeRoomFor(sds s, size_t addlen); |
||||
void sdsIncrLen(sds s, int incr); |
||||
sds sdsRemoveFreeSpace(sds s); |
||||
size_t sdsAllocSize(sds s); |
||||
void *sdsAllocPtr(sds s); |
||||
|
||||
/* Export the allocator used by SDS to the program using SDS.
|
||||
* Sometimes the program SDS is linked to, may use a different set of |
||||
* allocators, but may want to allocate or free things that SDS will |
||||
* respectively free or allocate. */ |
||||
void *sds_malloc(size_t size); |
||||
void *sds_realloc(void *ptr, size_t size); |
||||
void sds_free(void *ptr); |
||||
|
||||
#ifdef REDIS_TEST |
||||
int sdsTest(int argc, char *argv[]); |
||||
#endif |
||||
|
||||
#endif |
||||
@ -0,0 +1,42 @@
|
||||
/* SDSLib 2.0 -- A C dynamic strings library
|
||||
* |
||||
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com> |
||||
* Copyright (c) 2015, Oran Agra |
||||
* Copyright (c) 2015, Redis Labs, Inc |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright notice, |
||||
* this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* * Neither the name of Redis nor the names of its contributors may be used |
||||
* to endorse or promote products derived from this software without |
||||
* specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
* POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
/* SDS allocator selection.
|
||||
* |
||||
* This file is used in order to change the SDS allocator at compile time. |
||||
* Just define the following defines to what you want to use. Also add |
||||
* the include of your alternate allocator if needed (not needed in order |
||||
* to use the default libc allocator). */ |
||||
|
||||
#define s_malloc malloc |
||||
#define s_realloc realloc |
||||
#define s_free free |
||||
@ -0,0 +1,42 @@
|
||||
#ifndef _WIN32_HELPER_INCLUDE |
||||
#define _WIN32_HELPER_INCLUDE |
||||
#ifdef _MSC_VER |
||||
|
||||
#ifndef inline |
||||
#define inline __inline |
||||
#endif |
||||
|
||||
#ifndef va_copy |
||||
#define va_copy(d,s) ((d) = (s)) |
||||
#endif |
||||
|
||||
#ifndef snprintf |
||||
#define snprintf c99_snprintf |
||||
|
||||
__inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) |
||||
{ |
||||
int count = -1; |
||||
|
||||
if (size != 0) |
||||
count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); |
||||
if (count == -1) |
||||
count = _vscprintf(format, ap); |
||||
|
||||
return count; |
||||
} |
||||
|
||||
__inline int c99_snprintf(char* str, size_t size, const char* format, ...) |
||||
{ |
||||
int count; |
||||
va_list ap; |
||||
|
||||
va_start(ap, format); |
||||
count = c99_vsnprintf(str, size, format, ap); |
||||
va_end(ap); |
||||
|
||||
return count; |
||||
} |
||||
#endif |
||||
|
||||
#endif |
||||
#endif |
||||
@ -1,16 +0,0 @@
|
||||
language: c |
||||
compiler: |
||||
- gcc |
||||
- clang |
||||
|
||||
env: |
||||
- CFLAGS="-Werror" |
||||
- PRE="valgrind --track-origins=yes --leak-check=full" |
||||
- TARGET="32bit" TARGET_VARS="32bit-vars" CFLAGS="-Werror" |
||||
- TARGET="32bit" TARGET_VARS="32bit-vars" PRE="valgrind --track-origins=yes --leak-check=full" |
||||
|
||||
install: |
||||
- sudo apt-get update -qq |
||||
- sudo apt-get install libc6-dbg libc6-dev libc6-i686:i386 libc6-dev-i386 libc6-dbg:i386 valgrind -y |
||||
|
||||
script: make $TARGET CFLAGS="$CFLAGS" && make check PRE="$PRE" && make $TARGET_VARS hiredis-example |
||||
@ -1,16 +0,0 @@
|
||||
### 0.3.0 - Dec 07, 2016 |
||||
|
||||
* Support redisClustervCommand, redisClustervAppendCommand and redisClustervAsyncCommand api. (deep011) |
||||
* Add flags HIRCLUSTER_FLAG_ADD_OPENSLOT and HIRCLUSTER_FLAG_ROUTE_USE_SLOTS. (deep011) |
||||
* Support redisClusterCommandArgv related api. (deep011) |
||||
* Fix some serious bugs. (deep011) |
||||
|
||||
### 0.2.1 - Nov 24, 2015 |
||||
|
||||
This release support redis cluster api. |
||||
|
||||
* Add hiredis 0.3.1. (deep011) |
||||
* Support cluster synchronous API. (deep011) |
||||
* Support multi-key command(mget/mset/del) for redis cluster. (deep011) |
||||
* Support cluster pipelining. (deep011) |
||||
* Support cluster asynchronous API. (deep011) |
||||
@ -1,255 +0,0 @@
|
||||
|
||||
# HIREDIS-VIP |
||||
|
||||
Hiredis-vip is a C client library for the [Redis](http://redis.io/) database. |
||||
|
||||
Hiredis-vip supported redis cluster. |
||||
|
||||
Hiredis-vip fully contained and based on [Hiredis](https://github.com/redis/hiredis) . |
||||
|
||||
## CLUSTER SUPPORT |
||||
|
||||
### FEATURES: |
||||
|
||||
* **`SUPPORT REDIS CLUSTER`**: |
||||
* Connect to redis cluster and run commands. |
||||
|
||||
* **`SUPPORT MULTI-KEY COMMAND`**: |
||||
* Support `MSET`, `MGET` and `DEL`. |
||||
|
||||
* **`SUPPORT PIPELING`**: |
||||
* Support redis pipeline and can contain multi-key command like above. |
||||
|
||||
* **`SUPPORT Asynchronous API`**: |
||||
* User can run commands with asynchronous mode. |
||||
|
||||
### CLUSTER API: |
||||
|
||||
```c |
||||
redisClusterContext *redisClusterConnect(const char *addrs, int flags); |
||||
redisClusterContext *redisClusterConnectWithTimeout(const char *addrs, const struct timeval tv, int flags); |
||||
redisClusterContext *redisClusterConnectNonBlock(const char *addrs, int flags); |
||||
void redisClusterFree(redisClusterContext *cc); |
||||
void redisClusterSetMaxRedirect(redisClusterContext *cc, int max_redirect_count); |
||||
void *redisClusterFormattedCommand(redisClusterContext *cc, char *cmd, int len); |
||||
void *redisClustervCommand(redisClusterContext *cc, const char *format, va_list ap); |
||||
void *redisClusterCommand(redisClusterContext *cc, const char *format, ...); |
||||
void *redisClusterCommandArgv(redisClusterContext *cc, int argc, const char **argv, const size_t *argvlen); |
||||
redisContext *ctx_get_by_node(struct cluster_node *node, const struct timeval *timeout, int flags); |
||||
int redisClusterAppendFormattedCommand(redisClusterContext *cc, char *cmd, int len); |
||||
int redisClustervAppendCommand(redisClusterContext *cc, const char *format, va_list ap); |
||||
int redisClusterAppendCommand(redisClusterContext *cc, const char *format, ...); |
||||
int redisClusterAppendCommandArgv(redisClusterContext *cc, int argc, const char **argv, const size_t *argvlen); |
||||
int redisClusterGetReply(redisClusterContext *cc, void **reply); |
||||
void redisClusterReset(redisClusterContext *cc); |
||||
|
||||
redisClusterAsyncContext *redisClusterAsyncConnect(const char *addrs, int flags); |
||||
int redisClusterAsyncSetConnectCallback(redisClusterAsyncContext *acc, redisConnectCallback *fn); |
||||
int redisClusterAsyncSetDisconnectCallback(redisClusterAsyncContext *acc, redisDisconnectCallback *fn); |
||||
int redisClusterAsyncFormattedCommand(redisClusterAsyncContext *acc, redisClusterCallbackFn *fn, void *privdata, char *cmd, int len); |
||||
int redisClustervAsyncCommand(redisClusterAsyncContext *acc, redisClusterCallbackFn *fn, void *privdata, const char *format, va_list ap); |
||||
int redisClusterAsyncCommand(redisClusterAsyncContext *acc, redisClusterCallbackFn *fn, void *privdata, const char *format, ...); |
||||
int redisClusterAsyncCommandArgv(redisClusterAsyncContext *acc, redisClusterCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen); |
||||
|
||||
void redisClusterAsyncDisconnect(redisClusterAsyncContext *acc); |
||||
void redisClusterAsyncFree(redisClusterAsyncContext *acc); |
||||
``` |
||||
|
||||
## Quick usage |
||||
|
||||
If you want used but not read the follow, please reference the examples: |
||||
https://github.com/vipshop/hiredis-vip/wiki |
||||
|
||||
## Cluster synchronous API |
||||
|
||||
To consume the synchronous API, there are only a few function calls that need to be introduced: |
||||
|
||||
```c |
||||
redisClusterContext *redisClusterConnect(const char *addrs, int flags); |
||||
void redisClusterSetMaxRedirect(redisClusterContext *cc, int max_redirect_count); |
||||
void *redisClusterCommand(redisClusterContext *cc, const char *format, ...); |
||||
void redisClusterFree(redisClusterContext *cc); |
||||
``` |
||||
|
||||
### Cluster connecting |
||||
|
||||
The function `redisClusterConnect` is used to create a so-called `redisClusterContext`. The |
||||
context is where Hiredis-vip Cluster holds state for connections. The `redisClusterContext` |
||||
struct has an integer `err` field that is non-zero when the connection is in |
||||
an error state. The field `errstr` will contain a string with a description of |
||||
the error. |
||||
After trying to connect to Redis using `redisClusterContext` you should |
||||
check the `err` field to see if establishing the connection was successful: |
||||
```c |
||||
redisClusterContext *cc = redisClusterConnect("127.0.0.1:6379", HIRCLUSTER_FLAG_NULL); |
||||
if (cc != NULL && cc->err) { |
||||
printf("Error: %s\n", cc->errstr); |
||||
// handle error |
||||
} |
||||
``` |
||||
|
||||
### Cluster sending commands |
||||
|
||||
The next that will be introduced is `redisClusterCommand`. |
||||
This function takes a format similar to printf. In the simplest form, |
||||
it is used like this: |
||||
```c |
||||
reply = redisClusterCommand(clustercontext, "SET foo bar"); |
||||
``` |
||||
|
||||
The specifier `%s` interpolates a string in the command, and uses `strlen` to |
||||
determine the length of the string: |
||||
```c |
||||
reply = redisClusterCommand(clustercontext, "SET foo %s", value); |
||||
``` |
||||
Internally, Hiredis-vip splits the command in different arguments and will |
||||
convert it to the protocol used to communicate with Redis. |
||||
One or more spaces separates arguments, so you can use the specifiers |
||||
anywhere in an argument: |
||||
```c |
||||
reply = redisClusterCommand(clustercontext, "SET key:%s %s", myid, value); |
||||
``` |
||||
|
||||
### Cluster multi-key commands |
||||
|
||||
Hiredis-vip supports mget/mset/del multi-key commands. |
||||
Those multi-key commands is highly effective. |
||||
Millions of keys in one mget command just used several seconds. |
||||
|
||||
Example: |
||||
```c |
||||
reply = redisClusterCommand(clustercontext, "mget %s %s %s %s", key1, key2, key3, key4); |
||||
``` |
||||
|
||||
### Cluster cleaning up |
||||
|
||||
To disconnect and free the context the following function can be used: |
||||
```c |
||||
void redisClusterFree(redisClusterContext *cc); |
||||
``` |
||||
This function immediately closes the socket and then frees the allocations done in |
||||
creating the context. |
||||
|
||||
### Cluster pipelining |
||||
|
||||
The function `redisClusterGetReply` is exported as part of the Hiredis API and can be used |
||||
when a reply is expected on the socket. To pipeline commands, the only things that needs |
||||
to be done is filling up the output buffer. For this cause, two commands can be used that |
||||
are identical to the `redisClusterCommand` family, apart from not returning a reply: |
||||
```c |
||||
int redisClusterAppendCommand(redisClusterContext *cc, const char *format, ...); |
||||
int redisClusterAppendCommandArgv(redisClusterContext *cc, int argc, const char **argv); |
||||
``` |
||||
After calling either function one or more times, `redisClusterGetReply` can be used to receive the |
||||
subsequent replies. The return value for this function is either `REDIS_OK` or `REDIS_ERR`, where |
||||
the latter means an error occurred while reading a reply. Just as with the other commands, |
||||
the `err` field in the context can be used to find out what the cause of this error is. |
||||
```c |
||||
void redisClusterReset(redisClusterContext *cc); |
||||
``` |
||||
Warning: You must call `redisClusterReset` function after one pipelining anyway. |
||||
|
||||
The following examples shows a simple cluster pipeline: |
||||
```c |
||||
redisReply *reply; |
||||
redisClusterAppendCommand(clusterContext,"SET foo bar"); |
||||
redisClusterAppendCommand(clusterContext,"GET foo"); |
||||
redisClusterGetReply(clusterContext,&reply); // reply for SET |
||||
freeReplyObject(reply); |
||||
redisClusterGetReply(clusterContext,&reply); // reply for GET |
||||
freeReplyObject(reply); |
||||
redisClusterReset(clusterContext); |
||||
``` |
||||
|
||||
## Cluster asynchronous API |
||||
|
||||
Hiredis-vip comes with an cluster asynchronous API that works easily with any event library. |
||||
Now we just support and test for libevent and redis ae, if you need for other event libraries, |
||||
please contact with us, and we will support it quickly. |
||||
|
||||
### Connecting |
||||
|
||||
The function `redisAsyncConnect` can be used to establish a non-blocking connection to |
||||
Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The `err` field |
||||
should be checked after creation to see if there were errors creating the connection. |
||||
Because the connection that will be created is non-blocking, the kernel is not able to |
||||
instantly return if the specified host and port is able to accept a connection. |
||||
```c |
||||
redisClusterAsyncContext *acc = redisClusterAsyncConnect("127.0.0.1:6379", HIRCLUSTER_FLAG_NULL); |
||||
if (acc->err) { |
||||
printf("Error: %s\n", acc->errstr); |
||||
// handle error |
||||
} |
||||
``` |
||||
|
||||
The cluster asynchronous context can hold a disconnect callback function that is called when the |
||||
connection is disconnected (either because of an error or per user request). This function should |
||||
have the following prototype: |
||||
```c |
||||
void(const redisAsyncContext *c, int status); |
||||
``` |
||||
On a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the |
||||
user, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `err` |
||||
field in the context can be accessed to find out the cause of the error. |
||||
|
||||
You not need to reconnect in the disconnect callback, hiredis-vip will reconnect this connection itself |
||||
when commands come to this redis node. |
||||
|
||||
Setting the disconnect callback can only be done once per context. For subsequent calls it will |
||||
return `REDIS_ERR`. The function to set the disconnect callback has the following prototype: |
||||
```c |
||||
int redisClusterAsyncSetDisconnectCallback(redisClusterAsyncContext *acc, redisDisconnectCallback *fn); |
||||
``` |
||||
### Sending commands and their callbacks |
||||
|
||||
In an cluster asynchronous context, commands are automatically pipelined due to the nature of an event loop. |
||||
Therefore, unlike the cluster synchronous API, there is only a single way to send commands. |
||||
Because commands are sent to Redis cluster asynchronously, issuing a command requires a callback function |
||||
that is called when the reply is received. Reply callbacks should have the following prototype: |
||||
```c |
||||
void(redisClusterAsyncContext *acc, void *reply, void *privdata); |
||||
``` |
||||
The `privdata` argument can be used to curry arbitrary data to the callback from the point where |
||||
the command is initially queued for execution. |
||||
|
||||
The functions that can be used to issue commands in an asynchronous context are: |
||||
```c |
||||
int redisClusterAsyncCommand( |
||||
redisClusterAsyncContext *acc, |
||||
redisClusterCallbackFn *fn, |
||||
void *privdata, const char *format, ...); |
||||
``` |
||||
This function work like their blocking counterparts. The return value is `REDIS_OK` when the command |
||||
was successfully added to the output buffer and `REDIS_ERR` otherwise. Example: when the connection |
||||
is being disconnected per user-request, no new commands may be added to the output buffer and `REDIS_ERR` is |
||||
returned on calls to the `redisClusterAsyncCommand` family. |
||||
|
||||
If the reply for a command with a `NULL` callback is read, it is immediately freed. When the callback |
||||
for a command is non-`NULL`, the memory is freed immediately following the callback: the reply is only |
||||
valid for the duration of the callback. |
||||
|
||||
All pending callbacks are called with a `NULL` reply when the context encountered an error. |
||||
|
||||
### Disconnecting |
||||
|
||||
An cluster asynchronous connection can be terminated using: |
||||
```c |
||||
void redisClusterAsyncDisconnect(redisClusterAsyncContext *acc); |
||||
``` |
||||
When this function is called, the connection is **not** immediately terminated. Instead, new |
||||
commands are no longer accepted and the connection is only terminated when all pending commands |
||||
have been written to the socket, their respective replies have been read and their respective |
||||
callbacks have been executed. After this, the disconnection callback is executed with the |
||||
`REDIS_OK` status and the context object is freed. |
||||
|
||||
### Hooking it up to event library *X* |
||||
|
||||
There are a few hooks that need to be set on the cluster context object after it is created. |
||||
See the `adapters/` directory for bindings to *ae* and *libevent*. |
||||
|
||||
## AUTHORS |
||||
|
||||
Hiredis-vip was maintained and used at vipshop(https://github.com/vipshop). |
||||
The redis client library part in hiredis-vip is same as hiredis(https://github.com/redis/hiredis). |
||||
The redis cluster client library part in hiredis-vip is written by deep(https://github.com/deep011). |
||||
Hiredis-vip is released under the BSD license. |
||||
@ -1,341 +0,0 @@
|
||||
/* adlist.c - A generic doubly linked list implementation
|
||||
* |
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com> |
||||
* 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 Redis nor the names of its contributors may be used |
||||
* to endorse or promote products derived from this software without |
||||
* specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
* POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
|
||||
#include <stdlib.h> |
||||
#include "adlist.h" |
||||
#include "hiutil.h" |
||||
|
||||
/* Create a new list. The created list can be freed with
|
||||
* AlFreeList(), but private value of every node need to be freed |
||||
* by the user before to call AlFreeList(). |
||||
* |
||||
* On error, NULL is returned. Otherwise the pointer to the new list. */ |
||||
hilist *listCreate(void) |
||||
{ |
||||
struct hilist *list; |
||||
|
||||
if ((list = hi_alloc(sizeof(*list))) == NULL) |
||||
return NULL; |
||||
list->head = list->tail = NULL; |
||||
list->len = 0; |
||||
list->dup = NULL; |
||||
list->free = NULL; |
||||
list->match = NULL; |
||||
return list; |
||||
} |
||||
|
||||
/* Free the whole list.
|
||||
* |
||||
* This function can't fail. */ |
||||
void listRelease(hilist *list) |
||||
{ |
||||
unsigned long len; |
||||
listNode *current, *next; |
||||
|
||||
current = list->head; |
||||
len = list->len; |
||||
while(len--) { |
||||
next = current->next; |
||||
if (list->free) list->free(current->value); |
||||
hi_free(current); |
||||
current = next; |
||||
} |
||||
hi_free(list); |
||||
} |
||||
|
||||
/* Add a new node to the list, to head, containing the specified 'value'
|
||||
* pointer as value. |
||||
* |
||||
* On error, NULL is returned and no operation is performed (i.e. the |
||||
* list remains unaltered). |
||||
* On success the 'list' pointer you pass to the function is returned. */ |
||||
hilist *listAddNodeHead(hilist *list, void *value) |
||||
{ |
||||
listNode *node; |
||||
|
||||
if ((node = hi_alloc(sizeof(*node))) == NULL) |
||||
return NULL; |
||||
node->value = value; |
||||
if (list->len == 0) { |
||||
list->head = list->tail = node; |
||||
node->prev = node->next = NULL; |
||||
} else { |
||||
node->prev = NULL; |
||||
node->next = list->head; |
||||
list->head->prev = node; |
||||
list->head = node; |
||||
} |
||||
list->len++; |
||||
return list; |
||||
} |
||||
|
||||
/* Add a new node to the list, to tail, containing the specified 'value'
|
||||
* pointer as value. |
||||
* |
||||
* On error, NULL is returned and no operation is performed (i.e. the |
||||
* list remains unaltered). |
||||
* On success the 'list' pointer you pass to the function is returned. */ |
||||
hilist *listAddNodeTail(hilist *list, void *value) |
||||
{ |
||||
listNode *node; |
||||
|
||||
if ((node = hi_alloc(sizeof(*node))) == NULL) |
||||
return NULL; |
||||
node->value = value; |
||||
if (list->len == 0) { |
||||
list->head = list->tail = node; |
||||
node->prev = node->next = NULL; |
||||
} else { |
||||
node->prev = list->tail; |
||||
node->next = NULL; |
||||
list->tail->next = node; |
||||
list->tail = node; |
||||
} |
||||
list->len++; |
||||
return list; |
||||
} |
||||
|
||||
hilist *listInsertNode(hilist *list, listNode *old_node, void *value, int after) { |
||||
listNode *node; |
||||
|
||||
if ((node = hi_alloc(sizeof(*node))) == NULL) |
||||
return NULL; |
||||
node->value = value; |
||||
if (after) { |
||||
node->prev = old_node; |
||||
node->next = old_node->next; |
||||
if (list->tail == old_node) { |
||||
list->tail = node; |
||||
} |
||||
} else { |
||||
node->next = old_node; |
||||
node->prev = old_node->prev; |
||||
if (list->head == old_node) { |
||||
list->head = node; |
||||
} |
||||
} |
||||
if (node->prev != NULL) { |
||||
node->prev->next = node; |
||||
} |
||||
if (node->next != NULL) { |
||||
node->next->prev = node; |
||||
} |
||||
list->len++; |
||||
return list; |
||||
} |
||||
|
||||
/* Remove the specified node from the specified list.
|
||||
* It's up to the caller to free the private value of the node. |
||||
* |
||||
* This function can't fail. */ |
||||
void listDelNode(hilist *list, listNode *node) |
||||
{ |
||||
if (node->prev) |
||||
node->prev->next = node->next; |
||||
else |
||||
list->head = node->next; |
||||
if (node->next) |
||||
node->next->prev = node->prev; |
||||
else |
||||
list->tail = node->prev; |
||||
if (list->free) list->free(node->value); |
||||
hi_free(node); |
||||
list->len--; |
||||
} |
||||
|
||||
/* Returns a list iterator 'iter'. After the initialization every
|
||||
* call to listNext() will return the next element of the list. |
||||
* |
||||
* This function can't fail. */ |
||||
listIter *listGetIterator(hilist *list, int direction) |
||||
{ |
||||
listIter *iter; |
||||
|
||||
if ((iter = hi_alloc(sizeof(*iter))) == NULL) return NULL; |
||||
if (direction == AL_START_HEAD) |
||||
iter->next = list->head; |
||||
else |
||||
iter->next = list->tail; |
||||
iter->direction = direction; |
||||
return iter; |
||||
} |
||||
|
||||
/* Release the iterator memory */ |
||||
void listReleaseIterator(listIter *iter) { |
||||
hi_free(iter); |
||||
} |
||||
|
||||
/* Create an iterator in the list private iterator structure */ |
||||
void listRewind(hilist *list, listIter *li) { |
||||
li->next = list->head; |
||||
li->direction = AL_START_HEAD; |
||||
} |
||||
|
||||
void listRewindTail(hilist *list, listIter *li) { |
||||
li->next = list->tail; |
||||
li->direction = AL_START_TAIL; |
||||
} |
||||
|
||||
/* Return the next element of an iterator.
|
||||
* It's valid to remove the currently returned element using |
||||
* listDelNode(), but not to remove other elements. |
||||
* |
||||
* The function returns a pointer to the next element of the list, |
||||
* or NULL if there are no more elements, so the classical usage patter |
||||
* is: |
||||
* |
||||
* iter = listGetIterator(list,<direction>); |
||||
* while ((node = listNext(iter)) != NULL) { |
||||
* doSomethingWith(listNodeValue(node)); |
||||
* } |
||||
* |
||||
* */ |
||||
listNode *listNext(listIter *iter) |
||||
{ |
||||
listNode *current = iter->next; |
||||
|
||||
if (current != NULL) { |
||||
if (iter->direction == AL_START_HEAD) |
||||
iter->next = current->next; |
||||
else |
||||
iter->next = current->prev; |
||||
} |
||||
return current; |
||||
} |
||||
|
||||
/* Duplicate the whole list. On out of memory NULL is returned.
|
||||
* On success a copy of the original list is returned. |
||||
* |
||||
* The 'Dup' method set with listSetDupMethod() function is used |
||||
* to copy the node value. Otherwise the same pointer value of |
||||
* the original node is used as value of the copied node. |
||||
* |
||||
* The original list both on success or error is never modified. */ |
||||
hilist *listDup(hilist *orig) |
||||
{ |
||||
hilist *copy; |
||||
listIter *iter; |
||||
listNode *node; |
||||
|
||||
if ((copy = listCreate()) == NULL) |
||||
return NULL; |
||||
copy->dup = orig->dup; |
||||
copy->free = orig->free; |
||||
copy->match = orig->match; |
||||
iter = listGetIterator(orig, AL_START_HEAD); |
||||
while((node = listNext(iter)) != NULL) { |
||||
void *value; |
||||
|
||||
if (copy->dup) { |
||||
value = copy->dup(node->value); |
||||
if (value == NULL) { |
||||
listRelease(copy); |
||||
listReleaseIterator(iter); |
||||
return NULL; |
||||
} |
||||
} else |
||||
value = node->value; |
||||
if (listAddNodeTail(copy, value) == NULL) { |
||||
listRelease(copy); |
||||
listReleaseIterator(iter); |
||||
return NULL; |
||||
} |
||||
} |
||||
listReleaseIterator(iter); |
||||
return copy; |
||||
} |
||||
|
||||
/* Search the list for a node matching a given key.
|
||||
* The match is performed using the 'match' method |
||||
* set with listSetMatchMethod(). If no 'match' method |
||||
* is set, the 'value' pointer of every node is directly |
||||
* compared with the 'key' pointer. |
||||
* |
||||
* On success the first matching node pointer is returned |
||||
* (search starts from head). If no matching node exists |
||||
* NULL is returned. */ |
||||
listNode *listSearchKey(hilist *list, void *key) |
||||
{ |
||||
listIter *iter; |
||||
listNode *node; |
||||
|
||||
iter = listGetIterator(list, AL_START_HEAD); |
||||
while((node = listNext(iter)) != NULL) { |
||||
if (list->match) { |
||||
if (list->match(node->value, key)) { |
||||
listReleaseIterator(iter); |
||||
return node; |
||||
} |
||||
} else { |
||||
if (key == node->value) { |
||||
listReleaseIterator(iter); |
||||
return node; |
||||
} |
||||
} |
||||
} |
||||
listReleaseIterator(iter); |
||||
return NULL; |
||||
} |
||||
|
||||
/* Return the element at the specified zero-based index
|
||||
* where 0 is the head, 1 is the element next to head |
||||
* and so on. Negative integers are used in order to count |
||||
* from the tail, -1 is the last element, -2 the penultimate |
||||
* and so on. If the index is out of range NULL is returned. */ |
||||
listNode *listIndex(hilist *list, long index) { |
||||
listNode *n; |
||||
|
||||
if (index < 0) { |
||||
index = (-index)-1; |
||||
n = list->tail; |
||||
while(index-- && n) n = n->prev; |
||||
} else { |
||||
n = list->head; |
||||
while(index-- && n) n = n->next; |
||||
} |
||||
return n; |
||||
} |
||||
|
||||
/* Rotate the list removing the tail node and inserting it to the head. */ |
||||
void listRotate(hilist *list) { |
||||
listNode *tail = list->tail; |
||||
|
||||
if (listLength(list) <= 1) return; |
||||
|
||||
/* Detach current tail */ |
||||
list->tail = tail->prev; |
||||
list->tail->next = NULL; |
||||
/* Move it as head */ |
||||
list->head->prev = tail; |
||||
tail->prev = NULL; |
||||
tail->next = list->head; |
||||
list->head = tail; |
||||
} |
||||
@ -1,93 +0,0 @@
|
||||
/* adlist.h - A generic doubly linked list implementation
|
||||
* |
||||
* Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com> |
||||
* 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 Redis nor the names of its contributors may be used |
||||
* to endorse or promote products derived from this software without |
||||
* specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
* POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
#ifndef __ADLIST_H__ |
||||
#define __ADLIST_H__ |
||||
|
||||
/* Node, List, and Iterator are the only data structures used currently. */ |
||||
|
||||
typedef struct listNode { |
||||
struct listNode *prev; |
||||
struct listNode *next; |
||||
void *value; |
||||
} listNode; |
||||
|
||||
typedef struct listIter { |
||||
listNode *next; |
||||
int direction; |
||||
} listIter; |
||||
|
||||
typedef struct hilist { |
||||
listNode *head; |
||||
listNode *tail; |
||||
void *(*dup)(void *ptr); |
||||
void (*free)(void *ptr); |
||||
int (*match)(void *ptr, void *key); |
||||
unsigned long len; |
||||
} hilist; |
||||
|
||||
/* Functions implemented as macros */ |
||||
#define listLength(l) ((l)->len) |
||||
#define listFirst(l) ((l)->head) |
||||
#define listLast(l) ((l)->tail) |
||||
#define listPrevNode(n) ((n)->prev) |
||||
#define listNextNode(n) ((n)->next) |
||||
#define listNodeValue(n) ((n)->value) |
||||
|
||||
#define listSetDupMethod(l,m) ((l)->dup = (m)) |
||||
#define listSetFreeMethod(l,m) ((l)->free = (m)) |
||||
#define listSetMatchMethod(l,m) ((l)->match = (m)) |
||||
|
||||
#define listGetDupMethod(l) ((l)->dup) |
||||
#define listGetFree(l) ((l)->free) |
||||
#define listGetMatchMethod(l) ((l)->match) |
||||
|
||||
/* Prototypes */ |
||||
hilist *listCreate(void); |
||||
void listRelease(hilist *list); |
||||
hilist *listAddNodeHead(hilist *list, void *value); |
||||
hilist *listAddNodeTail(hilist *list, void *value); |
||||
hilist *listInsertNode(hilist *list, listNode *old_node, void *value, int after); |
||||
void listDelNode(hilist *list, listNode *node); |
||||
listIter *listGetIterator(hilist *list, int direction); |
||||
listNode *listNext(listIter *iter); |
||||
void listReleaseIterator(listIter *iter); |
||||
hilist *listDup(hilist *orig); |
||||
listNode *listSearchKey(hilist *list, void *key); |
||||
listNode *listIndex(hilist *list, long index); |
||||
void listRewind(hilist *list, listIter *li); |
||||
void listRewindTail(hilist *list, listIter *li); |
||||
void listRotate(hilist *list); |
||||
|
||||
/* Directions for iterators */ |
||||
#define AL_START_HEAD 0 |
||||
#define AL_START_TAIL 1 |
||||
|
||||
#endif /* __ADLIST_H__ */ |
||||
File diff suppressed because it is too large
Load Diff
@ -1,179 +0,0 @@
|
||||
#ifndef __COMMAND_H_ |
||||
#define __COMMAND_H_ |
||||
|
||||
#include <stdint.h> |
||||
|
||||
#include "hiredis.h" |
||||
#include "adlist.h" |
||||
|
||||
typedef enum cmd_parse_result { |
||||
CMD_PARSE_OK, /* parsing ok */ |
||||
CMD_PARSE_ENOMEM, /* out of memory */ |
||||
CMD_PARSE_ERROR, /* parsing error */ |
||||
CMD_PARSE_REPAIR, /* more to parse -> repair parsed & unparsed data */ |
||||
CMD_PARSE_AGAIN, /* incomplete -> parse again */ |
||||
} cmd_parse_result_t; |
||||
|
||||
#define CMD_TYPE_CODEC(ACTION) \ |
||||
ACTION( UNKNOWN ) \
|
||||
ACTION( REQ_REDIS_DEL ) /* redis commands - keys */ \
|
||||
ACTION( REQ_REDIS_EXISTS ) \
|
||||
ACTION( REQ_REDIS_EXPIRE ) \
|
||||
ACTION( REQ_REDIS_EXPIREAT ) \
|
||||
ACTION( REQ_REDIS_PEXPIRE ) \
|
||||
ACTION( REQ_REDIS_PEXPIREAT ) \
|
||||
ACTION( REQ_REDIS_PERSIST ) \
|
||||
ACTION( REQ_REDIS_PTTL ) \
|
||||
ACTION( REQ_REDIS_SORT ) \
|
||||
ACTION( REQ_REDIS_TTL ) \
|
||||
ACTION( REQ_REDIS_TYPE ) \
|
||||
ACTION( REQ_REDIS_APPEND ) /* redis requests - string */ \
|
||||
ACTION( REQ_REDIS_BITCOUNT ) \
|
||||
ACTION( REQ_REDIS_DECR ) \
|
||||
ACTION( REQ_REDIS_DECRBY ) \
|
||||
ACTION( REQ_REDIS_DUMP ) \
|
||||
ACTION( REQ_REDIS_GET ) \
|
||||
ACTION( REQ_REDIS_GETBIT ) \
|
||||
ACTION( REQ_REDIS_GETRANGE ) \
|
||||
ACTION( REQ_REDIS_GETSET ) \
|
||||
ACTION( REQ_REDIS_INCR ) \
|
||||
ACTION( REQ_REDIS_INCRBY ) \
|
||||
ACTION( REQ_REDIS_INCRBYFLOAT ) \
|
||||
ACTION( REQ_REDIS_MGET ) \
|
||||
ACTION( REQ_REDIS_MSET ) \
|
||||
ACTION( REQ_REDIS_PSETEX ) \
|
||||
ACTION( REQ_REDIS_RESTORE ) \
|
||||
ACTION( REQ_REDIS_SET ) \
|
||||
ACTION( REQ_REDIS_SETBIT ) \
|
||||
ACTION( REQ_REDIS_SETEX ) \
|
||||
ACTION( REQ_REDIS_SETNX ) \
|
||||
ACTION( REQ_REDIS_SETRANGE ) \
|
||||
ACTION( REQ_REDIS_STRLEN ) \
|
||||
ACTION( REQ_REDIS_HDEL ) /* redis requests - hashes */ \
|
||||
ACTION( REQ_REDIS_HEXISTS ) \
|
||||
ACTION( REQ_REDIS_HGET ) \
|
||||
ACTION( REQ_REDIS_HGETALL ) \
|
||||
ACTION( REQ_REDIS_HINCRBY ) \
|
||||
ACTION( REQ_REDIS_HINCRBYFLOAT ) \
|
||||
ACTION( REQ_REDIS_HKEYS ) \
|
||||
ACTION( REQ_REDIS_HLEN ) \
|
||||
ACTION( REQ_REDIS_HMGET ) \
|
||||
ACTION( REQ_REDIS_HMSET ) \
|
||||
ACTION( REQ_REDIS_HSET ) \
|
||||
ACTION( REQ_REDIS_HSETNX ) \
|
||||
ACTION( REQ_REDIS_HSCAN) \
|
||||
ACTION( REQ_REDIS_HVALS ) \
|
||||
ACTION( REQ_REDIS_LINDEX ) /* redis requests - lists */ \
|
||||
ACTION( REQ_REDIS_LINSERT ) \
|
||||
ACTION( REQ_REDIS_LLEN ) \
|
||||
ACTION( REQ_REDIS_LPOP ) \
|
||||
ACTION( REQ_REDIS_LPUSH ) \
|
||||
ACTION( REQ_REDIS_LPUSHX ) \
|
||||
ACTION( REQ_REDIS_LRANGE ) \
|
||||
ACTION( REQ_REDIS_LREM ) \
|
||||
ACTION( REQ_REDIS_LSET ) \
|
||||
ACTION( REQ_REDIS_LTRIM ) \
|
||||
ACTION( REQ_REDIS_PFADD ) /* redis requests - hyperloglog */ \
|
||||
ACTION( REQ_REDIS_PFCOUNT ) \
|
||||
ACTION( REQ_REDIS_PFMERGE ) \
|
||||
ACTION( REQ_REDIS_RPOP ) \
|
||||
ACTION( REQ_REDIS_RPOPLPUSH ) \
|
||||
ACTION( REQ_REDIS_RPUSH ) \
|
||||
ACTION( REQ_REDIS_RPUSHX ) \
|
||||
ACTION( REQ_REDIS_SADD ) /* redis requests - sets */ \
|
||||
ACTION( REQ_REDIS_SCARD ) \
|
||||
ACTION( REQ_REDIS_SDIFF ) \
|
||||
ACTION( REQ_REDIS_SDIFFSTORE ) \
|
||||
ACTION( REQ_REDIS_SINTER ) \
|
||||
ACTION( REQ_REDIS_SINTERSTORE ) \
|
||||
ACTION( REQ_REDIS_SISMEMBER ) \
|
||||
ACTION( REQ_REDIS_SMEMBERS ) \
|
||||
ACTION( REQ_REDIS_SMOVE ) \
|
||||
ACTION( REQ_REDIS_SPOP ) \
|
||||
ACTION( REQ_REDIS_SRANDMEMBER ) \
|
||||
ACTION( REQ_REDIS_SREM ) \
|
||||
ACTION( REQ_REDIS_SUNION ) \
|
||||
ACTION( REQ_REDIS_SUNIONSTORE ) \
|
||||
ACTION( REQ_REDIS_SSCAN) \
|
||||
ACTION( REQ_REDIS_ZADD ) /* redis requests - sorted sets */ \
|
||||
ACTION( REQ_REDIS_ZCARD ) \
|
||||
ACTION( REQ_REDIS_ZCOUNT ) \
|
||||
ACTION( REQ_REDIS_ZINCRBY ) \
|
||||
ACTION( REQ_REDIS_ZINTERSTORE ) \
|
||||
ACTION( REQ_REDIS_ZLEXCOUNT ) \
|
||||
ACTION( REQ_REDIS_ZRANGE ) \
|
||||
ACTION( REQ_REDIS_ZRANGEBYLEX ) \
|
||||
ACTION( REQ_REDIS_ZRANGEBYSCORE ) \
|
||||
ACTION( REQ_REDIS_ZRANK ) \
|
||||
ACTION( REQ_REDIS_ZREM ) \
|
||||
ACTION( REQ_REDIS_ZREMRANGEBYRANK ) \
|
||||
ACTION( REQ_REDIS_ZREMRANGEBYLEX ) \
|
||||
ACTION( REQ_REDIS_ZREMRANGEBYSCORE ) \
|
||||
ACTION( REQ_REDIS_ZREVRANGE ) \
|
||||
ACTION( REQ_REDIS_ZREVRANGEBYSCORE ) \
|
||||
ACTION( REQ_REDIS_ZREVRANK ) \
|
||||
ACTION( REQ_REDIS_ZSCORE ) \
|
||||
ACTION( REQ_REDIS_ZUNIONSTORE ) \
|
||||
ACTION( REQ_REDIS_ZSCAN) \
|
||||
ACTION( REQ_REDIS_EVAL ) /* redis requests - eval */ \
|
||||
ACTION( REQ_REDIS_EVALSHA ) \
|
||||
ACTION( REQ_REDIS_PING ) /* redis requests - ping/quit */ \
|
||||
ACTION( REQ_REDIS_QUIT) \
|
||||
ACTION( REQ_REDIS_AUTH) \
|
||||
ACTION( RSP_REDIS_STATUS ) /* redis response */ \
|
||||
ACTION( RSP_REDIS_ERROR ) \
|
||||
ACTION( RSP_REDIS_INTEGER ) \
|
||||
ACTION( RSP_REDIS_BULK ) \
|
||||
ACTION( RSP_REDIS_MULTIBULK ) \
|
||||
ACTION( SENTINEL ) \
|
||||
|
||||
|
||||
#define DEFINE_ACTION(_name) CMD_##_name, |
||||
typedef enum cmd_type { |
||||
CMD_TYPE_CODEC(DEFINE_ACTION) |
||||
} cmd_type_t; |
||||
#undef DEFINE_ACTION |
||||
|
||||
|
||||
struct keypos { |
||||
char *start; /* key start pos */ |
||||
char *end; /* key end pos */ |
||||
uint32_t remain_len; /* remain length after keypos->end for more key-value pairs in command, like mset */ |
||||
}; |
||||
|
||||
struct cmd { |
||||
|
||||
uint64_t id; /* command id */ |
||||
|
||||
cmd_parse_result_t result; /* command parsing result */ |
||||
char *errstr; /* error info when the command parse failed */ |
||||
|
||||
cmd_type_t type; /* command type */ |
||||
|
||||
char *cmd; |
||||
uint32_t clen; /* command length */ |
||||
|
||||
struct hiarray *keys; /* array of keypos, for req */ |
||||
|
||||
char *narg_start; /* narg start (redis) */ |
||||
char *narg_end; /* narg end (redis) */ |
||||
uint32_t narg; /* # arguments (redis) */ |
||||
|
||||
unsigned quit:1; /* quit request? */ |
||||
unsigned noforward:1; /* not need forward (example: ping) */ |
||||
|
||||
int slot_num; /* this command should send to witch slot?
|
||||
* -1:the keys in this command cross different slots*/ |
||||
struct cmd **frag_seq; /* sequence of fragment command, map from keys to fragments*/ |
||||
|
||||
redisReply *reply; |
||||
|
||||
hilist *sub_commands; /* just for pipeline and multi-key commands */ |
||||
}; |
||||
|
||||
void redis_parse_cmd(struct cmd *r); |
||||
|
||||
struct cmd *command_get(void); |
||||
void command_destroy(struct cmd *command); |
||||
|
||||
#endif |
||||
@ -1,23 +0,0 @@
|
||||
#ifndef __HIREDIS_FMACRO_H |
||||
#define __HIREDIS_FMACRO_H |
||||
|
||||
#if defined(__linux__) |
||||
#ifndef _BSD_SOURCE |
||||
#define _BSD_SOURCE |
||||
#endif |
||||
#define _DEFAULT_SOURCE |
||||
#endif |
||||
|
||||
#if defined(__sun__) |
||||
#define _POSIX_C_SOURCE 200112L |
||||
#elif defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__) |
||||
#define _XOPEN_SOURCE 600 |
||||
#else |
||||
#define _XOPEN_SOURCE |
||||
#endif |
||||
|
||||
#if __APPLE__ && __MACH__ |
||||
#define _OSX |
||||
#endif |
||||
|
||||
#endif |
||||
@ -1,188 +0,0 @@
|
||||
#include <stdlib.h> |
||||
|
||||
#include "hiutil.h" |
||||
#include "hiarray.h" |
||||
|
||||
struct hiarray * |
||||
hiarray_create(uint32_t n, size_t size) |
||||
{ |
||||
struct hiarray *a; |
||||
|
||||
ASSERT(n != 0 && size != 0); |
||||
|
||||
a = hi_alloc(sizeof(*a)); |
||||
if (a == NULL) { |
||||
return NULL; |
||||
} |
||||
|
||||
a->elem = hi_alloc(n * size); |
||||
if (a->elem == NULL) { |
||||
hi_free(a); |
||||
return NULL; |
||||
} |
||||
|
||||
a->nelem = 0; |
||||
a->size = size; |
||||
a->nalloc = n; |
||||
|
||||
return a; |
||||
} |
||||
|
||||
void |
||||
hiarray_destroy(struct hiarray *a) |
||||
{ |
||||
hiarray_deinit(a); |
||||
hi_free(a); |
||||
} |
||||
|
||||
int |
||||
hiarray_init(struct hiarray *a, uint32_t n, size_t size) |
||||
{ |
||||
ASSERT(n != 0 && size != 0); |
||||
|
||||
a->elem = hi_alloc(n * size); |
||||
if (a->elem == NULL) { |
||||
return HI_ENOMEM; |
||||
} |
||||
|
||||
a->nelem = 0; |
||||
a->size = size; |
||||
a->nalloc = n; |
||||
|
||||
return HI_OK; |
||||
} |
||||
|
||||
void |
||||
hiarray_deinit(struct hiarray *a) |
||||
{ |
||||
ASSERT(a->nelem == 0); |
||||
|
||||
if (a->elem != NULL) { |
||||
hi_free(a->elem); |
||||
} |
||||
} |
||||
|
||||
uint32_t |
||||
hiarray_idx(struct hiarray *a, void *elem) |
||||
{ |
||||
uint8_t *p, *q; |
||||
uint32_t off, idx; |
||||
|
||||
ASSERT(elem >= a->elem); |
||||
|
||||
p = a->elem; |
||||
q = elem; |
||||
off = (uint32_t)(q - p); |
||||
|
||||
ASSERT(off % (uint32_t)a->size == 0); |
||||
|
||||
idx = off / (uint32_t)a->size; |
||||
|
||||
return idx; |
||||
} |
||||
|
||||
void * |
||||
hiarray_push(struct hiarray *a) |
||||
{ |
||||
void *elem, *new; |
||||
size_t size; |
||||
|
||||
if (a->nelem == a->nalloc) { |
||||
|
||||
/* the array is full; allocate new array */ |
||||
size = a->size * a->nalloc; |
||||
new = hi_realloc(a->elem, 2 * size); |
||||
if (new == NULL) { |
||||
return NULL; |
||||
} |
||||
|
||||
a->elem = new; |
||||
a->nalloc *= 2; |
||||
} |
||||
|
||||
elem = (uint8_t *)a->elem + a->size * a->nelem; |
||||
a->nelem++; |
||||
|
||||
return elem; |
||||
} |
||||
|
||||
void * |
||||
hiarray_pop(struct hiarray *a) |
||||
{ |
||||
void *elem; |
||||
|
||||
ASSERT(a->nelem != 0); |
||||
|
||||
a->nelem--; |
||||
elem = (uint8_t *)a->elem + a->size * a->nelem; |
||||
|
||||
return elem; |
||||
} |
||||
|
||||
void * |
||||
hiarray_get(struct hiarray *a, uint32_t idx) |
||||
{ |
||||
void *elem; |
||||
|
||||
ASSERT(a->nelem != 0); |
||||
ASSERT(idx < a->nelem); |
||||
|
||||
elem = (uint8_t *)a->elem + (a->size * idx); |
||||
|
||||
return elem; |
||||
} |
||||
|
||||
void * |
||||
hiarray_top(struct hiarray *a) |
||||
{ |
||||
ASSERT(a->nelem != 0); |
||||
|
||||
return hiarray_get(a, a->nelem - 1); |
||||
} |
||||
|
||||
void |
||||
hiarray_swap(struct hiarray *a, struct hiarray *b) |
||||
{ |
||||
struct hiarray tmp; |
||||
|
||||
tmp = *a; |
||||
*a = *b; |
||||
*b = tmp; |
||||
} |
||||
|
||||
/*
|
||||
* Sort nelem elements of the array in ascending order based on the |
||||
* compare comparator. |
||||
*/ |
||||
void |
||||
hiarray_sort(struct hiarray *a, hiarray_compare_t compare) |
||||
{ |
||||
ASSERT(a->nelem != 0); |
||||
|
||||
qsort(a->elem, a->nelem, a->size, compare); |
||||
} |
||||
|
||||
/*
|
||||
* Calls the func once for each element in the array as long as func returns |
||||
* success. On failure short-circuits and returns the error status. |
||||
*/ |
||||
int |
||||
hiarray_each(struct hiarray *a, hiarray_each_t func, void *data) |
||||
{ |
||||
uint32_t i, nelem; |
||||
|
||||
ASSERT(array_n(a) != 0); |
||||
ASSERT(func != NULL); |
||||
|
||||
for (i = 0, nelem = hiarray_n(a); i < nelem; i++) { |
||||
void *elem = hiarray_get(a, i); |
||||
rstatus_t status; |
||||
|
||||
status = func(elem, data); |
||||
if (status != HI_OK) { |
||||
return status; |
||||
} |
||||
} |
||||
|
||||
return HI_OK; |
||||
} |
||||
@ -1,56 +0,0 @@
|
||||
#ifndef __HIARRAY_H_ |
||||
#define __HIARRAY_H_ |
||||
|
||||
#include <stdio.h> |
||||
|
||||
typedef int (*hiarray_compare_t)(const void *, const void *); |
||||
typedef int (*hiarray_each_t)(void *, void *); |
||||
|
||||
struct hiarray { |
||||
uint32_t nelem; /* # element */ |
||||
void *elem; /* element */ |
||||
size_t size; /* element size */ |
||||
uint32_t nalloc; /* # allocated element */ |
||||
}; |
||||
|
||||
#define null_hiarray { 0, NULL, 0, 0 } |
||||
|
||||
static inline void |
||||
hiarray_null(struct hiarray *a) |
||||
{ |
||||
a->nelem = 0; |
||||
a->elem = NULL; |
||||
a->size = 0; |
||||
a->nalloc = 0; |
||||
} |
||||
|
||||
static inline void |
||||
hiarray_set(struct hiarray *a, void *elem, size_t size, uint32_t nalloc) |
||||
{ |
||||
a->nelem = 0; |
||||
a->elem = elem; |
||||
a->size = size; |
||||
a->nalloc = nalloc; |
||||
} |
||||
|
||||
static inline uint32_t |
||||
hiarray_n(const struct hiarray *a) |
||||
{ |
||||
return a->nelem; |
||||
} |
||||
|
||||
struct hiarray *hiarray_create(uint32_t n, size_t size); |
||||
void hiarray_destroy(struct hiarray *a); |
||||
int hiarray_init(struct hiarray *a, uint32_t n, size_t size); |
||||
void hiarray_deinit(struct hiarray *a); |
||||
|
||||
uint32_t hiarray_idx(struct hiarray *a, void *elem); |
||||
void *hiarray_push(struct hiarray *a); |
||||
void *hiarray_pop(struct hiarray *a); |
||||
void *hiarray_get(struct hiarray *a, uint32_t idx); |
||||
void *hiarray_top(struct hiarray *a); |
||||
void hiarray_swap(struct hiarray *a, struct hiarray *b); |
||||
void hiarray_sort(struct hiarray *a, hiarray_compare_t compare); |
||||
int hiarray_each(struct hiarray *a, hiarray_each_t func, void *data); |
||||
|
||||
#endif |
||||
File diff suppressed because it is too large
Load Diff
@ -1,178 +0,0 @@
|
||||
|
||||
#ifndef __HIRCLUSTER_H |
||||
#define __HIRCLUSTER_H |
||||
|
||||
#include "hiredis.h" |
||||
#include "async.h" |
||||
|
||||
#define HIREDIS_VIP_MAJOR 0 |
||||
#define HIREDIS_VIP_MINOR 3 |
||||
#define HIREDIS_VIP_PATCH 0 |
||||
|
||||
#define REDIS_CLUSTER_SLOTS 16384 |
||||
|
||||
#define REDIS_ROLE_NULL 0 |
||||
#define REDIS_ROLE_MASTER 1 |
||||
#define REDIS_ROLE_SLAVE 2 |
||||
|
||||
|
||||
#define HIRCLUSTER_FLAG_NULL 0x0 |
||||
/* The flag to decide whether add slave node in
|
||||
* redisClusterContext->nodes. This is set in the |
||||
* least significant bit of the flags field in
|
||||
* redisClusterContext. (1000000000000) */ |
||||
#define HIRCLUSTER_FLAG_ADD_SLAVE 0x1000 |
||||
/* The flag to decide whether add open slot
|
||||
* for master node. (10000000000000) */ |
||||
#define HIRCLUSTER_FLAG_ADD_OPENSLOT 0x2000 |
||||
/* The flag to decide whether get the route
|
||||
* table by 'cluster slots' command. Default
|
||||
* is 'cluster nodes' command.*/ |
||||
#define HIRCLUSTER_FLAG_ROUTE_USE_SLOTS 0x4000 |
||||
|
||||
struct dict; |
||||
struct hilist; |
||||
|
||||
typedef struct cluster_node |
||||
{ |
||||
sds name; |
||||
sds addr; |
||||
sds host; |
||||
int port; |
||||
uint8_t role; |
||||
uint8_t myself; /* myself ? */ |
||||
redisContext *con; |
||||
redisAsyncContext *acon; |
||||
struct hilist *slots; |
||||
struct hilist *slaves; |
||||
int failure_count; |
||||
void *data; /* Not used by hiredis */ |
||||
struct hiarray *migrating; /* copen_slot[] */ |
||||
struct hiarray *importing; /* copen_slot[] */ |
||||
}cluster_node; |
||||
|
||||
typedef struct cluster_slot |
||||
{ |
||||
uint32_t start; |
||||
uint32_t end; |
||||
cluster_node *node; /* master that this slot region belong to */ |
||||
}cluster_slot; |
||||
|
||||
typedef struct copen_slot |
||||
{ |
||||
uint32_t slot_num; /* slot number */ |
||||
int migrate; /* migrating or importing? */ |
||||
sds remote_name; /* name for the node that this slot migrating to/importing from */ |
||||
cluster_node *node; /* master that this slot belong to */ |
||||
}copen_slot; |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/* Context for a connection to Redis cluster */ |
||||
typedef struct redisClusterContext { |
||||
int err; /* Error flags, 0 when there is no error */ |
||||
char errstr[128]; /* String representation of error when applicable */ |
||||
sds ip; |
||||
int port; |
||||
|
||||
int flags; |
||||
|
||||
enum redisConnectionType connection_type; |
||||
struct timeval *timeout; |
||||
|
||||
struct hiarray *slots; |
||||
|
||||
struct dict *nodes; |
||||
cluster_node *table[REDIS_CLUSTER_SLOTS]; |
||||
|
||||
uint64_t route_version; |
||||
|
||||
int max_redirect_count; |
||||
int retry_count; |
||||
|
||||
struct hilist *requests; |
||||
|
||||
int need_update_route; |
||||
int64_t update_route_time; |
||||
} redisClusterContext; |
||||
|
||||
redisClusterContext *redisClusterConnect(const char *addrs, int flags); |
||||
redisClusterContext *redisClusterConnectWithTimeout(const char *addrs,
|
||||
const struct timeval tv, int flags); |
||||
redisClusterContext *redisClusterConnectNonBlock(const char *addrs, int flags); |
||||
|
||||
void redisClusterFree(redisClusterContext *cc); |
||||
|
||||
void redisClusterSetMaxRedirect(redisClusterContext *cc, int max_redirect_count); |
||||
|
||||
void *redisClusterFormattedCommand(redisClusterContext *cc, char *cmd, int len); |
||||
void *redisClustervCommand(redisClusterContext *cc, const char *format, va_list ap); |
||||
void *redisClusterCommand(redisClusterContext *cc, const char *format, ...); |
||||
void *redisClusterCommandArgv(redisClusterContext *cc, int argc, const char **argv, const size_t *argvlen); |
||||
|
||||
redisContext *ctx_get_by_node(struct cluster_node *node, const struct timeval *timeout, int flags); |
||||
|
||||
int redisClusterAppendFormattedCommand(redisClusterContext *cc, char *cmd, int len); |
||||
int redisClustervAppendCommand(redisClusterContext *cc, const char *format, va_list ap); |
||||
int redisClusterAppendCommand(redisClusterContext *cc, const char *format, ...); |
||||
int redisClusterAppendCommandArgv(redisClusterContext *cc, int argc, const char **argv, const size_t *argvlen); |
||||
int redisClusterGetReply(redisClusterContext *cc, void **reply); |
||||
void redisClusterReset(redisClusterContext *cc); |
||||
|
||||
int cluster_update_route(redisClusterContext *cc); |
||||
int test_cluster_update_route(redisClusterContext *cc); |
||||
struct dict *parse_cluster_nodes(redisClusterContext *cc, char *str, int str_len, int flags); |
||||
struct dict *parse_cluster_slots(redisClusterContext *cc, redisReply *reply, int flags); |
||||
|
||||
|
||||
/*############redis cluster async############*/ |
||||
|
||||
struct redisClusterAsyncContext; |
||||
|
||||
typedef int (adapterAttachFn)(redisAsyncContext*, void*); |
||||
|
||||
typedef void (redisClusterCallbackFn)(struct redisClusterAsyncContext*, void*, void*); |
||||
|
||||
/* Context for an async connection to Redis */ |
||||
typedef struct redisClusterAsyncContext { |
||||
|
||||
redisClusterContext *cc; |
||||
|
||||
/* Setup error flags so they can be used directly. */ |
||||
int err; |
||||
char errstr[128]; /* String representation of error when applicable */ |
||||
|
||||
/* Not used by hiredis */ |
||||
void *data; |
||||
|
||||
void *adapter; |
||||
adapterAttachFn *attach_fn; |
||||
|
||||
/* Called when either the connection is terminated due to an error or per
|
||||
* user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */ |
||||
redisDisconnectCallback *onDisconnect; |
||||
|
||||
/* Called when the first write event was received. */ |
||||
redisConnectCallback *onConnect; |
||||
|
||||
} redisClusterAsyncContext; |
||||
|
||||
redisClusterAsyncContext *redisClusterAsyncConnect(const char *addrs, int flags); |
||||
int redisClusterAsyncSetConnectCallback(redisClusterAsyncContext *acc, redisConnectCallback *fn); |
||||
int redisClusterAsyncSetDisconnectCallback(redisClusterAsyncContext *acc, redisDisconnectCallback *fn); |
||||
int redisClusterAsyncFormattedCommand(redisClusterAsyncContext *acc, redisClusterCallbackFn *fn, void *privdata, char *cmd, int len); |
||||
int redisClustervAsyncCommand(redisClusterAsyncContext *acc, redisClusterCallbackFn *fn, void *privdata, const char *format, va_list ap); |
||||
int redisClusterAsyncCommand(redisClusterAsyncContext *acc, redisClusterCallbackFn *fn, void *privdata, const char *format, ...); |
||||
int redisClusterAsyncCommandArgv(redisClusterAsyncContext *acc, redisClusterCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen); |
||||
void redisClusterAsyncDisconnect(redisClusterAsyncContext *acc); |
||||
void redisClusterAsyncFree(redisClusterAsyncContext *acc); |
||||
|
||||
redisAsyncContext *actx_get_by_node(redisClusterAsyncContext *acc, cluster_node *node); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif |
||||
@ -1,554 +0,0 @@
|
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <stdarg.h> |
||||
#include <string.h> |
||||
#include <unistd.h> |
||||
#include <fcntl.h> |
||||
#include <errno.h> |
||||
|
||||
#include <sys/time.h> |
||||
#include <sys/types.h> |
||||
|
||||
#include <netinet/in.h> |
||||
#include <netinet/tcp.h> |
||||
|
||||
|
||||
#include "hiutil.h" |
||||
|
||||
#ifdef HI_HAVE_BACKTRACE |
||||
# include <execinfo.h> |
||||
#endif |
||||
|
||||
int |
||||
hi_set_blocking(int sd) |
||||
{ |
||||
int flags; |
||||
|
||||
flags = fcntl(sd, F_GETFL, 0); |
||||
if (flags < 0) { |
||||
return flags; |
||||
} |
||||
|
||||
return fcntl(sd, F_SETFL, flags & ~O_NONBLOCK); |
||||
} |
||||
|
||||
int |
||||
hi_set_nonblocking(int sd) |
||||
{ |
||||
int flags; |
||||
|
||||
flags = fcntl(sd, F_GETFL, 0); |
||||
if (flags < 0) { |
||||
return flags; |
||||
} |
||||
|
||||
return fcntl(sd, F_SETFL, flags | O_NONBLOCK); |
||||
} |
||||
|
||||
int |
||||
hi_set_reuseaddr(int sd) |
||||
{ |
||||
int reuse; |
||||
socklen_t len; |
||||
|
||||
reuse = 1; |
||||
len = sizeof(reuse); |
||||
|
||||
return setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &reuse, len); |
||||
} |
||||
|
||||
/*
|
||||
* Disable Nagle algorithm on TCP socket. |
||||
* |
||||
* This option helps to minimize transmit latency by disabling coalescing |
||||
* of data to fill up a TCP segment inside the kernel. Sockets with this |
||||
* option must use readv() or writev() to do data transfer in bulk and |
||||
* hence avoid the overhead of small packets. |
||||
*/ |
||||
int |
||||
hi_set_tcpnodelay(int sd) |
||||
{ |
||||
int nodelay; |
||||
socklen_t len; |
||||
|
||||
nodelay = 1; |
||||
len = sizeof(nodelay); |
||||
|
||||
return setsockopt(sd, IPPROTO_TCP, TCP_NODELAY, &nodelay, len); |
||||
} |
||||
|
||||
int |
||||
hi_set_linger(int sd, int timeout) |
||||
{ |
||||
struct linger linger; |
||||
socklen_t len; |
||||
|
||||
linger.l_onoff = 1; |
||||
linger.l_linger = timeout; |
||||
|
||||
len = sizeof(linger); |
||||
|
||||
return setsockopt(sd, SOL_SOCKET, SO_LINGER, &linger, len); |
||||
} |
||||
|
||||
int |
||||
hi_set_sndbuf(int sd, int size) |
||||
{ |
||||
socklen_t len; |
||||
|
||||
len = sizeof(size); |
||||
|
||||
return setsockopt(sd, SOL_SOCKET, SO_SNDBUF, &size, len); |
||||
} |
||||
|
||||
int |
||||
hi_set_rcvbuf(int sd, int size) |
||||
{ |
||||
socklen_t len; |
||||
|
||||
len = sizeof(size); |
||||
|
||||
return setsockopt(sd, SOL_SOCKET, SO_RCVBUF, &size, len); |
||||
} |
||||
|
||||
int |
||||
hi_get_soerror(int sd) |
||||
{ |
||||
int status, err; |
||||
socklen_t len; |
||||
|
||||
err = 0; |
||||
len = sizeof(err); |
||||
|
||||
status = getsockopt(sd, SOL_SOCKET, SO_ERROR, &err, &len); |
||||
if (status == 0) { |
||||
errno = err; |
||||
} |
||||
|
||||
return status; |
||||
} |
||||
|
||||
int |
||||
hi_get_sndbuf(int sd) |
||||
{ |
||||
int status, size; |
||||
socklen_t len; |
||||
|
||||
size = 0; |
||||
len = sizeof(size); |
||||
|
||||
status = getsockopt(sd, SOL_SOCKET, SO_SNDBUF, &size, &len); |
||||
if (status < 0) { |
||||
return status; |
||||
} |
||||
|
||||
return size; |
||||
} |
||||
|
||||
int |
||||
hi_get_rcvbuf(int sd) |
||||
{ |
||||
int status, size; |
||||
socklen_t len; |
||||
|
||||
size = 0; |
||||
len = sizeof(size); |
||||
|
||||
status = getsockopt(sd, SOL_SOCKET, SO_RCVBUF, &size, &len); |
||||
if (status < 0) { |
||||
return status; |
||||
} |
||||
|
||||
return size; |
||||
} |
||||
|
||||
int |
||||
_hi_atoi(uint8_t *line, size_t n) |
||||
{ |
||||
int value; |
||||
|
||||
if (n == 0) { |
||||
return -1; |
||||
} |
||||
|
||||
for (value = 0; n--; line++) { |
||||
if (*line < '0' || *line > '9') { |
||||
return -1; |
||||
} |
||||
|
||||
value = value * 10 + (*line - '0'); |
||||
} |
||||
|
||||
if (value < 0) { |
||||
return -1; |
||||
} |
||||
|
||||
return value; |
||||
} |
||||
|
||||
void
|
||||
_hi_itoa(uint8_t *s, int num) |
||||
{ |
||||
uint8_t c; |
||||
uint8_t sign = 0; |
||||
|
||||
if(s == NULL) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
uint32_t len, i; |
||||
len = 0; |
||||
|
||||
if(num < 0) |
||||
{ |
||||
sign = 1; |
||||
num = abs(num); |
||||
} |
||||
else if(num == 0) |
||||
{ |
||||
s[len++] = '0'; |
||||
return; |
||||
} |
||||
|
||||
while(num % 10 || num /10) |
||||
{ |
||||
c = num %10 + '0'; |
||||
num = num /10; |
||||
s[len+1] = s[len]; |
||||
s[len] = c; |
||||
len ++; |
||||
} |
||||
|
||||
if(sign == 1) |
||||
{ |
||||
s[len++] = '-'; |
||||
} |
||||
|
||||
s[len] = '\0'; |
||||
|
||||
for(i = 0; i < len/2; i ++) |
||||
{ |
||||
c = s[i]; |
||||
s[i] = s[len - i -1]; |
||||
s[len - i -1] = c; |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
int |
||||
hi_valid_port(int n) |
||||
{ |
||||
if (n < 1 || n > UINT16_MAX) { |
||||
return 0; |
||||
} |
||||
|
||||
return 1; |
||||
} |
||||
|
||||
int _uint_len(uint32_t num) |
||||
{ |
||||
int n = 0; |
||||
|
||||
if(num == 0) |
||||
{ |
||||
return 1; |
||||
} |
||||
|
||||
while(num != 0) |
||||
{ |
||||
n ++; |
||||
num /= 10; |
||||
} |
||||
|
||||
return n; |
||||
} |
||||
|
||||
void * |
||||
_hi_alloc(size_t size, const char *name, int line) |
||||
{ |
||||
void *p; |
||||
|
||||
ASSERT(size != 0); |
||||
|
||||
p = malloc(size); |
||||
|
||||
if(name == NULL && line == 1) |
||||
{ |
||||
|
||||
} |
||||
|
||||
return p; |
||||
} |
||||
|
||||
void * |
||||
_hi_zalloc(size_t size, const char *name, int line) |
||||
{ |
||||
void *p; |
||||
|
||||
p = _hi_alloc(size, name, line); |
||||
if (p != NULL) { |
||||
memset(p, 0, size); |
||||
} |
||||
|
||||
return p; |
||||
} |
||||
|
||||
void * |
||||
_hi_calloc(size_t nmemb, size_t size, const char *name, int line) |
||||
{ |
||||
return _hi_zalloc(nmemb * size, name, line); |
||||
} |
||||
|
||||
void * |
||||
_hi_realloc(void *ptr, size_t size, const char *name, int line) |
||||
{ |
||||
void *p; |
||||
|
||||
ASSERT(size != 0); |
||||
|
||||
p = realloc(ptr, size); |
||||
|
||||
if(name == NULL && line == 1) |
||||
{ |
||||
|
||||
} |
||||
|
||||
return p; |
||||
} |
||||
|
||||
void |
||||
_hi_free(void *ptr, const char *name, int line) |
||||
{ |
||||
ASSERT(ptr != NULL); |
||||
|
||||
if(name == NULL && line == 1) |
||||
{ |
||||
|
||||
} |
||||
|
||||
free(ptr); |
||||
} |
||||
|
||||
void |
||||
hi_stacktrace(int skip_count) |
||||
{ |
||||
if(skip_count > 0) |
||||
{ |
||||
|
||||
} |
||||
|
||||
#ifdef HI_HAVE_BACKTRACE |
||||
void *stack[64]; |
||||
char **symbols; |
||||
int size, i, j; |
||||
|
||||
size = backtrace(stack, 64); |
||||
symbols = backtrace_symbols(stack, size); |
||||
if (symbols == NULL) { |
||||
return; |
||||
} |
||||
|
||||
skip_count++; /* skip the current frame also */ |
||||
|
||||
for (i = skip_count, j = 0; i < size; i++, j++) { |
||||
printf("[%d] %s\n", j, symbols[i]); |
||||
} |
||||
|
||||
free(symbols); |
||||
#endif |
||||
} |
||||
|
||||
void |
||||
hi_stacktrace_fd(int fd) |
||||
{ |
||||
if(fd > 0) |
||||
{ |
||||
|
||||
} |
||||
#ifdef HI_HAVE_BACKTRACE |
||||
void *stack[64]; |
||||
int size; |
||||
|
||||
size = backtrace(stack, 64); |
||||
backtrace_symbols_fd(stack, size, fd); |
||||
#endif |
||||
} |
||||
|
||||
void |
||||
hi_assert(const char *cond, const char *file, int line, int panic) |
||||
{ |
||||
|
||||
printf("File: %s Line: %d: %s\n", file, line, cond); |
||||
|
||||
if (panic) { |
||||
hi_stacktrace(1); |
||||
abort(); |
||||
} |
||||
abort(); |
||||
} |
||||
|
||||
int |
||||
_vscnprintf(char *buf, size_t size, const char *fmt, va_list args) |
||||
{ |
||||
int n; |
||||
|
||||
n = vsnprintf(buf, size, fmt, args); |
||||
|
||||
/*
|
||||
* The return value is the number of characters which would be written |
||||
* into buf not including the trailing '\0'. If size is == 0 the |
||||
* function returns 0. |
||||
* |
||||
* On error, the function also returns 0. This is to allow idiom such |
||||
* as len += _vscnprintf(...) |
||||
* |
||||
* See: http://lwn.net/Articles/69419/
|
||||
*/ |
||||
if (n <= 0) { |
||||
return 0; |
||||
} |
||||
|
||||
if (n < (int) size) { |
||||
return n; |
||||
} |
||||
|
||||
return (int)(size - 1); |
||||
} |
||||
|
||||
int |
||||
_scnprintf(char *buf, size_t size, const char *fmt, ...) |
||||
{ |
||||
va_list args; |
||||
int n; |
||||
|
||||
va_start(args, fmt); |
||||
n = _vscnprintf(buf, size, fmt, args); |
||||
va_end(args); |
||||
|
||||
return n; |
||||
} |
||||
|
||||
/*
|
||||
* Send n bytes on a blocking descriptor |
||||
*/ |
||||
ssize_t |
||||
_hi_sendn(int sd, const void *vptr, size_t n) |
||||
{ |
||||
size_t nleft; |
||||
ssize_t nsend; |
||||
const char *ptr; |
||||
|
||||
ptr = vptr; |
||||
nleft = n; |
||||
while (nleft > 0) { |
||||
nsend = send(sd, ptr, nleft, 0); |
||||
if (nsend < 0) { |
||||
if (errno == EINTR) { |
||||
continue; |
||||
} |
||||
return nsend; |
||||
} |
||||
if (nsend == 0) { |
||||
return -1; |
||||
} |
||||
|
||||
nleft -= (size_t)nsend; |
||||
ptr += nsend; |
||||
} |
||||
|
||||
return (ssize_t)n; |
||||
} |
||||
|
||||
/*
|
||||
* Recv n bytes from a blocking descriptor |
||||
*/ |
||||
ssize_t |
||||
_hi_recvn(int sd, void *vptr, size_t n) |
||||
{ |
||||
size_t nleft; |
||||
ssize_t nrecv; |
||||
char *ptr; |
||||
|
||||
ptr = vptr; |
||||
nleft = n; |
||||
while (nleft > 0) { |
||||
nrecv = recv(sd, ptr, nleft, 0); |
||||
if (nrecv < 0) { |
||||
if (errno == EINTR) { |
||||
continue; |
||||
} |
||||
return nrecv; |
||||
} |
||||
if (nrecv == 0) { |
||||
break; |
||||
} |
||||
|
||||
nleft -= (size_t)nrecv; |
||||
ptr += nrecv; |
||||
} |
||||
|
||||
return (ssize_t)(n - nleft); |
||||
} |
||||
|
||||
/*
|
||||
* Return the current time in microseconds since Epoch |
||||
*/ |
||||
int64_t |
||||
hi_usec_now(void) |
||||
{ |
||||
struct timeval now; |
||||
int64_t usec; |
||||
int status; |
||||
|
||||
status = gettimeofday(&now, NULL); |
||||
if (status < 0) { |
||||
return -1; |
||||
} |
||||
|
||||
usec = (int64_t)now.tv_sec * 1000000LL + (int64_t)now.tv_usec; |
||||
|
||||
return usec; |
||||
} |
||||
|
||||
/*
|
||||
* Return the current time in milliseconds since Epoch |
||||
*/ |
||||
int64_t |
||||
hi_msec_now(void) |
||||
{ |
||||
return hi_usec_now() / 1000LL; |
||||
} |
||||
|
||||
void print_string_with_length(char *s, size_t len) |
||||
{ |
||||
char *token; |
||||
for(token = s; token <= s + len; token ++) |
||||
{ |
||||
printf("%c", *token); |
||||
} |
||||
printf("\n"); |
||||
} |
||||
|
||||
void print_string_with_length_fix_CRLF(char *s, size_t len) |
||||
{ |
||||
char *token; |
||||
for(token = s; token < s + len; token ++) |
||||
{ |
||||
if(*token == CR) |
||||
{ |
||||
printf("\\r"); |
||||
} |
||||
else if(*token == LF) |
||||
{ |
||||
printf("\\n"); |
||||
} |
||||
else |
||||
{ |
||||
printf("%c", *token); |
||||
} |
||||
} |
||||
printf("\n"); |
||||
} |
||||
|
||||
@ -1,265 +0,0 @@
|
||||
#ifndef __HIUTIL_H_ |
||||
#define __HIUTIL_H_ |
||||
|
||||
#include <stdarg.h> |
||||
#include <stdint.h> |
||||
#include <sys/types.h> |
||||
|
||||
#define HI_OK 0 |
||||
#define HI_ERROR -1 |
||||
#define HI_EAGAIN -2 |
||||
#define HI_ENOMEM -3 |
||||
|
||||
typedef int rstatus_t; /* return type */ |
||||
|
||||
#define LF (uint8_t) 10 |
||||
#define CR (uint8_t) 13 |
||||
#define CRLF "\x0d\x0a" |
||||
#define CRLF_LEN (sizeof("\x0d\x0a") - 1) |
||||
|
||||
#define NELEMS(a) ((sizeof(a)) / sizeof((a)[0])) |
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b)) |
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b)) |
||||
|
||||
#define SQUARE(d) ((d) * (d)) |
||||
#define VAR(s, s2, n) (((n) < 2) ? 0.0 : ((s2) - SQUARE(s)/(n)) / ((n) - 1)) |
||||
#define STDDEV(s, s2, n) (((n) < 2) ? 0.0 : sqrt(VAR((s), (s2), (n)))) |
||||
|
||||
#define HI_INET4_ADDRSTRLEN (sizeof("255.255.255.255") - 1) |
||||
#define HI_INET6_ADDRSTRLEN \ |
||||
(sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") - 1) |
||||
#define HI_INET_ADDRSTRLEN MAX(HI_INET4_ADDRSTRLEN, HI_INET6_ADDRSTRLEN) |
||||
#define HI_UNIX_ADDRSTRLEN \ |
||||
(sizeof(struct sockaddr_un) - offsetof(struct sockaddr_un, sun_path)) |
||||
|
||||
#define HI_MAXHOSTNAMELEN 256 |
||||
|
||||
/*
|
||||
* Length of 1 byte, 2 bytes, 4 bytes, 8 bytes and largest integral |
||||
* type (uintmax_t) in ascii, including the null terminator '\0' |
||||
* |
||||
* From stdint.h, we have: |
||||
* # define UINT8_MAX (255) |
||||
* # define UINT16_MAX (65535) |
||||
* # define UINT32_MAX (4294967295U) |
||||
* # define UINT64_MAX (__UINT64_C(18446744073709551615)) |
||||
*/ |
||||
#define HI_UINT8_MAXLEN (3 + 1) |
||||
#define HI_UINT16_MAXLEN (5 + 1) |
||||
#define HI_UINT32_MAXLEN (10 + 1) |
||||
#define HI_UINT64_MAXLEN (20 + 1) |
||||
#define HI_UINTMAX_MAXLEN HI_UINT64_MAXLEN |
||||
|
||||
/*
|
||||
* Make data 'd' or pointer 'p', n-byte aligned, where n is a power of 2 |
||||
* of 2. |
||||
*/ |
||||
#define HI_ALIGNMENT sizeof(unsigned long) /* platform word */ |
||||
#define HI_ALIGN(d, n) (((d) + (n - 1)) & ~(n - 1)) |
||||
#define HI_ALIGN_PTR(p, n) \ |
||||
(void *) (((uintptr_t) (p) + ((uintptr_t) n - 1)) & ~((uintptr_t) n - 1)) |
||||
|
||||
|
||||
|
||||
#define str3icmp(m, c0, c1, c2) \ |
||||
((m[0] == c0 || m[0] == (c0 ^ 0x20)) && \
|
||||
(m[1] == c1 || m[1] == (c1 ^ 0x20)) && \
|
||||
(m[2] == c2 || m[2] == (c2 ^ 0x20))) |
||||
|
||||
#define str4icmp(m, c0, c1, c2, c3) \ |
||||
(str3icmp(m, c0, c1, c2) && (m[3] == c3 || m[3] == (c3 ^ 0x20))) |
||||
|
||||
#define str5icmp(m, c0, c1, c2, c3, c4) \ |
||||
(str4icmp(m, c0, c1, c2, c3) && (m[4] == c4 || m[4] == (c4 ^ 0x20))) |
||||
|
||||
#define str6icmp(m, c0, c1, c2, c3, c4, c5) \ |
||||
(str5icmp(m, c0, c1, c2, c3, c4) && (m[5] == c5 || m[5] == (c5 ^ 0x20))) |
||||
|
||||
#define str7icmp(m, c0, c1, c2, c3, c4, c5, c6) \ |
||||
(str6icmp(m, c0, c1, c2, c3, c4, c5) && \
|
||||
(m[6] == c6 || m[6] == (c6 ^ 0x20))) |
||||
|
||||
#define str8icmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \ |
||||
(str7icmp(m, c0, c1, c2, c3, c4, c5, c6) && \
|
||||
(m[7] == c7 || m[7] == (c7 ^ 0x20))) |
||||
|
||||
#define str9icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8) \ |
||||
(str8icmp(m, c0, c1, c2, c3, c4, c5, c6, c7) && \
|
||||
(m[8] == c8 || m[8] == (c8 ^ 0x20))) |
||||
|
||||
#define str10icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9) \ |
||||
(str9icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8) && \
|
||||
(m[9] == c9 || m[9] == (c9 ^ 0x20))) |
||||
|
||||
#define str11icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10) \ |
||||
(str10icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9) && \
|
||||
(m[10] == c10 || m[10] == (c10 ^ 0x20))) |
||||
|
||||
#define str12icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11) \ |
||||
(str11icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10) && \
|
||||
(m[11] == c11 || m[11] == (c11 ^ 0x20))) |
||||
|
||||
#define str13icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12) \ |
||||
(str12icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11) && \
|
||||
(m[12] == c12 || m[12] == (c12 ^ 0x20))) |
||||
|
||||
#define str14icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) \ |
||||
(str13icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12) && \
|
||||
(m[13] == c13 || m[13] == (c13 ^ 0x20))) |
||||
|
||||
#define str15icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14) \ |
||||
(str14icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) && \
|
||||
(m[14] == c14 || m[14] == (c14 ^ 0x20))) |
||||
|
||||
#define str16icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15) \ |
||||
(str15icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14) && \
|
||||
(m[15] == c15 || m[15] == (c15 ^ 0x20))) |
||||
|
||||
|
||||
|
||||
/*
|
||||
* Wrapper to workaround well known, safe, implicit type conversion when |
||||
* invoking system calls. |
||||
*/ |
||||
#define hi_gethostname(_name, _len) \ |
||||
gethostname((char *)_name, (size_t)_len) |
||||
|
||||
#define hi_atoi(_line, _n) \ |
||||
_hi_atoi((uint8_t *)_line, (size_t)_n) |
||||
#define hi_itoa(_line, _n) \ |
||||
_hi_itoa((uint8_t *)_line, (int)_n) |
||||
|
||||
#define uint_len(_n) \ |
||||
_uint_len((uint32_t)_n) |
||||
|
||||
|
||||
int hi_set_blocking(int sd); |
||||
int hi_set_nonblocking(int sd); |
||||
int hi_set_reuseaddr(int sd); |
||||
int hi_set_tcpnodelay(int sd); |
||||
int hi_set_linger(int sd, int timeout); |
||||
int hi_set_sndbuf(int sd, int size); |
||||
int hi_set_rcvbuf(int sd, int size); |
||||
int hi_get_soerror(int sd); |
||||
int hi_get_sndbuf(int sd); |
||||
int hi_get_rcvbuf(int sd); |
||||
|
||||
int _hi_atoi(uint8_t *line, size_t n); |
||||
void _hi_itoa(uint8_t *s, int num); |
||||
|
||||
int hi_valid_port(int n); |
||||
|
||||
int _uint_len(uint32_t num); |
||||
|
||||
|
||||
/*
|
||||
* Memory allocation and free wrappers. |
||||
* |
||||
* These wrappers enables us to loosely detect double free, dangling |
||||
* pointer access and zero-byte alloc. |
||||
*/ |
||||
#define hi_alloc(_s) \ |
||||
_hi_alloc((size_t)(_s), __FILE__, __LINE__) |
||||
|
||||
#define hi_zalloc(_s) \ |
||||
_hi_zalloc((size_t)(_s), __FILE__, __LINE__) |
||||
|
||||
#define hi_calloc(_n, _s) \ |
||||
_hi_calloc((size_t)(_n), (size_t)(_s), __FILE__, __LINE__) |
||||
|
||||
#define hi_realloc(_p, _s) \ |
||||
_hi_realloc(_p, (size_t)(_s), __FILE__, __LINE__) |
||||
|
||||
#define hi_free(_p) do { \ |
||||
_hi_free(_p, __FILE__, __LINE__); \
|
||||
(_p) = NULL; \
|
||||
} while (0) |
||||
|
||||
void *_hi_alloc(size_t size, const char *name, int line); |
||||
void *_hi_zalloc(size_t size, const char *name, int line); |
||||
void *_hi_calloc(size_t nmemb, size_t size, const char *name, int line); |
||||
void *_hi_realloc(void *ptr, size_t size, const char *name, int line); |
||||
void _hi_free(void *ptr, const char *name, int line); |
||||
|
||||
|
||||
#define hi_strndup(_s, _n) \ |
||||
strndup((char *)(_s), (size_t)(_n)); |
||||
|
||||
/*
|
||||
* Wrappers to send or receive n byte message on a blocking |
||||
* socket descriptor. |
||||
*/ |
||||
#define hi_sendn(_s, _b, _n) \ |
||||
_hi_sendn(_s, _b, (size_t)(_n)) |
||||
|
||||
#define hi_recvn(_s, _b, _n) \ |
||||
_hi_recvn(_s, _b, (size_t)(_n)) |
||||
|
||||
/*
|
||||
* Wrappers to read or write data to/from (multiple) buffers |
||||
* to a file or socket descriptor. |
||||
*/ |
||||
#define hi_read(_d, _b, _n) \ |
||||
read(_d, _b, (size_t)(_n)) |
||||
|
||||
#define hi_readv(_d, _b, _n) \ |
||||
readv(_d, _b, (int)(_n)) |
||||
|
||||
#define hi_write(_d, _b, _n) \ |
||||
write(_d, _b, (size_t)(_n)) |
||||
|
||||
#define hi_writev(_d, _b, _n) \ |
||||
writev(_d, _b, (int)(_n)) |
||||
|
||||
ssize_t _hi_sendn(int sd, const void *vptr, size_t n); |
||||
ssize_t _hi_recvn(int sd, void *vptr, size_t n); |
||||
|
||||
/*
|
||||
* Wrappers for defining custom assert based on whether macro |
||||
* HI_ASSERT_PANIC or HI_ASSERT_LOG was defined at the moment |
||||
* ASSERT was called. |
||||
*/ |
||||
#ifdef HI_ASSERT_PANIC |
||||
|
||||
#define ASSERT(_x) do { \ |
||||
if (!(_x)) { \
|
||||
hi_assert(#_x, __FILE__, __LINE__, 1); \
|
||||
} \
|
||||
} while (0) |
||||
|
||||
#define NOT_REACHED() ASSERT(0) |
||||
|
||||
#elif HI_ASSERT_LOG |
||||
|
||||
#define ASSERT(_x) do { \ |
||||
if (!(_x)) { \
|
||||
hi_assert(#_x, __FILE__, __LINE__, 0); \
|
||||
} \
|
||||
} while (0) |
||||
|
||||
#define NOT_REACHED() ASSERT(0) |
||||
|
||||
#else |
||||
|
||||
#define ASSERT(_x) |
||||
|
||||
#define NOT_REACHED() |
||||
|
||||
#endif |
||||
|
||||
void hi_assert(const char *cond, const char *file, int line, int panic); |
||||
void hi_stacktrace(int skip_count); |
||||
void hi_stacktrace_fd(int fd); |
||||
|
||||
int _scnprintf(char *buf, size_t size, const char *fmt, ...); |
||||
int _vscnprintf(char *buf, size_t size, const char *fmt, va_list args); |
||||
int64_t hi_usec_now(void); |
||||
int64_t hi_msec_now(void); |
||||
|
||||
void print_string_with_length(char *s, size_t len); |
||||
void print_string_with_length_fix_CRLF(char *s, size_t len); |
||||
|
||||
uint16_t crc16(const char *buf, int len); |
||||
|
||||
#endif |
||||
@ -1,105 +0,0 @@
|
||||
/* SDS (Simple Dynamic Strings), A C dynamic strings library.
|
||||
* |
||||
* Copyright (c) 2006-2014, Salvatore Sanfilippo <antirez at gmail dot com> |
||||
* 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 Redis nor the names of its contributors may be used |
||||
* to endorse or promote products derived from this software without |
||||
* specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
* POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
#ifndef __SDS_H |
||||
#define __SDS_H |
||||
|
||||
#define SDS_MAX_PREALLOC (1024*1024) |
||||
|
||||
#include <sys/types.h> |
||||
#include <stdarg.h> |
||||
#ifdef _MSC_VER |
||||
#include "win32.h" |
||||
#endif |
||||
|
||||
typedef char *sds; |
||||
|
||||
struct sdshdr { |
||||
int len; |
||||
int free; |
||||
char buf[]; |
||||
}; |
||||
|
||||
static inline size_t sdslen(const sds s) { |
||||
struct sdshdr *sh = (struct sdshdr *)(s-sizeof *sh); |
||||
return sh->len; |
||||
} |
||||
|
||||
static inline size_t sdsavail(const sds s) { |
||||
struct sdshdr *sh = (struct sdshdr *)(s-sizeof *sh); |
||||
return sh->free; |
||||
} |
||||
|
||||
sds sdsnewlen(const void *init, size_t initlen); |
||||
sds sdsnew(const char *init); |
||||
sds sdsempty(void); |
||||
size_t sdslen(const sds s); |
||||
sds sdsdup(const sds s); |
||||
void sdsfree(sds s); |
||||
size_t sdsavail(const sds s); |
||||
sds sdsgrowzero(sds s, size_t len); |
||||
sds sdscatlen(sds s, const void *t, size_t len); |
||||
sds sdscat(sds s, const char *t); |
||||
sds sdscatsds(sds s, const sds t); |
||||
sds sdscpylen(sds s, const char *t, size_t len); |
||||
sds sdscpy(sds s, const char *t); |
||||
|
||||
sds sdscatvprintf(sds s, const char *fmt, va_list ap); |
||||
#ifdef __GNUC__ |
||||
sds sdscatprintf(sds s, const char *fmt, ...) |
||||
__attribute__((format(printf, 2, 3))); |
||||
#else |
||||
sds sdscatprintf(sds s, const char *fmt, ...); |
||||
#endif |
||||
|
||||
sds sdscatfmt(sds s, char const *fmt, ...); |
||||
void sdstrim(sds s, const char *cset); |
||||
void sdsrange(sds s, int start, int end); |
||||
void sdsupdatelen(sds s); |
||||
void sdsclear(sds s); |
||||
int sdscmp(const sds s1, const sds s2); |
||||
sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); |
||||
void sdsfreesplitres(sds *tokens, int count); |
||||
void sdstolower(sds s); |
||||
void sdstoupper(sds s); |
||||
sds sdsfromlonglong(long long value); |
||||
sds sdscatrepr(sds s, const char *p, size_t len); |
||||
sds *sdssplitargs(const char *line, int *argc); |
||||
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); |
||||
sds sdsjoin(char **argv, int argc, char *sep, size_t seplen); |
||||
sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen); |
||||
|
||||
/* Low level functions exposed to the user API */ |
||||
sds sdsMakeRoomFor(sds s, size_t addlen); |
||||
void sdsIncrLen(sds s, int incr); |
||||
sds sdsRemoveFreeSpace(sds s); |
||||
size_t sdsAllocSize(sds s); |
||||
|
||||
#endif |
||||
@ -0,0 +1,32 @@
|
||||
# Prerequisites |
||||
*.d |
||||
|
||||
# Compiled Object files |
||||
*.slo |
||||
*.lo |
||||
*.o |
||||
*.obj |
||||
|
||||
# Precompiled Headers |
||||
*.gch |
||||
*.pch |
||||
|
||||
# Compiled Dynamic libraries |
||||
*.so |
||||
*.dylib |
||||
*.dll |
||||
|
||||
# Fortran module files |
||||
*.mod |
||||
*.smod |
||||
|
||||
# Compiled Static libraries |
||||
*.lai |
||||
*.la |
||||
*.a |
||||
*.lib |
||||
|
||||
# Executables |
||||
*.exe |
||||
*.out |
||||
*.app |
||||
@ -0,0 +1,51 @@
|
||||
project(redis++) |
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") |
||||
cmake_minimum_required(VERSION 3.0.0) |
||||
else() |
||||
cmake_minimum_required(VERSION 2.8.0) |
||||
endif() |
||||
|
||||
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -W -Werror -fPIC") |
||||
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) |
||||
|
||||
set(PROJECT_SOURCE_DIR ${PROJECT_SOURCE_DIR}/src/sw/redis++) |
||||
|
||||
file(GLOB PROJECT_SOURCE_FILES "${PROJECT_SOURCE_DIR}/*.cpp") |
||||
|
||||
set(STATIC_LIB static) |
||||
#set(SHARED_LIB shared) |
||||
|
||||
add_library(${STATIC_LIB} STATIC ${PROJECT_SOURCE_FILES}) |
||||
# add_library(${SHARED_LIB} SHARED ${PROJECT_SOURCE_FILES}) |
||||
|
||||
# hiredis dependency |
||||
find_path(HIREDIS_HEADER hiredis) |
||||
target_include_directories(${STATIC_LIB} PUBLIC ${HIREDIS_HEADER}) |
||||
# target_include_directories(${SHARED_LIB} PUBLIC ${HIREDIS_HEADER}) |
||||
|
||||
#find_library(HIREDIS_LIB hiredis) |
||||
#target_link_libraries(${SHARED_LIB} ${HIREDIS_LIB}) |
||||
|
||||
set_target_properties(${STATIC_LIB} PROPERTIES OUTPUT_NAME ${PROJECT_NAME}) |
||||
#set_target_properties(${SHARED_LIB} PROPERTIES OUTPUT_NAME ${PROJECT_NAME}) |
||||
|
||||
set_target_properties(${STATIC_LIB} PROPERTIES CLEAN_DIRECT_OUTPUT 1) |
||||
#set_target_properties(${SHARED_LIB} PROPERTIES CLEAN_DIRECT_OUTPUT 1) |
||||
|
||||
# add_subdirectory(test) |
||||
|
||||
|
||||
# Install static lib. |
||||
install(TARGETS ${STATIC_LIB} |
||||
ARCHIVE DESTINATION lib) |
||||
|
||||
# Install shared lib. |
||||
#install(TARGETS ${SHARED_LIB} |
||||
# LIBRARY DESTINATION lib) |
||||
|
||||
#Install headers. |
||||
set(HEADER_PATH "sw/redis++") |
||||
file(GLOB HEADERS "${PROJECT_SOURCE_DIR}/*.h*") |
||||
install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/${HEADER_PATH}) |
||||
@ -0,0 +1,201 @@
|
||||
Apache License |
||||
Version 2.0, January 2004 |
||||
http://www.apache.org/licenses/ |
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
||||
|
||||
1. Definitions. |
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, |
||||
and distribution as defined by Sections 1 through 9 of this document. |
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by |
||||
the copyright owner that is granting the License. |
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all |
||||
other entities that control, are controlled by, or are under common |
||||
control with that entity. For the purposes of this definition, |
||||
"control" means (i) the power, direct or indirect, to cause the |
||||
direction or management of such entity, whether by contract or |
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
||||
outstanding shares, or (iii) beneficial ownership of such entity. |
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity |
||||
exercising permissions granted by this License. |
||||
|
||||
"Source" form shall mean the preferred form for making modifications, |
||||
including but not limited to software source code, documentation |
||||
source, and configuration files. |
||||
|
||||
"Object" form shall mean any form resulting from mechanical |
||||
transformation or translation of a Source form, including but |
||||
not limited to compiled object code, generated documentation, |
||||
and conversions to other media types. |
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or |
||||
Object form, made available under the License, as indicated by a |
||||
copyright notice that is included in or attached to the work |
||||
(an example is provided in the Appendix below). |
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object |
||||
form, that is based on (or derived from) the Work and for which the |
||||
editorial revisions, annotations, elaborations, or other modifications |
||||
represent, as a whole, an original work of authorship. For the purposes |
||||
of this License, Derivative Works shall not include works that remain |
||||
separable from, or merely link (or bind by name) to the interfaces of, |
||||
the Work and Derivative Works thereof. |
||||
|
||||
"Contribution" shall mean any work of authorship, including |
||||
the original version of the Work and any modifications or additions |
||||
to that Work or Derivative Works thereof, that is intentionally |
||||
submitted to Licensor for inclusion in the Work by the copyright owner |
||||
or by an individual or Legal Entity authorized to submit on behalf of |
||||
the copyright owner. For the purposes of this definition, "submitted" |
||||
means any form of electronic, verbal, or written communication sent |
||||
to the Licensor or its representatives, including but not limited to |
||||
communication on electronic mailing lists, source code control systems, |
||||
and issue tracking systems that are managed by, or on behalf of, the |
||||
Licensor for the purpose of discussing and improving the Work, but |
||||
excluding communication that is conspicuously marked or otherwise |
||||
designated in writing by the copyright owner as "Not a Contribution." |
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity |
||||
on behalf of whom a Contribution has been received by Licensor and |
||||
subsequently incorporated within the Work. |
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of |
||||
this License, each Contributor hereby grants to You a perpetual, |
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
copyright license to reproduce, prepare Derivative Works of, |
||||
publicly display, publicly perform, sublicense, and distribute the |
||||
Work and such Derivative Works in Source or Object form. |
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of |
||||
this License, each Contributor hereby grants to You a perpetual, |
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
(except as stated in this section) patent license to make, have made, |
||||
use, offer to sell, sell, import, and otherwise transfer the Work, |
||||
where such license applies only to those patent claims licensable |
||||
by such Contributor that are necessarily infringed by their |
||||
Contribution(s) alone or by combination of their Contribution(s) |
||||
with the Work to which such Contribution(s) was submitted. If You |
||||
institute patent litigation against any entity (including a |
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work |
||||
or a Contribution incorporated within the Work constitutes direct |
||||
or contributory patent infringement, then any patent licenses |
||||
granted to You under this License for that Work shall terminate |
||||
as of the date such litigation is filed. |
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the |
||||
Work or Derivative Works thereof in any medium, with or without |
||||
modifications, and in Source or Object form, provided that You |
||||
meet the following conditions: |
||||
|
||||
(a) You must give any other recipients of the Work or |
||||
Derivative Works a copy of this License; and |
||||
|
||||
(b) You must cause any modified files to carry prominent notices |
||||
stating that You changed the files; and |
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works |
||||
that You distribute, all copyright, patent, trademark, and |
||||
attribution notices from the Source form of the Work, |
||||
excluding those notices that do not pertain to any part of |
||||
the Derivative Works; and |
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its |
||||
distribution, then any Derivative Works that You distribute must |
||||
include a readable copy of the attribution notices contained |
||||
within such NOTICE file, excluding those notices that do not |
||||
pertain to any part of the Derivative Works, in at least one |
||||
of the following places: within a NOTICE text file distributed |
||||
as part of the Derivative Works; within the Source form or |
||||
documentation, if provided along with the Derivative Works; or, |
||||
within a display generated by the Derivative Works, if and |
||||
wherever such third-party notices normally appear. The contents |
||||
of the NOTICE file are for informational purposes only and |
||||
do not modify the License. You may add Your own attribution |
||||
notices within Derivative Works that You distribute, alongside |
||||
or as an addendum to the NOTICE text from the Work, provided |
||||
that such additional attribution notices cannot be construed |
||||
as modifying the License. |
||||
|
||||
You may add Your own copyright statement to Your modifications and |
||||
may provide additional or different license terms and conditions |
||||
for use, reproduction, or distribution of Your modifications, or |
||||
for any such Derivative Works as a whole, provided Your use, |
||||
reproduction, and distribution of the Work otherwise complies with |
||||
the conditions stated in this License. |
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, |
||||
any Contribution intentionally submitted for inclusion in the Work |
||||
by You to the Licensor shall be under the terms and conditions of |
||||
this License, without any additional terms or conditions. |
||||
Notwithstanding the above, nothing herein shall supersede or modify |
||||
the terms of any separate license agreement you may have executed |
||||
with Licensor regarding such Contributions. |
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade |
||||
names, trademarks, service marks, or product names of the Licensor, |
||||
except as required for reasonable and customary use in describing the |
||||
origin of the Work and reproducing the content of the NOTICE file. |
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or |
||||
agreed to in writing, Licensor provides the Work (and each |
||||
Contributor provides its Contributions) on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
||||
implied, including, without limitation, any warranties or conditions |
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
||||
PARTICULAR PURPOSE. You are solely responsible for determining the |
||||
appropriateness of using or redistributing the Work and assume any |
||||
risks associated with Your exercise of permissions under this License. |
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, |
||||
whether in tort (including negligence), contract, or otherwise, |
||||
unless required by applicable law (such as deliberate and grossly |
||||
negligent acts) or agreed to in writing, shall any Contributor be |
||||
liable to You for damages, including any direct, indirect, special, |
||||
incidental, or consequential damages of any character arising as a |
||||
result of this License or out of the use or inability to use the |
||||
Work (including but not limited to damages for loss of goodwill, |
||||
work stoppage, computer failure or malfunction, or any and all |
||||
other commercial damages or losses), even if such Contributor |
||||
has been advised of the possibility of such damages. |
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing |
||||
the Work or Derivative Works thereof, You may choose to offer, |
||||
and charge a fee for, acceptance of support, warranty, indemnity, |
||||
or other liability obligations and/or rights consistent with this |
||||
License. However, in accepting such obligations, You may act only |
||||
on Your own behalf and on Your sole responsibility, not on behalf |
||||
of any other Contributor, and only if You agree to indemnify, |
||||
defend, and hold each Contributor harmless for any liability |
||||
incurred by, or claims asserted against, such Contributor by reason |
||||
of your accepting any such warranty or additional liability. |
||||
|
||||
END OF TERMS AND CONDITIONS |
||||
|
||||
APPENDIX: How to apply the Apache License to your work. |
||||
|
||||
To apply the Apache License to your work, attach the following |
||||
boilerplate notice, with the fields enclosed by brackets "[]" |
||||
replaced with your own identifying information. (Don't include |
||||
the brackets!) The text should be enclosed in the appropriate |
||||
comment syntax for the file format. We also recommend that a |
||||
file or class name and description of purpose be included on the |
||||
same "printed page" as the copyright notice for easier |
||||
identification within third-party archives. |
||||
|
||||
Copyright [yyyy] [name of copyright owner] |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
@ -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
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue