#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);
memcached_allocated is_allocated;
memcached_host_st *hosts;
unsigned int number_of_hosts;
+ unsigned int cursor_server;
char connected;
};
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. */
#include <memcached.h>
-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);
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;
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;
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;
+}
#include <memcached.h>
/*
- 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;
}
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])
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);
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));
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();
increment_test();
decrement_test();
+ quit_test();
+ mget_test();
/* Clean up whatever we might have left */
flush_test();