hiredis 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +1 -0
- data/ext/hiredis_ext/connection.c +1 -1
- data/ext/hiredis_ext/extconf.rb +9 -13
- data/ext/hiredis_ext/reader.c +12 -15
- data/lib/hiredis/version.rb +1 -1
- data/vendor/hiredis/COPYING +25 -6
- data/vendor/hiredis/Makefile +60 -48
- data/vendor/hiredis/async.c +4 -11
- data/vendor/hiredis/async.h +2 -3
- data/vendor/hiredis/fmacros.h +4 -5
- data/vendor/hiredis/hiredis.c +417 -255
- data/vendor/hiredis/hiredis.h +52 -32
- data/vendor/hiredis/net.c +29 -25
- data/vendor/hiredis/net.h +2 -2
- data/vendor/hiredis/sds.c +24 -23
- data/vendor/hiredis/sds.h +10 -0
- data/vendor/hiredis/test.c +83 -52
- metadata +14 -27
- data/vendor/hiredis/util.h +0 -40
data/Rakefile
CHANGED
data/ext/hiredis_ext/extconf.rb
CHANGED
@@ -2,20 +2,16 @@ require 'mkmf'
|
|
2
2
|
|
3
3
|
RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
|
4
4
|
|
5
|
-
|
6
|
-
|
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
|
-
|
10
|
-
|
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
|
-
|
20
|
-
|
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')
|
data/ext/hiredis_ext/reader.c
CHANGED
@@ -61,38 +61,35 @@ redisReplyObjectFunctions redisExtReplyObjectFunctions = {
|
|
61
61
|
freeObject
|
62
62
|
};
|
63
63
|
|
64
|
-
static void reader_mark(
|
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
|
-
|
72
|
-
|
73
|
-
return Data_Wrap_Struct(klass, reader_mark,
|
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
|
-
|
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,
|
83
|
-
|
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
|
-
|
87
|
+
redisReader *reader;
|
89
88
|
VALUE reply;
|
90
89
|
|
91
|
-
Data_Get_Struct(klass,
|
92
|
-
if (
|
93
|
-
|
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
|
}
|
data/lib/hiredis/version.rb
CHANGED
data/vendor/hiredis/COPYING
CHANGED
@@ -1,10 +1,29 @@
|
|
1
|
-
Copyright (c)
|
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
|
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
|
-
|
7
|
-
|
8
|
-
|
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
|
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.
|
data/vendor/hiredis/Makefile
CHANGED
@@ -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
|
6
|
-
BINS
|
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
|
12
|
+
CFLAGS?=$(OPTIMIZATION) -fPIC -Wall -W -D__EXTENSIONS__ -D_XPG6 $(ARCH) $(PROF)
|
12
13
|
CCLINK?=-ldl -lnsl -lsocket -lm -lpthread
|
13
|
-
LDFLAGS?=-L.
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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.
|
25
|
+
LDFLAGS?=-L.
|
22
26
|
OBJARCH?=-arch i386 -arch x86_64
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
34
|
+
CFLAGS?=$(OPTIMIZATION) -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings $(ARCH) $(PROF)
|
29
35
|
CCLINK?=-lm -pthread
|
30
|
-
LDFLAGS?=-L.
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
40
|
-
|
41
|
-
|
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: $
|
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
|
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
|
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
|
-
$
|
55
|
-
$
|
66
|
+
$(DYLIBNAME): $(OBJ)
|
67
|
+
$(DYLIB_MAKE_CMD) $(OBJ)
|
56
68
|
|
57
|
-
$
|
58
|
-
$
|
69
|
+
$(STLIBNAME): $(OBJ)
|
70
|
+
$(STLIB_MAKE_CMD) $(OBJ)
|
59
71
|
|
60
|
-
dynamic: $
|
61
|
-
static: $
|
72
|
+
dynamic: $(DYLIBNAME)
|
73
|
+
static: $(STLIBNAME)
|
62
74
|
|
63
75
|
# Binaries:
|
64
|
-
hiredis-example-libevent: example-libevent.c adapters/libevent.h $
|
65
|
-
$(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS)
|
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 $
|
68
|
-
$(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS)
|
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 $
|
76
|
-
$(CC) -o $@ $(CCOPT) $(DEBUG) -I$(AE_DIR) $(LDFLAGS)
|
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 $
|
80
|
-
$(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS)
|
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 $
|
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: $
|
95
|
-
mkdir -p $(
|
96
|
-
$(INSTALL) hiredis.h async.h adapters $(
|
97
|
-
$(INSTALL) $
|
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 ""
|
data/vendor/hiredis/async.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
|
-
* Copyright (c) 2009-
|
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
|
|
data/vendor/hiredis/async.h
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
|
-
* Copyright (c) 2009-
|
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);
|
data/vendor/hiredis/fmacros.h
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
#ifndef
|
2
|
-
#define
|
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
|
data/vendor/hiredis/hiredis.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
|
-
* Copyright (c) 2009-
|
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 =
|
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
|
-
|
93
|
-
|
94
|
-
|
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
|
-
|
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
|
107
|
-
char *
|
108
|
-
|
109
|
-
|
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(
|
115
|
-
|
116
|
-
r->str =
|
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
|
-
|
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
|
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
|
-
|
131
|
-
redisOOM();
|
143
|
+
|
132
144
|
if (task->parent) {
|
133
|
-
|
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
|
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
|
-
|
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
|
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
|
-
|
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 (
|
275
|
-
if (
|
360
|
+
if (cur->type == REDIS_REPLY_INTEGER) {
|
361
|
+
if (r->fn && r->fn->createInteger)
|
276
362
|
obj = r->fn->createInteger(cur,readLongLong(p));
|
277
|
-
|
278
|
-
obj =
|
279
|
-
}
|
363
|
+
else
|
364
|
+
obj = (void*)REDIS_REPLY_INTEGER;
|
280
365
|
} else {
|
281
|
-
|
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
|
381
|
+
return REDIS_OK;
|
288
382
|
}
|
289
|
-
|
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
|
-
|
310
|
-
(
|
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
|
-
|
317
|
-
(
|
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
|
433
|
+
return REDIS_OK;
|
330
434
|
}
|
331
435
|
}
|
332
|
-
|
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
|
-
|
345
|
-
"No support for nested multi bulk replies with depth > 1")
|
346
|
-
return
|
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
|
-
|
355
|
-
(
|
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
|
-
|
359
|
-
(
|
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
|
499
|
+
return REDIS_OK;
|
380
500
|
}
|
381
|
-
|
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
|
-
|
410
|
-
|
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
|
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
|
550
|
+
return REDIS_ERR; /* Avoid warning. */
|
434
551
|
}
|
435
552
|
}
|
436
553
|
|
437
|
-
|
438
|
-
redisReader *r
|
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
|
-
|
447
|
-
|
448
|
-
|
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
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
if (r->
|
462
|
-
r
|
463
|
-
return
|
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
|
-
|
469
|
-
|
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
|
478
|
-
|
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
|
-
|
489
|
-
|
490
|
-
r->fn->freeObject(r->reply);
|
582
|
+
int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
|
583
|
+
sds newbuf;
|
491
584
|
|
492
|
-
/*
|
493
|
-
if (r->
|
494
|
-
|
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
|
-
|
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
|
518
|
-
|
519
|
-
if (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)
|
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
|
-
|
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
|
586
|
-
static
|
587
|
-
(
|
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
|
-
|
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
|
599
|
-
int
|
600
|
-
char **
|
601
|
-
int argc = 0
|
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
|
-
|
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 (
|
614
|
-
|
615
|
-
|
616
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
635
|
-
interpolated = 1;
|
742
|
+
newarg = sdscatlen(curarg,arg,size);
|
636
743
|
break;
|
637
744
|
case '%':
|
638
|
-
|
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
|
-
|
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 (
|
701
|
-
|
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(
|
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 (
|
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(
|
715
|
-
memcpy(cmd+pos,
|
716
|
-
pos += sdslen(
|
717
|
-
sdsfree(
|
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
|
-
|
723
|
-
|
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 +=
|
899
|
+
totlen += bulklen(len);
|
765
900
|
}
|
766
901
|
|
767
902
|
/* Build the command at protocol level */
|
768
903
|
cmd = malloc(totlen+1);
|
769
|
-
if (
|
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[
|
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
|
923
|
+
void __redisSetError(redisContext *c, int type, const char *str) {
|
924
|
+
size_t len;
|
925
|
+
|
786
926
|
c->err = type;
|
787
|
-
if (
|
788
|
-
|
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
|
935
|
+
strerror_r(errno,c->errstr,sizeof(c->errstr));
|
793
936
|
}
|
794
937
|
}
|
795
938
|
|
796
939
|
static redisContext *redisContextInit(void) {
|
797
|
-
redisContext *c
|
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 =
|
947
|
+
c->errstr[0] = '\0';
|
800
948
|
c->obuf = sdsempty();
|
801
|
-
c->
|
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
|
-
|
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
|
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
|
-
|
909
|
-
|
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
|
-
|
951
|
-
|
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
|
-
|
997
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 *
|
1035
|
-
void *
|
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,&
|
1040
|
-
return
|
1041
|
-
return
|
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
|
-
|
1048
|
-
|
1049
|
-
|
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
|
-
|
1067
|
-
|
1068
|
-
|
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
|
}
|