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
Show whitespace changes
Inline
Side-by-side
hiredis.c
View file @
457cdbf7
...
...
@@ -37,6 +37,17 @@
#include "anet.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
*
createReplyObject
(
int
type
,
sds
reply
);
...
...
@@ -93,107 +104,218 @@ static redisReply *redisIOError(void) {
return
createReplyObject
(
REDIS_REPLY_ERROR
,
sdsnew
(
"I/O error"
));
}
/* In a real high performance C client this should be bufferized */
static
sds
redisReadLine
(
int
fd
)
{
sds
line
=
sdsempty
();
while
(
1
)
{
char
c
;
ssize_t
ret
;
ret
=
read
(
fd
,
&
c
,
1
);
if
(
ret
==
-
1
)
{
sdsfree
(
line
);
return
NULL
;
}
else
if
((
ret
==
0
)
||
(
c
==
'\n'
))
{
break
;
}
else
{
line
=
sdscatlen
(
line
,
&
c
,
1
);
}
static
char
*
readBytes
(
redisReader
*
r
,
int
bytes
)
{
char
*
p
;
if
(
r
->
len
-
r
->
pos
>=
bytes
)
{
p
=
r
->
buf
+
r
->
pos
;
r
->
pos
+=
bytes
;
return
p
;
}
return
sdstrim
(
line
,
"
\r\n
"
)
;
return
NULL
;
}
static
redisReply
*
redisReadSingleLineReply
(
int
fd
,
int
type
)
{
sds
buf
=
redisReadLine
(
fd
);
if
(
buf
==
NULL
)
return
redisIOError
();
return
createReplyObject
(
type
,
buf
);
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
;
}
static
redisReply
*
redisReadIntegerReply
(
int
fd
)
{
sds
buf
=
redisReadLine
(
fd
);
redisReply
*
r
=
malloc
(
sizeof
(
*
r
));
static
int
processLineItem
(
redisReader
*
r
)
{
redisReply
*
cur
=
r
->
rlist
[
r
->
rpos
];
char
*
p
;
int
len
;
if
(
r
==
NULL
)
redisOOM
();
if
(
buf
==
NULL
)
{
free
(
r
);
return
redisIOError
();
if
((
p
=
readLine
(
r
,
&
len
))
!=
NULL
)
{
if
(
cur
->
type
==
REDIS_REPLY_INTEGER
)
{
cur
->
integer
=
strtoll
(
p
,
NULL
,
10
);
}
else
{
cur
->
reply
=
sdsnewlen
(
p
,
len
);
}
r
->
type
=
REDIS_REPLY_INTEGER
;
r
->
integer
=
strtoll
(
buf
,
NULL
,
10
);
sdsfree
(
buf
);
return
r
;
}
static
redisReply
*
redisReadBulkReply
(
int
fd
)
{
sds
replylen
=
redisReadLine
(
fd
);
sds
buf
;
char
crlf
[
2
];
int
bulklen
;
if
(
replylen
==
NULL
)
return
redisIOError
();
bulklen
=
atoi
(
replylen
);
sdsfree
(
replylen
);
if
(
bulklen
==
-
1
)
return
createReplyObject
(
REDIS_REPLY_NIL
,
sdsempty
());
buf
=
sdsnewlen
(
NULL
,
bulklen
);
anetRead
(
fd
,
buf
,
bulklen
);
anetRead
(
fd
,
crlf
,
2
);
return
createReplyObject
(
REDIS_REPLY_STRING
,
buf
);
/* for API compat, set STATUS to STRING */
if
(
cur
->
type
==
REDIS_REPLY_STATUS
)
cur
->
type
=
REDIS_REPLY_STRING
;
r
->
rpos
++
;
return
0
;
}
return
-
1
;
}
static
redisReply
*
redisReadMultiBulkReply
(
int
fd
)
{
sds
replylen
=
redisReadLine
(
fd
);
long
elements
,
j
;
redisReply
*
r
;
static
int
processBulkItem
(
redisReader
*
r
)
{
redisReply
*
cur
=
r
->
rlist
[
r
->
rpos
];
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
(
replylen
==
NULL
)
return
redisIOError
();
elements
=
strtol
(
replylen
,
NULL
,
10
);
sdsfree
(
replylen
);
len
=
sdslen
(
cur
->
reply
);
/* add two bytes for crlf */
if
((
p
=
readBytes
(
r
,
len
+
2
))
!=
NULL
)
{
memcpy
(
cur
->
reply
,
p
,
len
);
r
->
rpos
++
;
return
0
;
}
return
-
1
;
}
if
(
elements
==
-
1
)
return
createReplyObject
(
REDIS_REPLY_NIL
,
sdsempty
());
static
int
processMultiBulkItem
(
redisReader
*
r
)
{
redisReply
*
cur
=
r
->
rlist
[
r
->
rpos
];
char
*
p
;
int
elements
,
j
;
if
((
p
=
readLine
(
r
,
NULL
))
!=
NULL
)
{
elements
=
atoi
(
p
);
if
(
elements
==
-
1
)
{
/* empty */
cur
->
type
=
REDIS_REPLY_NIL
;
cur
->
reply
=
sdsempty
();
r
->
rpos
++
;
return
0
;
}
}
else
{
return
-
1
;
}
if
((
r
=
malloc
(
sizeof
(
*
r
)))
==
NULL
)
redisOOM
();
r
->
type
=
REDIS_REPLY_ARRAY
;
r
->
elements
=
elements
;
if
((
r
->
element
=
malloc
(
sizeof
(
*
r
)
*
elements
))
==
NULL
)
redisOOM
();
for
(
j
=
0
;
j
<
elements
;
j
++
)
r
->
element
[
j
]
=
redisReadReply
(
fd
);
return
r
;
cur
->
elements
=
elements
;
r
->
rlen
+=
elements
;
r
->
rpos
++
;
/* 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
;
}
static
redisReply
*
redisReadReply
(
int
fd
)
{
char
type
;
static
int
processItem
(
redisReader
*
r
)
{
redisReply
*
cur
=
r
->
rlist
[
r
->
rpos
];
char
*
p
;
if
(
anetRead
(
fd
,
&
type
,
1
)
<=
0
)
return
redisIOError
();
switch
(
type
)
{
/* check if we need to read type */
if
(
cur
->
type
<
0
)
{
if
((
p
=
readBytes
(
r
,
1
))
!=
NULL
)
{
switch
(
p
[
0
])
{
case
'-'
:
return
redisReadSingleLineReply
(
fd
,
REDIS_REPLY_ERROR
);
cur
->
type
=
REDIS_REPLY_ERROR
;
break
;
case
'+'
:
return
redisReadSingleLineReply
(
fd
,
REDIS_REPLY_STRING
);
cur
->
type
=
REDIS_REPLY_STATUS
;
break
;
case
':'
:
return
redisReadIntegerReply
(
fd
);
cur
->
type
=
REDIS_REPLY_INTEGER
;
break
;
case
'$'
:
return
redisReadBulkReply
(
fd
);
cur
->
type
=
REDIS_REPLY_STRING
;
break
;
case
'*'
:
return
redisReadMultiBulkReply
(
fd
);
cur
->
type
=
REDIS_REPLY_ARRAY
;
break
;
default:
printf
(
"protocol error, got '%c' as reply type byte
\n
"
,
type
);
printf
(
"protocol error, got '%c' as reply type byte
\n
"
,
p
[
0
]
);
exit
(
1
);
}
}
else
{
/* could not consume 1 byte */
return
-
1
;
}
}
/* process typed item */
switch
(
cur
->
type
)
{
case
REDIS_REPLY_ERROR
:
case
REDIS_REPLY_STATUS
:
case
REDIS_REPLY_INTEGER
:
return
processLineItem
(
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
)
{
redisReader
r
;
int
bytes
;
/* setup read buffer */
r
.
buf
=
malloc
(
READ_BUFFER_SIZE
+
1
);
r
.
len
=
READ_BUFFER_SIZE
;
r
.
avail
=
0
;
r
.
pos
=
0
;
/* setup list of items to process */
r
.
rlist
=
malloc
(
sizeof
(
redisReply
*
));
r
.
rlist
[
0
]
=
createReplyObject
(
-
1
,
NULL
);
r
.
rlen
=
1
;
r
.
rpos
=
0
;
while
(
r
.
rpos
<
r
.
rlen
)
{
/* 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
...
...
hiredis.h
View file @
457cdbf7
...
...
@@ -35,6 +35,7 @@
#define REDIS_REPLY_ARRAY 2
#define REDIS_REPLY_INTEGER 3
#define REDIS_REPLY_NIL 4
#define REDIS_REPLY_STATUS 5
#include "sds.h"
...
...
test.c
View file @
457cdbf7
...
...
@@ -99,6 +99,23 @@ int main(void) {
!
memcmp
(
reply
->
element
[
1
]
->
reply
,
"foo"
,
3
))
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
)
{
printf
(
"ALL TESTS PASSED
\n
"
);
}
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