Commit 3c378b84 authored by Pieter Noordhuis's avatar Pieter Noordhuis
Browse files

Basic error facility

parent a25cd930
......@@ -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;
}
......@@ -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
......@@ -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) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment