Use hiredis 0.13.3

refs #4991
This commit is contained in:
Michael Friedrich 2017-03-09 12:26:09 +01:00
parent d3e3159e6b
commit 321b067a18
20 changed files with 252 additions and 783 deletions

View File

@ -4,13 +4,6 @@ compiler:
- gcc - gcc
- clang - clang
os:
- linux
- osx
before_script:
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew update; brew install redis; fi
addons: addons:
apt: apt:
packages: packages:
@ -28,12 +21,4 @@ env:
- TARGET="32bit" TARGET_VARS="32bit-vars" CFLAGS="-Werror" - TARGET="32bit" TARGET_VARS="32bit-vars" CFLAGS="-Werror"
- TARGET="32bit" TARGET_VARS="32bit-vars" PRE="valgrind --track-origins=yes --leak-check=full" - 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 script: make $TARGET CFLAGS="$CFLAGS" && make check PRE="$PRE" && make $TARGET_VARS hiredis-example

View File

@ -1,42 +1,3 @@
### 1.0.0 (unreleased)
**Fixes**:
* 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**:
* 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.
* 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) ### 0.13.3 (2015-09-16)
* Revert "Clear `REDIS_CONNECTED` flag when connection is closed". * Revert "Clear `REDIS_CONNECTED` flag when connection is closed".

View File

@ -40,8 +40,8 @@ CC:=$(shell sh -c 'type $(CC) >/dev/null 2>/dev/null && echo $(CC) || echo gcc')
CXX:=$(shell sh -c 'type $(CXX) >/dev/null 2>/dev/null && echo $(CXX) || echo g++') CXX:=$(shell sh -c 'type $(CXX) >/dev/null 2>/dev/null && echo $(CXX) || echo g++')
OPTIMIZATION?=-O3 OPTIMIZATION?=-O3
WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings
DEBUG_FLAGS?= -g -ggdb DEBUG?= -g -ggdb
REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS) $(ARCH) REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CFLAGS) $(WARNINGS) $(DEBUG) $(ARCH)
REAL_LDFLAGS=$(LDFLAGS) $(ARCH) REAL_LDFLAGS=$(LDFLAGS) $(ARCH)
DYLIBSUFFIX=so DYLIBSUFFIX=so

View File

