mirror of
https://github.com/wassname/ray.git
synced 2026-07-09 05:03:02 +08:00
task queue tests and extensions (#18)
* task queue tests and extensions * clean up test
This commit is contained in:
committed by
Robert Nishihara
parent
313241e303
commit
7a079547b0
@@ -7,6 +7,9 @@ all: $(BUILD)/libcommon.a
|
||||
$(BUILD)/libcommon.a: event_loop.o common.o task.o io.o state/redis.o
|
||||
ar rcs $@ $^
|
||||
|
||||
$(BUILD)/common_tests: test/common_tests.c $(BUILD)/libcommon.a
|
||||
$(CC) -o $@ test/common_tests.c $(BUILD)/libcommon.a $(CFLAGS)
|
||||
|
||||
$(BUILD)/db_tests: hiredis test/db_tests.c $(BUILD)/libcommon.a
|
||||
$(CC) -o $@ test/db_tests.c $(BUILD)/libcommon.a thirdparty/hiredis/libhiredis.a $(CFLAGS)
|
||||
|
||||
@@ -29,8 +32,8 @@ redis:
|
||||
hiredis:
|
||||
git submodule update --init --recursive -- "thirdparty/hiredis" ; cd thirdparty/hiredis ; make
|
||||
|
||||
test: hiredis redis $(BUILD)/db_tests $(BUILD)/io_tests $(BUILD)/task_tests $(BUILD)/redis_tests FORCE
|
||||
test: hiredis redis $(BUILD)/common_tests $(BUILD)/db_tests $(BUILD)/io_tests $(BUILD)/task_tests $(BUILD)/redis_tests FORCE
|
||||
./thirdparty/redis-3.2.3/src/redis-server &
|
||||
sleep 1s ; ./build/db_tests ; ./build/io_tests ; ./build/task_tests ; ./build/redis_tests
|
||||
sleep 1s ; ./build/common_tests ; ./build/db_tests ; ./build/io_tests ; ./build/task_tests ; ./build/redis_tests
|
||||
|
||||
FORCE:
|
||||
|
||||
@@ -31,3 +31,63 @@ char *sha1_to_hex(const unsigned char *sha1, char *buffer) {
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const signed char hexval_table[256] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 00-07 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 08-0f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 10-17 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 18-1f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 20-27 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 28-2f */
|
||||
+0, +1, +2, +3, +4, +5, +6, +7, /* 30-37 */
|
||||
+8, +9, -1, -1, -1, -1, -1, -1, /* 38-3f */
|
||||
-1, 10, 11, 12, 13, 14, 15, -1, /* 40-47 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 48-4f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 50-57 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 58-5f */
|
||||
-1, 10, 11, 12, 13, 14, 15, -1, /* 60-67 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 68-67 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 70-77 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 78-7f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 80-87 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 88-8f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 90-97 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* 98-9f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* a0-a7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* a8-af */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* b0-b7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* b8-bf */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* c0-c7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* c8-cf */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* d0-d7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* d8-df */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* e0-e7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* e8-ef */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* f0-f7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, /* f8-ff */
|
||||
};
|
||||
|
||||
static inline unsigned int hexval(unsigned char c) {
|
||||
return hexval_table[c];
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert two consecutive hexadecimal digits into a char. Return a
|
||||
* negative value on error. Don't run over the end of short strings.
|
||||
*/
|
||||
static inline int hex2chr(const char *s) {
|
||||
int val = hexval(s[0]);
|
||||
return (val < 0) ? val : (val << 4) | hexval(s[1]);
|
||||
}
|
||||
|
||||
int hex_to_sha1(const char *hex, unsigned char *sha1) {
|
||||
int i;
|
||||
for (i = 0; i < UNIQUE_ID_SIZE; i++) {
|
||||
int val = hex2chr(hex);
|
||||
if (val < 0)
|
||||
return -1;
|
||||
*sha1++ = val;
|
||||
hex += 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
|
||||
#define UNIQUE_ID_SIZE 20
|
||||
|
||||
// Cleanup method for running tests with the greatest library.
|
||||
// Runs the test, then clears the Redis database.
|
||||
/* Cleanup method for running tests with the greatest library.
|
||||
* Runs the test, then clears the Redis database. */
|
||||
#define RUN_REDIS_TEST(context, test) \
|
||||
RUN_TEST(test); \
|
||||
freeReplyObject(redisCommand(context, "FLUSHALL"));
|
||||
@@ -44,6 +44,11 @@ unique_id globally_unique_id(void);
|
||||
* UNIQUE_ID_SIZE + 1 */
|
||||
char *sha1_to_hex(const unsigned char *sha1, char *buffer);
|
||||
|
||||
/* Convert a hexdecimal string of length 40 to a 20 byte sha1 hash. This
|
||||
* function assumes that sha1 points to an already allocated char array of size
|
||||
* UNIQUE_ID_SIZE. */
|
||||
int hex_to_sha1(const char *hex, unsigned char *sha1);
|
||||
|
||||
typedef unique_id object_id;
|
||||
|
||||
#endif
|
||||
|
||||
+3
-3
@@ -59,10 +59,10 @@ void event_loop_detach(event_loop *loop, int64_t index, int shall_close) {
|
||||
}
|
||||
|
||||
/* Poll the file descriptors associated to this event loop.
|
||||
* See http://linux.die.net/man/2/poll */
|
||||
int event_loop_poll(event_loop *loop) {
|
||||
* See http://linux.die.net/man/2/poll. The timeout is in milliseconds. */
|
||||
int event_loop_poll(event_loop *loop, int timeout) {
|
||||
return poll((struct pollfd *) utarray_front(loop->waiting),
|
||||
utarray_len(loop->waiting), -1);
|
||||
utarray_len(loop->waiting), timeout);
|
||||
}
|
||||
|
||||
/* Get the total number of file descriptors participating in the event loop. */
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ int64_t event_loop_attach(event_loop *loop,
|
||||
int fd,
|
||||
int events);
|
||||
void event_loop_detach(event_loop *loop, int64_t index, int shall_close);
|
||||
int event_loop_poll(event_loop *loop);
|
||||
int event_loop_poll(event_loop *loop, int timeout);
|
||||
int64_t event_loop_size(event_loop *loop);
|
||||
struct pollfd *event_loop_get(event_loop *loop, int64_t index);
|
||||
void event_loop_set_data(event_loop *loop, int64_t index, void *data);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "utstring.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "db.h"
|
||||
#include "object_table.h"
|
||||
@@ -215,6 +217,22 @@ void object_table_lookup(db_conn *db,
|
||||
}
|
||||
}
|
||||
|
||||
void task_queue_submit_task(db_conn *db, task_iid task_iid, task_spec *task) {
|
||||
/* For converting an id to hex, which has double the number
|
||||
* of bytes compared to the id (+ 1 byte for '\0'). */
|
||||
static char hex[2 * UNIQUE_ID_SIZE + 1];
|
||||
UT_string *command;
|
||||
utstring_new(command);
|
||||
sha1_to_hex(&task_iid.id[0], &hex[0]);
|
||||
utstring_printf(command, "HMSET queue:%s", &hex[0]);
|
||||
print_task(task, command);
|
||||
redisAsyncCommand(db->context, NULL, NULL, utstring_body(command));
|
||||
if (db->context->err) {
|
||||
LOG_REDIS_ERR(db->context, "error in task_queue submit_task");
|
||||
}
|
||||
utstring_free(command);
|
||||
}
|
||||
|
||||
void send_redis_command(int socket_fd, const char *format, ...) {
|
||||
char *cmd;
|
||||
va_list ap;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "utarray.h"
|
||||
|
||||
#include "task.h"
|
||||
#include "common.h"
|
||||
#include "io.h"
|
||||
@@ -30,7 +32,8 @@ typedef struct {
|
||||
} task_arg;
|
||||
|
||||
struct task_spec_impl {
|
||||
function_id func_id;
|
||||
/* Function ID of the task. */
|
||||
function_id function_id;
|
||||
/* Total number of arguments. */
|
||||
int64_t num_args;
|
||||
/* Index of the last argument that has been constructed. */
|
||||
@@ -52,14 +55,14 @@ struct task_spec_impl {
|
||||
(sizeof(task_spec) + ((NUM_ARGS) + (NUM_RETURNS)) * sizeof(task_arg) + \
|
||||
(ARGS_VALUE_SIZE))
|
||||
|
||||
task_spec *alloc_task_spec(function_id func_id,
|
||||
task_spec *alloc_task_spec(function_id function_id,
|
||||
int64_t num_args,
|
||||
int64_t num_returns,
|
||||
int64_t args_value_size) {
|
||||
int64_t size = TASK_SPEC_SIZE(num_args, num_returns, args_value_size);
|
||||
task_spec *task = malloc(size);
|
||||
memset(task, 0, size);
|
||||
task->func_id = func_id;
|
||||
task->function_id = function_id;
|
||||
task->num_args = num_args;
|
||||
task->arg_index = 0;
|
||||
task->num_returns = num_returns;
|
||||
@@ -72,6 +75,10 @@ int64_t task_size(task_spec *spec) {
|
||||
spec->args_value_size);
|
||||
}
|
||||
|
||||
unique_id *task_function(task_spec *spec) {
|
||||
return &spec->function_id;
|
||||
}
|
||||
|
||||
int64_t task_num_args(task_spec *spec) {
|
||||
return spec->num_args;
|
||||
}
|
||||
@@ -153,3 +160,86 @@ task_spec *read_task(int fd) {
|
||||
CHECK(task_size(spec) == length);
|
||||
return spec;
|
||||
}
|
||||
|
||||
void print_task(task_spec *spec, UT_string *output) {
|
||||
/* For converting an id to hex, which has double the number
|
||||
* of bytes compared to the id (+ 1 byte for '\0'). */
|
||||
static char hex[2 * UNIQUE_ID_SIZE + 1];
|
||||
/* Print function id. */
|
||||
sha1_to_hex(&task_function(spec)->id[0], &hex[0]);
|
||||
utstring_printf(output, "fun %s ", &hex[0]);
|
||||
/* Print arguments. */
|
||||
for (int i = 0; i < task_num_args(spec); ++i) {
|
||||
sha1_to_hex(&task_arg_id(spec, i)->id[0], &hex[0]);
|
||||
utstring_printf(output, " id:%d %s", i, &hex[0]);
|
||||
}
|
||||
/* Print return ids. */
|
||||
for (int i = 0; i < task_num_returns(spec); ++i) {
|
||||
object_id *object_id = task_return(spec, i);
|
||||
sha1_to_hex(&object_id->id[0], &hex[0]);
|
||||
utstring_printf(output, " ret:%d %s", i, &hex[0]);
|
||||
}
|
||||
}
|
||||
|
||||
UT_icd unique_id_icd = {sizeof(unique_id), NULL, NULL, NULL};
|
||||
|
||||
task_spec *parse_task(char *task_string, int64_t task_length) {
|
||||
/* We make one pass through task_string to store all the argument ids
|
||||
* in "args" and all the return ids in "returns". */
|
||||
UT_array *args;
|
||||
utarray_new(args, &unique_id_icd);
|
||||
UT_array *returns;
|
||||
utarray_new(returns, &unique_id_icd);
|
||||
function_id function_id;
|
||||
char *cursor = strtok(task_string, " ");
|
||||
int index = 0;
|
||||
while (cursor != NULL) {
|
||||
/* This will be equal to "args" or "returns" depending on whether we
|
||||
* are processing an argument id or a return id. */
|
||||
UT_array *target = NULL;
|
||||
if (strncmp("fun", cursor, 3) == 0) {
|
||||
/* Parse function id. */
|
||||
CHECK(cursor + 2 * UNIQUE_ID_SIZE + 1 <= task_string + task_length);
|
||||
cursor = strtok(NULL, " ");
|
||||
hex_to_sha1(cursor, &function_id.id[0]);
|
||||
cursor = strtok(NULL, " ");
|
||||
CHECK(cursor);
|
||||
continue;
|
||||
} else if (strncmp("id:", cursor, 3) == 0) {
|
||||
/* Parse pass by reference argument. */
|
||||
sscanf(cursor, "id:%d", &index);
|
||||
target = args;
|
||||
} else if (strncmp("val:", cursor, 4) == 0) {
|
||||
/* Parse pass by value argument. */
|
||||
sscanf(cursor, "val:%d", &index);
|
||||
CHECK(0); /* Not implemented yet */
|
||||
} else if (strncmp("ret:", cursor, 4) == 0) {
|
||||
/* Parse return object reference. */
|
||||
sscanf(cursor, "ret:%d", &index);
|
||||
target = returns;
|
||||
}
|
||||
cursor = strtok(NULL, " ");
|
||||
CHECK(cursor);
|
||||
if (index >= utarray_len(target)) {
|
||||
utarray_resize(target, index + 1);
|
||||
}
|
||||
object_id *id = (object_id *) utarray_eltptr(target, index);
|
||||
hex_to_sha1(cursor, &id->id[0]);
|
||||
cursor = strtok(NULL, " ");
|
||||
}
|
||||
/* TODO(pcm): Implement pass by value. */
|
||||
/* Now assemble the task specification. */
|
||||
task_spec *spec =
|
||||
alloc_task_spec(function_id, utarray_len(args), utarray_len(returns), 0);
|
||||
for (int i = 0; i < utarray_len(args); ++i) {
|
||||
object_id *id = (object_id *) utarray_eltptr(args, i);
|
||||
task_args_add_ref(spec, *id);
|
||||
}
|
||||
for (int i = 0; i < utarray_len(returns); ++i) {
|
||||
object_id *id = (object_id *) utarray_eltptr(returns, i);
|
||||
*task_return(spec, i) = *id;
|
||||
}
|
||||
utarray_free(args);
|
||||
utarray_free(returns);
|
||||
return spec;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common.h"
|
||||
#include "utstring.h"
|
||||
|
||||
typedef unique_id function_id;
|
||||
typedef unique_id object_id;
|
||||
@@ -22,7 +23,7 @@ enum arg_type { ARG_BY_REF, ARG_BY_VAL };
|
||||
/* Construct and modify task specifications. */
|
||||
|
||||
/* Allocating and initializing a task. */
|
||||
task_spec *alloc_task_spec(function_id func_id,
|
||||
task_spec *alloc_task_spec(function_id function_id,
|
||||
int64_t num_args,
|
||||
int64_t num_returns,
|
||||
int64_t args_value_size);
|
||||
@@ -30,6 +31,9 @@ task_spec *alloc_task_spec(function_id func_id,
|
||||
/* Size of the task in bytes. */
|
||||
int64_t task_size(task_spec *spec);
|
||||
|
||||
/* Return the function ID of the task. */
|
||||
unique_id *task_function(task_spec *spec);
|
||||
|
||||
/* Getting the number of arguments and returns. */
|
||||
int64_t task_num_args(task_spec *spec);
|
||||
int64_t task_num_returns(task_spec *spec);
|
||||
@@ -58,4 +62,10 @@ void write_task(int fd, task_spec *spec);
|
||||
* responsibility to free the task after it has been used. */
|
||||
task_spec *read_task(int fd);
|
||||
|
||||
/* Print task as a humanly readable string. */
|
||||
void print_task(task_spec *spec, UT_string *output);
|
||||
|
||||
/* Parse task as printed by print_task. */
|
||||
task_spec *parse_task(char *task_string, int64_t task_length);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "greatest.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
SUITE(common_tests);
|
||||
|
||||
TEST sha1_test(void) {
|
||||
static char hex[2 * UNIQUE_ID_SIZE + 1];
|
||||
static unsigned char id[UNIQUE_ID_SIZE];
|
||||
unique_id uid = globally_unique_id();
|
||||
sha1_to_hex(&uid.id[0], &hex[0]);
|
||||
hex_to_sha1(&hex[0], &id[0]);
|
||||
ASSERT(memcmp(&uid.id[0], &id[0], 20) == 0);
|
||||
PASS();
|
||||
}
|
||||
|
||||
SUITE(common_tests) {
|
||||
RUN_TEST(sha1_test);
|
||||
}
|
||||
|
||||
GREATEST_MAIN_DEFS();
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
GREATEST_MAIN_BEGIN();
|
||||
RUN_SUITE(common_tests);
|
||||
GREATEST_MAIN_END();
|
||||
}
|
||||
+37
-2
@@ -3,6 +3,7 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "event_loop.h"
|
||||
#include "test/example_task.h"
|
||||
#include "state/db.h"
|
||||
#include "state/object_table.h"
|
||||
#include "state/redis.h"
|
||||
@@ -62,7 +63,7 @@ TEST object_table_lookup_test(void) {
|
||||
object_table_add(&conn2, id);
|
||||
object_table_lookup(&conn1, id, sync_test_callback);
|
||||
while (!lookup_successful) {
|
||||
int num_ready = event_loop_poll(&loop);
|
||||
int num_ready = event_loop_poll(&loop, -1);
|
||||
if (num_ready < 0) {
|
||||
exit(-1);
|
||||
}
|
||||
@@ -81,7 +82,7 @@ TEST object_table_lookup_test(void) {
|
||||
lookup_successful = 0;
|
||||
object_table_lookup(&conn1, id, test_callback);
|
||||
while (!lookup_successful) {
|
||||
int num_ready = event_loop_poll(&loop);
|
||||
int num_ready = event_loop_poll(&loop, -1);
|
||||
if (num_ready < 0) {
|
||||
exit(-1);
|
||||
}
|
||||
@@ -112,10 +113,44 @@ TEST object_table_lookup_test(void) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST task_queue_test(void) {
|
||||
event_loop loop;
|
||||
event_loop_init(&loop);
|
||||
db_conn conn;
|
||||
db_connect("127.0.0.1", 6379, "local_scheduler", "", -1, &conn);
|
||||
int64_t index = db_attach(&conn, &loop, 0);
|
||||
|
||||
task_spec *task = example_task();
|
||||
task_queue_submit_task(&conn, globally_unique_id(), task);
|
||||
while (1) {
|
||||
int num_ready = event_loop_poll(&loop, 100);
|
||||
if (num_ready < 0) {
|
||||
exit(-1);
|
||||
}
|
||||
if (num_ready == 0) {
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < event_loop_size(&loop); ++i) {
|
||||
struct pollfd *waiting = event_loop_get(&loop, i);
|
||||
if (waiting->revents == 0)
|
||||
continue;
|
||||
if (i == index) {
|
||||
db_event(&conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free_task_spec(task);
|
||||
db_disconnect(&conn);
|
||||
event_loop_free(&loop);
|
||||
PASS();
|
||||
}
|
||||
|
||||
SUITE(db_tests) {
|
||||
redisContext *context = redisConnect("127.0.0.1", 6379);
|
||||
redisCommand(context, "FLUSHALL");
|
||||
RUN_REDIS_TEST(context, object_table_lookup_test);
|
||||
RUN_TEST(task_queue_test);
|
||||
redisFree(context);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef EXAMPLE_TASK_H
|
||||
#define EXAMPLE_TASK_H
|
||||
|
||||
#include "task.h"
|
||||
|
||||
task_spec *example_task(void) {
|
||||
function_id func_id = globally_unique_id();
|
||||
task_spec *task = alloc_task_spec(func_id, 2, 1, 0);
|
||||
task_args_add_ref(task, globally_unique_id());
|
||||
task_args_add_ref(task, globally_unique_id());
|
||||
return task;
|
||||
}
|
||||
|
||||
#endif
|
||||
+1
-1
@@ -85,7 +85,7 @@ TEST async_redis_socket_test(void) {
|
||||
send_redis_command(client_fd, test_set_format, test_key, test_value);
|
||||
|
||||
while (!lookup_successful) {
|
||||
int num_ready = event_loop_poll(&loop);
|
||||
int num_ready = event_loop_poll(&loop, -1);
|
||||
if (num_ready < 0) {
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "test/example_task.h"
|
||||
#include "task.h"
|
||||
|
||||
SUITE(task_tests);
|
||||
@@ -56,9 +57,28 @@ TEST send_task(void) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST print_and_parse_task(void) {
|
||||
task_spec *task = example_task();
|
||||
|
||||
UT_string *output;
|
||||
utstring_new(output);
|
||||
print_task(task, output);
|
||||
task_spec *result = parse_task(utstring_body(output), utstring_len(output));
|
||||
utstring_free(output);
|
||||
|
||||
ASSERT_EQ(task_size(task), task_size(result));
|
||||
ASSERT(memcmp(task, result, task_size(task)) == 0);
|
||||
|
||||
free_task_spec(task);
|
||||
free_task_spec(result);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
SUITE(task_tests) {
|
||||
RUN_TEST(task_test);
|
||||
RUN_TEST(send_task);
|
||||
RUN_TEST(print_and_parse_task);
|
||||
}
|
||||
|
||||
GREATEST_MAIN_DEFS();
|
||||
|
||||
Vendored
+398
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
Copyright (c) 2008-2016, Troy D. Hanson http://troydhanson.github.com/uthash/
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* a dynamic string implementation using macros
|
||||
*/
|
||||
#ifndef UTSTRING_H
|
||||
#define UTSTRING_H
|
||||
|
||||
#define UTSTRING_VERSION 2.0.1
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define _UNUSED_ __attribute__ ((__unused__))
|
||||
#else
|
||||
#define _UNUSED_
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifndef oom
|
||||
#define oom() exit(-1)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
char *d;
|
||||
size_t n; /* allocd size */
|
||||
size_t i; /* index of first unused byte */
|
||||
} UT_string;
|
||||
|
||||
#define utstring_reserve(s,amt) \
|
||||
do { \
|
||||
if (((s)->n - (s)->i) < (size_t)(amt)) { \
|
||||
char *utstring_tmp = (char*)realloc( \
|
||||
(s)->d, (s)->n + (amt)); \
|
||||
if (utstring_tmp == NULL) oom(); \
|
||||
(s)->d = utstring_tmp; \
|
||||
(s)->n += (amt); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define utstring_init(s) \
|
||||
do { \
|
||||
(s)->n = 0; (s)->i = 0; (s)->d = NULL; \
|
||||
utstring_reserve(s,100); \
|
||||
(s)->d[0] = '\0'; \
|
||||
} while(0)
|
||||
|
||||
#define utstring_done(s) \
|
||||
do { \
|
||||
if ((s)->d != NULL) free((s)->d); \
|
||||
(s)->n = 0; \
|
||||
} while(0)
|
||||
|
||||
#define utstring_free(s) \
|
||||
do { \
|
||||
utstring_done(s); \
|
||||
free(s); \
|
||||
} while(0)
|
||||
|
||||
#define utstring_new(s) \
|
||||
do { \
|
||||
s = (UT_string*)calloc(sizeof(UT_string),1); \
|
||||
if (!s) oom(); \
|
||||
utstring_init(s); \
|
||||
} while(0)
|
||||
|
||||
#define utstring_renew(s) \
|
||||
do { \
|
||||
if (s) { \
|
||||
utstring_clear(s); \
|
||||
} else { \
|
||||
utstring_new(s); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define utstring_clear(s) \
|
||||
do { \
|
||||
(s)->i = 0; \
|
||||
(s)->d[0] = '\0'; \
|
||||
} while(0)
|
||||
|
||||
#define utstring_bincpy(s,b,l) \
|
||||
do { \
|
||||
utstring_reserve((s),(l)+1); \
|
||||
if (l) memcpy(&(s)->d[(s)->i], b, l); \
|
||||
(s)->i += (l); \
|
||||
(s)->d[(s)->i]='\0'; \
|
||||
} while(0)
|
||||
|
||||
#define utstring_concat(dst,src) \
|
||||
do { \
|
||||
utstring_reserve((dst),((src)->i)+1); \
|
||||
if ((src)->i) memcpy(&(dst)->d[(dst)->i], (src)->d, (src)->i); \
|
||||
(dst)->i += (src)->i; \
|
||||
(dst)->d[(dst)->i]='\0'; \
|
||||
} while(0)
|
||||
|
||||
#define utstring_len(s) ((unsigned)((s)->i))
|
||||
|
||||
#define utstring_body(s) ((s)->d)
|
||||
|
||||
_UNUSED_ static void utstring_printf_va(UT_string *s, const char *fmt, va_list ap) {
|
||||
int n;
|
||||
va_list cp;
|
||||
for (;;) {
|
||||
#ifdef _WIN32
|
||||
cp = ap;
|
||||
#else
|
||||
va_copy(cp, ap);
|
||||
#endif
|
||||
n = vsnprintf (&s->d[s->i], s->n-s->i, fmt, cp);
|
||||
va_end(cp);
|
||||
|
||||
if ((n > -1) && ((size_t) n < (s->n-s->i))) {
|
||||
s->i += n;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Else try again with more space. */
|
||||
if (n > -1) utstring_reserve(s,n+1); /* exact */
|
||||
else utstring_reserve(s,(s->n)*2); /* 2x */
|
||||
}
|
||||
}
|
||||
#ifdef __GNUC__
|
||||
/* support printf format checking (2=the format string, 3=start of varargs) */
|
||||
static void utstring_printf(UT_string *s, const char *fmt, ...)
|
||||
__attribute__ (( format( printf, 2, 3) ));
|
||||
#endif
|
||||
_UNUSED_ static void utstring_printf(UT_string *s, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap,fmt);
|
||||
utstring_printf_va(s,fmt,ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* begin substring search functions *
|
||||
******************************************************************************/
|
||||
/* Build KMP table from left to right. */
|
||||
_UNUSED_ static void _utstring_BuildTable(
|
||||
const char *P_Needle,
|
||||
size_t P_NeedleLen,
|
||||
long *P_KMP_Table)
|
||||
{
|
||||
long i, j;
|
||||
|
||||
i = 0;
|
||||
j = i - 1;
|
||||
P_KMP_Table[i] = j;
|
||||
while (i < (long) P_NeedleLen)
|
||||
{
|
||||
while ( (j > -1) && (P_Needle[i] != P_Needle[j]) )
|
||||
{
|
||||
j = P_KMP_Table[j];
|
||||
}
|
||||
i++;
|
||||
j++;
|
||||
if (i < (long) P_NeedleLen)
|
||||
{
|
||||
if (P_Needle[i] == P_Needle[j])
|
||||
{
|
||||
P_KMP_Table[i] = P_KMP_Table[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
P_KMP_Table[i] = j;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
P_KMP_Table[i] = j;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Build KMP table from right to left. */
|
||||
_UNUSED_ static void _utstring_BuildTableR(
|
||||
const char *P_Needle,
|
||||
size_t P_NeedleLen,
|
||||
long *P_KMP_Table)
|
||||
{
|
||||
long i, j;
|
||||
|
||||
i = P_NeedleLen - 1;
|
||||
j = i + 1;
|
||||
P_KMP_Table[i + 1] = j;
|
||||
while (i >= 0)
|
||||
{
|
||||
while ( (j < (long) P_NeedleLen) && (P_Needle[i] != P_Needle[j]) )
|
||||
{
|
||||
j = P_KMP_Table[j + 1];
|
||||
}
|
||||
i--;
|
||||
j--;
|
||||
if (i >= 0)
|
||||
{
|
||||
if (P_Needle[i] == P_Needle[j])
|
||||
{
|
||||
P_KMP_Table[i + 1] = P_KMP_Table[j + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
P_KMP_Table[i + 1] = j;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
P_KMP_Table[i + 1] = j;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Search data from left to right. ( Multiple search mode. ) */
|
||||
_UNUSED_ static long _utstring_find(
|
||||
const char *P_Haystack,
|
||||
size_t P_HaystackLen,
|
||||
const char *P_Needle,
|
||||
size_t P_NeedleLen,
|
||||
long *P_KMP_Table)
|
||||
{
|
||||
long i, j;
|
||||
long V_FindPosition = -1;
|
||||
|
||||
/* Search from left to right. */
|
||||
i = j = 0;
|
||||
while ( (j < (int)P_HaystackLen) && (((P_HaystackLen - j) + i) >= P_NeedleLen) )
|
||||
{
|
||||
while ( (i > -1) && (P_Needle[i] != P_Haystack[j]) )
|
||||
{
|
||||
i = P_KMP_Table[i];
|
||||
}
|
||||
i++;
|
||||
j++;
|
||||
if (i >= (int)P_NeedleLen)
|
||||
{
|
||||
/* Found. */
|
||||
V_FindPosition = j - i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return V_FindPosition;
|
||||
}
|
||||
|
||||
|
||||
/* Search data from right to left. ( Multiple search mode. ) */
|
||||
_UNUSED_ static long _utstring_findR(
|
||||
const char *P_Haystack,
|
||||
size_t P_HaystackLen,
|
||||
const char *P_Needle,
|
||||
size_t P_NeedleLen,
|
||||
long *P_KMP_Table)
|
||||
{
|
||||
long i, j;
|
||||
long V_FindPosition = -1;
|
||||
|
||||
/* Search from right to left. */
|
||||
j = (P_HaystackLen - 1);
|
||||
i = (P_NeedleLen - 1);
|
||||
while ( (j >= 0) && (j >= i) )
|
||||
{
|
||||
while ( (i < (int)P_NeedleLen) && (P_Needle[i] != P_Haystack[j]) )
|
||||
{
|
||||
i = P_KMP_Table[i + 1];
|
||||
}
|
||||
i--;
|
||||
j--;
|
||||
if (i < 0)
|
||||
{
|
||||
/* Found. */
|
||||
V_FindPosition = j + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return V_FindPosition;
|
||||
}
|
||||
|
||||
|
||||
/* Search data from left to right. ( One time search mode. ) */
|
||||
_UNUSED_ static long utstring_find(
|
||||
UT_string *s,
|
||||
long P_StartPosition, /* Start from 0. -1 means last position. */
|
||||
const char *P_Needle,
|
||||
size_t P_NeedleLen)
|
||||
{
|
||||
long V_StartPosition;
|
||||
long V_HaystackLen;
|
||||
long *V_KMP_Table;
|
||||
long V_FindPosition = -1;
|
||||
|
||||
if (P_StartPosition < 0)
|
||||
{
|
||||
V_StartPosition = s->i + P_StartPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
V_StartPosition = P_StartPosition;
|
||||
}
|
||||
V_HaystackLen = s->i - V_StartPosition;
|
||||
if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) )
|
||||
{
|
||||
V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1));
|
||||
if (V_KMP_Table != NULL)
|
||||
{
|
||||
_utstring_BuildTable(P_Needle, P_NeedleLen, V_KMP_Table);
|
||||
|
||||
V_FindPosition = _utstring_find(s->d + V_StartPosition,
|
||||
V_HaystackLen,
|
||||
P_Needle,
|
||||
P_NeedleLen,
|
||||
V_KMP_Table);
|
||||
if (V_FindPosition >= 0)
|
||||
{
|
||||
V_FindPosition += V_StartPosition;
|
||||
}
|
||||
|
||||
free(V_KMP_Table);
|
||||
}
|
||||
}
|
||||
|
||||
return V_FindPosition;
|
||||
}
|
||||
|
||||
|
||||
/* Search data from right to left. ( One time search mode. ) */
|
||||
_UNUSED_ static long utstring_findR(
|
||||
UT_string *s,
|
||||
long P_StartPosition, /* Start from 0. -1 means last position. */
|
||||
const char *P_Needle,
|
||||
size_t P_NeedleLen)
|
||||
{
|
||||
long V_StartPosition;
|
||||
long V_HaystackLen;
|
||||
long *V_KMP_Table;
|
||||
long V_FindPosition = -1;
|
||||
|
||||
if (P_StartPosition < 0)
|
||||
{
|
||||
V_StartPosition = s->i + P_StartPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
V_StartPosition = P_StartPosition;
|
||||
}
|
||||
V_HaystackLen = V_StartPosition + 1;
|
||||
if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) )
|
||||
{
|
||||
V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1));
|
||||
if (V_KMP_Table != NULL)
|
||||
{
|
||||
_utstring_BuildTableR(P_Needle, P_NeedleLen, V_KMP_Table);
|
||||
|
||||
V_FindPosition = _utstring_findR(s->d,
|
||||
V_HaystackLen,
|
||||
P_Needle,
|
||||
P_NeedleLen,
|
||||
V_KMP_Table);
|
||||
|
||||
free(V_KMP_Table);
|
||||
}
|
||||
}
|
||||
|
||||
return V_FindPosition;
|
||||
}
|
||||
/*******************************************************************************
|
||||
* end substring search functions *
|
||||
******************************************************************************/
|
||||
|
||||
#endif /* UTSTRING_H */
|
||||
Reference in New Issue
Block a user