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
hiredis
Commits
fa8bcca1
Commit
fa8bcca1
authored
Apr 09, 2014
by
Pieter Noordhuis
Browse files
Merge pull request #234 from mattsta/next-version
Next version of hiredis
parents
065e9055
4369ee5d
Changes
15
Show whitespace changes
Inline
Side-by-side
Makefile
View file @
fa8bcca1
...
...
@@ -11,6 +11,18 @@ LIBNAME=libhiredis
HIREDIS_MAJOR
=
0
HIREDIS_MINOR
=
10
# redis-server configuration used for testing
REDIS_PORT
=
56379
REDIS_SERVER
=
redis-server
define
REDIS_TEST_CONFIG
daemonize
yes
pidfile
/tmp/hiredis-test-redis.pid
port
$(REDIS_PORT)
bind
127.0.0.1
unixsocket
/tmp/hiredis-test-redis.sock
endef
export
REDIS_TEST_CONFIG
# Fallback to gcc when $CC is not in $PATH.
CC
:=
$(
shell
sh
-c
'type
$(CC)
>/dev/null 2>/dev/null && echo
$(CC)
|| echo gcc'
)
OPTIMIZATION
?=
-O3
...
...
@@ -97,14 +109,8 @@ test: hiredis-test
./hiredis-test
check
:
hiredis-test
echo
\
"daemonize yes
\n
"
\
"pidfile /tmp/hiredis-test-redis.pid
\n
"
\
"port 56379
\n
"
\
"bind 127.0.0.1
\n
"
\
"unixsocket /tmp/hiredis-test-redis.sock"
\
| redis-server -
./hiredis-test
-h
127.0.0.1
-p
56379
-s
/tmp/hiredis-test-redis.sock
||
\
@
echo
"
$$
REDIS_TEST_CONFIG"
|
$(REDIS_SERVER)
-
./hiredis-test
-h
127.0.0.1
-p
$(REDIS_PORT)
-s
/tmp/hiredis-test-redis.sock
||
\
(
kill
`
cat
/tmp/hiredis-test-redis.pid
`
&&
false
)
kill
`
cat
/tmp/hiredis-test-redis.pid
`
...
...
adapters/ae.h
View file @
fa8bcca1
/*
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * 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 __HIREDIS_AE_H__
#define __HIREDIS_AE_H__
#include <sys/types.h>
...
...
adapters/libev.h
View file @
fa8bcca1
/*
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * 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 __HIREDIS_LIBEV_H__
#define __HIREDIS_LIBEV_H__
#include <stdlib.h>
...
...
adapters/libevent.h
View file @
fa8bcca1
/*
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * 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 __HIREDIS_LIBEVENT_H__
#define __HIREDIS_LIBEVENT_H__
#include <event.h>
...
...
adapters/libuv.h
View file @
fa8bcca1
...
...
@@ -3,7 +3,7 @@
#include <uv.h>
#include "../hiredis.h"
#include "../async.h"
#include <string.h>
typedef
struct
redisLibuvEvents
{
redisAsyncContext
*
context
;
...
...
@@ -11,6 +11,7 @@ typedef struct redisLibuvEvents {
int
events
;
}
redisLibuvEvents
;
int
redisLibuvAttach
(
redisAsyncContext
*
,
uv_loop_t
*
);
static
void
redisLibuvPoll
(
uv_poll_t
*
handle
,
int
status
,
int
events
)
{
redisLibuvEvents
*
p
=
(
redisLibuvEvents
*
)
handle
->
data
;
...
...
@@ -86,7 +87,7 @@ static void redisLibuvCleanup(void *privdata) {
}
int
redisLibuvAttach
(
redisAsyncContext
*
ac
,
uv_loop_t
*
loop
)
{
static
int
redisLibuvAttach
(
redisAsyncContext
*
ac
,
uv_loop_t
*
loop
)
{
redisContext
*
c
=
&
(
ac
->
c
);
if
(
ac
->
ev
.
data
!=
NULL
)
{
...
...
@@ -99,7 +100,7 @@ int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) {
ac
->
ev
.
delWrite
=
redisLibuvDelWrite
;
ac
->
ev
.
cleanup
=
redisLibuvCleanup
;
redisLibuvEvents
*
p
=
malloc
(
sizeof
(
*
p
));
redisLibuvEvents
*
p
=
(
redisLibuvEvents
*
)
malloc
(
sizeof
(
*
p
));
if
(
!
p
)
{
return
REDIS_ERR
;
...
...
async.c
View file @
fa8bcca1
...
...
@@ -165,6 +165,14 @@ redisAsyncContext *redisAsyncConnect(const char *ip, int port) {
return
ac
;
}
redisAsyncContext
*
redisAsyncConnectBind
(
const
char
*
ip
,
int
port
,
char
*
source_addr
)
{
redisContext
*
c
=
redisConnectBindNonBlock
(
ip
,
port
,
source_addr
);
redisAsyncContext
*
ac
=
redisAsyncInitialize
(
c
);
__redisAsyncCopyError
(
ac
);
return
ac
;
}
redisAsyncContext
*
redisAsyncConnectUnix
(
const
char
*
path
)
{
redisContext
*
c
;
redisAsyncContext
*
ac
;
...
...
@@ -391,7 +399,7 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
void
redisProcessCallbacks
(
redisAsyncContext
*
ac
)
{
redisContext
*
c
=
&
(
ac
->
c
);
redisCallback
cb
;
redisCallback
cb
=
{
NULL
,
NULL
,
NULL
}
;
void
*
reply
=
NULL
;
int
status
;
...
...
@@ -473,7 +481,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
static
int
__redisAsyncHandleConnect
(
redisAsyncContext
*
ac
)
{
redisContext
*
c
=
&
(
ac
->
c
);
if
(
redisCheckSocketError
(
c
,
c
->
fd
)
==
REDIS_ERR
)
{
if
(
redisCheckSocketError
(
c
)
==
REDIS_ERR
)
{
/* Try again later when connect(2) is still in progress. */
if
(
errno
==
EINPROGRESS
)
return
REDIS_OK
;
...
...
async.h
View file @
fa8bcca1
...
...
@@ -102,6 +102,7 @@ typedef struct redisAsyncContext {
/* Functions that proxy to hiredis */
redisAsyncContext
*
redisAsyncConnect
(
const
char
*
ip
,
int
port
);
redisAsyncContext
*
redisAsyncConnectBind
(
const
char
*
ip
,
int
port
,
char
*
source
);
redisAsyncContext
*
redisAsyncConnectUnix
(
const
char
*
path
);
int
redisAsyncSetConnectCallback
(
redisAsyncContext
*
ac
,
redisConnectCallback
*
fn
);
int
redisAsyncSetDisconnectCallback
(
redisAsyncContext
*
ac
,
redisDisconnectCallback
*
fn
);
...
...
fmacros.h
View file @
fa8bcca1
...
...
@@ -7,7 +7,7 @@
#if defined(__sun__)
#define _POSIX_C_SOURCE 200112L
#elif defined(__linux__)
#elif defined(__linux__)
|| defined(__OpenBSD__) || defined(__NetBSD__)
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE
...
...
hiredis.c
View file @
fa8bcca1
...
...
@@ -650,7 +650,7 @@ int redisReaderGetReply(redisReader *r, void **reply) {
/* 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
);
sdsrange
(
r
->
buf
,
r
->
pos
,
-
1
);
r
->
pos
=
0
;
r
->
len
=
sdslen
(
r
->
buf
);
}
...
...
@@ -1010,6 +1010,13 @@ void redisFree(redisContext *c) {
free
(
c
);
}
int
redisFreeKeepFd
(
redisContext
*
c
)
{
int
fd
=
c
->
fd
;
c
->
fd
=
-
1
;
redisFree
(
c
);
return
fd
;
}
/* Connect to a Redis instance. On error the field error in the returned
* context will be set to the return value of the error function.
* When no set of reply functions is given, the default set will be used. */
...
...
@@ -1049,6 +1056,14 @@ redisContext *redisConnectNonBlock(const char *ip, int port) {
return
c
;
}
redisContext
*
redisConnectBindNonBlock
(
const
char
*
ip
,
int
port
,
char
*
source_addr
)
{
redisContext
*
c
=
redisContextInit
();
c
->
flags
&=
~
REDIS_BLOCK
;
redisContextConnectBindTcp
(
c
,
ip
,
port
,
NULL
,
source_addr
);
return
c
;
}
redisContext
*
redisConnectUnix
(
const
char
*
path
)
{
redisContext
*
c
;
...
...
@@ -1085,6 +1100,18 @@ redisContext *redisConnectUnixNonBlock(const char *path) {
return
c
;
}
redisContext
*
redisConnectFd
(
int
fd
)
{
redisContext
*
c
;
c
=
redisContextInit
();
if
(
c
==
NULL
)
return
NULL
;
c
->
fd
=
fd
;
c
->
flags
|=
REDIS_BLOCK
|
REDIS_CONNECTED
;
return
c
;
}
/* Set read/write timeout on a blocking socket. */
int
redisSetTimeout
(
redisContext
*
c
,
const
struct
timeval
tv
)
{
if
(
c
->
flags
&
REDIS_BLOCK
)
...
...
@@ -1162,7 +1189,7 @@ int redisBufferWrite(redisContext *c, int *done) {
sdsfree
(
c
->
obuf
);
c
->
obuf
=
sdsempty
();
}
else
{
c
->
obuf
=
sdsrange
(
c
->
obuf
,
nwritten
,
-
1
);
sdsrange
(
c
->
obuf
,
nwritten
,
-
1
);
}
}
}
...
...
@@ -1230,6 +1257,15 @@ int __redisAppendCommand(redisContext *c, char *cmd, size_t len) {
return
REDIS_OK
;
}
int
redisAppendFormattedCommand
(
redisContext
*
c
,
char
*
format
,
size_t
len
)
{
if
(
__redisAppendCommand
(
c
,
format
,
len
)
!=
REDIS_OK
)
{
return
REDIS_ERR
;
}
return
REDIS_OK
;
}
int
redisvAppendCommand
(
redisContext
*
c
,
const
char
*
format
,
va_list
ap
)
{
char
*
cmd
;
int
len
;
...
...
hiredis.h
View file @
fa8bcca1
...
...
@@ -175,12 +175,15 @@ typedef struct redisContext {
redisContext
*
redisConnect
(
const
char
*
ip
,
int
port
);
redisContext
*
redisConnectWithTimeout
(
const
char
*
ip
,
int
port
,
const
struct
timeval
tv
);
redisContext
*
redisConnectNonBlock
(
const
char
*
ip
,
int
port
);
redisContext
*
redisConnectBindNonBlock
(
const
char
*
ip
,
int
port
,
char
*
source
);
redisContext
*
redisConnectUnix
(
const
char
*
path
);
redisContext
*
redisConnectUnixWithTimeout
(
const
char
*
path
,
const
struct
timeval
tv
);
redisContext
*
redisConnectUnixNonBlock
(
const
char
*
path
);
redisContext
*
redisConnectFd
(
int
fd
);
int
redisSetTimeout
(
redisContext
*
c
,
const
struct
timeval
tv
);
int
redisEnableKeepAlive
(
redisContext
*
c
);
void
redisFree
(
redisContext
*
c
);
int
redisFreeKeepFd
(
redisContext
*
c
);
int
redisBufferRead
(
redisContext
*
c
);
int
redisBufferWrite
(
redisContext
*
c
,
int
*
done
);
...
...
@@ -191,6 +194,10 @@ int redisBufferWrite(redisContext *c, int *done);
int
redisGetReply
(
redisContext
*
c
,
void
**
reply
);
int
redisGetReplyFromReader
(
redisContext
*
c
,
void
**
reply
);
/* Write a formatted command to the output buffer. Use these functions in blocking mode
* to get a pipeline of commands. */
int
redisAppendFormattedCommand
(
redisContext
*
c
,
char
*
format
,
size_t
len
);
/* Write a command to the output buffer. Use these functions in blocking mode
* to get a pipeline of commands. */
int
redisvAppendCommand
(
redisContext
*
c
,
const
char
*
format
,
va_list
ap
);
...
...
net.c
View file @
fa8bcca1
...
...
@@ -54,6 +54,13 @@
/* Defined in hiredis.c */
void
__redisSetError
(
redisContext
*
c
,
int
type
,
const
char
*
str
);
static
void
redisContextCloseFd
(
redisContext
*
c
)
{
if
(
c
&&
c
->
fd
>
0
)
{
close
(
c
->
fd
);
c
->
fd
=
-
1
;
}
}
static
void
__redisSetErrorFromErrno
(
redisContext
*
c
,
int
type
,
const
char
*
prefix
)
{
char
buf
[
128
]
=
{
0
};
size_t
len
=
0
;
...
...
@@ -64,11 +71,11 @@ static void __redisSetErrorFromErrno(redisContext *c, int type, const char *pref
__redisSetError
(
c
,
type
,
buf
);
}
static
int
redisSetReuseAddr
(
redisContext
*
c
,
int
fd
)
{
static
int
redisSetReuseAddr
(
redisContext
*
c
)
{
int
on
=
1
;
if
(
setsockopt
(
fd
,
SOL_SOCKET
,
SO_REUSEADDR
,
&
on
,
sizeof
(
on
))
==
-
1
)
{
if
(
setsockopt
(
c
->
fd
,
SOL_SOCKET
,
SO_REUSEADDR
,
&
on
,
sizeof
(
on
))
==
-
1
)
{
__redisSetErrorFromErrno
(
c
,
REDIS_ERR_IO
,
NULL
);
close
(
fd
);
redisContextCloseFd
(
c
);
return
REDIS_ERR
;
}
return
REDIS_OK
;
...
...
@@ -80,23 +87,24 @@ static int redisCreateSocket(redisContext *c, int type) {
__redisSetErrorFromErrno
(
c
,
REDIS_ERR_IO
,
NULL
);
return
REDIS_ERR
;
}
c
->
fd
=
s
;
if
(
type
==
AF_INET
)
{
if
(
redisSetReuseAddr
(
c
,
s
)
==
REDIS_ERR
)
{
if
(
redisSetReuseAddr
(
c
)
==
REDIS_ERR
)
{
return
REDIS_ERR
;
}
}
return
s
;
return
REDIS_OK
;
}
static
int
redisSetBlocking
(
redisContext
*
c
,
int
fd
,
int
blocking
)
{
static
int
redisSetBlocking
(
redisContext
*
c
,
int
blocking
)
{
int
flags
;
/* Set the socket nonblocking.
* Note that fcntl(2) for F_GETFL and F_SETFL can't be
* interrupted by a signal. */
if
((
flags
=
fcntl
(
fd
,
F_GETFL
))
==
-
1
)
{
if
((
flags
=
fcntl
(
c
->
fd
,
F_GETFL
))
==
-
1
)
{
__redisSetErrorFromErrno
(
c
,
REDIS_ERR_IO
,
"fcntl(F_GETFL)"
);
close
(
fd
);
redisContextCloseFd
(
c
);
return
REDIS_ERR
;
}
...
...
@@ -105,9 +113,9 @@ static int redisSetBlocking(redisContext *c, int fd, int blocking) {
else
flags
|=
O_NONBLOCK
;
if
(
fcntl
(
fd
,
F_SETFL
,
flags
)
==
-
1
)
{
if
(
fcntl
(
c
->
fd
,
F_SETFL
,
flags
)
==
-
1
)
{
__redisSetErrorFromErrno
(
c
,
REDIS_ERR_IO
,
"fcntl(F_SETFL)"
);
close
(
fd
);
redisContextCloseFd
(
c
);
return
REDIS_ERR
;
}
return
REDIS_OK
;
...
...
@@ -122,13 +130,15 @@ int redisKeepAlive(redisContext *c, int interval) {
return
REDIS_ERR
;
}
#ifdef _OSX
val
=
interval
;
#ifdef _OSX
if
(
setsockopt
(
fd
,
IPPROTO_TCP
,
TCP_KEEPALIVE
,
&
val
,
sizeof
(
val
))
<
0
)
{
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
strerror
(
errno
));
return
REDIS_ERR
;
}
#else
#ifndef __sun
val
=
interval
;
if
(
setsockopt
(
fd
,
IPPROTO_TCP
,
TCP_KEEPIDLE
,
&
val
,
sizeof
(
val
))
<
0
)
{
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
strerror
(
errno
));
...
...
@@ -147,16 +157,17 @@ int redisKeepAlive(redisContext *c, int interval) {
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
strerror
(
errno
));
return
REDIS_ERR
;
}
#endif
#endif
return
REDIS_OK
;
}
static
int
redisSetTcpNoDelay
(
redisContext
*
c
,
int
fd
)
{
static
int
redisSetTcpNoDelay
(
redisContext
*
c
)
{
int
yes
=
1
;
if
(
setsockopt
(
fd
,
IPPROTO_TCP
,
TCP_NODELAY
,
&
yes
,
sizeof
(
yes
))
==
-
1
)
{
if
(
setsockopt
(
c
->
fd
,
IPPROTO_TCP
,
TCP_NODELAY
,
&
yes
,
sizeof
(
yes
))
==
-
1
)
{
__redisSetErrorFromErrno
(
c
,
REDIS_ERR_IO
,
"setsockopt(TCP_NODELAY)"
);
close
(
fd
);
redisContextCloseFd
(
c
);
return
REDIS_ERR
;
}
return
REDIS_OK
;
...
...
@@ -164,19 +175,19 @@ static int redisSetTcpNoDelay(redisContext *c, int fd) {
#define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
static
int
redisContextWaitReady
(
redisContext
*
c
,
int
fd
,
const
struct
timeval
*
timeout
)
{
static
int
redisContextWaitReady
(
redisContext
*
c
,
const
struct
timeval
*
timeout
)
{
struct
pollfd
wfd
[
1
];
long
msec
;
msec
=
-
1
;
wfd
[
0
].
fd
=
fd
;
wfd
[
0
].
fd
=
c
->
fd
;
wfd
[
0
].
events
=
POLLOUT
;
/* Only use timeout when not NULL. */
if
(
timeout
!=
NULL
)
{
if
(
timeout
->
tv_usec
>
1000000
||
timeout
->
tv_sec
>
__MAX_MSEC
)
{
__redisSetErrorFromErrno
(
c
,
REDIS_ERR_IO
,
NULL
);
close
(
fd
);
redisContextCloseFd
(
c
);
return
REDIS_ERR
;
}
...
...
@@ -192,40 +203,38 @@ static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *
if
((
res
=
poll
(
wfd
,
1
,
msec
))
==
-
1
)
{
__redisSetErrorFromErrno
(
c
,
REDIS_ERR_IO
,
"poll(2)"
);
close
(
fd
);
redisContextCloseFd
(
c
);
return
REDIS_ERR
;
}
else
if
(
res
==
0
)
{
errno
=
ETIMEDOUT
;
__redisSetErrorFromErrno
(
c
,
REDIS_ERR_IO
,
NULL
);
close
(
fd
);
redisContextCloseFd
(
c
);
return
REDIS_ERR
;
}
if
(
redisCheckSocketError
(
c
,
fd
)
!=
REDIS_OK
)
if
(
redisCheckSocketError
(
c
)
!=
REDIS_OK
)
return
REDIS_ERR
;
return
REDIS_OK
;
}
__redisSetErrorFromErrno
(
c
,
REDIS_ERR_IO
,
NULL
);
close
(
fd
);
redisContextCloseFd
(
c
);
return
REDIS_ERR
;
}
int
redisCheckSocketError
(
redisContext
*
c
,
int
fd
)
{
int
redisCheckSocketError
(
redisContext
*
c
)
{
int
err
=
0
;
socklen_t
errlen
=
sizeof
(
err
);
if
(
getsockopt
(
fd
,
SOL_SOCKET
,
SO_ERROR
,
&
err
,
&
errlen
)
==
-
1
)
{
if
(
getsockopt
(
c
->
fd
,
SOL_SOCKET
,
SO_ERROR
,
&
err
,
&
errlen
)
==
-
1
)
{
__redisSetErrorFromErrno
(
c
,
REDIS_ERR_IO
,
"getsockopt(SO_ERROR)"
);
close
(
fd
);
return
REDIS_ERR
;
}
if
(
err
)
{
errno
=
err
;
__redisSetErrorFromErrno
(
c
,
REDIS_ERR_IO
,
NULL
);
close
(
fd
);
return
REDIS_ERR
;
}
...
...
@@ -244,10 +253,12 @@ int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
return
REDIS_OK
;
}
int
redisContextConnectTcp
(
redisContext
*
c
,
const
char
*
addr
,
int
port
,
const
struct
timeval
*
timeout
)
{
static
int
_redisContextConnectTcp
(
redisContext
*
c
,
const
char
*
addr
,
int
port
,
const
struct
timeval
*
timeout
,
char
*
source_addr
)
{
int
s
,
rv
;
char
_port
[
6
];
/* strlen("65535"); */
struct
addrinfo
hints
,
*
servinfo
,
*
p
;
struct
addrinfo
hints
,
*
servinfo
,
*
bservinfo
,
*
p
,
*
b
;
int
blocking
=
(
c
->
flags
&
REDIS_BLOCK
);
snprintf
(
_port
,
6
,
"%d"
,
port
);
...
...
@@ -271,25 +282,47 @@ int redisContextConnectTcp(redisContext *c, const char *addr, int port, const st
if
((
s
=
socket
(
p
->
ai_family
,
p
->
ai_socktype
,
p
->
ai_protocol
))
==
-
1
)
continue
;
if
(
redisSetBlocking
(
c
,
s
,
0
)
!=
REDIS_OK
)
c
->
fd
=
s
;
if
(
redisSetBlocking
(
c
,
0
)
!=
REDIS_OK
)
goto
error
;
if
(
source_addr
)
{
int
bound
=
0
;
/* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
if
((
rv
=
getaddrinfo
(
source_addr
,
NULL
,
&
hints
,
&
bservinfo
))
!=
0
)
{
char
buf
[
128
];
snprintf
(
buf
,
sizeof
(
buf
),
"Can't get addr: %s"
,
gai_strerror
(
rv
));
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
buf
);
goto
error
;
}
for
(
b
=
bservinfo
;
b
!=
NULL
;
b
=
b
->
ai_next
)
{
if
(
bind
(
s
,
b
->
ai_addr
,
b
->
ai_addrlen
)
!=
-
1
)
{
bound
=
1
;
break
;
}
}
if
(
!
bound
)
{
char
buf
[
128
];
snprintf
(
buf
,
sizeof
(
buf
),
"Can't bind socket: %s"
,
strerror
(
errno
));
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
buf
);
goto
error
;
}
}
if
(
connect
(
s
,
p
->
ai_addr
,
p
->
ai_addrlen
)
==
-
1
)
{
if
(
errno
==
EHOSTUNREACH
)
{
close
(
s
);
redisContextCloseFd
(
c
);
continue
;
}
else
if
(
errno
==
EINPROGRESS
&&
!
blocking
)
{
/* This is ok. */
}
else
{
if
(
redisContextWaitReady
(
c
,
s
,
timeout
)
!=
REDIS_OK
)
if
(
redisContextWaitReady
(
c
,
timeout
)
!=
REDIS_OK
)
goto
error
;
}
}
if
(
blocking
&&
redisSetBlocking
(
c
,
s
,
1
)
!=
REDIS_OK
)
if
(
blocking
&&
redisSetBlocking
(
c
,
1
)
!=
REDIS_OK
)
goto
error
;
if
(
redisSetTcpNoDelay
(
c
,
s
)
!=
REDIS_OK
)
if
(
redisSetTcpNoDelay
(
c
)
!=
REDIS_OK
)
goto
error
;
c
->
fd
=
s
;
c
->
flags
|=
REDIS_CONNECTED
;
rv
=
REDIS_OK
;
goto
end
;
...
...
@@ -308,32 +341,40 @@ end:
return
rv
;
// Need to return REDIS_OK if alright
}
int
redisContextConnectTcp
(
redisContext
*
c
,
const
char
*
addr
,
int
port
,
const
struct
timeval
*
timeout
)
{
return
_redisContextConnectTcp
(
c
,
addr
,
port
,
timeout
,
NULL
);
}
int
redisContextConnectBindTcp
(
redisContext
*
c
,
const
char
*
addr
,
int
port
,
const
struct
timeval
*
timeout
,
char
*
source_addr
)
{
return
_redisContextConnectTcp
(
c
,
addr
,
port
,
timeout
,
source_addr
);
}
int
redisContextConnectUnix
(
redisContext
*
c
,
const
char
*
path
,
const
struct
timeval
*
timeout
)
{
int
s
;
int
blocking
=
(
c
->
flags
&
REDIS_BLOCK
);
struct
sockaddr_un
sa
;
if
(
(
s
=
redisCreateSocket
(
c
,
AF_LOCAL
)
)
<
0
)
if
(
redisCreateSocket
(
c
,
AF_LOCAL
)
<
0
)
return
REDIS_ERR
;
if
(
redisSetBlocking
(
c
,
s
,
0
)
!=
REDIS_OK
)
if
(
redisSetBlocking
(
c
,
0
)
!=
REDIS_OK
)
return
REDIS_ERR
;
sa
.
sun_family
=
AF_LOCAL
;
strncpy
(
sa
.
sun_path
,
path
,
sizeof
(
sa
.
sun_path
)
-
1
);
if
(
connect
(
s
,
(
struct
sockaddr
*
)
&
sa
,
sizeof
(
sa
))
==
-
1
)
{
if
(
connect
(
c
->
fd
,
(
struct
sockaddr
*
)
&
sa
,
sizeof
(
sa
))
==
-
1
)
{
if
(
errno
==
EINPROGRESS
&&
!
blocking
)
{
/* This is ok. */
}
else
{
if
(
redisContextWaitReady
(
c
,
s
,
timeout
)
!=
REDIS_OK
)
if
(
redisContextWaitReady
(
c
,
timeout
)
!=
REDIS_OK
)
return
REDIS_ERR
;
}
}
/* Reset socket to be blocking after connect(2). */
if
(
blocking
&&
redisSetBlocking
(
c
,
s
,
1
)
!=
REDIS_OK
)
if
(
blocking
&&
redisSetBlocking
(
c
,
1
)
!=
REDIS_OK
)
return
REDIS_ERR
;
c
->
fd
=
s
;
c
->
flags
|=
REDIS_CONNECTED
;
return
REDIS_OK
;
}
net.h
View file @
fa8bcca1
...
...
@@ -39,9 +39,11 @@
#define AF_LOCAL AF_UNIX
#endif
int
redisCheckSocketError
(
redisContext
*
c
,
int
fd
);
int
redisCheckSocketError
(
redisContext
*
c
);
int
redisContextSetTimeout
(
redisContext
*
c
,
const
struct
timeval
tv
);
int
redisContextConnectTcp
(
redisContext
*
c
,
const
char
*
addr
,
int
port
,
const
struct
timeval
*
timeout
);
int
redisContextConnectBindTcp
(
redisContext
*
c
,
const
char
*
addr
,
int
port
,
const
struct
timeval
*
timeout
,
char
*
source_addr
);
int
redisContextConnectUnix
(
redisContext
*
c
,
const
char
*
path
,
const
struct
timeval
*
timeout
);
int
redisKeepAlive
(
redisContext
*
c
,
int
interval
);
...
...
sds.c
View file @
fa8bcca1
/* SDS
Lib
, A C dynamic strings library
/* SDS
(Simple Dynamic Strings)
, A C dynamic strings library
.
*
* Copyright (c) 2006-201
0
, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2006-201
4
, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
...
...
@@ -32,83 +32,188 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include
"sds
.h
"
#include
<assert
.h
>
#ifdef SDS_ABORT_ON_OOM
static
void
sdsOomAbort
(
void
)
{
fprintf
(
stderr
,
"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)
\n
"
);
abort
();
}
#endif
#include "sds.h"
/* Create a new sds string with the content specified by the 'init' pointer
* and 'initlen'.
* If NULL is used for 'init' the string is initialized with zero bytes.
*
* The string is always null-termined (all the sds strings are, always) so
* even if you create an sds string with:
*
* mystring = sdsnewlen("abc",3");
*
* You can print the string with printf() as there is an implicit \0 at the
* end of the string. However the string is binary safe and can contain
* \0 characters in the middle, as the length is stored in the sds header. */
sds
sdsnewlen
(
const
void
*
init
,
size_t
initlen
)
{
struct
sdshdr
*
sh
;
sh
=
malloc
(
sizeof
(
struct
sdshdr
)
+
initlen
+
1
);
#ifdef SDS_ABORT_ON_OOM
if
(
sh
==
NULL
)
sdsOomAbort
();
#else
if
(
init
)
{
sh
=
malloc
(
sizeof
*
sh
+
initlen
+
1
);
}
else
{
sh
=
calloc
(
sizeof
*
sh
+
initlen
+
1
,
1
);
}
if
(
sh
==
NULL
)
return
NULL
;
#endif
sh
->
len
=
initlen
;
sh
->
free
=
0
;
if
(
initlen
)
{
if
(
init
)
memcpy
(
sh
->
buf
,
init
,
initlen
);
else
memset
(
sh
->
buf
,
0
,
initlen
);
}
if
(
initlen
&&
init
)
memcpy
(
sh
->
buf
,
init
,
initlen
);
sh
->
buf
[
initlen
]
=
'\0'
;
return
(
char
*
)
sh
->
buf
;
}
/* Create an empty (zero length) sds string. Even in this case the string
* always has an implicit null term. */
sds
sdsempty
(
void
)
{
return
sdsnewlen
(
""
,
0
);
}
/* Create a new sds string starting from a null termined C string. */
sds
sdsnew
(
const
char
*
init
)
{
size_t
initlen
=
(
init
==
NULL
)
?
0
:
strlen
(
init
);
return
sdsnewlen
(
init
,
initlen
);
}
/* Duplicate an sds string. */
sds
sdsdup
(
const
sds
s
)
{
return
sdsnewlen
(
s
,
sdslen
(
s
));
}
/* Free an sds string. No operation is performed if 's' is NULL. */
void
sdsfree
(
sds
s
)
{
if
(
s
==
NULL
)
return
;
free
(
s
-
sizeof
(
struct
sdshdr
));
}
/* Set the sds string length to the length as obtained with strlen(), so
* considering as content only up to the first null term character.
*
* This function is useful when the sds string is hacked manually in some
* way, like in the following example:
*
* s = sdsnew("foobar");
* s[2] = '\0';
* sdsupdatelen(s);
* printf("%d\n", sdslen(s));
*
* The output will be "2", but if we comment out the call to sdsupdatelen()
* the output will be "6" as the string was modified but the logical length
* remains 6 bytes. */
void
sdsupdatelen
(
sds
s
)
{
struct
sdshdr
*
sh
=
(
void
*
)
(
s
-
(
sizeof
(
struct
sdshdr
)))
;
struct
sdshdr
*
sh
=
(
void
*
)
(
s
-
sizeof
*
sh
);
;
int
reallen
=
strlen
(
s
);
sh
->
free
+=
(
sh
->
len
-
reallen
);
sh
->
len
=
reallen
;
}
static
sds
sdsMakeRoomFor
(
sds
s
,
size_t
addlen
)
{
/* Modify an sds string on-place to make it empty (zero length).
* However all the existing buffer is not discarded but set as free space
* so that next append operations will not require allocations up to the
* number of bytes previously available. */
void
sdsclear
(
sds
s
)
{
struct
sdshdr
*
sh
=
(
void
*
)
(
s
-
sizeof
*
sh
);;
sh
->
free
+=
sh
->
len
;
sh
->
len
=
0
;
sh
->
buf
[
0
]
=
'\0'
;
}
/* Enlarge the free space at the end of the sds string so that the caller
* is sure that after calling this function can overwrite up to addlen
* bytes after the end of the string, plus one more byte for nul term.
*
* Note: this does not change the *length* of the sds string as returned
* by sdslen(), but only the free buffer space we have. */
sds
sdsMakeRoomFor
(
sds
s
,
size_t
addlen
)
{
struct
sdshdr
*
sh
,
*
newsh
;
size_t
free
=
sdsavail
(
s
);
size_t
len
,
newlen
;
if
(
free
>=
addlen
)
return
s
;
len
=
sdslen
(
s
);
sh
=
(
void
*
)
(
s
-
(
sizeof
(
struct
sdshdr
)));
newlen
=
(
len
+
addlen
)
*
2
;
newsh
=
realloc
(
sh
,
sizeof
(
struct
sdshdr
)
+
newlen
+
1
);
#ifdef SDS_ABORT_ON_OOM
if
(
newsh
==
NULL
)
sdsOomAbort
();
#else
sh
=
(
void
*
)
(
s
-
sizeof
*
sh
);;
newlen
=
(
len
+
addlen
);
if
(
newlen
<
SDS_MAX_PREALLOC
)
newlen
*=
2
;
else
newlen
+=
SDS_MAX_PREALLOC
;
newsh
=
realloc
(
sh
,
sizeof
*
newsh
+
newlen
+
1
);
if
(
newsh
==
NULL
)
return
NULL
;
#endif
newsh
->
free
=
newlen
-
len
;
return
newsh
->
buf
;
}
/* Reallocate the sds string so that it has no free space at the end. The
* contained string remains not altered, but next concatenation operations
* will require a reallocation.
*
* After the call, the passed sds string is no longer valid and all the
* references must be substituted with the new pointer returned by the call. */
sds
sdsRemoveFreeSpace
(
sds
s
)
{
struct
sdshdr
*
sh
;
sh
=
(
void
*
)
(
s
-
sizeof
*
sh
);;
sh
=
realloc
(
sh
,
sizeof
*
sh
+
sh
->
len
+
1
);
sh
->
free
=
0
;
return
sh
->
buf
;
}
/* Return the total size of the allocation of the specifed sds string,
* including:
* 1) The sds header before the pointer.
* 2) The string.
* 3) The free buffer at the end if any.
* 4) The implicit null term.
*/
size_t
sdsAllocSize
(
sds
s
)
{
struct
sdshdr
*
sh
=
(
void
*
)
(
s
-
sizeof
*
sh
);;
return
sizeof
(
*
sh
)
+
sh
->
len
+
sh
->
free
+
1
;
}
/* Increment the sds length and decrements the left free space at the
* end of the string according to 'incr'. Also set the null term
* in the new end of the string.
*
* This function is used in order to fix the string length after the
* user calls sdsMakeRoomFor(), writes something after the end of
* the current string, and finally needs to set the new length.
*
* Note: it is possible to use a negative increment in order to
* right-trim the string.
*
* Usage example:
*
* Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the
* following schema, to cat bytes coming from the kernel to the end of an
* sds string without copying into an intermediate buffer:
*
* oldlen = sdslen(s);
* s = sdsMakeRoomFor(s, BUFFER_SIZE);
* nread = read(fd, s+oldlen, BUFFER_SIZE);
* ... check for nread <= 0 and handle it ...
* sdsIncrLen(s, nread);
*/
void
sdsIncrLen
(
sds
s
,
int
incr
)
{
struct
sdshdr
*
sh
=
(
void
*
)
(
s
-
sizeof
*
sh
);;
assert
(
sh
->
free
>=
incr
);
sh
->
len
+=
incr
;
sh
->
free
-=
incr
;
assert
(
sh
->
free
>=
0
);
s
[
sh
->
len
]
=
'\0'
;
}
/* 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. */
* the original length of the sds will be set to zero.
*
* if the specified length is smaller than the current length, no operation
* is performed. */
sds
sdsgrowzero
(
sds
s
,
size_t
len
)
{
struct
sdshdr
*
sh
=
(
void
*
)(
s
-
(
sizeof
(
struct
sdshdr
))
);
struct
sdshdr
*
sh
=
(
void
*
)
(
s
-
sizeof
*
sh
);
size_t
totlen
,
curlen
=
sh
->
len
;
if
(
len
<=
curlen
)
return
s
;
...
...
@@ -116,7 +221,7 @@ sds sdsgrowzero(sds s, size_t len) {
if
(
s
==
NULL
)
return
NULL
;
/* Make sure added region doesn't contain garbage */
sh
=
(
void
*
)(
s
-
(
sizeof
(
struct
sdshdr
))
);
sh
=
(
void
*
)(
s
-
sizeof
*
sh
);
memset
(
s
+
curlen
,
0
,(
len
-
curlen
+
1
));
/* also set trailing \0 byte */
totlen
=
sh
->
len
+
sh
->
free
;
sh
->
len
=
len
;
...
...
@@ -124,13 +229,18 @@ sds sdsgrowzero(sds s, size_t len) {
return
s
;
}
/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the
* end of the specified sds string 's'.
*
* After the call, the passed sds string is no longer valid and all the
* references must be substituted with the new pointer returned by the call. */
sds
sdscatlen
(
sds
s
,
const
void
*
t
,
size_t
len
)
{
struct
sdshdr
*
sh
;
size_t
curlen
=
sdslen
(
s
);
s
=
sdsMakeRoomFor
(
s
,
len
);
if
(
s
==
NULL
)
return
NULL
;
sh
=
(
void
*
)
(
s
-
(
sizeof
(
struct
sdshdr
)))
;
sh
=
(
void
*
)
(
s
-
sizeof
*
sh
);
;
memcpy
(
s
+
curlen
,
t
,
len
);
sh
->
len
=
curlen
+
len
;
sh
->
free
=
sh
->
free
-
len
;
...
...
@@ -138,18 +248,32 @@ sds sdscatlen(sds s, const void *t, size_t len) {
return
s
;
}
/* Append the specified null termianted C string to the sds string 's'.
*
* After the call, the passed sds string is no longer valid and all the
* references must be substituted with the new pointer returned by the call. */
sds
sdscat
(
sds
s
,
const
char
*
t
)
{
return
sdscatlen
(
s
,
t
,
strlen
(
t
));
}
sds
sdscpylen
(
sds
s
,
char
*
t
,
size_t
len
)
{
struct
sdshdr
*
sh
=
(
void
*
)
(
s
-
(
sizeof
(
struct
sdshdr
)));
/* Append the specified sds 't' to the existing sds 's'.
*
* After the call, the modified sds string is no longer valid and all the
* references must be substituted with the new pointer returned by the call. */
sds
sdscatsds
(
sds
s
,
const
sds
t
)
{
return
sdscatlen
(
s
,
t
,
sdslen
(
t
));
}
/* Destructively modify the sds string 's' to hold the specified binary
* safe string pointed by 't' of length 'len' bytes. */
sds
sdscpylen
(
sds
s
,
const
char
*
t
,
size_t
len
)
{
struct
sdshdr
*
sh
=
(
void
*
)
(
s
-
sizeof
*
sh
);;
size_t
totlen
=
sh
->
free
+
sh
->
len
;
if
(
totlen
<
len
)
{
s
=
sdsMakeRoomFor
(
s
,
len
-
sh
->
len
);
if
(
s
==
NULL
)
return
NULL
;
sh
=
(
void
*
)
(
s
-
(
sizeof
(
struct
sdshdr
)))
;
sh
=
(
void
*
)
(
s
-
sizeof
*
sh
);
;
totlen
=
sh
->
free
+
sh
->
len
;
}
memcpy
(
s
,
t
,
len
);
...
...
@@ -159,10 +283,13 @@ sds sdscpylen(sds s, char *t, size_t len) {
return
s
;
}
sds
sdscpy
(
sds
s
,
char
*
t
)
{
/* Like sdscpylen() but 't' must be a null-termined string so that the length
* of the string is obtained with strlen(). */
sds
sdscpy
(
sds
s
,
const
char
*
t
)
{
return
sdscpylen
(
s
,
t
,
strlen
(
t
));
}
/* Like sdscatpritf() but gets va_list instead of being variadic. */
sds
sdscatvprintf
(
sds
s
,
const
char
*
fmt
,
va_list
ap
)
{
va_list
cpy
;
char
*
buf
,
*
t
;
...
...
@@ -170,15 +297,10 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
while
(
1
)
{
buf
=
malloc
(
buflen
);
#ifdef SDS_ABORT_ON_OOM
if
(
buf
==
NULL
)
sdsOomAbort
();
#else
if
(
buf
==
NULL
)
return
NULL
;
#endif
buf
[
buflen
-
2
]
=
'\0'
;
va_copy
(
cpy
,
ap
);
vsnprintf
(
buf
,
buflen
,
fmt
,
cpy
);
va_end
(
cpy
);
if
(
buf
[
buflen
-
2
]
!=
'\0'
)
{
free
(
buf
);
buflen
*=
2
;
...
...
@@ -191,6 +313,22 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
return
t
;
}
/* Append to the sds string 's' a string obtained using printf-alike format
* specifier.
*
* After the call, the modified sds string is no longer valid and all the
* references must be substituted with the new pointer returned by the call.
*
* Example:
*
* s = sdsempty("Sum is: ");
* s = sdscatprintf(s,"%d+%d = %d",a,b,a+b).
*
* Often you need to create a string from scratch with the printf-alike
* format. When this is the need, just use sdsempty() as the target string:
*
* s = sdscatprintf(sdsempty(), "... your format ...", args);
*/
sds
sdscatprintf
(
sds
s
,
const
char
*
fmt
,
...)
{
va_list
ap
;
char
*
t
;
...
...
@@ -200,8 +338,22 @@ sds sdscatprintf(sds s, const char *fmt, ...) {
return
t
;
}
sds
sdstrim
(
sds
s
,
const
char
*
cset
)
{
struct
sdshdr
*
sh
=
(
void
*
)
(
s
-
(
sizeof
(
struct
sdshdr
)));
/* Remove the part of the string from left and from right composed just of
* contiguous characters found in 'cset', that is a null terminted C string.
*
* After the call, the modified sds string is no longer valid and all the
* references must be substituted with the new pointer returned by the call.
*
* Example:
*
* s = sdsnew("AA...AA.a.aa.aHelloWorld :::");
* s = sdstrim(s,"A. :");
* printf("%s\n", s);
*
* Output will be just "Hello World".
*/
void
sdstrim
(
sds
s
,
const
char
*
cset
)
{
struct
sdshdr
*
sh
=
(
void
*
)
(
s
-
sizeof
*
sh
);;
char
*
start
,
*
end
,
*
sp
,
*
ep
;
size_t
len
;
...
...
@@ -214,14 +366,29 @@ sds sdstrim(sds s, const char *cset) {
sh
->
buf
[
len
]
=
'\0'
;
sh
->
free
=
sh
->
free
+
(
sh
->
len
-
len
);
sh
->
len
=
len
;
return
s
;
}
sds
sdsrange
(
sds
s
,
int
start
,
int
end
)
{
struct
sdshdr
*
sh
=
(
void
*
)
(
s
-
(
sizeof
(
struct
sdshdr
)));
/* Turn the string into a smaller (or equal) string containing only the
* substring specified by the 'start' and 'end' indexes.
*
* start and end can be negative, where -1 means the last character of the
* string, -2 the penultimate character, and so forth.
*
* The interval is inclusive, so the start and end characters will be part
* of the resulting string.
*
* The string is modified in-place.
*
* Example:
*
* s = sdsnew("Hello World");
* sdsrange(s,1,-1); => "ello World"
*/
void
sdsrange
(
sds
s
,
int
start
,
int
end
)
{
struct
sdshdr
*
sh
=
(
void
*
)
(
s
-
sizeof
*
sh
);;
size_t
newlen
,
len
=
sdslen
(
s
);
if
(
len
==
0
)
return
s
;
if
(
len
==
0
)
return
;
if
(
start
<
0
)
{
start
=
len
+
start
;
if
(
start
<
0
)
start
=
0
;
...
...
@@ -245,22 +412,34 @@ sds sdsrange(sds s, int start, int end) {
sh
->
buf
[
newlen
]
=
0
;
sh
->
free
=
sh
->
free
+
(
sh
->
len
-
newlen
);
sh
->
len
=
newlen
;
return
s
;
}
/* Apply tolower() to every character of the sds string 's'. */
void
sdstolower
(
sds
s
)
{
int
len
=
sdslen
(
s
),
j
;
for
(
j
=
0
;
j
<
len
;
j
++
)
s
[
j
]
=
tolower
(
s
[
j
]);
}
/* Apply toupper() to every character of the sds string 's'. */
void
sdstoupper
(
sds
s
)
{
int
len
=
sdslen
(
s
),
j
;
for
(
j
=
0
;
j
<
len
;
j
++
)
s
[
j
]
=
toupper
(
s
[
j
]);
}
int
sdscmp
(
sds
s1
,
sds
s2
)
{
/* Compare two sds strings s1 and s2 with memcmp().
*
* Return value:
*
* 1 if s1 > s2.
* -1 if s1 < s2.
* 0 if s1 and s2 are exactly the same binary string.
*
* If two strings share exactly the same prefix, but one of the two has
* additional characters, the longer string is considered to be greater than
* the smaller one. */
int
sdscmp
(
const
sds
s1
,
const
sds
s2
)
{
size_t
l1
,
l2
,
minlen
;
int
cmp
;
...
...
@@ -288,18 +467,15 @@ int sdscmp(sds s1, sds s2) {
* requires length arguments. sdssplit() is just the
* same function but for zero-terminated strings.
*/
sds
*
sdssplitlen
(
char
*
s
,
int
len
,
char
*
sep
,
int
seplen
,
int
*
count
)
{
sds
*
sdssplitlen
(
const
char
*
s
,
int
len
,
const
char
*
sep
,
int
seplen
,
int
*
count
)
{
int
elements
=
0
,
slots
=
5
,
start
=
0
,
j
;
sds
*
tokens
;
sds
*
tokens
=
malloc
(
sizeof
(
sds
)
*
slots
);
#ifdef SDS_ABORT_ON_OOM
if
(
tokens
==
NULL
)
sdsOomAbort
();
#endif
if
(
seplen
<
1
||
len
<
0
)
return
NULL
;
tokens
=
malloc
(
sizeof
(
sds
)
*
slots
);
if
(
tokens
==
NULL
)
return
NULL
;
if
(
seplen
<
1
||
len
<
0
)
{
free
(
tokens
);
return
NULL
;
}
if
(
len
==
0
)
{
*
count
=
0
;
return
tokens
;
...
...
@@ -311,25 +487,13 @@ sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
slots
*=
2
;
newtokens
=
realloc
(
tokens
,
sizeof
(
sds
)
*
slots
);
if
(
newtokens
==
NULL
)
{
#ifdef SDS_ABORT_ON_OOM
sdsOomAbort
();
#else
goto
cleanup
;
#endif
}
if
(
newtokens
==
NULL
)
goto
cleanup
;
tokens
=
newtokens
;
}
/* search the separator */
if
((
seplen
==
1
&&
*
(
s
+
j
)
==
sep
[
0
])
||
(
memcmp
(
s
+
j
,
sep
,
seplen
)
==
0
))
{
tokens
[
elements
]
=
sdsnewlen
(
s
+
start
,
j
-
start
);
if
(
tokens
[
elements
]
==
NULL
)
{
#ifdef SDS_ABORT_ON_OOM
sdsOomAbort
();
#else
goto
cleanup
;
#endif
}
if
(
tokens
[
elements
]
==
NULL
)
goto
cleanup
;
elements
++
;
start
=
j
+
seplen
;
j
=
j
+
seplen
-
1
;
/* skip the separator */
...
...
@@ -337,28 +501,22 @@ sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
}
/* Add the final element. We are sure there is room in the tokens array. */
tokens
[
elements
]
=
sdsnewlen
(
s
+
start
,
len
-
start
);
if
(
tokens
[
elements
]
==
NULL
)
{
#ifdef SDS_ABORT_ON_OOM
sdsOomAbort
();
#else
goto
cleanup
;
#endif
}
if
(
tokens
[
elements
]
==
NULL
)
goto
cleanup
;
elements
++
;
*
count
=
elements
;
return
tokens
;
#ifndef SDS_ABORT_ON_OOM
cleanup:
{
int
i
;
for
(
i
=
0
;
i
<
elements
;
i
++
)
sdsfree
(
tokens
[
i
]);
free
(
tokens
);
*
count
=
0
;
return
NULL
;
}
#endif
}
/* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */
void
sdsfreesplitres
(
sds
*
tokens
,
int
count
)
{
if
(
!
tokens
)
return
;
while
(
count
--
)
...
...
@@ -366,6 +524,10 @@ void sdsfreesplitres(sds *tokens, int count) {
free
(
tokens
);
}
/* Create an sds string from a long long value. It is much faster than:
*
* sdscatprintf(sdsempty(),"%lld\n", value);
*/
sds
sdsfromlonglong
(
long
long
value
)
{
char
buf
[
32
],
*
p
;
unsigned
long
long
v
;
...
...
@@ -381,10 +543,14 @@ sds sdsfromlonglong(long long value) {
return
sdsnewlen
(
p
,
32
-
(
p
-
buf
));
}
sds
sdscatrepr
(
sds
s
,
char
*
p
,
size_t
len
)
{
/* Append to the sds string "s" an escaped string representation where
* all the non-printable characters (tested with isprint()) are turned into
* escapes in the form "\n\r\a...." or "\x<hex-number>".
*
* After the call, the modified sds string is no longer valid and all the
* references must be substituted with the new pointer returned by the call. */
sds
sdscatrepr
(
sds
s
,
const
char
*
p
,
size_t
len
)
{
s
=
sdscatlen
(
s
,
"
\"
"
,
1
);
if
(
s
==
NULL
)
return
NULL
;
while
(
len
--
)
{
switch
(
*
p
)
{
case
'\\'
:
...
...
@@ -404,27 +570,64 @@ sds sdscatrepr(sds s, char *p, size_t len) {
break
;
}
p
++
;
if
(
s
==
NULL
)
return
NULL
;
}
return
sdscatlen
(
s
,
"
\"
"
,
1
);
}
/* Helper function for sdssplitargs() that returns non zero if 'c'
* is a valid hex digit. */
int
is_hex_digit
(
char
c
)
{
return
(
c
>=
'0'
&&
c
<=
'9'
)
||
(
c
>=
'a'
&&
c
<=
'f'
)
||
(
c
>=
'A'
&&
c
<=
'F'
);
}
/* Helper function for sdssplitargs() that converts a hex digit into an
* integer from 0 to 15 */
int
hex_digit_to_int
(
char
c
)
{
switch
(
c
)
{
case
'0'
:
return
0
;
case
'1'
:
return
1
;
case
'2'
:
return
2
;
case
'3'
:
return
3
;
case
'4'
:
return
4
;
case
'5'
:
return
5
;
case
'6'
:
return
6
;
case
'7'
:
return
7
;
case
'8'
:
return
8
;
case
'9'
:
return
9
;
case
'a'
:
case
'A'
:
return
10
;
case
'b'
:
case
'B'
:
return
11
;
case
'c'
:
case
'C'
:
return
12
;
case
'd'
:
case
'D'
:
return
13
;
case
'e'
:
case
'E'
:
return
14
;
case
'f'
:
case
'F'
:
return
15
;
default:
return
0
;
}
}
/* Split a line into arguments, where every argument can be in the
* following programming-language REPL-alike form:
*
* foo bar "newline are supported\n" and "\xff\x00otherstuff"
*
* The number of arguments is stored into *argc, and an array
* of sds is returned. The caller should sdsfree() all the returned
* strings and finally free() the array itself.
* of sds is returned.
*
* The caller should free the resulting array of sds strings with
* sdsfreesplitres().
*
* Note that sdscatrepr() is able to convert back a string into
* a quoted string in the same format sdssplitargs() is able to parse.
*
* The function returns the allocated tokens on success, even when the
* input string is empty, or NULL if the input contains unbalanced
* quotes or closed quotes followed by non space characters
* as in: "foo"bar or "foo'
*/
sds
*
sdssplitargs
(
char
*
line
,
int
*
argc
)
{
char
*
p
=
line
;
sds
*
sdssplitargs
(
const
char
*
line
,
int
*
argc
)
{
const
char
*
p
=
line
;
char
*
current
=
NULL
;
char
**
vector
=
NULL
,
**
_vector
=
NULL
;
char
**
vector
=
NULL
;
*
argc
=
0
;
while
(
1
)
{
...
...
@@ -433,16 +636,23 @@ sds *sdssplitargs(char *line, int *argc) {
if
(
*
p
)
{
/* get a token */
int
inq
=
0
;
/* set to 1 if we are in "quotes" */
int
insq
=
0
;
/* set to 1 if we are in 'single quotes' */
int
done
=
0
;
if
(
current
==
NULL
)
{
current
=
sdsempty
();
if
(
current
==
NULL
)
goto
err
;
}
if
(
current
==
NULL
)
current
=
sdsempty
();
while
(
!
done
)
{
if
(
inq
)
{
if
(
*
p
==
'\\'
&&
*
(
p
+
1
))
{
if
(
*
p
==
'\\'
&&
*
(
p
+
1
)
==
'x'
&&
is_hex_digit
(
*
(
p
+
2
))
&&
is_hex_digit
(
*
(
p
+
3
)))
{
unsigned
char
byte
;
byte
=
(
hex_digit_to_int
(
*
(
p
+
2
))
*
16
)
+
hex_digit_to_int
(
*
(
p
+
3
));
current
=
sdscatlen
(
current
,(
char
*
)
&
byte
,
1
);
p
+=
3
;
}
else
if
(
*
p
==
'\\'
&&
*
(
p
+
1
))
{
char
c
;
p
++
;
...
...
@@ -456,7 +666,23 @@ sds *sdssplitargs(char *line, int *argc) {
}
current
=
sdscatlen
(
current
,
&
c
,
1
);
}
else
if
(
*
p
==
'"'
)
{
/* closing quote must be followed by a space */
/* closing quote must be followed by a space or
* nothing at all. */
if
(
*
(
p
+
1
)
&&
!
isspace
(
*
(
p
+
1
)))
goto
err
;
done
=
1
;
}
else
if
(
!*
p
)
{
/* unterminated quotes */
goto
err
;
}
else
{
current
=
sdscatlen
(
current
,
p
,
1
);
}
}
else
if
(
insq
)
{
if
(
*
p
==
'\\'
&&
*
(
p
+
1
)
==
'\''
)
{
p
++
;
current
=
sdscatlen
(
current
,
"'"
,
1
);
}
else
if
(
*
p
==
'\''
)
{
/* closing quote must be followed by a space or
* nothing at all. */
if
(
*
(
p
+
1
)
&&
!
isspace
(
*
(
p
+
1
)))
goto
err
;
done
=
1
;
}
else
if
(
!*
p
)
{
...
...
@@ -477,23 +703,24 @@ sds *sdssplitargs(char *line, int *argc) {
case
'"'
:
inq
=
1
;
break
;
case
'\''
:
insq
=
1
;
break
;
default:
current
=
sdscatlen
(
current
,
p
,
1
);
break
;
}
}
if
(
*
p
)
p
++
;
if
(
current
==
NULL
)
goto
err
;
}
/* add the token to the vector */
_vector
=
realloc
(
vector
,((
*
argc
)
+
1
)
*
sizeof
(
char
*
));
if
(
_vector
==
NULL
)
goto
err
;
vector
=
_vector
;
vector
=
realloc
(
vector
,((
*
argc
)
+
1
)
*
sizeof
(
char
*
));
vector
[
*
argc
]
=
current
;
(
*
argc
)
++
;
current
=
NULL
;
}
else
{
/* Even on empty input string return something not NULL. */
if
(
vector
==
NULL
)
vector
=
malloc
(
sizeof
(
void
*
));
return
vector
;
}
}
...
...
@@ -501,30 +728,67 @@ sds *sdssplitargs(char *line, int *argc) {
err:
while
((
*
argc
)
--
)
sdsfree
(
vector
[
*
argc
]);
if
(
vector
!=
NULL
)
free
(
vector
);
if
(
current
!=
NULL
)
sdsfree
(
current
);
free
(
vector
);
if
(
current
)
sdsfree
(
current
);
*
argc
=
0
;
return
NULL
;
}
/* Modify the string substituting all the occurrences of the set of
* characters specified in the 'from' string to the corresponding character
* in the 'to' array.
*
* For instance: sdsmapchars(mystring, "ho", "01", 2)
* will have the effect of turning the string "hello" into "0ell1".
*
* The function returns the sds string pointer, that is always the same
* as the input pointer since no resize is needed. */
sds
sdsmapchars
(
sds
s
,
const
char
*
from
,
const
char
*
to
,
size_t
setlen
)
{
size_t
j
,
i
,
l
=
sdslen
(
s
);
for
(
j
=
0
;
j
<
l
;
j
++
)
{
for
(
i
=
0
;
i
<
setlen
;
i
++
)
{
if
(
s
[
j
]
==
from
[
i
])
{
s
[
j
]
=
to
[
i
];
break
;
}
}
}
return
s
;
}
/* Join an array of C strings using the specified separator (also a C string).
* Returns the result as an sds string. */
sds
sdsjoin
(
char
**
argv
,
int
argc
,
char
*
sep
,
size_t
seplen
)
{
sds
join
=
sdsempty
();
int
j
;
for
(
j
=
0
;
j
<
argc
;
j
++
)
{
join
=
sdscat
(
join
,
argv
[
j
]);
if
(
j
!=
argc
-
1
)
join
=
sdscatlen
(
join
,
sep
,
seplen
);
}
return
join
;
}
/* Like sdsjoin, but joins an array of SDS strings. */
sds
sdsjoinsds
(
sds
*
argv
,
int
argc
,
const
char
*
sep
,
size_t
seplen
)
{
sds
join
=
sdsempty
();
int
j
;
for
(
j
=
0
;
j
<
argc
;
j
++
)
{
join
=
sdscatsds
(
join
,
argv
[
j
]);
if
(
j
!=
argc
-
1
)
join
=
sdscatlen
(
join
,
sep
,
seplen
);
}
return
join
;
}
#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);
#include "testhelp.h"
int
main
(
void
)
{
{
struct
sdshdr
*
sh
;
sds
x
=
sdsnew
(
"foo"
),
y
;
test_cond
(
"Create a string and obtain the length"
,
...
...
@@ -554,36 +818,43 @@ int main(void) {
sdslen
(
x
)
==
3
&&
memcmp
(
x
,
"123
\0
"
,
4
)
==
0
)
sdsfree
(
x
);
x
=
sdstrim
(
sdsnew
(
"xxciaoyyy"
),
"xy"
);
x
=
sdsnew
(
"xxciaoyyy"
);
sdstrim
(
x
,
"xy"
);
test_cond
(
"sdstrim() correctly trims characters"
,
sdslen
(
x
)
==
4
&&
memcmp
(
x
,
"ciao
\0
"
,
5
)
==
0
)
y
=
sdsrange
(
sdsdup
(
x
),
1
,
1
);
y
=
sdsdup
(
x
);
sdsrange
(
y
,
1
,
1
);
test_cond
(
"sdsrange(...,1,1)"
,
sdslen
(
y
)
==
1
&&
memcmp
(
y
,
"i
\0
"
,
2
)
==
0
)
sdsfree
(
y
);
y
=
sdsrange
(
sdsdup
(
x
),
1
,
-
1
);
y
=
sdsdup
(
x
);
sdsrange
(
y
,
1
,
-
1
);
test_cond
(
"sdsrange(...,1,-1)"
,
sdslen
(
y
)
==
3
&&
memcmp
(
y
,
"iao
\0
"
,
4
)
==
0
)
sdsfree
(
y
);
y
=
sdsrange
(
sdsdup
(
x
),
-
2
,
-
1
);
y
=
sdsdup
(
x
);
sdsrange
(
y
,
-
2
,
-
1
);
test_cond
(
"sdsrange(...,-2,-1)"
,
sdslen
(
y
)
==
2
&&
memcmp
(
y
,
"ao
\0
"
,
3
)
==
0
)
sdsfree
(
y
);
y
=
sdsrange
(
sdsdup
(
x
),
2
,
1
);
y
=
sdsdup
(
x
);
sdsrange
(
y
,
2
,
1
);
test_cond
(
"sdsrange(...,2,1)"
,
sdslen
(
y
)
==
0
&&
memcmp
(
y
,
"
\0
"
,
1
)
==
0
)
sdsfree
(
y
);
y
=
sdsrange
(
sdsdup
(
x
),
1
,
100
);
y
=
sdsdup
(
x
);
sdsrange
(
y
,
1
,
100
);
test_cond
(
"sdsrange(...,1,100)"
,
sdslen
(
y
)
==
3
&&
memcmp
(
y
,
"iao
\0
"
,
4
)
==
0
)
sdsfree
(
y
);
y
=
sdsrange
(
sdsdup
(
x
),
100
,
100
);
y
=
sdsdup
(
x
);
sdsrange
(
y
,
100
,
100
);
test_cond
(
"sdsrange(...,100,100)"
,
sdslen
(
y
)
==
0
&&
memcmp
(
y
,
"
\0
"
,
1
)
==
0
)
...
...
@@ -604,7 +875,33 @@ int main(void) {
x
=
sdsnew
(
"aar"
);
y
=
sdsnew
(
"bar"
);
test_cond
(
"sdscmp(bar,bar)"
,
sdscmp
(
x
,
y
)
<
0
)
sdsfree
(
y
);
sdsfree
(
x
);
x
=
sdsnewlen
(
"
\a\n\0
foo
\r
"
,
7
);
y
=
sdscatrepr
(
sdsempty
(),
x
,
sdslen
(
x
));
test_cond
(
"sdscatrepr(...data...)"
,
memcmp
(
y
,
"
\"\\
a
\\
n
\\
x00foo
\\
r
\"
"
,
15
)
==
0
)
{
int
oldfree
;
sdsfree
(
x
);
x
=
sdsnew
(
"0"
);
sh
=
(
void
*
)
(
x
-
(
sizeof
(
struct
sdshdr
)));
test_cond
(
"sdsnew() free/len buffers"
,
sh
->
len
==
1
&&
sh
->
free
==
0
);
x
=
sdsMakeRoomFor
(
x
,
1
);
sh
=
(
void
*
)
(
x
-
(
sizeof
(
struct
sdshdr
)));
test_cond
(
"sdsMakeRoomFor()"
,
sh
->
len
==
1
&&
sh
->
free
>
0
);
oldfree
=
sh
->
free
;
x
[
1
]
=
'1'
;
sdsIncrLen
(
x
,
1
);
test_cond
(
"sdsIncrLen() -- content"
,
x
[
0
]
==
'0'
&&
x
[
1
]
==
'1'
);
test_cond
(
"sdsIncrLen() -- len"
,
sh
->
len
==
2
);
test_cond
(
"sdsIncrLen() -- free"
,
sh
->
free
==
oldfree
-
1
);
}
}
test_report
()
return
0
;
}
#endif
sds.h
View file @
fa8bcca1
/* SDS
Lib
, A C dynamic strings library
/* SDS
(Simple Dynamic Strings)
, A C dynamic strings library
.
*
* Copyright (c) 2006-201
0
, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2006-201
4
, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
...
...
@@ -31,6 +31,8 @@
#ifndef __SDS_H
#define __SDS_H
#define SDS_MAX_PREALLOC (1024*1024)
#include <sys/types.h>
#include <stdarg.h>
...
...
@@ -43,12 +45,12 @@ struct sdshdr {
};
static
inline
size_t
sdslen
(
const
sds
s
)
{
struct
sdshdr
*
sh
=
(
void
*
)(
s
-
(
sizeof
(
struct
sdshdr
))
);
struct
sdshdr
*
sh
=
(
void
*
)(
s
-
sizeof
*
sh
);
return
sh
->
len
;
}
static
inline
size_t
sdsavail
(
const
sds
s
)
{
struct
sdshdr
*
sh
=
(
void
*
)(
s
-
(
sizeof
(
struct
sdshdr
))
);
struct
sdshdr
*
sh
=
(
void
*
)(
s
-
sizeof
*
sh
);
return
sh
->
free
;
}
...
...
@@ -58,12 +60,13 @@ sds sdsempty(void);
size_t
sdslen
(
const
sds
s
);
sds
sdsdup
(
const
sds
s
);
void
sdsfree
(
sds
s
);
size_t
sdsavail
(
sds
s
);
size_t
sdsavail
(
const
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
);
sds
sdscpy
(
sds
s
,
char
*
t
);
sds
sdscatsds
(
sds
s
,
const
sds
t
);
sds
sdscpylen
(
sds
s
,
const
char
*
t
,
size_t
len
);
sds
sdscpy
(
sds
s
,
const
char
*
t
);
sds
sdscatvprintf
(
sds
s
,
const
char
*
fmt
,
va_list
ap
);
#ifdef __GNUC__
...
...
@@ -73,16 +76,26 @@ sds sdscatprintf(sds s, const char *fmt, ...)
sds
sdscatprintf
(
sds
s
,
const
char
*
fmt
,
...);
#endif
sds
sdstrim
(
sds
s
,
const
char
*
cset
);
sds
sdsrange
(
sds
s
,
int
start
,
int
end
);
void
sdstrim
(
sds
s
,
const
char
*
cset
);
void
sdsrange
(
sds
s
,
int
start
,
int
end
);
void
sdsupdatelen
(
sds
s
);
int
sdscmp
(
sds
s1
,
sds
s2
);
sds
*
sdssplitlen
(
char
*
s
,
int
len
,
char
*
sep
,
int
seplen
,
int
*
count
);
void
sdsclear
(
sds
s
);
int
sdscmp
(
const
sds
s1
,
const
sds
s2
);
sds
*
sdssplitlen
(
const
char
*
s
,
int
len
,
const
char
*
sep
,
int
seplen
,
int
*
count
);
void
sdsfreesplitres
(
sds
*
tokens
,
int
count
);
void
sdstolower
(
sds
s
);
void
sdstoupper
(
sds
s
);
sds
sdsfromlonglong
(
long
long
value
);
sds
sdscatrepr
(
sds
s
,
char
*
p
,
size_t
len
);
sds
*
sdssplitargs
(
char
*
line
,
int
*
argc
);
sds
sdscatrepr
(
sds
s
,
const
char
*
p
,
size_t
len
);
sds
*
sdssplitargs
(
const
char
*
line
,
int
*
argc
);
sds
sdsmapchars
(
sds
s
,
const
char
*
from
,
const
char
*
to
,
size_t
setlen
);
sds
sdsjoin
(
char
**
argv
,
int
argc
,
char
*
sep
,
size_t
seplen
);
sds
sdsjoinsds
(
sds
*
argv
,
int
argc
,
const
char
*
sep
,
size_t
seplen
);
/* Low level functions exposed to the user API */
sds
sdsMakeRoomFor
(
sds
s
,
size_t
addlen
);
void
sdsIncrLen
(
sds
s
,
int
incr
);
sds
sdsRemoveFreeSpace
(
sds
s
);
size_t
sdsAllocSize
(
sds
s
);
#endif
test.c
View file @
fa8bcca1
...
...
@@ -14,7 +14,8 @@
enum
connection_type
{
CONN_TCP
,
CONN_UNIX
CONN_UNIX
,
CONN_FD
};
struct
config
{
...
...
@@ -64,7 +65,7 @@ static redisContext *select_database(redisContext *c) {
return
c
;
}
static
void
disconnect
(
redisContext
*
c
)
{
static
int
disconnect
(
redisContext
*
c
,
int
keep_fd
)
{
redisReply
*
reply
;
/* Make sure we're on DB 9. */
...
...
@@ -75,8 +76,11 @@ static void disconnect(redisContext *c) {
assert
(
reply
!=
NULL
);
freeReplyObject
(
reply
);
/* Free the context as well. */
/* Free the context as well, but keep the fd if requested. */
if
(
keep_fd
)
return
redisFreeKeepFd
(
c
);
redisFree
(
c
);
return
-
1
;
}
static
redisContext
*
connect
(
struct
config
config
)
{
...
...
@@ -86,6 +90,14 @@ static redisContext *connect(struct config config) {
c
=
redisConnect
(
config
.
tcp
.
host
,
config
.
tcp
.
port
);
}
else
if
(
config
.
type
==
CONN_UNIX
)
{
c
=
redisConnectUnix
(
config
.
unix
.
path
);
}
else
if
(
config
.
type
==
CONN_FD
)
{
/* Create a dummy connection just to get an fd to inherit */
redisContext
*
dummy_ctx
=
redisConnectUnix
(
config
.
unix
.
path
);
if
(
dummy_ctx
)
{
int
fd
=
disconnect
(
dummy_ctx
,
1
);
printf
(
"Connecting to inherited fd %d
\n
"
,
fd
);
c
=
redisConnectFd
(
fd
);
}
}
else
{
assert
(
NULL
);
}
...
...
@@ -205,6 +217,28 @@ static void test_format_commands(void) {
free
(
cmd
);
}
static
void
test_append_formatted_commands
(
struct
config
config
)
{
redisContext
*
c
;
redisReply
*
reply
;
char
*
cmd
;
int
len
;
c
=
connect
(
config
);
test
(
"Append format command: "
);
len
=
redisFormatCommand
(
&
cmd
,
"SET foo bar"
);
test_cond
(
redisAppendFormattedCommand
(
c
,
cmd
,
len
)
==
REDIS_OK
);
assert
(
redisGetReply
(
c
,
(
void
*
)
&
reply
)
==
REDIS_OK
);
free
(
cmd
);
freeReplyObject
(
reply
);
disconnect
(
c
,
0
);
}
static
void
test_reply_reader
(
void
)
{
redisReader
*
reader
;
void
*
reply
;
...
...
@@ -293,6 +327,7 @@ static void test_blocking_connection_errors(void) {
(
strcmp
(
c
->
errstr
,
"Name or service not known"
)
==
0
||
strcmp
(
c
->
errstr
,
"Can't resolve: idontexist.local"
)
==
0
||
strcmp
(
c
->
errstr
,
"nodename nor servname provided, or not known"
)
==
0
||
strcmp
(
c
->
errstr
,
"No address associated with hostname"
)
==
0
||
strcmp
(
c
->
errstr
,
"no address associated with name"
)
==
0
));
redisFree
(
c
);
...
...
@@ -383,7 +418,7 @@ static void test_blocking_connection(struct config config) {
strcasecmp
(
reply
->
element
[
1
]
->
str
,
"pong"
)
==
0
);
freeReplyObject
(
reply
);
disconnect
(
c
);
disconnect
(
c
,
0
);
}
static
void
test_blocking_io_errors
(
struct
config
config
)
{
...
...
@@ -523,7 +558,7 @@ static void test_throughput(struct config config) {
free
(
replies
);
printf
(
"
\t
(%dx LRANGE with 500 elements (pipelined): %.3fs)
\n
"
,
num
,
(
t2
-
t1
)
/
1000000
.
0
);
disconnect
(
c
);
disconnect
(
c
,
0
);
}
// static long __test_callback_flags = 0;
...
...
@@ -636,6 +671,7 @@ int main(int argc, char **argv) {
}
};
int
throughput
=
1
;
int
test_inherit_fd
=
1
;
/* Ignore broken pipe signal (for I/O error tests). */
signal
(
SIGPIPE
,
SIG_IGN
);
...
...
@@ -654,6 +690,8 @@ int main(int argc, char **argv) {
cfg
.
unix
.
path
=
argv
[
0
];
}
else
if
(
argc
>=
1
&&
!
strcmp
(
argv
[
0
],
"--skip-throughput"
))
{
throughput
=
0
;
}
else
if
(
argc
>=
1
&&
!
strcmp
(
argv
[
0
],
"--skip-inherit-fd"
))
{
test_inherit_fd
=
0
;
}
else
{
fprintf
(
stderr
,
"Invalid argument: %s
\n
"
,
argv
[
0
]);
exit
(
1
);
...
...
@@ -670,6 +708,7 @@ int main(int argc, char **argv) {
test_blocking_connection
(
cfg
);
test_blocking_io_errors
(
cfg
);
test_invalid_timeout_errors
(
cfg
);
test_append_formatted_commands
(
cfg
);
if
(
throughput
)
test_throughput
(
cfg
);
printf
(
"
\n
Testing against Unix socket connection (%s):
\n
"
,
cfg
.
unix
.
path
);
...
...
@@ -678,6 +717,12 @@ int main(int argc, char **argv) {
test_blocking_io_errors
(
cfg
);
if
(
throughput
)
test_throughput
(
cfg
);
if
(
test_inherit_fd
)
{
printf
(
"
\n
Testing against inherited fd (%s):
\n
"
,
cfg
.
unix
.
path
);
cfg
.
type
=
CONN_FD
;
test_blocking_connection
(
cfg
);
}
if
(
fails
)
{
printf
(
"*** %d TESTS FAILED ***
\n
"
,
fails
);
return
1
;
...
...
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