Found a bug where string might get allocated incorrectly.
} memcached_connection;
typedef enum {
- MEMCACHED_NOT_ALLOCATED= 0,
- MEMCACHED_ALLOCATED= 1,
+ MEMCACHED_NOT_ALLOCATED,
+ MEMCACHED_ALLOCATED,
} memcached_allocated;
struct memcached_server_st {
};
struct memcached_string_st {
+ memcached_st *root;
memcached_allocated is_allocated;
char *string;
char *end;
};
struct memcached_result_st {
+ memcached_allocated is_allocated;
+ memcached_st *root;
memcached_string_st key;
- memcached_string_st result;
+ memcached_string_st value;
uint16_t flags;
uint64_t cas;
};
char ** memcached_stat_get_keys(memcached_st *ptr, memcached_stat_st *stat,
memcached_return *error);
+/* Result Struct */
+#define memcache_result_key_value(A) memcached_string_value(A->key)
+#define memcache_result_key_length(A) memcached_string_length(A->key)
+#define memcache_result_result_value(A) memcached_string_value(A->value)
+#define memcache_result_result_length(A) memcached_string_length(A->value)
+#define memcache_result_flags(A) A->flags
+#define memcache_result_cas(A) A->cas
+
/* Some personal debugging functions */
#ifdef HAVE_DEBUG
#define WATCHPOINT fprintf(stderr, "\nWATCHPOINT %s:%d (%s)\n", __FILE__, __LINE__,__func__);fflush(stdout);
memcached_quit.c \
memcached_parse.c \
memcached_response.c \
+ memcached_result.c \
memcached_storage.c \
memcached_string.c \
memcached_stats.c \
#include "libmemcached_probes.h"
+#define MEMCACHED_BLOCK_SIZE 1024
+
typedef enum {
MEM_NO_BLOCK= (1 << 0),
MEM_TCP_NODELAY= (1 << 1),
#define memcached_server_response_decrement(A,B) A->hosts[B].stack_responses--
/* String Struct */
-#define memcached_string_length(A, B) (size_t)(B->end - B->string)
-#define memcached_string_size(A, B) B->current_size
-#define memcached_string_value(A, B) B->string
+#define memcached_string_length(A) (size_t)(A->end - A->string)
+#define memcached_string_set_length(A, B) A->end= A->string + B
+#define memcached_string_size(A) A->current_size
+#define memcached_string_value(A) A->string
memcached_string_st *memcached_string_create(memcached_st *ptr,
memcached_string_st *string,
size_t initial_size);
-memcached_return memcached_string_append_character(memcached_st *ptr,
- memcached_string_st *string,
+memcached_return memcached_string_check(memcached_string_st *string, size_t need);
+char *memcached_string_c_copy(memcached_string_st *string);
+memcached_return memcached_string_append_character(memcached_string_st *string,
char character);
-memcached_return memcached_string_append(memcached_st *ptr, memcached_string_st *string,
+memcached_return memcached_string_append(memcached_string_st *string,
char *value, size_t length);
-size_t memcached_string_backspace(memcached_st *ptr, memcached_string_st *string, size_t remove);
-memcached_return memcached_string_reset(memcached_st *ptr, memcached_string_st *string);
-void memcached_string_free(memcached_st *ptr, memcached_string_st *string);
+size_t memcached_string_backspace(memcached_string_st *string, size_t remove);
+memcached_return memcached_string_reset(memcached_string_st *string);
+void memcached_string_free(memcached_string_st *string);
+
+/* Result Struct */
+#define memcache_result_key_value(A) memcached_string_value(A->key)
+#define memcache_result_key_length(A) memcached_string_length(A->key)
+#define memcache_result_result_value(A) memcached_string_value(A->value)
+#define memcache_result_result_length(A) memcached_string_length(A->value)
+#define memcache_result_flags(A) A->flags
+#define memcache_result_cas(A) A->cas
+
+memcached_result_st *memcached_result_create(memcached_st *ptr,
+ memcached_result_st *result);
+void memcached_result_free(memcached_result_st *result);
#endif /* __COMMON_H__ */
memcached_st *memcached_create(memcached_st *ptr)
{
+ memcached_string_st *string_ptr;
if (!ptr)
{
ptr= (memcached_st *)malloc(sizeof(memcached_st));
{
memset(ptr, 0, sizeof(memcached_st));
}
+ string_ptr= memcached_string_create(ptr, &ptr->result_buffer, 0);
+ WATCHPOINT_ASSERT(string_ptr);
return ptr;
}
ptr->hosts= NULL;
}
+ memcached_string_free(&ptr->result_buffer);
+
if (ptr->is_allocated == MEMCACHED_ALLOCATED)
free(ptr);
else
memset(ptr, 0, sizeof(memcached_st));
}
+/*
+ clone is the destination, while ptr is the structure to clone.
+ If ptr is NULL the call is the same as if a memcached_create() was
+ called.
+*/
memcached_st *memcached_clone(memcached_st *clone, memcached_st *ptr)
{
memcached_return rc;
#include "common.h"
#include "memcached_io.h"
-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)
+static memcached_return memcached_value_fetch(memcached_st *ptr, char *key, size_t *key_length,
+ memcached_string_st *value,
+ uint16_t *flags,
+ char load_key,
+ unsigned int server_key)
{
+ memcached_return rc;
char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
char *string_ptr;
char *end_ptr;
memset(buffer, 0, MEMCACHED_DEFAULT_COMMAND_SIZE);
end_ptr= buffer + MEMCACHED_DEFAULT_COMMAND_SIZE;
- *value_length= 0;
*flags= 0;
- *error= memcached_response(ptr, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, server_key);
+ memcached_string_reset(value);
+
+ rc= memcached_response(ptr, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, server_key);
- if (*error == MEMCACHED_SUCCESS)
+ if (rc == MEMCACHED_SUCCESS)
{
char *next_ptr;
+ size_t value_length;
string_ptr= buffer;
string_ptr+= 6; /* "VALUE " */
goto read_error;
for (next_ptr= string_ptr; end_ptr > string_ptr && *string_ptr != ' '; string_ptr++);
- *value_length= (size_t)strtoll(next_ptr, &string_ptr, 10);
+ value_length= (size_t)strtoll(next_ptr, &string_ptr, 10);
if (end_ptr == string_ptr)
goto read_error;
if (end_ptr < string_ptr)
goto read_error;
- if (*value_length)
+ if (value_length)
{
size_t read_length;
size_t to_read;
- char *value;
char *value_ptr;
/* We add two bytes so that we can walk the \r\n */
- value= (char *)malloc(((*value_length) +2) * sizeof(char));
- if (!value)
+ rc= memcached_string_check(value, value_length+2);
+ if (rc != MEMCACHED_SUCCESS)
{
- *value_length= 0;
- *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
- return NULL;
+ value_length= 0;
+ return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
}
- memset(value, 0, ((*value_length) +2) * sizeof(char));
- value_ptr= value;
+ value_ptr= memcached_string_value(value);
read_length= 0;
/*
We read the \r\n into the string since not doing so is more
We are null terminating through, which will most likely make
some people lazy about using the return length.
*/
- to_read= (*value_length) + 2;
+ to_read= (value_length) + 2;
read_length= memcached_io_read(ptr, server_key,
value_ptr, to_read);
- if (read_length != (size_t)(*value_length + 2))
+ if (read_length != (size_t)(value_length + 2))
{
- free(value);
goto read_error;
}
- value[*value_length]= 0;
- value[(*value_length) + 1]= 0;
+ /* This next bit blows the API, but this is internal....*/
+ {
+ char *char_ptr;
+ char_ptr= memcached_string_value(value);;
+ char_ptr[value_length]= 0;
+ char_ptr[value_length + 1]= 0;
+ memcached_string_set_length(value, value_length);
+ }
- return value;
+ return MEMCACHED_SUCCESS;
}
}
- else if (*error == MEMCACHED_END)
- *error= MEMCACHED_NOTFOUND;
+ else if (rc == MEMCACHED_END)
+ rc= MEMCACHED_NOTFOUND;
+
+ return rc;
- return NULL;
read_error:
- *error= MEMCACHED_PARTIAL_READ;
- return NULL;
+ return MEMCACHED_PARTIAL_READ;
}
/*
char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
char *buf_ptr= buffer;
unsigned int server_key;
- char *value= NULL;
+ memcached_string_st *result_buffer;
LIBMEMCACHED_MEMCACHED_GET_START();
server_key= memcached_generate_hash(ptr, key, key_length);
+ result_buffer= &ptr->result_buffer;
*value_length= 0;
*error= memcached_connect(ptr, server_key);
goto error;
}
- value= memcached_value_fetch(ptr, key, &key_length, value_length, flags,
- error, 0, server_key);
+ *error= memcached_value_fetch(ptr, key, &key_length, result_buffer,
+ flags, 0, server_key);
+ *value_length= memcached_string_length(result_buffer);
if (*error == MEMCACHED_END && *value_length == 0)
{
*error= MEMCACHED_NOTFOUND;
LIBMEMCACHED_MEMCACHED_GET_END();
- return value;
+
+ return memcached_string_c_copy(result_buffer);
error:
- free(value);
*value_length= 0;
LIBMEMCACHED_MEMCACHED_GET_END();
uint16_t *flags,
memcached_return *error)
{
- char *value_check;
+ memcached_string_st *result_buffer;
+ result_buffer= &ptr->result_buffer;
while (ptr->cursor_server < ptr->number_of_hosts)
{
continue;
}
- value_check= memcached_value_fetch(ptr, key, key_length, value_length, flags,
- error, 1, ptr->cursor_server);
+ *error = memcached_value_fetch(ptr, key, key_length, result_buffer,
+ flags, 1, ptr->cursor_server);
+ *value_length= memcached_string_length(result_buffer);
if (*error == MEMCACHED_NOTFOUND)
ptr->cursor_server++;
else if (*error != MEMCACHED_SUCCESS)
return NULL;
else
- return value_check;
+ return memcached_string_c_copy(result_buffer);
}
memcached_return memcached_string_check(memcached_string_st *string, size_t need)
{
- if (need > (size_t)(string->current_size - (size_t)(string->end - string->string)))
+ if (need && need > (size_t)(string->current_size - (size_t)(string->end - string->string)))
{
size_t current_offset= string->end - string->string;
char *new_value;
memset(string, 0, sizeof(memcached_string_st));
string->is_allocated= MEMCACHED_ALLOCATED;
}
- string->end= string->string;
- string->block_size= initial_size;
+ string->block_size= MEMCACHED_BLOCK_SIZE;
+ string->root= ptr;
rc= memcached_string_check(string, initial_size);
if (rc != MEMCACHED_SUCCESS)
return string;
}
-memcached_return memcached_string_append_character(memcached_st *ptr,
- memcached_string_st *string,
+memcached_return memcached_string_append_character(memcached_string_st *string,
char character)
{
memcached_return rc;
return MEMCACHED_SUCCESS;
}
-memcached_return memcached_string_append(memcached_st *ptr, memcached_string_st *string,
+memcached_return memcached_string_append(memcached_string_st *string,
char *value, size_t length)
{
memcached_return rc;
return MEMCACHED_SUCCESS;
}
-size_t memcached_string_backspace(memcached_st *ptr, memcached_string_st *string, size_t remove)
+size_t memcached_string_backspace(memcached_string_st *string, size_t remove)
{
if (string->end - string->string > remove)
{
return remove;
}
-memcached_return memcached_string_reset(memcached_st *ptr, memcached_string_st *string)
+char *memcached_string_c_copy(memcached_string_st *string)
+{
+ char *c_ptr;
+ c_ptr= (char *)malloc(memcached_string_length(string) * sizeof(char));
+ if (!c_ptr)
+ return NULL;
+
+ memcpy(c_ptr, memcached_string_value(string), memcached_string_length(string));
+
+ return c_ptr;
+}
+
+memcached_return memcached_string_reset(memcached_string_st *string)
{
string->end= string->string;
return MEMCACHED_SUCCESS;
}
-void memcached_string_free(memcached_st *ptr, memcached_string_st *string)
+void memcached_string_free(memcached_string_st *string)
{
- free(string->string);
+ if (string->string)
+ free(string->string);
if (string->is_allocated == MEMCACHED_ALLOCATED)
free(string);
}
free(keys);
}
+void result_static(memcached_st *memc)
+{
+ memcached_result_st result;
+ memcached_result_st *result_ptr;
+
+ result_ptr= memcached_result_create(memc, &result);
+ assert(result.is_allocated == MEMCACHED_NOT_ALLOCATED);
+ assert(result_ptr);
+ memcached_result_free(&result);
+}
+
+void result_alloc(memcached_st *memc)
+{
+ memcached_result_st *result;
+
+ result= memcached_result_create(memc, NULL);
+ assert(result);
+ memcached_result_free(result);
+}
+
void string_static_null(memcached_st *memc)
{
memcached_string_st string;
string_ptr= memcached_string_create(memc, &string, 0);
assert(string.is_allocated == MEMCACHED_NOT_ALLOCATED);
assert(string_ptr);
- memcached_string_free(memc, &string);
+ memcached_string_free(&string);
}
void string_alloc_null(memcached_st *memc)
string= memcached_string_create(memc, NULL, 0);
assert(string);
- memcached_string_free(memc, string);
+ memcached_string_free(string);
}
void string_alloc_with_size(memcached_st *memc)
string= memcached_string_create(memc, NULL, 1024);
assert(string);
- memcached_string_free(memc, string);
+ memcached_string_free(string);
}
void string_alloc_with_size_toobig(memcached_st *memc)
for (x= 0; x < 1024; x++)
{
memcached_return rc;
- rc= memcached_string_append(memc, string, buffer, SMALL_STRING_LEN);
+ rc= memcached_string_append(string, buffer, SMALL_STRING_LEN);
assert(rc == MEMCACHED_SUCCESS);
}
- memcached_string_free(memc, string);
+ memcached_string_free(string);
}
void string_alloc_append_toobig(memcached_st *memc)
for (x= 0; x < 1024; x++)
{
- rc= memcached_string_append(memc, string, buffer, SMALL_STRING_LEN);
+ rc= memcached_string_append(string, buffer, SMALL_STRING_LEN);
assert(rc == MEMCACHED_SUCCESS);
}
- rc= memcached_string_append(memc, string, buffer, INT64_MAX);
+ rc= memcached_string_append(string, buffer, INT64_MAX);
assert(rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE);
- memcached_string_free(memc, string);
+ memcached_string_free(string);
}
void add_host_test1(memcached_st *memc)
{0, 0, 0}
};
+ test_st result_tests[] ={
+ {"result static", 0, result_static},
+ {"result alloc", 0, result_alloc},
+ {0, 0, 0}
+ };
+
test_st user_tests[] ={
{"user_supplied_bug1", 0, user_supplied_bug1 },
{"user_supplied_bug2", 0, user_supplied_bug2 },
{"unix_socket", pre_unix_socket, 0, tests},
{"unix_socket_nodelay", pre_nodelay, 0, tests},
{"string", 0, 0, string_tests},
+ {"result", 0, 0, result_tests},
{"user", 0, 0, user_tests},
{0, 0, 0, 0}
};