@ -1,7 +1,5 @@
[![Build Status](https://travis-ci.org/redis/hiredis.png)](https://travis-ci.org/redis/hiredis) [![Build Status](https://travis-ci.org/redis/hiredis.png)](https://travis-ci.org/redis/hiredis)
**This Readme reflects the latest changed in the master branch. See [v0.13.3](https://github.com/redis/hiredis/tree/v0.13.3) for the Readme and documentation for the latest release.**
# HIREDIS # HIREDIS
Hiredis is a minimalistic C client library for the [Redis](http://redis.io/) database. Hiredis is a minimalistic C client library for the [Redis](http://redis.io/) database.
@ -22,15 +20,7 @@ Redis version >= 1.2.0.
The library comes with multiple APIs. There is the The library comes with multiple APIs. There is the
*synchronous API*, the *asynchronous API* and the *reply parsing API*. *synchronous API*, the *asynchronous API* and the *reply parsing API*.
## Upgrading to `1.0.0` ## UPGRADING
Version 1.0.0 marks a stable release of hiredis.
It includes some minor breaking changes, mostly to make the exposed API more uniform and self-explanatory.
It also bundles the updated `sds` library, to sync up with upstream and Redis.
For most applications a recompile against the new hiredis should be enough.
For code changes see the [Changelog](CHANGELOG.md).
## Upgrading from `<0.9.0`
Version 0.9.0 is a major overhaul of hiredis in every aspect. However, upgrading existing Version 0.9.0 is a major overhaul of hiredis in every aspect. However, upgrading existing
code using hiredis should not be a big pain. The key thing to keep in mind when code using hiredis should not be a big pain. The key thing to keep in mind when
@ -58,18 +48,12 @@ After trying to connect to Redis using `redisConnect` you should
check the `err` field to see if establishing the connection was successful: check the `err` field to see if establishing the connection was successful:
```c ```c
redisContext *c = redisConnect("127.0.0.1", 6379); redisContext *c = redisConnect("127.0.0.1", 6379);
if (c == NULL || c->err) { if (c != NULL && c->err) {
if (c) { printf("Error: %s\n", c->errstr);
printf("Error: %s\n", c->errstr); // handle error
// handle error
} else {
printf("Can't allocate redis context\n");
}
} }
``` ```
*Note: A `redisContext` is not thread-safe.*
### Sending commands ### Sending commands
There are several ways to issue commands to Redis. The first that will be introduced is There are several ways to issue commands to Redis. The first that will be introduced is
@ -257,9 +241,6 @@ Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The
should be checked after creation to see if there were errors creating the connection. 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 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. instantly return if the specified host and port is able to accept a connection.
*Note: A `redisAsyncContext` is not thread-safe.*
```c ```c
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) { if (c->err) {

View File

@ -30,13 +30,13 @@
#ifndef __HIREDIS_LIBEVENT_H__ #ifndef __HIREDIS_LIBEVENT_H__
#define __HIREDIS_LIBEVENT_H__ #define __HIREDIS_LIBEVENT_H__
#include <event2/event.h> #include <event.h>
#include "../hiredis.h" #include "../hiredis.h"
#include "../async.h" #include "../async.h"
typedef struct redisLibeventEvents { typedef struct redisLibeventEvents {
redisAsyncContext *context; redisAsyncContext *context;
struct event *rev, *wev; struct event rev, wev;
} redisLibeventEvents; } redisLibeventEvents;
static void redisLibeventReadEvent(int fd, short event, void *arg) { static void redisLibeventReadEvent(int fd, short event, void *arg) {
@ -53,28 +53,28 @@ static void redisLibeventWriteEvent(int fd, short event, void *arg) {
static void redisLibeventAddRead(void *privdata) { static void redisLibeventAddRead(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata; redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_add(e->rev,NULL); event_add(&e->rev,NULL);
} }
static void redisLibeventDelRead(void *privdata) { static void redisLibeventDelRead(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata; redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(e->rev); event_del(&e->rev);
} }
static void redisLibeventAddWrite(void *privdata) { static void redisLibeventAddWrite(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata; redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_add(e->wev,NULL); event_add(&e->wev,NULL);
} }
static void redisLibeventDelWrite(void *privdata) { static void redisLibeventDelWrite(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata; redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(e->wev); event_del(&e->wev);
} }
static void redisLibeventCleanup(void *privdata) { static void redisLibeventCleanup(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata; redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(e->rev); event_del(&e->rev);
event_del(e->wev); event_del(&e->wev);
free(e); free(e);
} }
@ -99,10 +99,10 @@ static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
ac->ev.data = e; ac->ev.data = e;
/* Initialize and install read/write events */ /* Initialize and install read/write events */
e->rev = event_new(base, c->fd, EV_READ, redisLibeventReadEvent, e); event_set(&e->rev,c->fd,EV_READ,redisLibeventReadEvent,e);
e->wev = event_new(base, c->fd, EV_WRITE, redisLibeventWriteEvent, e); event_set(&e->wev,c->fd,EV_WRITE,redisLibeventWriteEvent,e);
event_add(e->rev, NULL); event_base_set(base,&e->rev);
event_add(e->wev, NULL); event_base_set(base,&e->wev);
return REDIS_OK; return REDIS_OK;
} }
#endif #endif

View File

@ -20,10 +20,10 @@ static void redisLibuvPoll(uv_poll_t* handle, int status, int events) {
return; return;
} }
if (p->context != NULL && (events & UV_READABLE)) { if (events & UV_READABLE) {
redisAsyncHandleRead(p->context); redisAsyncHandleRead(p->context);
} }
if (p->context != NULL && (events & UV_WRITABLE)) { if (events & UV_WRITABLE) {
redisAsyncHandleWrite(p->context); redisAsyncHandleWrite(p->context);
} }
} }
@ -83,7 +83,6 @@ static void on_close(uv_handle_t* handle) {
static void redisLibuvCleanup(void *privdata) { static void redisLibuvCleanup(void *privdata) {
redisLibuvEvents* p = (redisLibuvEvents*)privdata; redisLibuvEvents* p = (redisLibuvEvents*)privdata;
p->context = NULL; // indicate that context might no longer exist
uv_close((uv_handle_t*)&p->handle, on_close); uv_close((uv_handle_t*)&p->handle, on_close);
} }

View File

@ -1,36 +0,0 @@
# Appveyor configuration file for CI build of hiredis on Windows (under Cygwin)
environment:
matrix:
- CYG_ROOT: C:\cygwin64
CYG_SETUP: setup-x86_64.exe
CYG_MIRROR: http://cygwin.mirror.constant.com
CYG_CACHE: C:\cygwin64\var\cache\setup
CYG_BASH: C:\cygwin64\bin\bash
CC: gcc
- CYG_ROOT: C:\cygwin
CYG_SETUP: setup-x86.exe
CYG_MIRROR: http://cygwin.mirror.constant.com
CYG_CACHE: C:\cygwin\var\cache\setup
CYG_BASH: C:\cygwin\bin\bash
CC: gcc
TARGET: 32bit
TARGET_VARS: 32bit-vars
# Cache Cygwin files to speed up build
cache:
- '%CYG_CACHE%'
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:
- ps: 'Start-FileDownload "http://cygwin.com/$env:CYG_SETUP" -FileName "$env:CYG_SETUP"'
- '%CYG_SETUP% --quiet-mode --no-shortcuts --only-site --root "%CYG_ROOT%" --site "%CYG_MIRROR%" --local-package-dir "%CYG_CACHE%" --packages automake,bison,gcc-core,libtool,make,gettext-devel,gettext,intltool,pkg-config,clang,llvm > NUL 2>&1'
- '%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"'

View File

@ -489,7 +489,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
} }
/* Internal helper function to detect socket status the first time a read or /* Internal helper function to detect socket status the first time a read or
* write event fires. When connecting was not successful, the connect callback * write event fires. When connecting was not succesful, the connect callback
* is called with a REDIS_ERR status and the context is free'd. */ * is called with a REDIS_ERR status and the context is free'd. */
static int __redisAsyncHandleConnect(redisAsyncContext *ac) { static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
redisContext *c = &(ac->c); redisContext *c = &(ac->c);

View File

@ -161,7 +161,7 @@ static int dictReplace(dict *ht, void *key, void *val) {
dictEntry *entry, auxentry; dictEntry *entry, auxentry;
/* Try to add the element. If the key /* Try to add the element. If the key
* does not exists dictAdd will succeed. */ * does not exists dictAdd will suceed. */
if (dictAdd(ht, key, val) == DICT_OK) if (dictAdd(ht, key, val) == DICT_OK)
return 1; return 1;
/* It already exists, get the entry */ /* It already exists, get the entry */
@ -293,7 +293,7 @@ static void dictReleaseIterator(dictIterator *iter) {
/* Expand the hash table if needed */ /* Expand the hash table if needed */
static int _dictExpandIfNeeded(dict *ht) { static int _dictExpandIfNeeded(dict *ht) {
/* If the hash table is empty expand it to the initial size, /* If the hash table is empty expand it to the intial size,
* if the table is "full" dobule its size. */ * if the table is "full" dobule its size. */
if (ht->size == 0) if (ht->size == 0)
return dictExpand(ht, DICT_HT_INITIAL_SIZE); return dictExpand(ht, DICT_HT_INITIAL_SIZE);

View File

@ -57,7 +57,7 @@ int main(int argc, char **argv) {
for (j = 0; j < 10; j++) { for (j = 0; j < 10; j++) {
char buf[64]; char buf[64];
snprintf(buf,64,"%u",j); snprintf(buf,64,"%d",j);
reply = redisCommand(c,"LPUSH mylist element-%s", buf); reply = redisCommand(c,"LPUSH mylist element-%s", buf);
freeReplyObject(reply); freeReplyObject(reply);
} }

View File

@ -6,19 +6,15 @@
#define _DEFAULT_SOURCE #define _DEFAULT_SOURCE
#endif #endif
#if defined(__CYGWIN__)
#include <sys/cdefs.h>
#endif
#if defined(__sun__) #if defined(__sun__)
#define _POSIX_C_SOURCE 200112L #define _POSIX_C_SOURCE 200112L
#else #elif defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__)
#if !(defined(__APPLE__) && defined(__MACH__))
#define _XOPEN_SOURCE 600 #define _XOPEN_SOURCE 600
#endif #else
#define _XOPEN_SOURCE
#endif #endif
#if defined(__APPLE__) && defined(__MACH__) #if __APPLE__ && __MACH__
#define _OSX #define _OSX
#endif #endif

View File

@ -507,7 +507,7 @@ int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv,
cmd = sdscatfmt(cmd, "*%i\r\n", argc); cmd = sdscatfmt(cmd, "*%i\r\n", argc);
for (j=0; j < argc; j++) { for (j=0; j < argc; j++) {
len = argvlen ? argvlen[j] : strlen(argv[j]); len = argvlen ? argvlen[j] : strlen(argv[j]);
cmd = sdscatfmt(cmd, "$%u\r\n", len); cmd = sdscatfmt(cmd, "$%T\r\n", len);
cmd = sdscatlen(cmd, argv[j], len); cmd = sdscatlen(cmd, argv[j], len);
cmd = sdscatlen(cmd, "\r\n", sizeof("\r\n")-1); cmd = sdscatlen(cmd, "\r\n", sizeof("\r\n")-1);
} }
@ -822,10 +822,10 @@ int redisBufferRead(redisContext *c) {
/* Write the output buffer to the socket. /* Write the output buffer to the socket.
* *
* Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was
* successfully written to the socket. When the buffer is empty after the * succesfully written to the socket. When the buffer is empty after the
* write operation, "done" is set to 1 (if given). * write operation, "done" is set to 1 (if given).
* *
* Returns REDIS_ERR if an error occurred trying to write and sets * Returns REDIS_ERR if an error occured trying to write and sets
* c->errstr to hold the appropriate error string. * c->errstr to hold the appropriate error string.
*/ */
int redisBufferWrite(redisContext *c, int *done) { int redisBufferWrite(redisContext *c, int *done) {
@ -984,7 +984,7 @@ int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const s
* context is non-blocking, the "reply" pointer will not be used and the * context is non-blocking, the "reply" pointer will not be used and the
* command is simply appended to the write buffer. * command is simply appended to the write buffer.
* *
* Returns the reply when a reply was successfully retrieved. Returns NULL * Returns the reply when a reply was succesfully retrieved. Returns NULL
* otherwise. When NULL is returned in a blocking context, the error field * otherwise. When NULL is returned in a blocking context, the error field
* in the context will be set. * in the context will be set.
*/ */

View File

@ -98,8 +98,8 @@
* then GNU strerror_r returned an internal static buffer and we \ * then GNU strerror_r returned an internal static buffer and we \
* need to copy the result into our private buffer. */ \ * need to copy the result into our private buffer. */ \
if (err_str != (buf)) { \ if (err_str != (buf)) { \
strncpy((buf), err_str, ((len) - 1)); \ buf[(len)] = '\0'; \
buf[(len)-1] = '\0'; \ strncat((buf), err_str, ((len) - 1)); \
} \ } \
} while (0) } while (0)
#endif #endif
@ -112,7 +112,7 @@ extern "C" {
typedef struct redisReply { typedef struct redisReply {
int type; /* REDIS_REPLY_* */ int type; /* REDIS_REPLY_* */
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */ long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
size_t len; /* Length of string */ int len; /* Length of string */
char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */ char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */ size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */ struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
@ -133,7 +133,7 @@ void redisFreeSdsCommand(sds cmd);
enum redisConnectionType { enum redisConnectionType {
REDIS_CONN_TCP, REDIS_CONN_TCP,
REDIS_CONN_UNIX REDIS_CONN_UNIX,
}; };
/* Context for a connection to Redis */ /* Context for a connection to Redis */
@ -179,7 +179,7 @@ redisContext *redisConnectFd(int fd);
* host, ip (or path), timeout and bind address are reused, * host, ip (or path), timeout and bind address are reused,
* flags are used unmodified from the existing context. * flags are used unmodified from the existing context.
* *
* Returns REDIS_OK on successful connect or REDIS_ERR otherwise. * Returns REDIS_OK on successfull connect or REDIS_ERR otherwise.
*/ */
int redisReconnect(redisContext *c); int redisReconnect(redisContext *c);

View File

@ -65,13 +65,12 @@ static void redisContextCloseFd(redisContext *c) {
} }
static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) { static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
int errorno = errno; /* snprintf() may change errno */
char buf[128] = { 0 }; char buf[128] = { 0 };
size_t len = 0; size_t len = 0;
if (prefix != NULL) if (prefix != NULL)
len = snprintf(buf,sizeof(buf),"%s: ",prefix); len = snprintf(buf,sizeof(buf),"%s: ",prefix);
__redis_strerror_r(errorno, (char *)(buf + len), sizeof(buf) - len); __redis_strerror_r(errno, (char *)(buf + len), sizeof(buf) - len);
__redisSetError(c,type,buf); __redisSetError(c,type,buf);
} }
@ -179,15 +178,19 @@ static int redisSetTcpNoDelay(redisContext *c) {
#define __MAX_MSEC (((LONG_MAX) - 999) / 1000) #define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
static int redisContextTimeoutMsec(redisContext *c, long *result) static int redisContextWaitReady(redisContext *c, const struct timeval *timeout) {
{ struct pollfd wfd[1];
const struct timeval *timeout = c->timeout; long msec;
long msec = -1;
msec = -1;
wfd[0].fd = c->fd;
wfd[0].events = POLLOUT;
/* Only use timeout when not NULL. */ /* Only use timeout when not NULL. */
if (timeout != NULL) { if (timeout != NULL) {
if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) { if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
*result = msec; __redisSetErrorFromErrno(c, REDIS_ERR_IO, NULL);
redisContextCloseFd(c);
return REDIS_ERR; return REDIS_ERR;
} }
@ -198,16 +201,6 @@ static int redisContextTimeoutMsec(redisContext *c, long *result)
} }
} }
*result = msec;
return REDIS_OK;
}
static int redisContextWaitReady(redisContext *c, long msec) {
struct pollfd wfd[1];
wfd[0].fd = c->fd;
wfd[0].events = POLLOUT;
if (errno == EINPROGRESS) { if (errno == EINPROGRESS) {
int res; int res;
@ -272,9 +265,7 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
int blocking = (c->flags & REDIS_BLOCK); int blocking = (c->flags & REDIS_BLOCK);
int reuseaddr = (c->flags & REDIS_REUSEADDR); int reuseaddr = (c->flags & REDIS_REUSEADDR);
int reuses = 0; int reuses = 0;
long timeout_msec = -1;
servinfo = NULL;
c->connection_type = REDIS_CONN_TCP; c->connection_type = REDIS_CONN_TCP;
c->tcp.port = port; c->tcp.port = port;
@ -305,11 +296,6 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
c->timeout = NULL; c->timeout = NULL;
} }
if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {
__redisSetError(c, REDIS_ERR_IO, "Invalid timeout specified");
goto error;
}
if (source_addr == NULL) { if (source_addr == NULL) {
free(c->tcp.source_addr); free(c->tcp.source_addr);
c->tcp.source_addr = NULL; c->tcp.source_addr = NULL;
@ -385,11 +371,10 @@ addrretry:
if (++reuses >= REDIS_CONNECT_RETRIES) { if (++reuses >= REDIS_CONNECT_RETRIES) {
goto error; goto error;
} else { } else {
redisContextCloseFd(c);
goto addrretry; goto addrretry;
} }
} else { } else {
if (redisContextWaitReady(c,timeout_msec) != REDIS_OK) if (redisContextWaitReady(c,c->timeout) != REDIS_OK)
goto error; goto error;
} }
} }
@ -430,7 +415,6 @@ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) { int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
int blocking = (c->flags & REDIS_BLOCK); int blocking = (c->flags & REDIS_BLOCK);
struct sockaddr_un sa; struct sockaddr_un sa;
long timeout_msec = -1;
if (redisCreateSocket(c,AF_LOCAL) < 0) if (redisCreateSocket(c,AF_LOCAL) < 0)
return REDIS_ERR; return REDIS_ERR;
@ -454,16 +438,13 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time
c->timeout = NULL; c->timeout = NULL;
} }
if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)
return REDIS_ERR;
sa.sun_family = AF_LOCAL; sa.sun_family = AF_LOCAL;
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1); strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
if (connect(c->fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) { if (connect(c->fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
if (errno == EINPROGRESS && !blocking) { if (errno == EINPROGRESS && !blocking) {
/* This is ok. */ /* This is ok. */
} else { } else {
if (redisContextWaitReady(c,timeout_msec) != REDIS_OK) if (redisContextWaitReady(c,c->timeout) != REDIS_OK)
return REDIS_ERR; return REDIS_ERR;
} }
} }

View File

@ -127,7 +127,7 @@ static char *seekNewline(char *s, size_t len) {
* might not have a trailing NULL character. */ * might not have a trailing NULL character. */
while (pos < _len) { while (pos < _len) {
while(pos < _len && s[pos] != '\r') pos++; while(pos < _len && s[pos] != '\r') pos++;
if (pos==_len) { if (s[pos] != '\r') {
/* Not found. */ /* Not found. */
return NULL; return NULL;
} else { } else {

View File

@ -38,7 +38,7 @@
#define REDIS_OK 0 #define REDIS_OK 0
/* When an error occurs, the err flag in a context is set to hold the type of /* 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 * error that occured. REDIS_ERR_IO means there was an I/O error and you
* should use the "errno" variable to find out what is wrong. * should use the "errno" variable to find out what is wrong.
* For other values, the "errstr" field will hold a description. */ * For other values, the "errstr" field will hold a description. */
#define REDIS_ERR_IO 1 /* Error in read or write */ #define REDIS_ERR_IO 1 /* Error in read or write */
@ -100,9 +100,14 @@ void redisReaderFree(redisReader *r);
int redisReaderFeed(redisReader *r, const char *buf, size_t len); int redisReaderFeed(redisReader *r, const char *buf, size_t len);
int redisReaderGetReply(redisReader *r, void **reply); int redisReaderGetReply(redisReader *r, void **reply);
#define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p)) /* Backwards compatibility, can be removed on big version bump. */
#define redisReaderGetObject(_r) (((redisReader*)(_r))->reply) #define redisReplyReaderCreate redisReaderCreate
#define redisReaderGetError(_r) (((redisReader*)(_r))->errstr) #define redisReplyReaderFree redisReaderFree
#define redisReplyReaderFeed redisReaderFeed
#define redisReplyReaderGetReply redisReaderGetReply
#define redisReplyReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))
#define redisReplyReaderGetObject(_r) (((redisReader*)(_r))->reply)
#define redisReplyReaderGetError(_r) (((redisReader*)(_r))->errstr)
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -1,8 +1,6 @@
/* SDSLib 2.0 -- A C dynamic strings library /* SDS (Simple Dynamic Strings), A C dynamic strings library.
* *
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2006-2014, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2015, Oran Agra
* Copyright (c) 2015, Redis Labs, Inc
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -35,36 +33,8 @@
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <assert.h> #include <assert.h>
#include "sds.h" #include "sds.h"
#include "sdsalloc.h"
static inline int sdsHdrSize(char type) {
switch(type&SDS_TYPE_MASK) {
case SDS_TYPE_5:
return sizeof(struct sdshdr5);
case SDS_TYPE_8:
return sizeof(struct sdshdr8);
case SDS_TYPE_16:
return sizeof(struct sdshdr16);
case SDS_TYPE_32:
return sizeof(struct sdshdr32);
case SDS_TYPE_64:
return sizeof(struct sdshdr64);
}
return 0;
}
static inline char sdsReqType(size_t string_size) {
if (string_size < 32)
return SDS_TYPE_5;
if (string_size < 0xff)
return SDS_TYPE_8;
if (string_size < 0xffff)
return SDS_TYPE_16;
if (string_size < 0xffffffff)
return SDS_TYPE_32;
return SDS_TYPE_64;
}
/* Create a new sds string with the content specified by the 'init' pointer /* Create a new sds string with the content specified by the 'init' pointer
* and 'initlen'. * and 'initlen'.
@ -73,65 +43,26 @@ static inline char sdsReqType(size_t string_size) {
* The string is always null-termined (all the sds strings are, always) so * The string is always null-termined (all the sds strings are, always) so
* even if you create an sds string with: * even if you create an sds string with:
* *
* mystring = sdsnewlen("abc",3); * mystring = sdsnewlen("abc",3");
* *
* You can print the string with printf() as there is an implicit \0 at the * You can print the string with printf() as there is an implicit \0 at the
* end of the string. However the string is binary safe and can contain * end of the string. However the string is binary safe and can contain
* \0 characters in the middle, as the length is stored in the sds header. */ * \0 characters in the middle, as the length is stored in the sds header. */
sds sdsnewlen(const void *init, size_t initlen) { sds sdsnewlen(const void *init, size_t initlen) {
void *sh; struct sdshdr *sh;
sds s;
char type = sdsReqType(initlen);
/* Empty strings are usually created in order to append. Use type 8
* since type 5 is not good at this. */
if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;
int hdrlen = sdsHdrSize(type);
unsigned char *fp; /* flags pointer. */
sh = s_malloc(hdrlen+initlen+1); if (init) {
if (sh == NULL) return NULL; sh = malloc(sizeof *sh+initlen+1);
if (!init) } else {
memset(sh, 0, hdrlen+initlen+1); sh = calloc(sizeof *sh+initlen+1,1);
s = (char*)sh+hdrlen;
fp = ((unsigned char*)s)-1;
switch(type) {
case SDS_TYPE_5: {
*fp = type | (initlen << SDS_TYPE_BITS);
break;
}
case SDS_TYPE_8: {
SDS_HDR_VAR(8,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
case SDS_TYPE_16: {
SDS_HDR_VAR(16,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
case SDS_TYPE_32: {
SDS_HDR_VAR(32,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
case SDS_TYPE_64: {
SDS_HDR_VAR(64,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
} }
if (sh == NULL) return NULL;
sh->len = initlen;
sh->free = 0;
if (initlen && init) if (initlen && init)
memcpy(s, init, initlen); memcpy(sh->buf, init, initlen);
s[initlen] = '\0'; sh->buf[initlen] = '\0';
return s; return (char*)sh->buf;
} }
/* Create an empty (zero length) sds string. Even in this case the string /* Create an empty (zero length) sds string. Even in this case the string
@ -140,7 +71,7 @@ sds sdsempty(void) {
return sdsnewlen("",0); return sdsnewlen("",0);
} }
/* Create a new sds string starting from a null terminated C string. */ /* Create a new sds string starting from a null termined C string. */
sds sdsnew(const char *init) { sds sdsnew(const char *init) {
size_t initlen = (init == NULL) ? 0 : strlen(init); size_t initlen = (init == NULL) ? 0 : strlen(init);
return sdsnewlen(init, initlen); return sdsnewlen(init, initlen);
@ -154,7 +85,7 @@ sds sdsdup(const sds s) {
/* Free an sds string. No operation is performed if 's' is NULL. */ /* Free an sds string. No operation is performed if 's' is NULL. */
void sdsfree(sds s) { void sdsfree(sds s) {
if (s == NULL) return; if (s == NULL) return;
s_free((char*)s-sdsHdrSize(s[-1])); free(s-sizeof(struct sdshdr));
} }
/* Set the sds string length to the length as obtained with strlen(), so /* Set the sds string length to the length as obtained with strlen(), so
@ -172,17 +103,21 @@ void sdsfree(sds s) {
* the output will be "6" as the string was modified but the logical length * the output will be "6" as the string was modified but the logical length
* remains 6 bytes. */ * remains 6 bytes. */
void sdsupdatelen(sds s) { void sdsupdatelen(sds s) {
struct sdshdr *sh = (void*) (s-sizeof *sh);
int reallen = strlen(s); int reallen = strlen(s);
sdssetlen(s, reallen); sh->free += (sh->len-reallen);
sh->len = reallen;
} }
/* Modify an sds string in-place to make it empty (zero length). /* Modify an sds string on-place to make it empty (zero length).
* However all the existing buffer is not discarded but set as free space * However all the existing buffer is not discarded but set as free space
* so that next append operations will not require allocations up to the * so that next append operations will not require allocations up to the
* number of bytes previously available. */ * number of bytes previously available. */
void sdsclear(sds s) { void sdsclear(sds s) {
sdssetlen(s, 0); struct sdshdr *sh = (void*) (s-sizeof *sh);
s[0] = '\0'; sh->free += sh->len;
sh->len = 0;
sh->buf[0] = '\0';
} }
/* Enlarge the free space at the end of the sds string so that the caller /* Enlarge the free space at the end of the sds string so that the caller
@ -192,48 +127,23 @@ void sdsclear(sds s) {
* Note: this does not change the *length* of the sds string as returned * Note: this does not change the *length* of the sds string as returned
* by sdslen(), but only the free buffer space we have. */ * by sdslen(), but only the free buffer space we have. */
sds sdsMakeRoomFor(sds s, size_t addlen) { sds sdsMakeRoomFor(sds s, size_t addlen) {
void *sh, *newsh; struct sdshdr *sh, *newsh;
size_t avail = sdsavail(s); size_t free = sdsavail(s);
size_t len, newlen; size_t len, newlen;
char type, oldtype = s[-1] & SDS_TYPE_MASK;
int hdrlen;
/* Return ASAP if there is enough space left. */
if (avail >= addlen) return s;
if (free >= addlen) return s;
len = sdslen(s); len = sdslen(s);
sh = (char*)s-sdsHdrSize(oldtype); sh = (void*) (s-sizeof *sh);
newlen = (len+addlen); newlen = (len+addlen);
if (newlen < SDS_MAX_PREALLOC) if (newlen < SDS_MAX_PREALLOC)
newlen *= 2; newlen *= 2;
else else
newlen += SDS_MAX_PREALLOC; newlen += SDS_MAX_PREALLOC;
newsh = realloc(sh, sizeof *newsh+newlen+1);
if (newsh == NULL) return NULL;
type = sdsReqType(newlen); newsh->free = newlen - len;
return newsh->buf;
/* Don't use type 5: the user is appending to the string and type 5 is
* not able to remember empty space, so sdsMakeRoomFor() must be called
* at every appending operation. */
if (type == SDS_TYPE_5) type = SDS_TYPE_8;
hdrlen = sdsHdrSize(type);
if (oldtype==type) {
newsh = s_realloc(sh, hdrlen+newlen+1);
if (newsh == NULL) return NULL;
s = (char*)newsh+hdrlen;
} else {
/* Since the header size changes, need to move the string forward,
* and can't use realloc */
newsh = s_malloc(hdrlen+newlen+1);
if (newsh == NULL) return NULL;
memcpy((char*)newsh+hdrlen, s, len+1);
s_free(sh);
s = (char*)newsh+hdrlen;
s[-1] = type;
sdssetlen(s, len);
}
sdssetalloc(s, newlen);
return s;
} }
/* Reallocate the sds string so that it has no free space at the end. The /* Reallocate the sds string so that it has no free space at the end. The
@ -243,29 +153,12 @@ sds sdsMakeRoomFor(sds s, size_t addlen) {
* After the call, the passed sds string is no longer valid and all the * After the call, the passed sds string is no longer valid and all the
* references must be substituted with the new pointer returned by the call. */ * references must be substituted with the new pointer returned by the call. */
sds sdsRemoveFreeSpace(sds s) { sds sdsRemoveFreeSpace(sds s) {
void *sh, *newsh; struct sdshdr *sh;
char type, oldtype = s[-1] & SDS_TYPE_MASK;
int hdrlen;
size_t len = sdslen(s);
sh = (char*)s-sdsHdrSize(oldtype);
type = sdsReqType(len); sh = (void*) (s-sizeof *sh);
hdrlen = sdsHdrSize(type); sh = realloc(sh, sizeof *sh+sh->len+1);
if (oldtype==type) { sh->free = 0;
newsh = s_realloc(sh, hdrlen+len+1); return sh->buf;
if (newsh == NULL) return NULL;
s = (char*)newsh+hdrlen;
} else {
newsh = s_malloc(hdrlen+len+1);
if (newsh == NULL) return NULL;
memcpy((char*)newsh+hdrlen, s, len+1);
s_free(sh);
s = (char*)newsh+hdrlen;
s[-1] = type;
sdssetlen(s, len);
}
sdssetalloc(s, len);
return s;
} }
/* Return the total size of the allocation of the specifed sds string, /* Return the total size of the allocation of the specifed sds string,
@ -276,14 +169,9 @@ sds sdsRemoveFreeSpace(sds s) {
* 4) The implicit null term. * 4) The implicit null term.
*/ */
size_t sdsAllocSize(sds s) { size_t sdsAllocSize(sds s) {
size_t alloc = sdsalloc(s); struct sdshdr *sh = (void*) (s-sizeof *sh);
return sdsHdrSize(s[-1])+alloc+1;
}
/* Return the pointer of the actual SDS allocation (normally SDS strings return sizeof(*sh)+sh->len+sh->free+1;
* are referenced by the start of the string buffer). */
void *sdsAllocPtr(sds s) {
return (void*) (s-sdsHdrSize(s[-1]));
} }
/* Increment the sds length and decrements the left free space at the /* Increment the sds length and decrements the left free space at the
@ -310,44 +198,13 @@ void *sdsAllocPtr(sds s) {
* sdsIncrLen(s, nread); * sdsIncrLen(s, nread);
*/ */
void sdsIncrLen(sds s, int incr) { void sdsIncrLen(sds s, int incr) {
unsigned char flags = s[-1]; struct sdshdr *sh = (void*) (s-sizeof *sh);
size_t len;
switch(flags&SDS_TYPE_MASK) { assert(sh->free >= incr);
case SDS_TYPE_5: { sh->len += incr;
unsigned char *fp = ((unsigned char*)s)-1; sh->free -= incr;
unsigned char oldlen = SDS_TYPE_5_LEN(flags); assert(sh->free >= 0);
assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr))); s[sh->len] = '\0';
*fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS);
len = oldlen+incr;
break;
}
case SDS_TYPE_8: {
SDS_HDR_VAR(8,s);
assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
len = (sh->len += incr);
break;
}
case SDS_TYPE_16: {
SDS_HDR_VAR(16,s);
assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
len = (sh->len += incr);
break;
}
case SDS_TYPE_32: {
SDS_HDR_VAR(32,s);
assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
len = (sh->len += incr);
break;
}
case SDS_TYPE_64: {
SDS_HDR_VAR(64,s);
assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr)));
len = (sh->len += incr);
break;
}
default: len = 0; /* Just to avoid compilation warnings. */
}
s[len] = '\0';
} }
/* Grow the sds to have the specified length. Bytes that were not part of /* Grow the sds to have the specified length. Bytes that were not part of
@ -356,15 +213,19 @@ void sdsIncrLen(sds s, int incr) {
* if the specified length is smaller than the current length, no operation * if the specified length is smaller than the current length, no operation
* is performed. */ * is performed. */
sds sdsgrowzero(sds s, size_t len) { sds sdsgrowzero(sds s, size_t len) {
size_t curlen = sdslen(s); struct sdshdr *sh = (void*) (s-sizeof *sh);
size_t totlen, curlen = sh->len;
if (len <= curlen) return s; if (len <= curlen) return s;
s = sdsMakeRoomFor(s,len-curlen); s = sdsMakeRoomFor(s,len-curlen);
if (s == NULL) return NULL; if (s == NULL) return NULL;
/* Make sure added region doesn't contain garbage */ /* Make sure added region doesn't contain garbage */
sh = (void*)(s-sizeof *sh);
memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */ memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
sdssetlen(s, len); totlen = sh->len+sh->free;
sh->len = len;
sh->free = totlen-sh->len;
return s; return s;
} }
@ -374,12 +235,15 @@ sds sdsgrowzero(sds s, size_t len) {
* After the call, the passed sds string is no longer valid and all the * After the call, the passed sds string is no longer valid and all the
* references must be substituted with the new pointer returned by the call. */ * references must be substituted with the new pointer returned by the call. */
sds sdscatlen(sds s, const void *t, size_t len) { sds sdscatlen(sds s, const void *t, size_t len) {
struct sdshdr *sh;
size_t curlen = sdslen(s); size_t curlen = sdslen(s);
s = sdsMakeRoomFor(s,len); s = sdsMakeRoomFor(s,len);
if (s == NULL) return NULL; if (s == NULL) return NULL;
sh = (void*) (s-sizeof *sh);
memcpy(s+curlen, t, len); memcpy(s+curlen, t, len);
sdssetlen(s, curlen+len); sh->len = curlen+len;
sh->free = sh->free-len;
s[curlen+len] = '\0'; s[curlen+len] = '\0';
return s; return s;
} }
@ -403,13 +267,19 @@ sds sdscatsds(sds s, const sds t) {
/* Destructively modify the sds string 's' to hold the specified binary /* Destructively modify the sds string 's' to hold the specified binary
* safe string pointed by 't' of length 'len' bytes. */ * safe string pointed by 't' of length 'len' bytes. */
sds sdscpylen(sds s, const char *t, size_t len) { sds sdscpylen(sds s, const char *t, size_t len) {
if (sdsalloc(s) < len) { struct sdshdr *sh = (void*) (s-sizeof *sh);
s = sdsMakeRoomFor(s,len-sdslen(s)); size_t totlen = sh->free+sh->len;
if (totlen < len) {
s = sdsMakeRoomFor(s,len-sh->len);
if (s == NULL) return NULL; if (s == NULL) return NULL;
sh = (void*) (s-sizeof *sh);
totlen = sh->free+sh->len;
} }
memcpy(s, t, len); memcpy(s, t, len);
s[len] = '\0'; s[len] = '\0';
sdssetlen(s, len); sh->len = len;
sh->free = totlen-len;
return s; return s;
} }
@ -423,7 +293,7 @@ sds sdscpy(sds s, const char *t) {
* conversion. 's' must point to a string with room for at least * conversion. 's' must point to a string with room for at least
* SDS_LLSTR_SIZE bytes. * SDS_LLSTR_SIZE bytes.
* *
* The function returns the length of the null-terminated string * The function returns the lenght of the null-terminated string
* representation stored at 's'. */ * representation stored at 's'. */
#define SDS_LLSTR_SIZE 21 #define SDS_LLSTR_SIZE 21
int sdsll2str(char *s, long long value) { int sdsll2str(char *s, long long value) {
@ -486,52 +356,27 @@ int sdsull2str(char *s, unsigned long long v) {
return l; return l;
} }
/* Create an sds string from a long long value. It is much faster than: /* Like sdscatpritf() but gets va_list instead of being variadic. */
*
* sdscatprintf(sdsempty(),"%lld\n", value);
*/
sds sdsfromlonglong(long long value) {
char buf[SDS_LLSTR_SIZE];
int len = sdsll2str(buf,value);
return sdsnewlen(buf,len);
}
/* Like sdscatprintf() but gets va_list instead of being variadic. */
sds sdscatvprintf(sds s, const char *fmt, va_list ap) { sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
va_list cpy; va_list cpy;
char staticbuf[1024], *buf = staticbuf, *t; char *buf, *t;
size_t buflen = strlen(fmt)*2; size_t buflen = 16;
/* We try to start using a static buffer for speed.
* If not possible we revert to heap allocation. */
if (buflen > sizeof(staticbuf)) {
buf = s_malloc(buflen);
if (buf == NULL) return NULL;
} else {
buflen = sizeof(staticbuf);
}
/* Try with buffers two times bigger every time we fail to
* fit the string in the current buffer size. */
while(1) { while(1) {
buf = malloc(buflen);
if (buf == NULL) return NULL;
buf[buflen-2] = '\0'; buf[buflen-2] = '\0';
va_copy(cpy,ap); va_copy(cpy,ap);
vsnprintf(buf, buflen, fmt, cpy); vsnprintf(buf, buflen, fmt, cpy);
va_end(cpy);
if (buf[buflen-2] != '\0') { if (buf[buflen-2] != '\0') {
if (buf != staticbuf) s_free(buf); free(buf);
buflen *= 2; buflen *= 2;
buf = s_malloc(buflen);
if (buf == NULL) return NULL;
continue; continue;
} }
break; break;
} }
/* Finally concat the obtained string to the SDS string and return it. */
t = sdscat(s, buf); t = sdscat(s, buf);
if (buf != staticbuf) s_free(buf); free(buf);
return t; return t;
} }
@ -544,7 +389,7 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
* Example: * Example:
* *
* s = sdsnew("Sum is: "); * s = sdsnew("Sum is: ");
* s = sdscatprintf(s,"%d+%d = %d",a,b,a+b). * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b);
* *
* Often you need to create a string from scratch with the printf-alike * Often you need to create a string from scratch with the printf-alike
* format. When this is the need, just use sdsempty() as the target string: * format. When this is the need, just use sdsempty() as the target string:
@ -574,24 +419,29 @@ sds sdscatprintf(sds s, const char *fmt, ...) {
* %I - 64 bit signed integer (long long, int64_t) * %I - 64 bit signed integer (long long, int64_t)
* %u - unsigned int * %u - unsigned int
* %U - 64 bit unsigned integer (unsigned long long, uint64_t) * %U - 64 bit unsigned integer (unsigned long long, uint64_t)
* %T - A size_t variable.
* %% - Verbatim "%" character. * %% - Verbatim "%" character.
*/ */
sds sdscatfmt(sds s, char const *fmt, ...) { sds sdscatfmt(sds s, char const *fmt, ...) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
size_t initlen = sdslen(s);
const char *f = fmt; const char *f = fmt;
int i; int i;
va_list ap; va_list ap;
va_start(ap,fmt); va_start(ap,fmt);
i = sdslen(s); /* Position of the next byte to write to dest str. */ f = fmt; /* Next format specifier byte to process. */
i = initlen; /* Position of the next byte to write to dest str. */
while(*f) { while(*f) {
char next, *str; char next, *str;
size_t l; int l;
long long num; long long num;
unsigned long long unum; unsigned long long unum;
/* Make sure there is always space for at least 1 char. */ /* Make sure there is always space for at least 1 char. */
if (sdsavail(s)==0) { if (sh->free == 0) {
s = sdsMakeRoomFor(s,1); s = sdsMakeRoomFor(s,1);
sh = (void*) (s-(sizeof(struct sdshdr)));
} }
switch(*f) { switch(*f) {
@ -603,11 +453,13 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
case 'S': case 'S':
str = va_arg(ap,char*); str = va_arg(ap,char*);
l = (next == 's') ? strlen(str) : sdslen(str); l = (next == 's') ? strlen(str) : sdslen(str);
if (sdsavail(s) < l) { if (sh->free < l) {
s = sdsMakeRoomFor(s,l); s = sdsMakeRoomFor(s,l);
sh = (void*) (s-(sizeof(struct sdshdr)));
} }
memcpy(s+i,str,l); memcpy(s+i,str,l);
sdsinclen(s,l); sh->len += l;
sh->free -= l;
i += l; i += l;
break; break;
case 'i': case 'i':
@ -619,40 +471,49 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
{ {
char buf[SDS_LLSTR_SIZE]; char buf[SDS_LLSTR_SIZE];
l = sdsll2str(buf,num); l = sdsll2str(buf,num);
if (sdsavail(s) < l) { if (sh->free < l) {
s = sdsMakeRoomFor(s,l); s = sdsMakeRoomFor(s,l);
sh = (void*) (s-(sizeof(struct sdshdr)));
} }
memcpy(s+i,buf,l); memcpy(s+i,buf,l);
sdsinclen(s,l); sh->len += l;
sh->free -= l;
i += l; i += l;
} }
break; break;
case 'u': case 'u':
case 'U': case 'U':
case 'T':
if (next == 'u') if (next == 'u')
unum = va_arg(ap,unsigned int); unum = va_arg(ap,unsigned int);
else else if(next == 'U')
unum = va_arg(ap,unsigned long long); unum = va_arg(ap,unsigned long long);
else
unum = (unsigned long long)va_arg(ap,size_t);
{ {
char buf[SDS_LLSTR_SIZE]; char buf[SDS_LLSTR_SIZE];
l = sdsull2str(buf,unum); l = sdsull2str(buf,unum);
if (sdsavail(s) < l) { if (sh->free < l) {
s = sdsMakeRoomFor(s,l); s = sdsMakeRoomFor(s,l);
sh = (void*) (s-(sizeof(struct sdshdr)));
} }
memcpy(s+i,buf,l); memcpy(s+i,buf,l);
sdsinclen(s,l); sh->len += l;
sh->free -= l;
i += l; i += l;
} }
break; break;
default: /* Handle %% and generally %<unknown>. */ default: /* Handle %% and generally %<unknown>. */
s[i++] = next; s[i++] = next;
sdsinclen(s,1); sh->len += 1;
sh->free -= 1;
break; break;
} }
break; break;
default: default:
s[i++] = *f; s[i++] = *f;
sdsinclen(s,1); sh->len += 1;
sh->free -= 1;
break; break;
} }
f++; f++;
@ -664,6 +525,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
return s; return s;
} }
/* Remove the part of the string from left and from right composed just of /* Remove the part of the string from left and from right composed just of
* contiguous characters found in 'cset', that is a null terminted C string. * contiguous characters found in 'cset', that is a null terminted C string.
* *
@ -673,24 +535,25 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
* Example: * Example:
* *
* s = sdsnew("AA...AA.a.aa.aHelloWorld :::"); * s = sdsnew("AA...AA.a.aa.aHelloWorld :::");
* s = sdstrim(s,"Aa. :"); * s = sdstrim(s,"A. :");
* printf("%s\n", s); * printf("%s\n", s);
* *
* Output will be just "Hello World". * Output will be just "Hello World".
*/ */
sds sdstrim(sds s, const char *cset) { void sdstrim(sds s, const char *cset) {
struct sdshdr *sh = (void*) (s-sizeof *sh);
char *start, *end, *sp, *ep; char *start, *end, *sp, *ep;
size_t len; size_t len;
sp = start = s; sp = start = s;
ep = end = s+sdslen(s)-1; ep = end = s+sdslen(s)-1;
while(sp <= end && strchr(cset, *sp)) sp++; while(sp <= end && strchr(cset, *sp)) sp++;
while(ep > sp && strchr(cset, *ep)) ep--; while(ep > start && strchr(cset, *ep)) ep--;
len = (sp > ep) ? 0 : ((ep-sp)+1); len = (sp > ep) ? 0 : ((ep-sp)+1);
if (s != sp) memmove(s, sp, len); if (sh->buf != sp) memmove(sh->buf, sp, len);
s[len] = '\0'; sh->buf[len] = '\0';
sdssetlen(s,len); sh->free = sh->free+(sh->len-len);
return s; sh->len = len;
} }
/* Turn the string into a smaller (or equal) string containing only the /* Turn the string into a smaller (or equal) string containing only the
@ -710,6 +573,7 @@ sds sdstrim(sds s, const char *cset) {
* sdsrange(s,1,-1); => "ello World" * sdsrange(s,1,-1); => "ello World"
*/ */
void sdsrange(sds s, int start, int end) { void sdsrange(sds s, int start, int end) {
struct sdshdr *sh = (void*) (s-sizeof *sh);
size_t newlen, len = sdslen(s); size_t newlen, len = sdslen(s);
if (len == 0) return; if (len == 0) return;
@ -732,9 +596,10 @@ void sdsrange(sds s, int start, int end) {
} else { } else {
start = 0; start = 0;
} }
if (start && newlen) memmove(s, s+start, newlen); if (start && newlen) memmove(sh->buf, sh->buf+start, newlen);
s[newlen] = 0; sh->buf[newlen] = 0;
sdssetlen(s,newlen); sh->free = sh->free+(sh->len-newlen);
sh->len = newlen;
} }
/* Apply tolower() to every character of the sds string 's'. */ /* Apply tolower() to every character of the sds string 's'. */
@ -755,8 +620,8 @@ void sdstoupper(sds s) {
* *
* Return value: * Return value:
* *
* positive if s1 > s2. * 1 if s1 > s2.
* negative if s1 < s2. * -1 if s1 < s2.
* 0 if s1 and s2 are exactly the same binary string. * 0 if s1 and s2 are exactly the same binary string.
* *
* If two strings share exactly the same prefix, but one of the two has * If two strings share exactly the same prefix, but one of the two has
@ -796,7 +661,7 @@ sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count
if (seplen < 1 || len < 0) return NULL; if (seplen < 1 || len < 0) return NULL;
tokens = s_malloc(sizeof(sds)*slots); tokens = malloc(sizeof(sds)*slots);
if (tokens == NULL) return NULL; if (tokens == NULL) return NULL;
if (len == 0) { if (len == 0) {
@ -809,7 +674,7 @@ sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count
sds *newtokens; sds *newtokens;
slots *= 2; slots *= 2;
newtokens = s_realloc(tokens,sizeof(sds)*slots); newtokens = realloc(tokens,sizeof(sds)*slots);
if (newtokens == NULL) goto cleanup; if (newtokens == NULL) goto cleanup;
tokens = newtokens; tokens = newtokens;
} }
@ -833,7 +698,7 @@ cleanup:
{ {
int i; int i;
for (i = 0; i < elements; i++) sdsfree(tokens[i]); for (i = 0; i < elements; i++) sdsfree(tokens[i]);
s_free(tokens); free(tokens);
*count = 0; *count = 0;
return NULL; return NULL;
} }
@ -844,7 +709,26 @@ void sdsfreesplitres(sds *tokens, int count) {
if (!tokens) return; if (!tokens) return;
while(count--) while(count--)
sdsfree(tokens[count]); sdsfree(tokens[count]);
s_free(tokens); free(tokens);
}
/* Create an sds string from a long long value. It is much faster than:
*
* sdscatprintf(sdsempty(),"%lld\n", value);
*/
sds sdsfromlonglong(long long value) {
char buf[32], *p;
unsigned long long v;
v = (value < 0) ? -value : value;
p = buf+31; /* point to the last character */
do {
*p-- = '0'+(v%10);
v /= 10;
} while(v);
if (value < 0) *p-- = '-';
p++;
return sdsnewlen(p,32-(p-buf));
} }
/* Append to the sds string "s" an escaped string representation where /* Append to the sds string "s" an escaped string representation where
@ -1018,13 +902,13 @@ sds *sdssplitargs(const char *line, int *argc) {
if (*p) p++; if (*p) p++;
} }
/* add the token to the vector */ /* add the token to the vector */
vector = s_realloc(vector,((*argc)+1)*sizeof(char*)); vector = realloc(vector,((*argc)+1)*sizeof(char*));
vector[*argc] = current; vector[*argc] = current;
(*argc)++; (*argc)++;
current = NULL; current = NULL;
} else { } else {
/* Even on empty input string return something not NULL. */ /* Even on empty input string return something not NULL. */
if (vector == NULL) vector = s_malloc(sizeof(void*)); if (vector == NULL) vector = malloc(sizeof(void*));
return vector; return vector;
} }
} }
@ -1032,7 +916,7 @@ sds *sdssplitargs(const char *line, int *argc) {
err: err:
while((*argc)--) while((*argc)--)
sdsfree(vector[*argc]); sdsfree(vector[*argc]);
s_free(vector); free(vector);
if (current) sdsfree(current); if (current) sdsfree(current);
*argc = 0; *argc = 0;
return NULL; return NULL;
@ -1063,13 +947,13 @@ sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {
/* Join an array of C strings using the specified separator (also a C string). /* Join an array of C strings using the specified separator (also a C string).
* Returns the result as an sds string. */ * Returns the result as an sds string. */
sds sdsjoin(char **argv, int argc, char *sep) { sds sdsjoin(char **argv, int argc, char *sep, size_t seplen) {
sds join = sdsempty(); sds join = sdsempty();
int j; int j;
for (j = 0; j < argc; j++) { for (j = 0; j < argc; j++) {
join = sdscat(join, argv[j]); join = sdscat(join, argv[j]);
if (j != argc-1) join = sdscat(join,sep); if (j != argc-1) join = sdscatlen(join,sep,seplen);
} }
return join; return join;
} }
@ -1086,23 +970,13 @@ sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {
return join; return join;
} }
/* Wrappers to the allocators used by SDS. Note that SDS will actually #ifdef SDS_TEST_MAIN
* just use the macros defined into sdsalloc.h in order to avoid to pay
* the overhead of function calls. Here we define these wrappers only for
* the programs SDS is linked to, if they want to touch the SDS internals
* even if they use a different allocator. */
void *sds_malloc(size_t size) { return s_malloc(size); }
void *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); }
void sds_free(void *ptr) { s_free(ptr); }
#if defined(SDS_TEST_MAIN)
#include <stdio.h> #include <stdio.h>
#include "testhelp.h" #include "testhelp.h"
#include "limits.h"
#define UNUSED(x) (void)(x) int main(void) {
int sdsTest(void) {
{ {
struct sdshdr *sh;
sds x = sdsnew("foo"), y; sds x = sdsnew("foo"), y;
test_cond("Create a string and obtain the length", test_cond("Create a string and obtain the length",
@ -1129,35 +1003,7 @@ int sdsTest(void) {
sdsfree(x); sdsfree(x);
x = sdscatprintf(sdsempty(),"%d",123); x = sdscatprintf(sdsempty(),"%d",123);
test_cond("sdscatprintf() seems working in the base case", test_cond("sdscatprintf() seems working in the base case",
sdslen(x) == 3 && memcmp(x,"123\0",4) == 0) sdslen(x) == 3 && memcmp(x,"123\0",4) ==0)
sdsfree(x);
x = sdsnew("--");
x = sdscatfmt(x, "Hello %s World %I,%I--", "Hi!", LLONG_MIN,LLONG_MAX);
test_cond("sdscatfmt() seems working in the base case",
sdslen(x) == 60 &&
memcmp(x,"--Hello Hi! World -9223372036854775808,"
"9223372036854775807--",60) == 0)
printf("[%s]\n",x);
sdsfree(x);
x = sdsnew("--");
x = sdscatfmt(x, "%u,%U--", UINT_MAX, ULLONG_MAX);
test_cond("sdscatfmt() seems working with unsigned numbers",
sdslen(x) == 35 &&
memcmp(x,"--4294967295,18446744073709551615--",35) == 0)
sdsfree(x);
x = sdsnew(" x ");
sdstrim(x," x");
test_cond("sdstrim() works when all chars match",
sdslen(x) == 0)
sdsfree(x);
x = sdsnew(" x ");
sdstrim(x," ");
test_cond("sdstrim() works when a single char remains",
sdslen(x) == 1 && x[0] == 'x')
sdsfree(x); sdsfree(x);
x = sdsnew("xxciaoyyy"); x = sdsnew("xxciaoyyy");
@ -1226,47 +1072,24 @@ int sdsTest(void) {
memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0) memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0)
{ {
unsigned int oldfree; int oldfree;
char *p;
int step = 10, j, i;
sdsfree(x); sdsfree(x);
sdsfree(y);
x = sdsnew("0"); x = sdsnew("0");
test_cond("sdsnew() free/len buffers", sdslen(x) == 1 && sdsavail(x) == 0); sh = (void*) (x-(sizeof(struct sdshdr)));
test_cond("sdsnew() free/len buffers", sh->len == 1 && sh->free == 0);
/* Run the test a few times in order to hit the first two x = sdsMakeRoomFor(x,1);
* SDS header types. */ sh = (void*) (x-(sizeof(struct sdshdr)));
for (i = 0; i < 10; i++) { test_cond("sdsMakeRoomFor()", sh->len == 1 && sh->free > 0);
int oldlen = sdslen(x); oldfree = sh->free;
x = sdsMakeRoomFor(x,step); x[1] = '1';
int type = x[-1]&SDS_TYPE_MASK; sdsIncrLen(x,1);
test_cond("sdsIncrLen() -- content", x[0] == '0' && x[1] == '1');
test_cond("sdsMakeRoomFor() len", sdslen(x) == oldlen); test_cond("sdsIncrLen() -- len", sh->len == 2);
if (type != SDS_TYPE_5) { test_cond("sdsIncrLen() -- free", sh->free == oldfree-1);
test_cond("sdsMakeRoomFor() free", sdsavail(x) >= step);
oldfree = sdsavail(x);
}
p = x+oldlen;
for (j = 0; j < step; j++) {
p[j] = 'A'+j;
}
sdsIncrLen(x,step);
}
test_cond("sdsMakeRoomFor() content",
memcmp("0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ",x,101) == 0);
test_cond("sdsMakeRoomFor() final length",sdslen(x)==101);
sdsfree(x);
} }
} }
test_report() test_report()
return 0; return 0;
} }
#endif #endif
#ifdef SDS_TEST_MAIN
int main(void) {
return sdsTest();
}
#endif

View File

@ -1,8 +1,6 @@
/* SDSLib 2.0 -- A C dynamic strings library /* SDS (Simple Dynamic Strings), A C dynamic strings library.
* *
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2006-2014, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2015, Oran Agra
* Copyright (c) 2015, Redis Labs, Inc
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -37,188 +35,35 @@
#include <sys/types.h> #include <sys/types.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h> #ifdef _MSC_VER
#include "win32.h"
#endif
typedef char *sds; typedef char *sds;
/* Note: sdshdr5 is never used, we just access the flags byte directly. struct sdshdr {
* However is here to document the layout of type 5 SDS strings. */ int len;
struct __attribute__ ((__packed__)) sdshdr5 { int free;
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
char buf[]; 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) { static inline size_t sdslen(const sds s) {
unsigned char flags = s[-1]; struct sdshdr *sh = (struct sdshdr *)(s-sizeof *sh);
switch(flags&SDS_TYPE_MASK) { return sh->len;
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) { static inline size_t sdsavail(const sds s) {
unsigned char flags = s[-1]; struct sdshdr *sh = (struct sdshdr *)(s-sizeof *sh);
switch(flags&SDS_TYPE_MASK) { return sh->free;
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 sdsnewlen(const void *init, size_t initlen);
sds sdsnew(const char *init); sds sdsnew(const char *init);
sds sdsempty(void); sds sdsempty(void);
size_t sdslen(const sds s);
sds sdsdup(const sds s); sds sdsdup(const sds s);
void sdsfree(sds s); void sdsfree(sds s);
size_t sdsavail(const sds s);
sds sdsgrowzero(sds s, size_t len); sds sdsgrowzero(sds s, size_t len);
sds sdscatlen(sds s, const void *t, size_t len); sds sdscatlen(sds s, const void *t, size_t len);
sds sdscat(sds s, const char *t); sds sdscat(sds s, const char *t);
@ -235,7 +80,7 @@ sds sdscatprintf(sds s, const char *fmt, ...);
#endif #endif
sds sdscatfmt(sds s, char const *fmt, ...); sds sdscatfmt(sds s, char const *fmt, ...);
sds sdstrim(sds s, const char *cset); void sdstrim(sds s, const char *cset);
void sdsrange(sds s, int start, int end); void sdsrange(sds s, int start, int end);
void sdsupdatelen(sds s); void sdsupdatelen(sds s);
void sdsclear(sds s); void sdsclear(sds s);
@ -248,7 +93,7 @@ sds sdsfromlonglong(long long value);
sds sdscatrepr(sds s, const char *p, size_t len); sds sdscatrepr(sds s, const char *p, size_t len);
sds *sdssplitargs(const char *line, int *argc); sds *sdssplitargs(const char *line, int *argc);
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);
sds sdsjoin(char **argv, int argc, char *sep); sds sdsjoin(char **argv, int argc, char *sep, size_t seplen);
sds sdsjoinsds(sds *argv, int argc, const 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 */ /* Low level functions exposed to the user API */
@ -256,18 +101,5 @@ sds sdsMakeRoomFor(sds s, size_t addlen);
void sdsIncrLen(sds s, int incr); void sdsIncrLen(sds s, int incr);
sds sdsRemoveFreeSpace(sds s); sds sdsRemoveFreeSpace(sds s);
size_t sdsAllocSize(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 #endif

View File

@ -1,42 +0,0 @@
/* 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

View File

@ -30,7 +30,7 @@ struct config {
struct { struct {
const char *path; const char *path;
} unix_sock; } unix;
}; };
/* The following lines make up our testing "framework" :) */ /* The following lines make up our testing "framework" :) */
@ -97,10 +97,10 @@ static redisContext *connect(struct config config) {
if (config.type == CONN_TCP) { if (config.type == CONN_TCP) {
c = redisConnect(config.tcp.host, config.tcp.port); c = redisConnect(config.tcp.host, config.tcp.port);
} else if (config.type == CONN_UNIX) { } else if (config.type == CONN_UNIX) {
c = redisConnectUnix(config.unix_sock.path); c = redisConnectUnix(config.unix.path);
} else if (config.type == CONN_FD) { } else if (config.type == CONN_FD) {
/* Create a dummy connection just to get an fd to inherit */ /* Create a dummy connection just to get an fd to inherit */
redisContext *dummy_ctx = redisConnectUnix(config.unix_sock.path); redisContext *dummy_ctx = redisConnectUnix(config.unix.path);
if (dummy_ctx) { if (dummy_ctx) {
int fd = disconnect(dummy_ctx, 1); int fd = disconnect(dummy_ctx, 1);
printf("Connecting to inherited fd %d\n", fd); printf("Connecting to inherited fd %d\n", fd);
@ -224,22 +224,6 @@ static void test_format_commands(void) {
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 && test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(7+2)+4+(3+2)); len == 4+4+(3+2)+4+(7+2)+4+(3+2));
free(cmd); free(cmd);
sds sds_cmd;
sds_cmd = sdsempty();
test("Format command into sds by passing argc/argv without lengths: ");
len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,NULL);
test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
sdsfree(sds_cmd);
sds_cmd = sdsempty();
test("Format command into sds by passing argc/argv with lengths: ");
len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,lens);
test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(7+2)+4+(3+2));
sdsfree(sds_cmd);
} }
static void test_append_formatted_commands(struct config config) { static void test_append_formatted_commands(struct config config) {
@ -344,12 +328,12 @@ static void test_reply_reader(void) {
} }
static void test_free_null(void) { static void test_free_null(void) {
void *redisCtx = NULL; void *redisContext = NULL;
void *reply = NULL; void *reply = NULL;
test("Don't fail when redisFree is passed a NULL value: "); test("Don't fail when redisFree is passed a NULL value: ");
redisFree(redisCtx); redisFree(redisContext);
test_cond(redisCtx == NULL); test_cond(redisContext == NULL);
test("Don't fail when freeReplyObject is passed a NULL value: "); test("Don't fail when freeReplyObject is passed a NULL value: ");
freeReplyObject(reply); freeReplyObject(reply);
@ -377,7 +361,7 @@ static void test_blocking_connection_errors(void) {
strcmp(c->errstr,"Connection refused") == 0); strcmp(c->errstr,"Connection refused") == 0);
redisFree(c); redisFree(c);
test("Returns error when the unix_sock socket path doesn't accept connections: "); test("Returns error when the unix socket path doesn't accept connections: ");
c = redisConnectUnix((char*)"/tmp/idontexist.sock"); c = redisConnectUnix((char*)"/tmp/idontexist.sock");
test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */ test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */
redisFree(c); redisFree(c);
@ -498,7 +482,7 @@ static void test_blocking_connection_timeouts(struct config config) {
test("Reconnect properly uses owned parameters: "); test("Reconnect properly uses owned parameters: ");
config.tcp.host = "foo"; config.tcp.host = "foo";
config.unix_sock.path = "foo"; config.unix.path = "foo";
redisReconnect(c); redisReconnect(c);
reply = redisCommand(c, "PING"); reply = redisCommand(c, "PING");
test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0); test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0);
@ -568,7 +552,7 @@ static void test_invalid_timeout_errors(struct config config) {
c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout); c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout);
test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0); test_cond(c->err == REDIS_ERR_IO);
redisFree(c); redisFree(c);
test("Set error when an invalid timeout sec value is given to redisConnectWithTimeout: "); test("Set error when an invalid timeout sec value is given to redisConnectWithTimeout: ");
@ -578,7 +562,7 @@ static void test_invalid_timeout_errors(struct config config) {
c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout); c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout);
test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0); test_cond(c->err == REDIS_ERR_IO);
redisFree(c); redisFree(c);
} }
@ -752,7 +736,7 @@ int main(int argc, char **argv) {
.host = "127.0.0.1", .host = "127.0.0.1",
.port = 6379 .port = 6379
}, },
.unix_sock = { .unix = {
.path = "/tmp/redis.sock" .path = "/tmp/redis.sock"
} }
}; };
@ -773,7 +757,7 @@ int main(int argc, char **argv) {
cfg.tcp.port = atoi(argv[0]); cfg.tcp.port = atoi(argv[0]);
} else if (argc >= 2 && !strcmp(argv[0],"-s")) { } else if (argc >= 2 && !strcmp(argv[0],"-s")) {
argv++; argc--; argv++; argc--;
cfg.unix_sock.path = argv[0]; cfg.unix.path = argv[0];
} else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) { } else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) {
throughput = 0; throughput = 0;
} else if (argc >= 1 && !strcmp(argv[0],"--skip-inherit-fd")) { } else if (argc >= 1 && !strcmp(argv[0],"--skip-inherit-fd")) {
@ -799,7 +783,7 @@ int main(int argc, char **argv) {
test_append_formatted_commands(cfg); test_append_formatted_commands(cfg);
if (throughput) test_throughput(cfg); if (throughput) test_throughput(cfg);
printf("\nTesting against Unix socket connection (%s):\n", cfg.unix_sock.path); printf("\nTesting against Unix socket connection (%s):\n", cfg.unix.path);
cfg.type = CONN_UNIX; cfg.type = CONN_UNIX;
test_blocking_connection(cfg); test_blocking_connection(cfg);
test_blocking_connection_timeouts(cfg); test_blocking_connection_timeouts(cfg);
@ -807,7 +791,7 @@ int main(int argc, char **argv) {
if (throughput) test_throughput(cfg); if (throughput) test_throughput(cfg);
if (test_inherit_fd) { if (test_inherit_fd) {
printf("\nTesting against inherited fd (%s):\n", cfg.unix_sock.path); printf("\nTesting against inherited fd (%s):\n", cfg.unix.path);
cfg.type = CONN_FD; cfg.type = CONN_FD;
test_blocking_connection(cfg); test_blocking_connection(cfg);
} }