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
3c378b84
Commit
3c378b84
authored
Jul 07, 2011
by
Pieter Noordhuis
Browse files
Basic error facility
parent
a25cd930
Changes
3
Hide 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