Commit 1375e400 authored by Pieter Noordhuis's avatar Pieter Noordhuis
Browse files

Extract object code to separate file (+refactor)

parent 70fe4177
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "object.h"
void redis_object_free(redis_object **ptr) {
redis_object *obj = *ptr;
switch(obj->type) {
case REDIS_STRING:
case REDIS_STATUS:
case REDIS_ERROR:
if (obj->str != NULL) {
free(obj->str);
obj->str = NULL;
obj->len = 0;
}
break;
case REDIS_ARRAY:
if (obj->element != NULL) {
unsigned int j;
for (j = 0; j < obj->elements; j++) {
if (obj->element[j] != NULL) {
redis_object_free(&obj->element[j]);
}
}
free(obj->element);
obj->element = NULL;
obj->elements = 0;
}
break;
}
free(*ptr);
*ptr = NULL;
}
static redis_object *_object_create_from_protocol(redis_protocol *p) {
redis_object *obj, *parent;
/* Create object when the callback was not fired before. */
if (p->data == NULL) {
obj = p->data = malloc(sizeof(redis_object));
if (obj == NULL) {
return NULL;
}
obj->type = p->type;
obj->str = NULL;
obj->len = 0;
obj->element = NULL;
obj->elements = 0;
if (p->parent) {
parent = (redis_object*)p->parent->data;
assert(parent->type == REDIS_ARRAY);
parent->element[p->parent->cursor] = obj;
}
}
obj = (redis_object*)p->data;
assert(obj && obj->type == p->type);
return obj;
}
static int _object_string_cb(redis_parser *parser, redis_protocol *p, const char *buf, size_t len) {
redis_object *self;
char *dst;
((void)parser);
self = _object_create_from_protocol(p);
if (self == NULL) {
return -1;
}
if (self->type == REDIS_STRING) {
assert(p->size >= 0);
dst = self->str;
/* The size is known upfront: allocate memory */
if (dst == NULL) {
dst = malloc(p->size+1);
if (dst == NULL) {
return -1;
}
}
} else {
assert(p->size < 0);
/* The size is not known upfront: dynamically allocate memory */
dst = realloc(self->str, self->len + len + 1);
if (dst == NULL) {
return -1;
}
}
/* Copy provided buffer */
memcpy(dst + self->len, buf, len);
self->str = dst;
self->len += len;
self->str[self->len] = '\0';
return 0;
}
static int _object_array_cb(redis_parser *parser, redis_protocol *p, size_t len) {
redis_object *self;
((void)parser);
self = _object_create_from_protocol(p);
if (self == NULL) {
return -1;
}
if (len > 0) {
self->element = calloc(len, sizeof(redis_object*));
if (self->element == NULL) {
return -1;
}
}
self->elements = len;
return 0;
}
static int _object_integer_cb(redis_parser *parser, redis_protocol *p, int64_t value) {
redis_object *self;
((void)parser);
self = _object_create_from_protocol(p);
if (self == NULL) {
return -1;
}
self->integer = value;
return 0;
}
static int _object_nil_cb(redis_parser *parser, redis_protocol *p) {
redis_object *self;
((void)parser);
self = _object_create_from_protocol(p);
if (self == NULL) {
return -1;
}
return 0;
}
static void _object_destroy_cb(redis_parser *parser, redis_protocol *p) {
assert(NULL);
}
/* Set of callbacks that can be passed to the parser. */
redis_parser_callbacks redis_object_parser_callbacks = {
.on_string = _object_string_cb,
.on_array = _object_array_cb,
.on_integer = _object_integer_cb,
.on_nil = _object_nil_cb,
.destroy = _object_destroy_cb
};
#ifndef _HIREDIS_OBJECT_H
#define _HIREDIS_OBJECT_H 1
#include "parser.h"
typedef struct redis_object_s redis_object;
struct redis_object_s {
int type; /* Object type */
int64_t integer; /* Value for REDIS_INTEGER */
char *str; /* REDIS_STRING, REDIS_STATUS, REDIS_ERROR */
unsigned int len; /* String length */
struct redis_object_s **element; /* Actual elements in REDIS_ARRAY */
unsigned int elements; /* Number of elements REDIS_ARRAY */
};
void redis_object_free(redis_object **obj);
extern redis_parser_callbacks redis_object_parser_callbacks;
#endif
#include "../fmacros.h"
/* misc */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* local */
#include "test-helper.h"
#include "../parser.h"
#include "../object.h"
void test_string(redis_parser *parser) {
const char *buf = "$5\r\nhello\r\n";
redis_protocol *res;
redis_object *obj;
redis_parser_init(parser, &redis_object_parser_callbacks);
assert(redis_parser_execute(parser, &res, buf, 11) == 11);
obj = (redis_object*)res->data;
assert(obj != NULL);
assert_equal_int(obj->type, REDIS_STRING);
assert_equal_string(obj->str, "hello");
assert_equal_int(obj->len, 5);
redis_object_free(&obj);
}
void test_chunked_string(redis_parser *parser) {
const char *buf = "$5\r\nhello\r\n";
redis_protocol *res;
redis_object *obj;
redis_parser_init(parser, &redis_object_parser_callbacks);
assert(redis_parser_execute(parser, &res, buf+0, 6) == 6);
assert(redis_parser_execute(parser, &res, buf+6, 5) == 5);
obj = (redis_object*)res->data;
assert(obj != NULL);
assert_equal_int(obj->type, REDIS_STRING);
assert_equal_string(obj->str, "hello");
assert_equal_int(obj->len, 5);
redis_object_free(&obj);
}
void test_empty_string(redis_parser *parser) {
const char *buf = "$0\r\n\r\n";
redis_protocol *res;
redis_object *obj;
redis_parser_init(parser, &redis_object_parser_callbacks);
assert(redis_parser_execute(parser, &res, buf, 6) == 6);
obj = (redis_object*)res->data;
assert(obj != NULL);
assert_equal_int(obj->type, REDIS_STRING);
assert_equal_string(obj->str, "");
assert_equal_int(obj->len, 0);
redis_object_free(&obj);
}
void test_nil(redis_parser *parser) {
const char *buf = "$-1\r\n";
redis_protocol *res;
redis_object *obj;
redis_parser_init(parser, &redis_object_parser_callbacks);
assert(redis_parser_execute(parser, &res, buf, 5) == 5);
obj = (redis_object*)res->data;
assert(obj != NULL);
assert_equal_int(obj->type, REDIS_NIL);
redis_object_free(&obj);
}
void test_array(redis_parser *parser) {
const char *buf =
"*2\r\n"
"$5\r\nhello\r\n"
"$5\r\nworld\r\n";
redis_protocol *res;
redis_object *obj;
redis_parser_init(parser, &redis_object_parser_callbacks);
assert(redis_parser_execute(parser, &res, buf, 26) == 26);
obj = (redis_object*)res->data;
assert(obj != NULL);
assert_equal_int(obj->type, REDIS_ARRAY);
assert_equal_int(obj->elements, 2);
assert_equal_int(obj->element[0]->type, REDIS_STRING);
assert_equal_string(obj->element[0]->str, "hello");
assert_equal_int(obj->element[0]->len, 5);
assert_equal_int(obj->element[1]->type, REDIS_STRING);
assert_equal_string(obj->element[1]->str, "world");
assert_equal_int(obj->element[1]->len, 5);
redis_object_free(&obj);
}
void test_empty_array(redis_parser *parser) {
const char *buf = "*0\r\n";
redis_protocol *res;
redis_object *obj;
redis_parser_init(parser, &redis_object_parser_callbacks);
assert(redis_parser_execute(parser, &res, buf, 4) == 4);
obj = (redis_object*)res->data;
assert(obj != NULL);
assert_equal_int(obj->type, REDIS_ARRAY);
assert_equal_int(obj->elements, 0);
redis_object_free(&obj);
}
int main(int argc, char **argv) {
redis_parser *parser = malloc(sizeof(redis_parser));
printf("redis_object: %lu bytes\n", sizeof(redis_object));
test_string(parser);
test_chunked_string(parser);
test_empty_string(parser);
test_nil(parser);
test_array(parser);
test_empty_array(parser);
//test_integer(parser);
//test_status(parser);
//test_error(parser);
//test_abort_after_error(parser);
free(parser);
return 0;
}
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