hiredis 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -28,4 +28,5 @@ desc "Run tests"
28
28
  Rake::TestTask.new(:test) do |t|
29
29
  t.libs << "test"
30
30
  t.pattern = 'test/**/*_test.rb'
31
+ t.verbose = true
31
32
  end
@@ -80,7 +80,7 @@ static VALUE connection_generic_connect(VALUE self, redisContext *c) {
80
80
  }
81
81
  }
82
82
 
83
- redisSetReplyObjectFunctions(c,&redisExtReplyObjectFunctions);
83
+ c->reader->fn = &redisExtReplyObjectFunctions;
84
84
  pc->context = c;
85
85
  return Qnil;
86
86
  }
@@ -2,20 +2,16 @@ require 'mkmf'
2
2
 
3
3
  RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
4
4
 
5
- def need_header(*args)
6
- abort "\n--- #{args.first} is missing\n\n" if !find_header(*args)
5
+ hiredis_dir = File.expand_path(File.join(File.dirname(__FILE__), %w{.. .. vendor hiredis}))
6
+ unless File.directory?(hiredis_dir)
7
+ STDERR.puts "vendor/hiredis missing, please checkout its submodule..."
8
+ exit 1
7
9
  end
8
10
 
9
- def need_library(*args)
10
- abort "\n--- lib#{args.first} is missing\n\n" if !find_library(*args)
11
- end
12
-
13
- bundled_hiredis_dir = File.join(File.dirname(__FILE__), %w{.. .. vendor hiredis})
14
- dir_config('hiredis', bundled_hiredis_dir, bundled_hiredis_dir)
15
-
16
- # Compile hiredis when the bundled version can be found
17
- system("cd #{bundled_hiredis_dir} && make static") if File.directory?(bundled_hiredis_dir)
11
+ # Make sure hiredis is built...
12
+ system("cd #{hiredis_dir} && make static")
18
13
 
19
- need_header('hiredis.h')
20
- need_library('hiredis', 'redisReplyReaderCreate')
14
+ # Statically link to hiredis (mkmf can't do this for us)
15
+ $CFLAGS << " -I#{hiredis_dir}"
16
+ $LDFLAGS << " #{hiredis_dir}/libhiredis.a"
21
17
  create_makefile('hiredis/ext/hiredis_ext')
@@ -61,38 +61,35 @@ redisReplyObjectFunctions redisExtReplyObjectFunctions = {
61
61
  freeObject
62
62
  };
63
63
 
64
- static void reader_mark(void *reader) {
65
- VALUE root;
66
- root = (VALUE)redisReplyReaderGetObject(reader);
64
+ static void reader_mark(redisReader *reader) {
65
+ VALUE root = (VALUE)reader->reply;
67
66
  if (root != 0 && TYPE(root) == T_ARRAY) rb_gc_mark(root);
68
67
  }
69
68
 
70
69
  static VALUE reader_allocate(VALUE klass) {
71
- void *reader = redisReplyReaderCreate();
72
- redisReplyReaderSetReplyObjectFunctions(reader,&redisExtReplyObjectFunctions);
73
- return Data_Wrap_Struct(klass, reader_mark, redisReplyReaderFree, reader);
70
+ redisReader *reader = redisReaderCreate();
71
+ reader->fn = &redisExtReplyObjectFunctions;
72
+ return Data_Wrap_Struct(klass, reader_mark, redisReaderFree, reader);
74
73
  }
75
74
 
76
75
  static VALUE reader_feed(VALUE klass, VALUE str) {
77
- void *reader;
76
+ redisReader *reader;
78
77
 
79
78
  if (TYPE(str) != T_STRING)
80
79
  rb_raise(rb_eTypeError, "not a string");
81
80
 
82
- Data_Get_Struct(klass, void, reader);
83
- redisReplyReaderFeed(reader,RSTRING_PTR(str),(size_t)RSTRING_LEN(str));
81
+ Data_Get_Struct(klass, redisReader, reader);
82
+ redisReaderFeed(reader,RSTRING_PTR(str),(size_t)RSTRING_LEN(str));
84
83
  return INT2NUM(0);
85
84
  }
86
85
 
87
86
  static VALUE reader_gets(VALUE klass) {
88
- void *reader;
87
+ redisReader *reader;
89
88
  VALUE reply;
90
89
 
91
- Data_Get_Struct(klass, void, reader);
92
- if (redisReplyReaderGetReply(reader,(void**)&reply) != REDIS_OK) {
93
- char *errstr = redisReplyReaderGetError(reader);
94
- rb_raise(rb_eRuntimeError,"%s",errstr);
95
- }
90
+ Data_Get_Struct(klass, redisReader, reader);
91
+ if (redisReaderGetReply(reader,(void**)&reply) != REDIS_OK)
92
+ rb_raise(rb_eRuntimeError,"%s",reader->errstr);
96
93
 
97
94
  return reply;
98
95
  }
@@ -1,3 +1,3 @@
1
1
  module Hiredis
2
- VERSION = "0.3.1"
2
+ VERSION = "0.3.2"
3
3
  end
@@ -1,10 +1,29 @@
1
- Copyright (c) 2006-2009, Salvatore Sanfilippo
1
+ Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
2
+ Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
3
+
2
4
  All rights reserved.
3
5
 
4
- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright notice,
10
+ this list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
5
15
 
6
- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7
- * 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.
8
- * 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.
16
+ * Neither the name of Redis nor the names of its contributors may be used
17
+ to endorse or promote products derived from this software without specific
18
+ prior written permission.
9
19
 
10
- 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.
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
24
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -2,99 +2,111 @@
2
2
  # Copyright (C) 2010 Salvatore Sanfilippo <antirez at gmail dot com>
3
3
  # This file is released under the BSD license, see the COPYING file
4
4
 
5
- OBJ = net.o hiredis.o sds.o async.o
6
- BINS = hiredis-example hiredis-test
5
+ OBJ=net.o hiredis.o sds.o async.o
6
+ BINS=hiredis-example hiredis-test
7
+ LIBNAME=libhiredis
7
8
 
8
9
  uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
9
10
  OPTIMIZATION?=-O3
10
11
  ifeq ($(uname_S),SunOS)
11
- CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -D__EXTENSIONS__ -D_XPG6 $(ARCH) $(PROF)
12
+ CFLAGS?=$(OPTIMIZATION) -fPIC -Wall -W -D__EXTENSIONS__ -D_XPG6 $(ARCH) $(PROF)
12
13
  CCLINK?=-ldl -lnsl -lsocket -lm -lpthread
13
- LDFLAGS?=-L. -Wl,-R,.
14
- DYLIBNAME?=libhiredis.so
15
- DYLIB_MAKE_CMD?=$(CC) -G -o ${DYLIBNAME} ${OBJ}
16
- STLIBNAME?=libhiredis.a
17
- STLIB_MAKE_CMD?=ar rcs ${STLIBNAME} ${OBJ}
18
- else ifeq ($(uname_S),Darwin)
19
- CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings $(ARCH) $(PROF)
14
+ LDFLAGS?=-L.
15
+ DYLIBSUFFIX=so
16
+ STLIBSUFFIX=a
17
+ DYLIBNAME?=$(LIBNAME).$(DYLIBSUFFIX)
18
+ DYLIB_MAKE_CMD?=$(CC) -G -o $(DYLIBNAME)
19
+ STLIBNAME?=$(LIBNAME).$(STLIBSUFFIX)
20
+ STLIB_MAKE_CMD?=ar rcs $(STLIBNAME)
21
+ else
22
+ ifeq ($(uname_S),Darwin)
23
+ CFLAGS?=$(OPTIMIZATION) -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings $(ARCH) $(PROF)
20
24
  CCLINK?=-lm -pthread
21
- LDFLAGS?=-L. -Wl,-rpath,.
25
+ LDFLAGS?=-L.
22
26
  OBJARCH?=-arch i386 -arch x86_64
23
- DYLIBNAME?=libhiredis.dylib
24
- DYLIB_MAKE_CMD?=libtool -dynamic -o ${DYLIBNAME} -lm ${DEBUG} - ${OBJ}
25
- STLIBNAME?=libhiredis.a
26
- STLIB_MAKE_CMD?=libtool -static -o ${STLIBNAME} - ${OBJ}
27
+ DYLIBSUFFIX=dylib
28
+ STLIBSUFFIX=a
29
+ DYLIBNAME?=$(LIBNAME).$(DYLIBSUFFIX)
30
+ DYLIB_MAKE_CMD?=libtool -dynamic -o $(DYLIBNAME) -lm $(DEBUG) -
31
+ STLIBNAME?=$(LIBNAME).$(STLIBSUFFIX)
32
+ STLIB_MAKE_CMD?=libtool -static -o $(STLIBNAME) -
27
33
  else
28
- CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings $(ARCH) $(PROF)
34
+ CFLAGS?=$(OPTIMIZATION) -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings $(ARCH) $(PROF)
29
35
  CCLINK?=-lm -pthread
30
- LDFLAGS?=-L. -Wl,-rpath,.
31
- DYLIBNAME?=libhiredis.so
32
- DYLIB_MAKE_CMD?=gcc -shared -Wl,-soname,${DYLIBNAME} -o ${DYLIBNAME} ${OBJ}
33
- STLIBNAME?=libhiredis.a
34
- STLIB_MAKE_CMD?=ar rcs ${STLIBNAME} ${OBJ}
36
+ LDFLAGS?=-L.
37
+ DYLIBSUFFIX=so
38
+ STLIBSUFFIX=a
39
+ DYLIBNAME?=$(LIBNAME).$(DYLIBSUFFIX)
40
+ DYLIB_MAKE_CMD?=gcc -shared -Wl,-soname,$(DYLIBNAME) -o $(DYLIBNAME)
41
+ STLIBNAME?=$(LIBNAME).$(STLIBSUFFIX)
42
+ STLIB_MAKE_CMD?=ar rcs $(STLIBNAME)
43
+ endif
35
44
  endif
45
+
36
46
  CCOPT= $(CFLAGS) $(CCLINK)
37
47
  DEBUG?= -g -ggdb
38
48
 
39
- PREFIX?= /usr/local
40
- INSTALL_INC= $(PREFIX)/include/hiredis
41
- INSTALL_LIB= $(PREFIX)/lib
49
+ PREFIX?=/usr/local
50
+ INCLUDE_PATH?=include/hiredis
51
+ LIBRARY_PATH?=lib
52
+ INSTALL_INCLUDE_PATH= $(PREFIX)/$(INCLUDE_PATH)
53
+ INSTALL_LIBRARY_PATH= $(PREFIX)/$(LIBRARY_PATH)
42
54
  INSTALL= cp -a
43
55
 
44
- all: ${DYLIBNAME} ${BINS}
56
+ all: $(DYLIBNAME) $(BINS)
45
57
 
46
58
  # Deps (use make dep to generate this)
47
- net.o: net.c fmacros.h net.h
48
- async.o: async.c async.h hiredis.h sds.h util.h dict.c dict.h
59
+ net.o: net.c fmacros.h net.h hiredis.h
60
+ async.o: async.c async.h hiredis.h sds.h dict.c dict.h
49
61
  example.o: example.c hiredis.h
50
- hiredis.o: hiredis.c hiredis.h net.h sds.h util.h
62
+ hiredis.o: hiredis.c fmacros.h hiredis.h net.h sds.h
51
63
  sds.o: sds.c sds.h
52
64
  test.o: test.c hiredis.h
53
65
 
54
- ${DYLIBNAME}: ${OBJ}
55
- ${DYLIB_MAKE_CMD}
66
+ $(DYLIBNAME): $(OBJ)
67
+ $(DYLIB_MAKE_CMD) $(OBJ)
56
68
 
57
- ${STLIBNAME}: ${OBJ}
58
- ${STLIB_MAKE_CMD}
69
+ $(STLIBNAME): $(OBJ)
70
+ $(STLIB_MAKE_CMD) $(OBJ)
59
71
 
60
- dynamic: ${DYLIBNAME}
61
- static: ${STLIBNAME}
72
+ dynamic: $(DYLIBNAME)
73
+ static: $(STLIBNAME)
62
74
 
63
75
  # Binaries:
64
- hiredis-example-libevent: example-libevent.c adapters/libevent.h ${DYLIBNAME}
65
- $(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lhiredis -levent example-libevent.c
76
+ hiredis-example-libevent: example-libevent.c adapters/libevent.h $(STLIBNAME)
77
+ $(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) $(STLIBNAME) example-libevent.c -levent
66
78
 
67
- hiredis-example-libev: example-libev.c adapters/libev.h ${DYLIBNAME}
68
- $(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lhiredis -lev example-libev.c
79
+ hiredis-example-libev: example-libev.c adapters/libev.h $(STLIBNAME)
80
+ $(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) $(STLIBNAME) example-libev.c -lev
69
81
 
70
82
  ifndef AE_DIR
71
83
  hiredis-example-ae:
72
84
  @echo "Please specify AE_DIR (e.g. <redis repository>/src)"
73
85
  @false
74
86
  else
75
- hiredis-example-ae: example-ae.c adapters/ae.h ${DYLIBNAME}
76
- $(CC) -o $@ $(CCOPT) $(DEBUG) -I$(AE_DIR) $(LDFLAGS) -lhiredis example-ae.c $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o
87
+ hiredis-example-ae: example-ae.c adapters/ae.h $(STLIBNAME)
88
+ $(CC) -o $@ $(CCOPT) $(DEBUG) -I$(AE_DIR) $(LDFLAGS) $(STLIBNAME) example-ae.c $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o
77
89
  endif
78
90
 
79
- hiredis-%: %.o ${DYLIBNAME}
80
- $(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lhiredis $<
91
+ hiredis-%: %.o $(STLIBNAME)
92
+ $(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) $(STLIBNAME) $<
81
93
 
82
94
  test: hiredis-test
83
95
  ./hiredis-test
84
96
 
85
97
  .c.o:
86
- $(CC) -c $(CFLAGS) $(OBJARCH) $(DEBUG) $(COMPILE_TIME) $<
98
+ $(CC) -std=c99 -pedantic -c $(CFLAGS) $(OBJARCH) $(DEBUG) $(COMPILE_TIME) $<
87
99
 
88
100
  clean:
89
- rm -rf ${DYLIBNAME} ${STLIBNAME} $(BINS) hiredis-example* *.o *.gcda *.gcno *.gcov
101
+ rm -rf $(DYLIBNAME) $(STLIBNAME) $(BINS) hiredis-example* *.o *.gcda *.gcno *.gcov
90
102
 
91
103
  dep:
92
104
  $(CC) -MM *.c
93
105
 
94
- install: ${DYLIBNAME} ${STLIBNAME}
95
- mkdir -p $(INSTALL_INC) $(INSTALL_LIB)
96
- $(INSTALL) hiredis.h async.h adapters $(INSTALL_INC)
97
- $(INSTALL) ${DYLIBNAME} ${STLIBNAME} $(INSTALL_LIB)
106
+ install: $(DYLIBNAME) $(STLIBNAME)
107
+ mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH)
108
+ $(INSTALL) hiredis.h async.h adapters $(INSTALL_INCLUDE_PATH)
109
+ $(INSTALL) $(DYLIBNAME) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)
98
110
 
99
111
  32bit:
100
112
  @echo ""
@@ -1,6 +1,6 @@
1
1
  /*
2
- * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
3
- * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
2
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
3
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
4
4
  *
5
5
  * All rights reserved.
6
6
  *
@@ -36,7 +36,6 @@
36
36
  #include "async.h"
37
37
  #include "dict.c"
38
38
  #include "sds.h"
39
- #include "util.h"
40
39
 
41
40
  /* Forward declaration of function in hiredis.c */
42
41
  void __redisAppendCommand(redisContext *c, char *cmd, size_t len);
@@ -136,11 +135,6 @@ redisAsyncContext *redisAsyncConnectUnix(const char *path) {
136
135
  return ac;
137
136
  }
138
137
 
139
- int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFunctions *fn) {
140
- redisContext *c = &(ac->c);
141
- return redisSetReplyObjectFunctions(c,fn);
142
- }
143
-
144
138
  int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
145
139
  if (ac->onConnect == NULL) {
146
140
  ac->onConnect = fn;
@@ -168,7 +162,6 @@ static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
168
162
 
169
163
  /* Copy callback from stack to heap */
170
164
  cb = malloc(sizeof(*cb));
171
- if (!cb) redisOOM();
172
165
  if (source != NULL) {
173
166
  memcpy(cb,source,sizeof(*cb));
174
167
  cb->next = NULL;
@@ -375,7 +368,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
375
368
 
376
369
  if (cb.fn != NULL) {
377
370
  __redisRunCallback(ac,&cb,reply);
378
- c->fn->freeObject(reply);
371
+ c->reader->fn->freeObject(reply);
379
372
 
380
373
  /* Proceed with free'ing when redisAsyncFree() was called. */
381
374
  if (c->flags & REDIS_FREEING) {
@@ -387,7 +380,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
387
380
  * or there were no callbacks to begin with. Either way, don't
388
381
  * abort with an error, but simply ignore it because the client
389
382
  * doesn't know what the server will spit out over the wire. */
390
- c->fn->freeObject(reply);
383
+ c->reader->fn->freeObject(reply);
391
384
  }
392
385
  }
393
386
 
@@ -1,6 +1,6 @@
1
1
  /*
2
- * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
3
- * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
2
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
3
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
4
4
  *
5
5
  * All rights reserved.
6
6
  *
@@ -103,7 +103,6 @@ typedef struct redisAsyncContext {
103
103
  /* Functions that proxy to hiredis */
104
104
  redisAsyncContext *redisAsyncConnect(const char *ip, int port);
105
105
  redisAsyncContext *redisAsyncConnectUnix(const char *path);
106
- int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFunctions *fn);
107
106
  int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
108
107
  int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
109
108
  void redisAsyncDisconnect(redisAsyncContext *ac);
@@ -1,7 +1,9 @@
1
- #ifndef _REDIS_FMACRO_H
2
- #define _REDIS_FMACRO_H
1
+ #ifndef __HIREDIS_FMACRO_H
2
+ #define __HIREDIS_FMACRO_H
3
3
 
4
+ #ifndef _BSD_SOURCE
4
5
  #define _BSD_SOURCE
6
+ #endif
5
7
 
6
8
  #ifdef __linux__
7
9
  #define _XOPEN_SOURCE 700
@@ -9,7 +11,4 @@
9
11
  #define _XOPEN_SOURCE
10
12
  #endif
11
13
 
12
- #define _LARGEFILE_SOURCE
13
- #define _FILE_OFFSET_BITS 64
14
-
15
14
  #endif
@@ -1,6 +1,6 @@
1
1
  /*
2
- * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
3
- * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
2
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
3
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
4
4
  *
5
5
  * All rights reserved.
6
6
  *
@@ -29,6 +29,7 @@
29
29
  * POSSIBILITY OF SUCH DAMAGE.
30
30
  */
31
31
 
32
+ #include "fmacros.h"
32
33
  #include <string.h>
33
34
  #include <stdlib.h>
34
35
  #include <unistd.h>
@@ -39,30 +40,15 @@
39
40
  #include "hiredis.h"
40
41
  #include "net.h"
41
42
  #include "sds.h"
42
- #include "util.h"
43
-
44
- typedef struct redisReader {
45
- struct redisReplyObjectFunctions *fn;
46
- sds error; /* holds optional error */
47
- void *reply; /* holds temporary reply */
48
-
49
- sds buf; /* read buffer */
50
- size_t pos; /* buffer cursor */
51
- size_t len; /* buffer length */
52
-
53
- redisReadTask rstack[3]; /* stack of read tasks */
54
- int ridx; /* index of stack */
55
- void *privdata; /* user-settable arbitrary field */
56
- } redisReader;
57
43
 
58
44
  static redisReply *createReplyObject(int type);
59
45
  static void *createStringObject(const redisReadTask *task, char *str, size_t len);
60
46
  static void *createArrayObject(const redisReadTask *task, int elements);
61
47
  static void *createIntegerObject(const redisReadTask *task, long long value);
62
48
  static void *createNilObject(const redisReadTask *task);
63
- static void redisSetReplyReaderError(redisReader *r, sds err);
64
49
 
65
- /* Default set of functions to build the reply. */
50
+ /* Default set of functions to build the reply. Keep in mind that such a
51
+ * function returning NULL is interpreted as OOM. */
66
52
  static redisReplyObjectFunctions defaultFunctions = {
67
53
  createStringObject,
68
54
  createArrayObject,
@@ -73,9 +59,11 @@ static redisReplyObjectFunctions defaultFunctions = {
73
59
 
74
60
  /* Create a reply object */
75
61
  static redisReply *createReplyObject(int type) {
76
- redisReply *r = malloc(sizeof(*r));
62
+ redisReply *r = calloc(1,sizeof(*r));
63
+
64
+ if (r == NULL)
65
+ return NULL;
77
66
 
78
- if (!r) redisOOM();
79
67
  r->type = type;
80
68
  return r;
81
69
  }
@@ -89,35 +77,49 @@ void freeReplyObject(void *reply) {
89
77
  case REDIS_REPLY_INTEGER:
90
78
  break; /* Nothing to free */
91
79
  case REDIS_REPLY_ARRAY:
92
- for (j = 0; j < r->elements; j++)
93
- if (r->element[j]) freeReplyObject(r->element[j]);
94
- free(r->element);
80
+ if (r->elements > 0 && r->element != NULL) {
81
+ for (j = 0; j < r->elements; j++)
82
+ if (r->element[j] != NULL)
83
+ freeReplyObject(r->element[j]);
84
+ free(r->element);
85
+ }
95
86
  break;
96
87
  case REDIS_REPLY_ERROR:
97
88
  case REDIS_REPLY_STATUS:
98
89
  case REDIS_REPLY_STRING:
99
- free(r->str);
90
+ if (r->str != NULL)
91
+ free(r->str);
100
92
  break;
101
93
  }
102
94
  free(r);
103
95
  }
104
96
 
105
97
  static void *createStringObject(const redisReadTask *task, char *str, size_t len) {
106
- redisReply *r = createReplyObject(task->type);
107
- char *value = malloc(len+1);
108
- if (!value) redisOOM();
109
- assert(task->type == REDIS_REPLY_ERROR ||
98
+ redisReply *r, *parent;
99
+ char *buf;
100
+
101
+ r = createReplyObject(task->type);
102
+ if (r == NULL)
103
+ return NULL;
104
+
105
+ buf = malloc(len+1);
106
+ if (buf == NULL) {
107
+ freeReplyObject(r);
108
+ return NULL;
109
+ }
110
+
111
+ assert(task->type == REDIS_REPLY_ERROR ||
110
112
  task->type == REDIS_REPLY_STATUS ||
111
113
  task->type == REDIS_REPLY_STRING);
112
114
 
113
115
  /* Copy string value */
114
- memcpy(value,str,len);
115
- value[len] = '\0';
116
- r->str = value;
116
+ memcpy(buf,str,len);
117
+ buf[len] = '\0';
118
+ r->str = buf;
117
119
  r->len = len;
118
120
 
119
121
  if (task->parent) {
120
- redisReply *parent = task->parent->obj;
122
+ parent = task->parent->obj;
121
123
  assert(parent->type == REDIS_REPLY_ARRAY);
122
124
  parent->element[task->idx] = r;
123
125
  }
@@ -125,12 +127,22 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
125
127
  }
126
128
 
127
129
  static void *createArrayObject(const redisReadTask *task, int elements) {
128
- redisReply *r = createReplyObject(REDIS_REPLY_ARRAY);
130
+ redisReply *r, *parent;
131
+
132
+ r = createReplyObject(REDIS_REPLY_ARRAY);
133
+ if (r == NULL)
134
+ return NULL;
135
+
136
+ r->element = calloc(elements,sizeof(redisReply*));
137
+ if (r->element == NULL) {
138
+ freeReplyObject(r);
139
+ return NULL;
140
+ }
141
+
129
142
  r->elements = elements;
130
- if ((r->element = calloc(sizeof(redisReply*),elements)) == NULL)
131
- redisOOM();
143
+
132
144
  if (task->parent) {
133
- redisReply *parent = task->parent->obj;
145
+ parent = task->parent->obj;
134
146
  assert(parent->type == REDIS_REPLY_ARRAY);
135
147
  parent->element[task->idx] = r;
136
148
  }
@@ -138,10 +150,16 @@ static void *createArrayObject(const redisReadTask *task, int elements) {
138
150
  }
139
151
 
140
152
  static void *createIntegerObject(const redisReadTask *task, long long value) {
141
- redisReply *r = createReplyObject(REDIS_REPLY_INTEGER);
153
+ redisReply *r, *parent;
154
+
155
+ r = createReplyObject(REDIS_REPLY_INTEGER);
156
+ if (r == NULL)
157
+ return NULL;
158
+
142
159
  r->integer = value;
160
+
143
161
  if (task->parent) {
144
- redisReply *parent = task->parent->obj;
162
+ parent = task->parent->obj;
145
163
  assert(parent->type == REDIS_REPLY_ARRAY);
146
164
  parent->element[task->idx] = r;
147
165
  }
@@ -149,15 +167,83 @@ static void *createIntegerObject(const redisReadTask *task, long long value) {
149
167
  }
150
168
 
151
169
  static void *createNilObject(const redisReadTask *task) {
152
- redisReply *r = createReplyObject(REDIS_REPLY_NIL);
170
+ redisReply *r, *parent;
171
+
172
+ r = createReplyObject(REDIS_REPLY_NIL);
173
+ if (r == NULL)
174
+ return NULL;
175
+
153
176
  if (task->parent) {
154
- redisReply *parent = task->parent->obj;
177
+ parent = task->parent->obj;
155
178
  assert(parent->type == REDIS_REPLY_ARRAY);
156
179
  parent->element[task->idx] = r;
157
180
  }
158
181
  return r;
159
182
  }
160
183
 
184
+ static void __redisReaderSetError(redisReader *r, int type, const char *str) {
185
+ size_t len;
186
+
187
+ if (r->reply != NULL && r->fn && r->fn->freeObject) {
188
+ r->fn->freeObject(r->reply);
189
+ r->reply = NULL;
190
+ }
191
+
192
+ /* Clear input buffer on errors. */
193
+ if (r->buf != NULL) {
194
+ sdsfree(r->buf);
195
+ r->buf = NULL;
196
+ r->pos = r->len = 0;
197
+ }
198
+
199
+ /* Reset task stack. */
200
+ r->ridx = -1;
201
+
202
+ /* Set error. */
203
+ r->err = type;
204
+ len = strlen(str);
205
+ len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1);
206
+ memcpy(r->errstr,str,len);
207
+ r->errstr[len] = '\0';
208
+ }
209
+
210
+ static size_t chrtos(char *buf, size_t size, char byte) {
211
+ size_t len = 0;
212
+
213
+ switch(byte) {
214
+ case '\\':
215
+ case '"':
216
+ len = snprintf(buf,size,"\"\\%c\"",byte);
217
+ break;
218
+ case '\n': len = snprintf(buf,size,"\"\\n\""); break;
219
+ case '\r': len = snprintf(buf,size,"\"\\r\""); break;
220
+ case '\t': len = snprintf(buf,size,"\"\\t\""); break;
221
+ case '\a': len = snprintf(buf,size,"\"\\a\""); break;
222
+ case '\b': len = snprintf(buf,size,"\"\\b\""); break;
223
+ default:
224
+ if (isprint(byte))
225
+ len = snprintf(buf,size,"\"%c\"",byte);
226
+ else
227
+ len = snprintf(buf,size,"\"\\x%02x\"",(unsigned char)byte);
228
+ break;
229
+ }
230
+
231
+ return len;
232
+ }
233
+
234
+ static void __redisReaderSetErrorProtocolByte(redisReader *r, char byte) {
235
+ char cbuf[8], sbuf[128];
236
+
237
+ chrtos(cbuf,sizeof(cbuf),byte);
238
+ snprintf(sbuf,sizeof(sbuf),
239
+ "Protocol error, got %s as reply type byte", cbuf);
240
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf);
241
+ }
242
+
243
+ static void __redisReaderSetErrorOOM(redisReader *r) {
244
+ __redisReaderSetError(r,REDIS_ERR_OOM,"Out of memory");
245
+ }
246
+
161
247
  static char *readBytes(redisReader *r, unsigned int bytes) {
162
248
  char *p;
163
249
  if (r->len-r->pos >= bytes) {
@@ -271,22 +357,31 @@ static int processLineItem(redisReader *r) {
271
357
  int len;
272
358
 
273
359
  if ((p = readLine(r,&len)) != NULL) {
274
- if (r->fn) {
275
- if (cur->type == REDIS_REPLY_INTEGER) {
360
+ if (cur->type == REDIS_REPLY_INTEGER) {
361
+ if (r->fn && r->fn->createInteger)
276
362
  obj = r->fn->createInteger(cur,readLongLong(p));
277
- } else {
278
- obj = r->fn->createString(cur,p,len);
279
- }
363
+ else
364
+ obj = (void*)REDIS_REPLY_INTEGER;
280
365
  } else {
281
- obj = (void*)(size_t)(cur->type);
366
+ /* Type will be error or status. */
367
+ if (r->fn && r->fn->createString)
368
+ obj = r->fn->createString(cur,p,len);
369
+ else
370
+ obj = (void*)(size_t)(cur->type);
371
+ }
372
+
373
+ if (obj == NULL) {
374
+ __redisReaderSetErrorOOM(r);
375
+ return REDIS_ERR;
282
376
  }
283
377
 
284
378
  /* Set reply if this is the root object. */
285
379
  if (r->ridx == 0) r->reply = obj;
286
380
  moveToNextTask(r);
287
- return 0;
381
+ return REDIS_OK;
288
382
  }
289
- return -1;
383
+
384
+ return REDIS_ERR;
290
385
  }
291
386
 
292
387
  static int processBulkItem(redisReader *r) {
@@ -306,30 +401,40 @@ static int processBulkItem(redisReader *r) {
306
401
 
307
402
  if (len < 0) {
308
403
  /* The nil object can always be created. */
309
- obj = r->fn ? r->fn->createNil(cur) :
310
- (void*)REDIS_REPLY_NIL;
404
+ if (r->fn && r->fn->createNil)
405
+ obj = r->fn->createNil(cur);
406
+ else
407
+ obj = (void*)REDIS_REPLY_NIL;
311
408
  success = 1;
312
409
  } else {
313
410
  /* Only continue when the buffer contains the entire bulk item. */
314
411
  bytelen += len+2; /* include \r\n */
315
412
  if (r->pos+bytelen <= r->len) {
316
- obj = r->fn ? r->fn->createString(cur,s+2,len) :
317
- (void*)REDIS_REPLY_STRING;
413
+ if (r->fn && r->fn->createString)
414
+ obj = r->fn->createString(cur,s+2,len);
415
+ else
416
+ obj = (void*)REDIS_REPLY_STRING;
318
417
  success = 1;
319
418
  }
320
419
  }
321
420
 
322
421
  /* Proceed when obj was created. */
323
422
  if (success) {
423
+ if (obj == NULL) {
424
+ __redisReaderSetErrorOOM(r);
425
+ return REDIS_ERR;
426
+ }
427
+
324
428
  r->pos += bytelen;
325
429
 
326
430
  /* Set reply if this is the root object. */
327
431
  if (r->ridx == 0) r->reply = obj;
328
432
  moveToNextTask(r);
329
- return 0;
433
+ return REDIS_OK;
330
434
  }
331
435
  }
332
- return -1;
436
+
437
+ return REDIS_ERR;
333
438
  }
334
439
 
335
440
  static int processMultiBulkItem(redisReader *r) {
@@ -341,9 +446,9 @@ static int processMultiBulkItem(redisReader *r) {
341
446
 
342
447
  /* Set error for nested multi bulks with depth > 1 */
343
448
  if (r->ridx == 2) {
344
- redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
345
- "No support for nested multi bulk replies with depth > 1"));
346
- return -1;
449
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
450
+ "No support for nested multi bulk replies with depth > 1");
451
+ return REDIS_ERR;
347
452
  }
348
453
 
349
454
  if ((p = readLine(r,NULL)) != NULL) {
@@ -351,12 +456,27 @@ static int processMultiBulkItem(redisReader *r) {
351
456
  root = (r->ridx == 0);
352
457
 
353
458
  if (elements == -1) {
354
- obj = r->fn ? r->fn->createNil(cur) :
355
- (void*)REDIS_REPLY_NIL;
459
+ if (r->fn && r->fn->createNil)
460
+ obj = r->fn->createNil(cur);
461
+ else
462
+ obj = (void*)REDIS_REPLY_NIL;
463
+
464
+ if (obj == NULL) {
465
+ __redisReaderSetErrorOOM(r);
466
+ return REDIS_ERR;
467
+ }
468
+
356
469
  moveToNextTask(r);
357
470
  } else {
358
- obj = r->fn ? r->fn->createArray(cur,elements) :
359
- (void*)REDIS_REPLY_ARRAY;
471
+ if (r->fn && r->fn->createArray)
472
+ obj = r->fn->createArray(cur,elements);
473
+ else
474
+ obj = (void*)REDIS_REPLY_ARRAY;
475
+
476
+ if (obj == NULL) {
477
+ __redisReaderSetErrorOOM(r);
478
+ return REDIS_ERR;
479
+ }
360
480
 
361
481
  /* Modify task stack when there are more than 0 elements. */
362
482
  if (elements > 0) {
@@ -376,15 +496,15 @@ static int processMultiBulkItem(redisReader *r) {
376
496
 
377
497
  /* Set reply if this is the root object. */
378
498
  if (root) r->reply = obj;
379
- return 0;
499
+ return REDIS_OK;
380
500
  }
381
- return -1;
501
+
502
+ return REDIS_ERR;
382
503
  }
383
504
 
384
505
  static int processItem(redisReader *r) {
385
506
  redisReadTask *cur = &(r->rstack[r->ridx]);
386
507
  char *p;
387
- sds byte;
388
508
 
389
509
  /* check if we need to read type */
390
510
  if (cur->type < 0) {
@@ -406,15 +526,12 @@ static int processItem(redisReader *r) {
406
526
  cur->type = REDIS_REPLY_ARRAY;
407
527
  break;
408
528
  default:
409
- byte = sdscatrepr(sdsempty(),p,1);
410
- redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
411
- "Protocol error, got %s as reply type byte", byte));
412
- sdsfree(byte);
413
- return -1;
529
+ __redisReaderSetErrorProtocolByte(r,*p);
530
+ return REDIS_ERR;
414
531
  }
415
532
  } else {
416
533
  /* could not consume 1 byte */
417
- return -1;
534
+ return REDIS_ERR;
418
535
  }
419
536
  }
420
537
 
@@ -430,93 +547,78 @@ static int processItem(redisReader *r) {
430
547
  return processMultiBulkItem(r);
431
548
  default:
432
549
  assert(NULL);
433
- return -1;
550
+ return REDIS_ERR; /* Avoid warning. */
434
551
  }
435
552
  }
436
553
 
437
- void *redisReplyReaderCreate(void) {
438
- redisReader *r = calloc(sizeof(redisReader),1);
439
- r->error = NULL;
440
- r->fn = &defaultFunctions;
441
- r->buf = sdsempty();
442
- r->ridx = -1;
443
- return r;
444
- }
554
+ redisReader *redisReaderCreate(void) {
555
+ redisReader *r;
445
556
 
446
- /* Set the function set to build the reply. Returns REDIS_OK when there
447
- * is no temporary object and it can be set, REDIS_ERR otherwise. */
448
- int redisReplyReaderSetReplyObjectFunctions(void *reader, redisReplyObjectFunctions *fn) {
449
- redisReader *r = reader;
450
- if (r->reply == NULL) {
451
- r->fn = fn;
452
- return REDIS_OK;
453
- }
454
- return REDIS_ERR;
455
- }
557
+ r = calloc(sizeof(redisReader),1);
558
+ if (r == NULL)
559
+ return NULL;
456
560
 
457
- /* Set the private data field that is used in the read tasks. This argument can
458
- * be used to curry arbitrary data to the custom reply object functions. */
459
- int redisReplyReaderSetPrivdata(void *reader, void *privdata) {
460
- redisReader *r = reader;
461
- if (r->reply == NULL) {
462
- r->privdata = privdata;
463
- return REDIS_OK;
561
+ r->err = 0;
562
+ r->errstr[0] = '\0';
563
+ r->fn = &defaultFunctions;
564
+ r->buf = sdsempty();
565
+ if (r->buf == NULL) {
566
+ free(r);
567
+ return NULL;
464
568
  }
465
- return REDIS_ERR;
466
- }
467
569
 
468
- /* External libraries wrapping hiredis might need access to the temporary
469
- * variable while the reply is built up. When the reader contains an
470
- * object in between receiving some bytes to parse, this object might
471
- * otherwise be free'd by garbage collection. */
472
- void *redisReplyReaderGetObject(void *reader) {
473
- redisReader *r = reader;
474
- return r->reply;
570
+ r->ridx = -1;
571
+ return r;
475
572
  }
476
573
 
477
- void redisReplyReaderFree(void *reader) {
478
- redisReader *r = reader;
479
- if (r->error != NULL)
480
- sdsfree(r->error);
481
- if (r->reply != NULL && r->fn)
574
+ void redisReaderFree(redisReader *r) {
575
+ if (r->reply != NULL && r->fn && r->fn->freeObject)
482
576
  r->fn->freeObject(r->reply);
483
577
  if (r->buf != NULL)
484
578
  sdsfree(r->buf);
485
579
  free(r);
486
580
  }
487
581
 
488
- static void redisSetReplyReaderError(redisReader *r, sds err) {
489
- if (r->reply != NULL)
490
- r->fn->freeObject(r->reply);
582
+ int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
583
+ sds newbuf;
491
584
 
492
- /* Clear remaining buffer when we see a protocol error. */
493
- if (r->buf != NULL) {
494
- sdsfree(r->buf);
495
- r->buf = sdsempty();
496
- r->pos = r->len = 0;
497
- }
498
- r->ridx = -1;
499
- r->error = err;
500
- }
501
-
502
- char *redisReplyReaderGetError(void *reader) {
503
- redisReader *r = reader;
504
- return r->error;
505
- }
506
-
507
- void redisReplyReaderFeed(void *reader, const char *buf, size_t len) {
508
- redisReader *r = reader;
585
+ /* Return early when this reader is in an erroneous state. */
586
+ if (r->err)
587
+ return REDIS_ERR;
509
588
 
510
589
  /* Copy the provided buffer. */
511
590
  if (buf != NULL && len >= 1) {
512
- r->buf = sdscatlen(r->buf,buf,len);
591
+ /* Destroy internal buffer when it is empty and is quite large. */
592
+ if (r->len == 0 && sdsavail(r->buf) > 16*1024) {
593
+ sdsfree(r->buf);
594
+ r->buf = sdsempty();
595
+ r->pos = 0;
596
+
597
+ /* r->buf should not be NULL since we just free'd a larger one. */
598
+ assert(r->buf != NULL);
599
+ }
600
+
601
+ newbuf = sdscatlen(r->buf,buf,len);
602
+ if (newbuf == NULL) {
603
+ __redisReaderSetErrorOOM(r);
604
+ return REDIS_ERR;
605
+ }
606
+
607
+ r->buf = newbuf;
513
608
  r->len = sdslen(r->buf);
514
609
  }
610
+
611
+ return REDIS_OK;
515
612
  }
516
613
 
517
- int redisReplyReaderGetReply(void *reader, void **reply) {
518
- redisReader *r = reader;
519
- if (reply != NULL) *reply = NULL;
614
+ int redisReaderGetReply(redisReader *r, void **reply) {
615
+ /* Default target pointer to NULL. */
616
+ if (reply != NULL)
617
+ *reply = NULL;
618
+
619
+ /* Return early when this reader is in an erroneous state. */
620
+ if (r->err)
621
+ return REDIS_ERR;
520
622
 
521
623
  /* When the buffer is empty, there will never be a reply. */
522
624
  if (r->len == 0)
@@ -535,9 +637,13 @@ int redisReplyReaderGetReply(void *reader, void **reply) {
535
637
 
536
638
  /* Process items in reply. */
537
639
  while (r->ridx >= 0)
538
- if (processItem(r) < 0)
640
+ if (processItem(r) != REDIS_OK)
539
641
  break;
540
642
 
643
+ /* Return ASAP when an error occurred. */
644
+ if (r->err)
645
+ return REDIS_ERR;
646
+
541
647
  /* Discard part of the buffer when we've consumed at least 1k, to avoid
542
648
  * doing unnecessary calls to memmove() in sds.c. */
543
649
  if (r->pos >= 1024) {
@@ -548,22 +654,9 @@ int redisReplyReaderGetReply(void *reader, void **reply) {
548
654
 
549
655
  /* Emit a reply when there is one. */
550
656
  if (r->ridx == -1) {
551
- void *aux = r->reply;
657
+ if (reply != NULL)
658
+ *reply = r->reply;
552
659
  r->reply = NULL;
553
-
554
- /* Destroy the buffer when it is empty and is quite large. */
555
- if (r->len == 0 && sdsavail(r->buf) > 16*1024) {
556
- sdsfree(r->buf);
557
- r->buf = sdsempty();
558
- r->pos = 0;
559
- }
560
-
561
- /* Check if there actually *is* a reply. */
562
- if (r->error != NULL) {
563
- return REDIS_ERR;
564
- } else {
565
- if (reply != NULL) *reply = aux;
566
- }
567
660
  }
568
661
  return REDIS_OK;
569
662
  }
@@ -582,60 +675,74 @@ static int intlen(int i) {
582
675
  return len;
583
676
  }
584
677
 
585
- /* Helper function for redisvFormatCommand(). */
586
- static void addArgument(sds a, char ***argv, int *argc, int *totlen) {
587
- (*argc)++;
588
- if ((*argv = realloc(*argv, sizeof(char*)*(*argc))) == NULL) redisOOM();
589
- if (totlen) *totlen = *totlen+1+intlen(sdslen(a))+2+sdslen(a)+2;
590
- (*argv)[(*argc)-1] = a;
678
+ /* Helper that calculates the bulk length given a certain string length. */
679
+ static size_t bulklen(size_t len) {
680
+ return 1+intlen(len)+2+len+2;
591
681
  }
592
682
 
593
683
  int redisvFormatCommand(char **target, const char *format, va_list ap) {
594
- size_t size;
595
- const char *arg, *c = format;
684
+ const char *c = format;
596
685
  char *cmd = NULL; /* final command */
597
686
  int pos; /* position in final command */
598
- sds current; /* current argument */
599
- int interpolated = 0; /* did we do interpolation on an argument? */
600
- char **argv = NULL;
601
- int argc = 0, j;
687
+ sds curarg, newarg; /* current argument */
688
+ int touched = 0; /* was the current argument touched? */
689
+ char **curargv = NULL, **newargv = NULL;
690
+ int argc = 0;
602
691
  int totlen = 0;
692
+ int j;
603
693
 
604
694
  /* Abort if there is not target to set */
605
695
  if (target == NULL)
606
696
  return -1;
607
697
 
608
698
  /* Build the command string accordingly to protocol */
609
- current = sdsempty();
699
+ curarg = sdsempty();
700
+ if (curarg == NULL)
701
+ return -1;
702
+
610
703
  while(*c != '\0') {
611
704
  if (*c != '%' || c[1] == '\0') {
612
705
  if (*c == ' ') {
613
- if (sdslen(current) != 0) {
614
- addArgument(current, &argv, &argc, &totlen);
615
- current = sdsempty();
616
- interpolated = 0;
706
+ if (touched) {
707
+ newargv = realloc(curargv,sizeof(char*)*(argc+1));
708
+ if (newargv == NULL) goto err;
709
+ curargv = newargv;
710
+ curargv[argc++] = curarg;
711
+ totlen += bulklen(sdslen(curarg));
712
+
713
+ /* curarg is put in argv so it can be overwritten. */
714
+ curarg = sdsempty();
715
+ if (curarg == NULL) goto err;
716
+ touched = 0;
617
717
  }
618
718
  } else {
619
- current = sdscatlen(current,c,1);
719
+ newarg = sdscatlen(curarg,c,1);
720
+ if (newarg == NULL) goto err;
721
+ curarg = newarg;
722
+ touched = 1;
620
723
  }
621
724
  } else {
725
+ char *arg;
726
+ size_t size;
727
+
728
+ /* Set newarg so it can be checked even if it is not touched. */
729
+ newarg = curarg;
730
+
622
731
  switch(c[1]) {
623
732
  case 's':
624
733
  arg = va_arg(ap,char*);
625
734
  size = strlen(arg);
626
735
  if (size > 0)
627
- current = sdscatlen(current,arg,size);
628
- interpolated = 1;
736
+ newarg = sdscatlen(curarg,arg,size);
629
737
  break;
630
738
  case 'b':
631
739
  arg = va_arg(ap,char*);
632
740
  size = va_arg(ap,size_t);
633
741
  if (size > 0)
634
- current = sdscatlen(current,arg,size);
635
- interpolated = 1;
742
+ newarg = sdscatlen(curarg,arg,size);
636
743
  break;
637
744
  case '%':
638
- current = sdscat(current,"%");
745
+ newarg = sdscat(curarg,"%");
639
746
  break;
640
747
  default:
641
748
  /* Try to detect printf format */
@@ -677,8 +784,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
677
784
  memcpy(_format,c,_l);
678
785
  _format[_l] = '\0';
679
786
  va_copy(_cpy,ap);
680
- current = sdscatvprintf(current,_format,_cpy);
681
- interpolated = 1;
787
+ newarg = sdscatvprintf(curarg,_format,_cpy);
682
788
  va_end(_cpy);
683
789
 
684
790
  /* Update current position (note: outer blocks
@@ -691,38 +797,67 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
691
797
  va_arg(ap,void);
692
798
  }
693
799
  }
800
+
801
+ if (newarg == NULL) goto err;
802
+ curarg = newarg;
803
+
804
+ touched = 1;
694
805
  c++;
695
806
  }
696
807
  c++;
697
808
  }
698
809
 
699
810
  /* Add the last argument if needed */
700
- if (interpolated || sdslen(current) != 0) {
701
- addArgument(current, &argv, &argc, &totlen);
811
+ if (touched) {
812
+ newargv = realloc(curargv,sizeof(char*)*(argc+1));
813
+ if (newargv == NULL) goto err;
814
+ curargv = newargv;
815
+ curargv[argc++] = curarg;
816
+ totlen += bulklen(sdslen(curarg));
702
817
  } else {
703
- sdsfree(current);
818
+ sdsfree(curarg);
704
819
  }
705
820
 
821
+ /* Clear curarg because it was put in curargv or was free'd. */
822
+ curarg = NULL;
823
+
706
824
  /* Add bytes needed to hold multi bulk count */
707
825
  totlen += 1+intlen(argc)+2;
708
826
 
709
827
  /* Build the command at protocol level */
710
828
  cmd = malloc(totlen+1);
711
- if (!cmd) redisOOM();
829
+ if (cmd == NULL) goto err;
830
+
712
831
  pos = sprintf(cmd,"*%d\r\n",argc);
713
832
  for (j = 0; j < argc; j++) {
714
- pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(argv[j]));
715
- memcpy(cmd+pos,argv[j],sdslen(argv[j]));
716
- pos += sdslen(argv[j]);
717
- sdsfree(argv[j]);
833
+ pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(curargv[j]));
834
+ memcpy(cmd+pos,curargv[j],sdslen(curargv[j]));
835
+ pos += sdslen(curargv[j]);
836
+ sdsfree(curargv[j]);
718
837
  cmd[pos++] = '\r';
719
838
  cmd[pos++] = '\n';
720
839
  }
721
840
  assert(pos == totlen);
722
- free(argv);
723
- cmd[totlen] = '\0';
841
+ cmd[pos] = '\0';
842
+
843
+ free(curargv);
724
844
  *target = cmd;
725
845
  return totlen;
846
+
847
+ err:
848
+ while(argc--)
849
+ sdsfree(curargv[argc]);
850
+ free(curargv);
851
+
852
+ if (curarg != NULL)
853
+ sdsfree(curarg);
854
+
855
+ /* No need to check cmd since it is the last statement that can fail,
856
+ * but do it anyway to be as defensive as possible. */
857
+ if (cmd != NULL)
858
+ free(cmd);
859
+
860
+ return -1;
726
861
  }
727
862
 
728
863
  /* Format a command according to the Redis protocol. This function
@@ -761,12 +896,14 @@ int redisFormatCommandArgv(char **target, int argc, const char **argv, const siz
761
896
  totlen = 1+intlen(argc)+2;
762
897
  for (j = 0; j < argc; j++) {
763
898
  len = argvlen ? argvlen[j] : strlen(argv[j]);
764
- totlen += 1+intlen(len)+2+len+2;
899
+ totlen += bulklen(len);
765
900
  }
766
901
 
767
902
  /* Build the command at protocol level */
768
903
  cmd = malloc(totlen+1);
769
- if (!cmd) redisOOM();
904
+ if (cmd == NULL)
905
+ return -1;
906
+
770
907
  pos = sprintf(cmd,"*%d\r\n",argc);
771
908
  for (j = 0; j < argc; j++) {
772
909
  len = argvlen ? argvlen[j] : strlen(argv[j]);
@@ -777,41 +914,49 @@ int redisFormatCommandArgv(char **target, int argc, const char **argv, const siz
777
914
  cmd[pos++] = '\n';
778
915
  }
779
916
  assert(pos == totlen);
780
- cmd[totlen] = '\0';
917
+ cmd[pos] = '\0';
918
+
781
919
  *target = cmd;
782
920
  return totlen;
783
921
  }
784
922
 
785
- void __redisSetError(redisContext *c, int type, const sds errstr) {
923
+ void __redisSetError(redisContext *c, int type, const char *str) {
924
+ size_t len;
925
+
786
926
  c->err = type;
787
- if (errstr != NULL) {
788
- c->errstr = errstr;
927
+ if (str != NULL) {
928
+ len = strlen(str);
929
+ len = len < (sizeof(c->errstr)-1) ? len : (sizeof(c->errstr)-1);
930
+ memcpy(c->errstr,str,len);
931
+ c->errstr[len] = '\0';
789
932
  } else {
790
933
  /* Only REDIS_ERR_IO may lack a description! */
791
934
  assert(type == REDIS_ERR_IO);
792
- c->errstr = sdsnew(strerror(errno));
935
+ strerror_r(errno,c->errstr,sizeof(c->errstr));
793
936
  }
794
937
  }
795
938
 
796
939
  static redisContext *redisContextInit(void) {
797
- redisContext *c = calloc(sizeof(redisContext),1);
940
+ redisContext *c;
941
+
942
+ c = calloc(1,sizeof(redisContext));
943
+ if (c == NULL)
944
+ return NULL;
945
+
798
946
  c->err = 0;
799
- c->errstr = NULL;
947
+ c->errstr[0] = '\0';
800
948
  c->obuf = sdsempty();
801
- c->fn = &defaultFunctions;
802
- c->reader = NULL;
949
+ c->reader = redisReaderCreate();
803
950
  return c;
804
951
  }
805
952
 
806
953
  void redisFree(redisContext *c) {
807
954
  if (c->fd > 0)
808
955
  close(c->fd);
809
- if (c->errstr != NULL)
810
- sdsfree(c->errstr);
811
956
  if (c->obuf != NULL)
812
957
  sdsfree(c->obuf);
813
958
  if (c->reader != NULL)
814
- redisReplyReaderFree(c->reader);
959
+ redisReaderFree(c->reader);
815
960
  free(c);
816
961
  }
817
962
 
@@ -867,24 +1012,6 @@ int redisSetTimeout(redisContext *c, struct timeval tv) {
867
1012
  return REDIS_ERR;
868
1013
  }
869
1014
 
870
- /* Set the replyObjectFunctions to use. Returns REDIS_ERR when the reader
871
- * was already initialized and the function set could not be re-set.
872
- * Return REDIS_OK when they could be set. */
873
- int redisSetReplyObjectFunctions(redisContext *c, redisReplyObjectFunctions *fn) {
874
- if (c->reader != NULL)
875
- return REDIS_ERR;
876
- c->fn = fn;
877
- return REDIS_OK;
878
- }
879
-
880
- /* Helper function to lazily create a reply reader. */
881
- static void __redisCreateReplyReader(redisContext *c) {
882
- if (c->reader == NULL) {
883
- c->reader = redisReplyReaderCreate();
884
- assert(redisReplyReaderSetReplyObjectFunctions(c->reader,c->fn) == REDIS_OK);
885
- }
886
- }
887
-
888
1015
  /* Use this function to handle a read event on the descriptor. It will try
889
1016
  * and read some bytes from the socket and feed them to the reply parser.
890
1017
  *
@@ -892,7 +1019,13 @@ static void __redisCreateReplyReader(redisContext *c) {
892
1019
  * see if there is a reply available. */
893
1020
  int redisBufferRead(redisContext *c) {
894
1021
  char buf[2048];
895
- int nread = read(c->fd,buf,sizeof(buf));
1022
+ int nread;
1023
+
1024
+ /* Return early when the context has seen an error. */
1025
+ if (c->err)
1026
+ return REDIS_ERR;
1027
+
1028
+ nread = read(c->fd,buf,sizeof(buf));
896
1029
  if (nread == -1) {
897
1030
  if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) {
898
1031
  /* Try again later */
@@ -901,12 +1034,13 @@ int redisBufferRead(redisContext *c) {
901
1034
  return REDIS_ERR;
902
1035
  }
903
1036
  } else if (nread == 0) {
904
- __redisSetError(c,REDIS_ERR_EOF,
905
- sdsnew("Server closed the connection"));
1037
+ __redisSetError(c,REDIS_ERR_EOF,"Server closed the connection");
906
1038
  return REDIS_ERR;
907
1039
  } else {
908
- __redisCreateReplyReader(c);
909
- redisReplyReaderFeed(c->reader,buf,nread);
1040
+ if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) {
1041
+ __redisSetError(c,c->reader->err,c->reader->errstr);
1042
+ return REDIS_ERR;
1043
+ }
910
1044
  }
911
1045
  return REDIS_OK;
912
1046
  }
@@ -922,6 +1056,11 @@ int redisBufferRead(redisContext *c) {
922
1056
  */
923
1057
  int redisBufferWrite(redisContext *c, int *done) {
924
1058
  int nwritten;
1059
+
1060
+ /* Return early when the context has seen an error. */
1061
+ if (c->err)
1062
+ return REDIS_ERR;
1063
+
925
1064
  if (sdslen(c->obuf) > 0) {
926
1065
  nwritten = write(c->fd,c->obuf,sdslen(c->obuf));
927
1066
  if (nwritten == -1) {
@@ -947,10 +1086,8 @@ int redisBufferWrite(redisContext *c, int *done) {
947
1086
  /* Internal helper function to try and get a reply from the reader,
948
1087
  * or set an error in the context otherwise. */
949
1088
  int redisGetReplyFromReader(redisContext *c, void **reply) {
950
- __redisCreateReplyReader(c);
951
- if (redisReplyReaderGetReply(c->reader,reply) == REDIS_ERR) {
952
- __redisSetError(c,REDIS_ERR_PROTOCOL,
953
- sdsnew(((redisReader*)c->reader)->error));
1089
+ if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) {
1090
+ __redisSetError(c,c->reader->err,c->reader->errstr);
954
1091
  return REDIS_ERR;
955
1092
  }
956
1093
  return REDIS_OK;
@@ -993,31 +1130,65 @@ int redisGetReply(redisContext *c, void **reply) {
993
1130
  * is used, you need to call redisGetReply yourself to retrieve
994
1131
  * the reply (or replies in pub/sub).
995
1132
  */
996
- void __redisAppendCommand(redisContext *c, char *cmd, size_t len) {
997
- c->obuf = sdscatlen(c->obuf,cmd,len);
1133
+ int __redisAppendCommand(redisContext *c, char *cmd, size_t len) {
1134
+ sds newbuf;
1135
+
1136
+ newbuf = sdscatlen(c->obuf,cmd,len);
1137
+ if (newbuf == NULL) {
1138
+ __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1139
+ return REDIS_ERR;
1140
+ }
1141
+
1142
+ c->obuf = newbuf;
1143
+ return REDIS_OK;
998
1144
  }
999
1145
 
1000
- void redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
1146
+ int redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
1001
1147
  char *cmd;
1002
1148
  int len;
1149
+
1003
1150
  len = redisvFormatCommand(&cmd,format,ap);
1004
- __redisAppendCommand(c,cmd,len);
1151
+ if (len == -1) {
1152
+ __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1153
+ return REDIS_ERR;
1154
+ }
1155
+
1156
+ if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
1157
+ free(cmd);
1158
+ return REDIS_ERR;
1159
+ }
1160
+
1005
1161
  free(cmd);
1162
+ return REDIS_OK;
1006
1163
  }
1007
1164
 
1008
- void redisAppendCommand(redisContext *c, const char *format, ...) {
1165
+ int redisAppendCommand(redisContext *c, const char *format, ...) {
1009
1166
  va_list ap;
1167
+ int ret;
1168
+
1010
1169
  va_start(ap,format);
1011
- redisvAppendCommand(c,format,ap);
1170
+ ret = redisvAppendCommand(c,format,ap);
1012
1171
  va_end(ap);
1172
+ return ret;
1013
1173
  }
1014
1174
 
1015
- void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1175
+ int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1016
1176
  char *cmd;
1017
1177
  int len;
1178
+
1018
1179
  len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
1019
- __redisAppendCommand(c,cmd,len);
1180
+ if (len == -1) {
1181
+ __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1182
+ return REDIS_ERR;
1183
+ }
1184
+
1185
+ if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
1186
+ free(cmd);
1187
+ return REDIS_ERR;
1188
+ }
1189
+
1020
1190
  free(cmd);
1191
+ return REDIS_OK;
1021
1192
  }
1022
1193
 
1023
1194
  /* Helper function for the redisCommand* family of functions.
@@ -1031,26 +1202,21 @@ void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const
1031
1202
  * otherwise. When NULL is returned in a blocking context, the error field
1032
1203
  * in the context will be set.
1033
1204
  */
1034
- static void *__redisCommand(redisContext *c, char *cmd, size_t len) {
1035
- void *aux = NULL;
1036
- __redisAppendCommand(c,cmd,len);
1205
+ static void *__redisBlockForReply(redisContext *c) {
1206
+ void *reply;
1037
1207
 
1038
1208
  if (c->flags & REDIS_BLOCK) {
1039
- if (redisGetReply(c,&aux) == REDIS_OK)
1040
- return aux;
1041
- return NULL;
1209
+ if (redisGetReply(c,&reply) != REDIS_OK)
1210
+ return NULL;
1211
+ return reply;
1042
1212
  }
1043
1213
  return NULL;
1044
1214
  }
1045
1215
 
1046
1216
  void *redisvCommand(redisContext *c, const char *format, va_list ap) {
1047
- char *cmd;
1048
- int len;
1049
- void *reply = NULL;
1050
- len = redisvFormatCommand(&cmd,format,ap);
1051
- reply = __redisCommand(c,cmd,len);
1052
- free(cmd);
1053
- return reply;
1217
+ if (redisvAppendCommand(c,format,ap) != REDIS_OK)
1218
+ return NULL;
1219
+ return __redisBlockForReply(c);
1054
1220
  }
1055
1221
 
1056
1222
  void *redisCommand(redisContext *c, const char *format, ...) {
@@ -1063,11 +1229,7 @@ void *redisCommand(redisContext *c, const char *format, ...) {
1063
1229
  }
1064
1230
 
1065
1231
  void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1066
- char *cmd;
1067
- int len;
1068
- void *reply = NULL;
1069
- len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
1070
- reply = __redisCommand(c,cmd,len);
1071
- free(cmd);
1072
- return reply;
1232
+ if (redisAppendCommandArgv(c,argc,argv,argvlen) != REDIS_OK)
1233
+ return NULL;
1234
+ return __redisBlockForReply(c);
1073
1235
  }