From: Date: Mon, 24 Sep 2007 16:15:15 +0000 (+0200) Subject: Rewrote return read() to now read exactly character by character. X-Git-Tag: 0.2~9 X-Git-Url: https://git.m6w6.name/?a=commitdiff_plain;ds=inline;h=5672a07389f0e0ea5dc4858c02ac3f3bc7ecf3f7;p=awesomized%2Flibmemcached Rewrote return read() to now read exactly character by character. First version of multi key get has been added. It is...lame... but it works. I will rewrite it to not call malloc each time, but instead clean up on its own. --- diff --git a/include/memcached.h b/include/memcached.h index da704b19..a9c64359 100644 --- a/include/memcached.h +++ b/include/memcached.h @@ -35,6 +35,7 @@ typedef struct memcached_host_st memcached_host_st; #define MEMCACHED_DEFAULT_PORT 11211 #define MEMCACHED_DEFAULT_COMMAND_SIZE 350 #define HUGE_STRING_LEN 8196 +#define MEMCACHED_MAX_KEY 251 /* We add one to have it null terminated */ #define WATCHPOINT printf("WATCHPOINT %s:%d\n", __FILE__, __LINE__);fflush(stdout); @@ -104,6 +105,7 @@ struct memcached_st { memcached_allocated is_allocated; memcached_host_st *hosts; unsigned int number_of_hosts; + unsigned int cursor_server; char connected; }; @@ -138,12 +140,19 @@ memcached_return memcached_stat_hostname(memcached_stat_st *stat, char *args, char *hostname, unsigned int port); memcached_return memcached_flush(memcached_st *ptr, time_t expiration); memcached_return memcached_verbosity(memcached_st *ptr, unsigned int verbosity); -memcached_return memcached_quit(memcached_st *ptr, char *hostname, unsigned port); +void memcached_quit(memcached_st *ptr); char *memcached_get(memcached_st *ptr, char *key, size_t key_length, size_t *value_length, uint16_t *flags, memcached_return *error); -memcached_return memcached_server_add(memcached_st *ptr, char *hostname, unsigned int port); +memcached_return memcached_mget(memcached_st *ptr, + char **keys, size_t *key_length, + unsigned int number_of_keys); +char *memcached_fetch(memcached_st *ptr, char *key, size_t *key_length, + size_t *value_length, uint16_t *flags, + memcached_return *error); +memcached_return memcached_server_add(memcached_st *ptr, char *hostname, + unsigned int port); char *memcached_strerror(memcached_st *ptr, memcached_return rc); /* These are all private, do not use. */ diff --git a/lib/memcached_get.c b/lib/memcached_get.c index a1885450..a29deab3 100644 --- a/lib/memcached_get.c +++ b/lib/memcached_get.c @@ -1,33 +1,16 @@ #include -char *memcached_get(memcached_st *ptr, char *key, size_t key_length, - size_t *value_length, - uint16_t *flags, - memcached_return *error) +static char *memcached_value_fetch(memcached_st *ptr, char *key, size_t *key_length, + size_t *value_length, + uint16_t *flags, + memcached_return *error, + char load_key, + unsigned int server_key) { - size_t send_length; char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; char *string_ptr; - unsigned int server_key; *value_length= 0; - *error= memcached_connect(ptr); - - if (*error != MEMCACHED_SUCCESS) - return NULL; - - server_key= memcached_generate_hash(key, key_length) % ptr->number_of_hosts; - - send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, "get %.*s\r\n", - (int)key_length, key); - if (*error != MEMCACHED_SUCCESS) - return NULL; - - if ((write(ptr->hosts[server_key].fd, buffer, send_length) == -1)) - { - *error= MEMCACHED_WRITE_FAILURE; - return NULL; - } memset(buffer, 0, MEMCACHED_DEFAULT_COMMAND_SIZE); *error= memcached_response(ptr, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, server_key); @@ -39,8 +22,18 @@ char *memcached_get(memcached_st *ptr, char *key, size_t key_length, string_ptr= buffer; string_ptr+= 6; /* "VALUE " */ - /* We do nothing with the key, since we only asked for one key */ - for (end_ptr= string_ptr; *end_ptr != ' '; end_ptr++); + /* We load the key */ + if (load_key) + { + memset(key, 0, MEMCACHED_MAX_KEY); + for (end_ptr= string_ptr; *end_ptr != ' '; end_ptr++) + { + *key= *end_ptr; + key++; + } + } + else /* Skip characters */ + for (end_ptr= string_ptr; *end_ptr != ' '; end_ptr++); /* Flags fetch */ string_ptr= end_ptr + 1; @@ -57,39 +50,27 @@ char *memcached_get(memcached_st *ptr, char *key, size_t key_length, if (*value_length) { - size_t need_to_copy; - char *pos_ptr; + size_t read_length; char *value; - value= (char *)malloc(*value_length * sizeof(char)); + /* We add two bytes so that we can walk the \r\n */ + value= (char *)malloc(((*value_length) +2) * sizeof(char)); if (!value) { + *value_length= 0; *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; return NULL; } - need_to_copy= (*value_length < (size_t)(buffer-string_ptr)) - ? *value_length - : (size_t)(buffer-string_ptr) ; - - pos_ptr= memcpy(value, string_ptr, need_to_copy); + read_length= read(ptr->hosts[server_key].fd, value, (*value_length)+2); - if ( need_to_copy > *value_length) + if ((read_length -2) != *value_length) { - size_t read_length; - size_t need_to_read; + free(value); + *error= MEMCACHED_PARTIAL_READ; - need_to_read= *value_length - need_to_copy; - - read_length= read(ptr->hosts[server_key].fd, pos_ptr, need_to_read); - if (read_length != need_to_read) - { - free(value); - *error= MEMCACHED_PARTIAL_READ; - - return NULL; - } + return NULL; } return value; @@ -98,3 +79,104 @@ char *memcached_get(memcached_st *ptr, char *key, size_t key_length, return NULL; } + +char *memcached_get(memcached_st *ptr, char *key, size_t key_length, + size_t *value_length, + uint16_t *flags, + memcached_return *error) +{ + size_t send_length; + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + unsigned int server_key; + + *value_length= 0; + *error= memcached_connect(ptr); + + if (*error != MEMCACHED_SUCCESS) + return NULL; + + server_key= memcached_generate_hash(key, key_length) % ptr->number_of_hosts; + + send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, "get %.*s\r\n", + (int)key_length, key); + if (*error != MEMCACHED_SUCCESS) + return NULL; + + if ((write(ptr->hosts[server_key].fd, buffer, send_length) == -1)) + { + *error= MEMCACHED_WRITE_FAILURE; + return NULL; + } + + return memcached_value_fetch(ptr, key, &key_length, value_length, flags, + error, 0, server_key); +} + +memcached_return memcached_mget(memcached_st *ptr, + char **keys, size_t *key_length, + unsigned int number_of_keys) +{ + char buffer[HUGE_STRING_LEN]; + char *buffer_ptr; + unsigned int x; + memcached_return rc; + + ptr->cursor_server= 0; + memset(buffer, 0, HUGE_STRING_LEN); + + rc= memcached_connect(ptr); + + if (rc != MEMCACHED_SUCCESS) + return rc; + + memcpy(buffer, "get", strlen("get")); + buffer_ptr= buffer; + buffer_ptr+=strlen("get"); + + for (x= 0; x < number_of_keys; x++) + { + *buffer_ptr= ' '; + buffer_ptr++; + memcpy(buffer_ptr, keys[x], key_length[x]); + buffer_ptr+= key_length[x]; + } + + memcpy(buffer_ptr, "\r\n", 2); + buffer_ptr+=2; + + /* + This must be fixed. Right now we hit every server, and send keys + to all servers. We should fix this quickly. + */ + for (x= 0; x < ptr->number_of_hosts; x++) + { + if ((write(ptr->hosts[x].fd, buffer, (size_t)(buffer_ptr - buffer)) == -1)) + { + memcached_quit(ptr); + rc= MEMCACHED_SOME_ERRORS; + } + } + + return rc; +} + +char *memcached_fetch(memcached_st *ptr, char *key, size_t *key_length, + size_t *value_length, + uint16_t *flags, + memcached_return *error) +{ + char *value_check; + + while (ptr->cursor_server < ptr->number_of_hosts) + { + value_check= memcached_value_fetch(ptr, key, key_length, value_length, flags, + error, 1, ptr->cursor_server); + + if (*error == MEMCACHED_NOTFOUND) + ptr->cursor_server++; + else + return value_check; + } + + return NULL; +} diff --git a/lib/memcached_quit.c b/lib/memcached_quit.c index 8d0e5551..24a88c2d 100644 --- a/lib/memcached_quit.c +++ b/lib/memcached_quit.c @@ -1,10 +1,21 @@ #include /* - When this is implemented you will be able to remove single hosts - from your current pool of hosts. + This closes all connections (forces flush of input as well). + + Maybe add a host specific, or key specific version? */ -memcached_return memcached_quit(memcached_st *ptr, char *hostname, unsigned port) +void memcached_quit(memcached_st *ptr) { - return MEMCACHED_SUCCESS; + unsigned int x; + + if (ptr->hosts) + { + for (x= 0; x < ptr->number_of_hosts; x++) + { + if (ptr->hosts[x].fd > 0) + close(ptr->hosts[x].fd); + } + } + ptr->connected= 0; } diff --git a/lib/memcached_response.c b/lib/memcached_response.c index 693f9b30..1d0937af 100644 --- a/lib/memcached_response.c +++ b/lib/memcached_response.c @@ -12,9 +12,24 @@ memcached_return memcached_response(memcached_st *ptr, unsigned int server_key) { size_t send_length; + char *buffer_ptr; memset(buffer, 0, buffer_length); - send_length= read(ptr->hosts[server_key].fd, buffer, buffer_length); + + buffer_ptr= buffer; + while (1) + { + unsigned int read_length; + read_length= read(ptr->hosts[server_key].fd, buffer_ptr, 1); + + if (read_length != 1) + return MEMCACHED_UNKNOWN_READ_FAILURE; + + if (*buffer_ptr == '\n') + break; + else + buffer_ptr++; + } if (send_length) switch(buffer[0]) diff --git a/lib/memcached_verbosity.c b/lib/memcached_verbosity.c index 506dbb07..201e8bef 100644 --- a/lib/memcached_verbosity.c +++ b/lib/memcached_verbosity.c @@ -20,7 +20,10 @@ memcached_return memcached_verbosity(memcached_st *ptr, unsigned int verbosity) memcached_return rc; if ((write(ptr->hosts[x].fd, buffer, send_length) == -1)) - return MEMCACHED_WRITE_FAILURE; + { + continue; + return MEMCACHED_SOME_ERRORS; + } rc= memcached_response(ptr, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, x); diff --git a/tests/test.c b/tests/test.c index 4cf41c63..f4ba3480 100644 --- a/tests/test.c +++ b/tests/test.c @@ -160,6 +160,8 @@ void get_test2(void) string= memcached_get(memc, key, strlen(key), &string_length, &flags, &rc); + assert(string); + assert(rc == MEMCACHED_SUCCESS); assert(string_length == strlen(value)); assert(!memcmp(string, value, string_length)); @@ -233,11 +235,86 @@ void decrement_test(void) memcached_deinit(memc); } +void quit_test(void) +{ + memcached_st *memc; + memcached_return rc; + char *key= "fudge"; + char *value= "sanford and sun"; + + memc= memcached_init(NULL); + assert(memc); + rc= memcached_set(memc, key, strlen(key), + value, strlen(value), + (time_t)10, (uint16_t)3); + assert(rc == MEMCACHED_SUCCESS); + memcached_quit(memc); + + rc= memcached_set(memc, key, strlen(key), + value, strlen(value), + (time_t)50, (uint16_t)9); + assert(rc == MEMCACHED_SUCCESS); + + memcached_deinit(memc); +} + +void mget_test(void) +{ + memcached_st *memc; + memcached_return rc; + char *keys[]= {"fudge", "son", "food"}; + size_t key_length[]= {5, 3, 4}; + unsigned int x; + uint16_t flags; + + char return_key[MEMCACHED_MAX_KEY]; + size_t return_key_length; + char *return_value; + size_t return_value_length; + + memc= memcached_init(NULL); + assert(memc); + + rc= memcached_mget(memc, keys, key_length, 3); + assert(rc == MEMCACHED_SUCCESS); + + while (return_value= memcached_fetch(memc, return_key, &return_key_length, + &return_value_length, &flags, &rc)) + { + assert(return_value); + } + assert(rc == MEMCACHED_NOTFOUND); + + for (x= 0; x < 3; x++) + { + rc= memcached_set(memc, keys[x], key_length[x], + keys[x], key_length[x], + (time_t)50, (uint16_t)9); + assert(rc == MEMCACHED_SUCCESS); + } + + rc= memcached_mget(memc, keys, key_length, 3); + assert(rc == MEMCACHED_SUCCESS); + + x= 0; + while (return_value= memcached_fetch(memc, return_key, &return_key_length, + &return_value_length, &flags, &rc)) + { + assert(return_value); + assert(rc == MEMCACHED_SUCCESS); + assert(key_length[x] == return_value_length); + assert(!memcmp(return_value, keys[x], return_value_length)); + x++; + } + + memcached_deinit(memc); +} + + int main(void) { /* Clean the server before beginning testing */ flush_test(); - init_test(); allocation_test(); connection_test(); @@ -253,6 +330,8 @@ int main(void) increment_test(); decrement_test(); + quit_test(); + mget_test(); /* Clean up whatever we might have left */ flush_test();