Browse files

merge 2.4.9

  • Loading branch information...
2 parents 34c3eb3 + 1dfe75a commit c43bb92bcb1c652545f98cebd5ce9701808c1474 @HenryRawas HenryRawas committed Mar 21, 2012
Showing with 2,206 additions and 982 deletions.
  1. +38 −0 00-RELEASENOTES
  2. +2 −0 Makefile
  3. +16 −0 deps/hiredis/CHANGELOG.md
  4. +25 −6 deps/hiredis/COPYING
  5. +98 −65 deps/hiredis/Makefile
  6. +43 −2 deps/hiredis/README.md
  7. +0 −2 deps/hiredis/TODO
  8. +18 −16 deps/hiredis/adapters/ae.h
  9. +19 −15 deps/hiredis/adapters/libev.h
  10. +18 −16 deps/hiredis/adapters/libevent.h
  11. +90 −28 deps/hiredis/async.c
  12. +3 −4 deps/hiredis/async.h
  13. +8 −5 deps/hiredis/example-ae.c
  14. +8 −4 deps/hiredis/example-libev.c
  15. +8 −4 deps/hiredis/example-libevent.c
  16. +5 −3 deps/hiredis/fmacros.h
  17. +463 −259 deps/hiredis/hiredis.c
  18. +54 −34 deps/hiredis/hiredis.h
  19. +123 −96 deps/hiredis/net.c
  20. +3 −2 deps/hiredis/net.h
  21. +23 −12 deps/hiredis/sds.c
  22. +271 −149 deps/hiredis/test.c
  23. +0 −40 deps/hiredis/util.h
  24. +5 −5 msvs/RedisServer.sln
  25. +1 −0 msvs/RedisServer.vcxproj
  26. +21 −15 redis.conf
  27. +5 −2 src/Makefile
  28. +1 −0 src/aof.c
  29. +1 −1 src/config.h
  30. +8 −3 src/db.c
  31. +23 −2 src/debug.c
  32. +219 −0 src/memtest.c
  33. +89 −5 src/networking.c
  34. +6 −2 src/rdb.c
  35. +199 −91 src/redis-benchmark.c
  36. +18 −11 src/redis-check-aof.c
  37. +18 −10 src/redis-cli.c
  38. +140 −29 src/redis.c
  39. +8 −1 src/redis.h
  40. +5 −1 src/sds.c
  41. +2 −0 src/sds.h
  42. +7 −1 src/t_hash.c
  43. +3 −2 src/t_string.c
  44. +2 −2 src/t_zset.c
  45. +1 −1 src/version.h
  46. +10 −1 src/win32fixes.c
  47. +1 −0 src/win32fixes.h
  48. +14 −0 src/zmalloc.c
  49. +4 −0 src/zmalloc.h
  50. +4 −9 tests/integration/replication.tcl
  51. +8 −0 tests/unit/type/hash.tcl
  52. +47 −26 utils/install_server.sh
