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
457cdbf7
Commit
457cdbf7
authored
Sep 19, 2010
by
Pieter Noordhuis
Browse files
Rewrite reply parsing to use a read buffer
parent
66036d11
Changes
3
Hide whitespace changes
Inline
Side-by-side
hiredis.c
View file @
457cdbf7
...
@@ -37,6 +37,17 @@
...
@@ -37,6 +37,17 @@
#include "anet.h"
#include "anet.h"
#include "sds.h"
#include "sds.h"
typedef
struct
redisReader
{
char
*
buf
;
/* read buffer */
int
len
;
/* buffer length */
int
avail
;
/* available bytes for consumption */
int
pos
;
/* buffer cursor */
redisReply
**
rlist
;
/* list of items to process */
int
rlen
;
/* list length */
int
rpos
;
/* list cursor */
}
redisReader
;
static
redisReply
*
redisReadReply
(
int
fd
);
static
redisReply
*
redisReadReply
(
int
fd
);
static
redisReply
*
createReplyObject
(
int
type
,
sds
reply
);
static
redisReply
*
createReplyObject
(
int
type
,
sds
reply
);
...
@@ -93,107 +104,218 @@ static redisReply *redisIOError(void) {
...
@@ -93,107 +104,218 @@ static redisReply *redisIOError(void) {
return
createReplyObject
(
REDIS_REPLY_ERROR
,
sdsnew
(
"I/O error"
));
return
createReplyObject
(
REDIS_REPLY_ERROR
,
sdsnew
(
"I/O error"
));
}
}
/* In a real high performance C client this should be bufferized */
static
char
*
readBytes
(
redisReader
*
r
,
int
bytes
)
{
static
sds
redisReadLine
(
int
fd
)
{
char
*
p
;
sds
line
=
sdsempty
();
if
(
r
->
len
-
r
->
pos
>=
bytes
)
{
p
=
r
->
buf
+
r
->
pos
;
r
->
pos
+=
bytes
;
return
p
;
}
return
NULL
;
}
static
char
*
readLine
(
redisReader
*
r
,
int
*
_len
)
{
char
*
p
,
*
s
=
strstr
(
r
->
buf
+
r
->
pos
,
"
\r\n
"
);
int
len
;
if
(
s
!=
NULL
)
{
p
=
r
->
buf
+
r
->
pos
;
len
=
s
-
(
r
->
buf
+
r
->
pos
);
r
->
pos
+=
len
+
2
;
/* skip \r\n */
if
(
_len
)
*
_len
=
len
;
return
p
;
}
return
NULL
;
}
while
(
1
)
{
static
int
processLineItem
(
redisReader
*
r
)
{
char
c
;
redisReply
*
cur
=
r
->
rlist
[
r
->
rpos
];
ssize_t
ret
;
char
*
p
;
int
len
;
ret
=
read
(
fd
,
&
c
,
1
);
if
((
p
=
readLine
(
r
,
&
len
))
!=
NULL
)
{
if
(
ret
==
-
1
)
{
if
(
cur
->
type
==
REDIS_REPLY_INTEGER
)
{
sdsfree
(
line
);
cur
->
integer
=
strtoll
(
p
,
NULL
,
10
);
return
NULL
;
}
else
if
((
ret
==
0
)
||
(
c
==
'\n'
))
{
break
;
}
else
{
}
else
{
line
=
sds
cat
len
(
line
,
&
c
,
1
);
cur
->
reply
=
sds
new
len
(
p
,
len
);
}
}
}
return
sdstrim
(
line
,
"
\r\n
"
);
}
static
redisReply
*
redisReadSingleLineReply
(
int
fd
,
int
type
)
{
/* for API compat, set STATUS to STRING */
sds
buf
=
redisReadLine
(
fd
);
if
(
cur
->
type
==
REDIS_REPLY_STATUS
)
cur
->
type
=
REDIS_REPLY_STRING
;
if
(
buf
==
NULL
)
return
redisIOError
();
return
createReplyObject
(
type
,
buf
);
r
->
rpos
++
;
return
0
;
}
return
-
1
;
}
}
static
redisReply
*
redisReadIntegerReply
(
int
fd
)
{
static
int
processBulkItem
(
redisReader
*
r
)
{
sds
buf
=
redisReadLine
(
fd
);
redisReply
*
cur
=
r
->
rlist
[
r
->
rpos
];
redisReply
*
r
=
malloc
(
sizeof
(
*
r
));
char
*
p
;
int
len
;
if
(
cur
->
reply
==
NULL
)
{
if
((
p
=
readLine
(
r
,
NULL
))
!=
NULL
)
{
len
=
atoi
(
p
);
if
(
len
==
-
1
)
{
/* nil means this item is done */
cur
->
type
=
REDIS_REPLY_NIL
;
cur
->
reply
=
sdsempty
();
r
->
rpos
++
;
return
0
;
}
else
{
cur
->
reply
=
sdsnewlen
(
NULL
,
len
);
}
}
else
{
return
-
1
;
}
}
if
(
r
==
NULL
)
redisOOM
();
len
=
sdslen
(
cur
->
reply
);
if
(
buf
==
NULL
)
{
/* add two bytes for crlf */
free
(
r
);
if
((
p
=
readBytes
(
r
,
len
+
2
))
!=
NULL
)
{
return
redisIOError
();
memcpy
(
cur
->
reply
,
p
,
len
);
r
->
rpos
++
;
return
0
;
}
}
r
->
type
=
REDIS_REPLY_INTEGER
;
return
-
1
;
r
->
integer
=
strtoll
(
buf
,
NULL
,
10
);
sdsfree
(
buf
);
return
r
;
}
}
static
redisReply
*
redisReadBulkReply
(
int
fd
)
{
static
int
processMultiBulkItem
(
redisReader
*
r
)
{
sds
replylen
=
redisReadLine
(
fd
)
;
redisReply
*
cur
=
r
->
rlist
[
r
->
rpos
]
;
sds
buf
;
char
*
p
;
char
crlf
[
2
]
;
int
elements
,
j
;
int
bulklen
;
if
((
p
=
readLine
(
r
,
NULL
))
!=
NULL
)
{
if
(
replylen
==
NULL
)
return
redisIOError
(
);
elements
=
atoi
(
p
);
bulklen
=
atoi
(
replylen
);
if
(
elements
==
-
1
)
{
sdsfree
(
replylen
);
/* empty */
if
(
bulklen
==
-
1
)
cur
->
type
=
REDIS_REPLY_NIL
;
return
createReplyObject
(
REDIS_REPLY_NIL
,
sdsempty
()
)
;
cur
->
reply
=
sdsempty
();
r
->
rpos
++
;
buf
=
sdsnewlen
(
NULL
,
bulklen
)
;
return
0
;
anetRead
(
fd
,
buf
,
bulklen
);
}
anetRead
(
fd
,
crlf
,
2
);
}
else
{
return
createReplyObject
(
REDIS_REPLY_STRING
,
buf
)
;
return
-
1
;
}
}
static
redisReply
*
redisReadMultiBulkReply
(
int
fd
)
{
cur
->
elements
=
elements
;
sds
replylen
=
redisReadLine
(
fd
);
r
->
rlen
+=
elements
;
long
elements
,
j
;
r
->
rpos
++
;
redisReply
*
r
;
/* create placeholder items */
if
((
cur
->
element
=
malloc
(
sizeof
(
redisReply
*
)
*
elements
))
==
NULL
)
redisOOM
();
if
((
r
->
rlist
=
realloc
(
r
->
rlist
,
sizeof
(
redisReply
*
)
*
r
->
rlen
))
==
NULL
)
redisOOM
();
/* move existing items backwards */
memmove
(
&
(
r
->
rlist
[
r
->
rpos
+
elements
]),
&
(
r
->
rlist
[
r
->
rpos
]),
(
r
->
rlen
-
(
r
->
rpos
+
elements
))
*
sizeof
(
redisReply
*
));
/* populate item list */
for
(
j
=
0
;
j
<
elements
;
j
++
)
{
cur
->
element
[
j
]
=
createReplyObject
(
-
1
,
NULL
);
r
->
rlist
[
r
->
rpos
+
j
]
=
cur
->
element
[
j
];
}
return
0
;
}
if
(
replylen
==
NULL
)
return
redisIOError
();
static
int
processItem
(
redisReader
*
r
)
{
elements
=
strtol
(
replylen
,
NULL
,
10
)
;
redisReply
*
cur
=
r
->
rlist
[
r
->
rpos
]
;
sdsfree
(
replylen
)
;
char
*
p
;
if
(
elements
==
-
1
)
/* check if we need to read type */
return
createReplyObject
(
REDIS_REPLY_NIL
,
sdsempty
());
if
(
cur
->
type
<
0
)
{
if
((
p
=
readBytes
(
r
,
1
))
!=
NULL
)
{
switch
(
p
[
0
])
{
case
'-'
:
cur
->
type
=
REDIS_REPLY_ERROR
;
break
;
case
'+'
:
cur
->
type
=
REDIS_REPLY_STATUS
;
break
;
case
':'
:
cur
->
type
=
REDIS_REPLY_INTEGER
;
break
;
case
'$'
:
cur
->
type
=
REDIS_REPLY_STRING
;
break
;
case
'*'
:
cur
->
type
=
REDIS_REPLY_ARRAY
;
break
;
default:
printf
(
"protocol error, got '%c' as reply type byte
\n
"
,
p
[
0
]);
exit
(
1
);
}
}
else
{
/* could not consume 1 byte */
return
-
1
;
}
}
if
((
r
=
malloc
(
sizeof
(
*
r
)))
==
NULL
)
redisOOM
();
/* process typed item */
r
->
type
=
REDIS_REPLY_ARRAY
;
switch
(
cur
->
type
)
{
r
->
elements
=
elements
;
case
REDIS_REPLY_ERROR
:
if
((
r
->
element
=
malloc
(
sizeof
(
*
r
)
*
elements
))
==
NULL
)
redisOOM
();
case
REDIS_REPLY_STATUS
:
for
(
j
=
0
;
j
<
elements
;
j
++
)
case
REDIS_REPLY_INTEGER
:
r
->
element
[
j
]
=
redisReadReply
(
fd
);
return
processLineItem
(
r
);
return
r
;
case
REDIS_REPLY_STRING
:
return
processBulkItem
(
r
);
case
REDIS_REPLY_ARRAY
:
return
processMultiBulkItem
(
r
);
default:
printf
(
"unknown item type: %d
\n
"
,
cur
->
type
);
exit
(
1
);
}
}
}
#define READ_BUFFER_SIZE 2048
static
redisReply
*
redisReadReply
(
int
fd
)
{
static
redisReply
*
redisReadReply
(
int
fd
)
{
char
type
;
redisReader
r
;
int
bytes
;
if
(
anetRead
(
fd
,
&
type
,
1
)
<=
0
)
return
redisIOError
();
switch
(
type
)
{
/* setup read buffer */
case
'-'
:
r
.
buf
=
malloc
(
READ_BUFFER_SIZE
+
1
);
return
redisReadSingleLineReply
(
fd
,
REDIS_REPLY_ERROR
);
r
.
len
=
READ_BUFFER_SIZE
;
case
'+'
:
r
.
avail
=
0
;
return
redisReadSingleLineReply
(
fd
,
REDIS_REPLY_STRING
);
r
.
pos
=
0
;
case
':'
:
return
redisReadIntegerReply
(
fd
);
/* setup list of items to process */
case
'$'
:
r
.
rlist
=
malloc
(
sizeof
(
redisReply
*
));
return
redisReadBulkReply
(
fd
);
r
.
rlist
[
0
]
=
createReplyObject
(
-
1
,
NULL
);
case
'*'
:
r
.
rlen
=
1
;
return
redisReadMultiBulkReply
(
fd
);
r
.
rpos
=
0
;
default:
printf
(
"protocol error, got '%c' as reply type byte
\n
"
,
type
);
while
(
r
.
rpos
<
r
.
rlen
)
{
exit
(
1
);
/* discard the buffer upto pos */
if
(
r
.
pos
>
0
)
{
memmove
(
r
.
buf
,
r
.
buf
+
r
.
pos
,
r
.
len
-
r
.
pos
);
r
.
avail
-=
r
.
pos
;
r
.
pos
=
0
;
}
/* make sure there is room for at least BUFFER_SIZE */
if
(
r
.
len
-
r
.
avail
<
READ_BUFFER_SIZE
)
{
r
.
buf
=
realloc
(
r
.
buf
,
r
.
avail
+
READ_BUFFER_SIZE
+
1
);
r
.
len
=
r
.
avail
+
READ_BUFFER_SIZE
;
}
/* read from socket into buffer */
if
((
bytes
=
read
(
fd
,
r
.
buf
+
r
.
avail
,
READ_BUFFER_SIZE
))
<=
0
)
return
redisIOError
();
r
.
avail
+=
bytes
;
r
.
buf
[
r
.
avail
]
=
'\0'
;
/* process items in reply */
while
(
r
.
rpos
<
r
.
rlen
)
if
(
processItem
(
&
r
)
<
0
)
break
;
}
}
free
(
r
.
buf
);
free
(
r
.
rlist
);
return
r
.
rlist
[
0
];
}
}
/* Helper function for redisCommand(). It's used to append the next argument
/* Helper function for redisCommand(). It's used to append the next argument
...
...
hiredis.h
View file @
457cdbf7
...
@@ -35,6 +35,7 @@
...
@@ -35,6 +35,7 @@
#define REDIS_REPLY_ARRAY 2
#define REDIS_REPLY_ARRAY 2
#define REDIS_REPLY_INTEGER 3
#define REDIS_REPLY_INTEGER 3
#define REDIS_REPLY_NIL 4
#define REDIS_REPLY_NIL 4
#define REDIS_REPLY_STATUS 5
#include "sds.h"
#include "sds.h"
...
...
test.c
View file @
457cdbf7
...
@@ -99,6 +99,23 @@ int main(void) {
...
@@ -99,6 +99,23 @@ int main(void) {
!
memcmp
(
reply
->
element
[
1
]
->
reply
,
"foo"
,
3
))
!
memcmp
(
reply
->
element
[
1
]
->
reply
,
"foo"
,
3
))
freeReplyObject
(
reply
);
freeReplyObject
(
reply
);
/* test 9 (m/e with multi bulk reply *before* other reply).
* specifically test ordering of reply items to parse. */
printf
(
"#10 can handle nested multi bulk replies: "
);
freeReplyObject
(
redisCommand
(
fd
,
"MULTI"
));
freeReplyObject
(
redisCommand
(
fd
,
"LRANGE mylist 0 -1"
));
freeReplyObject
(
redisCommand
(
fd
,
"PING"
));
reply
=
(
redisCommand
(
fd
,
"EXEC"
));
test_cond
(
reply
->
type
==
REDIS_REPLY_ARRAY
&&
reply
->
elements
==
2
&&
reply
->
element
[
0
]
->
type
==
REDIS_REPLY_ARRAY
&&
reply
->
element
[
0
]
->
elements
==
2
&&
!
memcmp
(
reply
->
element
[
0
]
->
element
[
0
]
->
reply
,
"bar"
,
3
)
&&
!
memcmp
(
reply
->
element
[
0
]
->
element
[
1
]
->
reply
,
"foo"
,
3
)
&&
reply
->
element
[
1
]
->
type
==
REDIS_REPLY_STRING
&&
strcasecmp
(
reply
->
element
[
1
]
->
reply
,
"pong"
)
==
0
);
freeReplyObject
(
reply
);
if
(
fails
==
0
)
{
if
(
fails
==
0
)
{
printf
(
"ALL TESTS PASSED
\n
"
);
printf
(
"ALL TESTS PASSED
\n
"
);
}
else
{
}
else
{
...
...
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