Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
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