View
38 00-RELEASENOTES
@@ -18,6 +18,44 @@ to modify your program in order to use Redis 2.4.
CHANGELOG
---------
+What's new in Redis 2.4.9
+=========================
+
+UPGRADE URGENCY: low. Mostly new features and minor bug fixing.
+
+* [FEATURE] Redis server is now able to test your memory for broken RAM.
+ Usage: ./redis-server --test-memory <megabytes>.
+* [FEATURE] redis-benchmark backported from unstable. Pipelining, run selected
+ tests, and a few more features.
+* [BUGFIX] utils/install_server.sh script now works on Redhat / Centos.
+* [BUGFIX] Minor fix to redis-cli (github issue #306).
+
+What's new in Redis 2.4.8
+=========================
+
+UPGRADE URGENCY: moderate if you don't experience any of the fixed problems.
+
+* [BUGFIX] Make install now uses cp -f to avoid 'text file busy' errors.
+* [BUGFIX] redis-check-aof is now large files safe also on 32 bit systems.
+* [BUGFIX] Issue #327 fixed: maxmemory and replication now work much better.
+* [BUGFIX] Now HINCRBY can detect overflows too. Fix for issue #330.
+* [BUGFIX] Fixed compilation with latest clang.
+* [BUGFIX] Fixed handling of empty sorted sets produced in RDB by
+ very old Redis versions (1.2.x).
+
+What's new in Redis 2.4.7
+=========================
+
+UPGRADE URGENCY: low/moderate if you don't experience any of the fixed problems.
+
+* [BUGFIX] Fixed false positive in issue #141 regression test.
+* [BUGFIX] Slave should not expire keys when loading an RDB after a SYNC.
+* [BUGFIX] Don't increment stats for key misses / hits when key is written.
+* [BUGFIX] sds.c library now don't allocate more than 1MB ahead.
+* 32 bit instances without a maxmemory set now get a default limit of 3.5GB with
+ maxmemory-policy set to noeviction.
+* Better crash report on crash (containing current client and command arguments).
+
What's new in Redis 2.4.6
=========================
View
2 Makefile
@@ -13,6 +13,8 @@ clean:
cd deps/hiredis && $(MAKE) $@
cd deps/linenoise && $(MAKE) $@
+distclean: clean
+
$(TARGETS):
cd src && $(MAKE) $@
View
16 deps/hiredis/CHANGELOG.md
@@ -0,0 +1,16 @@
+### 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.
+
View
31 deps/hiredis/COPYING
@@ -1,10 +1,29 @@
-Copyright (c) 2006-2009, Salvatore Sanfilippo
+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:
+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.
- * 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.
+* 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.
+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.
View
163 deps/hiredis/Makefile
@@ -1,115 +1,148 @@
# Hiredis Makefile
-# Copyright (C) 2010 Salvatore Sanfilippo <antirez at gmail dot com>
+# Copyright (C) 2010-2011 Salvatore Sanfilippo <antirez at gmail dot com>
+# Copyright (C) 2010-2011 Pieter Noordhuis <pcnoordhuis at gmail dot com>
# This file is released under the BSD license, see the COPYING file
-OBJ = net.o hiredis.o sds.o async.o
-BINS = hiredis-example hiredis-test
+OBJ=net.o hiredis.o sds.o async.o
+BINS=hiredis-example hiredis-test
+LIBNAME=libhiredis
-uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
+HIREDIS_MAJOR=0
+HIREDIS_MINOR=10
+
+# Fallback to gcc when $CC is not in $PATH.
+CC:=$(shell sh -c 'type $(CC) >/dev/null 2>/dev/null && echo $(CC) || echo gcc')
OPTIMIZATION?=-O3
+WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings
+DEBUG?= -g -ggdb
+REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CFLAGS) $(WARNINGS) $(DEBUG) $(ARCH)
+REAL_LDFLAGS=$(LDFLAGS) $(ARCH)
+
+DYLIBSUFFIX=so
+STLIBSUFFIX=a
+DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR).$(HIREDIS_MINOR)
+DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)
+DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX)
+DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
+STLIBNAME=$(LIBNAME).$(STLIBSUFFIX)
+STLIB_MAKE_CMD=ar rcs $(STLIBNAME)
+
+# Platform-specific overrides
+uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
ifeq ($(uname_S),SunOS)
- CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -D__EXTENSIONS__ -D_XPG6 $(ARCH) $(PROF)
- CCLINK?=-ldl -lnsl -lsocket -lm -lpthread
- LDFLAGS?=-L. -Wl,-R,.
- DYLIBNAME?=libhiredis.so
- DYLIB_MAKE_CMD?=$(CC) -G -o ${DYLIBNAME} ${OBJ}
- STLIBNAME?=libhiredis.a
- STLIB_MAKE_CMD?=ar rcs ${STLIBNAME} ${OBJ}
-else
-ifeq ($(uname_S),Darwin)
- CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings $(ARCH) $(PROF)
- CCLINK?=-lm -pthread
- LDFLAGS?=-L. -Wl,-rpath,.
- OBJARCH?=-arch i386 -arch x86_64
- DYLIBNAME?=libhiredis.dylib
- DYLIB_MAKE_CMD?=libtool -dynamic -o ${DYLIBNAME} -lm ${DEBUG} - ${OBJ}
- STLIBNAME?=libhiredis.a
- STLIB_MAKE_CMD?=libtool -static -o ${STLIBNAME} - ${OBJ}
-else
- CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings $(ARCH) $(PROF)
- CCLINK?=-lm -pthread
- LDFLAGS?=-L. -Wl,-rpath,.
- DYLIBNAME?=libhiredis.so
- DYLIB_MAKE_CMD?=gcc -shared -Wl,-soname,${DYLIBNAME} -o ${DYLIBNAME} ${OBJ}
- STLIBNAME?=libhiredis.a
- STLIB_MAKE_CMD?=ar rcs ${STLIBNAME} ${OBJ}
+ REAL_LDFLAGS+= -ldl -lnsl -lsocket
+ DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS)
+ INSTALL= cp -r
endif
+ifeq ($(uname_S),Darwin)
+ DYLIBSUFFIX=dylib
+ DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(DYLIBSUFFIX)
+ DYLIB_MAJOR_NAME=$(LIBNAME).$(HIREDIS_MAJOR).$(DYLIBSUFFIX)
+ DYLIB_MAKE_CMD=$(CC) -shared -Wl,-install_name,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
endif
-CCOPT= $(CFLAGS) $(CCLINK)
-DEBUG?= -g -ggdb
-
-PREFIX?= /usr/local
-INSTALL_INC= $(PREFIX)/include/hiredis
-INSTALL_LIB= $(PREFIX)/lib
-INSTALL= cp -a
-
-all: ${DYLIBNAME} ${BINS}
+all: $(DYLIBNAME) $(BINS)
# Deps (use make dep to generate this)
-net.o: net.c fmacros.h net.h
-async.o: async.c async.h hiredis.h sds.h util.h dict.c dict.h
+net.o: net.c fmacros.h net.h hiredis.h
+async.o: async.c async.h hiredis.h sds.h dict.c dict.h
example.o: example.c hiredis.h
-hiredis.o: hiredis.c hiredis.h net.h sds.h util.h
+hiredis.o: hiredis.c fmacros.h hiredis.h net.h sds.h
sds.o: sds.c sds.h
test.o: test.c hiredis.h
-${DYLIBNAME}: ${OBJ}
- ${DYLIB_MAKE_CMD}
+$(DYLIBNAME): $(OBJ)
+ $(DYLIB_MAKE_CMD) $(OBJ)
-${STLIBNAME}: ${OBJ}
- ${STLIB_MAKE_CMD}
+$(STLIBNAME): $(OBJ)
+ $(STLIB_MAKE_CMD) $(OBJ)
-dynamic: ${DYLIBNAME}
-static: ${STLIBNAME}
+dynamic: $(DYLIBNAME)
+static: $(STLIBNAME)
# Binaries:
-hiredis-example-libevent: example-libevent.c adapters/libevent.h ${DYLIBNAME}
- $(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lhiredis -levent example-libevent.c
+hiredis-example-libevent: example-libevent.c adapters/libevent.h $(STLIBNAME)
+ $(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -levent example-libevent.c $(STLIBNAME)
-hiredis-example-libev: example-libev.c adapters/libev.h ${DYLIBNAME}
- $(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lhiredis -lev example-libev.c
+hiredis-example-libev: example-libev.c adapters/libev.h $(STLIBNAME)
+ $(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -lev example-libev.c $(STLIBNAME)
ifndef AE_DIR
hiredis-example-ae:
@echo "Please specify AE_DIR (e.g. <redis repository>/src)"
@false
else
-hiredis-example-ae: example-ae.c adapters/ae.h ${DYLIBNAME}
- $(CC) -o $@ $(CCOPT) $(DEBUG) -I$(AE_DIR) $(LDFLAGS) -lhiredis example-ae.c $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o
+hiredis-example-ae: example-ae.c adapters/ae.h $(STLIBNAME)
+ $(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I$(AE_DIR) $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o example-ae.c $(STLIBNAME)
endif
-hiredis-%: %.o ${DYLIBNAME}
- $(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lhiredis $<
+hiredis-%: %.o $(STLIBNAME)
+ $(CC) -o $@ $(REAL_LDFLAGS) $< $(STLIBNAME)
test: hiredis-test
./hiredis-test
+check: hiredis-test
+ echo \
+ "daemonize yes\n" \
+ "pidfile /tmp/hiredis-test-redis.pid\n" \
+ "port 56379\n" \
+ "bind 127.0.0.1\n" \
+ "unixsocket /tmp/hiredis-test-redis.sock" \
+ | redis-server -
+ ./hiredis-test -h 127.0.0.1 -p 56379 -s /tmp/hiredis-test-redis.sock || \
+ ( kill `cat /tmp/hiredis-test-redis.pid` && false )
+ kill `cat /tmp/hiredis-test-redis.pid`
+
.c.o:
- $(CC) -c $(CFLAGS) $(OBJARCH) $(DEBUG) $(COMPILE_TIME) $<
+ $(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $<
clean:
- rm -rf ${DYLIBNAME} ${STLIBNAME} $(BINS) hiredis-example* *.o *.gcda *.gcno *.gcov
+ rm -rf $(DYLIBNAME) $(STLIBNAME) $(BINS) hiredis-example* *.o *.gcda *.gcno *.gcov
dep:
$(CC) -MM *.c
-install: ${DYLIBNAME} ${STLIBNAME}
- mkdir -p $(INSTALL_INC) $(INSTALL_LIB)
- $(INSTALL) hiredis.h async.h adapters $(INSTALL_INC)
- $(INSTALL) ${DYLIBNAME} ${STLIBNAME} $(INSTALL_LIB)
+# Installation related variables and target
+PREFIX?=/usr/local
+INCLUDE_PATH?=include/hiredis
+LIBRARY_PATH?=lib
+INSTALL_INCLUDE_PATH= $(PREFIX)/$(INCLUDE_PATH)
+INSTALL_LIBRARY_PATH= $(PREFIX)/$(LIBRARY_PATH)
+
+ifeq ($(uname_S),SunOS)
+ INSTALL?= cp -r
+endif
+
+INSTALL?= cp -a
+
+install: $(DYLIBNAME) $(STLIBNAME)
+ mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH)
+ $(INSTALL) hiredis.h async.h adapters $(INSTALL_INCLUDE_PATH)
+ $(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME)
+ cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIB_MAJOR_NAME)
+ cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MAJOR_NAME) $(DYLIBNAME)
+ $(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)
32bit:
@echo ""
- @echo "WARNING: if it fails under Linux you probably need to install libc6-dev-i386"
+ @echo "WARNING: if this fails under Linux you probably need to install libc6-dev-i386"
@echo ""
- $(MAKE) ARCH="-m32"
+ $(MAKE) CFLAGS="-m32" LDFLAGS="-m32"
gprof:
- $(MAKE) PROF="-pg"
+ $(MAKE) CFLAGS="-pg" LDFLAGS="-pg"
gcov:
- $(MAKE) PROF="-fprofile-arcs -ftest-coverage"
+ $(MAKE) CFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs"
+
+coverage: gcov
+ make check
+ mkdir -p tmp/lcov
+ lcov -d . -c -o tmp/lcov/hiredis.info
+ genhtml --legend -o tmp/lcov/report tmp/lcov/hiredis.info
noopt:
$(MAKE) OPTIMIZATION=""
+
+.PHONY: all test check clean dep install 32bit gprof gcov noopt
View
45 deps/hiredis/README.md
@@ -116,6 +116,12 @@ Note that this function will take care of freeing sub-replies 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) free's 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:
@@ -280,7 +286,8 @@ is being disconnected per user-request, no new commands may be added to the outp
returned on calls to the `redisAsyncCommand` family.
If the reply for a command with a `NULL` callback is read, it is immediately free'd. When the callback
-for a command is non-`NULL`, it is responsible for cleaning up the reply.
+for a command is non-`NULL`, the memory is free'd 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.
@@ -303,7 +310,41 @@ See the `adapters/` directory for bindings to *libev* and *libevent*.
## Reply parsing API
-To be done.
+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:
+
+ redisReader *redisReaderCreate(void);
+ void redisReaderFree(redisReader *reader);
+ int redisReaderFeed(redisReader *reader, const char *buf, size_t len);
+ int redisReaderGetReply(redisReader *reader, void **reply);
+
+### 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).
+
+### 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.
## AUTHORS
View
2 deps/hiredis/TODO
@@ -1,2 +0,0 @@
-- add redisCommandVector()
-- add support for pipelining
View
34 deps/hiredis/adapters/ae.h
@@ -1,3 +1,5 @@
+#ifndef __HIREDIS_AE_H__
+#define __HIREDIS_AE_H__
#include <sys/types.h>
#include <ae.h>
#include "../hiredis.h"
@@ -10,21 +12,21 @@ typedef struct redisAeEvents {
int reading, writing;
} redisAeEvents;
-void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
+static void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
((void)el); ((void)fd); ((void)mask);
redisAeEvents *e = (redisAeEvents*)privdata;
redisAsyncHandleRead(e->context);
}
-void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
+static void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
((void)el); ((void)fd); ((void)mask);
redisAeEvents *e = (redisAeEvents*)privdata;
redisAsyncHandleWrite(e->context);
}
-void redisAeAddRead(void *privdata) {
+static void redisAeAddRead(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (!e->reading) {
@@ -33,7 +35,7 @@ void redisAeAddRead(void *privdata) {
}
}
-void redisAeDelRead(void *privdata) {
+static void redisAeDelRead(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (e->reading) {
@@ -42,7 +44,7 @@ void redisAeDelRead(void *privdata) {
}
}
-void redisAeAddWrite(void *privdata) {
+static void redisAeAddWrite(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (!e->writing) {
@@ -51,7 +53,7 @@ void redisAeAddWrite(void *privdata) {
}
}
-void redisAeDelWrite(void *privdata) {
+static void redisAeDelWrite(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (e->writing) {
@@ -60,19 +62,19 @@ void redisAeDelWrite(void *privdata) {
}
}
-void redisAeCleanup(void *privdata) {
+static void redisAeCleanup(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
redisAeDelRead(privdata);
redisAeDelWrite(privdata);
free(e);
}
-int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
+static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
redisContext *c = &(ac->c);
redisAeEvents *e;
/* Nothing should be attached when something is already attached */
- if (ac->_adapter_data != NULL)
+ if (ac->ev.data != NULL)
return REDIS_ERR;
/* Create container for context and r/w events */
@@ -83,13 +85,13 @@ int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
e->reading = e->writing = 0;
/* Register functions to start/stop listening for events */
- ac->evAddRead = redisAeAddRead;
- ac->evDelRead = redisAeDelRead;
- ac->evAddWrite = redisAeAddWrite;
- ac->evDelWrite = redisAeDelWrite;
- ac->evCleanup = redisAeCleanup;
- ac->_adapter_data = e;
+ ac->ev.addRead = redisAeAddRead;
+ ac->ev.delRead = redisAeDelRead;
+ ac->ev.addWrite = redisAeAddWrite;
+ ac->ev.delWrite = redisAeDelWrite;
+ ac->ev.cleanup = redisAeCleanup;
+ ac->ev.data = e;
return REDIS_OK;
}
-
+#endif
View
34 deps/hiredis/adapters/libev.h
@@ -1,3 +1,6 @@
+#ifndef __HIREDIS_LIBEV_H__
+#define __HIREDIS_LIBEV_H__
+#include <stdlib.h>
#include <sys/types.h>
#include <ev.h>
#include "../hiredis.h"
@@ -10,7 +13,7 @@ typedef struct redisLibevEvents {
ev_io rev, wev;
} redisLibevEvents;
-void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
+static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
#if EV_MULTIPLICITY
((void)loop);
#endif
@@ -20,7 +23,7 @@ void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
redisAsyncHandleRead(e->context);
}
-void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) {
+static void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) {
#if EV_MULTIPLICITY
((void)loop);
#endif
@@ -30,7 +33,7 @@ void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) {
redisAsyncHandleWrite(e->context);
}
-void redisLibevAddRead(void *privdata) {
+static void redisLibevAddRead(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
@@ -40,7 +43,7 @@ void redisLibevAddRead(void *privdata) {
}
}
-void redisLibevDelRead(void *privdata) {
+static void redisLibevDelRead(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
@@ -50,7 +53,7 @@ void redisLibevDelRead(void *privdata) {
}
}
-void redisLibevAddWrite(void *privdata) {
+static void redisLibevAddWrite(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
@@ -60,7 +63,7 @@ void redisLibevAddWrite(void *privdata) {
}
}
-void redisLibevDelWrite(void *privdata) {
+static void redisLibevDelWrite(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
@@ -70,19 +73,19 @@ void redisLibevDelWrite(void *privdata) {
}
}
-void redisLibevCleanup(void *privdata) {
+static void redisLibevCleanup(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
redisLibevDelRead(privdata);
redisLibevDelWrite(privdata);
free(e);
}
-int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
+static int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
redisContext *c = &(ac->c);
redisLibevEvents *e;
/* Nothing should be attached when something is already attached */
- if (ac->_adapter_data != NULL)
+ if (ac->ev.data != NULL)
return REDIS_ERR;
/* Create container for context and r/w events */
@@ -98,16 +101,17 @@ int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
e->wev.data = e;
/* Register functions to start/stop listening for events */
- ac->evAddRead = redisLibevAddRead;
- ac->evDelRead = redisLibevDelRead;
- ac->evAddWrite = redisLibevAddWrite;
- ac->evDelWrite = redisLibevDelWrite;
- ac->evCleanup = redisLibevCleanup;
- ac->_adapter_data = e;
+ ac->ev.addRead = redisLibevAddRead;
+ ac->ev.delRead = redisLibevDelRead;
+ ac->ev.addWrite = redisLibevAddWrite;
+ ac->ev.delWrite = redisLibevDelWrite;
+ ac->ev.cleanup = redisLibevCleanup;
+ ac->ev.data = e;
/* Initialize read/write events */
ev_io_init(&e->rev,redisLibevReadEvent,c->fd,EV_READ);
ev_io_init(&e->wev,redisLibevWriteEvent,c->fd,EV_WRITE);
return REDIS_OK;
}
+#endif
View
34 deps/hiredis/adapters/libevent.h
@@ -1,4 +1,5 @@
-#include <sys/types.h>
+#ifndef __HIREDIS_LIBEVENT_H__
+#define __HIREDIS_LIBEVENT_H__
#include <event.h>
#include "../hiredis.h"
#include "../async.h"
@@ -8,64 +9,64 @@ typedef struct redisLibeventEvents {
struct event rev, wev;
} redisLibeventEvents;
-void redisLibeventReadEvent(int fd, short event, void *arg) {
+static void redisLibeventReadEvent(int fd, short event, void *arg) {
((void)fd); ((void)event);
redisLibeventEvents *e = (redisLibeventEvents*)arg;
redisAsyncHandleRead(e->context);
}
-void redisLibeventWriteEvent(int fd, short event, void *arg) {
+static void redisLibeventWriteEvent(int fd, short event, void *arg) {
((void)fd); ((void)event);
redisLibeventEvents *e = (redisLibeventEvents*)arg;
redisAsyncHandleWrite(e->context);
}
-void redisLibeventAddRead(void *privdata) {
+static void redisLibeventAddRead(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_add(&e->rev,NULL);
}
-void redisLibeventDelRead(void *privdata) {
+static void redisLibeventDelRead(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(&e->rev);
}
-void redisLibeventAddWrite(void *privdata) {
+static void redisLibeventAddWrite(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_add(&e->wev,NULL);
}
-void redisLibeventDelWrite(void *privdata) {
+static void redisLibeventDelWrite(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(&e->wev);
}
-void redisLibeventCleanup(void *privdata) {
+static void redisLibeventCleanup(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(&e->rev);
event_del(&e->wev);
free(e);
}
-int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
+static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
redisContext *c = &(ac->c);
redisLibeventEvents *e;
/* Nothing should be attached when something is already attached */
- if (ac->_adapter_data != NULL)
+ if (ac->ev.data != NULL)
return REDIS_ERR;
/* Create container for context and r/w events */
e = (redisLibeventEvents*)malloc(sizeof(*e));
e->context = ac;
/* Register functions to start/stop listening for events */
- ac->evAddRead = redisLibeventAddRead;
- ac->evDelRead = redisLibeventDelRead;
- ac->evAddWrite = redisLibeventAddWrite;
- ac->evDelWrite = redisLibeventDelWrite;
- ac->evCleanup = redisLibeventCleanup;
- ac->_adapter_data = e;
+ ac->ev.addRead = redisLibeventAddRead;
+ ac->ev.delRead = redisLibeventDelRead;
+ ac->ev.addWrite = redisLibeventAddWrite;
+ ac->ev.delWrite = redisLibeventDelWrite;
+ ac->ev.cleanup = redisLibeventCleanup;
+ ac->ev.data = e;
/* Initialize and install read/write events */
event_set(&e->rev,c->fd,EV_READ,redisLibeventReadEvent,e);
@@ -74,3 +75,4 @@ int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
event_base_set(base,&e->wev);
return REDIS_OK;
}
+#endif
View
118 deps/hiredis/async.c
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
- * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ * 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.
*
@@ -29,16 +29,38 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
+#include "fmacros.h"
+#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
#include <strings.h>
#endif
#include <assert.h>
#include <ctype.h>
+#include <errno.h>
#include "async.h"
+#include "net.h"
#include "dict.c"
#include "sds.h"
-#include "util.h"
+#ifdef _WIN32
+ #include "../../src/win32fixes.h"
+#endif
+
+#define _EL_ADD_READ(ctx) do { \
+ if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \
+ } while(0)
+#define _EL_DEL_READ(ctx) do { \
+ if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \
+ } while(0)
+#define _EL_ADD_WRITE(ctx) do { \
+ if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \
+ } while(0)
+#define _EL_DEL_WRITE(ctx) do { \
+ if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \
+ } while(0)
+#define _EL_CLEANUP(ctx) do { \
+ if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \
+ } while(0);
#ifdef _WIN32
#define strcasecmp _stricmp
@@ -143,19 +165,14 @@ redisAsyncContext *redisAsyncConnectUnix(const char *path) {
return ac;
}
-int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFunctions *fn) {
- redisContext *c = &(ac->c);
- return redisSetReplyObjectFunctions(c,fn);
-}
-
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
if (ac->onConnect == NULL) {
ac->onConnect = fn;
/* The common way to detect an established connection is to wait for
* the first write event to be fired. This assumes the related event
* library functions are already set. */
- if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data);
+ _EL_ADD_WRITE(ac);
return REDIS_OK;
}
return REDIS_ERR;
@@ -175,7 +192,6 @@ static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
/* Copy callback from stack to heap */
cb = malloc(sizeof(*cb));
- if (!cb) redisOOM();
if (source != NULL) {
memcpy(cb,source,sizeof(*cb));
cb->next = NULL;
@@ -244,7 +260,7 @@ static void __redisAsyncFree(redisAsyncContext *ac) {
dictRelease(ac->sub.patterns);
/* Signal event lib to clean up */
- if (ac->ev.cleanup) ac->ev.cleanup(ac->ev.data);
+ _EL_CLEANUP(ac);
/* Execute disconnect callback. When redisAsyncFree() initiated destroying
* this context, the status will always be REDIS_OK. */
@@ -375,14 +391,27 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
/* Even if the context is subscribed, pending regular callbacks will
* get a reply before pub/sub messages arrive. */
if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) {
- /* No more regular callbacks, the context *must* be subscribed. */
+ /* A spontaneous reply in a not-subscribed context can only be the
+ * error reply that is sent when a new connection exceeds the
+ * maximum number of allowed connections on the server side. This
+ * is seen as an error instead of a regular reply because the
+ * server closes the connection after sending it. To prevent the
+ * error from being overwritten by an EOF error the connection is
+ * closed here. See issue #43. */
+ if ( !(c->flags & REDIS_SUBSCRIBED) && ((redisReply*)reply)->type == REDIS_REPLY_ERROR ) {
+ c->err = REDIS_ERR_OTHER;
+ snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str);
+ __redisAsyncDisconnect(ac);
+ return;
+ }
+ /* No more regular callbacks and no errors, the context *must* be subscribed. */
assert(c->flags & REDIS_SUBSCRIBED);
__redisGetSubscribeCallback(ac,reply,&cb);
}
if (cb.fn != NULL) {
__redisRunCallback(ac,&cb,reply);
- c->fn->freeObject(reply);
+ c->reader->fn->freeObject(reply);
/* Proceed with free'ing when redisAsyncFree() was called. */
if (c->flags & REDIS_FREEING) {
@@ -394,7 +423,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
* or there were no callbacks to begin with. Either way, don't
* abort with an error, but simply ignore it because the client
* doesn't know what the server will spit out over the wire. */
- c->fn->freeObject(reply);
+ c->reader->fn->freeObject(reply);
}
}
@@ -403,17 +432,48 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
__redisAsyncDisconnect(ac);
}
+/* Internal helper function to detect socket status the first time a read or
+ * write event fires. When connecting was not succesful, the connect callback
+ * is called with a REDIS_ERR status and the context is free'd. */
+static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
+ redisContext *c = &(ac->c);
+
+ if (redisCheckSocketError(c,c->fd) == REDIS_ERR) {
+ /* Try again later when connect(2) is still in progress. */
+ if (errno == EINPROGRESS)
+ return REDIS_OK;
+
+ if (ac->onConnect) ac->onConnect(ac,REDIS_ERR);
+ __redisAsyncDisconnect(ac);
+ return REDIS_ERR;
+ }
+
+ /* Mark context as connected. */
+ c->flags |= REDIS_CONNECTED;
+ if (ac->onConnect) ac->onConnect(ac,REDIS_OK);
+ return REDIS_OK;
+}
+
/* This function should be called when the socket is readable.
* It processes all replies that can be read and executes their callbacks.
*/
void redisAsyncHandleRead(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
+ if (!(c->flags & REDIS_CONNECTED)) {
+ /* Abort connect was not successful. */
+ if (__redisAsyncHandleConnect(ac) != REDIS_OK)
+ return;
+ /* Try again later when the context is still not connected. */
+ if (!(c->flags & REDIS_CONNECTED))
+ return;
+ }
+
if (redisBufferRead(c) == REDIS_ERR) {
__redisAsyncDisconnect(ac);
} else {
/* Always re-schedule reads */
- if (ac->ev.addRead) ac->ev.addRead(ac->ev.data);
+ _EL_ADD_READ(ac);
redisProcessCallbacks(ac);
}
}
@@ -422,24 +482,26 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
int done = 0;
+ if (!(c->flags & REDIS_CONNECTED)) {
+ /* Abort connect was not successful. */
+ if (__redisAsyncHandleConnect(ac) != REDIS_OK)
+ return;
+ /* Try again later when the context is still not connected. */
+ if (!(c->flags & REDIS_CONNECTED))
+ return;
+ }
+
if (redisBufferWrite(c,&done) == REDIS_ERR) {
__redisAsyncDisconnect(ac);
} else {
/* Continue writing when not done, stop writing otherwise */
- if (!done) {
- if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data);
- } else {
- if (ac->ev.delWrite) ac->ev.delWrite(ac->ev.data);
- }
+ if (!done)
+ _EL_ADD_WRITE(ac);
+ else
+ _EL_DEL_WRITE(ac);
/* Always schedule reads after writes */
- if (ac->ev.addRead) ac->ev.addRead(ac->ev.data);
-
- /* Fire onConnect when this is the first write event. */
- if (!(c->flags & REDIS_CONNECTED)) {
- c->flags |= REDIS_CONNECTED;
- if (ac->onConnect) ac->onConnect(ac);
- }
+ _EL_ADD_READ(ac);
}
}
@@ -517,7 +579,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
__redisAppendCommand(c,cmd,len);
/* Always schedule a write when the write buffer is non-empty */
- if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data);
+ _EL_ADD_WRITE(ac);
return REDIS_OK;
}
View
7 deps/hiredis/async.h
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
- * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ * 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.
*
@@ -55,7 +55,7 @@ typedef struct redisCallbackList {
/* Connection callback prototypes */
typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status);
-typedef void (redisConnectCallback)(const struct redisAsyncContext*);
+typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status);
/* Context for an async connection to Redis */
typedef struct redisAsyncContext {
@@ -103,7 +103,6 @@ typedef struct redisAsyncContext {
/* Functions that proxy to hiredis */
redisAsyncContext *redisAsyncConnect(const char *ip, int port);
redisAsyncContext *redisAsyncConnectUnix(const char *path);
-int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFunctions *fn);
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
void redisAsyncDisconnect(redisAsyncContext *ac);
View
13 deps/hiredis/example-ae.c
@@ -18,17 +18,20 @@ void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisAsyncDisconnect(c);
}
-void connectCallback(const redisAsyncContext *c) {
- ((void)c);
- printf("connected...\n");
+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");
- aeStop(loop);
+ printf("Disconnected...\n");
}
int main (int argc, char **argv) {
View
12 deps/hiredis/example-libev.c
@@ -15,16 +15,20 @@ void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisAsyncDisconnect(c);
}
-void connectCallback(const redisAsyncContext *c) {
- ((void)c);
- printf("connected...\n");
+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");
+ printf("Disconnected...\n");
}
int main (int argc, char **argv) {
View
12 deps/hiredis/example-libevent.c
@@ -15,16 +15,20 @@ void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisAsyncDisconnect(c);
}
-void connectCallback(const redisAsyncContext *c) {
- ((void)c);
- printf("connected...\n");
+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");
+ printf("Disconnected...\n");
}
int main (int argc, char **argv) {
View
8 deps/hiredis/fmacros.h
@@ -1,12 +1,14 @@
#ifndef __HIREDIS_FMACRO_H
#define __HIREDIS_FMACRO_H
-#ifndef _BSD_SOURCE
+#if !defined(_BSD_SOURCE)
#define _BSD_SOURCE
#endif
-#ifdef __linux__
-#define _XOPEN_SOURCE 700
+#if defined(__sun__)
+#define _POSIX_C_SOURCE 200112L
+#elif defined(__linux__)
+#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE
#endif
View
722 deps/hiredis/hiredis.c
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
- * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ * 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.
*
@@ -29,6 +29,7 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
+#include "fmacros.h"
#include <string.h>
#include <stdlib.h>
#ifndef _WIN32
@@ -41,30 +42,18 @@
#include "hiredis.h"
#include "net.h"
#include "sds.h"
-#include "util.h"
-
-typedef struct redisReader {
- struct redisReplyObjectFunctions *fn;
- sds error; /* holds optional error */
- void *reply; /* holds temporary reply */
-
- sds buf; /* read buffer */
- size_t pos; /* buffer cursor */
- size_t len; /* buffer length */
-
- redisReadTask rstack[9]; /* stack of read tasks */
- int ridx; /* index of stack */
- void *privdata; /* user-settable arbitrary field */
-} redisReader;
+#ifdef _WIN32
+ #include "../../src/win32fixes.h"
+#endif
static redisReply *createReplyObject(int type);
static void *createStringObject(const redisReadTask *task, char *str, size_t len);
static void *createArrayObject(const redisReadTask *task, int elements);
static void *createIntegerObject(const redisReadTask *task, long long value);
static void *createNilObject(const redisReadTask *task);
-static void redisSetReplyReaderError(redisReader *r, sds err);
-/* Default set of functions to build the reply. */
+/* Default set of functions to build the reply. Keep in mind that such a
+ * function returning NULL is interpreted as OOM. */
static redisReplyObjectFunctions defaultFunctions = {
createStringObject,
createArrayObject,
@@ -75,9 +64,11 @@ static redisReplyObjectFunctions defaultFunctions = {
/* Create a reply object */
static redisReply *createReplyObject(int type) {
- redisReply *r = malloc(sizeof(*r));
+ redisReply *r = calloc(1,sizeof(*r));
+
+ if (r == NULL)
+ return NULL;
- if (!r) redisOOM();
r->type = type;
return r;
}
@@ -91,75 +82,175 @@ void freeReplyObject(void *reply) {
case REDIS_REPLY_INTEGER:
break; /* Nothing to free */
case REDIS_REPLY_ARRAY:
- for (j = 0; j < r->elements; j++)
- if (r->element[j]) freeReplyObject(r->element[j]);
- free(r->element);
+ if (r->element != NULL) {
+ for (j = 0; j < r->elements; j++)
+ if (r->element[j] != NULL)
+ freeReplyObject(r->element[j]);
+ free(r->element);
+ }
break;
case REDIS_REPLY_ERROR:
case REDIS_REPLY_STATUS:
case REDIS_REPLY_STRING:
- free(r->str);
+ if (r->str != NULL)
+ free(r->str);
break;
}
free(r);
}
static void *createStringObject(const redisReadTask *task, char *str, size_t len) {
- redisReply *r = createReplyObject(task->type);
- char *value = malloc(len+1);
- if (!value) redisOOM();
- assert(task->type == REDIS_REPLY_ERROR ||
+ redisReply *r, *parent;
+ char *buf;
+
+ r = createReplyObject(task->type);
+ if (r == NULL)
+ return NULL;
+
+ buf = malloc(len+1);
+ if (buf == NULL) {
+ freeReplyObject(r);
+ return NULL;
+ }
+
+ assert(task->type == REDIS_REPLY_ERROR ||
task->type == REDIS_REPLY_STATUS ||
task->type == REDIS_REPLY_STRING);
/* Copy string value */
- memcpy(value,str,len);
- value[len] = '\0';
- r->str = value;
+ memcpy(buf,str,len);
+ buf[len] = '\0';
+ r->str = buf;
r->len = len;
if (task->parent) {
- redisReply *parent = task->parent->obj;
+ parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY);
parent->element[task->idx] = r;
}
return r;
}
static void *createArrayObject(const redisReadTask *task, int elements) {
- redisReply *r = createReplyObject(REDIS_REPLY_ARRAY);
+ redisReply *r, *parent;
+
+ r = createReplyObject(REDIS_REPLY_ARRAY);
+ if (r == NULL)
+ return NULL;
+
+ if (elements > 0) {
+ r->element = calloc(elements,sizeof(redisReply*));
+ if (r->element == NULL) {
+ freeReplyObject(r);
+ return NULL;
+ }
+ }
+
r->elements = elements;
- if ((r->element = calloc(sizeof(redisReply*),elements)) == NULL)
- redisOOM();
+
if (task->parent) {
- redisReply *parent = task->parent->obj;
+ parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY);
parent->element[task->idx] = r;
}
return r;
}
static void *createIntegerObject(const redisReadTask *task, long long value) {
- redisReply *r = createReplyObject(REDIS_REPLY_INTEGER);
+ redisReply *r, *parent;
+
+ r = createReplyObject(REDIS_REPLY_INTEGER);
+ if (r == NULL)
+ return NULL;
+
r->integer = value;
+
if (task->parent) {
- redisReply *parent = task->parent->obj;
+ parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY);
parent->element[task->idx] = r;
}
return r;
}
static void *createNilObject(const redisReadTask *task) {
- redisReply *r = createReplyObject(REDIS_REPLY_NIL);
+ redisReply *r, *parent;
+
+ r = createReplyObject(REDIS_REPLY_NIL);
+ if (r == NULL)
+ return NULL;
+
if (task->parent) {
- redisReply *parent = task->parent->obj;
+ parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY);
parent->element[task->idx] = r;
}
return r;
}
+static void __redisReaderSetError(redisReader *r, int type, const char *str) {
+ size_t len;
+
+ if (r->reply != NULL && r->fn && r->fn->freeObject) {
+ r->fn->freeObject(r->reply);
+ r->reply = NULL;
+ }
+
+ /* Clear input buffer on errors. */
+ if (r->buf != NULL) {
+ sdsfree(r->buf);
+ r->buf = NULL;
+ r->pos = r->len = 0;
+ }
+
+ /* Reset task stack. */
+ r->ridx = -1;
+
+ /* Set error. */
+ r->err = type;
+ len = strlen(str);
+ len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1);
+ memcpy(r->errstr,str,len);
+ r->errstr[len] = '\0';
+}
+
+static size_t chrtos(char *buf, size_t size, char byte) {
+ size_t len = 0;
+
+ switch(byte) {
+ case '\\':
+ case '"':
+ len = snprintf(buf,size,"\"\\%c\"",byte);
+ break;
+ case '\n': len = snprintf(buf,size,"\"\\n\""); break;
+ case '\r': len = snprintf(buf,size,"\"\\r\""); break;
+ case '\t': len = snprintf(buf,size,"\"\\t\""); break;
+ case '\a': len = snprintf(buf,size,"\"\\a\""); break;
+ case '\b': len = snprintf(buf,size,"\"\\b\""); break;
+ default:
+ if (isprint(byte))
+ len = snprintf(buf,size,"\"%c\"",byte);
+ else
+ len = snprintf(buf,size,"\"\\x%02x\"",(unsigned char)byte);
+ break;
+ }
+
+ return len;
+}
+
+static void __redisReaderSetErrorProtocolByte(redisReader *r, char byte) {
+ char cbuf[8], sbuf[128];
+
+ chrtos(cbuf,sizeof(cbuf),byte);
+ snprintf(sbuf,sizeof(sbuf),
+ "Protocol error, got %s as reply type byte", cbuf);
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf);
+}
+
+static void __redisReaderSetErrorOOM(redisReader *r) {
+ __redisReaderSetError(r,REDIS_ERR_OOM,"Out of memory");
+}
+
static char *readBytes(redisReader *r, unsigned int bytes) {
char *p;
if (r->len-r->pos >= bytes) {
@@ -286,12 +377,18 @@ static int processLineItem(redisReader *r) {
obj = (void*)(size_t)(cur->type);
}
+ if (obj == NULL) {
+ __redisReaderSetErrorOOM(r);
+ return REDIS_ERR;
+ }
+
/* Set reply if this is the root object. */
if (r->ridx == 0) r->reply = obj;
moveToNextTask(r);
- return 0;
+ return REDIS_OK;
}
- return -1;
+
+ return REDIS_ERR;
}
static int processBulkItem(redisReader *r) {
@@ -334,15 +431,21 @@ static int processBulkItem(redisReader *r) {
/* Proceed when obj was created. */
if (success) {
+ if (obj == NULL) {
+ __redisReaderSetErrorOOM(r);
+ return REDIS_ERR;
+ }
+
r->pos += bytelen;
/* Set reply if this is the root object. */
if (r->ridx == 0) r->reply = obj;
moveToNextTask(r);
- return 0;
+ return REDIS_OK;
}
}
- return -1;
+
+ return REDIS_ERR;
}
static int processMultiBulkItem(redisReader *r) {
@@ -356,11 +459,11 @@ static int processMultiBulkItem(redisReader *r) {
#endif
int root = 0;
- /* Set error for nested multi bulks with depth > 1 */
+ /* Set error for nested multi bulks with depth > 2 */
if (r->ridx == 8) {
- redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
- "No support for nested multi bulk replies with depth > 7"));
- return -1;
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
+ "No support for nested multi bulk replies with depth > 7");
+ return REDIS_ERR;
}
if ((p = readLine(r,NULL)) != NULL) {
@@ -372,13 +475,24 @@ static int processMultiBulkItem(redisReader *r) {
obj = r->fn->createNil(cur);
else
obj = (void*)REDIS_REPLY_NIL;
+
+ if (obj == NULL) {
+ __redisReaderSetErrorOOM(r);
+ return REDIS_ERR;
+ }
+
moveToNextTask(r);
} else {
if (r->fn && r->fn->createArray)
obj = r->fn->createArray(cur,(int)elements);
else
obj = (void*)REDIS_REPLY_ARRAY;
+ if (obj == NULL) {
+ __redisReaderSetErrorOOM(r);
+ return REDIS_ERR;
+ }
+
/* Modify task stack when there are more than 0 elements. */
if (elements > 0) {
cur->elements = (int)elements;
@@ -397,15 +511,15 @@ static int processMultiBulkItem(redisReader *r) {
/* Set reply if this is the root object. */
if (root) r->reply = obj;
- return 0;
+ return REDIS_OK;
}
- return -1;
+
+ return REDIS_ERR;
}
static int processItem(redisReader *r) {
redisReadTask *cur = &(r->rstack[r->ridx]);
char *p;
- sds byte;
/* check if we need to read type */
if (cur->type < 0) {
@@ -427,15 +541,12 @@ static int processItem(redisReader *r) {
cur->type = REDIS_REPLY_ARRAY;
break;
default:
- byte = sdscatrepr(sdsempty(),p,1);
- redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
- "Protocol error, got %s as reply type byte", byte));
- sdsfree(byte);
- return -1;
+ __redisReaderSetErrorProtocolByte(r,*p);
+ return REDIS_ERR;
}
} else {
/* could not consume 1 byte */
- return -1;
+ return REDIS_ERR;
}
}
@@ -451,100 +562,80 @@ static int processItem(redisReader *r) {
return processMultiBulkItem(r);
default:
assert(NULL);
- return -1;
+ return REDIS_ERR; /* Avoid warning. */
}
}
-void *redisReplyReaderCreate(void) {
- redisReader *r = calloc(sizeof(redisReader),1);
- r->error = NULL;
+redisReader *redisReaderCreate(void) {
+ redisReader *r;
+
+ r = calloc(sizeof(redisReader),1);
+ if (r == NULL)
+ return NULL;
+
+ r->err = 0;
+ r->errstr[0] = '\0';
r->fn = &defaultFunctions;
r->buf = sdsempty();
- r->ridx = -1;
- return r;
-}
-
-/* Set the function set to build the reply. Returns REDIS_OK when there
- * is no temporary object and it can be set, REDIS_ERR otherwise. */
-int redisReplyReaderSetReplyObjectFunctions(void *reader, redisReplyObjectFunctions *fn) {
- redisReader *r = reader;
- if (r->reply == NULL) {
- r->fn = fn;
- return REDIS_OK;
+ if (r->buf == NULL) {
+ free(r);
+ return NULL;
}
- return REDIS_ERR;
-}
-/* Set the private data field that is used in the read tasks. This argument can
- * be used to curry arbitrary data to the custom reply object functions. */
-int redisReplyReaderSetPrivdata(void *reader, void *privdata) {
- redisReader *r = reader;
- if (r->reply == NULL) {
- r->privdata = privdata;
- return REDIS_OK;
- }
- return REDIS_ERR;
-}
-
-/* External libraries wrapping hiredis might need access to the temporary
- * variable while the reply is built up. When the reader contains an
- * object in between receiving some bytes to parse, this object might
- * otherwise be free'd by garbage collection. */
-void *redisReplyReaderGetObject(void *reader) {
- redisReader *r = reader;
- return r->reply;
+ r->ridx = -1;
+ return r;
}
-void redisReplyReaderFree(void *reader) {
- redisReader *r = reader;
- if (r->error != NULL)
- sdsfree(r->error);
- if (r->reply != NULL && r->fn)
+void redisReaderFree(redisReader *r) {
+ if (r->reply != NULL && r->fn && r->fn->freeObject)
r->fn->freeObject(r->reply);
if (r->buf != NULL)
sdsfree(r->buf);
free(r);
}
-static void redisSetReplyReaderError(redisReader *r, sds err) {
- if (r->reply != NULL)
- r->fn->freeObject(r->reply);
-
- /* Clear remaining buffer when we see a protocol error. */
- if (r->buf != NULL) {
- sdsfree(r->buf);
- r->buf = sdsempty();
- r->pos = r->len = 0;
- }
- r->ridx = -1;
- r->error = err;
-}
-
-char *redisReplyReaderGetError(void *reader) {
- redisReader *r = reader;
- return r->error;
-}
+int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
+ sds newbuf;
-void redisReplyReaderFeed(void *reader, const char *buf, size_t len) {
- redisReader *r = reader;
+ /* Return early when this reader is in an erroneous state. */
+ if (r->err)
+ return REDIS_ERR;
/* Copy the provided buffer. */
if (buf != NULL && len >= 1) {
+#if 0
/* Destroy internal buffer when it is empty and is quite large. */
if (r->len == 0 && sdsavail(r->buf) > 16*1024) {
sdsfree(r->buf);
r->buf = sdsempty();
r->pos = 0;
+
+ /* r->buf should not be NULL since we just free'd a larger one. */
+ assert(r->buf != NULL);
+ }
+#endif
+
+ newbuf = sdscatlen(r->buf,buf,len);
+ if (newbuf == NULL) {
+ __redisReaderSetErrorOOM(r);
+ return REDIS_ERR;
}
- r->buf = sdscatlen(r->buf,buf,len);
+ r->buf = newbuf;
r->len = sdslen(r->buf);
}
+
+ return REDIS_OK;
}
-int redisReplyReaderGetReply(void *reader, void **reply) {
- redisReader *r = reader;
- if (reply != NULL) *reply = NULL;
+int redisReaderGetReply(redisReader *r, void **reply) {
+ /* Default target pointer to NULL. */
+ if (reply != NULL)
+ *reply = NULL;
+
+ /* Return early when this reader is in an erroneous state. */
+ if (r->err)
+ return REDIS_ERR;
/* When the buffer is empty, there will never be a reply. */
if (r->len == 0)
@@ -563,9 +654,13 @@ int redisReplyReaderGetReply(void *reader, void **reply) {
/* Process items in reply. */
while (r->ridx >= 0)
- if (processItem(r) < 0)
+ if (processItem(r) != REDIS_OK)
break;
+ /* Return ASAP when an error occurred. */
+ if (r->err)
+ return REDIS_ERR;
+
/* Discard part of the buffer when we've consumed at least 1k, to avoid
* doing unnecessary calls to memmove() in sds.c. */
if (r->pos >= 1024) {
@@ -576,15 +671,9 @@ int redisReplyReaderGetReply(void *reader, void **reply) {
/* Emit a reply when there is one. */
if (r->ridx == -1) {
- void *aux = r->reply;
+ if (reply != NULL)
+ *reply = r->reply;
r->reply = NULL;
-
- /* Check if there actually *is* a reply. */
- if (r->error != NULL) {
- return REDIS_ERR;
- } else {
- if (reply != NULL) *reply = aux;
- }
}
return REDIS_OK;
}
@@ -603,63 +692,79 @@ static int intlen(int i) {
return len;
}
-/* Helper function for redisvFormatCommand(). */
-static void addArgument(sds a, char ***argv, int *argc, int *totlen) {
- (*argc)++;
- if ((*argv = (char **)realloc(*argv, sizeof(char*)*(*argc))) == NULL) redisOOM();
- if (totlen) *totlen = *totlen+1+intlen(sdslen(a))+2+sdslen(a)+2;
- (*argv)[(*argc)-1] = a;
+/* Helper that calculates the bulk length given a certain string length. */
+static size_t bulklen(size_t len) {
+ return 1+intlen(len)+2+len+2;
}
int redisvFormatCommand(char **target, const char *format, va_list ap) {
- size_t size;
- const char *arg, *c = format;
+ const char *c = format;
char *cmd = NULL; /* final command */
int pos; /* position in final command */
- sds current; /* current argument */
+ sds curarg, newarg; /* current argument */
int touched = 0; /* was the current argument touched? */
- char **argv = NULL;
- int argc = 0, j;
+ char **curargv = NULL, **newargv = NULL;
+ int argc = 0;
int totlen = 0;
+ int j;
/* Abort if there is not target to set */
if (target == NULL)
return -1;
/* Build the command string accordingly to protocol */
- current = sdsempty();
+ curarg = sdsempty();
+ if (curarg == NULL)
+ return -1;
+
while(*c != '\0') {
if (*c != '%' || c[1] == '\0') {
if (*c == ' ') {
if (touched) {
- addArgument(current, &argv, &argc, &totlen);
- current = sdsempty();
+ newargv = realloc(curargv,sizeof(char*)*(argc+1));
+ if (newargv == NULL) goto err;
+ curargv = newargv;
+ curargv[argc++] = curarg;
+ totlen += bulklen(sdslen(curarg));
+
+ /* curarg is put in argv so it can be overwritten. */
+ curarg = sdsempty();
+ if (curarg == NULL) goto err;
touched = 0;
}
} else {
- current = sdscatlen(current,c,1);
+ newarg = sdscatlen(curarg,c,1);
+ if (newarg == NULL) goto err;
+ curarg = newarg;
touched = 1;
}
} else {
+ char *arg;
+ size_t size;
+
+ /* Set newarg so it can be checked even if it is not touched. */
+ newarg = curarg;
+
switch(c[1]) {
case 's':
arg = va_arg(ap,char*);
size = strlen(arg);
if (size > 0)
- current = sdscatlen(current,arg,size);
+ newarg = sdscatlen(curarg,arg,size);
break;
case 'b':
arg = va_arg(ap,char*);
size = va_arg(ap,size_t);
if (size > 0)
- current = sdscatlen(current,arg,size);
+ newarg = sdscatlen(curarg,arg,size);
break;
case '%':
- current = sdscat(current,"%");
+ newarg = sdscat(curarg,"%");
break;
default:
/* Try to detect printf format */
{
+ static const char intfmts[] = "diouxX";
char _format[16];
const char *_p = c+1;
size_t _l = 0;
@@ -681,39 +786,85 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
while (*_p != '\0' && isdigit(*_p)) _p++;
}
- /* Modifiers */
- if (*_p != '\0') {
- if (*_p == 'h' || *_p == 'l') {
- /* Allow a single repetition for these modifiers */
- if (_p[0] == _p[1]) _p++;
- _p++;
+ /* Copy va_list before consuming with va_arg */
+ va_copy(_cpy,ap);
+
+ /* Integer conversion (without modifiers) */
+ if (strchr(intfmts,*_p) != NULL) {
+ va_arg(ap,int);
+ goto fmt_valid;
+ }
+
+ /* Double conversion (without modifiers) */
+ if (strchr("eEfFgGaA",*_p) != NULL) {
+ va_arg(ap,double);
+ goto fmt_valid;
+ }
+
+ /* Size: char */
+ if (_p[0] == 'h' && _p[1] == 'h') {
+ _p += 2;
+ if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
+ va_arg(ap,int); /* char gets promoted to int */
+ goto fmt_valid;
}
+ goto fmt_invalid;
}
- /* Conversion specifier */
- if (*_p != '\0' && strchr("diouxXeEfFgGaA",*_p) != NULL) {
- _l = (_p+1)-c;
- if (_l < sizeof(_format)-2) {
- memcpy(_format,c,_l);
- _format[_l] = '\0';
- va_copy(_cpy,ap);
- current = sdscatvprintf(current,_format,_cpy);
- va_end(_cpy);
-
- /* Update current position (note: outer blocks
- * increment c twice so compensate here) */
- c = _p-1;
+ /* Size: short */
+ if (_p[0] == 'h') {
+ _p += 1;
+ if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
+ va_arg(ap,int); /* short gets promoted to int */
+ goto fmt_valid;
}
+ goto fmt_invalid;
}
- /* Consume and discard vararg */
-#ifdef _WIN32
- va_arg(ap,void *);
-#else
- va_arg(ap,void);
-#endif
+ /* Size: long long */
+ if (_p[0] == 'l' && _p[1] == 'l') {
+ _p += 2;
+ if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
+ va_arg(ap,long long);
+ goto fmt_valid;
+ }
+ goto fmt_invalid;
+ }
+
+ /* Size: long */
+ if (_p[0] == 'l') {
+ _p += 1;
+ if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
+ va_arg(ap,long);
+ goto fmt_valid;
+ }
+ goto fmt_invalid;
+ }
+
+ fmt_invalid:
+ va_end(_cpy);
+ goto err;
+
+ fmt_valid:
+ _l = (_p+1)-c;
+ if (_l < sizeof(_format)-2) {
+ memcpy(_format,c,_l);
+ _format[_l] = '\0';
+ newarg = sdscatvprintf(curarg,_format,_cpy);
+
+ /* Update current position (note: outer blocks
+ * increment c twice so compensate here) */
+ c = _p-1;
+ }
+
+ va_end(_cpy);
+ break;
}
}
+
+ if (newarg == NULL) goto err;
+ curarg = newarg;
+
touched = 1;
c++;
}
@@ -722,35 +873,59 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
/* Add the last argument if needed */
if (touched) {
- addArgument(current, &argv, &argc, &totlen);
+ newargv = realloc(curargv,sizeof(char*)*(argc+1));
+ if (newargv == NULL) goto err;
+ curargv = newargv;
+ curargv[argc++] = curarg;
+ totlen += bulklen(sdslen(curarg));
} else {
- sdsfree(current);
+ sdsfree(curarg);
}
+ /* Clear curarg because it was put in curargv or was free'd. */
+ curarg = NULL;
+
/* Add bytes needed to hold multi bulk count */
totlen += 1+intlen(argc)+2;
/* Build the command at protocol level */
cmd = (char *)malloc(totlen+1);
- if (!cmd) redisOOM();
+ if (cmd == NULL) goto err;
+
pos = sprintf(cmd,"*%d\r\n",argc);
for (j = 0; j < argc; j++) {
#ifdef _WIN32
- pos += sprintf(cmd+pos,"$%llu\r\n",(unsigned long long)sdslen(argv[j]));
+ pos += sprintf(cmd+pos,"$%llu\r\n",(unsigned long long)sdslen(curargv[j]));
#else
- pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(argv[j]));
+ pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(curargv[j]));
#endif
- memcpy(cmd+pos,argv[j],sdslen(argv[j]));
- pos += sdslen(argv[j]);
- sdsfree(argv[j]);
+ memcpy(cmd+pos,curargv[j],sdslen(curargv[j]));
+ pos += sdslen(curargv[j]);
+ sdsfree(curargv[j]);
cmd[pos++] = '\r';
cmd[pos++] = '\n';
}
assert(pos == totlen);
- free(argv);
- cmd[totlen] = '\0';
+ cmd[pos] = '\0';
+
+ free(curargv);
*target = cmd;
return totlen;
+
+err:
+ while(argc--)
+ sdsfree(curargv[argc]);
+ free(curargv);
+
+ if (curarg != NULL)
+ sdsfree(curarg);
+
+ /* No need to check cmd since it is the last statement that can fail,
+ * but do it anyway to be as defensive as possible. */
+ if (cmd != NULL)
+ free(cmd);
+
+ return -1;
}
/* Format a command according to the Redis protocol. This function
@@ -789,12 +964,14 @@ int redisFormatCommandArgv(char **target, int argc, const char **argv, const siz
totlen = 1+intlen(argc)+2;
for (j = 0; j < argc; j++) {
len = argvlen ? argvlen[j] : strlen(argv[j]);
- totlen += 1+intlen(len)+2+len+2;
+ totlen += bulklen(len);
}
/* Build the command at protocol level */
cmd = malloc(totlen+1);
- if (!cmd) redisOOM();
+ if (cmd == NULL)
+ return -1;
+
pos = sprintf(cmd,"*%d\r\n",argc);
for (j = 0; j < argc; j++) {
len = argvlen ? argvlen[j] : strlen(argv[j]);
@@ -809,29 +986,39 @@ int redisFormatCommandArgv(char **target, int argc, const char **argv, const siz
cmd[pos++] = '\n';
}
assert(pos == totlen);
- cmd[totlen] = '\0';
+ cmd[pos] = '\0';
+
*target = cmd;
return totlen;
}
-void __redisSetError(redisContext *c, int type, const sds errstr) {
+void __redisSetError(redisContext *c, int type, const char *str) {
+ size_t len;
+
c->err = type;
- if (errstr != NULL) {
- c->errstr = errstr;
+ if (str != NULL) {
+ len = strlen(str);
+ len = len < (sizeof(c->errstr)-1) ? len : (sizeof(c->errstr)-1);
+ memcpy(c->errstr,str,len);
+ c->errstr[len] = '\0';
} else {
/* Only REDIS_ERR_IO may lack a description! */
assert(type == REDIS_ERR_IO);
- c->errstr = sdsnew(strerror(errno));
+ strerror_r(errno,c->errstr,sizeof(c->errstr));
}
}
static redisContext *redisContextInit(void) {
- redisContext *c = calloc(sizeof(redisContext),1);
+ redisContext *c;
+
+ c = calloc(1,sizeof(redisContext));
+ if (c == NULL)
+ return NULL;
+
c->err = 0;
- c->errstr = NULL;
+ c->errstr[0] = '\0';
c->obuf = sdsempty();
- c->fn = &defaultFunctions;
- c->reader = NULL;
+ c->reader = redisReaderCreate();
return c;
}
@@ -842,12 +1029,10 @@ void redisFree(redisContext *c) {
#else
close(c->fd);
#endif
- if (c->errstr != NULL)
- sdsfree(c->errstr);
if (c->obuf != NULL)
sdsfree(c->obuf);
if (c->reader != NULL)
- redisReplyReaderFree(c->reader);
+ redisReaderFree(c->reader);
free(c);
}
@@ -918,40 +1103,28 @@ int redisSetTimeout(redisContext *c, struct timeval tv) {
return REDIS_ERR;
}
-/* Set the replyObjectFunctions to use. Returns REDIS_ERR when the reader
- * was already initialized and the function set could not be re-set.
- * Return REDIS_OK when they could be set. */
-int redisSetReplyObjectFunctions(redisContext *c, redisReplyObjectFunctions *fn) {
- if (c->reader != NULL)
- return REDIS_ERR;
- c->fn = fn;
- return REDIS_OK;
-}
-
-/* Helper function to lazily create a reply reader. */
-static void __redisCreateReplyReader(redisContext *c) {
- if (c->reader == NULL) {
- c->reader = redisReplyReaderCreate();
- assert(redisReplyReaderSetReplyObjectFunctions(c->reader,c->fn) == REDIS_OK);
- }
-}
-
/* Use this function to handle a read event on the descriptor. It will try
* and read some bytes from the socket and feed them to the reply parser.
*
* After this function is called, you may use redisContextReadReply to
* see if there is a reply available. */
int redisBufferRead(redisContext *c) {
- char buf[2048];
+ char buf[1024*16];
+ int nread;
+
+ /* Return early when the context has seen an error. */
+ if (c->err)
+ return REDIS_ERR;
+
#ifdef _WIN32
- int nread = recv((SOCKET)c->fd,buf,sizeof(buf),0);
+ nread = recv((SOCKET)c->fd,buf,sizeof(buf),0);
if (nread == -1) {
errno = WSAGetLastError();
if ((errno == ENOENT) || (errno == WSAEWOULDBLOCK))
errno = EAGAIN;
}
#else
- int nread = read(c->fd,buf,sizeof(buf));
+ nread = read(c->fd,buf,sizeof(buf));
#endif
if (nread == -1) {
if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) {
@@ -961,12 +1134,13 @@ int redisBufferRead(redisContext *c) {
return REDIS_ERR;
}
} else if (nread == 0) {
- __redisSetError(c,REDIS_ERR_EOF,
- sdsnew("Server closed the connection"));
+ __redisSetError(c,REDIS_ERR_EOF,"Server closed the connection");
return REDIS_ERR;
} else {
- __redisCreateReplyReader(c);
- redisReplyReaderFeed(c->reader,buf,nread);
+ if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) {
+ __redisSetError(c,c->reader->err,c->reader->errstr);
+ return REDIS_ERR;
+ }
}
return REDIS_OK;
}
@@ -982,8 +1156,10 @@ int redisBufferReadDone(redisContext *c, char *buf, int nread) {
sdsnew("Server closed the connection"));
return REDIS_ERR;
} else {
- __redisCreateReplyReader(c);
- redisReplyReaderFeed(c->reader,buf,nread);
+ if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) {
+ __redisSetError(c,c->reader->err,c->reader->errstr);
+ return REDIS_ERR;
+ }
}
return REDIS_OK;
}
@@ -992,13 +1168,18 @@ int redisBufferReadDone(redisContext *c, char *buf, int nread) {
*
* Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was