Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
9703b1b3
Commit
9703b1b3
authored
Apr 19, 2011
by
Pieter Noordhuis
Browse files
Update hiredis
parent
5af30201
Changes
15
Hide whitespace changes
Inline
Side-by-side
deps/hiredis/Makefile
View file @
9703b1b3
...
...
@@ -15,8 +15,9 @@ ifeq ($(uname_S),SunOS)
DYLIB_MAKE_CMD
?=
$(CC)
-G
-o
${DYLIBNAME}
${OBJ}
STLIBNAME
?=
libhiredis.a
STLIB_MAKE_CMD
?=
ar rcs
${STLIBNAME}
${OBJ}
else
ifeq
($(uname_S),Darwin)
CFLAGS
?=
-std
=
c99
-pedantic
$(OPTIMIZATION)
-fPIC
-Wall
-W
-Wwrite-strings
$(ARCH)
$(PROF)
else
ifeq
($(uname_S),Darwin)
CFLAGS
?=
-std
=
c99
-pedantic
$(OPTIMIZATION)
-fPIC
-Wall
-W
-Wstrict-prototypes
-Wwrite-strings
$(ARCH)
$(PROF)
CCLINK
?=
-lm
-pthread
LDFLAGS
?=
-L
.
-Wl
,-rpath,.
OBJARCH
?=
-arch
i386
-arch
x86_64
...
...
@@ -25,7 +26,7 @@ else ifeq ($(uname_S),Darwin)
STLIBNAME
?=
libhiredis.a
STLIB_MAKE_CMD
?=
libtool
-static
-o
${STLIBNAME}
-
${OBJ}
else
CFLAGS
?=
-std
=
c99
-pedantic
$(OPTIMIZATION)
-fPIC
-Wall
-W
-Wwrite-strings
$(ARCH)
$(PROF)
CFLAGS
?=
-std
=
c99
-pedantic
$(OPTIMIZATION)
-fPIC
-Wall
-W
-Wstrict-prototypes
-Wwrite-strings
$(ARCH)
$(PROF)
CCLINK
?=
-lm
-pthread
LDFLAGS
?=
-L
.
-Wl
,-rpath,.
DYLIBNAME
?=
libhiredis.so
...
...
@@ -33,6 +34,8 @@ else
STLIBNAME
?=
libhiredis.a
STLIB_MAKE_CMD
?=
ar rcs
${STLIBNAME}
${OBJ}
endif
endif
CCOPT
=
$(CFLAGS)
$(CCLINK)
DEBUG
?=
-g
-ggdb
...
...
@@ -45,7 +48,7 @@ all: ${DYLIBNAME} ${BINS}
# Deps (use make dep to generate this)
net.o
:
net.c fmacros.h net.h
async.o
:
async.c async.h hiredis.h sds.h util.h
async.o
:
async.c async.h hiredis.h sds.h util.h
dict.c dict.h
example.o
:
example.c hiredis.h
hiredis.o
:
hiredis.c hiredis.h net.h sds.h util.h
sds.o
:
sds.c sds.h
...
...
deps/hiredis/README.md
View file @
9703b1b3
...
...
@@ -108,7 +108,7 @@ was received:
*
**`REDIS_REPLY_ARRAY`**
:
*
A multi bulk reply. The number of elements in the multi bulk reply is stored in
`reply->elements`
. Every element in the multi bulk reply is a
`redisReply`
object as well
and can be accessed via
`reply->element
s
[..index..]`
.
and can be accessed via
`reply->element[..index..]`
.
Redis may reply with nested arrays but this is fully supported.
Replies should be freed using the
`freeReplyObject()`
function.
...
...
@@ -171,7 +171,7 @@ the latter means an error occurred while reading a reply. Just as with the other
the
`err`
field in the context can be used to find out what the cause of this error is.
The following examples shows a simple pipeline (resulting in only a single call to
`write(2)`
and
a single call to
`
write
(2)`
):
a single call to
`
read
(2)`
):
redisReply *reply;
redisAppendCommand(context,"SET foo bar");
...
...
deps/hiredis/async.c
View file @
9703b1b3
...
...
@@ -30,14 +30,58 @@
*/
#include <string.h>
#include <strings.h>
#include <assert.h>
#include <ctype.h>
#include "async.h"
#include "dict.c"
#include "sds.h"
#include "util.h"
/* Forward declaration of function in hiredis.c */
void
__redisAppendCommand
(
redisContext
*
c
,
char
*
cmd
,
size_t
len
);
/* Functions managing dictionary of callbacks for pub/sub. */
static
unsigned
int
callbackHash
(
const
void
*
key
)
{
return
dictGenHashFunction
((
unsigned
char
*
)
key
,
sdslen
((
char
*
)
key
));
}
static
void
*
callbackValDup
(
void
*
privdata
,
const
void
*
src
)
{
((
void
)
privdata
);
redisCallback
*
dup
=
malloc
(
sizeof
(
*
dup
));
memcpy
(
dup
,
src
,
sizeof
(
*
dup
));
return
dup
;
}
static
int
callbackKeyCompare
(
void
*
privdata
,
const
void
*
key1
,
const
void
*
key2
)
{
int
l1
,
l2
;
((
void
)
privdata
);
l1
=
sdslen
((
sds
)
key1
);
l2
=
sdslen
((
sds
)
key2
);
if
(
l1
!=
l2
)
return
0
;
return
memcmp
(
key1
,
key2
,
l1
)
==
0
;
}
static
void
callbackKeyDestructor
(
void
*
privdata
,
void
*
key
)
{
((
void
)
privdata
);
sdsfree
((
sds
)
key
);
}
static
void
callbackValDestructor
(
void
*
privdata
,
void
*
val
)
{
((
void
)
privdata
);
free
(
val
);
}
static
dictType
callbackDict
=
{
callbackHash
,
NULL
,
callbackValDup
,
callbackKeyCompare
,
callbackKeyDestructor
,
callbackValDestructor
};
static
redisAsyncContext
*
redisAsyncInitialize
(
redisContext
*
c
)
{
redisAsyncContext
*
ac
=
realloc
(
c
,
sizeof
(
redisAsyncContext
));
c
=
&
(
ac
->
c
);
...
...
@@ -50,19 +94,23 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
ac
->
err
=
0
;
ac
->
errstr
=
NULL
;
ac
->
data
=
NULL
;
ac
->
_adapter_data
=
NULL
;
ac
->
evAddRead
=
NULL
;
ac
->
evDelRead
=
NULL
;
ac
->
evAddWrite
=
NULL
;
ac
->
evDelWrite
=
NULL
;
ac
->
evCleanup
=
NULL
;
ac
->
ev
.
data
=
NULL
;
ac
->
ev
.
addRead
=
NULL
;
ac
->
ev
.
delRead
=
NULL
;
ac
->
ev
.
addWrite
=
NULL
;
ac
->
ev
.
delWrite
=
NULL
;
ac
->
ev
.
cleanup
=
NULL
;
ac
->
onConnect
=
NULL
;
ac
->
onDisconnect
=
NULL
;
ac
->
replies
.
head
=
NULL
;
ac
->
replies
.
tail
=
NULL
;
ac
->
sub
.
invalid
.
head
=
NULL
;
ac
->
sub
.
invalid
.
tail
=
NULL
;
ac
->
sub
.
channels
=
dictCreate
(
&
callbackDict
,
NULL
);
ac
->
sub
.
patterns
=
dictCreate
(
&
callbackDict
,
NULL
);
return
ac
;
}
...
...
@@ -96,6 +144,11 @@ int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFun
int
redisAsyncSetConnectCallback
(
redisAsyncContext
*
ac
,
redisConnectCallback
*
fn
)
{
if
(
ac
->
onConnect
==
NULL
)
{
ac
->
onConnect
=
fn
;
/* The common way to detect an established connection is to wait for
* the first write event to be fired. This assumes the related event
* library functions are already set. */
if
(
ac
->
ev
.
addWrite
)
ac
->
ev
.
addWrite
(
ac
->
ev
.
data
);
return
REDIS_OK
;
}
return
REDIS_ERR
;
...
...
@@ -114,11 +167,11 @@ static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
redisCallback
*
cb
;
/* Copy callback from stack to heap */
cb
=
c
alloc
(
1
,
sizeof
(
*
cb
));
cb
=
m
alloc
(
sizeof
(
*
cb
));
if
(
!
cb
)
redisOOM
();
if
(
source
!=
NULL
)
{
cb
->
fn
=
source
->
fn
;
cb
->
privdata
=
source
->
privdata
;
memcpy
(
cb
,
source
,
sizeof
(
*
cb
))
;
cb
->
next
=
NULL
;
}
/* Store callback in list */
...
...
@@ -146,51 +199,150 @@ static int __redisShiftCallback(redisCallbackList *list, redisCallback *target)
return
REDIS_ERR
;
}
/* Tries to do a clean disconnect from Redis, meaning it stops new commands
* from being issued, but tries to flush the output buffer and execute
* callbacks for all remaining replies.
*
* This functions is generally called from within a callback, so the
* processCallbacks function will pick up the flag when there are no
* more replies. */
void
redisAsyncDisconnect
(
redisAsyncContext
*
ac
)
{
static
void
__redisRunCallback
(
redisAsyncContext
*
ac
,
redisCallback
*
cb
,
redisReply
*
reply
)
{
redisContext
*
c
=
&
(
ac
->
c
);
c
->
flags
|=
REDIS_DISCONNECTING
;
if
(
cb
->
fn
!=
NULL
)
{
c
->
flags
|=
REDIS_IN_CALLBACK
;
cb
->
fn
(
ac
,
reply
,
cb
->
privdata
);
c
->
flags
&=
~
REDIS_IN_CALLBACK
;
}
}
/* Helper function to free the context. */
static
void
__redisAsyncFree
(
redisAsyncContext
*
ac
)
{
redisContext
*
c
=
&
(
ac
->
c
);
redisCallback
cb
;
dictIterator
*
it
;
dictEntry
*
de
;
/* Execute pending callbacks with NULL reply. */
while
(
__redisShiftCallback
(
&
ac
->
replies
,
&
cb
)
==
REDIS_OK
)
__redisRunCallback
(
ac
,
&
cb
,
NULL
);
/* Execute callbacks for invalid commands */
while
(
__redisShiftCallback
(
&
ac
->
sub
.
invalid
,
&
cb
)
==
REDIS_OK
)
__redisRunCallback
(
ac
,
&
cb
,
NULL
);
/* Run subscription callbacks callbacks with NULL reply */
it
=
dictGetIterator
(
ac
->
sub
.
channels
);
while
((
de
=
dictNext
(
it
))
!=
NULL
)
__redisRunCallback
(
ac
,
dictGetEntryVal
(
de
),
NULL
);
dictReleaseIterator
(
it
);
dictRelease
(
ac
->
sub
.
channels
);
it
=
dictGetIterator
(
ac
->
sub
.
patterns
);
while
((
de
=
dictNext
(
it
))
!=
NULL
)
__redisRunCallback
(
ac
,
dictGetEntryVal
(
de
),
NULL
);
dictReleaseIterator
(
it
);
dictRelease
(
ac
->
sub
.
patterns
);
/* Signal event lib to clean up */
if
(
ac
->
ev
.
cleanup
)
ac
->
ev
.
cleanup
(
ac
->
ev
.
data
);
/* Execute disconnect callback. When redisAsyncFree() initiated destroying
* this context, the status will always be REDIS_OK. */
if
(
ac
->
onDisconnect
&&
(
c
->
flags
&
REDIS_CONNECTED
))
{
if
(
c
->
flags
&
REDIS_FREEING
)
{
ac
->
onDisconnect
(
ac
,
REDIS_OK
);
}
else
{
ac
->
onDisconnect
(
ac
,(
ac
->
err
==
0
)
?
REDIS_OK
:
REDIS_ERR
);
}
}
/* Cleanup self */
redisFree
(
c
);
}
/* Free the async context. When this function is called from a callback,
* control needs to be returned to redisProcessCallbacks() before actual
* free'ing. To do so, a flag is set on the context which is picked up by
* redisProcessCallbacks(). Otherwise, the context is immediately free'd. */
void
redisAsyncFree
(
redisAsyncContext
*
ac
)
{
redisContext
*
c
=
&
(
ac
->
c
);
c
->
flags
|=
REDIS_FREEING
;
if
(
!
(
c
->
flags
&
REDIS_IN_CALLBACK
))
__redisAsyncFree
(
ac
);
}
/* Helper function to make the disconnect happen and clean up. */
static
void
__redisAsyncDisconnect
(
redisAsyncContext
*
ac
)
{
redisContext
*
c
=
&
(
ac
->
c
);
redisCallback
cb
;
int
status
;
/* Make sure error is accessible if there is any */
__redisAsyncCopyError
(
ac
);
status
=
(
ac
->
err
==
0
)
?
REDIS_OK
:
REDIS_ERR
;
if
(
status
==
REDIS_OK
)
{
/* When the connection is cleanly disconnected, there should not
* be pending callbacks. */
if
(
ac
->
err
==
0
)
{
/* For clean disconnects, there should be no pending callbacks. */
assert
(
__redisShiftCallback
(
&
ac
->
replies
,
NULL
)
==
REDIS_ERR
);
}
else
{
/* Callbacks should not be able to issue new commands. */
/* Disconnection is caused by an error, make sure that pending
* callbacks cannot call new commands. */
c
->
flags
|=
REDIS_DISCONNECTING
;
/* Execute pending callbacks with NULL reply. */
while
(
__redisShiftCallback
(
&
ac
->
replies
,
&
cb
)
==
REDIS_OK
)
{
if
(
cb
.
fn
!=
NULL
)
cb
.
fn
(
ac
,
NULL
,
cb
.
privdata
);
}
}
/* Signal event lib to clean up */
if
(
ac
->
evCleanup
)
ac
->
evCleanup
(
ac
->
_adapter_data
);
/* For non-clean disconnects, __redisAsyncFree() will execute pending
* callbacks with a NULL-reply. */
__redisAsyncFree
(
ac
);
}
/* Execute callback with proper status */
if
(
ac
->
onDisconnect
)
ac
->
onDisconnect
(
ac
,
status
);
/* Tries to do a clean disconnect from Redis, meaning it stops new commands
* from being issued, but tries to flush the output buffer and execute
* callbacks for all remaining replies. When this function is called from a
* callback, there might be more replies and we can safely defer disconnecting
* to redisProcessCallbacks(). Otherwise, we can only disconnect immediately
* when there are no pending callbacks. */
void
redisAsyncDisconnect
(
redisAsyncContext
*
ac
)
{
redisContext
*
c
=
&
(
ac
->
c
);
c
->
flags
|=
REDIS_DISCONNECTING
;
if
(
!
(
c
->
flags
&
REDIS_IN_CALLBACK
)
&&
ac
->
replies
.
head
==
NULL
)
__redisAsyncDisconnect
(
ac
);
}
/* Cleanup self */
redisFree
(
c
);
static
int
__redisGetSubscribeCallback
(
redisAsyncContext
*
ac
,
redisReply
*
reply
,
redisCallback
*
dstcb
)
{
redisContext
*
c
=
&
(
ac
->
c
);
dict
*
callbacks
;
dictEntry
*
de
;
int
pvariant
;
char
*
stype
;
sds
sname
;
/* Custom reply functions are not supported for pub/sub. This will fail
* very hard when they are used... */
if
(
reply
->
type
==
REDIS_REPLY_ARRAY
)
{
assert
(
reply
->
elements
>=
2
);
assert
(
reply
->
element
[
0
]
->
type
==
REDIS_REPLY_STRING
);
stype
=
reply
->
element
[
0
]
->
str
;
pvariant
=
(
tolower
(
stype
[
0
])
==
'p'
)
?
1
:
0
;
if
(
pvariant
)
callbacks
=
ac
->
sub
.
patterns
;
else
callbacks
=
ac
->
sub
.
channels
;
/* Locate the right callback */
assert
(
reply
->
element
[
1
]
->
type
==
REDIS_REPLY_STRING
);
sname
=
sdsnewlen
(
reply
->
element
[
1
]
->
str
,
reply
->
element
[
1
]
->
len
);
de
=
dictFind
(
callbacks
,
sname
);
if
(
de
!=
NULL
)
{
memcpy
(
dstcb
,
dictGetEntryVal
(
de
),
sizeof
(
*
dstcb
));
/* If this is an unsubscribe message, remove it. */
if
(
strcasecmp
(
stype
+
pvariant
,
"unsubscribe"
)
==
0
)
{
dictDelete
(
callbacks
,
sname
);
/* If this was the last unsubscribe message, revert to
* non-subscribe mode. */
assert
(
reply
->
element
[
2
]
->
type
==
REDIS_REPLY_INTEGER
);
if
(
reply
->
element
[
2
]
->
integer
==
0
)
c
->
flags
&=
~
REDIS_SUBSCRIBED
;
}
}
sdsfree
(
sname
);
}
else
{
/* Shift callback for invalid commands. */
__redisShiftCallback
(
&
ac
->
sub
.
invalid
,
dstcb
);
}
return
REDIS_OK
;
}
void
redisProcessCallbacks
(
redisAsyncContext
*
ac
)
{
...
...
@@ -213,11 +365,28 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
break
;
}
/* Shift callback and execute it */
assert
(
__redisShiftCallback
(
&
ac
->
replies
,
&
cb
)
==
REDIS_OK
);
/* Even if the context is subscribed, pending regular callbacks will
* get a reply before pub/sub messages arrive. */
if
(
__redisShiftCallback
(
&
ac
->
replies
,
&
cb
)
!=
REDIS_OK
)
{
/* No more regular callbacks, the context *must* be subscribed. */
assert
(
c
->
flags
&
REDIS_SUBSCRIBED
);
__redisGetSubscribeCallback
(
ac
,
reply
,
&
cb
);
}
if
(
cb
.
fn
!=
NULL
)
{
cb
.
fn
(
ac
,
reply
,
cb
.
privdata
);
__redisRunCallback
(
ac
,
&
cb
,
reply
);
c
->
fn
->
freeObject
(
reply
);
/* Proceed with free'ing when redisAsyncFree() was called. */
if
(
c
->
flags
&
REDIS_FREEING
)
{
__redisAsyncFree
(
ac
);
return
;
}
}
else
{
/* No callback for this reply. This can either be a NULL callback,
* or there were no callbacks to begin with. Either way, don't
* abort with an error, but simply ignore it because the client
* doesn't know what the server will spit out over the wire. */
c
->
fn
->
freeObject
(
reply
);
}
}
...
...
@@ -237,7 +406,7 @@ void redisAsyncHandleRead(redisAsyncContext *ac) {
__redisAsyncDisconnect
(
ac
);
}
else
{
/* Always re-schedule reads */
if
(
ac
->
ev
A
ddRead
)
ac
->
ev
A
ddRead
(
ac
->
_adapter_
data
);
if
(
ac
->
ev
.
a
ddRead
)
ac
->
ev
.
a
ddRead
(
ac
->
ev
.
data
);
redisProcessCallbacks
(
ac
);
}
}
...
...
@@ -251,13 +420,13 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
}
else
{
/* Continue writing when not done, stop writing otherwise */
if
(
!
done
)
{
if
(
ac
->
ev
A
ddWrite
)
ac
->
ev
A
ddWrite
(
ac
->
_adapter_
data
);
if
(
ac
->
ev
.
a
ddWrite
)
ac
->
ev
.
a
ddWrite
(
ac
->
ev
.
data
);
}
else
{
if
(
ac
->
ev
D
elWrite
)
ac
->
ev
D
elWrite
(
ac
->
_adapter_
data
);
if
(
ac
->
ev
.
d
elWrite
)
ac
->
ev
.
d
elWrite
(
ac
->
ev
.
data
);
}
/* Always schedule reads after writes */
if
(
ac
->
ev
A
ddRead
)
ac
->
ev
A
ddRead
(
ac
->
_adapter_
data
);
if
(
ac
->
ev
.
a
ddRead
)
ac
->
ev
.
a
ddRead
(
ac
->
ev
.
data
);
/* Fire onConnect when this is the first write event. */
if
(
!
(
c
->
flags
&
REDIS_CONNECTED
))
{
...
...
@@ -267,26 +436,81 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
}
}
/* Helper function for the redisAsyncCommand* family of functions.
*
* Write a formatted command to the output buffer and register the provided
* callback function with the context.
*/
/* Sets a pointer to the first argument and its length starting at p. Returns
* the number of bytes to skip to get to the following argument. */
static
char
*
nextArgument
(
char
*
start
,
char
**
str
,
size_t
*
len
)
{
char
*
p
=
start
;
if
(
p
[
0
]
!=
'$'
)
{
p
=
strchr
(
p
,
'$'
);
if
(
p
==
NULL
)
return
NULL
;
}
*
len
=
(
int
)
strtol
(
p
+
1
,
NULL
,
10
);
p
=
strchr
(
p
,
'\r'
);
assert
(
p
);
*
str
=
p
+
2
;
return
p
+
2
+
(
*
len
)
+
2
;
}
/* Helper function for the redisAsyncCommand* family of functions. Writes a
* formatted command to the output buffer and registers the provided callback
* function with the context. */
static
int
__redisAsyncCommand
(
redisAsyncContext
*
ac
,
redisCallbackFn
*
fn
,
void
*
privdata
,
char
*
cmd
,
size_t
len
)
{
redisContext
*
c
=
&
(
ac
->
c
);
redisCallback
cb
;
int
pvariant
,
hasnext
;
char
*
cstr
,
*
astr
;
size_t
clen
,
alen
;
char
*
p
;
sds
sname
;
/* Don't accept new commands when the connection is lazily closed. */
if
(
c
->
flags
&
REDIS_DISCONNECTING
)
return
REDIS_ERR
;
__redisAppendCommand
(
c
,
cmd
,
len
);
/* Don't accept new commands when the connection is about to be closed. */
if
(
c
->
flags
&
(
REDIS_DISCONNECTING
|
REDIS_FREEING
))
return
REDIS_ERR
;
/* S
tore
callback */
/* S
etup
callback */
cb
.
fn
=
fn
;
cb
.
privdata
=
privdata
;
__redisPushCallback
(
&
ac
->
replies
,
&
cb
);
/* Find out which command will be appended. */
p
=
nextArgument
(
cmd
,
&
cstr
,
&
clen
);
assert
(
p
!=
NULL
);
hasnext
=
(
p
[
0
]
==
'$'
);
pvariant
=
(
tolower
(
cstr
[
0
])
==
'p'
)
?
1
:
0
;
cstr
+=
pvariant
;
clen
-=
pvariant
;
if
(
hasnext
&&
strncasecmp
(
cstr
,
"subscribe
\r\n
"
,
11
)
==
0
)
{
c
->
flags
|=
REDIS_SUBSCRIBED
;
/* Add every channel/pattern to the list of subscription callbacks. */
while
((
p
=
nextArgument
(
p
,
&
astr
,
&
alen
))
!=
NULL
)
{
sname
=
sdsnewlen
(
astr
,
alen
);
if
(
pvariant
)
dictReplace
(
ac
->
sub
.
patterns
,
sname
,
&
cb
);
else
dictReplace
(
ac
->
sub
.
channels
,
sname
,
&
cb
);
}
}
else
if
(
strncasecmp
(
cstr
,
"unsubscribe
\r\n
"
,
13
)
==
0
)
{
/* It is only useful to call (P)UNSUBSCRIBE when the context is
* subscribed to one or more channels or patterns. */
if
(
!
(
c
->
flags
&
REDIS_SUBSCRIBED
))
return
REDIS_ERR
;
/* (P)UNSUBSCRIBE does not have its own response: every channel or
* pattern that is unsubscribed will receive a message. This means we
* should not append a callback function for this command. */
}
else
{
if
(
c
->
flags
&
REDIS_SUBSCRIBED
)
/* This will likely result in an error reply, but it needs to be
* received and passed to the callback. */
__redisPushCallback
(
&
ac
->
sub
.
invalid
,
&
cb
);
else
__redisPushCallback
(
&
ac
->
replies
,
&
cb
);
}
__redisAppendCommand
(
c
,
cmd
,
len
);
/* Always schedule a write when the write buffer is non-empty */
if
(
ac
->
ev
A
ddWrite
)
ac
->
ev
A
ddWrite
(
ac
->
_adapter_
data
);
if
(
ac
->
ev
.
a
ddWrite
)
ac
->
ev
.
a
ddWrite
(
ac
->
ev
.
data
);
return
REDIS_OK
;
}
...
...
deps/hiredis/async.h
View file @
9703b1b3
/*
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
...
...
@@ -36,6 +38,7 @@ extern "C" {
#endif
struct
redisAsyncContext
;
/* need forward declaration of redisAsyncContext */
struct
dict
;
/* dictionary header is included in async.c */
/* Reply callback prototype and container */
typedef
void
(
redisCallbackFn
)(
struct
redisAsyncContext
*
,
void
*
,
void
*
);
...
...
@@ -66,16 +69,18 @@ typedef struct redisAsyncContext {
/* Not used by hiredis */
void
*
data
;
/* Used by the different event lib adapters to store their private data */
void
*
_adapter_data
;
/* Event library data and hooks */
struct
{
void
*
data
;
/* Called when the library expects to start reading/writing.
* The supplied functions should be idempotent. */
void
(
*
evAddRead
)(
void
*
privdata
);
void
(
*
evDelRead
)(
void
*
privdata
);
void
(
*
evAddWrite
)(
void
*
privdata
);
void
(
*
evDelWrite
)(
void
*
privdata
);
void
(
*
evCleanup
)(
void
*
privdata
);
/* Hooks that are called when the library expects to start
* reading/writing. These functions should be idempotent. */
void
(
*
addRead
)(
void
*
privdata
);
void
(
*
delRead
)(
void
*
privdata
);
void
(
*
addWrite
)(
void
*
privdata
);
void
(
*
delWrite
)(
void
*
privdata
);
void
(
*
cleanup
)(
void
*
privdata
);
}
ev
;
/* Called when either the connection is terminated due to an error or per
* user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */
...
...
@@ -84,16 +89,25 @@ typedef struct redisAsyncContext {
/* Called when the first write event was received. */
redisConnectCallback
*
onConnect
;
/* Re
ply
callbacks */
/* Re
gular command
callbacks */
redisCallbackList
replies
;
/* Subscription callbacks */
struct
{
redisCallbackList
invalid
;
struct
dict
*
channels
;
struct
dict
*
patterns
;
}
sub
;
}
redisAsyncContext
;
/* Functions that proxy to hiredis */
redisAsyncContext
*
redisAsyncConnect
(
const
char
*
ip
,
int
port
);
redisAsyncContext
*
redisAsyncConnectUnix
(
const
char
*
path
);
int
redisAsyncSetReplyObjectFunctions
(
redisAsyncContext
*
ac
,
redisReplyObjectFunctions
*
fn
);
int
redisAsyncSetConnectCallback
(
redisAsyncContext
*
ac
,
redisConnectCallback
*
fn
);
int
redisAsyncSetDisconnectCallback
(
redisAsyncContext
*
ac
,
redisDisconnectCallback
*
fn
);
void
redisAsyncDisconnect
(
redisAsyncContext
*
ac
);
void
redisAsyncFree
(
redisAsyncContext
*
ac
);
/* Handle read/write events */
void
redisAsyncHandleRead
(
redisAsyncContext
*
ac
);
...
...
deps/hiredis/dict.c
0 → 100644
View file @
9703b1b3
/* Hash table implementation.
*
* This file implements in memory hash tables with insert/del/replace/find/
* get-random-element operations. Hash tables will auto resize if needed
* tables of power of two in size are used, collisions are handled by
* chaining. See the source code for more information... :)
*
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "fmacros.h"
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#include "dict.h"
/* -------------------------- private prototypes ---------------------------- */
static
int
_dictExpandIfNeeded
(
dict
*
ht
);
static
unsigned
long
_dictNextPower
(
unsigned
long
size
);
static
int
_dictKeyIndex
(
dict
*
ht
,
const
void
*
key
);
static
int
_dictInit
(
dict
*
ht
,
dictType
*
type
,
void
*
privDataPtr
);
/* -------------------------- hash functions -------------------------------- */
/* Generic hash function (a popular one from Bernstein).
* I tested a few and this was the best. */
static
unsigned
int
dictGenHashFunction
(
const
unsigned
char
*
buf
,
int
len
)
{
unsigned
int
hash
=
5381
;
while
(
len
--
)
hash
=
((
hash
<<
5
)
+
hash
)
+
(
*
buf
++
);
/* hash * 33 + c */
return
hash
;
}
/* ----------------------------- API implementation ------------------------- */
/* Reset an hashtable already initialized with ht_init().
* NOTE: This function should only called by ht_destroy(). */
static
void
_dictReset
(
dict
*
ht
)
{
ht
->
table
=
NULL
;
ht
->
size
=
0
;
ht
->
sizemask
=
0
;
ht
->
used
=
0
;
}
/* Create a new hash table */
static
dict
*
dictCreate
(
dictType
*
type
,
void
*
privDataPtr
)
{
dict
*
ht
=
malloc
(
sizeof
(
*
ht
));
_dictInit
(
ht
,
type
,
privDataPtr
);
return
ht
;
}
/* Initialize the hash table */
static
int
_dictInit
(
dict
*
ht
,
dictType
*
type
,
void
*
privDataPtr
)
{
_dictReset
(
ht
);
ht
->
type
=
type
;
ht
->
privdata
=
privDataPtr
;
return
DICT_OK
;
}
/* Expand or create the hashtable */
static
int
dictExpand
(
dict
*
ht
,
unsigned
long
size
)
{
dict
n
;
/* the new hashtable */
unsigned
long
realsize
=
_dictNextPower
(
size
),
i
;
/* the size is invalid if it is smaller than the number of
* elements already inside the hashtable */
if
(
ht
->
used
>
size
)
return
DICT_ERR
;
_dictInit
(
&
n
,
ht
->
type
,
ht
->
privdata
);
n
.
size
=
realsize
;
n
.
sizemask
=
realsize
-
1
;
n
.
table
=
calloc
(
realsize
,
sizeof
(
dictEntry
*
));
/* Copy all the elements from the old to the new table:
* note that if the old hash table is empty ht->size is zero,
* so dictExpand just creates an hash table. */
n
.
used
=
ht
->
used
;
for
(
i
=
0
;
i
<
ht
->
size
&&
ht
->
used
>
0
;
i
++
)
{
dictEntry
*
he
,
*
nextHe
;
if
(
ht
->
table
[
i
]
==
NULL
)
continue
;
/* For each hash entry on this slot... */
he
=
ht
->
table
[
i
];
while
(
he
)
{
unsigned
int
h
;
nextHe
=
he
->
next
;
/* Get the new element index */
h
=
dictHashKey
(
ht
,
he
->
key
)
&
n
.
sizemask
;
he
->
next
=
n
.
table
[
h
];
n
.
table
[
h
]
=
he
;
ht
->
used
--
;
/* Pass to the next element */
he
=
nextHe
;
}
}
assert
(
ht
->
used
==
0
);
free
(
ht
->
table
);
/* Remap the new hashtable in the old */
*
ht
=
n
;
return
DICT_OK
;
}
/* Add an element to the target hash table */
static
int
dictAdd
(
dict
*
ht
,
void
*
key
,
void
*
val
)
{
int
index
;
dictEntry
*
entry
;
/* Get the index of the new element, or -1 if
* the element already exists. */
if
((
index
=
_dictKeyIndex
(
ht
,
key
))
==
-
1
)
return
DICT_ERR
;
/* Allocates the memory and stores key */
entry
=
malloc
(
sizeof
(
*
entry
));
entry
->
next
=
ht
->
table
[
index
];
ht
->
table
[
index
]
=
entry
;
/* Set the hash entry fields. */
dictSetHashKey
(
ht
,
entry
,
key
);
dictSetHashVal
(
ht
,
entry
,
val
);
ht
->
used
++
;
return
DICT_OK
;
}
/* Add an element, discarding the old if the key already exists.
* Return 1 if the key was added from scratch, 0 if there was already an
* element with such key and dictReplace() just performed a value update
* operation. */
static
int
dictReplace
(
dict
*
ht
,
void
*
key
,
void
*
val
)
{
dictEntry
*
entry
,
auxentry
;
/* Try to add the element. If the key
* does not exists dictAdd will suceed. */
if
(
dictAdd
(
ht
,
key
,
val
)
==
DICT_OK
)
return
1
;
/* It already exists, get the entry */
entry
=
dictFind
(
ht
,
key
);
/* Free the old value and set the new one */
/* Set the new value and free the old one. Note that it is important
* to do that in this order, as the value may just be exactly the same
* as the previous one. In this context, think to reference counting,
* you want to increment (set), and then decrement (free), and not the
* reverse. */
auxentry
=
*
entry
;
dictSetHashVal
(
ht
,
entry
,
val
);
dictFreeEntryVal
(
ht
,
&
auxentry
);
return
0
;
}
/* Search and remove an element */
static
int
dictDelete
(
dict
*
ht
,
const
void
*
key
)
{
unsigned
int
h
;
dictEntry
*
de
,
*
prevde
;
if
(
ht
->
size
==
0
)
return
DICT_ERR
;
h
=
dictHashKey
(
ht
,
key
)
&
ht
->
sizemask
;
de
=
ht
->
table
[
h
];
prevde
=
NULL
;
while
(
de
)
{
if
(
dictCompareHashKeys
(
ht
,
key
,
de
->
key
))
{
/* Unlink the element from the list */
if
(
prevde
)
prevde
->
next
=
de
->
next
;
else
ht
->
table
[
h
]
=
de
->
next
;
dictFreeEntryKey
(
ht
,
de
);
dictFreeEntryVal
(
ht
,
de
);
free
(
de
);
ht
->
used
--
;
return
DICT_OK
;
}
prevde
=
de
;
de
=
de
->
next
;
}
return
DICT_ERR
;
/* not found */
}
/* Destroy an entire hash table */
static
int
_dictClear
(
dict
*
ht
)
{
unsigned
long
i
;
/* Free all the elements */
for
(
i
=
0
;
i
<
ht
->
size
&&
ht
->
used
>
0
;
i
++
)
{
dictEntry
*
he
,
*
nextHe
;
if
((
he
=
ht
->
table
[
i
])
==
NULL
)
continue
;
while
(
he
)
{
nextHe
=
he
->
next
;
dictFreeEntryKey
(
ht
,
he
);
dictFreeEntryVal
(
ht
,
he
);
free
(
he
);
ht
->
used
--
;
he
=
nextHe
;
}
}
/* Free the table and the allocated cache structure */
free
(
ht
->
table
);
/* Re-initialize the table */
_dictReset
(
ht
);
return
DICT_OK
;
/* never fails */
}
/* Clear & Release the hash table */
static
void
dictRelease
(
dict
*
ht
)
{
_dictClear
(
ht
);
free
(
ht
);
}
static
dictEntry
*
dictFind
(
dict
*
ht
,
const
void
*
key
)
{
dictEntry
*
he
;
unsigned
int
h
;
if
(
ht
->
size
==
0
)
return
NULL
;
h
=
dictHashKey
(
ht
,
key
)
&
ht
->
sizemask
;
he
=
ht
->
table
[
h
];
while
(
he
)
{
if
(
dictCompareHashKeys
(
ht
,
key
,
he
->
key
))
return
he
;
he
=
he
->
next
;
}
return
NULL
;
}
static
dictIterator
*
dictGetIterator
(
dict
*
ht
)
{
dictIterator
*
iter
=
malloc
(
sizeof
(
*
iter
));
iter
->
ht
=
ht
;
iter
->
index
=
-
1
;
iter
->
entry
=
NULL
;
iter
->
nextEntry
=
NULL
;
return
iter
;
}
static
dictEntry
*
dictNext
(
dictIterator
*
iter
)
{
while
(
1
)
{
if
(
iter
->
entry
==
NULL
)
{
iter
->
index
++
;
if
(
iter
->
index
>=
(
signed
)
iter
->
ht
->
size
)
break
;
iter
->
entry
=
iter
->
ht
->
table
[
iter
->
index
];
}
else
{
iter
->
entry
=
iter
->
nextEntry
;
}
if
(
iter
->
entry
)
{
/* We need to save the 'next' here, the iterator user
* may delete the entry we are returning. */
iter
->
nextEntry
=
iter
->
entry
->
next
;
return
iter
->
entry
;
}
}
return
NULL
;
}
static
void
dictReleaseIterator
(
dictIterator
*
iter
)
{
free
(
iter
);
}
/* ------------------------- private functions ------------------------------ */
/* Expand the hash table if needed */
static
int
_dictExpandIfNeeded
(
dict
*
ht
)
{
/* If the hash table is empty expand it to the intial size,
* if the table is "full" dobule its size. */
if
(
ht
->
size
==
0
)
return
dictExpand
(
ht
,
DICT_HT_INITIAL_SIZE
);
if
(
ht
->
used
==
ht
->
size
)
return
dictExpand
(
ht
,
ht
->
size
*
2
);
return
DICT_OK
;
}
/* Our hash table capability is a power of two */
static
unsigned
long
_dictNextPower
(
unsigned
long
size
)
{
unsigned
long
i
=
DICT_HT_INITIAL_SIZE
;
if
(
size
>=
LONG_MAX
)
return
LONG_MAX
;
while
(
1
)
{
if
(
i
>=
size
)
return
i
;
i
*=
2
;
}
}
/* Returns the index of a free slot that can be populated with
* an hash entry for the given 'key'.
* If the key already exists, -1 is returned. */
static
int
_dictKeyIndex
(
dict
*
ht
,
const
void
*
key
)
{
unsigned
int
h
;
dictEntry
*
he
;
/* Expand the hashtable if needed */
if
(
_dictExpandIfNeeded
(
ht
)
==
DICT_ERR
)
return
-
1
;
/* Compute the key hash value */
h
=
dictHashKey
(
ht
,
key
)
&
ht
->
sizemask
;
/* Search if this slot does not already contain the given key */
he
=
ht
->
table
[
h
];
while
(
he
)
{
if
(
dictCompareHashKeys
(
ht
,
key
,
he
->
key
))
return
-
1
;
he
=
he
->
next
;
}
return
h
;
}
deps/hiredis/dict.h
0 → 100644
View file @
9703b1b3
/* Hash table implementation.
*
* This file implements in memory hash tables with insert/del/replace/find/
* get-random-element operations. Hash tables will auto resize if needed
* tables of power of two in size are used, collisions are handled by
* chaining. See the source code for more information... :)
*
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __DICT_H
#define __DICT_H
#define DICT_OK 0
#define DICT_ERR 1
/* Unused arguments generate annoying warnings... */
#define DICT_NOTUSED(V) ((void) V)
typedef
struct
dictEntry
{
void
*
key
;
void
*
val
;
struct
dictEntry
*
next
;
}
dictEntry
;
typedef
struct
dictType
{
unsigned
int
(
*
hashFunction
)(
const
void
*
key
);
void
*
(
*
keyDup
)(
void
*
privdata
,
const
void
*
key
);
void
*
(
*
valDup
)(
void
*
privdata
,
const
void
*
obj
);
int
(
*
keyCompare
)(
void
*
privdata
,
const
void
*
key1
,
const
void
*
key2
);
void
(
*
keyDestructor
)(
void
*
privdata
,
void
*
key
);
void
(
*
valDestructor
)(
void
*
privdata
,
void
*
obj
);
}
dictType
;
typedef
struct
dict
{
dictEntry
**
table
;
dictType
*
type
;
unsigned
long
size
;
unsigned
long
sizemask
;
unsigned
long
used
;
void
*
privdata
;
}
dict
;
typedef
struct
dictIterator
{
dict
*
ht
;
int
index
;
dictEntry
*
entry
,
*
nextEntry
;
}
dictIterator
;
/* This is the initial size of every hash table */
#define DICT_HT_INITIAL_SIZE 4
/* ------------------------------- Macros ------------------------------------*/
#define dictFreeEntryVal(ht, entry) \
if ((ht)->type->valDestructor) \
(ht)->type->valDestructor((ht)->privdata, (entry)->val)
#define dictSetHashVal(ht, entry, _val_) do { \
if ((ht)->type->valDup) \
entry->val = (ht)->type->valDup((ht)->privdata, _val_); \
else \
entry->val = (_val_); \
} while(0)
#define dictFreeEntryKey(ht, entry) \
if ((ht)->type->keyDestructor) \
(ht)->type->keyDestructor((ht)->privdata, (entry)->key)
#define dictSetHashKey(ht, entry, _key_) do { \
if ((ht)->type->keyDup) \
entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \
else \
entry->key = (_key_); \
} while(0)
#define dictCompareHashKeys(ht, key1, key2) \
(((ht)->type->keyCompare) ? \
(ht)->type->keyCompare((ht)->privdata, key1, key2) : \
(key1) == (key2))
#define dictHashKey(ht, key) (ht)->type->hashFunction(key)
#define dictGetEntryKey(he) ((he)->key)
#define dictGetEntryVal(he) ((he)->val)
#define dictSlots(ht) ((ht)->size)
#define dictSize(ht) ((ht)->used)
/* API */
static
unsigned
int
dictGenHashFunction
(
const
unsigned
char
*
buf
,
int
len
);
static
dict
*
dictCreate
(
dictType
*
type
,
void
*
privDataPtr
);
static
int
dictExpand
(
dict
*
ht
,
unsigned
long
size
);
static
int
dictAdd
(
dict
*
ht
,
void
*
key
,
void
*
val
);
static
int
dictReplace
(
dict
*
ht
,
void
*
key
,
void
*
val
);
static
int
dictDelete
(
dict
*
ht
,
const
void
*
key
);
static
void
dictRelease
(
dict
*
ht
);
static
dictEntry
*
dictFind
(
dict
*
ht
,
const
void
*
key
);
static
dictIterator
*
dictGetIterator
(
dict
*
ht
);
static
dictEntry
*
dictNext
(
dictIterator
*
iter
);
static
void
dictReleaseIterator
(
dictIterator
*
iter
);
#endif
/* __DICT_H */
deps/hiredis/example.c
View file @
9703b1b3
...
...
@@ -9,7 +9,8 @@ int main(void) {
redisContext
*
c
;
redisReply
*
reply
;
c
=
redisConnect
((
char
*
)
"127.0.0.1"
,
6379
);
struct
timeval
timeout
=
{
1
,
500000
};
// 1.5 seconds
c
=
redisConnectWithTimeout
((
char
*
)
"127.0.0.2"
,
6379
,
timeout
);
if
(
c
->
err
)
{
printf
(
"Connection error: %s
\n
"
,
c
->
errstr
);
exit
(
1
);
...
...
deps/hiredis/fmacros.h
View file @
9703b1b3
#ifndef _REDIS_FMACRO_H
#define _REDIS_FMACRO_H
#ifndef _
_HI
REDIS_FMACRO_H
#define _
_HI
REDIS_FMACRO_H
#ifndef _BSD_SOURCE
#define _BSD_SOURCE
#endif
#ifdef __linux__
#define _XOPEN_SOURCE 700
...
...
@@ -9,7 +11,4 @@
#define _XOPEN_SOURCE
#endif
#define _LARGEFILE_SOURCE
#define _FILE_OFFSET_BITS 64
#endif
deps/hiredis/hiredis.c
View file @
9703b1b3
...
...
@@ -271,14 +271,17 @@ static int processLineItem(redisReader *r) {
int
len
;
if
((
p
=
readLine
(
r
,
&
len
))
!=
NULL
)
{
if
(
r
->
fn
)
{
if
(
cu
r
->
type
==
REDIS_REPLY_INTEGER
)
{
if
(
cu
r
->
type
==
REDIS_REPLY_INTEGER
)
{
if
(
r
->
fn
&&
r
->
fn
->
createInteger
)
obj
=
r
->
fn
->
createInteger
(
cur
,
readLongLong
(
p
));
}
else
{
obj
=
r
->
fn
->
createString
(
cur
,
p
,
len
);
}
else
obj
=
(
void
*
)
REDIS_REPLY_INTEGER
;
}
else
{
obj
=
(
void
*
)(
size_t
)(
cur
->
type
);
/* Type will be error or status. */
if
(
r
->
fn
&&
r
->
fn
->
createString
)
obj
=
r
->
fn
->
createString
(
cur
,
p
,
len
);
else
obj
=
(
void
*
)(
size_t
)(
cur
->
type
);
}
/* Set reply if this is the root object. */
...
...
@@ -306,15 +309,19 @@ static int processBulkItem(redisReader *r) {
if
(
len
<
0
)
{
/* The nil object can always be created. */
obj
=
r
->
fn
?
r
->
fn
->
createNil
(
cur
)
:
(
void
*
)
REDIS_REPLY_NIL
;
if
(
r
->
fn
&&
r
->
fn
->
createNil
)
obj
=
r
->
fn
->
createNil
(
cur
);
else
obj
=
(
void
*
)
REDIS_REPLY_NIL
;
success
=
1
;
}
else
{
/* Only continue when the buffer contains the entire bulk item. */
bytelen
+=
len
+
2
;
/* include \r\n */
if
(
r
->
pos
+
bytelen
<=
r
->
len
)
{
obj
=
r
->
fn
?
r
->
fn
->
createString
(
cur
,
s
+
2
,
len
)
:
(
void
*
)
REDIS_REPLY_STRING
;
if
(
r
->
fn
&&
r
->
fn
->
createString
)
obj
=
r
->
fn
->
createString
(
cur
,
s
+
2
,
len
);
else
obj
=
(
void
*
)
REDIS_REPLY_STRING
;
success
=
1
;
}
}
...
...
@@ -351,12 +358,16 @@ static int processMultiBulkItem(redisReader *r) {
root
=
(
r
->
ridx
==
0
);
if
(
elements
==
-
1
)
{
obj
=
r
->
fn
?
r
->
fn
->
createNil
(
cur
)
:
(
void
*
)
REDIS_REPLY_NIL
;
if
(
r
->
fn
&&
r
->
fn
->
createNil
)
obj
=
r
->
fn
->
createNil
(
cur
);
else
obj
=
(
void
*
)
REDIS_REPLY_NIL
;
moveToNextTask
(
r
);
}
else
{
obj
=
r
->
fn
?
r
->
fn
->
createArray
(
cur
,
elements
)
:
(
void
*
)
REDIS_REPLY_ARRAY
;
if
(
r
->
fn
&&
r
->
fn
->
createArray
)
obj
=
r
->
fn
->
createArray
(
cur
,
elements
);
else
obj
=
(
void
*
)
REDIS_REPLY_ARRAY
;
/* Modify task stack when there are more than 0 elements. */
if
(
elements
>
0
)
{
...
...
@@ -434,7 +445,7 @@ static int processItem(redisReader *r) {
}
}
void
*
redisReplyReaderCreate
()
{
void
*
redisReplyReaderCreate
(
void
)
{
redisReader
*
r
=
calloc
(
sizeof
(
redisReader
),
1
);
r
->
error
=
NULL
;
r
->
fn
=
&
defaultFunctions
;
...
...
@@ -493,7 +504,7 @@ static void redisSetReplyReaderError(redisReader *r, sds err) {
if
(
r
->
buf
!=
NULL
)
{
sdsfree
(
r
->
buf
);
r
->
buf
=
sdsempty
();
r
->
pos
=
0
;
r
->
pos
=
r
->
len
=
0
;
}
r
->
ridx
=
-
1
;
r
->
error
=
err
;
...
...
@@ -504,11 +515,18 @@ char *redisReplyReaderGetError(void *reader) {
return
r
->
error
;
}
void
redisReplyReaderFeed
(
void
*
reader
,
char
*
buf
,
size_t
len
)
{
void
redisReplyReaderFeed
(
void
*
reader
,
const
char
*
buf
,
size_t
len
)
{
redisReader
*
r
=
reader
;
/* Copy the provided buffer. */
if
(
buf
!=
NULL
&&
len
>=
1
)
{
/* Destroy internal buffer when it is empty and is quite large. */
if
(
r
->
len
==
0
&&
sdsavail
(
r
->
buf
)
>
16
*
1024
)
{
sdsfree
(
r
->
buf
);
r
->
buf
=
sdsempty
();
r
->
pos
=
0
;
}
r
->
buf
=
sdscatlen
(
r
->
buf
,
buf
,
len
);
r
->
len
=
sdslen
(
r
->
buf
);
}
...
...
@@ -538,15 +556,10 @@ int redisReplyReaderGetReply(void *reader, void **reply) {
if
(
processItem
(
r
)
<
0
)
break
;
/* Discard the consumed part of the buffer. */
if
(
r
->
pos
>
0
)
{
if
(
r
->
pos
==
r
->
len
)
{
/* sdsrange has a quirck on this edge case. */
sdsfree
(
r
->
buf
);
r
->
buf
=
sdsempty
();
}
else
{
r
->
buf
=
sdsrange
(
r
->
buf
,
r
->
pos
,
r
->
len
);
}
/* Discard part of the buffer when we've consumed at least 1k, to avoid
* doing unnecessary calls to memmove() in sds.c. */
if
(
r
->
pos
>=
1024
)
{
r
->
buf
=
sdsrange
(
r
->
buf
,
r
->
pos
,
-
1
);
r
->
pos
=
0
;
r
->
len
=
sdslen
(
r
->
buf
);
}
...
...
@@ -556,13 +569,6 @@ int redisReplyReaderGetReply(void *reader, void **reply) {
void
*
aux
=
r
->
reply
;
r
->
reply
=
NULL
;
/* Destroy the buffer when it is empty and is quite large. */
if
(
r
->
len
==
0
&&
sdsavail
(
r
->
buf
)
>
16
*
1024
)
{
sdsfree
(
r
->
buf
);
r
->
buf
=
sdsempty
();
r
->
pos
=
0
;
}
/* Check if there actually *is* a reply. */
if
(
r
->
error
!=
NULL
)
{
return
REDIS_ERR
;
...
...
@@ -601,7 +607,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
char
*
cmd
=
NULL
;
/* final command */
int
pos
;
/* position in final command */
sds
current
;
/* current argument */
int
interpolat
ed
=
0
;
/*
did we do interpolation on an
argument? */
int
touch
ed
=
0
;
/*
was the current
argument
touched
? */
char
**
argv
=
NULL
;
int
argc
=
0
,
j
;
int
totlen
=
0
;
...
...
@@ -615,13 +621,14 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
while
(
*
c
!=
'\0'
)
{
if
(
*
c
!=
'%'
||
c
[
1
]
==
'\0'
)
{
if
(
*
c
==
' '
)
{
if
(
sdslen
(
current
)
!=
0
)
{
if
(
touched
)
{
addArgument
(
current
,
&
argv
,
&
argc
,
&
totlen
);
current
=
sdsempty
();
interpolat
ed
=
0
;
touch
ed
=
0
;
}
}
else
{
current
=
sdscatlen
(
current
,
c
,
1
);
touched
=
1
;
}
}
else
{
switch
(
c
[
1
])
{
...
...
@@ -630,14 +637,12 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
size
=
strlen
(
arg
);
if
(
size
>
0
)
current
=
sdscatlen
(
current
,
arg
,
size
);
interpolated
=
1
;
break
;
case
'b'
:
arg
=
va_arg
(
ap
,
char
*
);
size
=
va_arg
(
ap
,
size_t
);
if
(
size
>
0
)
current
=
sdscatlen
(
current
,
arg
,
size
);
interpolated
=
1
;
break
;
case
'%'
:
current
=
sdscat
(
current
,
"%"
);
...
...
@@ -683,7 +688,6 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
_format
[
_l
]
=
'\0'
;
va_copy
(
_cpy
,
ap
);
current
=
sdscatvprintf
(
current
,
_format
,
_cpy
);
interpolated
=
1
;
va_end
(
_cpy
);
/* Update current position (note: outer blocks
...
...
@@ -696,13 +700,14 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
va_arg
(
ap
,
void
);
}
}
touched
=
1
;
c
++
;
}
c
++
;
}
/* Add the last argument if needed */
if
(
interpolated
||
sdslen
(
current
)
!=
0
)
{
if
(
touched
)
{
addArgument
(
current
,
&
argv
,
&
argc
,
&
totlen
);
}
else
{
sdsfree
(
current
);
...
...
@@ -798,7 +803,7 @@ void __redisSetError(redisContext *c, int type, const sds errstr) {
}
}
static
redisContext
*
redisContextInit
()
{
static
redisContext
*
redisContextInit
(
void
)
{
redisContext
*
c
=
calloc
(
sizeof
(
redisContext
),
1
);
c
->
err
=
0
;
c
->
errstr
=
NULL
;
...
...
@@ -809,8 +814,7 @@ static redisContext *redisContextInit() {
}
void
redisFree
(
redisContext
*
c
)
{
/* Disconnect before free'ing if not yet disconnected. */
if
(
c
->
flags
&
REDIS_CONNECTED
)
if
(
c
->
fd
>
0
)
close
(
c
->
fd
);
if
(
c
->
errstr
!=
NULL
)
sdsfree
(
c
->
errstr
);
...
...
@@ -827,31 +831,52 @@ void redisFree(redisContext *c) {
redisContext
*
redisConnect
(
const
char
*
ip
,
int
port
)
{
redisContext
*
c
=
redisContextInit
();
c
->
flags
|=
REDIS_BLOCK
;
redisContextConnectTcp
(
c
,
ip
,
port
);
redisContextConnectTcp
(
c
,
ip
,
port
,
NULL
);
return
c
;
}
redisContext
*
redisConnectWithTimeout
(
const
char
*
ip
,
int
port
,
struct
timeval
tv
)
{
redisContext
*
c
=
redisContextInit
();
c
->
flags
|=
REDIS_BLOCK
;
redisContextConnectTcp
(
c
,
ip
,
port
,
&
tv
);
return
c
;
}
redisContext
*
redisConnectNonBlock
(
const
char
*
ip
,
int
port
)
{
redisContext
*
c
=
redisContextInit
();
c
->
flags
&=
~
REDIS_BLOCK
;
redisContextConnectTcp
(
c
,
ip
,
port
);
redisContextConnectTcp
(
c
,
ip
,
port
,
NULL
);
return
c
;
}
redisContext
*
redisConnectUnix
(
const
char
*
path
)
{
redisContext
*
c
=
redisContextInit
();
c
->
flags
|=
REDIS_BLOCK
;
redisContextConnectUnix
(
c
,
path
);
redisContextConnectUnix
(
c
,
path
,
NULL
);
return
c
;
}
redisContext
*
redisConnectUnixWithTimeout
(
const
char
*
path
,
struct
timeval
tv
)
{
redisContext
*
c
=
redisContextInit
();
c
->
flags
|=
REDIS_BLOCK
;
redisContextConnectUnix
(
c
,
path
,
&
tv
);
return
c
;
}
redisContext
*
redisConnectUnixNonBlock
(
const
char
*
path
)
{
redisContext
*
c
=
redisContextInit
();
c
->
flags
&=
~
REDIS_BLOCK
;
redisContextConnectUnix
(
c
,
path
);
redisContextConnectUnix
(
c
,
path
,
NULL
);
return
c
;
}
/* Set read/write timeout on a blocking socket. */
int
redisSetTimeout
(
redisContext
*
c
,
struct
timeval
tv
)
{
if
(
c
->
flags
&
REDIS_BLOCK
)
return
redisContextSetTimeout
(
c
,
tv
);
return
REDIS_ERR
;
}
/* Set the replyObjectFunctions to use. Returns REDIS_ERR when the reader
* was already initialized and the function set could not be re-set.
* Return REDIS_OK when they could be set. */
...
...
@@ -879,7 +904,7 @@ int redisBufferRead(redisContext *c) {
char
buf
[
2048
];
int
nread
=
read
(
c
->
fd
,
buf
,
sizeof
(
buf
));
if
(
nread
==
-
1
)
{
if
(
errno
==
EAGAIN
)
{
if
(
errno
==
EAGAIN
&&
!
(
c
->
flags
&
REDIS_BLOCK
)
)
{
/* Try again later */
}
else
{
__redisSetError
(
c
,
REDIS_ERR_IO
,
NULL
);
...
...
@@ -910,7 +935,7 @@ int redisBufferWrite(redisContext *c, int *done) {
if
(
sdslen
(
c
->
obuf
)
>
0
)
{
nwritten
=
write
(
c
->
fd
,
c
->
obuf
,
sdslen
(
c
->
obuf
));
if
(
nwritten
==
-
1
)
{
if
(
errno
==
EAGAIN
)
{
if
(
errno
==
EAGAIN
&&
!
(
c
->
flags
&
REDIS_BLOCK
)
)
{
/* Try again later */
}
else
{
__redisSetError
(
c
,
REDIS_ERR_IO
,
NULL
);
...
...
deps/hiredis/hiredis.h
View file @
9703b1b3
...
...
@@ -33,6 +33,7 @@
#define __HIREDIS_H
#include <stdio.h>
/* for size_t */
#include <stdarg.h>
/* for va_list */
#include <sys/time.h>
/* for struct timeval */
#define HIREDIS_MAJOR 0
#define HIREDIS_MINOR 9
...
...
@@ -64,6 +65,16 @@
* should be terminated once all replies have been read. */
#define REDIS_DISCONNECTING 0x4
/* Flag specific to the async API which means that the context should be clean
* up as soon as possible. */
#define REDIS_FREEING 0x8
/* Flag that is set when an async callback is executed. */
#define REDIS_IN_CALLBACK 0x10
/* Flag that is set when the async context has one or more subscriptions. */
#define REDIS_SUBSCRIBED 0x20
#define REDIS_REPLY_STRING 1
#define REDIS_REPLY_ARRAY 2
#define REDIS_REPLY_INTEGER 3
...
...
@@ -118,13 +129,13 @@ typedef struct redisContext {
}
redisContext
;
void
freeReplyObject
(
void
*
reply
);
void
*
redisReplyReaderCreate
();
void
*
redisReplyReaderCreate
(
void
);
int
redisReplyReaderSetReplyObjectFunctions
(
void
*
reader
,
redisReplyObjectFunctions
*
fn
);
int
redisReplyReaderSetPrivdata
(
void
*
reader
,
void
*
privdata
);
void
*
redisReplyReaderGetObject
(
void
*
reader
);
char
*
redisReplyReaderGetError
(
void
*
reader
);
void
redisReplyReaderFree
(
void
*
ptr
);
void
redisReplyReaderFeed
(
void
*
reader
,
char
*
buf
,
size_t
len
);
void
redisReplyReaderFeed
(
void
*
reader
,
const
char
*
buf
,
size_t
len
);
int
redisReplyReaderGetReply
(
void
*
reader
,
void
**
reply
);
/* Functions to format a command according to the protocol. */
...
...
@@ -133,9 +144,12 @@ int redisFormatCommand(char **target, const char *format, ...);
int
redisFormatCommandArgv
(
char
**
target
,
int
argc
,
const
char
**
argv
,
const
size_t
*
argvlen
);
redisContext
*
redisConnect
(
const
char
*
ip
,
int
port
);
redisContext
*
redisConnectWithTimeout
(
const
char
*
ip
,
int
port
,
struct
timeval
tv
);
redisContext
*
redisConnectNonBlock
(
const
char
*
ip
,
int
port
);
redisContext
*
redisConnectUnix
(
const
char
*
path
);
redisContext
*
redisConnectUnixWithTimeout
(
const
char
*
path
,
struct
timeval
tv
);
redisContext
*
redisConnectUnixNonBlock
(
const
char
*
path
);
int
redisSetTimeout
(
redisContext
*
c
,
struct
timeval
tv
);
int
redisSetReplyObjectFunctions
(
redisContext
*
c
,
redisReplyObjectFunctions
*
fn
);
void
redisFree
(
redisContext
*
c
);
int
redisBufferRead
(
redisContext
*
c
);
...
...
deps/hiredis/net.c
View file @
9703b1b3
...
...
@@ -4,6 +4,7 @@
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
...
...
@@ -32,6 +33,7 @@
#include "fmacros.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
...
...
@@ -66,7 +68,7 @@ static int redisCreateSocket(redisContext *c, int type) {
return
s
;
}
static
int
redisSet
Non
Block
(
redisContext
*
c
,
int
fd
)
{
static
int
redisSetBlock
ing
(
redisContext
*
c
,
int
fd
,
int
blocking
)
{
int
flags
;
/* Set the socket nonblocking.
...
...
@@ -78,9 +80,15 @@ static int redisSetNonBlock(redisContext *c, int fd) {
close
(
fd
);
return
REDIS_ERR
;
}
if
(
fcntl
(
fd
,
F_SETFL
,
flags
|
O_NONBLOCK
)
==
-
1
)
{
if
(
blocking
)
flags
&=
~
O_NONBLOCK
;
else
flags
|=
O_NONBLOCK
;
if
(
fcntl
(
fd
,
F_SETFL
,
flags
)
==
-
1
)
{
__redisSetError
(
c
,
REDIS_ERR_IO
,
sdscatprintf
(
sdsempty
(),
"fcntl(F_SETFL
,O_NONBLOCK
): %s"
,
strerror
(
errno
)));
sdscatprintf
(
sdsempty
(),
"fcntl(F_SETFL): %s"
,
strerror
(
errno
)));
close
(
fd
);
return
REDIS_ERR
;
}
...
...
@@ -92,19 +100,89 @@ static int redisSetTcpNoDelay(redisContext *c, int fd) {
if
(
setsockopt
(
fd
,
IPPROTO_TCP
,
TCP_NODELAY
,
&
yes
,
sizeof
(
yes
))
==
-
1
)
{
__redisSetError
(
c
,
REDIS_ERR_IO
,
sdscatprintf
(
sdsempty
(),
"setsockopt(TCP_NODELAY): %s"
,
strerror
(
errno
)));
close
(
fd
);
return
REDIS_ERR
;
}
return
REDIS_OK
;
}
int
redisContextConnectTcp
(
redisContext
*
c
,
const
char
*
addr
,
int
port
)
{
static
int
redisContextWaitReady
(
redisContext
*
c
,
int
fd
,
const
struct
timeval
*
timeout
)
{
struct
timeval
to
;
struct
timeval
*
toptr
=
NULL
;
fd_set
wfd
;
int
err
;
socklen_t
errlen
;
/* Only use timeout when not NULL. */
if
(
timeout
!=
NULL
)
{
to
=
*
timeout
;
toptr
=
&
to
;
}
if
(
errno
==
EINPROGRESS
)
{
FD_ZERO
(
&
wfd
);
FD_SET
(
fd
,
&
wfd
);
if
(
select
(
FD_SETSIZE
,
NULL
,
&
wfd
,
NULL
,
toptr
)
==
-
1
)
{
__redisSetError
(
c
,
REDIS_ERR_IO
,
sdscatprintf
(
sdsempty
(),
"select(2): %s"
,
strerror
(
errno
)));
close
(
fd
);
return
REDIS_ERR
;
}
if
(
!
FD_ISSET
(
fd
,
&
wfd
))
{
errno
=
ETIMEDOUT
;
__redisSetError
(
c
,
REDIS_ERR_IO
,
NULL
);
close
(
fd
);
return
REDIS_ERR
;
}
err
=
0
;
errlen
=
sizeof
(
err
);
if
(
getsockopt
(
fd
,
SOL_SOCKET
,
SO_ERROR
,
&
err
,
&
errlen
)
==
-
1
)
{
__redisSetError
(
c
,
REDIS_ERR_IO
,
sdscatprintf
(
sdsempty
(),
"getsockopt(SO_ERROR): %s"
,
strerror
(
errno
)));
close
(
fd
);
return
REDIS_ERR
;
}
if
(
err
)
{
errno
=
err
;
__redisSetError
(
c
,
REDIS_ERR_IO
,
NULL
);
close
(
fd
);
return
REDIS_ERR
;
}
return
REDIS_OK
;
}
__redisSetError
(
c
,
REDIS_ERR_IO
,
NULL
);
close
(
fd
);
return
REDIS_ERR
;
}
int
redisContextSetTimeout
(
redisContext
*
c
,
struct
timeval
tv
)
{
if
(
setsockopt
(
c
->
fd
,
SOL_SOCKET
,
SO_RCVTIMEO
,
&
tv
,
sizeof
(
tv
))
==
-
1
)
{
__redisSetError
(
c
,
REDIS_ERR_IO
,
sdscatprintf
(
sdsempty
(),
"setsockopt(SO_RCVTIMEO): %s"
,
strerror
(
errno
)));
return
REDIS_ERR
;
}
if
(
setsockopt
(
c
->
fd
,
SOL_SOCKET
,
SO_SNDTIMEO
,
&
tv
,
sizeof
(
tv
))
==
-
1
)
{
__redisSetError
(
c
,
REDIS_ERR_IO
,
sdscatprintf
(
sdsempty
(),
"setsockopt(SO_SNDTIMEO): %s"
,
strerror
(
errno
)));
return
REDIS_ERR
;
}
return
REDIS_OK
;
}
int
redisContextConnectTcp
(
redisContext
*
c
,
const
char
*
addr
,
int
port
,
struct
timeval
*
timeout
)
{
int
s
;
int
blocking
=
(
c
->
flags
&
REDIS_BLOCK
);
struct
sockaddr_in
sa
;
if
((
s
=
redisCreateSocket
(
c
,
AF_INET
))
==
REDIS_ERR
)
if
((
s
=
redisCreateSocket
(
c
,
AF_INET
))
<
0
)
return
REDIS_ERR
;
if
(
!
blocking
&&
redisSet
Non
Block
(
c
,
s
)
=
=
REDIS_
ERR
)
if
(
redisSetBlock
ing
(
c
,
s
,
0
)
!
=
REDIS_
OK
)
return
REDIS_ERR
;
sa
.
sin_family
=
AF_INET
;
...
...
@@ -126,30 +204,31 @@ int redisContextConnectTcp(redisContext *c, const char *addr, int port) {
if
(
errno
==
EINPROGRESS
&&
!
blocking
)
{
/* This is ok. */
}
else
{
__redisSetError
(
c
,
REDIS_ERR_IO
,
NULL
);
close
(
s
);
return
REDIS_ERR
;
if
(
redisContextWaitReady
(
c
,
s
,
timeout
)
!=
REDIS_OK
)
return
REDIS_ERR
;
}
}
if
(
redisSetTcpNoDelay
(
c
,
s
)
!=
REDIS_OK
)
{
close
(
s
);
/* Reset socket to be blocking after connect(2). */
if
(
blocking
&&
redisSetBlocking
(
c
,
s
,
1
)
!=
REDIS_OK
)
return
REDIS_ERR
;
if
(
redisSetTcpNoDelay
(
c
,
s
)
!=
REDIS_OK
)
return
REDIS_ERR
;
}
c
->
fd
=
s
;
c
->
flags
|=
REDIS_CONNECTED
;
return
REDIS_OK
;
}
int
redisContextConnectUnix
(
redisContext
*
c
,
const
char
*
path
)
{
int
redisContextConnectUnix
(
redisContext
*
c
,
const
char
*
path
,
struct
timeval
*
timeout
)
{
int
s
;
int
blocking
=
(
c
->
flags
&
REDIS_BLOCK
);
struct
sockaddr_un
sa
;
if
((
s
=
redisCreateSocket
(
c
,
AF_LOCAL
))
==
REDIS_ERR
)
if
((
s
=
redisCreateSocket
(
c
,
AF_LOCAL
))
<
0
)
return
REDIS_ERR
;
if
(
!
blocking
&&
redisSet
Non
Block
(
c
,
s
)
!=
REDIS_OK
)
if
(
redisSetBlock
ing
(
c
,
s
,
0
)
!=
REDIS_OK
)
return
REDIS_ERR
;
sa
.
sun_family
=
AF_LOCAL
;
...
...
@@ -158,12 +237,15 @@ int redisContextConnectUnix(redisContext *c, const char *path) {
if
(
errno
==
EINPROGRESS
&&
!
blocking
)
{
/* This is ok. */
}
else
{
__redisSetError
(
c
,
REDIS_ERR_IO
,
NULL
);
close
(
s
);
return
REDIS_ERR
;
if
(
redisContextWaitReady
(
c
,
s
,
timeout
)
!=
REDIS_OK
)
return
REDIS_ERR
;
}
}
/* Reset socket to be blocking after connect(2). */
if
(
blocking
&&
redisSetBlocking
(
c
,
s
,
1
)
!=
REDIS_OK
)
return
REDIS_ERR
;
c
->
fd
=
s
;
c
->
flags
|=
REDIS_CONNECTED
;
return
REDIS_OK
;
...
...
deps/hiredis/net.h
View file @
9703b1b3
/* Extracted from anet.c to work properly with Hiredis error reporting.
*
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
...
...
@@ -37,7 +39,8 @@
#define AF_LOCAL AF_UNIX
#endif
int
redisContextConnectTcp
(
redisContext
*
c
,
const
char
*
addr
,
int
port
);
int
redisContextConnectUnix
(
redisContext
*
c
,
const
char
*
path
);
int
redisContextSetTimeout
(
redisContext
*
c
,
struct
timeval
tv
);
int
redisContextConnectTcp
(
redisContext
*
c
,
const
char
*
addr
,
int
port
,
struct
timeval
*
timeout
);
int
redisContextConnectUnix
(
redisContext
*
c
,
const
char
*
path
,
struct
timeval
*
timeout
);
#endif
deps/hiredis/sds.c
View file @
9703b1b3
...
...
@@ -30,11 +30,11 @@
#define SDS_ABORT_ON_OOM
#include "sds.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "sds.h"
static
void
sdsOomAbort
(
void
)
{
fprintf
(
stderr
,
"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)
\n
"
);
...
...
@@ -69,11 +69,6 @@ sds sdsnew(const char *init) {
return
sdsnewlen
(
init
,
initlen
);
}
size_t
sdslen
(
const
sds
s
)
{
struct
sdshdr
*
sh
=
(
void
*
)
(
s
-
(
sizeof
(
struct
sdshdr
)));
return
sh
->
len
;
}
sds
sdsdup
(
const
sds
s
)
{
return
sdsnewlen
(
s
,
sdslen
(
s
));
}
...
...
@@ -83,11 +78,6 @@ void sdsfree(sds s) {
free
(
s
-
sizeof
(
struct
sdshdr
));
}
size_t
sdsavail
(
sds
s
)
{
struct
sdshdr
*
sh
=
(
void
*
)
(
s
-
(
sizeof
(
struct
sdshdr
)));
return
sh
->
free
;
}
void
sdsupdatelen
(
sds
s
)
{
struct
sdshdr
*
sh
=
(
void
*
)
(
s
-
(
sizeof
(
struct
sdshdr
)));
int
reallen
=
strlen
(
s
);
...
...
@@ -115,6 +105,25 @@ static sds sdsMakeRoomFor(sds s, size_t addlen) {
return
newsh
->
buf
;
}
/* Grow the sds to have the specified length. Bytes that were not part of
* the original length of the sds will be set to zero. */
sds
sdsgrowzero
(
sds
s
,
size_t
len
)
{
struct
sdshdr
*
sh
=
(
void
*
)(
s
-
(
sizeof
(
struct
sdshdr
)));
size_t
totlen
,
curlen
=
sh
->
len
;
if
(
len
<=
curlen
)
return
s
;
s
=
sdsMakeRoomFor
(
s
,
len
-
curlen
);
if
(
s
==
NULL
)
return
NULL
;
/* Make sure added region doesn't contain garbage */
sh
=
(
void
*
)(
s
-
(
sizeof
(
struct
sdshdr
)));
memset
(
s
+
curlen
,
0
,(
len
-
curlen
+
1
));
/* also set trailing \0 byte */
totlen
=
sh
->
len
+
sh
->
free
;
sh
->
len
=
len
;
sh
->
free
=
totlen
-
sh
->
len
;
return
s
;
}
sds
sdscatlen
(
sds
s
,
const
void
*
t
,
size_t
len
)
{
struct
sdshdr
*
sh
;
size_t
curlen
=
sdslen
(
s
);
...
...
@@ -222,13 +231,16 @@ sds sdsrange(sds s, int start, int end) {
}
newlen
=
(
start
>
end
)
?
0
:
(
end
-
start
)
+
1
;
if
(
newlen
!=
0
)
{
if
(
start
>=
(
signed
)
len
)
start
=
len
-
1
;
if
(
end
>=
(
signed
)
len
)
end
=
len
-
1
;
newlen
=
(
start
>
end
)
?
0
:
(
end
-
start
)
+
1
;
if
(
start
>=
(
signed
)
len
)
{
newlen
=
0
;
}
else
if
(
end
>=
(
signed
)
len
)
{
end
=
len
-
1
;
newlen
=
(
start
>
end
)
?
0
:
(
end
-
start
)
+
1
;
}
}
else
{
start
=
0
;
}
if
(
start
!=
0
)
memmove
(
sh
->
buf
,
sh
->
buf
+
start
,
newlen
);
if
(
start
&&
newlen
)
memmove
(
sh
->
buf
,
sh
->
buf
+
start
,
newlen
);
sh
->
buf
[
newlen
]
=
0
;
sh
->
free
=
sh
->
free
+
(
sh
->
len
-
newlen
);
sh
->
len
=
newlen
;
...
...
@@ -477,3 +489,106 @@ err:
if
(
current
)
sdsfree
(
current
);
return
NULL
;
}
#ifdef SDS_TEST_MAIN
#include <stdio.h>
int
__failed_tests
=
0
;
int
__test_num
=
0
;
#define test_cond(descr,_c) do { \
__test_num++; printf("%d - %s: ", __test_num, descr); \
if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \
} while(0);
#define test_report() do { \
printf("%d tests, %d passed, %d failed\n", __test_num, \
__test_num-__failed_tests, __failed_tests); \
if (__failed_tests) { \
printf("=== WARNING === We have failed tests here...\n"); \
} \
} while(0);
int
main
(
void
)
{
{
sds
x
=
sdsnew
(
"foo"
),
y
;
test_cond
(
"Create a string and obtain the length"
,
sdslen
(
x
)
==
3
&&
memcmp
(
x
,
"foo
\0
"
,
4
)
==
0
)
sdsfree
(
x
);
x
=
sdsnewlen
(
"foo"
,
2
);
test_cond
(
"Create a string with specified length"
,
sdslen
(
x
)
==
2
&&
memcmp
(
x
,
"fo
\0
"
,
3
)
==
0
)
x
=
sdscat
(
x
,
"bar"
);
test_cond
(
"Strings concatenation"
,
sdslen
(
x
)
==
5
&&
memcmp
(
x
,
"fobar
\0
"
,
6
)
==
0
);
x
=
sdscpy
(
x
,
"a"
);
test_cond
(
"sdscpy() against an originally longer string"
,
sdslen
(
x
)
==
1
&&
memcmp
(
x
,
"a
\0
"
,
2
)
==
0
)
x
=
sdscpy
(
x
,
"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"
);
test_cond
(
"sdscpy() against an originally shorter string"
,
sdslen
(
x
)
==
33
&&
memcmp
(
x
,
"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk
\0
"
,
33
)
==
0
)
sdsfree
(
x
);
x
=
sdscatprintf
(
sdsempty
(),
"%d"
,
123
);
test_cond
(
"sdscatprintf() seems working in the base case"
,
sdslen
(
x
)
==
3
&&
memcmp
(
x
,
"123
\0
"
,
4
)
==
0
)
sdsfree
(
x
);
x
=
sdstrim
(
sdsnew
(
"xxciaoyyy"
),
"xy"
);
test_cond
(
"sdstrim() correctly trims characters"
,
sdslen
(
x
)
==
4
&&
memcmp
(
x
,
"ciao
\0
"
,
5
)
==
0
)
y
=
sdsrange
(
sdsdup
(
x
),
1
,
1
);
test_cond
(
"sdsrange(...,1,1)"
,
sdslen
(
y
)
==
1
&&
memcmp
(
y
,
"i
\0
"
,
2
)
==
0
)
sdsfree
(
y
);
y
=
sdsrange
(
sdsdup
(
x
),
1
,
-
1
);
test_cond
(
"sdsrange(...,1,-1)"
,
sdslen
(
y
)
==
3
&&
memcmp
(
y
,
"iao
\0
"
,
4
)
==
0
)
sdsfree
(
y
);
y
=
sdsrange
(
sdsdup
(
x
),
-
2
,
-
1
);
test_cond
(
"sdsrange(...,-2,-1)"
,
sdslen
(
y
)
==
2
&&
memcmp
(
y
,
"ao
\0
"
,
3
)
==
0
)
sdsfree
(
y
);
y
=
sdsrange
(
sdsdup
(
x
),
2
,
1
);
test_cond
(
"sdsrange(...,2,1)"
,
sdslen
(
y
)
==
0
&&
memcmp
(
y
,
"
\0
"
,
1
)
==
0
)
sdsfree
(
y
);
y
=
sdsrange
(
sdsdup
(
x
),
1
,
100
);
test_cond
(
"sdsrange(...,1,100)"
,
sdslen
(
y
)
==
3
&&
memcmp
(
y
,
"iao
\0
"
,
4
)
==
0
)
sdsfree
(
y
);
y
=
sdsrange
(
sdsdup
(
x
),
100
,
100
);
test_cond
(
"sdsrange(...,100,100)"
,
sdslen
(
y
)
==
0
&&
memcmp
(
y
,
"
\0
"
,
1
)
==
0
)
sdsfree
(
y
);
sdsfree
(
x
);
x
=
sdsnew
(
"foo"
);
y
=
sdsnew
(
"foa"
);
test_cond
(
"sdscmp(foo,foa)"
,
sdscmp
(
x
,
y
)
>
0
)
sdsfree
(
y
);
sdsfree
(
x
);
x
=
sdsnew
(
"bar"
);
y
=
sdsnew
(
"bar"
);
test_cond
(
"sdscmp(bar,bar)"
,
sdscmp
(
x
,
y
)
==
0
)
sdsfree
(
y
);
sdsfree
(
x
);
x
=
sdsnew
(
"aar"
);
y
=
sdsnew
(
"bar"
);
test_cond
(
"sdscmp(bar,bar)"
,
sdscmp
(
x
,
y
)
<
0
)
}
test_report
()
}
#endif
deps/hiredis/sds.h
View file @
9703b1b3
...
...
@@ -42,13 +42,24 @@ struct sdshdr {
char
buf
[];
};
static
inline
size_t
sdslen
(
const
sds
s
)
{
struct
sdshdr
*
sh
=
(
void
*
)(
s
-
(
sizeof
(
struct
sdshdr
)));
return
sh
->
len
;
}
static
inline
size_t
sdsavail
(
const
sds
s
)
{
struct
sdshdr
*
sh
=
(
void
*
)(
s
-
(
sizeof
(
struct
sdshdr
)));
return
sh
->
free
;
}
sds
sdsnewlen
(
const
void
*
init
,
size_t
initlen
);
sds
sdsnew
(
const
char
*
init
);
sds
sdsempty
();
sds
sdsempty
(
void
);
size_t
sdslen
(
const
sds
s
);
sds
sdsdup
(
const
sds
s
);
void
sdsfree
(
sds
s
);
size_t
sdsavail
(
sds
s
);
sds
sdsgrowzero
(
sds
s
,
size_t
len
);
sds
sdscatlen
(
sds
s
,
const
void
*
t
,
size_t
len
);
sds
sdscat
(
sds
s
,
const
char
*
t
);
sds
sdscpylen
(
sds
s
,
char
*
t
,
size_t
len
);
...
...
deps/hiredis/test.c
View file @
9703b1b3
...
...
@@ -6,6 +6,7 @@
#include <assert.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include "hiredis.h"
...
...
@@ -31,7 +32,7 @@ static void __connect(redisContext **target) {
}
}
static
void
test_format_commands
()
{
static
void
test_format_commands
(
void
)
{
char
*
cmd
;
int
len
;
...
...
@@ -53,6 +54,12 @@ static void test_format_commands() {
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
)
+
4
+
(
0
+
2
));
free
(
cmd
);
test
(
"Format command with an empty string in between proper interpolations: "
);
len
=
redisFormatCommand
(
&
cmd
,
"SET %s %s"
,
""
,
"foo"
);
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$0
\r\n\r\n
$3
\r\n
foo
\r\n
"
,
len
)
==
0
&&
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
0
+
2
)
+
4
+
(
3
+
2
));
free
(
cmd
);
test
(
"Format command with %%b string interpolation: "
);
len
=
redisFormatCommand
(
&
cmd
,
"SET %b %b"
,
"foo"
,
3
,
"b
\0
r"
,
3
);
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$3
\r\n
foo
\r\n
$3
\r\n
b
\0
r
\r\n
"
,
len
)
==
0
&&
...
...
@@ -115,7 +122,7 @@ static void test_format_commands() {
free
(
cmd
);
}
static
void
test_blocking_connection
()
{
static
void
test_blocking_connection
(
void
)
{
redisContext
*
c
;
redisReply
*
reply
;
int
major
,
minor
;
...
...
@@ -246,13 +253,21 @@ static void test_blocking_connection() {
* conditions, the error will be set to EOF. */
assert
(
c
->
err
==
REDIS_ERR_EOF
&&
strcmp
(
c
->
errstr
,
"Server closed the connection"
)
==
0
);
redisFree
(
c
);
/* Clean up context and reconnect again */
__connect
(
&
c
);
test
(
"Returns I/O error on socket timeout: "
);
struct
timeval
tv
=
{
0
,
1000
};
assert
(
redisSetTimeout
(
c
,
tv
)
==
REDIS_OK
);
test_cond
(
redisGetReply
(
c
,(
void
**
)
&
reply
)
==
REDIS_ERR
&&
c
->
err
==
REDIS_ERR_IO
&&
errno
==
EAGAIN
);
redisFree
(
c
);
/* Context should be connected */
__connect
(
&
c
);
}
static
void
test_reply_reader
()
{
static
void
test_reply_reader
(
void
)
{
void
*
reader
;
void
*
reply
;
char
*
err
;
...
...
@@ -309,10 +324,19 @@ static void test_reply_reader() {
ret
=
redisReplyReaderGetReply
(
reader
,
&
reply
);
test_cond
(
ret
==
REDIS_OK
&&
reply
==
(
void
*
)
REDIS_REPLY_STATUS
);
redisReplyReaderFree
(
reader
);
test
(
"Properly reset state after protocol error: "
);
reader
=
redisReplyReaderCreate
();
redisReplyReaderSetReplyObjectFunctions
(
reader
,
NULL
);
redisReplyReaderFeed
(
reader
,(
char
*
)
"x"
,
1
);
ret
=
redisReplyReaderGetReply
(
reader
,
&
reply
);
assert
(
ret
==
REDIS_ERR
);
ret
=
redisReplyReaderGetReply
(
reader
,
&
reply
);
test_cond
(
ret
==
REDIS_OK
&&
reply
==
NULL
)
}
static
void
test_throughput
()
{
int
i
;
static
void
test_throughput
(
void
)
{
int
i
,
num
;
long
long
t1
,
t2
;
redisContext
*
c
=
blocking_context
;
redisReply
**
replies
;
...
...
@@ -321,31 +345,60 @@ static void test_throughput() {
for
(
i
=
0
;
i
<
500
;
i
++
)
freeReplyObject
(
redisCommand
(
c
,
"LPUSH mylist foo"
));
replies
=
malloc
(
sizeof
(
redisReply
*
)
*
1000
);
num
=
1000
;
replies
=
malloc
(
sizeof
(
redisReply
*
)
*
num
);
t1
=
usec
();
for
(
i
=
0
;
i
<
1000
;
i
++
)
{
for
(
i
=
0
;
i
<
num
;
i
++
)
{
replies
[
i
]
=
redisCommand
(
c
,
"PING"
);
assert
(
replies
[
i
]
!=
NULL
&&
replies
[
i
]
->
type
==
REDIS_REPLY_STATUS
);
}
t2
=
usec
();
for
(
i
=
0
;
i
<
1000
;
i
++
)
freeReplyObject
(
replies
[
i
]);
for
(
i
=
0
;
i
<
num
;
i
++
)
freeReplyObject
(
replies
[
i
]);
free
(
replies
);
printf
(
"
\t
(
1000
x PING: %.
2
fs)
\n
"
,
(
t2
-
t1
)
/
1000000
.
0
);
printf
(
"
\t
(
%d
x PING: %.
3
fs)
\n
"
,
num
,
(
t2
-
t1
)
/
1000000
.
0
);
replies
=
malloc
(
sizeof
(
redisReply
*
)
*
1000
);
replies
=
malloc
(
sizeof
(
redisReply
*
)
*
num
);
t1
=
usec
();
for
(
i
=
0
;
i
<
1000
;
i
++
)
{
for
(
i
=
0
;
i
<
num
;
i
++
)
{
replies
[
i
]
=
redisCommand
(
c
,
"LRANGE mylist 0 499"
);
assert
(
replies
[
i
]
!=
NULL
&&
replies
[
i
]
->
type
==
REDIS_REPLY_ARRAY
);
assert
(
replies
[
i
]
!=
NULL
&&
replies
[
i
]
->
elements
==
500
);
}
t2
=
usec
();
for
(
i
=
0
;
i
<
1000
;
i
++
)
freeReplyObject
(
replies
[
i
]);
for
(
i
=
0
;
i
<
num
;
i
++
)
freeReplyObject
(
replies
[
i
]);
free
(
replies
);
printf
(
"
\t
(%dx LRANGE with 500 elements: %.3fs)
\n
"
,
num
,
(
t2
-
t1
)
/
1000000
.
0
);
num
=
10000
;
replies
=
malloc
(
sizeof
(
redisReply
*
)
*
num
);
for
(
i
=
0
;
i
<
num
;
i
++
)
redisAppendCommand
(
c
,
"PING"
);
t1
=
usec
();
for
(
i
=
0
;
i
<
num
;
i
++
)
{
assert
(
redisGetReply
(
c
,
(
void
*
)
&
replies
[
i
])
==
REDIS_OK
);
assert
(
replies
[
i
]
!=
NULL
&&
replies
[
i
]
->
type
==
REDIS_REPLY_STATUS
);
}
t2
=
usec
();
for
(
i
=
0
;
i
<
num
;
i
++
)
freeReplyObject
(
replies
[
i
]);
free
(
replies
);
printf
(
"
\t
(%dx PING (pipelined): %.3fs)
\n
"
,
num
,
(
t2
-
t1
)
/
1000000
.
0
);
replies
=
malloc
(
sizeof
(
redisReply
*
)
*
num
);
for
(
i
=
0
;
i
<
num
;
i
++
)
redisAppendCommand
(
c
,
"LRANGE mylist 0 499"
);
t1
=
usec
();
for
(
i
=
0
;
i
<
num
;
i
++
)
{
assert
(
redisGetReply
(
c
,
(
void
*
)
&
replies
[
i
])
==
REDIS_OK
);
assert
(
replies
[
i
]
!=
NULL
&&
replies
[
i
]
->
type
==
REDIS_REPLY_ARRAY
);
assert
(
replies
[
i
]
!=
NULL
&&
replies
[
i
]
->
elements
==
500
);
}
t2
=
usec
();
for
(
i
=
0
;
i
<
num
;
i
++
)
freeReplyObject
(
replies
[
i
]);
free
(
replies
);
printf
(
"
\t
(
1000
x LRANGE with 500 elements: %.
2
fs)
\n
"
,
(
t2
-
t1
)
/
1000000
.
0
);
printf
(
"
\t
(
%d
x LRANGE with 500 elements
(pipelined)
: %.
3
fs)
\n
"
,
num
,
(
t2
-
t1
)
/
1000000
.
0
);
}
static
void
cleanup
()
{
static
void
cleanup
(
void
)
{
redisContext
*
c
=
blocking_context
;
redisReply
*
reply
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment