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
3c378b84
Commit
3c378b84
authored
Jul 07, 2011
by
Pieter Noordhuis
Browse files
Basic error facility
parent
a25cd930
Changes
3
Show whitespace changes
Inline
Side-by-side
parser.c
View file @
3c378b84
...
...
@@ -5,12 +5,18 @@
#include <assert.h>
#include "parser.h"
#define ERRNO(code) REDIS_PARSER_##code
#define SET_ERRNO(code) do { \
parser->errno = ERRNO(code); \
} while(0)
/* The redis_protocol_t argument to the callback function must be provided by
* the caller because "ISO C99 requires rest arguments to be used". */
#define CALLBACK(X, ...) do { \
if (callbacks && callbacks->on_##X) { \
if (callbacks->on_##X(parser, __VA_ARGS__) == 0) { \
return pos-buf; \
SET_ERRNO(ERR_CALLBACK); \
goto error; \
} \
} \
} while(0)
...
...
@@ -26,27 +32,33 @@
__tmp->data = NULL; \
} while(0)
#define REDIS_PARSER_STATES(X)
\
X(unused)
/* = 0 in enum */
\
X(type_char)
\
X(integer_start)
\
X(integer_pos_19)
\
X(integer_pos_09)
\
X(integer_neg_19)
\
X(integer_neg_09)
\
X(integer_cr)
\
X(integer_lf)
\
X(bulk)
\
X(bulk_cr)
\
X(bulk_lf)
\
X(line)
\
X(line_lf)
\
#define
_ENUM
_GEN(name) s_##name,
#define REDIS_PARSER_STATES(
_
X) \
_
X(unused)
/* = 0 in enum */
\
_
X(type_char) \
_
X(integer_start) \
_
X(integer_pos_19) \
_
X(integer_pos_09) \
_
X(integer_neg_19) \
_
X(integer_neg_09) \
_
X(integer_cr) \
_
X(integer_lf) \
_
X(bulk) \
_
X(bulk_cr) \
_
X(bulk_lf) \
_
X(line) \
_
X(line_lf) \
#define _GEN(name) s_##name,
enum
state
{
REDIS_PARSER_STATES
(
_
ENUM_
GEN
)
REDIS_PARSER_STATES
(
_GEN
)
};
#undef _ENUM_GEN
#undef _GEN
#define _GEN(code, description) description,
static
const
char
*
errno_str
[]
=
{
REDIS_PARSER_ERRORS
(
_GEN
)
};
#undef _GEN
#define STATE(st) \
case s_##st: \
...
...
@@ -184,6 +196,7 @@ size_t redis_parser_execute(redis_parser_t *parser, redis_protocol_t **dst, cons
assert
(
NULL
);
}
SET_ERRNO
(
ERR_INVALID_TYPE
);
goto
error
;
}
...
...
@@ -213,6 +226,7 @@ size_t redis_parser_execute(redis_parser_t *parser, redis_protocol_t **dst, cons
ADVANCE_AND_MOVE
(
integer_cr
);
}
SET_ERRNO
(
ERR_INVALID_INT
);
goto
error
;
}
...
...
@@ -224,6 +238,7 @@ size_t redis_parser_execute(redis_parser_t *parser, redis_protocol_t **dst, cons
ADVANCE_AND_MOVE
(
integer_pos_09
);
}
SET_ERRNO
(
ERR_INVALID_INT
);
goto
error
;
}
...
...
@@ -231,11 +246,15 @@ size_t redis_parser_execute(redis_parser_t *parser, redis_protocol_t **dst, cons
char
ch
=
*
pos
;
if
(
ch
>=
'0'
&&
ch
<=
'9'
)
{
if
(
i64
.
ui64
>
((
uint64_t
)
INT64_MAX
/
10
))
/* Overflow */
if
(
i64
.
ui64
>
((
uint64_t
)
INT64_MAX
/
10
))
{
/* Overflow */
SET_ERRNO
(
ERR_OVERFLOW
);
goto
error
;
}
i64
.
ui64
*=
10
;
if
(
i64
.
ui64
>
((
uint64_t
)
INT64_MAX
-
(
ch
-
'0'
)))
/* Overflow */
if
(
i64
.
ui64
>
((
uint64_t
)
INT64_MAX
-
(
ch
-
'0'
)))
{
/* Overflow */
SET_ERRNO
(
ERR_OVERFLOW
);
goto
error
;
}
i64
.
ui64
+=
ch
-
'0'
;
ADVANCE_AND_MOVE
(
integer_pos_09
);
}
else
if
(
ch
==
'\r'
)
{
...
...
@@ -243,6 +262,7 @@ size_t redis_parser_execute(redis_parser_t *parser, redis_protocol_t **dst, cons
ADVANCE_AND_MOVE
(
integer_lf
);
}
SET_ERRNO
(
ERR_INVALID_INT
);
goto
error
;
}
...
...
@@ -254,6 +274,7 @@ size_t redis_parser_execute(redis_parser_t *parser, redis_protocol_t **dst, cons
ADVANCE_AND_MOVE
(
integer_neg_09
);
}
SET_ERRNO
(
ERR_INVALID_INT
);
goto
error
;
}
...
...
@@ -261,11 +282,15 @@ size_t redis_parser_execute(redis_parser_t *parser, redis_protocol_t **dst, cons
char
ch
=
*
pos
;
if
(
ch
>=
'0'
&&
ch
<=
'9'
)
{
if
(
i64
.
ui64
>
(((
uint64_t
)
-
(
INT64_MIN
+
1
)
+
1
)
/
10
))
/* Overflow */
if
(
i64
.
ui64
>
(((
uint64_t
)
-
(
INT64_MIN
+
1
)
+
1
)
/
10
))
{
/* Overflow */
SET_ERRNO
(
ERR_OVERFLOW
);
goto
error
;
}
i64
.
ui64
*=
10
;
if
(
i64
.
ui64
>
(((
uint64_t
)
-
(
INT64_MIN
+
1
)
+
1
)
-
(
ch
-
'0'
)))
/* Overflow */
if
(
i64
.
ui64
>
(((
uint64_t
)
-
(
INT64_MIN
+
1
)
+
1
)
-
(
ch
-
'0'
)))
{
/* Overflow */
SET_ERRNO
(
ERR_OVERFLOW
);
goto
error
;
}
i64
.
ui64
+=
ch
-
'0'
;
ADVANCE_AND_MOVE
(
integer_neg_09
);
}
else
if
(
ch
==
'\r'
)
{
...
...
@@ -273,6 +298,7 @@ size_t redis_parser_execute(redis_parser_t *parser, redis_protocol_t **dst, cons
ADVANCE_AND_MOVE
(
integer_lf
);
}
SET_ERRNO
(
ERR_INVALID_INT
);
goto
error
;
}
...
...
@@ -281,11 +307,13 @@ size_t redis_parser_execute(redis_parser_t *parser, redis_protocol_t **dst, cons
ADVANCE_AND_MOVE
(
integer_lf
);
}
SET_ERRNO
(
ERR_EXPECTED_CR
);
goto
error
;
}
STATE
(
integer_lf
)
{
if
(
*
pos
!=
'\n'
)
{
SET_ERRNO
(
ERR_EXPECTED_LF
);
goto
error
;
}
...
...
@@ -355,6 +383,7 @@ size_t redis_parser_execute(redis_parser_t *parser, redis_protocol_t **dst, cons
ADVANCE_AND_MOVE
(
bulk_lf
);
}
SET_ERRNO
(
ERR_EXPECTED_CR
);
goto
error
;
}
...
...
@@ -363,6 +392,7 @@ size_t redis_parser_execute(redis_parser_t *parser, redis_protocol_t **dst, cons
goto
done
;
}
SET_ERRNO
(
ERR_EXPECTED_LF
);
goto
error
;
}
}
...
...
@@ -409,6 +439,19 @@ finalize:
error:
/* TODO: set some kind of error */
goto
finalize
;
if
(
parser
->
errno
==
ERRNO
(
OK
))
{
SET_ERRNO
(
ERR_UNKNOWN
);
}
return
pos
-
buf
;
}
redis_parser_errno_t
redis_parser_errno
(
redis_parser_t
*
parser
)
{
return
parser
->
errno
;
}
const
char
*
redis_parser_strerror
(
redis_parser_errno_t
errno
)
{
if
(
errno
<
(
sizeof
(
errno_str
)
/
sizeof
(
errno_str
[
0
])))
return
errno_str
[
errno
];
return
NULL
;
}
parser.h
View file @
3c378b84
...
...
@@ -26,6 +26,22 @@ struct redis_parser_cb_s {
redis_nil_cb
on_nil
;
};
#define REDIS_PARSER_ERRORS(_X) \
_X(OK, NULL)
/* = 0 in enum */
\
_X(ERR_UNKNOWN, "unknown") \
_X(ERR_CALLBACK, "callback failed") \
_X(ERR_INVALID_TYPE, "invalid type character") \
_X(ERR_INVALID_INT, "invalid integer character") \
_X(ERR_OVERFLOW, "overflow") \
_X(ERR_EXPECTED_CR, "expected \\r") \
_X(ERR_EXPECTED_LF, "expected \\n") \
#define _REDIS_PARSER_ERRNO_ENUM_GEN(code, description) REDIS_PARSER_##code,
typedef
enum
redis_parser_errno_e
{
REDIS_PARSER_ERRORS
(
_REDIS_PARSER_ERRNO_ENUM_GEN
)
}
redis_parser_errno_t
;
#undef _REDIS_PARSER_ERRNO_ENUM_GEN
struct
redis_protocol_s
{
unsigned
char
type
;
/* payload type */
const
redis_protocol_t
*
parent
;
/* when nested, parent object */
...
...
@@ -50,6 +66,7 @@ struct redis_parser_s {
/* private: parser state */
unsigned
char
state
;
unsigned
char
errno
;
/* private: temporary integer (integer reply, bulk length) */
struct
redis_parser_int64_s
{
...
...
@@ -60,5 +77,7 @@ struct redis_parser_s {
void
redis_parser_init
(
redis_parser_t
*
parser
,
const
redis_parser_cb_t
*
callbacks
);
size_t
redis_parser_execute
(
redis_parser_t
*
parser
,
redis_protocol_t
**
dst
,
const
char
*
buf
,
size_t
len
);
redis_parser_errno_t
redis_parser_errno
(
redis_parser_t
*
parser
);
const
char
*
redis_parser_strerror
(
redis_parser_errno_t
errno
);
#endif // __REDIS_PARSER_H
test/parser.c
View file @
3c378b84
...
...
@@ -274,36 +274,42 @@ void test_integer(redis_parser_t *p) {
RESET_PARSER_T
(
p
);
assert
(
redis_parser_execute
(
p
,
&
res
,
buf
,
strlen
(
buf
))
==
2
);
assert
(
res
==
NULL
);
assert
(
redis_parser_errno
(
p
)
==
REDIS_PARSER_ERR_INVALID_INT
);
/* Signed zero, negative */
buf
=
":-0
\r\n
"
;
RESET_PARSER_T
(
p
);
assert
(
redis_parser_execute
(
p
,
&
res
,
buf
,
strlen
(
buf
))
==
2
);
assert
(
res
==
NULL
);
assert
(
redis_parser_errno
(
p
)
==
REDIS_PARSER_ERR_INVALID_INT
);
/* Start with 0 */
buf
=
":0123
\r\n
"
;
RESET_PARSER_T
(
p
);
assert
(
redis_parser_execute
(
p
,
&
res
,
buf
,
strlen
(
buf
))
==
2
);
assert
(
res
==
NULL
);
assert
(
redis_parser_errno
(
p
)
==
REDIS_PARSER_ERR_EXPECTED_CR
);
/* Start with non-digit */
buf
=
":x123
\r\n
"
;
RESET_PARSER_T
(
p
);
assert
(
redis_parser_execute
(
p
,
&
res
,
buf
,
strlen
(
buf
))
==
1
);
assert
(
res
==
NULL
);
assert
(
redis_parser_errno
(
p
)
==
REDIS_PARSER_ERR_INVALID_INT
);
/* Non-digit in the middle */
buf
=
":12x3
\r\n
"
;
RESET_PARSER_T
(
p
);
assert
(
redis_parser_execute
(
p
,
&
res
,
buf
,
strlen
(
buf
))
==
3
);
assert
(
res
==
NULL
);
assert
(
redis_parser_errno
(
p
)
==
REDIS_PARSER_ERR_INVALID_INT
);
/* Non-digit at the end */
buf
=
":123x
\r\n
"
;
RESET_PARSER_T
(
p
);
assert
(
redis_parser_execute
(
p
,
&
res
,
buf
,
strlen
(
buf
))
==
4
);
assert
(
res
==
NULL
);
assert
(
redis_parser_errno
(
p
)
==
REDIS_PARSER_ERR_INVALID_INT
);
/* Signed 64-bit maximum */
buf
=
":9223372036854775807
\r\n
"
;
...
...
@@ -317,6 +323,7 @@ void test_integer(redis_parser_t *p) {
RESET_PARSER_T
(
p
);
assert
(
redis_parser_execute
(
p
,
&
res
,
buf
,
strlen
(
buf
))
==
strlen
(
buf
)
-
3
);
assert
(
res
==
NULL
);
assert
(
redis_parser_errno
(
p
)
==
REDIS_PARSER_ERR_OVERFLOW
);
/* Signed 64-bit minimum */
buf
=
":-9223372036854775808
\r\n
"
;
...
...
@@ -330,6 +337,7 @@ void test_integer(redis_parser_t *p) {
RESET_PARSER_T
(
p
);
assert
(
redis_parser_execute
(
p
,
&
res
,
buf
,
strlen
(
buf
))
==
strlen
(
buf
)
-
3
);
assert
(
res
==
NULL
);
assert
(
redis_parser_errno
(
p
)
==
REDIS_PARSER_ERR_OVERFLOW
);
}
void
test_nil
(
redis_parser_t
*
p
)
{
...
...
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