From: Brian Aker Date: Thu, 28 Apr 2011 00:28:00 +0000 (-0700) Subject: Merge in conversion to C++. X-Git-Tag: 0.51~12^2 X-Git-Url: https://git.m6w6.name/?a=commitdiff_plain;h=ae6bc7501efd5aeaaee92dabe2da0ec2d1625c5b;p=awesomized%2Flibmemcached Merge in conversion to C++. --- diff --git a/Makefile.am b/Makefile.am index 80b2c03e..2c276016 100644 --- a/Makefile.am +++ b/Makefile.am @@ -35,6 +35,8 @@ EXTRA_DIST= \ include libtest/include.am include libmemcached/include.am +include libmemcached/protocol/include.am +include libmemcached/util/include.am include clients/include.am include libhashkit/include.am include tests/include.am @@ -93,6 +95,4 @@ lcov-clean: clean find . -name '*.gcno' | xargs rm -f find . -name '*.gcda' | xargs rm -f -CLEANFILES+= config/top.h - - +DISTCLEANFILES+= config/top.h diff --git a/clients/execute.c b/clients/execute.c deleted file mode 100644 index 0beaae4b..00000000 --- a/clients/execute.c +++ /dev/null @@ -1,131 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: - * - */ - -/* - Execute a memcached_set() a set of pairs. - Return the number of rows set. -*/ - -#include "config.h" -#include "execute.h" - -unsigned int execute_set(memcached_st *memc, pairs_st *pairs, unsigned int number_of) -{ - memcached_return_t rc; - unsigned int x; - unsigned int pairs_sent; - - for (x= 0, pairs_sent= 0; x < number_of; x++) - { - rc= memcached_set(memc, pairs[x].key, pairs[x].key_length, - pairs[x].value, pairs[x].value_length, - 0, 0); - if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_BUFFERED) - fprintf(stderr, "Failured on insert of %.*s\n", - (unsigned int)pairs[x].key_length, pairs[x].key); - else - pairs_sent++; - } - - return pairs_sent; -} - -/* - Execute a memcached_get() on a set of pairs. - Return the number of rows retrieved. -*/ -unsigned int execute_get(memcached_st *memc, pairs_st *pairs, unsigned int number_of) -{ - memcached_return_t rc; - unsigned int x; - unsigned int retrieved; - - - for (retrieved= 0,x= 0; x < number_of; x++) - { - char *value; - size_t value_length; - uint32_t flags; - unsigned int fetch_key; - - fetch_key= (unsigned int)((unsigned int)random() % number_of); - - value= memcached_get(memc, pairs[fetch_key].key, pairs[fetch_key].key_length, - &value_length, &flags, &rc); - - if (rc != MEMCACHED_SUCCESS) - fprintf(stderr, "Failured on read of %.*s\n", - (unsigned int)pairs[fetch_key].key_length, pairs[fetch_key].key); - else - retrieved++; - - free(value); - } - - return retrieved; -} - -/** - * Callback function to count the number of results - */ -static memcached_return_t callback_counter(const memcached_st *ptr, - memcached_result_st *result, - void *context) -{ - (void)ptr; - (void)result; - unsigned int *counter= (unsigned int *)context; - *counter= *counter + 1; - - return MEMCACHED_SUCCESS; -} - -/** - * Try to run a large mget to get all of the keys - * @param memc memcached handle - * @param keys the keys to get - * @param key_length the length of the keys - * @param number_of the number of keys to try to get - * @return the number of keys received - */ -unsigned int execute_mget(memcached_st *memc, - const char * const *keys, - size_t *key_length, - unsigned int number_of) -{ - unsigned int retrieved= 0; - memcached_execute_fn callbacks[1]= { [0]= &callback_counter }; - memcached_return_t rc; - rc= memcached_mget_execute(memc, keys, key_length, - (size_t)number_of, callbacks, &retrieved, 1); - - if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_NOTFOUND || - rc == MEMCACHED_BUFFERED || rc == MEMCACHED_END) - { - rc= memcached_fetch_execute(memc, callbacks, (void *)&retrieved, 1); - if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_NOTFOUND && rc != MEMCACHED_END) - { - fprintf(stderr, "Failed to execute mget: %s\n", - memcached_strerror(memc, rc)); - memcached_quit(memc); - return 0; - } - } - else - { - fprintf(stderr, "Failed to execute mget: %s\n", - memcached_strerror(memc, rc)); - memcached_quit(memc); - return 0; - } - - return retrieved; -} diff --git a/clients/execute.cc b/clients/execute.cc new file mode 100644 index 00000000..7f89f773 --- /dev/null +++ b/clients/execute.cc @@ -0,0 +1,131 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: + * + */ + +/* + Execute a memcached_set() a set of pairs. + Return the number of rows set. +*/ + +#include "config.h" +#include "execute.h" + +unsigned int execute_set(memcached_st *memc, pairs_st *pairs, unsigned int number_of) +{ + memcached_return_t rc; + unsigned int x; + unsigned int pairs_sent; + + for (x= 0, pairs_sent= 0; x < number_of; x++) + { + rc= memcached_set(memc, pairs[x].key, pairs[x].key_length, + pairs[x].value, pairs[x].value_length, + 0, 0); + if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_BUFFERED) + fprintf(stderr, "Failured on insert of %.*s\n", + (unsigned int)pairs[x].key_length, pairs[x].key); + else + pairs_sent++; + } + + return pairs_sent; +} + +/* + Execute a memcached_get() on a set of pairs. + Return the number of rows retrieved. +*/ +unsigned int execute_get(memcached_st *memc, pairs_st *pairs, unsigned int number_of) +{ + memcached_return_t rc; + unsigned int x; + unsigned int retrieved; + + + for (retrieved= 0,x= 0; x < number_of; x++) + { + char *value; + size_t value_length; + uint32_t flags; + unsigned int fetch_key; + + fetch_key= (unsigned int)((unsigned int)random() % number_of); + + value= memcached_get(memc, pairs[fetch_key].key, pairs[fetch_key].key_length, + &value_length, &flags, &rc); + + if (rc != MEMCACHED_SUCCESS) + fprintf(stderr, "Failured on read of %.*s\n", + (unsigned int)pairs[fetch_key].key_length, pairs[fetch_key].key); + else + retrieved++; + + free(value); + } + + return retrieved; +} + +/** + * Callback function to count the number of results + */ +static memcached_return_t callback_counter(const memcached_st *ptr, + memcached_result_st *result, + void *context) +{ + (void)ptr; + (void)result; + unsigned int *counter= (unsigned int *)context; + *counter= *counter + 1; + + return MEMCACHED_SUCCESS; +} + +/** + * Try to run a large mget to get all of the keys + * @param memc memcached handle + * @param keys the keys to get + * @param key_length the length of the keys + * @param number_of the number of keys to try to get + * @return the number of keys received + */ +unsigned int execute_mget(memcached_st *memc, + const char * const *keys, + size_t *key_length, + unsigned int number_of) +{ + unsigned int retrieved= 0; + memcached_execute_fn callbacks[]= { callback_counter }; + memcached_return_t rc; + rc= memcached_mget_execute(memc, keys, key_length, + (size_t)number_of, callbacks, &retrieved, 1); + + if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_NOTFOUND || + rc == MEMCACHED_BUFFERED || rc == MEMCACHED_END) + { + rc= memcached_fetch_execute(memc, callbacks, (void *)&retrieved, 1); + if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_NOTFOUND && rc != MEMCACHED_END) + { + fprintf(stderr, "Failed to execute mget: %s\n", + memcached_strerror(memc, rc)); + memcached_quit(memc); + return 0; + } + } + else + { + fprintf(stderr, "Failed to execute mget: %s\n", + memcached_strerror(memc, rc)); + memcached_quit(memc); + return 0; + } + + return retrieved; +} diff --git a/clients/execute.h b/clients/execute.h index 176c6fff..05678c4d 100644 --- a/clients/execute.h +++ b/clients/execute.h @@ -9,17 +9,22 @@ * */ -#ifndef CLIENTS_EXECUTE_H -#define CLIENTS_EXECUTE_H +#pragma once #include #include "libmemcached/memcached.h" -#include "generator.h" +#include "clients/generator.h" + +#ifdef __cplusplus +extern "C" { +#endif unsigned int execute_set(memcached_st *memc, pairs_st *pairs, unsigned int number_of); unsigned int execute_get(memcached_st *memc, pairs_st *pairs, unsigned int number_of); unsigned int execute_mget(memcached_st *memc, const char * const *keys, size_t *key_length, unsigned int number_of); -#endif +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/clients/generator.c b/clients/generator.c deleted file mode 100644 index 80b398b2..00000000 --- a/clients/generator.c +++ /dev/null @@ -1,96 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: - * - */ - -#include "config.h" - -#include -#include -#include -#include - -#include "generator.h" - -/* Use this for string generation */ -static const char ALPHANUMERICS[]= - "0123456789ABCDEFGHIJKLMNOPQRSTWXYZabcdefghijklmnopqrstuvwxyz"; - -#define ALPHANUMERICS_SIZE (sizeof(ALPHANUMERICS)-1) - -static size_t get_alpha_num(void) -{ - return (size_t)random() % ALPHANUMERICS_SIZE; -} - -static void get_random_string(char *buffer, size_t size) -{ - char *buffer_ptr= buffer; - - while (--size) - *buffer_ptr++= ALPHANUMERICS[get_alpha_num()]; - *buffer_ptr++= ALPHANUMERICS[get_alpha_num()]; -} - -void pairs_free(pairs_st *pairs) -{ - uint32_t x; - - if (! pairs) - return; - - /* We free until we hit the null pair we stores during creation */ - for (x= 0; pairs[x].key; x++) - { - free(pairs[x].key); - if (pairs[x].value) - free(pairs[x].value); - } - - free(pairs); -} - -pairs_st *pairs_generate(uint64_t number_of, size_t value_length) -{ - unsigned int x; - pairs_st *pairs; - - pairs= (pairs_st*)calloc((size_t)number_of + 1, sizeof(pairs_st)); - - if (!pairs) - goto error; - - for (x= 0; x < number_of; x++) - { - pairs[x].key= (char *)calloc(100, sizeof(char)); - if (!pairs[x].key) - goto error; - get_random_string(pairs[x].key, 100); - pairs[x].key_length= 100; - - if (value_length) - { - pairs[x].value= (char *)calloc(value_length, sizeof(char)); - if (!pairs[x].value) - goto error; - get_random_string(pairs[x].value, value_length); - pairs[x].value_length= value_length; - } - else - { - pairs[x].value= NULL; - pairs[x].value_length= 0; - } - } - - return pairs; -error: - fprintf(stderr, "Memory Allocation failure in pairs_generate.\n"); - exit(0); -} diff --git a/clients/generator.cc b/clients/generator.cc new file mode 100644 index 00000000..80b398b2 --- /dev/null +++ b/clients/generator.cc @@ -0,0 +1,96 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: + * + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "generator.h" + +/* Use this for string generation */ +static const char ALPHANUMERICS[]= + "0123456789ABCDEFGHIJKLMNOPQRSTWXYZabcdefghijklmnopqrstuvwxyz"; + +#define ALPHANUMERICS_SIZE (sizeof(ALPHANUMERICS)-1) + +static size_t get_alpha_num(void) +{ + return (size_t)random() % ALPHANUMERICS_SIZE; +} + +static void get_random_string(char *buffer, size_t size) +{ + char *buffer_ptr= buffer; + + while (--size) + *buffer_ptr++= ALPHANUMERICS[get_alpha_num()]; + *buffer_ptr++= ALPHANUMERICS[get_alpha_num()]; +} + +void pairs_free(pairs_st *pairs) +{ + uint32_t x; + + if (! pairs) + return; + + /* We free until we hit the null pair we stores during creation */ + for (x= 0; pairs[x].key; x++) + { + free(pairs[x].key); + if (pairs[x].value) + free(pairs[x].value); + } + + free(pairs); +} + +pairs_st *pairs_generate(uint64_t number_of, size_t value_length) +{ + unsigned int x; + pairs_st *pairs; + + pairs= (pairs_st*)calloc((size_t)number_of + 1, sizeof(pairs_st)); + + if (!pairs) + goto error; + + for (x= 0; x < number_of; x++) + { + pairs[x].key= (char *)calloc(100, sizeof(char)); + if (!pairs[x].key) + goto error; + get_random_string(pairs[x].key, 100); + pairs[x].key_length= 100; + + if (value_length) + { + pairs[x].value= (char *)calloc(value_length, sizeof(char)); + if (!pairs[x].value) + goto error; + get_random_string(pairs[x].value, value_length); + pairs[x].value_length= value_length; + } + else + { + pairs[x].value= NULL; + pairs[x].value_length= 0; + } + } + + return pairs; +error: + fprintf(stderr, "Memory Allocation failure in pairs_generate.\n"); + exit(0); +} diff --git a/clients/generator.h b/clients/generator.h index 196e71b2..e60bfb38 100644 --- a/clients/generator.h +++ b/clients/generator.h @@ -13,8 +13,7 @@ Code to generate data to be pushed into memcached */ -#ifndef __GENERATOR_H__ -#define __GENERATOR_H__ +#pragma once typedef struct pairs_st pairs_st; @@ -25,7 +24,13 @@ struct pairs_st { size_t value_length; }; +#ifdef __cplusplus +extern "C" { +#endif + pairs_st *pairs_generate(uint64_t number_of, size_t value_length); void pairs_free(pairs_st *pairs); +#ifdef __cplusplus +} // extern "C" #endif diff --git a/clients/include.am b/clients/include.am index 07d49a70..05b1ed7e 100644 --- a/clients/include.am +++ b/clients/include.am @@ -2,10 +2,10 @@ # included from Top Level Makefile.am # All paths should be given relative to the root -CLIENTS_LDADDS = \ - $(LIBM) \ - clients/libutilities.la \ - libmemcached/libmemcached.la +CLIENTS_LDADDS= \ + $(LIBM) \ + clients/libutilities.la \ + libmemcached/libmemcached.la if HAVE_SASL CLIENTS_LDADDS+= $(LIBSASL) @@ -44,53 +44,52 @@ noinst_HEADERS+= \ clients/utilities.h noinst_LTLIBRARIES+= clients/libutilities.la -clients_libutilities_la_SOURCES= clients/utilities.c +clients_libutilities_la_SOURCES= clients/utilities.cc noinst_LTLIBRARIES+= clients/libgenexec.la -clients_libgenexec_la_SOURCES= clients/generator.c clients/execute.c +clients_libgenexec_la_SOURCES= clients/generator.cc clients/execute.cc -clients_memcat_SOURCES= clients/memcat.c +clients_memcat_SOURCES= clients/memcat.cc clients_memcat_LDADD= $(CLIENTS_LDADDS) clients_memparse_SOURCES= clients/memparse.cc clients_memparse_LDADD= $(CLIENTS_LDADDS) -clients_memcp_SOURCES= clients/memcp.c +clients_memcp_SOURCES= clients/memcp.cc clients_memcp_LDADD= $(CLIENTS_LDADDS) -clients_memdump_SOURCES= clients/memdump.c +clients_memdump_SOURCES= clients/memdump.cc clients_memdump_LDADD= $(CLIENTS_LDADDS) -clients_memstat_SOURCES= clients/memstat.c +clients_memstat_SOURCES= clients/memstat.cc clients_memstat_LDADD= $(CLIENTS_LDADDS) -clients_memrm_SOURCES= clients/memrm.c +clients_memrm_SOURCES= clients/memrm.cc clients_memrm_LDADD= $(CLIENTS_LDADDS) -clients_memflush_SOURCES= clients/memflush.c +clients_memflush_SOURCES= clients/memflush.cc clients_memflush_LDADD= $(CLIENTS_LDADDS) -clients_memerror_SOURCES= clients/memerror.c +clients_memerror_SOURCES= clients/memerror.cc clients_memerror_LDADD= $(CLIENTS_LDADDS) -clients_memslap_SOURCES = clients/memslap.c +clients_memslap_SOURCES = clients/memslap.cc clients_memslap_LDADD = $(PTHREAD_LIBS) clients/libgenexec.la $(CLIENTS_LDADDS) clients_memaslap_SOURCES= \ - clients/memaslap.c \ - clients/ms_conn.c \ - clients/ms_setting.c \ - clients/ms_sigsegv.c \ - clients/ms_stats.c \ - clients/ms_task.c \ - clients/ms_thread.c + clients/memaslap.c \ + clients/ms_conn.c \ + clients/ms_setting.c \ + clients/ms_sigsegv.c \ + clients/ms_stats.c \ + clients/ms_task.c \ + clients/ms_thread.c clients_memaslap_LDADD= $(LTLIBEVENT) clients/libgenexec.la $(CLIENTS_LDADDS) -clients_memcapable_SOURCES= clients/memcapable.c +clients_memcapable_SOURCES= \ + clients/memcapable.cc \ + libmemcached/byteorder.cc clients_memcapable_LDADD= $(CLIENTS_LDADDS) -if BUILD_BYTEORDER -clients_memcapable_LDADD+= libmemcached/libbyteorder.la -endif test-start-server: clients/memflush --servers=localhost diff --git a/clients/memcapable.c b/clients/memcapable.c deleted file mode 100644 index 69d2557e..00000000 --- a/clients/memcapable.c +++ /dev/null @@ -1,2087 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: - * - */ - -/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ -#undef NDEBUG -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include "utilities.h" - -#ifdef linux -/* /usr/include/netinet/in.h defines macros from ntohs() to _bswap_nn to - * optimize the conversion functions, but the prototypes generate warnings - * from gcc. The conversion methods isn't the bottleneck for my app, so - * just remove the warnings by undef'ing the optimization .. - */ -#undef ntohs -#undef ntohl -#endif - -/* Should we generate coredumps when we enounter an error (-c) */ -static bool do_core= false; -/* connection to the server */ -static memcached_socket_t sock; -/* Should the output from test failures be verbose or quiet? */ -static bool verbose= false; - -/* The number of seconds to wait for an IO-operation */ -static int timeout= 2; - -/* - * Instead of having to cast between the different datatypes we create - * a union of all of the different types of pacages we want to send. - * A lot of the different commands use the same packet layout, so I'll - * just define the different types I need. The typedefs only contain - * the header of the message, so we need some space for keys and body - * To avoid to have to do multiple writes, lets add a chunk of memory - * to use. 1k should be more than enough for header, key and body. - */ -typedef union -{ - protocol_binary_request_no_extras plain; - protocol_binary_request_flush flush; - protocol_binary_request_incr incr; - protocol_binary_request_set set; - char bytes[1024]; -} command; - -typedef union -{ - protocol_binary_response_no_extras plain; - protocol_binary_response_incr incr; - protocol_binary_response_decr decr; - char bytes[1024]; -} response; - -enum test_return -{ - TEST_SKIP, TEST_PASS, TEST_PASS_RECONNECT, TEST_FAIL -}; - -/** - * Try to get an addrinfo struct for a given port on a given host - */ -static struct addrinfo *lookuphost(const char *hostname, const char *port) -{ - struct addrinfo *ai= 0; - struct addrinfo hints= {.ai_family=AF_UNSPEC, - .ai_protocol=IPPROTO_TCP, - .ai_socktype=SOCK_STREAM}; - int error= getaddrinfo(hostname, port, &hints, &ai); - - if (error != 0) - { - if (error != EAI_SYSTEM) - fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error)); - else - perror("getaddrinfo()"); - } - - return ai; -} - -/** - * Set the socket in nonblocking mode - * @return -1 if failure, the socket otherwise - */ -static memcached_socket_t set_noblock(void) -{ -#ifdef WIN32 - u_long arg = 1; - if (ioctlsocket(sock, FIONBIO, &arg) == SOCKET_ERROR) - { - perror("Failed to set nonblocking io"); - closesocket(sock); - return INVALID_SOCKET; - } -#else - int flags= fcntl(sock, F_GETFL, 0); - if (flags == -1) - { - perror("Failed to get socket flags"); - closesocket(sock); - return INVALID_SOCKET; - } - - if ((flags & O_NONBLOCK) != O_NONBLOCK) - { - if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) - { - perror("Failed to set socket to nonblocking mode"); - closesocket(sock); - return INVALID_SOCKET; - } - } -#endif - return sock; -} - -/** - * Try to open a connection to the server - * @param hostname the name of the server to connect to - * @param port the port number (or service) to connect to - * @return positive integer if success, -1 otherwise - */ -static memcached_socket_t connect_server(const char *hostname, const char *port) -{ - struct addrinfo *ai= lookuphost(hostname, port); - sock= INVALID_SOCKET; - if (ai != NULL) - { - if ((sock= socket(ai->ai_family, ai->ai_socktype, - ai->ai_protocol)) != INVALID_SOCKET) - { - if (connect(sock, ai->ai_addr, ai->ai_addrlen) == SOCKET_ERROR) - { - fprintf(stderr, "Failed to connect socket: %s\n", - strerror(get_socket_errno())); - closesocket(sock); - sock= INVALID_SOCKET; - } - else - { - sock= set_noblock(); - } - } - else - fprintf(stderr, "Failed to create socket: %s\n", - strerror(get_socket_errno())); - - freeaddrinfo(ai); - } - - return sock; -} - -static ssize_t timeout_io_op(memcached_socket_t fd, short direction, void *buf, size_t len) -{ - ssize_t ret; - - if (direction == POLLOUT) - ret= send(fd, buf, len, 0); - else - ret= recv(fd, buf, len, 0); - - if (ret == SOCKET_ERROR && get_socket_errno() == EWOULDBLOCK) { - struct pollfd fds= { - .events= direction, - .fd= fd - }; - - int err= poll(&fds, 1, timeout * 1000); - - if (err == 1) - { - if (direction == POLLOUT) - ret= send(fd, buf, len, 0); - else - ret= recv(fd, buf, len, 0); - } - else if (err == 0) - { - errno= ETIMEDOUT; - } - else - { - perror("Failed to poll"); - return -1; - } - } - - return ret; -} - -/** - * Ensure that an expression is true. If it isn't print out a message similar - * to assert() and create a coredump if the user wants that. If not an error - * message is returned. - * - */ -static enum test_return ensure(bool val, const char *expression, const char *file, int line) -{ - if (!val) - { - if (verbose) - fprintf(stderr, "\n%s:%d: %s", file, line, expression); - - if (do_core) - abort(); - - return TEST_FAIL; - } - - return TEST_PASS; -} - -#define verify(expression) do { if (ensure(expression, #expression, __FILE__, __LINE__) == TEST_FAIL) return TEST_FAIL; } while (0) -#define execute(expression) do { if (ensure(expression == TEST_PASS, #expression, __FILE__, __LINE__) == TEST_FAIL) return TEST_FAIL; } while (0) - -/** - * Send a chunk of memory over the socket (retry if the call is iterrupted - */ -static enum test_return retry_write(const void* buf, size_t len) -{ - size_t offset= 0; - const char* ptr= buf; - - do - { - size_t num_bytes= len - offset; - ssize_t nw= timeout_io_op(sock, POLLOUT, (void*)(ptr + offset), num_bytes); - if (nw == -1) - verify(get_socket_errno() == EINTR || get_socket_errno() == EAGAIN); - else - offset+= (size_t)nw; - } while (offset < len); - - return TEST_PASS; -} - -/** - * Resend a packet to the server (All fields in the command header should - * be in network byte order) - */ -static enum test_return resend_packet(command *cmd) -{ - size_t length= sizeof (protocol_binary_request_no_extras) + - ntohl(cmd->plain.message.header.request.bodylen); - - execute(retry_write(cmd, length)); - return TEST_PASS; -} - -/** - * Send a command to the server. The command header needs to be updated - * to network byte order - */ -static enum test_return send_packet(command *cmd) -{ - /* Fix the byteorder of the header */ - cmd->plain.message.header.request.keylen= - ntohs(cmd->plain.message.header.request.keylen); - cmd->plain.message.header.request.bodylen= - ntohl(cmd->plain.message.header.request.bodylen); - cmd->plain.message.header.request.cas= - ntohll(cmd->plain.message.header.request.cas); - - execute(resend_packet(cmd)); - return TEST_PASS; -} - -/** - * Read a fixed length chunk of data from the server - */ -static enum test_return retry_read(void *buf, size_t len) -{ - size_t offset= 0; - do - { - ssize_t nr= timeout_io_op(sock, POLLIN, ((char*) buf) + offset, len - offset); - switch (nr) { - case -1 : - fprintf(stderr, "Errno: %d %s\n", get_socket_errno(), strerror(errno)); - verify(get_socket_errno() == EINTR || get_socket_errno() == EAGAIN); - break; - case 0: - return TEST_FAIL; - default: - offset+= (size_t)nr; - } - } while (offset < len); - - return TEST_PASS; -} - -/** - * Receive a response from the server and conver the fields in the header - * to local byte order - */ -static enum test_return recv_packet(response *rsp) -{ - execute(retry_read(rsp, sizeof(protocol_binary_response_no_extras))); - - /* Fix the byte order in the packet header */ - rsp->plain.message.header.response.keylen= - ntohs(rsp->plain.message.header.response.keylen); - rsp->plain.message.header.response.status= - ntohs(rsp->plain.message.header.response.status); - rsp->plain.message.header.response.bodylen= - ntohl(rsp->plain.message.header.response.bodylen); - rsp->plain.message.header.response.cas= - ntohll(rsp->plain.message.header.response.cas); - - size_t bodysz= rsp->plain.message.header.response.bodylen; - if (bodysz > 0) - execute(retry_read(rsp->bytes + sizeof (protocol_binary_response_no_extras), bodysz)); - - return TEST_PASS; -} - -/** - * Create a storage command (add, set, replace etc) - * - * @param cmd destination buffer - * @param cc the storage command to create - * @param key the key to store - * @param keylen the length of the key - * @param dta the data to store with the key - * @param dtalen the length of the data to store with the key - * @param flags the flags to store along with the key - * @param exptime the expiry time for the key - */ -static void storage_command(command *cmd, - uint8_t cc, - const void* key, - size_t keylen, - const void* dta, - size_t dtalen, - uint32_t flags, - uint32_t exptime) -{ - /* all of the storage commands use the same command layout */ - protocol_binary_request_set *request= &cmd->set; - - memset(request, 0, sizeof (*request)); - request->message.header.request.magic= PROTOCOL_BINARY_REQ; - request->message.header.request.opcode= cc; - request->message.header.request.keylen= (uint16_t)keylen; - request->message.header.request.extlen= 8; - request->message.header.request.bodylen= (uint32_t)(keylen + 8 + dtalen); - request->message.header.request.opaque= 0xdeadbeef; - request->message.body.flags= flags; - request->message.body.expiration= exptime; - - off_t key_offset= sizeof (protocol_binary_request_no_extras) + 8; - memcpy(cmd->bytes + key_offset, key, keylen); - if (dta != NULL) - memcpy(cmd->bytes + key_offset + keylen, dta, dtalen); -} - -/** - * Create a basic command to send to the server - * @param cmd destination buffer - * @param cc the command to create - * @param key the key to store - * @param keylen the length of the key - * @param dta the data to store with the key - * @param dtalen the length of the data to store with the key - */ -static void raw_command(command *cmd, - uint8_t cc, - const void* key, - size_t keylen, - const void* dta, - size_t dtalen) -{ - /* all of the storage commands use the same command layout */ - memset(cmd, 0, sizeof (*cmd)); - cmd->plain.message.header.request.magic= PROTOCOL_BINARY_REQ; - cmd->plain.message.header.request.opcode= cc; - cmd->plain.message.header.request.keylen= (uint16_t)keylen; - cmd->plain.message.header.request.bodylen= (uint32_t)(keylen + dtalen); - cmd->plain.message.header.request.opaque= 0xdeadbeef; - - off_t key_offset= sizeof (protocol_binary_request_no_extras); - - if (key != NULL) - memcpy(cmd->bytes + key_offset, key, keylen); - - if (dta != NULL) - memcpy(cmd->bytes + key_offset + keylen, dta, dtalen); -} - -/** - * Create the flush command - * @param cmd destination buffer - * @param cc the command to create (FLUSH/FLUSHQ) - * @param exptime when to flush - * @param use_extra to force using of the extra field? - */ -static void flush_command(command *cmd, - uint8_t cc, uint32_t exptime, bool use_extra) -{ - memset(cmd, 0, sizeof (cmd->flush)); - cmd->flush.message.header.request.magic= PROTOCOL_BINARY_REQ; - cmd->flush.message.header.request.opcode= cc; - cmd->flush.message.header.request.opaque= 0xdeadbeef; - - if (exptime != 0 || use_extra) - { - cmd->flush.message.header.request.extlen= 4; - cmd->flush.message.body.expiration= htonl(exptime); - cmd->flush.message.header.request.bodylen= 4; - } -} - -/** - * Create a incr/decr command - * @param cc the cmd to create (FLUSH/FLUSHQ) - * @param key the key to operate on - * @param keylen the number of bytes in the key - * @param delta the number to add/subtract - * @param initial the initial value if the key doesn't exist - * @param exptime when the key should expire if it isn't set - */ -static void arithmetic_command(command *cmd, - uint8_t cc, - const void* key, - size_t keylen, - uint64_t delta, - uint64_t initial, - uint32_t exptime) -{ - memset(cmd, 0, sizeof (cmd->incr)); - cmd->incr.message.header.request.magic= PROTOCOL_BINARY_REQ; - cmd->incr.message.header.request.opcode= cc; - cmd->incr.message.header.request.keylen= (uint16_t)keylen; - cmd->incr.message.header.request.extlen= 20; - cmd->incr.message.header.request.bodylen= (uint32_t)(keylen + 20); - cmd->incr.message.header.request.opaque= 0xdeadbeef; - cmd->incr.message.body.delta= htonll(delta); - cmd->incr.message.body.initial= htonll(initial); - cmd->incr.message.body.expiration= htonl(exptime); - - off_t key_offset= sizeof (protocol_binary_request_no_extras) + 20; - memcpy(cmd->bytes + key_offset, key, keylen); -} - -/** - * Validate the response header from the server - * @param rsp the response to check - * @param cc the expected command - * @param status the expected status - */ -static enum test_return do_validate_response_header(response *rsp, - uint8_t cc, uint16_t status) -{ - verify(rsp->plain.message.header.response.magic == PROTOCOL_BINARY_RES); - verify(rsp->plain.message.header.response.opcode == cc); - verify(rsp->plain.message.header.response.datatype == PROTOCOL_BINARY_RAW_BYTES); - verify(rsp->plain.message.header.response.status == status); - verify(rsp->plain.message.header.response.opaque == 0xdeadbeef); - - if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) - { - switch (cc) { - case PROTOCOL_BINARY_CMD_ADDQ: - case PROTOCOL_BINARY_CMD_APPENDQ: - case PROTOCOL_BINARY_CMD_DECREMENTQ: - case PROTOCOL_BINARY_CMD_DELETEQ: - case PROTOCOL_BINARY_CMD_FLUSHQ: - case PROTOCOL_BINARY_CMD_INCREMENTQ: - case PROTOCOL_BINARY_CMD_PREPENDQ: - case PROTOCOL_BINARY_CMD_QUITQ: - case PROTOCOL_BINARY_CMD_REPLACEQ: - case PROTOCOL_BINARY_CMD_SETQ: - verify("Quiet command shouldn't return on success" == NULL); - default: - break; - } - - switch (cc) { - case PROTOCOL_BINARY_CMD_ADD: - case PROTOCOL_BINARY_CMD_REPLACE: - case PROTOCOL_BINARY_CMD_SET: - case PROTOCOL_BINARY_CMD_APPEND: - case PROTOCOL_BINARY_CMD_PREPEND: - verify(rsp->plain.message.header.response.keylen == 0); - verify(rsp->plain.message.header.response.extlen == 0); - verify(rsp->plain.message.header.response.bodylen == 0); - verify(rsp->plain.message.header.response.cas != 0); - break; - case PROTOCOL_BINARY_CMD_FLUSH: - case PROTOCOL_BINARY_CMD_NOOP: - case PROTOCOL_BINARY_CMD_QUIT: - case PROTOCOL_BINARY_CMD_DELETE: - verify(rsp->plain.message.header.response.keylen == 0); - verify(rsp->plain.message.header.response.extlen == 0); - verify(rsp->plain.message.header.response.bodylen == 0); - verify(rsp->plain.message.header.response.cas == 0); - break; - - case PROTOCOL_BINARY_CMD_DECREMENT: - case PROTOCOL_BINARY_CMD_INCREMENT: - verify(rsp->plain.message.header.response.keylen == 0); - verify(rsp->plain.message.header.response.extlen == 0); - verify(rsp->plain.message.header.response.bodylen == 8); - verify(rsp->plain.message.header.response.cas != 0); - break; - - case PROTOCOL_BINARY_CMD_STAT: - verify(rsp->plain.message.header.response.extlen == 0); - /* key and value exists in all packets except in the terminating */ - verify(rsp->plain.message.header.response.cas == 0); - break; - - case PROTOCOL_BINARY_CMD_VERSION: - verify(rsp->plain.message.header.response.keylen == 0); - verify(rsp->plain.message.header.response.extlen == 0); - verify(rsp->plain.message.header.response.bodylen != 0); - verify(rsp->plain.message.header.response.cas == 0); - break; - - case PROTOCOL_BINARY_CMD_GET: - case PROTOCOL_BINARY_CMD_GETQ: - verify(rsp->plain.message.header.response.keylen == 0); - verify(rsp->plain.message.header.response.extlen == 4); - verify(rsp->plain.message.header.response.cas != 0); - break; - - case PROTOCOL_BINARY_CMD_GETK: - case PROTOCOL_BINARY_CMD_GETKQ: - verify(rsp->plain.message.header.response.keylen != 0); - verify(rsp->plain.message.header.response.extlen == 4); - verify(rsp->plain.message.header.response.cas != 0); - break; - - default: - /* Undefined command code */ - break; - } - } - else - { - verify(rsp->plain.message.header.response.cas == 0); - verify(rsp->plain.message.header.response.extlen == 0); - if (cc != PROTOCOL_BINARY_CMD_GETK) - { - verify(rsp->plain.message.header.response.keylen == 0); - } - } - - return TEST_PASS; -} - -/* We call verify(validate_response_header), but that macro - * expects a boolean expression, and the function returns - * an enum.... Let's just create a macro to avoid cluttering - * the code with all of the == TEST_PASS ;-) - */ -#define validate_response_header(a,b,c) \ - do_validate_response_header(a,b,c) == TEST_PASS - - -static enum test_return send_binary_noop(void) -{ - command cmd; - raw_command(&cmd, PROTOCOL_BINARY_CMD_NOOP, NULL, 0, NULL, 0); - execute(send_packet(&cmd)); - return TEST_PASS; -} - -static enum test_return receive_binary_noop(void) -{ - response rsp; - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_NOOP, - PROTOCOL_BINARY_RESPONSE_SUCCESS)); - return TEST_PASS; -} - -static enum test_return test_binary_noop(void) -{ - execute(send_binary_noop()); - execute(receive_binary_noop()); - return TEST_PASS; -} - -static enum test_return test_binary_quit_impl(uint8_t cc) -{ - command cmd; - response rsp; - raw_command(&cmd, cc, NULL, 0, NULL, 0); - - execute(send_packet(&cmd)); - if (cc == PROTOCOL_BINARY_CMD_QUIT) - { - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_QUIT, - PROTOCOL_BINARY_RESPONSE_SUCCESS)); - } - - /* Socket should be closed now, read should return EXIT_SUCCESS */ - verify(timeout_io_op(sock, POLLIN, rsp.bytes, sizeof(rsp.bytes)) == 0); - - return TEST_PASS_RECONNECT; -} - -static enum test_return test_binary_quit(void) -{ - return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUIT); -} - -static enum test_return test_binary_quitq(void) -{ - return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUITQ); -} - -static enum test_return test_binary_set_impl(const char* key, uint8_t cc) -{ - command cmd; - response rsp; - - uint64_t value= 0xdeadbeefdeadcafe; - storage_command(&cmd, cc, key, strlen(key), &value, sizeof (value), 0, 0); - - /* set should always work */ - for (int ii= 0; ii < 10; ii++) - { - if (ii == 0) - execute(send_packet(&cmd)); - else - execute(resend_packet(&cmd)); - - if (cc == PROTOCOL_BINARY_CMD_SET) - { - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); - } - else - execute(test_binary_noop()); - } - - /* - * We need to get the current CAS id, and at this time we haven't - * verified that we have a working get - */ - if (cc == PROTOCOL_BINARY_CMD_SETQ) - { - cmd.set.message.header.request.opcode= PROTOCOL_BINARY_CMD_SET; - execute(resend_packet(&cmd)); - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_SET, - PROTOCOL_BINARY_RESPONSE_SUCCESS)); - cmd.set.message.header.request.opcode= PROTOCOL_BINARY_CMD_SETQ; - } - - /* try to set with the correct CAS value */ - cmd.plain.message.header.request.cas= - htonll(rsp.plain.message.header.response.cas); - execute(resend_packet(&cmd)); - if (cc == PROTOCOL_BINARY_CMD_SET) - { - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); - } - else - execute(test_binary_noop()); - - /* try to set with an incorrect CAS value */ - cmd.plain.message.header.request.cas= - htonll(rsp.plain.message.header.response.cas - 1); - execute(resend_packet(&cmd)); - execute(send_binary_noop()); - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS)); - execute(receive_binary_noop()); - - return TEST_PASS; -} - -static enum test_return test_binary_set(void) -{ - return test_binary_set_impl("test_binary_set", PROTOCOL_BINARY_CMD_SET); -} - -static enum test_return test_binary_setq(void) -{ - return test_binary_set_impl("test_binary_setq", PROTOCOL_BINARY_CMD_SETQ); -} - -static enum test_return test_binary_add_impl(const char* key, uint8_t cc) -{ - command cmd; - response rsp; - uint64_t value= 0xdeadbeefdeadcafe; - storage_command(&cmd, cc, key, strlen(key), &value, sizeof (value), 0, 0); - - /* first add should work, rest of them should fail (even with cas - as wildcard */ - for (int ii=0; ii < 10; ii++) - { - if (ii == 0) - execute(send_packet(&cmd)); - else - execute(resend_packet(&cmd)); - - if (cc == PROTOCOL_BINARY_CMD_ADD || ii > 0) - { - uint16_t expected_result; - if (ii == 0) - expected_result= PROTOCOL_BINARY_RESPONSE_SUCCESS; - else - expected_result= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS; - - execute(send_binary_noop()); - execute(recv_packet(&rsp)); - execute(receive_binary_noop()); - verify(validate_response_header(&rsp, cc, expected_result)); - } - else - execute(test_binary_noop()); - } - - return TEST_PASS; -} - -static enum test_return test_binary_add(void) -{ - return test_binary_add_impl("test_binary_add", PROTOCOL_BINARY_CMD_ADD); -} - -static enum test_return test_binary_addq(void) -{ - return test_binary_add_impl("test_binary_addq", PROTOCOL_BINARY_CMD_ADDQ); -} - -static enum test_return binary_set_item(const char *key, const char *value) -{ - command cmd; - response rsp; - storage_command(&cmd, PROTOCOL_BINARY_CMD_SET, key, strlen(key), - value, strlen(value), 0, 0); - execute(send_packet(&cmd)); - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_SET, - PROTOCOL_BINARY_RESPONSE_SUCCESS)); - return TEST_PASS; -} - -static enum test_return test_binary_replace_impl(const char* key, uint8_t cc) -{ - command cmd; - response rsp; - uint64_t value= 0xdeadbeefdeadcafe; - storage_command(&cmd, cc, key, strlen(key), &value, sizeof (value), 0, 0); - - /* first replace should fail, successive should succeed (when the - item is added! */ - for (int ii= 0; ii < 10; ii++) - { - if (ii == 0) - execute(send_packet(&cmd)); - else - execute(resend_packet(&cmd)); - - if (cc == PROTOCOL_BINARY_CMD_REPLACE || ii == 0) - { - uint16_t expected_result; - if (ii == 0) - expected_result=PROTOCOL_BINARY_RESPONSE_KEY_ENOENT; - else - expected_result=PROTOCOL_BINARY_RESPONSE_SUCCESS; - - execute(send_binary_noop()); - execute(recv_packet(&rsp)); - execute(receive_binary_noop()); - verify(validate_response_header(&rsp, cc, expected_result)); - - if (ii == 0) - execute(binary_set_item(key, key)); - } - else - execute(test_binary_noop()); - } - - /* verify that replace with CAS value works! */ - cmd.plain.message.header.request.cas= - htonll(rsp.plain.message.header.response.cas); - execute(resend_packet(&cmd)); - - if (cc == PROTOCOL_BINARY_CMD_REPLACE) - { - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); - } - else - execute(test_binary_noop()); - - /* try to set with an incorrect CAS value */ - cmd.plain.message.header.request.cas= - htonll(rsp.plain.message.header.response.cas - 1); - execute(resend_packet(&cmd)); - execute(send_binary_noop()); - execute(recv_packet(&rsp)); - execute(receive_binary_noop()); - verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS)); - - return TEST_PASS; -} - -static enum test_return test_binary_replace(void) -{ - return test_binary_replace_impl("test_binary_replace", PROTOCOL_BINARY_CMD_REPLACE); -} - -static enum test_return test_binary_replaceq(void) -{ - return test_binary_replace_impl("test_binary_replaceq", PROTOCOL_BINARY_CMD_REPLACEQ); -} - -static enum test_return test_binary_delete_impl(const char *key, uint8_t cc) -{ - command cmd; - response rsp; - raw_command(&cmd, cc, key, strlen(key), NULL, 0); - - /* The delete shouldn't work the first time, because the item isn't there */ - execute(send_packet(&cmd)); - execute(send_binary_noop()); - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT)); - execute(receive_binary_noop()); - execute(binary_set_item(key, key)); - - /* The item should be present now, resend*/ - execute(resend_packet(&cmd)); - if (cc == PROTOCOL_BINARY_CMD_DELETE) - { - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); - } - - execute(test_binary_noop()); - - return TEST_PASS; -} - -static enum test_return test_binary_delete(void) -{ - return test_binary_delete_impl("test_binary_delete", PROTOCOL_BINARY_CMD_DELETE); -} - -static enum test_return test_binary_deleteq(void) -{ - return test_binary_delete_impl("test_binary_deleteq", PROTOCOL_BINARY_CMD_DELETEQ); -} - -static enum test_return test_binary_get_impl(const char *key, uint8_t cc) -{ - command cmd; - response rsp; - - raw_command(&cmd, cc, key, strlen(key), NULL, 0); - execute(send_packet(&cmd)); - execute(send_binary_noop()); - - if (cc == PROTOCOL_BINARY_CMD_GET || cc == PROTOCOL_BINARY_CMD_GETK) - { - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT)); - } - - execute(receive_binary_noop()); - - execute(binary_set_item(key, key)); - execute(resend_packet(&cmd)); - execute(send_binary_noop()); - - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); - execute(receive_binary_noop()); - - return TEST_PASS; -} - -static enum test_return test_binary_get(void) -{ - return test_binary_get_impl("test_binary_get", PROTOCOL_BINARY_CMD_GET); -} - -static enum test_return test_binary_getk(void) -{ - return test_binary_get_impl("test_binary_getk", PROTOCOL_BINARY_CMD_GETK); -} - -static enum test_return test_binary_getq(void) -{ - return test_binary_get_impl("test_binary_getq", PROTOCOL_BINARY_CMD_GETQ); -} - -static enum test_return test_binary_getkq(void) -{ - return test_binary_get_impl("test_binary_getkq", PROTOCOL_BINARY_CMD_GETKQ); -} - -static enum test_return test_binary_incr_impl(const char* key, uint8_t cc) -{ - command cmd; - response rsp; - arithmetic_command(&cmd, cc, key, strlen(key), 1, 0, 0); - - uint64_t ii; - for (ii= 0; ii < 10; ++ii) - { - if (ii == 0) - execute(send_packet(&cmd)); - else - execute(resend_packet(&cmd)); - - if (cc == PROTOCOL_BINARY_CMD_INCREMENT) - { - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); - verify(ntohll(rsp.incr.message.body.value) == ii); - } - else - execute(test_binary_noop()); - } - - /* @todo add incorrect CAS */ - return TEST_PASS; -} - -static enum test_return test_binary_incr(void) -{ - return test_binary_incr_impl("test_binary_incr", PROTOCOL_BINARY_CMD_INCREMENT); -} - -static enum test_return test_binary_incrq(void) -{ - return test_binary_incr_impl("test_binary_incrq", PROTOCOL_BINARY_CMD_INCREMENTQ); -} - -static enum test_return test_binary_decr_impl(const char* key, uint8_t cc) -{ - command cmd; - response rsp; - arithmetic_command(&cmd, cc, key, strlen(key), 1, 9, 0); - - int ii; - for (ii= 9; ii > -1; --ii) - { - if (ii == 9) - execute(send_packet(&cmd)); - else - execute(resend_packet(&cmd)); - - if (cc == PROTOCOL_BINARY_CMD_DECREMENT) - { - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); - verify(ntohll(rsp.decr.message.body.value) == (uint64_t)ii); - } - else - execute(test_binary_noop()); - } - - /* decr 0 should not wrap */ - execute(resend_packet(&cmd)); - if (cc == PROTOCOL_BINARY_CMD_DECREMENT) - { - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); - verify(ntohll(rsp.decr.message.body.value) == 0); - } - else - { - /* @todo get the value and verify! */ - - } - - /* @todo add incorrect cas */ - execute(test_binary_noop()); - return TEST_PASS; -} - -static enum test_return test_binary_decr(void) -{ - return test_binary_decr_impl("test_binary_decr", - PROTOCOL_BINARY_CMD_DECREMENT); -} - -static enum test_return test_binary_decrq(void) -{ - return test_binary_decr_impl("test_binary_decrq", - PROTOCOL_BINARY_CMD_DECREMENTQ); -} - -static enum test_return test_binary_version(void) -{ - command cmd; - response rsp; - raw_command(&cmd, PROTOCOL_BINARY_CMD_VERSION, NULL, 0, NULL, 0); - - execute(send_packet(&cmd)); - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_VERSION, - PROTOCOL_BINARY_RESPONSE_SUCCESS)); - - return TEST_PASS; -} - -static enum test_return test_binary_flush_impl(const char *key, uint8_t cc) -{ - command cmd; - response rsp; - - for (int ii= 0; ii < 2; ++ii) - { - execute(binary_set_item(key, key)); - flush_command(&cmd, cc, 0, ii == 0); - execute(send_packet(&cmd)); - - if (cc == PROTOCOL_BINARY_CMD_FLUSH) - { - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); - } - else - execute(test_binary_noop()); - - raw_command(&cmd, PROTOCOL_BINARY_CMD_GET, key, strlen(key), NULL, 0); - execute(send_packet(&cmd)); - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_GET, - PROTOCOL_BINARY_RESPONSE_KEY_ENOENT)); - } - - return TEST_PASS; -} - -static enum test_return test_binary_flush(void) -{ - return test_binary_flush_impl("test_binary_flush", PROTOCOL_BINARY_CMD_FLUSH); -} - -static enum test_return test_binary_flushq(void) -{ - return test_binary_flush_impl("test_binary_flushq", PROTOCOL_BINARY_CMD_FLUSHQ); -} - -static enum test_return test_binary_concat_impl(const char *key, uint8_t cc) -{ - command cmd; - response rsp; - const char *value; - - if (cc == PROTOCOL_BINARY_CMD_APPEND || cc == PROTOCOL_BINARY_CMD_APPENDQ) - value="hello"; - else - value=" world"; - - execute(binary_set_item(key, value)); - - if (cc == PROTOCOL_BINARY_CMD_APPEND || cc == PROTOCOL_BINARY_CMD_APPENDQ) - value=" world"; - else - value="hello"; - - raw_command(&cmd, cc, key, strlen(key), value, strlen(value)); - execute(send_packet(&cmd)); - if (cc == PROTOCOL_BINARY_CMD_APPEND || cc == PROTOCOL_BINARY_CMD_PREPEND) - { - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); - } - else - execute(test_binary_noop()); - - raw_command(&cmd, PROTOCOL_BINARY_CMD_GET, key, strlen(key), NULL, 0); - execute(send_packet(&cmd)); - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_GET, - PROTOCOL_BINARY_RESPONSE_SUCCESS)); - verify(rsp.plain.message.header.response.bodylen - 4 == 11); - verify(memcmp(rsp.bytes + 28, "hello world", 11) == 0); - - return TEST_PASS; -} - -static enum test_return test_binary_append(void) -{ - return test_binary_concat_impl("test_binary_append", PROTOCOL_BINARY_CMD_APPEND); -} - -static enum test_return test_binary_prepend(void) -{ - return test_binary_concat_impl("test_binary_prepend", PROTOCOL_BINARY_CMD_PREPEND); -} - -static enum test_return test_binary_appendq(void) -{ - return test_binary_concat_impl("test_binary_appendq", PROTOCOL_BINARY_CMD_APPENDQ); -} - -static enum test_return test_binary_prependq(void) -{ - return test_binary_concat_impl("test_binary_prependq", PROTOCOL_BINARY_CMD_PREPENDQ); -} - -static enum test_return test_binary_stat(void) -{ - command cmd; - response rsp; - - raw_command(&cmd, PROTOCOL_BINARY_CMD_STAT, NULL, 0, NULL, 0); - execute(send_packet(&cmd)); - - do - { - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_STAT, - PROTOCOL_BINARY_RESPONSE_SUCCESS)); - } while (rsp.plain.message.header.response.keylen != 0); - - return TEST_PASS; -} - -static enum test_return send_string(const char *cmd) -{ - execute(retry_write(cmd, strlen(cmd))); - return TEST_PASS; -} - -static enum test_return receive_line(char *buffer, size_t size) -{ - size_t offset= 0; - while (offset < size) - { - execute(retry_read(buffer + offset, 1)); - if (buffer[offset] == '\n') - { - if (offset + 1 < size) - { - buffer[offset + 1]= '\0'; - return TEST_PASS; - } - else - return TEST_FAIL; - } - ++offset; - } - - return TEST_FAIL; -} - -static enum test_return receive_response(const char *msg) { - char buffer[80]; - execute(receive_line(buffer, sizeof(buffer))); - if (strcmp(msg, buffer) != 0) { - fprintf(stderr, "[%s]\n", buffer); - } - verify(strcmp(msg, buffer) == 0); - return TEST_PASS; -} - -static enum test_return receive_error_response(void) -{ - char buffer[80]; - execute(receive_line(buffer, sizeof(buffer))); - verify(strncmp(buffer, "ERROR", 5) == 0 || - strncmp(buffer, "CLIENT_ERROR", 12) == 0 || - strncmp(buffer, "SERVER_ERROR", 12) == 0); - return TEST_PASS; -} - -static enum test_return test_ascii_quit(void) -{ - /* Verify that quit handles unknown options */ - execute(send_string("quit foo bar\r\n")); - execute(receive_error_response()); - - /* quit doesn't support noreply */ - execute(send_string("quit noreply\r\n")); - execute(receive_error_response()); - - /* Verify that quit works */ - execute(send_string("quit\r\n")); - - /* Socket should be closed now, read should return EXIT_SUCCESS */ - char buffer[80]; - verify(timeout_io_op(sock, POLLIN, buffer, sizeof(buffer)) == 0); - return TEST_PASS_RECONNECT; - -} - -static enum test_return test_ascii_version(void) -{ - /* Verify that version command handles unknown options */ - execute(send_string("version foo bar\r\n")); - execute(receive_error_response()); - - /* version doesn't support noreply */ - execute(send_string("version noreply\r\n")); - execute(receive_error_response()); - - /* Verify that verify works */ - execute(send_string("version\r\n")); - char buffer[256]; - execute(receive_line(buffer, sizeof(buffer))); - verify(strncmp(buffer, "VERSION ", 8) == 0); - - return TEST_PASS; -} - -static enum test_return test_ascii_verbosity(void) -{ - /* This command does not adhere to the spec! */ - execute(send_string("verbosity foo bar my\r\n")); - execute(receive_error_response()); - - execute(send_string("verbosity noreply\r\n")); - execute(receive_error_response()); - - execute(send_string("verbosity 0 noreply\r\n")); - execute(test_ascii_version()); - - execute(send_string("verbosity\r\n")); - execute(receive_error_response()); - - execute(send_string("verbosity 1\r\n")); - execute(receive_response("OK\r\n")); - - execute(send_string("verbosity 0\r\n")); - execute(receive_response("OK\r\n")); - - return TEST_PASS; -} - - - -static enum test_return test_ascii_set_impl(const char* key, bool noreply) -{ - /* @todo add tests for bogus format! */ - char buffer[1024]; - snprintf(buffer, sizeof(buffer), "set %s 0 0 5%s\r\nvalue\r\n", key, noreply ? " noreply" : ""); - execute(send_string(buffer)); - - if (!noreply) - execute(receive_response("STORED\r\n")); - - return test_ascii_version(); -} - -static enum test_return test_ascii_set(void) -{ - return test_ascii_set_impl("test_ascii_set", false); -} - -static enum test_return test_ascii_set_noreply(void) -{ - return test_ascii_set_impl("test_ascii_set_noreply", true); -} - -static enum test_return test_ascii_add_impl(const char* key, bool noreply) -{ - /* @todo add tests for bogus format! */ - char buffer[1024]; - snprintf(buffer, sizeof(buffer), "add %s 0 0 5%s\r\nvalue\r\n", key, noreply ? " noreply" : ""); - execute(send_string(buffer)); - - if (!noreply) - execute(receive_response("STORED\r\n")); - - execute(send_string(buffer)); - - if (!noreply) - execute(receive_response("NOT_STORED\r\n")); - - return test_ascii_version(); -} - -static enum test_return test_ascii_add(void) -{ - return test_ascii_add_impl("test_ascii_add", false); -} - -static enum test_return test_ascii_add_noreply(void) -{ - return test_ascii_add_impl("test_ascii_add_noreply", true); -} - -static enum test_return ascii_get_unknown_value(char **key, char **value, ssize_t *ndata) -{ - char buffer[1024]; - - execute(receive_line(buffer, sizeof(buffer))); - verify(strncmp(buffer, "VALUE ", 6) == 0); - char *end= strchr(buffer + 6, ' '); - verify(end != NULL); - *end= '\0'; - *key= strdup(buffer + 6); - verify(*key != NULL); - char *ptr= end + 1; - - unsigned long val= strtoul(ptr, &end, 10); /* flags */ - verify(ptr != end); - verify(val == 0); - verify(end != NULL); - *ndata = (ssize_t)strtoul(end, &end, 10); /* size */ - verify(ptr != end); - verify(end != NULL); - while (*end != '\n' && isspace(*end)) - ++end; - verify(*end == '\n'); - - *value= malloc((size_t)*ndata); - verify(*value != NULL); - - execute(retry_read(*value, (size_t)*ndata)); - - execute(retry_read(buffer, 2)); - verify(memcmp(buffer, "\r\n", 2) == 0); - - return TEST_PASS; -} - -static enum test_return ascii_get_value(const char *key, const char *value) -{ - - char buffer[1024]; - size_t datasize= strlen(value); - - verify(datasize < sizeof(buffer)); - execute(receive_line(buffer, sizeof(buffer))); - verify(strncmp(buffer, "VALUE ", 6) == 0); - verify(strncmp(buffer + 6, key, strlen(key)) == 0); - char *ptr= buffer + 6 + strlen(key) + 1; - char *end; - - unsigned long val= strtoul(ptr, &end, 10); /* flags */ - verify(ptr != end); - verify(val == 0); - verify(end != NULL); - val= strtoul(end, &end, 10); /* size */ - verify(ptr != end); - verify(val == datasize); - verify(end != NULL); - while (*end != '\n' && isspace(*end)) - ++end; - verify(*end == '\n'); - - execute(retry_read(buffer, datasize)); - verify(memcmp(buffer, value, datasize) == 0); - - execute(retry_read(buffer, 2)); - verify(memcmp(buffer, "\r\n", 2) == 0); - - return TEST_PASS; -} - -static enum test_return ascii_get_item(const char *key, const char *value, - bool exist) -{ - char buffer[1024]; - size_t datasize= 0; - if (value != NULL) - datasize= strlen(value); - - verify(datasize < sizeof(buffer)); - snprintf(buffer, sizeof(buffer), "get %s\r\n", key); - execute(send_string(buffer)); - - if (exist) - execute(ascii_get_value(key, value)); - - execute(retry_read(buffer, 5)); - verify(memcmp(buffer, "END\r\n", 5) == 0); - - return TEST_PASS; -} - -static enum test_return ascii_gets_value(const char *key, const char *value, - unsigned long *cas) -{ - - char buffer[1024]; - size_t datasize= strlen(value); - - verify(datasize < sizeof(buffer)); - execute(receive_line(buffer, sizeof(buffer))); - verify(strncmp(buffer, "VALUE ", 6) == 0); - verify(strncmp(buffer + 6, key, strlen(key)) == 0); - char *ptr= buffer + 6 + strlen(key) + 1; - char *end; - - unsigned long val= strtoul(ptr, &end, 10); /* flags */ - verify(ptr != end); - verify(val == 0); - verify(end != NULL); - val= strtoul(end, &end, 10); /* size */ - verify(ptr != end); - verify(val == datasize); - verify(end != NULL); - *cas= strtoul(end, &end, 10); /* cas */ - verify(ptr != end); - verify(val == datasize); - verify(end != NULL); - - while (*end != '\n' && isspace(*end)) - ++end; - verify(*end == '\n'); - - execute(retry_read(buffer, datasize)); - verify(memcmp(buffer, value, datasize) == 0); - - execute(retry_read(buffer, 2)); - verify(memcmp(buffer, "\r\n", 2) == 0); - - return TEST_PASS; -} - -static enum test_return ascii_gets_item(const char *key, const char *value, - bool exist, unsigned long *cas) -{ - char buffer[1024]; - size_t datasize= 0; - if (value != NULL) - datasize= strlen(value); - - verify(datasize < sizeof(buffer)); - snprintf(buffer, sizeof(buffer), "gets %s\r\n", key); - execute(send_string(buffer)); - - if (exist) - execute(ascii_gets_value(key, value, cas)); - - execute(retry_read(buffer, 5)); - verify(memcmp(buffer, "END\r\n", 5) == 0); - - return TEST_PASS; -} - -static enum test_return ascii_set_item(const char *key, const char *value) -{ - char buffer[300]; - size_t len= strlen(value); - snprintf(buffer, sizeof(buffer), "set %s 0 0 %u\r\n", key, (unsigned int)len); - execute(send_string(buffer)); - execute(retry_write(value, len)); - execute(send_string("\r\n")); - execute(receive_response("STORED\r\n")); - return TEST_PASS; -} - -static enum test_return test_ascii_replace_impl(const char* key, bool noreply) -{ - char buffer[1024]; - snprintf(buffer, sizeof(buffer), "replace %s 0 0 5%s\r\nvalue\r\n", key, noreply ? " noreply" : ""); - execute(send_string(buffer)); - - if (noreply) - execute(test_ascii_version()); - else - execute(receive_response("NOT_STORED\r\n")); - - execute(ascii_set_item(key, "value")); - execute(ascii_get_item(key, "value", true)); - - - execute(send_string(buffer)); - - if (noreply) - execute(test_ascii_version()); - else - execute(receive_response("STORED\r\n")); - - return test_ascii_version(); -} - -static enum test_return test_ascii_replace(void) -{ - return test_ascii_replace_impl("test_ascii_replace", false); -} - -static enum test_return test_ascii_replace_noreply(void) -{ - return test_ascii_replace_impl("test_ascii_replace_noreply", true); -} - -static enum test_return test_ascii_cas_impl(const char* key, bool noreply) -{ - char buffer[1024]; - unsigned long cas; - - execute(ascii_set_item(key, "value")); - execute(ascii_gets_item(key, "value", true, &cas)); - - snprintf(buffer, sizeof(buffer), "cas %s 0 0 6 %lu%s\r\nvalue2\r\n", key, cas, noreply ? " noreply" : ""); - execute(send_string(buffer)); - - if (noreply) - execute(test_ascii_version()); - else - execute(receive_response("STORED\r\n")); - - /* reexecute the same command should fail due to illegal cas */ - execute(send_string(buffer)); - - if (noreply) - execute(test_ascii_version()); - else - execute(receive_response("EXISTS\r\n")); - - return test_ascii_version(); -} - -static enum test_return test_ascii_cas(void) -{ - return test_ascii_cas_impl("test_ascii_cas", false); -} - -static enum test_return test_ascii_cas_noreply(void) -{ - return test_ascii_cas_impl("test_ascii_cas_noreply", true); -} - -static enum test_return test_ascii_delete_impl(const char *key, bool noreply) -{ - execute(ascii_set_item(key, "value")); - - execute(send_string("delete\r\n")); - execute(receive_error_response()); - /* BUG: the server accepts delete a b */ - execute(send_string("delete a b c d e\r\n")); - execute(receive_error_response()); - - char buffer[1024]; - snprintf(buffer, sizeof(buffer), "delete %s%s\r\n", key, noreply ? " noreply" : ""); - execute(send_string(buffer)); - - if (noreply) - execute(test_ascii_version()); - else - execute(receive_response("DELETED\r\n")); - - execute(ascii_get_item(key, "value", false)); - execute(send_string(buffer)); - if (noreply) - execute(test_ascii_version()); - else - execute(receive_response("NOT_FOUND\r\n")); - - return TEST_PASS; -} - -static enum test_return test_ascii_delete(void) -{ - return test_ascii_delete_impl("test_ascii_delete", false); -} - -static enum test_return test_ascii_delete_noreply(void) -{ - return test_ascii_delete_impl("test_ascii_delete_noreply", true); -} - -static enum test_return test_ascii_get(void) -{ - execute(ascii_set_item("test_ascii_get", "value")); - - execute(send_string("get\r\n")); - execute(receive_error_response()); - execute(ascii_get_item("test_ascii_get", "value", true)); - execute(ascii_get_item("test_ascii_get_notfound", "value", false)); - - return TEST_PASS; -} - -static enum test_return test_ascii_gets(void) -{ - execute(ascii_set_item("test_ascii_gets", "value")); - - execute(send_string("gets\r\n")); - execute(receive_error_response()); - unsigned long cas; - execute(ascii_gets_item("test_ascii_gets", "value", true, &cas)); - execute(ascii_gets_item("test_ascii_gets_notfound", "value", false, &cas)); - - return TEST_PASS; -} - -static enum test_return test_ascii_mget(void) -{ - const uint32_t nkeys= 5; - const char * const keys[]= { - "test_ascii_mget1", - "test_ascii_mget2", - /* test_ascii_mget_3 does not exist :) */ - "test_ascii_mget4", - "test_ascii_mget5", - "test_ascii_mget6" - }; - - for (uint32_t x= 0; x < nkeys; ++x) - execute(ascii_set_item(keys[x], "value")); - - /* Ask for a key that doesn't exist as well */ - execute(send_string("get test_ascii_mget1 test_ascii_mget2 test_ascii_mget3 " - "test_ascii_mget4 test_ascii_mget5 " - "test_ascii_mget6\r\n")); - - char *returned[nkeys]; - - for (uint32_t x= 0; x < nkeys; ++x) - { - ssize_t nbytes = 0; - char *v= NULL; - execute(ascii_get_unknown_value(&returned[x], &v, &nbytes)); - verify(nbytes == 5); - verify(memcmp(v, "value", 5) == 0); - free(v); - } - - char buffer[5]; - execute(retry_read(buffer, 5)); - verify(memcmp(buffer, "END\r\n", 5) == 0); - - /* verify that we got all the keys we expected */ - for (uint32_t x= 0; x < nkeys; ++x) - { - bool found= false; - for (uint32_t y= 0; y < nkeys; ++y) - { - if (strcmp(keys[x], returned[y]) == 0) - { - found = true; - break; - } - } - verify(found); - } - - for (uint32_t x= 0; x < nkeys; ++x) - free(returned[x]); - - return TEST_PASS; -} - -static enum test_return test_ascii_incr_impl(const char* key, bool noreply) -{ - char cmd[300]; - snprintf(cmd, sizeof(cmd), "incr %s 1%s\r\n", key, noreply ? " noreply" : ""); - - execute(ascii_set_item(key, "0")); - for (int x= 1; x < 11; ++x) - { - execute(send_string(cmd)); - - if (noreply) - execute(test_ascii_version()); - else - { - char buffer[80]; - execute(receive_line(buffer, sizeof(buffer))); - int val= atoi(buffer); - verify(val == x); - } - } - - execute(ascii_get_item(key, "10", true)); - - return TEST_PASS; -} - -static enum test_return test_ascii_incr(void) -{ - return test_ascii_incr_impl("test_ascii_incr", false); -} - -static enum test_return test_ascii_incr_noreply(void) -{ - return test_ascii_incr_impl("test_ascii_incr_noreply", true); -} - -static enum test_return test_ascii_decr_impl(const char* key, bool noreply) -{ - char cmd[300]; - snprintf(cmd, sizeof(cmd), "decr %s 1%s\r\n", key, noreply ? " noreply" : ""); - - execute(ascii_set_item(key, "9")); - for (int x= 8; x > -1; --x) - { - execute(send_string(cmd)); - - if (noreply) - execute(test_ascii_version()); - else - { - char buffer[80]; - execute(receive_line(buffer, sizeof(buffer))); - int val= atoi(buffer); - verify(val == x); - } - } - - execute(ascii_get_item(key, "0", true)); - - /* verify that it doesn't wrap */ - execute(send_string(cmd)); - if (noreply) - execute(test_ascii_version()); - else - { - char buffer[80]; - execute(receive_line(buffer, sizeof(buffer))); - } - execute(ascii_get_item(key, "0", true)); - - return TEST_PASS; -} - -static enum test_return test_ascii_decr(void) -{ - return test_ascii_decr_impl("test_ascii_decr", false); -} - -static enum test_return test_ascii_decr_noreply(void) -{ - return test_ascii_decr_impl("test_ascii_decr_noreply", true); -} - - -static enum test_return test_ascii_flush_impl(const char *key, bool noreply) -{ -#if 0 - /* Verify that the flush_all command handles unknown options */ - /* Bug in the current memcached server! */ - execute(send_string("flush_all foo bar\r\n")); - execute(receive_error_response()); -#endif - - execute(ascii_set_item(key, key)); - execute(ascii_get_item(key, key, true)); - - if (noreply) - { - execute(send_string("flush_all noreply\r\n")); - execute(test_ascii_version()); - } - else - { - execute(send_string("flush_all\r\n")); - execute(receive_response("OK\r\n")); - } - - execute(ascii_get_item(key, key, false)); - - return TEST_PASS; -} - -static enum test_return test_ascii_flush(void) -{ - return test_ascii_flush_impl("test_ascii_flush", false); -} - -static enum test_return test_ascii_flush_noreply(void) -{ - return test_ascii_flush_impl("test_ascii_flush_noreply", true); -} - -static enum test_return test_ascii_concat_impl(const char *key, - bool append, - bool noreply) -{ - const char *value; - - if (append) - value="hello"; - else - value=" world"; - - execute(ascii_set_item(key, value)); - - if (append) - value=" world"; - else - value="hello"; - - char cmd[400]; - snprintf(cmd, sizeof(cmd), "%s %s 0 0 %u%s\r\n%s\r\n", - append ? "append" : "prepend", - key, (unsigned int)strlen(value), noreply ? " noreply" : "", - value); - execute(send_string(cmd)); - - if (noreply) - execute(test_ascii_version()); - else - execute(receive_response("STORED\r\n")); - - execute(ascii_get_item(key, "hello world", true)); - - snprintf(cmd, sizeof(cmd), "%s %s_notfound 0 0 %u%s\r\n%s\r\n", - append ? "append" : "prepend", - key, (unsigned int)strlen(value), noreply ? " noreply" : "", - value); - execute(send_string(cmd)); - - if (noreply) - execute(test_ascii_version()); - else - execute(receive_response("NOT_STORED\r\n")); - - return TEST_PASS; -} - -static enum test_return test_ascii_append(void) -{ - return test_ascii_concat_impl("test_ascii_append", true, false); -} - -static enum test_return test_ascii_prepend(void) -{ - return test_ascii_concat_impl("test_ascii_prepend", false, false); -} - -static enum test_return test_ascii_append_noreply(void) -{ - return test_ascii_concat_impl("test_ascii_append_noreply", true, true); -} - -static enum test_return test_ascii_prepend_noreply(void) -{ - return test_ascii_concat_impl("test_ascii_prepend_noreply", false, true); -} - -static enum test_return test_ascii_stat(void) -{ - execute(send_string("stats noreply\r\n")); - execute(receive_error_response()); - execute(send_string("stats\r\n")); - char buffer[1024]; - do { - execute(receive_line(buffer, sizeof(buffer))); - } while (strcmp(buffer, "END\r\n") != 0); - - return TEST_PASS_RECONNECT; -} - -typedef enum test_return(*TEST_FUNC)(void); - -struct testcase -{ - const char *description; - TEST_FUNC function; -}; - -struct testcase testcases[]= { - { "ascii quit", test_ascii_quit }, - { "ascii version", test_ascii_version }, - { "ascii verbosity", test_ascii_verbosity }, - { "ascii set", test_ascii_set }, - { "ascii set noreply", test_ascii_set_noreply }, - { "ascii get", test_ascii_get }, - { "ascii gets", test_ascii_gets }, - { "ascii mget", test_ascii_mget }, - { "ascii flush", test_ascii_flush }, - { "ascii flush noreply", test_ascii_flush_noreply }, - { "ascii add", test_ascii_add }, - { "ascii add noreply", test_ascii_add_noreply }, - { "ascii replace", test_ascii_replace }, - { "ascii replace noreply", test_ascii_replace_noreply }, - { "ascii cas", test_ascii_cas }, - { "ascii cas noreply", test_ascii_cas_noreply }, - { "ascii delete", test_ascii_delete }, - { "ascii delete noreply", test_ascii_delete_noreply }, - { "ascii incr", test_ascii_incr }, - { "ascii incr noreply", test_ascii_incr_noreply }, - { "ascii decr", test_ascii_decr }, - { "ascii decr noreply", test_ascii_decr_noreply }, - { "ascii append", test_ascii_append }, - { "ascii append noreply", test_ascii_append_noreply }, - { "ascii prepend", test_ascii_prepend }, - { "ascii prepend noreply", test_ascii_prepend_noreply }, - { "ascii stat", test_ascii_stat }, - { "binary noop", test_binary_noop }, - { "binary quit", test_binary_quit }, - { "binary quitq", test_binary_quitq }, - { "binary set", test_binary_set }, - { "binary setq", test_binary_setq }, - { "binary flush", test_binary_flush }, - { "binary flushq", test_binary_flushq }, - { "binary add", test_binary_add }, - { "binary addq", test_binary_addq }, - { "binary replace", test_binary_replace }, - { "binary replaceq", test_binary_replaceq }, - { "binary delete", test_binary_delete }, - { "binary deleteq", test_binary_deleteq }, - { "binary get", test_binary_get }, - { "binary getq", test_binary_getq }, - { "binary getk", test_binary_getk }, - { "binary getkq", test_binary_getkq }, - { "binary incr", test_binary_incr }, - { "binary incrq", test_binary_incrq }, - { "binary decr", test_binary_decr }, - { "binary decrq", test_binary_decrq }, - { "binary version", test_binary_version }, - { "binary append", test_binary_append }, - { "binary appendq", test_binary_appendq }, - { "binary prepend", test_binary_prepend }, - { "binary prependq", test_binary_prependq }, - { "binary stat", test_binary_stat }, - { NULL, NULL} -}; - -const int ascii_tests = 1; -const int binary_tests = 2; - -struct test_type_st -{ - bool ascii; - bool binary; -}; - -int main(int argc, char **argv) -{ - static const char * const status_msg[]= {"[skip]", "[pass]", "[pass]", "[FAIL]"}; - struct test_type_st tests= { true, true }; - int total= 0; - int failed= 0; - const char *hostname= "localhost"; - const char *port= "11211"; - int cmd; - bool prompt= false; - const char *testname= NULL; - - - - while ((cmd= getopt(argc, argv, "t:vch:p:PT:?ab")) != EOF) - { - switch (cmd) { - case 'a': - tests.ascii= true; - tests.binary= false; - break; - case 'b': - tests.ascii= false; - tests.binary= true; - break; - case 't': - timeout= atoi(optarg); - if (timeout == 0) - { - fprintf(stderr, "Invalid timeout. Please specify a number for -t\n"); - return EXIT_FAILURE; - } - break; - case 'v': verbose= true; - break; - case 'c': do_core= true; - break; - case 'h': hostname= optarg; - break; - case 'p': port= optarg; - break; - case 'P': prompt= true; - break; - case 'T': testname= optarg; - break; - default: - fprintf(stderr, "Usage: %s [-h hostname] [-p port] [-c] [-v] [-t n] [-P] [-T testname]'\n" - "\t-c\tGenerate coredump if a test fails\n" - "\t-v\tVerbose test output (print out the assertion)\n" - "\t-t n\tSet the timeout for io-operations to n seconds\n" - "\t-P\tPrompt the user before starting a test.\n" - "\t\t\t\"skip\" will skip the test\n" - "\t\t\t\"quit\" will terminate memcapable\n" - "\t\t\tEverything else will start the test\n" - "\t-T n\tJust run the test named n\n" - "\t-a\tOnly test the ascii protocol\n" - "\t-b\tOnly test the binary protocol\n", - argv[0]); - return EXIT_FAILURE; - } - } - - initialize_sockets(); - sock= connect_server(hostname, port); - if (sock == INVALID_SOCKET) - { - fprintf(stderr, "Failed to connect to <%s:%s>: %s\n", - hostname, port, strerror(get_socket_errno())); - return EXIT_FAILURE; - } - - for (int ii= 0; testcases[ii].description != NULL; ++ii) - { - if (testname != NULL && strcmp(testcases[ii].description, testname) != 0) - continue; - - if ((testcases[ii].description[0] == 'a' && (tests.ascii) == 0) || - (testcases[ii].description[0] == 'b' && (tests.binary) == 0)) - { - continue; - } - ++total; - fprintf(stdout, "%-40s", testcases[ii].description); - fflush(stdout); - - if (prompt) - { - fprintf(stdout, "\nPress when you are ready? "); - char buffer[80] = {0}; - if (fgets(buffer, sizeof(buffer), stdin) != NULL) { - if (strncmp(buffer, "skip", 4) == 0) - { - fprintf(stdout, "%-40s%s\n", testcases[ii].description, - status_msg[TEST_SKIP]); - fflush(stdout); - continue; - } - if (strncmp(buffer, "quit", 4) == 0) - exit(0); - } - - fprintf(stdout, "%-40s", testcases[ii].description); - fflush(stdout); - } - - bool reconnect= false; - enum test_return ret= testcases[ii].function(); - if (ret == TEST_FAIL) - { - reconnect= true; - ++failed; - if (verbose) - fprintf(stderr, "\n"); - } - else if (ret == TEST_PASS_RECONNECT) - reconnect= true; - - fprintf(stderr, "%s\n", status_msg[ret]); - if (reconnect) - { - closesocket(sock); - if ((sock= connect_server(hostname, port)) == INVALID_SOCKET) - { - fprintf(stderr, "Failed to connect to <%s:%s>: %s\n", - hostname, port, strerror(get_socket_errno())); - fprintf(stderr, "%d of %d tests failed\n", failed, total); - return EXIT_FAILURE; - } - } - } - - closesocket(sock); - if (failed == 0) - fprintf(stdout, "All tests passed\n"); - else - fprintf(stderr, "%d of %d tests failed\n", failed, total); - - return (failed == 0) ? 0 : 1; -} diff --git a/clients/memcapable.cc b/clients/memcapable.cc new file mode 100644 index 00000000..baac28b2 --- /dev/null +++ b/clients/memcapable.cc @@ -0,0 +1,2098 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: + * + */ + +/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ +#undef NDEBUG + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "utilities.h" + +#ifdef linux +/* /usr/include/netinet/in.h defines macros from ntohs() to _bswap_nn to + * optimize the conversion functions, but the prototypes generate warnings + * from gcc. The conversion methods isn't the bottleneck for my app, so + * just remove the warnings by undef'ing the optimization .. + */ +#undef ntohs +#undef ntohl +#endif + +/* Should we generate coredumps when we enounter an error (-c) */ +static bool do_core= false; +/* connection to the server */ +static memcached_socket_t sock; +/* Should the output from test failures be verbose or quiet? */ +static bool verbose= false; + +/* The number of seconds to wait for an IO-operation */ +static int timeout= 2; + +/* + * Instead of having to cast between the different datatypes we create + * a union of all of the different types of pacages we want to send. + * A lot of the different commands use the same packet layout, so I'll + * just define the different types I need. The typedefs only contain + * the header of the message, so we need some space for keys and body + * To avoid to have to do multiple writes, lets add a chunk of memory + * to use. 1k should be more than enough for header, key and body. + */ +typedef union +{ + protocol_binary_request_no_extras plain; + protocol_binary_request_flush flush; + protocol_binary_request_incr incr; + protocol_binary_request_set set; + char bytes[1024]; +} command; + +typedef union +{ + protocol_binary_response_no_extras plain; + protocol_binary_response_incr incr; + protocol_binary_response_decr decr; + char bytes[1024]; +} response; + +enum test_return +{ + TEST_SKIP, TEST_PASS, TEST_PASS_RECONNECT, TEST_FAIL +}; + +/** + * Try to get an addrinfo struct for a given port on a given host + */ +static struct addrinfo *lookuphost(const char *hostname, const char *port) +{ + struct addrinfo *ai= 0; + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family=AF_UNSPEC; + hints.ai_protocol=IPPROTO_TCP; + hints.ai_socktype=SOCK_STREAM; + + int error= getaddrinfo(hostname, port, &hints, &ai); + if (error != 0) + { + if (error != EAI_SYSTEM) + fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error)); + else + perror("getaddrinfo()"); + } + + return ai; +} + +/** + * Set the socket in nonblocking mode + * @return -1 if failure, the socket otherwise + */ +static memcached_socket_t set_noblock(void) +{ +#ifdef WIN32 + u_long arg = 1; + if (ioctlsocket(sock, FIONBIO, &arg) == SOCKET_ERROR) + { + perror("Failed to set nonblocking io"); + closesocket(sock); + return INVALID_SOCKET; + } +#else + int flags= fcntl(sock, F_GETFL, 0); + if (flags == -1) + { + perror("Failed to get socket flags"); + closesocket(sock); + return INVALID_SOCKET; + } + + if ((flags & O_NONBLOCK) != O_NONBLOCK) + { + if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) + { + perror("Failed to set socket to nonblocking mode"); + closesocket(sock); + return INVALID_SOCKET; + } + } +#endif + return sock; +} + +/** + * Try to open a connection to the server + * @param hostname the name of the server to connect to + * @param port the port number (or service) to connect to + * @return positive integer if success, -1 otherwise + */ +static memcached_socket_t connect_server(const char *hostname, const char *port) +{ + struct addrinfo *ai= lookuphost(hostname, port); + sock= INVALID_SOCKET; + if (ai != NULL) + { + if ((sock= socket(ai->ai_family, ai->ai_socktype, + ai->ai_protocol)) != INVALID_SOCKET) + { + if (connect(sock, ai->ai_addr, ai->ai_addrlen) == SOCKET_ERROR) + { + fprintf(stderr, "Failed to connect socket: %s\n", + strerror(get_socket_errno())); + closesocket(sock); + sock= INVALID_SOCKET; + } + else + { + sock= set_noblock(); + } + } + else + fprintf(stderr, "Failed to create socket: %s\n", + strerror(get_socket_errno())); + + freeaddrinfo(ai); + } + + return sock; +} + +static ssize_t timeout_io_op(memcached_socket_t fd, short direction, void *buf, size_t len) +{ + ssize_t ret; + + if (direction == POLLOUT) + { + ret= send(fd, buf, len, 0); + } + else + { + ret= recv(fd, buf, len, 0); + } + + if (ret == SOCKET_ERROR && get_socket_errno() == EWOULDBLOCK) + { + struct pollfd fds; + memset(&fds, 0, sizeof(struct pollfd)); + fds.events= direction; + fds.fd= fd; + + int err= poll(&fds, 1, timeout * 1000); + if (err == 1) + { + if (direction == POLLOUT) + { + ret= send(fd, buf, len, 0); + } + else + { + ret= recv(fd, buf, len, 0); + } + } + else if (err == 0) + { + errno= ETIMEDOUT; + } + else + { + perror("Failed to poll"); + return -1; + } + } + + return ret; +} + +/** + * Ensure that an expression is true. If it isn't print out a message similar + * to assert() and create a coredump if the user wants that. If not an error + * message is returned. + * + */ +static enum test_return ensure(bool val, const char *expression, const char *file, int line) +{ + if (!val) + { + if (verbose) + fprintf(stderr, "\n%s:%d: %s", file, line, expression); + + if (do_core) + abort(); + + return TEST_FAIL; + } + + return TEST_PASS; +} + +#define verify(expression) do { if (ensure(expression, #expression, __FILE__, __LINE__) == TEST_FAIL) return TEST_FAIL; } while (0) +#define execute(expression) do { if (ensure(expression == TEST_PASS, #expression, __FILE__, __LINE__) == TEST_FAIL) return TEST_FAIL; } while (0) + +/** + * Send a chunk of memory over the socket (retry if the call is iterrupted + */ +static enum test_return retry_write(const void* buf, size_t len) +{ + size_t offset= 0; + const char* ptr= static_cast(buf); + + do + { + size_t num_bytes= len - offset; + ssize_t nw= timeout_io_op(sock, POLLOUT, (void*)(ptr + offset), num_bytes); + if (nw == -1) + verify(get_socket_errno() == EINTR || get_socket_errno() == EAGAIN); + else + offset+= (size_t)nw; + } while (offset < len); + + return TEST_PASS; +} + +/** + * Resend a packet to the server (All fields in the command header should + * be in network byte order) + */ +static enum test_return resend_packet(command *cmd) +{ + size_t length= sizeof (protocol_binary_request_no_extras) + + ntohl(cmd->plain.message.header.request.bodylen); + + execute(retry_write(cmd, length)); + return TEST_PASS; +} + +/** + * Send a command to the server. The command header needs to be updated + * to network byte order + */ +static enum test_return send_packet(command *cmd) +{ + /* Fix the byteorder of the header */ + cmd->plain.message.header.request.keylen= + ntohs(cmd->plain.message.header.request.keylen); + cmd->plain.message.header.request.bodylen= + ntohl(cmd->plain.message.header.request.bodylen); + cmd->plain.message.header.request.cas= + ntohll(cmd->plain.message.header.request.cas); + + execute(resend_packet(cmd)); + return TEST_PASS; +} + +/** + * Read a fixed length chunk of data from the server + */ +static enum test_return retry_read(void *buf, size_t len) +{ + size_t offset= 0; + do + { + ssize_t nr= timeout_io_op(sock, POLLIN, ((char*) buf) + offset, len - offset); + switch (nr) { + case -1 : + fprintf(stderr, "Errno: %d %s\n", get_socket_errno(), strerror(errno)); + verify(get_socket_errno() == EINTR || get_socket_errno() == EAGAIN); + break; + case 0: + return TEST_FAIL; + default: + offset+= (size_t)nr; + } + } while (offset < len); + + return TEST_PASS; +} + +/** + * Receive a response from the server and conver the fields in the header + * to local byte order + */ +static enum test_return recv_packet(response *rsp) +{ + execute(retry_read(rsp, sizeof(protocol_binary_response_no_extras))); + + /* Fix the byte order in the packet header */ + rsp->plain.message.header.response.keylen= + ntohs(rsp->plain.message.header.response.keylen); + rsp->plain.message.header.response.status= + ntohs(rsp->plain.message.header.response.status); + rsp->plain.message.header.response.bodylen= + ntohl(rsp->plain.message.header.response.bodylen); + rsp->plain.message.header.response.cas= + ntohll(rsp->plain.message.header.response.cas); + + size_t bodysz= rsp->plain.message.header.response.bodylen; + if (bodysz > 0) + execute(retry_read(rsp->bytes + sizeof (protocol_binary_response_no_extras), bodysz)); + + return TEST_PASS; +} + +/** + * Create a storage command (add, set, replace etc) + * + * @param cmd destination buffer + * @param cc the storage command to create + * @param key the key to store + * @param keylen the length of the key + * @param dta the data to store with the key + * @param dtalen the length of the data to store with the key + * @param flags the flags to store along with the key + * @param exptime the expiry time for the key + */ +static void storage_command(command *cmd, + uint8_t cc, + const void* key, + size_t keylen, + const void* dta, + size_t dtalen, + uint32_t flags, + uint32_t exptime) +{ + /* all of the storage commands use the same command layout */ + protocol_binary_request_set *request= &cmd->set; + + memset(request, 0, sizeof (*request)); + request->message.header.request.magic= PROTOCOL_BINARY_REQ; + request->message.header.request.opcode= cc; + request->message.header.request.keylen= (uint16_t)keylen; + request->message.header.request.extlen= 8; + request->message.header.request.bodylen= (uint32_t)(keylen + 8 + dtalen); + request->message.header.request.opaque= 0xdeadbeef; + request->message.body.flags= flags; + request->message.body.expiration= exptime; + + off_t key_offset= sizeof (protocol_binary_request_no_extras) + 8; + memcpy(cmd->bytes + key_offset, key, keylen); + if (dta != NULL) + memcpy(cmd->bytes + key_offset + keylen, dta, dtalen); +} + +/** + * Create a basic command to send to the server + * @param cmd destination buffer + * @param cc the command to create + * @param key the key to store + * @param keylen the length of the key + * @param dta the data to store with the key + * @param dtalen the length of the data to store with the key + */ +static void raw_command(command *cmd, + uint8_t cc, + const void* key, + size_t keylen, + const void* dta, + size_t dtalen) +{ + /* all of the storage commands use the same command layout */ + memset(cmd, 0, sizeof (*cmd)); + cmd->plain.message.header.request.magic= PROTOCOL_BINARY_REQ; + cmd->plain.message.header.request.opcode= cc; + cmd->plain.message.header.request.keylen= (uint16_t)keylen; + cmd->plain.message.header.request.bodylen= (uint32_t)(keylen + dtalen); + cmd->plain.message.header.request.opaque= 0xdeadbeef; + + off_t key_offset= sizeof (protocol_binary_request_no_extras); + + if (key != NULL) + memcpy(cmd->bytes + key_offset, key, keylen); + + if (dta != NULL) + memcpy(cmd->bytes + key_offset + keylen, dta, dtalen); +} + +/** + * Create the flush command + * @param cmd destination buffer + * @param cc the command to create (FLUSH/FLUSHQ) + * @param exptime when to flush + * @param use_extra to force using of the extra field? + */ +static void flush_command(command *cmd, + uint8_t cc, uint32_t exptime, bool use_extra) +{ + memset(cmd, 0, sizeof (cmd->flush)); + cmd->flush.message.header.request.magic= PROTOCOL_BINARY_REQ; + cmd->flush.message.header.request.opcode= cc; + cmd->flush.message.header.request.opaque= 0xdeadbeef; + + if (exptime != 0 || use_extra) + { + cmd->flush.message.header.request.extlen= 4; + cmd->flush.message.body.expiration= htonl(exptime); + cmd->flush.message.header.request.bodylen= 4; + } +} + +/** + * Create a incr/decr command + * @param cc the cmd to create (FLUSH/FLUSHQ) + * @param key the key to operate on + * @param keylen the number of bytes in the key + * @param delta the number to add/subtract + * @param initial the initial value if the key doesn't exist + * @param exptime when the key should expire if it isn't set + */ +static void arithmetic_command(command *cmd, + uint8_t cc, + const void* key, + size_t keylen, + uint64_t delta, + uint64_t initial, + uint32_t exptime) +{ + memset(cmd, 0, sizeof (cmd->incr)); + cmd->incr.message.header.request.magic= PROTOCOL_BINARY_REQ; + cmd->incr.message.header.request.opcode= cc; + cmd->incr.message.header.request.keylen= (uint16_t)keylen; + cmd->incr.message.header.request.extlen= 20; + cmd->incr.message.header.request.bodylen= (uint32_t)(keylen + 20); + cmd->incr.message.header.request.opaque= 0xdeadbeef; + cmd->incr.message.body.delta= htonll(delta); + cmd->incr.message.body.initial= htonll(initial); + cmd->incr.message.body.expiration= htonl(exptime); + + off_t key_offset= sizeof (protocol_binary_request_no_extras) + 20; + memcpy(cmd->bytes + key_offset, key, keylen); +} + +/** + * Validate the response header from the server + * @param rsp the response to check + * @param cc the expected command + * @param status the expected status + */ +static enum test_return do_validate_response_header(response *rsp, + uint8_t cc, uint16_t status) +{ + verify(rsp->plain.message.header.response.magic == PROTOCOL_BINARY_RES); + verify(rsp->plain.message.header.response.opcode == cc); + verify(rsp->plain.message.header.response.datatype == PROTOCOL_BINARY_RAW_BYTES); + verify(rsp->plain.message.header.response.status == status); + verify(rsp->plain.message.header.response.opaque == 0xdeadbeef); + + if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) + { + switch (cc) { + case PROTOCOL_BINARY_CMD_ADDQ: + case PROTOCOL_BINARY_CMD_APPENDQ: + case PROTOCOL_BINARY_CMD_DECREMENTQ: + case PROTOCOL_BINARY_CMD_DELETEQ: + case PROTOCOL_BINARY_CMD_FLUSHQ: + case PROTOCOL_BINARY_CMD_INCREMENTQ: + case PROTOCOL_BINARY_CMD_PREPENDQ: + case PROTOCOL_BINARY_CMD_QUITQ: + case PROTOCOL_BINARY_CMD_REPLACEQ: + case PROTOCOL_BINARY_CMD_SETQ: + verify("Quiet command shouldn't return on success" == NULL); + default: + break; + } + + switch (cc) { + case PROTOCOL_BINARY_CMD_ADD: + case PROTOCOL_BINARY_CMD_REPLACE: + case PROTOCOL_BINARY_CMD_SET: + case PROTOCOL_BINARY_CMD_APPEND: + case PROTOCOL_BINARY_CMD_PREPEND: + verify(rsp->plain.message.header.response.keylen == 0); + verify(rsp->plain.message.header.response.extlen == 0); + verify(rsp->plain.message.header.response.bodylen == 0); + verify(rsp->plain.message.header.response.cas != 0); + break; + case PROTOCOL_BINARY_CMD_FLUSH: + case PROTOCOL_BINARY_CMD_NOOP: + case PROTOCOL_BINARY_CMD_QUIT: + case PROTOCOL_BINARY_CMD_DELETE: + verify(rsp->plain.message.header.response.keylen == 0); + verify(rsp->plain.message.header.response.extlen == 0); + verify(rsp->plain.message.header.response.bodylen == 0); + verify(rsp->plain.message.header.response.cas == 0); + break; + + case PROTOCOL_BINARY_CMD_DECREMENT: + case PROTOCOL_BINARY_CMD_INCREMENT: + verify(rsp->plain.message.header.response.keylen == 0); + verify(rsp->plain.message.header.response.extlen == 0); + verify(rsp->plain.message.header.response.bodylen == 8); + verify(rsp->plain.message.header.response.cas != 0); + break; + + case PROTOCOL_BINARY_CMD_STAT: + verify(rsp->plain.message.header.response.extlen == 0); + /* key and value exists in all packets except in the terminating */ + verify(rsp->plain.message.header.response.cas == 0); + break; + + case PROTOCOL_BINARY_CMD_VERSION: + verify(rsp->plain.message.header.response.keylen == 0); + verify(rsp->plain.message.header.response.extlen == 0); + verify(rsp->plain.message.header.response.bodylen != 0); + verify(rsp->plain.message.header.response.cas == 0); + break; + + case PROTOCOL_BINARY_CMD_GET: + case PROTOCOL_BINARY_CMD_GETQ: + verify(rsp->plain.message.header.response.keylen == 0); + verify(rsp->plain.message.header.response.extlen == 4); + verify(rsp->plain.message.header.response.cas != 0); + break; + + case PROTOCOL_BINARY_CMD_GETK: + case PROTOCOL_BINARY_CMD_GETKQ: + verify(rsp->plain.message.header.response.keylen != 0); + verify(rsp->plain.message.header.response.extlen == 4); + verify(rsp->plain.message.header.response.cas != 0); + break; + + default: + /* Undefined command code */ + break; + } + } + else + { + verify(rsp->plain.message.header.response.cas == 0); + verify(rsp->plain.message.header.response.extlen == 0); + if (cc != PROTOCOL_BINARY_CMD_GETK) + { + verify(rsp->plain.message.header.response.keylen == 0); + } + } + + return TEST_PASS; +} + +/* We call verify(validate_response_header), but that macro + * expects a boolean expression, and the function returns + * an enum.... Let's just create a macro to avoid cluttering + * the code with all of the == TEST_PASS ;-) + */ +#define validate_response_header(a,b,c) \ + do_validate_response_header(a,b,c) == TEST_PASS + + +static enum test_return send_binary_noop(void) +{ + command cmd; + raw_command(&cmd, PROTOCOL_BINARY_CMD_NOOP, NULL, 0, NULL, 0); + execute(send_packet(&cmd)); + return TEST_PASS; +} + +static enum test_return receive_binary_noop(void) +{ + response rsp; + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_NOOP, + PROTOCOL_BINARY_RESPONSE_SUCCESS)); + return TEST_PASS; +} + +static enum test_return test_binary_noop(void) +{ + execute(send_binary_noop()); + execute(receive_binary_noop()); + return TEST_PASS; +} + +static enum test_return test_binary_quit_impl(uint8_t cc) +{ + command cmd; + response rsp; + raw_command(&cmd, cc, NULL, 0, NULL, 0); + + execute(send_packet(&cmd)); + if (cc == PROTOCOL_BINARY_CMD_QUIT) + { + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_QUIT, + PROTOCOL_BINARY_RESPONSE_SUCCESS)); + } + + /* Socket should be closed now, read should return EXIT_SUCCESS */ + verify(timeout_io_op(sock, POLLIN, rsp.bytes, sizeof(rsp.bytes)) == 0); + + return TEST_PASS_RECONNECT; +} + +static enum test_return test_binary_quit(void) +{ + return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUIT); +} + +static enum test_return test_binary_quitq(void) +{ + return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUITQ); +} + +static enum test_return test_binary_set_impl(const char* key, uint8_t cc) +{ + command cmd; + response rsp; + + uint64_t value= 0xdeadbeefdeadcafe; + storage_command(&cmd, cc, key, strlen(key), &value, sizeof (value), 0, 0); + + /* set should always work */ + for (int ii= 0; ii < 10; ii++) + { + if (ii == 0) + execute(send_packet(&cmd)); + else + execute(resend_packet(&cmd)); + + if (cc == PROTOCOL_BINARY_CMD_SET) + { + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); + } + else + execute(test_binary_noop()); + } + + /* + * We need to get the current CAS id, and at this time we haven't + * verified that we have a working get + */ + if (cc == PROTOCOL_BINARY_CMD_SETQ) + { + cmd.set.message.header.request.opcode= PROTOCOL_BINARY_CMD_SET; + execute(resend_packet(&cmd)); + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_SET, + PROTOCOL_BINARY_RESPONSE_SUCCESS)); + cmd.set.message.header.request.opcode= PROTOCOL_BINARY_CMD_SETQ; + } + + /* try to set with the correct CAS value */ + cmd.plain.message.header.request.cas= + htonll(rsp.plain.message.header.response.cas); + execute(resend_packet(&cmd)); + if (cc == PROTOCOL_BINARY_CMD_SET) + { + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); + } + else + execute(test_binary_noop()); + + /* try to set with an incorrect CAS value */ + cmd.plain.message.header.request.cas= + htonll(rsp.plain.message.header.response.cas - 1); + execute(resend_packet(&cmd)); + execute(send_binary_noop()); + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS)); + execute(receive_binary_noop()); + + return TEST_PASS; +} + +static enum test_return test_binary_set(void) +{ + return test_binary_set_impl("test_binary_set", PROTOCOL_BINARY_CMD_SET); +} + +static enum test_return test_binary_setq(void) +{ + return test_binary_set_impl("test_binary_setq", PROTOCOL_BINARY_CMD_SETQ); +} + +static enum test_return test_binary_add_impl(const char* key, uint8_t cc) +{ + command cmd; + response rsp; + uint64_t value= 0xdeadbeefdeadcafe; + storage_command(&cmd, cc, key, strlen(key), &value, sizeof (value), 0, 0); + + /* first add should work, rest of them should fail (even with cas + as wildcard */ + for (int ii=0; ii < 10; ii++) + { + if (ii == 0) + execute(send_packet(&cmd)); + else + execute(resend_packet(&cmd)); + + if (cc == PROTOCOL_BINARY_CMD_ADD || ii > 0) + { + uint16_t expected_result; + if (ii == 0) + expected_result= PROTOCOL_BINARY_RESPONSE_SUCCESS; + else + expected_result= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS; + + execute(send_binary_noop()); + execute(recv_packet(&rsp)); + execute(receive_binary_noop()); + verify(validate_response_header(&rsp, cc, expected_result)); + } + else + execute(test_binary_noop()); + } + + return TEST_PASS; +} + +static enum test_return test_binary_add(void) +{ + return test_binary_add_impl("test_binary_add", PROTOCOL_BINARY_CMD_ADD); +} + +static enum test_return test_binary_addq(void) +{ + return test_binary_add_impl("test_binary_addq", PROTOCOL_BINARY_CMD_ADDQ); +} + +static enum test_return binary_set_item(const char *key, const char *value) +{ + command cmd; + response rsp; + storage_command(&cmd, PROTOCOL_BINARY_CMD_SET, key, strlen(key), + value, strlen(value), 0, 0); + execute(send_packet(&cmd)); + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_SET, + PROTOCOL_BINARY_RESPONSE_SUCCESS)); + return TEST_PASS; +} + +static enum test_return test_binary_replace_impl(const char* key, uint8_t cc) +{ + command cmd; + response rsp; + uint64_t value= 0xdeadbeefdeadcafe; + storage_command(&cmd, cc, key, strlen(key), &value, sizeof (value), 0, 0); + + /* first replace should fail, successive should succeed (when the + item is added! */ + for (int ii= 0; ii < 10; ii++) + { + if (ii == 0) + execute(send_packet(&cmd)); + else + execute(resend_packet(&cmd)); + + if (cc == PROTOCOL_BINARY_CMD_REPLACE || ii == 0) + { + uint16_t expected_result; + if (ii == 0) + expected_result=PROTOCOL_BINARY_RESPONSE_KEY_ENOENT; + else + expected_result=PROTOCOL_BINARY_RESPONSE_SUCCESS; + + execute(send_binary_noop()); + execute(recv_packet(&rsp)); + execute(receive_binary_noop()); + verify(validate_response_header(&rsp, cc, expected_result)); + + if (ii == 0) + execute(binary_set_item(key, key)); + } + else + execute(test_binary_noop()); + } + + /* verify that replace with CAS value works! */ + cmd.plain.message.header.request.cas= + htonll(rsp.plain.message.header.response.cas); + execute(resend_packet(&cmd)); + + if (cc == PROTOCOL_BINARY_CMD_REPLACE) + { + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); + } + else + execute(test_binary_noop()); + + /* try to set with an incorrect CAS value */ + cmd.plain.message.header.request.cas= + htonll(rsp.plain.message.header.response.cas - 1); + execute(resend_packet(&cmd)); + execute(send_binary_noop()); + execute(recv_packet(&rsp)); + execute(receive_binary_noop()); + verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS)); + + return TEST_PASS; +} + +static enum test_return test_binary_replace(void) +{ + return test_binary_replace_impl("test_binary_replace", PROTOCOL_BINARY_CMD_REPLACE); +} + +static enum test_return test_binary_replaceq(void) +{ + return test_binary_replace_impl("test_binary_replaceq", PROTOCOL_BINARY_CMD_REPLACEQ); +} + +static enum test_return test_binary_delete_impl(const char *key, uint8_t cc) +{ + command cmd; + response rsp; + raw_command(&cmd, cc, key, strlen(key), NULL, 0); + + /* The delete shouldn't work the first time, because the item isn't there */ + execute(send_packet(&cmd)); + execute(send_binary_noop()); + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT)); + execute(receive_binary_noop()); + execute(binary_set_item(key, key)); + + /* The item should be present now, resend*/ + execute(resend_packet(&cmd)); + if (cc == PROTOCOL_BINARY_CMD_DELETE) + { + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); + } + + execute(test_binary_noop()); + + return TEST_PASS; +} + +static enum test_return test_binary_delete(void) +{ + return test_binary_delete_impl("test_binary_delete", PROTOCOL_BINARY_CMD_DELETE); +} + +static enum test_return test_binary_deleteq(void) +{ + return test_binary_delete_impl("test_binary_deleteq", PROTOCOL_BINARY_CMD_DELETEQ); +} + +static enum test_return test_binary_get_impl(const char *key, uint8_t cc) +{ + command cmd; + response rsp; + + raw_command(&cmd, cc, key, strlen(key), NULL, 0); + execute(send_packet(&cmd)); + execute(send_binary_noop()); + + if (cc == PROTOCOL_BINARY_CMD_GET || cc == PROTOCOL_BINARY_CMD_GETK) + { + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT)); + } + + execute(receive_binary_noop()); + + execute(binary_set_item(key, key)); + execute(resend_packet(&cmd)); + execute(send_binary_noop()); + + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); + execute(receive_binary_noop()); + + return TEST_PASS; +} + +static enum test_return test_binary_get(void) +{ + return test_binary_get_impl("test_binary_get", PROTOCOL_BINARY_CMD_GET); +} + +static enum test_return test_binary_getk(void) +{ + return test_binary_get_impl("test_binary_getk", PROTOCOL_BINARY_CMD_GETK); +} + +static enum test_return test_binary_getq(void) +{ + return test_binary_get_impl("test_binary_getq", PROTOCOL_BINARY_CMD_GETQ); +} + +static enum test_return test_binary_getkq(void) +{ + return test_binary_get_impl("test_binary_getkq", PROTOCOL_BINARY_CMD_GETKQ); +} + +static enum test_return test_binary_incr_impl(const char* key, uint8_t cc) +{ + command cmd; + response rsp; + arithmetic_command(&cmd, cc, key, strlen(key), 1, 0, 0); + + uint64_t ii; + for (ii= 0; ii < 10; ++ii) + { + if (ii == 0) + execute(send_packet(&cmd)); + else + execute(resend_packet(&cmd)); + + if (cc == PROTOCOL_BINARY_CMD_INCREMENT) + { + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); + verify(ntohll(rsp.incr.message.body.value) == ii); + } + else + execute(test_binary_noop()); + } + + /* @todo add incorrect CAS */ + return TEST_PASS; +} + +static enum test_return test_binary_incr(void) +{ + return test_binary_incr_impl("test_binary_incr", PROTOCOL_BINARY_CMD_INCREMENT); +} + +static enum test_return test_binary_incrq(void) +{ + return test_binary_incr_impl("test_binary_incrq", PROTOCOL_BINARY_CMD_INCREMENTQ); +} + +static enum test_return test_binary_decr_impl(const char* key, uint8_t cc) +{ + command cmd; + response rsp; + arithmetic_command(&cmd, cc, key, strlen(key), 1, 9, 0); + + int ii; + for (ii= 9; ii > -1; --ii) + { + if (ii == 9) + execute(send_packet(&cmd)); + else + execute(resend_packet(&cmd)); + + if (cc == PROTOCOL_BINARY_CMD_DECREMENT) + { + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); + verify(ntohll(rsp.decr.message.body.value) == (uint64_t)ii); + } + else + execute(test_binary_noop()); + } + + /* decr 0 should not wrap */ + execute(resend_packet(&cmd)); + if (cc == PROTOCOL_BINARY_CMD_DECREMENT) + { + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); + verify(ntohll(rsp.decr.message.body.value) == 0); + } + else + { + /* @todo get the value and verify! */ + + } + + /* @todo add incorrect cas */ + execute(test_binary_noop()); + return TEST_PASS; +} + +static enum test_return test_binary_decr(void) +{ + return test_binary_decr_impl("test_binary_decr", + PROTOCOL_BINARY_CMD_DECREMENT); +} + +static enum test_return test_binary_decrq(void) +{ + return test_binary_decr_impl("test_binary_decrq", + PROTOCOL_BINARY_CMD_DECREMENTQ); +} + +static enum test_return test_binary_version(void) +{ + command cmd; + response rsp; + raw_command(&cmd, PROTOCOL_BINARY_CMD_VERSION, NULL, 0, NULL, 0); + + execute(send_packet(&cmd)); + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_VERSION, + PROTOCOL_BINARY_RESPONSE_SUCCESS)); + + return TEST_PASS; +} + +static enum test_return test_binary_flush_impl(const char *key, uint8_t cc) +{ + command cmd; + response rsp; + + for (int ii= 0; ii < 2; ++ii) + { + execute(binary_set_item(key, key)); + flush_command(&cmd, cc, 0, ii == 0); + execute(send_packet(&cmd)); + + if (cc == PROTOCOL_BINARY_CMD_FLUSH) + { + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); + } + else + execute(test_binary_noop()); + + raw_command(&cmd, PROTOCOL_BINARY_CMD_GET, key, strlen(key), NULL, 0); + execute(send_packet(&cmd)); + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_GET, + PROTOCOL_BINARY_RESPONSE_KEY_ENOENT)); + } + + return TEST_PASS; +} + +static enum test_return test_binary_flush(void) +{ + return test_binary_flush_impl("test_binary_flush", PROTOCOL_BINARY_CMD_FLUSH); +} + +static enum test_return test_binary_flushq(void) +{ + return test_binary_flush_impl("test_binary_flushq", PROTOCOL_BINARY_CMD_FLUSHQ); +} + +static enum test_return test_binary_concat_impl(const char *key, uint8_t cc) +{ + command cmd; + response rsp; + const char *value; + + if (cc == PROTOCOL_BINARY_CMD_APPEND || cc == PROTOCOL_BINARY_CMD_APPENDQ) + value="hello"; + else + value=" world"; + + execute(binary_set_item(key, value)); + + if (cc == PROTOCOL_BINARY_CMD_APPEND || cc == PROTOCOL_BINARY_CMD_APPENDQ) + value=" world"; + else + value="hello"; + + raw_command(&cmd, cc, key, strlen(key), value, strlen(value)); + execute(send_packet(&cmd)); + if (cc == PROTOCOL_BINARY_CMD_APPEND || cc == PROTOCOL_BINARY_CMD_PREPEND) + { + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); + } + else + execute(test_binary_noop()); + + raw_command(&cmd, PROTOCOL_BINARY_CMD_GET, key, strlen(key), NULL, 0); + execute(send_packet(&cmd)); + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_GET, + PROTOCOL_BINARY_RESPONSE_SUCCESS)); + verify(rsp.plain.message.header.response.bodylen - 4 == 11); + verify(memcmp(rsp.bytes + 28, "hello world", 11) == 0); + + return TEST_PASS; +} + +static enum test_return test_binary_append(void) +{ + return test_binary_concat_impl("test_binary_append", PROTOCOL_BINARY_CMD_APPEND); +} + +static enum test_return test_binary_prepend(void) +{ + return test_binary_concat_impl("test_binary_prepend", PROTOCOL_BINARY_CMD_PREPEND); +} + +static enum test_return test_binary_appendq(void) +{ + return test_binary_concat_impl("test_binary_appendq", PROTOCOL_BINARY_CMD_APPENDQ); +} + +static enum test_return test_binary_prependq(void) +{ + return test_binary_concat_impl("test_binary_prependq", PROTOCOL_BINARY_CMD_PREPENDQ); +} + +static enum test_return test_binary_stat(void) +{ + command cmd; + response rsp; + + raw_command(&cmd, PROTOCOL_BINARY_CMD_STAT, NULL, 0, NULL, 0); + execute(send_packet(&cmd)); + + do + { + execute(recv_packet(&rsp)); + verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_STAT, + PROTOCOL_BINARY_RESPONSE_SUCCESS)); + } while (rsp.plain.message.header.response.keylen != 0); + + return TEST_PASS; +} + +static enum test_return send_string(const char *cmd) +{ + execute(retry_write(cmd, strlen(cmd))); + return TEST_PASS; +} + +static enum test_return receive_line(char *buffer, size_t size) +{ + size_t offset= 0; + while (offset < size) + { + execute(retry_read(buffer + offset, 1)); + if (buffer[offset] == '\n') + { + if (offset + 1 < size) + { + buffer[offset + 1]= '\0'; + return TEST_PASS; + } + else + return TEST_FAIL; + } + ++offset; + } + + return TEST_FAIL; +} + +static enum test_return receive_response(const char *msg) { + char buffer[80]; + execute(receive_line(buffer, sizeof(buffer))); + if (strcmp(msg, buffer) != 0) { + fprintf(stderr, "[%s]\n", buffer); + } + verify(strcmp(msg, buffer) == 0); + return TEST_PASS; +} + +static enum test_return receive_error_response(void) +{ + char buffer[80]; + execute(receive_line(buffer, sizeof(buffer))); + verify(strncmp(buffer, "ERROR", 5) == 0 || + strncmp(buffer, "CLIENT_ERROR", 12) == 0 || + strncmp(buffer, "SERVER_ERROR", 12) == 0); + return TEST_PASS; +} + +static enum test_return test_ascii_quit(void) +{ + /* Verify that quit handles unknown options */ + execute(send_string("quit foo bar\r\n")); + execute(receive_error_response()); + + /* quit doesn't support noreply */ + execute(send_string("quit noreply\r\n")); + execute(receive_error_response()); + + /* Verify that quit works */ + execute(send_string("quit\r\n")); + + /* Socket should be closed now, read should return EXIT_SUCCESS */ + char buffer[80]; + verify(timeout_io_op(sock, POLLIN, buffer, sizeof(buffer)) == 0); + return TEST_PASS_RECONNECT; + +} + +static enum test_return test_ascii_version(void) +{ + /* Verify that version command handles unknown options */ + execute(send_string("version foo bar\r\n")); + execute(receive_error_response()); + + /* version doesn't support noreply */ + execute(send_string("version noreply\r\n")); + execute(receive_error_response()); + + /* Verify that verify works */ + execute(send_string("version\r\n")); + char buffer[256]; + execute(receive_line(buffer, sizeof(buffer))); + verify(strncmp(buffer, "VERSION ", 8) == 0); + + return TEST_PASS; +} + +static enum test_return test_ascii_verbosity(void) +{ + /* This command does not adhere to the spec! */ + execute(send_string("verbosity foo bar my\r\n")); + execute(receive_error_response()); + + execute(send_string("verbosity noreply\r\n")); + execute(receive_error_response()); + + execute(send_string("verbosity 0 noreply\r\n")); + execute(test_ascii_version()); + + execute(send_string("verbosity\r\n")); + execute(receive_error_response()); + + execute(send_string("verbosity 1\r\n")); + execute(receive_response("OK\r\n")); + + execute(send_string("verbosity 0\r\n")); + execute(receive_response("OK\r\n")); + + return TEST_PASS; +} + + + +static enum test_return test_ascii_set_impl(const char* key, bool noreply) +{ + /* @todo add tests for bogus format! */ + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "set %s 0 0 5%s\r\nvalue\r\n", key, noreply ? " noreply" : ""); + execute(send_string(buffer)); + + if (!noreply) + execute(receive_response("STORED\r\n")); + + return test_ascii_version(); +} + +static enum test_return test_ascii_set(void) +{ + return test_ascii_set_impl("test_ascii_set", false); +} + +static enum test_return test_ascii_set_noreply(void) +{ + return test_ascii_set_impl("test_ascii_set_noreply", true); +} + +static enum test_return test_ascii_add_impl(const char* key, bool noreply) +{ + /* @todo add tests for bogus format! */ + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "add %s 0 0 5%s\r\nvalue\r\n", key, noreply ? " noreply" : ""); + execute(send_string(buffer)); + + if (!noreply) + execute(receive_response("STORED\r\n")); + + execute(send_string(buffer)); + + if (!noreply) + execute(receive_response("NOT_STORED\r\n")); + + return test_ascii_version(); +} + +static enum test_return test_ascii_add(void) +{ + return test_ascii_add_impl("test_ascii_add", false); +} + +static enum test_return test_ascii_add_noreply(void) +{ + return test_ascii_add_impl("test_ascii_add_noreply", true); +} + +static enum test_return ascii_get_unknown_value(char **key, char **value, ssize_t *ndata) +{ + char buffer[1024]; + + execute(receive_line(buffer, sizeof(buffer))); + verify(strncmp(buffer, "VALUE ", 6) == 0); + char *end= strchr(buffer + 6, ' '); + verify(end != NULL); + *end= '\0'; + *key= strdup(buffer + 6); + verify(*key != NULL); + char *ptr= end + 1; + + unsigned long val= strtoul(ptr, &end, 10); /* flags */ + verify(ptr != end); + verify(val == 0); + verify(end != NULL); + *ndata = (ssize_t)strtoul(end, &end, 10); /* size */ + verify(ptr != end); + verify(end != NULL); + while (*end != '\n' && isspace(*end)) + ++end; + verify(*end == '\n'); + + *value= static_cast(malloc((size_t)*ndata)); + verify(*value != NULL); + + execute(retry_read(*value, (size_t)*ndata)); + + execute(retry_read(buffer, 2)); + verify(memcmp(buffer, "\r\n", 2) == 0); + + return TEST_PASS; +} + +static enum test_return ascii_get_value(const char *key, const char *value) +{ + + char buffer[1024]; + size_t datasize= strlen(value); + + verify(datasize < sizeof(buffer)); + execute(receive_line(buffer, sizeof(buffer))); + verify(strncmp(buffer, "VALUE ", 6) == 0); + verify(strncmp(buffer + 6, key, strlen(key)) == 0); + char *ptr= buffer + 6 + strlen(key) + 1; + char *end; + + unsigned long val= strtoul(ptr, &end, 10); /* flags */ + verify(ptr != end); + verify(val == 0); + verify(end != NULL); + val= strtoul(end, &end, 10); /* size */ + verify(ptr != end); + verify(val == datasize); + verify(end != NULL); + while (*end != '\n' && isspace(*end)) + ++end; + verify(*end == '\n'); + + execute(retry_read(buffer, datasize)); + verify(memcmp(buffer, value, datasize) == 0); + + execute(retry_read(buffer, 2)); + verify(memcmp(buffer, "\r\n", 2) == 0); + + return TEST_PASS; +} + +static enum test_return ascii_get_item(const char *key, const char *value, + bool exist) +{ + char buffer[1024]; + size_t datasize= 0; + if (value != NULL) + datasize= strlen(value); + + verify(datasize < sizeof(buffer)); + snprintf(buffer, sizeof(buffer), "get %s\r\n", key); + execute(send_string(buffer)); + + if (exist) + execute(ascii_get_value(key, value)); + + execute(retry_read(buffer, 5)); + verify(memcmp(buffer, "END\r\n", 5) == 0); + + return TEST_PASS; +} + +static enum test_return ascii_gets_value(const char *key, const char *value, + unsigned long *cas) +{ + + char buffer[1024]; + size_t datasize= strlen(value); + + verify(datasize < sizeof(buffer)); + execute(receive_line(buffer, sizeof(buffer))); + verify(strncmp(buffer, "VALUE ", 6) == 0); + verify(strncmp(buffer + 6, key, strlen(key)) == 0); + char *ptr= buffer + 6 + strlen(key) + 1; + char *end; + + unsigned long val= strtoul(ptr, &end, 10); /* flags */ + verify(ptr != end); + verify(val == 0); + verify(end != NULL); + val= strtoul(end, &end, 10); /* size */ + verify(ptr != end); + verify(val == datasize); + verify(end != NULL); + *cas= strtoul(end, &end, 10); /* cas */ + verify(ptr != end); + verify(val == datasize); + verify(end != NULL); + + while (*end != '\n' && isspace(*end)) + ++end; + verify(*end == '\n'); + + execute(retry_read(buffer, datasize)); + verify(memcmp(buffer, value, datasize) == 0); + + execute(retry_read(buffer, 2)); + verify(memcmp(buffer, "\r\n", 2) == 0); + + return TEST_PASS; +} + +static enum test_return ascii_gets_item(const char *key, const char *value, + bool exist, unsigned long *cas) +{ + char buffer[1024]; + size_t datasize= 0; + if (value != NULL) + datasize= strlen(value); + + verify(datasize < sizeof(buffer)); + snprintf(buffer, sizeof(buffer), "gets %s\r\n", key); + execute(send_string(buffer)); + + if (exist) + execute(ascii_gets_value(key, value, cas)); + + execute(retry_read(buffer, 5)); + verify(memcmp(buffer, "END\r\n", 5) == 0); + + return TEST_PASS; +} + +static enum test_return ascii_set_item(const char *key, const char *value) +{ + char buffer[300]; + size_t len= strlen(value); + snprintf(buffer, sizeof(buffer), "set %s 0 0 %u\r\n", key, (unsigned int)len); + execute(send_string(buffer)); + execute(retry_write(value, len)); + execute(send_string("\r\n")); + execute(receive_response("STORED\r\n")); + return TEST_PASS; +} + +static enum test_return test_ascii_replace_impl(const char* key, bool noreply) +{ + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "replace %s 0 0 5%s\r\nvalue\r\n", key, noreply ? " noreply" : ""); + execute(send_string(buffer)); + + if (noreply) + execute(test_ascii_version()); + else + execute(receive_response("NOT_STORED\r\n")); + + execute(ascii_set_item(key, "value")); + execute(ascii_get_item(key, "value", true)); + + + execute(send_string(buffer)); + + if (noreply) + execute(test_ascii_version()); + else + execute(receive_response("STORED\r\n")); + + return test_ascii_version(); +} + +static enum test_return test_ascii_replace(void) +{ + return test_ascii_replace_impl("test_ascii_replace", false); +} + +static enum test_return test_ascii_replace_noreply(void) +{ + return test_ascii_replace_impl("test_ascii_replace_noreply", true); +} + +static enum test_return test_ascii_cas_impl(const char* key, bool noreply) +{ + char buffer[1024]; + unsigned long cas; + + execute(ascii_set_item(key, "value")); + execute(ascii_gets_item(key, "value", true, &cas)); + + snprintf(buffer, sizeof(buffer), "cas %s 0 0 6 %lu%s\r\nvalue2\r\n", key, cas, noreply ? " noreply" : ""); + execute(send_string(buffer)); + + if (noreply) + execute(test_ascii_version()); + else + execute(receive_response("STORED\r\n")); + + /* reexecute the same command should fail due to illegal cas */ + execute(send_string(buffer)); + + if (noreply) + execute(test_ascii_version()); + else + execute(receive_response("EXISTS\r\n")); + + return test_ascii_version(); +} + +static enum test_return test_ascii_cas(void) +{ + return test_ascii_cas_impl("test_ascii_cas", false); +} + +static enum test_return test_ascii_cas_noreply(void) +{ + return test_ascii_cas_impl("test_ascii_cas_noreply", true); +} + +static enum test_return test_ascii_delete_impl(const char *key, bool noreply) +{ + execute(ascii_set_item(key, "value")); + + execute(send_string("delete\r\n")); + execute(receive_error_response()); + /* BUG: the server accepts delete a b */ + execute(send_string("delete a b c d e\r\n")); + execute(receive_error_response()); + + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "delete %s%s\r\n", key, noreply ? " noreply" : ""); + execute(send_string(buffer)); + + if (noreply) + execute(test_ascii_version()); + else + execute(receive_response("DELETED\r\n")); + + execute(ascii_get_item(key, "value", false)); + execute(send_string(buffer)); + if (noreply) + execute(test_ascii_version()); + else + execute(receive_response("NOT_FOUND\r\n")); + + return TEST_PASS; +} + +static enum test_return test_ascii_delete(void) +{ + return test_ascii_delete_impl("test_ascii_delete", false); +} + +static enum test_return test_ascii_delete_noreply(void) +{ + return test_ascii_delete_impl("test_ascii_delete_noreply", true); +} + +static enum test_return test_ascii_get(void) +{ + execute(ascii_set_item("test_ascii_get", "value")); + + execute(send_string("get\r\n")); + execute(receive_error_response()); + execute(ascii_get_item("test_ascii_get", "value", true)); + execute(ascii_get_item("test_ascii_get_notfound", "value", false)); + + return TEST_PASS; +} + +static enum test_return test_ascii_gets(void) +{ + execute(ascii_set_item("test_ascii_gets", "value")); + + execute(send_string("gets\r\n")); + execute(receive_error_response()); + unsigned long cas; + execute(ascii_gets_item("test_ascii_gets", "value", true, &cas)); + execute(ascii_gets_item("test_ascii_gets_notfound", "value", false, &cas)); + + return TEST_PASS; +} + +static enum test_return test_ascii_mget(void) +{ + const uint32_t nkeys= 5; + const char * const keys[]= { + "test_ascii_mget1", + "test_ascii_mget2", + /* test_ascii_mget_3 does not exist :) */ + "test_ascii_mget4", + "test_ascii_mget5", + "test_ascii_mget6" + }; + + for (uint32_t x= 0; x < nkeys; ++x) + execute(ascii_set_item(keys[x], "value")); + + /* Ask for a key that doesn't exist as well */ + execute(send_string("get test_ascii_mget1 test_ascii_mget2 test_ascii_mget3 " + "test_ascii_mget4 test_ascii_mget5 " + "test_ascii_mget6\r\n")); + + char *returned[nkeys]; + + for (uint32_t x= 0; x < nkeys; ++x) + { + ssize_t nbytes = 0; + char *v= NULL; + execute(ascii_get_unknown_value(&returned[x], &v, &nbytes)); + verify(nbytes == 5); + verify(memcmp(v, "value", 5) == 0); + free(v); + } + + char buffer[5]; + execute(retry_read(buffer, 5)); + verify(memcmp(buffer, "END\r\n", 5) == 0); + + /* verify that we got all the keys we expected */ + for (uint32_t x= 0; x < nkeys; ++x) + { + bool found= false; + for (uint32_t y= 0; y < nkeys; ++y) + { + if (strcmp(keys[x], returned[y]) == 0) + { + found = true; + break; + } + } + verify(found); + } + + for (uint32_t x= 0; x < nkeys; ++x) + free(returned[x]); + + return TEST_PASS; +} + +static enum test_return test_ascii_incr_impl(const char* key, bool noreply) +{ + char cmd[300]; + snprintf(cmd, sizeof(cmd), "incr %s 1%s\r\n", key, noreply ? " noreply" : ""); + + execute(ascii_set_item(key, "0")); + for (int x= 1; x < 11; ++x) + { + execute(send_string(cmd)); + + if (noreply) + execute(test_ascii_version()); + else + { + char buffer[80]; + execute(receive_line(buffer, sizeof(buffer))); + int val= atoi(buffer); + verify(val == x); + } + } + + execute(ascii_get_item(key, "10", true)); + + return TEST_PASS; +} + +static enum test_return test_ascii_incr(void) +{ + return test_ascii_incr_impl("test_ascii_incr", false); +} + +static enum test_return test_ascii_incr_noreply(void) +{ + return test_ascii_incr_impl("test_ascii_incr_noreply", true); +} + +static enum test_return test_ascii_decr_impl(const char* key, bool noreply) +{ + char cmd[300]; + snprintf(cmd, sizeof(cmd), "decr %s 1%s\r\n", key, noreply ? " noreply" : ""); + + execute(ascii_set_item(key, "9")); + for (int x= 8; x > -1; --x) + { + execute(send_string(cmd)); + + if (noreply) + execute(test_ascii_version()); + else + { + char buffer[80]; + execute(receive_line(buffer, sizeof(buffer))); + int val= atoi(buffer); + verify(val == x); + } + } + + execute(ascii_get_item(key, "0", true)); + + /* verify that it doesn't wrap */ + execute(send_string(cmd)); + if (noreply) + execute(test_ascii_version()); + else + { + char buffer[80]; + execute(receive_line(buffer, sizeof(buffer))); + } + execute(ascii_get_item(key, "0", true)); + + return TEST_PASS; +} + +static enum test_return test_ascii_decr(void) +{ + return test_ascii_decr_impl("test_ascii_decr", false); +} + +static enum test_return test_ascii_decr_noreply(void) +{ + return test_ascii_decr_impl("test_ascii_decr_noreply", true); +} + + +static enum test_return test_ascii_flush_impl(const char *key, bool noreply) +{ +#if 0 + /* Verify that the flush_all command handles unknown options */ + /* Bug in the current memcached server! */ + execute(send_string("flush_all foo bar\r\n")); + execute(receive_error_response()); +#endif + + execute(ascii_set_item(key, key)); + execute(ascii_get_item(key, key, true)); + + if (noreply) + { + execute(send_string("flush_all noreply\r\n")); + execute(test_ascii_version()); + } + else + { + execute(send_string("flush_all\r\n")); + execute(receive_response("OK\r\n")); + } + + execute(ascii_get_item(key, key, false)); + + return TEST_PASS; +} + +static enum test_return test_ascii_flush(void) +{ + return test_ascii_flush_impl("test_ascii_flush", false); +} + +static enum test_return test_ascii_flush_noreply(void) +{ + return test_ascii_flush_impl("test_ascii_flush_noreply", true); +} + +static enum test_return test_ascii_concat_impl(const char *key, + bool append, + bool noreply) +{ + const char *value; + + if (append) + value="hello"; + else + value=" world"; + + execute(ascii_set_item(key, value)); + + if (append) + value=" world"; + else + value="hello"; + + char cmd[400]; + snprintf(cmd, sizeof(cmd), "%s %s 0 0 %u%s\r\n%s\r\n", + append ? "append" : "prepend", + key, (unsigned int)strlen(value), noreply ? " noreply" : "", + value); + execute(send_string(cmd)); + + if (noreply) + execute(test_ascii_version()); + else + execute(receive_response("STORED\r\n")); + + execute(ascii_get_item(key, "hello world", true)); + + snprintf(cmd, sizeof(cmd), "%s %s_notfound 0 0 %u%s\r\n%s\r\n", + append ? "append" : "prepend", + key, (unsigned int)strlen(value), noreply ? " noreply" : "", + value); + execute(send_string(cmd)); + + if (noreply) + execute(test_ascii_version()); + else + execute(receive_response("NOT_STORED\r\n")); + + return TEST_PASS; +} + +static enum test_return test_ascii_append(void) +{ + return test_ascii_concat_impl("test_ascii_append", true, false); +} + +static enum test_return test_ascii_prepend(void) +{ + return test_ascii_concat_impl("test_ascii_prepend", false, false); +} + +static enum test_return test_ascii_append_noreply(void) +{ + return test_ascii_concat_impl("test_ascii_append_noreply", true, true); +} + +static enum test_return test_ascii_prepend_noreply(void) +{ + return test_ascii_concat_impl("test_ascii_prepend_noreply", false, true); +} + +static enum test_return test_ascii_stat(void) +{ + execute(send_string("stats noreply\r\n")); + execute(receive_error_response()); + execute(send_string("stats\r\n")); + char buffer[1024]; + do { + execute(receive_line(buffer, sizeof(buffer))); + } while (strcmp(buffer, "END\r\n") != 0); + + return TEST_PASS_RECONNECT; +} + +typedef enum test_return(*TEST_FUNC)(void); + +struct testcase +{ + const char *description; + TEST_FUNC function; +}; + +struct testcase testcases[]= { + { "ascii quit", test_ascii_quit }, + { "ascii version", test_ascii_version }, + { "ascii verbosity", test_ascii_verbosity }, + { "ascii set", test_ascii_set }, + { "ascii set noreply", test_ascii_set_noreply }, + { "ascii get", test_ascii_get }, + { "ascii gets", test_ascii_gets }, + { "ascii mget", test_ascii_mget }, + { "ascii flush", test_ascii_flush }, + { "ascii flush noreply", test_ascii_flush_noreply }, + { "ascii add", test_ascii_add }, + { "ascii add noreply", test_ascii_add_noreply }, + { "ascii replace", test_ascii_replace }, + { "ascii replace noreply", test_ascii_replace_noreply }, + { "ascii cas", test_ascii_cas }, + { "ascii cas noreply", test_ascii_cas_noreply }, + { "ascii delete", test_ascii_delete }, + { "ascii delete noreply", test_ascii_delete_noreply }, + { "ascii incr", test_ascii_incr }, + { "ascii incr noreply", test_ascii_incr_noreply }, + { "ascii decr", test_ascii_decr }, + { "ascii decr noreply", test_ascii_decr_noreply }, + { "ascii append", test_ascii_append }, + { "ascii append noreply", test_ascii_append_noreply }, + { "ascii prepend", test_ascii_prepend }, + { "ascii prepend noreply", test_ascii_prepend_noreply }, + { "ascii stat", test_ascii_stat }, + { "binary noop", test_binary_noop }, + { "binary quit", test_binary_quit }, + { "binary quitq", test_binary_quitq }, + { "binary set", test_binary_set }, + { "binary setq", test_binary_setq }, + { "binary flush", test_binary_flush }, + { "binary flushq", test_binary_flushq }, + { "binary add", test_binary_add }, + { "binary addq", test_binary_addq }, + { "binary replace", test_binary_replace }, + { "binary replaceq", test_binary_replaceq }, + { "binary delete", test_binary_delete }, + { "binary deleteq", test_binary_deleteq }, + { "binary get", test_binary_get }, + { "binary getq", test_binary_getq }, + { "binary getk", test_binary_getk }, + { "binary getkq", test_binary_getkq }, + { "binary incr", test_binary_incr }, + { "binary incrq", test_binary_incrq }, + { "binary decr", test_binary_decr }, + { "binary decrq", test_binary_decrq }, + { "binary version", test_binary_version }, + { "binary append", test_binary_append }, + { "binary appendq", test_binary_appendq }, + { "binary prepend", test_binary_prepend }, + { "binary prependq", test_binary_prependq }, + { "binary stat", test_binary_stat }, + { NULL, NULL} +}; + +const int ascii_tests = 1; +const int binary_tests = 2; + +struct test_type_st +{ + bool ascii; + bool binary; +}; + +int main(int argc, char **argv) +{ + static const char * const status_msg[]= {"[skip]", "[pass]", "[pass]", "[FAIL]"}; + struct test_type_st tests= { true, true }; + int total= 0; + int failed= 0; + const char *hostname= "localhost"; + const char *port= "11211"; + int cmd; + bool prompt= false; + const char *testname= NULL; + + + + while ((cmd= getopt(argc, argv, "t:vch:p:PT:?ab")) != EOF) + { + switch (cmd) { + case 'a': + tests.ascii= true; + tests.binary= false; + break; + case 'b': + tests.ascii= false; + tests.binary= true; + break; + case 't': + timeout= atoi(optarg); + if (timeout == 0) + { + fprintf(stderr, "Invalid timeout. Please specify a number for -t\n"); + return EXIT_FAILURE; + } + break; + case 'v': verbose= true; + break; + case 'c': do_core= true; + break; + case 'h': hostname= optarg; + break; + case 'p': port= optarg; + break; + case 'P': prompt= true; + break; + case 'T': testname= optarg; + break; + default: + fprintf(stderr, "Usage: %s [-h hostname] [-p port] [-c] [-v] [-t n] [-P] [-T testname]'\n" + "\t-c\tGenerate coredump if a test fails\n" + "\t-v\tVerbose test output (print out the assertion)\n" + "\t-t n\tSet the timeout for io-operations to n seconds\n" + "\t-P\tPrompt the user before starting a test.\n" + "\t\t\t\"skip\" will skip the test\n" + "\t\t\t\"quit\" will terminate memcapable\n" + "\t\t\tEverything else will start the test\n" + "\t-T n\tJust run the test named n\n" + "\t-a\tOnly test the ascii protocol\n" + "\t-b\tOnly test the binary protocol\n", + argv[0]); + return EXIT_FAILURE; + } + } + + initialize_sockets(); + sock= connect_server(hostname, port); + if (sock == INVALID_SOCKET) + { + fprintf(stderr, "Failed to connect to <%s:%s>: %s\n", + hostname, port, strerror(get_socket_errno())); + return EXIT_FAILURE; + } + + for (int ii= 0; testcases[ii].description != NULL; ++ii) + { + if (testname != NULL && strcmp(testcases[ii].description, testname) != 0) + continue; + + if ((testcases[ii].description[0] == 'a' && (tests.ascii) == 0) || + (testcases[ii].description[0] == 'b' && (tests.binary) == 0)) + { + continue; + } + ++total; + fprintf(stdout, "%-40s", testcases[ii].description); + fflush(stdout); + + if (prompt) + { + fprintf(stdout, "\nPress when you are ready? "); + char buffer[80] = {0}; + if (fgets(buffer, sizeof(buffer), stdin) != NULL) { + if (strncmp(buffer, "skip", 4) == 0) + { + fprintf(stdout, "%-40s%s\n", testcases[ii].description, + status_msg[TEST_SKIP]); + fflush(stdout); + continue; + } + if (strncmp(buffer, "quit", 4) == 0) + exit(0); + } + + fprintf(stdout, "%-40s", testcases[ii].description); + fflush(stdout); + } + + bool reconnect= false; + enum test_return ret= testcases[ii].function(); + if (ret == TEST_FAIL) + { + reconnect= true; + ++failed; + if (verbose) + fprintf(stderr, "\n"); + } + else if (ret == TEST_PASS_RECONNECT) + reconnect= true; + + fprintf(stderr, "%s\n", status_msg[ret]); + if (reconnect) + { + closesocket(sock); + if ((sock= connect_server(hostname, port)) == INVALID_SOCKET) + { + fprintf(stderr, "Failed to connect to <%s:%s>: %s\n", + hostname, port, strerror(get_socket_errno())); + fprintf(stderr, "%d of %d tests failed\n", failed, total); + return EXIT_FAILURE; + } + } + } + + closesocket(sock); + if (failed == 0) + fprintf(stdout, "All tests passed\n"); + else + fprintf(stderr, "%d of %d tests failed\n", failed, total); + + return (failed == 0) ? 0 : 1; +} diff --git a/clients/memcat.c b/clients/memcat.c deleted file mode 100644 index 3f0d92b4..00000000 --- a/clients/memcat.c +++ /dev/null @@ -1,242 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: - * - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include - -#include "utilities.h" - -#define PROGRAM_NAME "memcat" -#define PROGRAM_DESCRIPTION "Cat a set of key values to stdout." - - -/* Prototypes */ -void options_parse(int argc, char *argv[]); - -static int opt_binary= 0; -static int opt_verbose= 0; -static int opt_displayflag= 0; -static char *opt_servers= NULL; -static char *opt_hash= NULL; -static char *opt_username; -static char *opt_passwd; -static char *opt_file; - -int main(int argc, char *argv[]) -{ - memcached_st *memc; - char *string; - size_t string_length; - uint32_t flags; - memcached_return_t rc; - memcached_server_st *servers; - - int return_code= 0; - - options_parse(argc, argv); - initialize_sockets(); - - if (!opt_servers) - { - char *temp; - - if ((temp= getenv("MEMCACHED_SERVERS"))) - opt_servers= strdup(temp); - else - { - fprintf(stderr, "No Servers provided\n"); - exit(1); - } - } - - memc= memcached_create(NULL); - process_hash_option(memc, opt_hash); - - servers= memcached_servers_parse(opt_servers); - - memcached_server_push(memc, servers); - memcached_server_list_free(servers); - memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, - (uint64_t)opt_binary); - - if (!initialize_sasl(memc, opt_username, opt_passwd)) - { - memcached_free(memc); - return EXIT_FAILURE; - } - - while (optind < argc) - { - string= memcached_get(memc, argv[optind], strlen(argv[optind]), - &string_length, &flags, &rc); - if (rc == MEMCACHED_SUCCESS) - { - if (opt_displayflag) - { - if (opt_verbose) - printf("key: %s\nflags: ", argv[optind]); - printf("%x\n", flags); - } - else - { - if (opt_verbose) - { - printf("key: %s\nflags: %x\nlength: %zu\nvalue: ", - argv[optind], flags, string_length); - } - - if (opt_file) - { - FILE *fp; - size_t written; - - fp= fopen(opt_file, "w"); - if (!fp) - { - perror("fopen"); - return_code= -1; - break; - } - - written= fwrite(string, 1, string_length, fp); - if (written != string_length) - { - fprintf(stderr, "error writing file (written %zu, should be %zu)\n", written, string_length); - return_code= -1; - break; - } - - if (fclose(fp)) - { - fprintf(stderr, "error closing file\n"); - return_code= -1; - break; - } - } - else - { - printf("%.*s\n", (int)string_length, string); - } - free(string); - } - } - else if (rc != MEMCACHED_NOTFOUND) - { - fprintf(stderr, "memcat: %s: memcache error %s", - argv[optind], memcached_strerror(memc, rc)); - if (memcached_last_error_errno(memc)) - { - fprintf(stderr, " system error %s", strerror(memcached_last_error_errno(memc))); - } - fprintf(stderr, "\n"); - - return_code= -1; - break; - } - else // Unknown Issue - { - fprintf(stderr, "memcat: %s not found\n", argv[optind]); - return_code= -1; - } - optind++; - } - - memcached_free(memc); - - if (opt_servers) - free(opt_servers); - if (opt_hash) - free(opt_hash); - - shutdown_sasl(); - - return return_code; -} - - -void options_parse(int argc, char *argv[]) -{ - int option_index= 0; - int option_rv; - - memcached_programs_help_st help_options[]= - { - {0}, - }; - - static struct option long_options[]= - { - {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION}, - {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP}, - {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE}, - {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG}, - {(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS}, - {(OPTIONSTRING)"flag", no_argument, &opt_displayflag, OPT_FLAG}, - {(OPTIONSTRING)"hash", required_argument, NULL, OPT_HASH}, - {(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY}, - {(OPTIONSTRING)"username", required_argument, NULL, OPT_USERNAME}, - {(OPTIONSTRING)"password", required_argument, NULL, OPT_PASSWD}, - {(OPTIONSTRING)"file", required_argument, NULL, OPT_FILE}, - {0, 0, 0, 0}, - }; - - while (1) - { - option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index); - if (option_rv == -1) break; - switch (option_rv) - { - case 0: - break; - case OPT_BINARY: - opt_binary = 1; - break; - case OPT_VERBOSE: /* --verbose or -v */ - opt_verbose = OPT_VERBOSE; - break; - case OPT_DEBUG: /* --debug or -d */ - opt_verbose = OPT_DEBUG; - break; - case OPT_VERSION: /* --version or -V */ - version_command(PROGRAM_NAME); - break; - case OPT_HELP: /* --help or -h */ - help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options); - break; - case OPT_SERVERS: /* --servers or -s */ - opt_servers= strdup(optarg); - break; - case OPT_HASH: - opt_hash= strdup(optarg); - break; - case OPT_USERNAME: - opt_username= optarg; - break; - case OPT_PASSWD: - opt_passwd= optarg; - break; - case OPT_FILE: - opt_file= optarg; - break; - case '?': - /* getopt_long already printed an error message. */ - exit(1); - default: - abort(); - } - } -} diff --git a/clients/memcat.cc b/clients/memcat.cc new file mode 100644 index 00000000..12df3479 --- /dev/null +++ b/clients/memcat.cc @@ -0,0 +1,242 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "utilities.h" + +#define PROGRAM_NAME "memcat" +#define PROGRAM_DESCRIPTION "Cat a set of key values to stdout." + + +/* Prototypes */ +void options_parse(int argc, char *argv[]); + +static int opt_binary= 0; +static int opt_verbose= 0; +static int opt_displayflag= 0; +static char *opt_servers= NULL; +static char *opt_hash= NULL; +static char *opt_username; +static char *opt_passwd; +static char *opt_file; + +int main(int argc, char *argv[]) +{ + memcached_st *memc; + char *string; + size_t string_length; + uint32_t flags; + memcached_return_t rc; + memcached_server_st *servers; + + int return_code= 0; + + options_parse(argc, argv); + initialize_sockets(); + + if (!opt_servers) + { + char *temp; + + if ((temp= getenv("MEMCACHED_SERVERS"))) + opt_servers= strdup(temp); + else + { + fprintf(stderr, "No Servers provided\n"); + exit(1); + } + } + + memc= memcached_create(NULL); + process_hash_option(memc, opt_hash); + + servers= memcached_servers_parse(opt_servers); + + memcached_server_push(memc, servers); + memcached_server_list_free(servers); + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, + (uint64_t)opt_binary); + + if (!initialize_sasl(memc, opt_username, opt_passwd)) + { + memcached_free(memc); + return EXIT_FAILURE; + } + + while (optind < argc) + { + string= memcached_get(memc, argv[optind], strlen(argv[optind]), + &string_length, &flags, &rc); + if (rc == MEMCACHED_SUCCESS) + { + if (opt_displayflag) + { + if (opt_verbose) + printf("key: %s\nflags: ", argv[optind]); + printf("%x\n", flags); + } + else + { + if (opt_verbose) + { + printf("key: %s\nflags: %x\nlength: %lu\nvalue: ", + argv[optind], flags, (unsigned long)string_length); + } + + if (opt_file) + { + FILE *fp; + size_t written; + + fp= fopen(opt_file, "w"); + if (!fp) + { + perror("fopen"); + return_code= -1; + break; + } + + written= fwrite(string, 1, string_length, fp); + if (written != string_length) + { + fprintf(stderr, "error writing file (written %lu, should be %lu)\n", (unsigned long)written, (unsigned long)string_length); + return_code= -1; + break; + } + + if (fclose(fp)) + { + fprintf(stderr, "error closing file\n"); + return_code= -1; + break; + } + } + else + { + printf("%.*s\n", (int)string_length, string); + } + free(string); + } + } + else if (rc != MEMCACHED_NOTFOUND) + { + fprintf(stderr, "memcat: %s: memcache error %s", + argv[optind], memcached_strerror(memc, rc)); + if (memcached_last_error_errno(memc)) + { + fprintf(stderr, " system error %s", strerror(memcached_last_error_errno(memc))); + } + fprintf(stderr, "\n"); + + return_code= -1; + break; + } + else // Unknown Issue + { + fprintf(stderr, "memcat: %s not found\n", argv[optind]); + return_code= -1; + } + optind++; + } + + memcached_free(memc); + + if (opt_servers) + free(opt_servers); + if (opt_hash) + free(opt_hash); + + shutdown_sasl(); + + return return_code; +} + + +void options_parse(int argc, char *argv[]) +{ + int option_index= 0; + int option_rv; + + memcached_programs_help_st help_options[]= + { + {0}, + }; + + static struct option long_options[]= + { + {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION}, + {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP}, + {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE}, + {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG}, + {(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS}, + {(OPTIONSTRING)"flag", no_argument, &opt_displayflag, OPT_FLAG}, + {(OPTIONSTRING)"hash", required_argument, NULL, OPT_HASH}, + {(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY}, + {(OPTIONSTRING)"username", required_argument, NULL, OPT_USERNAME}, + {(OPTIONSTRING)"password", required_argument, NULL, OPT_PASSWD}, + {(OPTIONSTRING)"file", required_argument, NULL, OPT_FILE}, + {0, 0, 0, 0}, + }; + + while (1) + { + option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index); + if (option_rv == -1) break; + switch (option_rv) + { + case 0: + break; + case OPT_BINARY: + opt_binary = 1; + break; + case OPT_VERBOSE: /* --verbose or -v */ + opt_verbose = OPT_VERBOSE; + break; + case OPT_DEBUG: /* --debug or -d */ + opt_verbose = OPT_DEBUG; + break; + case OPT_VERSION: /* --version or -V */ + version_command(PROGRAM_NAME); + break; + case OPT_HELP: /* --help or -h */ + help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options); + break; + case OPT_SERVERS: /* --servers or -s */ + opt_servers= strdup(optarg); + break; + case OPT_HASH: + opt_hash= strdup(optarg); + break; + case OPT_USERNAME: + opt_username= optarg; + break; + case OPT_PASSWD: + opt_passwd= optarg; + break; + case OPT_FILE: + opt_file= optarg; + break; + case '?': + /* getopt_long already printed an error message. */ + exit(1); + default: + abort(); + } + } +} diff --git a/clients/memcp.c b/clients/memcp.c deleted file mode 100644 index bf3828ae..00000000 --- a/clients/memcp.c +++ /dev/null @@ -1,317 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: - * - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#include - -#include "client_options.h" -#include "utilities.h" - -#define PROGRAM_NAME "memcp" -#define PROGRAM_DESCRIPTION "Copy a set of files to a memcached cluster." - -/* Prototypes */ -static void options_parse(int argc, char *argv[]); - -static int opt_binary=0; -static int opt_verbose= 0; -static char *opt_servers= NULL; -static char *opt_hash= NULL; -static int opt_method= OPT_SET; -static uint32_t opt_flags= 0; -static time_t opt_expires= 0; -static char *opt_username; -static char *opt_passwd; - -static long strtol_wrapper(const char *nptr, int base, bool *error) -{ - long val; - char *endptr; - - errno= 0; /* To distinguish success/failure after call */ - val= strtol(nptr, &endptr, base); - - /* Check for various possible errors */ - - if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) - || (errno != 0 && val == 0)) - { - *error= true; - return EXIT_SUCCESS; - } - - if (endptr == nptr) - { - *error= true; - return EXIT_SUCCESS; - } - - *error= false; - return val; -} - -int main(int argc, char *argv[]) -{ - memcached_st *memc; - memcached_return_t rc; - memcached_server_st *servers; - - int return_code= 0; - - options_parse(argc, argv); - initialize_sockets(); - - memc= memcached_create(NULL); - process_hash_option(memc, opt_hash); - - if (!opt_servers) - { - char *temp; - - if ((temp= getenv("MEMCACHED_SERVERS"))) - { - opt_servers= strdup(temp); - } - else - { - fprintf(stderr, "No Servers provided\n"); - exit(1); - } - } - - if (opt_servers) - servers= memcached_servers_parse(opt_servers); - else - servers= memcached_servers_parse(argv[--argc]); - - memcached_server_push(memc, servers); - memcached_server_list_free(servers); - memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, - (uint64_t)opt_binary); - if (!initialize_sasl(memc, opt_username, opt_passwd)) - { - memcached_free(memc); - return EXIT_FAILURE; - } - - while (optind < argc) - { - struct stat sbuf; - int fd; - char *ptr; - ssize_t read_length; - char *file_buffer_ptr; - - fd= open(argv[optind], O_RDONLY); - if (fd < 0) - { - fprintf(stderr, "memcp: %s: %s\n", argv[optind], strerror(errno)); - optind++; - continue; - } - - (void)fstat(fd, &sbuf); - - ptr= rindex(argv[optind], '/'); - if (ptr) - ptr++; - else - ptr= argv[optind]; - - if (opt_verbose) - { - static const char *opstr[] = { "set", "add", "replace" }; - printf("op: %s\nsource file: %s\nlength: %zu\n" - "key: %s\nflags: %x\nexpires: %llu\n", - opstr[opt_method - OPT_SET], argv[optind], (size_t)sbuf.st_size, - ptr, opt_flags, (unsigned long long)opt_expires); - } - - if ((file_buffer_ptr= (char *)malloc(sizeof(char) * (size_t)sbuf.st_size)) == NULL) - { - fprintf(stderr, "malloc: %s\n", strerror(errno)); - exit(1); - } - - if ((read_length= read(fd, file_buffer_ptr, (size_t)sbuf.st_size)) == -1) - { - fprintf(stderr, "read: %s\n", strerror(errno)); - exit(1); - } - - if (read_length != sbuf.st_size) - { - fprintf(stderr, "Failure reading from file\n"); - exit(1); - } - - if (opt_method == OPT_ADD) - rc= memcached_add(memc, ptr, strlen(ptr), - file_buffer_ptr, (size_t)sbuf.st_size, - opt_expires, opt_flags); - else if (opt_method == OPT_REPLACE) - rc= memcached_replace(memc, ptr, strlen(ptr), - file_buffer_ptr, (size_t)sbuf.st_size, - opt_expires, opt_flags); - else - rc= memcached_set(memc, ptr, strlen(ptr), - file_buffer_ptr, (size_t)sbuf.st_size, - opt_expires, opt_flags); - - if (rc != MEMCACHED_SUCCESS) - { - fprintf(stderr, "memcp: %s: memcache error %s", - ptr, memcached_strerror(memc, rc)); - if (memcached_last_error_errno(memc)) - fprintf(stderr, " system error %s", strerror(memcached_last_error_errno(memc))); - fprintf(stderr, "\n"); - - return_code= -1; - } - - free(file_buffer_ptr); - close(fd); - optind++; - } - - memcached_free(memc); - - if (opt_servers) - free(opt_servers); - if (opt_hash) - free(opt_hash); - shutdown_sasl(); - - return return_code; -} - -static void options_parse(int argc, char *argv[]) -{ - int option_index= 0; - int option_rv; - - memcached_programs_help_st help_options[]= - { - {0}, - }; - - static struct option long_options[]= - { - {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION}, - {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP}, - {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE}, - {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG}, - {(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS}, - {(OPTIONSTRING)"flag", required_argument, NULL, OPT_FLAG}, - {(OPTIONSTRING)"expire", required_argument, NULL, OPT_EXPIRE}, - {(OPTIONSTRING)"set", no_argument, NULL, OPT_SET}, - {(OPTIONSTRING)"add", no_argument, NULL, OPT_ADD}, - {(OPTIONSTRING)"replace", no_argument, NULL, OPT_REPLACE}, - {(OPTIONSTRING)"hash", required_argument, NULL, OPT_HASH}, - {(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY}, - {(OPTIONSTRING)"username", required_argument, NULL, OPT_USERNAME}, - {(OPTIONSTRING)"password", required_argument, NULL, OPT_PASSWD}, - {0, 0, 0, 0}, - }; - - while (1) - { - option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index); - - if (option_rv == -1) break; - - switch (option_rv) - { - case 0: - break; - case OPT_BINARY: - opt_binary = 1; - break; - case OPT_VERBOSE: /* --verbose or -v */ - opt_verbose = OPT_VERBOSE; - break; - case OPT_DEBUG: /* --debug or -d */ - opt_verbose = OPT_DEBUG; - break; - case OPT_VERSION: /* --version or -V */ - version_command(PROGRAM_NAME); - break; - case OPT_HELP: /* --help or -h */ - help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options); - break; - case OPT_SERVERS: /* --servers or -s */ - opt_servers= strdup(optarg); - break; - case OPT_FLAG: /* --flag */ - { - bool strtol_error; - opt_flags= (uint32_t)strtol_wrapper(optarg, 16, &strtol_error); - if (strtol_error == true) - { - fprintf(stderr, "Bad value passed via --flag\n"); - exit(1); - } - break; - } - case OPT_EXPIRE: /* --expire */ - { - bool strtol_error; - opt_expires= (time_t)strtol_wrapper(optarg, 16, &strtol_error); - if (strtol_error == true) - { - fprintf(stderr, "Bad value passed via --flag\n"); - exit(1); - } - } - case OPT_SET: - opt_method= OPT_SET; - break; - case OPT_REPLACE: - opt_method= OPT_REPLACE; - break; - case OPT_ADD: - opt_method= OPT_ADD; - break; - case OPT_HASH: - opt_hash= strdup(optarg); - break; - case OPT_USERNAME: - opt_username= optarg; - break; - case OPT_PASSWD: - opt_passwd= optarg; - break; - case '?': - /* getopt_long already printed an error message. */ - exit(1); - default: - abort(); - } - } -} diff --git a/clients/memcp.cc b/clients/memcp.cc new file mode 100644 index 00000000..3869242b --- /dev/null +++ b/clients/memcp.cc @@ -0,0 +1,317 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include + +#include "client_options.h" +#include "utilities.h" + +#define PROGRAM_NAME "memcp" +#define PROGRAM_DESCRIPTION "Copy a set of files to a memcached cluster." + +/* Prototypes */ +static void options_parse(int argc, char *argv[]); + +static int opt_binary=0; +static int opt_verbose= 0; +static char *opt_servers= NULL; +static char *opt_hash= NULL; +static int opt_method= OPT_SET; +static uint32_t opt_flags= 0; +static time_t opt_expires= 0; +static char *opt_username; +static char *opt_passwd; + +static long strtol_wrapper(const char *nptr, int base, bool *error) +{ + long val; + char *endptr; + + errno= 0; /* To distinguish success/failure after call */ + val= strtol(nptr, &endptr, base); + + /* Check for various possible errors */ + + if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) + || (errno != 0 && val == 0)) + { + *error= true; + return EXIT_SUCCESS; + } + + if (endptr == nptr) + { + *error= true; + return EXIT_SUCCESS; + } + + *error= false; + return val; +} + +int main(int argc, char *argv[]) +{ + memcached_st *memc; + memcached_return_t rc; + memcached_server_st *servers; + + int return_code= 0; + + options_parse(argc, argv); + initialize_sockets(); + + memc= memcached_create(NULL); + process_hash_option(memc, opt_hash); + + if (!opt_servers) + { + char *temp; + + if ((temp= getenv("MEMCACHED_SERVERS"))) + { + opt_servers= strdup(temp); + } + else + { + fprintf(stderr, "No Servers provided\n"); + exit(1); + } + } + + if (opt_servers) + servers= memcached_servers_parse(opt_servers); + else + servers= memcached_servers_parse(argv[--argc]); + + memcached_server_push(memc, servers); + memcached_server_list_free(servers); + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, + (uint64_t)opt_binary); + if (!initialize_sasl(memc, opt_username, opt_passwd)) + { + memcached_free(memc); + return EXIT_FAILURE; + } + + while (optind < argc) + { + struct stat sbuf; + int fd; + char *ptr; + ssize_t read_length; + char *file_buffer_ptr; + + fd= open(argv[optind], O_RDONLY); + if (fd < 0) + { + fprintf(stderr, "memcp: %s: %s\n", argv[optind], strerror(errno)); + optind++; + continue; + } + + (void)fstat(fd, &sbuf); + + ptr= rindex(argv[optind], '/'); + if (ptr) + ptr++; + else + ptr= argv[optind]; + + if (opt_verbose) + { + static const char *opstr[] = { "set", "add", "replace" }; + printf("op: %s\nsource file: %s\nlength: %lu\n" + "key: %s\nflags: %x\nexpires: %lu\n", + opstr[opt_method - OPT_SET], argv[optind], (unsigned long)sbuf.st_size, + ptr, opt_flags, (unsigned long)opt_expires); + } + + if ((file_buffer_ptr= (char *)malloc(sizeof(char) * (size_t)sbuf.st_size)) == NULL) + { + fprintf(stderr, "malloc: %s\n", strerror(errno)); + exit(1); + } + + if ((read_length= read(fd, file_buffer_ptr, (size_t)sbuf.st_size)) == -1) + { + fprintf(stderr, "read: %s\n", strerror(errno)); + exit(1); + } + + if (read_length != sbuf.st_size) + { + fprintf(stderr, "Failure reading from file\n"); + exit(1); + } + + if (opt_method == OPT_ADD) + rc= memcached_add(memc, ptr, strlen(ptr), + file_buffer_ptr, (size_t)sbuf.st_size, + opt_expires, opt_flags); + else if (opt_method == OPT_REPLACE) + rc= memcached_replace(memc, ptr, strlen(ptr), + file_buffer_ptr, (size_t)sbuf.st_size, + opt_expires, opt_flags); + else + rc= memcached_set(memc, ptr, strlen(ptr), + file_buffer_ptr, (size_t)sbuf.st_size, + opt_expires, opt_flags); + + if (rc != MEMCACHED_SUCCESS) + { + fprintf(stderr, "memcp: %s: memcache error %s", + ptr, memcached_strerror(memc, rc)); + if (memcached_last_error_errno(memc)) + fprintf(stderr, " system error %s", strerror(memcached_last_error_errno(memc))); + fprintf(stderr, "\n"); + + return_code= -1; + } + + free(file_buffer_ptr); + close(fd); + optind++; + } + + memcached_free(memc); + + if (opt_servers) + free(opt_servers); + if (opt_hash) + free(opt_hash); + shutdown_sasl(); + + return return_code; +} + +static void options_parse(int argc, char *argv[]) +{ + int option_index= 0; + int option_rv; + + memcached_programs_help_st help_options[]= + { + {0}, + }; + + static struct option long_options[]= + { + {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION}, + {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP}, + {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE}, + {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG}, + {(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS}, + {(OPTIONSTRING)"flag", required_argument, NULL, OPT_FLAG}, + {(OPTIONSTRING)"expire", required_argument, NULL, OPT_EXPIRE}, + {(OPTIONSTRING)"set", no_argument, NULL, OPT_SET}, + {(OPTIONSTRING)"add", no_argument, NULL, OPT_ADD}, + {(OPTIONSTRING)"replace", no_argument, NULL, OPT_REPLACE}, + {(OPTIONSTRING)"hash", required_argument, NULL, OPT_HASH}, + {(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY}, + {(OPTIONSTRING)"username", required_argument, NULL, OPT_USERNAME}, + {(OPTIONSTRING)"password", required_argument, NULL, OPT_PASSWD}, + {0, 0, 0, 0}, + }; + + while (1) + { + option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index); + + if (option_rv == -1) break; + + switch (option_rv) + { + case 0: + break; + case OPT_BINARY: + opt_binary = 1; + break; + case OPT_VERBOSE: /* --verbose or -v */ + opt_verbose = OPT_VERBOSE; + break; + case OPT_DEBUG: /* --debug or -d */ + opt_verbose = OPT_DEBUG; + break; + case OPT_VERSION: /* --version or -V */ + version_command(PROGRAM_NAME); + break; + case OPT_HELP: /* --help or -h */ + help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options); + break; + case OPT_SERVERS: /* --servers or -s */ + opt_servers= strdup(optarg); + break; + case OPT_FLAG: /* --flag */ + { + bool strtol_error; + opt_flags= (uint32_t)strtol_wrapper(optarg, 16, &strtol_error); + if (strtol_error == true) + { + fprintf(stderr, "Bad value passed via --flag\n"); + exit(1); + } + break; + } + case OPT_EXPIRE: /* --expire */ + { + bool strtol_error; + opt_expires= (time_t)strtol_wrapper(optarg, 16, &strtol_error); + if (strtol_error == true) + { + fprintf(stderr, "Bad value passed via --flag\n"); + exit(1); + } + } + case OPT_SET: + opt_method= OPT_SET; + break; + case OPT_REPLACE: + opt_method= OPT_REPLACE; + break; + case OPT_ADD: + opt_method= OPT_ADD; + break; + case OPT_HASH: + opt_hash= strdup(optarg); + break; + case OPT_USERNAME: + opt_username= optarg; + break; + case OPT_PASSWD: + opt_passwd= optarg; + break; + case '?': + /* getopt_long already printed an error message. */ + exit(1); + default: + abort(); + } + } +} diff --git a/clients/memdump.c b/clients/memdump.c deleted file mode 100644 index 0e81dad4..00000000 --- a/clients/memdump.c +++ /dev/null @@ -1,183 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: - * - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "client_options.h" -#include "utilities.h" - -#define PROGRAM_NAME "memdump" -#define PROGRAM_DESCRIPTION "Dump all values from one or many servers." - -/* Prototypes */ -static void options_parse(int argc, char *argv[]); - -static int opt_binary=0; -static int opt_verbose= 0; -static char *opt_servers= NULL; -static char *opt_hash= NULL; -static char *opt_username; -static char *opt_passwd; - -/* Print the keys and counter how many were found */ -static memcached_return_t key_printer(const memcached_st *ptr, - const char *key, size_t key_length, - void *context) -{ - (void)ptr;(void)context; - printf("%.*s\n", (uint32_t)key_length, key); - - return MEMCACHED_SUCCESS; -} - -int main(int argc, char *argv[]) -{ - memcached_st *memc; - memcached_return_t rc; - memcached_server_st *servers; - memcached_dump_fn callbacks[1]; - - callbacks[0]= &key_printer; - - options_parse(argc, argv); - - memc= memcached_create(NULL); - process_hash_option(memc, opt_hash); - - if (!opt_servers) - { - char *temp; - - if ((temp= getenv("MEMCACHED_SERVERS"))) - opt_servers= strdup(temp); - else - { - fprintf(stderr, "No Servers provided\n"); - exit(1); - } - } - - if (opt_servers) - servers= memcached_servers_parse(opt_servers); - else - servers= memcached_servers_parse(argv[--argc]); - - memcached_server_push(memc, servers); - memcached_server_list_free(servers); - memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, - (uint64_t)opt_binary); - if (!initialize_sasl(memc, opt_username, opt_passwd)) - { - memcached_free(memc); - return EXIT_FAILURE; - } - - rc= memcached_dump(memc, callbacks, NULL, 1); - - if (rc != MEMCACHED_SUCCESS) - { - fprintf(stderr, "memdump: memcache error %s", memcached_strerror(memc, rc)); - if (memcached_last_error_errno(memc)) - fprintf(stderr, " system error %s", strerror(memcached_last_error_errno(memc))); - fprintf(stderr, "\n"); - } - - memcached_free(memc); - - if (opt_servers) - free(opt_servers); - if (opt_hash) - free(opt_hash); - - shutdown_sasl(); - - return EXIT_SUCCESS; -} - -static void options_parse(int argc, char *argv[]) -{ - int option_index= 0; - int option_rv; - - static struct option long_options[]= - { - {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION}, - {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP}, - {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE}, - {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG}, - {(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS}, - {(OPTIONSTRING)"hash", required_argument, NULL, OPT_HASH}, - {(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY}, - {(OPTIONSTRING)"username", required_argument, NULL, OPT_USERNAME}, - {(OPTIONSTRING)"password", required_argument, NULL, OPT_PASSWD}, - {0, 0, 0, 0} - }; - - while (1) - { - option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index); - - if (option_rv == -1) break; - - switch (option_rv) - { - case 0: - break; - case OPT_BINARY: - opt_binary = 1; - break; - case OPT_VERBOSE: /* --verbose or -v */ - opt_verbose = OPT_VERBOSE; - break; - case OPT_DEBUG: /* --debug or -d */ - opt_verbose = OPT_DEBUG; - break; - case OPT_VERSION: /* --version or -V */ - version_command(PROGRAM_NAME); - break; - case OPT_HELP: /* --help or -h */ - help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, NULL); - break; - case OPT_SERVERS: /* --servers or -s */ - opt_servers= strdup(optarg); - break; - case OPT_HASH: - opt_hash= strdup(optarg); - break; - case OPT_USERNAME: - opt_username= optarg; - break; - case OPT_PASSWD: - opt_passwd= optarg; - break; - case '?': - /* getopt_long already printed an error message. */ - exit(1); - default: - abort(); - } - } -} diff --git a/clients/memdump.cc b/clients/memdump.cc new file mode 100644 index 00000000..0e81dad4 --- /dev/null +++ b/clients/memdump.cc @@ -0,0 +1,183 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "client_options.h" +#include "utilities.h" + +#define PROGRAM_NAME "memdump" +#define PROGRAM_DESCRIPTION "Dump all values from one or many servers." + +/* Prototypes */ +static void options_parse(int argc, char *argv[]); + +static int opt_binary=0; +static int opt_verbose= 0; +static char *opt_servers= NULL; +static char *opt_hash= NULL; +static char *opt_username; +static char *opt_passwd; + +/* Print the keys and counter how many were found */ +static memcached_return_t key_printer(const memcached_st *ptr, + const char *key, size_t key_length, + void *context) +{ + (void)ptr;(void)context; + printf("%.*s\n", (uint32_t)key_length, key); + + return MEMCACHED_SUCCESS; +} + +int main(int argc, char *argv[]) +{ + memcached_st *memc; + memcached_return_t rc; + memcached_server_st *servers; + memcached_dump_fn callbacks[1]; + + callbacks[0]= &key_printer; + + options_parse(argc, argv); + + memc= memcached_create(NULL); + process_hash_option(memc, opt_hash); + + if (!opt_servers) + { + char *temp; + + if ((temp= getenv("MEMCACHED_SERVERS"))) + opt_servers= strdup(temp); + else + { + fprintf(stderr, "No Servers provided\n"); + exit(1); + } + } + + if (opt_servers) + servers= memcached_servers_parse(opt_servers); + else + servers= memcached_servers_parse(argv[--argc]); + + memcached_server_push(memc, servers); + memcached_server_list_free(servers); + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, + (uint64_t)opt_binary); + if (!initialize_sasl(memc, opt_username, opt_passwd)) + { + memcached_free(memc); + return EXIT_FAILURE; + } + + rc= memcached_dump(memc, callbacks, NULL, 1); + + if (rc != MEMCACHED_SUCCESS) + { + fprintf(stderr, "memdump: memcache error %s", memcached_strerror(memc, rc)); + if (memcached_last_error_errno(memc)) + fprintf(stderr, " system error %s", strerror(memcached_last_error_errno(memc))); + fprintf(stderr, "\n"); + } + + memcached_free(memc); + + if (opt_servers) + free(opt_servers); + if (opt_hash) + free(opt_hash); + + shutdown_sasl(); + + return EXIT_SUCCESS; +} + +static void options_parse(int argc, char *argv[]) +{ + int option_index= 0; + int option_rv; + + static struct option long_options[]= + { + {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION}, + {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP}, + {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE}, + {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG}, + {(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS}, + {(OPTIONSTRING)"hash", required_argument, NULL, OPT_HASH}, + {(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY}, + {(OPTIONSTRING)"username", required_argument, NULL, OPT_USERNAME}, + {(OPTIONSTRING)"password", required_argument, NULL, OPT_PASSWD}, + {0, 0, 0, 0} + }; + + while (1) + { + option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index); + + if (option_rv == -1) break; + + switch (option_rv) + { + case 0: + break; + case OPT_BINARY: + opt_binary = 1; + break; + case OPT_VERBOSE: /* --verbose or -v */ + opt_verbose = OPT_VERBOSE; + break; + case OPT_DEBUG: /* --debug or -d */ + opt_verbose = OPT_DEBUG; + break; + case OPT_VERSION: /* --version or -V */ + version_command(PROGRAM_NAME); + break; + case OPT_HELP: /* --help or -h */ + help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, NULL); + break; + case OPT_SERVERS: /* --servers or -s */ + opt_servers= strdup(optarg); + break; + case OPT_HASH: + opt_hash= strdup(optarg); + break; + case OPT_USERNAME: + opt_username= optarg; + break; + case OPT_PASSWD: + opt_passwd= optarg; + break; + case '?': + /* getopt_long already printed an error message. */ + exit(1); + default: + abort(); + } + } +} diff --git a/clients/memerror.c b/clients/memerror.c deleted file mode 100644 index c30dd2e9..00000000 --- a/clients/memerror.c +++ /dev/null @@ -1,102 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: - * - */ -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "utilities.h" - -#define PROGRAM_NAME "memerror" -#define PROGRAM_DESCRIPTION "Translate a memcached errror code into a string." - - -/* Prototypes */ -void options_parse(int argc, char *argv[]); - -static int opt_verbose= 0; - -int main(int argc, char *argv[]) -{ - unsigned long value; - options_parse(argc, argv); - - if (argc != 2) - return EXIT_FAILURE; - - value= strtoul(argv[1], (char **) NULL, 10); - - if (value < MEMCACHED_MAXIMUM_RETURN) - { - printf("%s\n", memcached_strerror(NULL, (memcached_return_t)value)); - } - else - { - fprintf(stderr, "Unknown Error Code\n"); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} - - -void options_parse(int argc, char *argv[]) -{ - int option_index= 0; - int option_rv; - - memcached_programs_help_st help_options[]= - { - {0}, - }; - - static struct option long_options[]= - { - {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION}, - {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP}, - {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE}, - {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG}, - {0, 0, 0, 0}, - }; - - while (1) - { - option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index); - if (option_rv == -1) break; - switch (option_rv) - { - case 0: - break; - case OPT_VERBOSE: /* --verbose or -v */ - opt_verbose = OPT_VERBOSE; - break; - case OPT_DEBUG: /* --debug or -d */ - opt_verbose = OPT_DEBUG; - break; - case OPT_VERSION: /* --version or -V */ - version_command(PROGRAM_NAME); - break; - case OPT_HELP: /* --help or -h */ - help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options); - break; - case '?': - /* getopt_long already printed an error message. */ - exit(1); - default: - abort(); - } - } -} diff --git a/clients/memerror.cc b/clients/memerror.cc new file mode 100644 index 00000000..c30dd2e9 --- /dev/null +++ b/clients/memerror.cc @@ -0,0 +1,102 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: + * + */ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "utilities.h" + +#define PROGRAM_NAME "memerror" +#define PROGRAM_DESCRIPTION "Translate a memcached errror code into a string." + + +/* Prototypes */ +void options_parse(int argc, char *argv[]); + +static int opt_verbose= 0; + +int main(int argc, char *argv[]) +{ + unsigned long value; + options_parse(argc, argv); + + if (argc != 2) + return EXIT_FAILURE; + + value= strtoul(argv[1], (char **) NULL, 10); + + if (value < MEMCACHED_MAXIMUM_RETURN) + { + printf("%s\n", memcached_strerror(NULL, (memcached_return_t)value)); + } + else + { + fprintf(stderr, "Unknown Error Code\n"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + + +void options_parse(int argc, char *argv[]) +{ + int option_index= 0; + int option_rv; + + memcached_programs_help_st help_options[]= + { + {0}, + }; + + static struct option long_options[]= + { + {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION}, + {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP}, + {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE}, + {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG}, + {0, 0, 0, 0}, + }; + + while (1) + { + option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index); + if (option_rv == -1) break; + switch (option_rv) + { + case 0: + break; + case OPT_VERBOSE: /* --verbose or -v */ + opt_verbose = OPT_VERBOSE; + break; + case OPT_DEBUG: /* --debug or -d */ + opt_verbose = OPT_DEBUG; + break; + case OPT_VERSION: /* --version or -V */ + version_command(PROGRAM_NAME); + break; + case OPT_HELP: /* --help or -h */ + help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options); + break; + case '?': + /* getopt_long already printed an error message. */ + exit(1); + default: + abort(); + } + } +} diff --git a/clients/memflush.c b/clients/memflush.c deleted file mode 100644 index 848bc1e7..00000000 --- a/clients/memflush.c +++ /dev/null @@ -1,154 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: - * - */ -#include "config.h" - -#include -#include -#include -#include -#include -#include "client_options.h" -#include "utilities.h" - -static int opt_binary= 0; -static int opt_verbose= 0; -static time_t opt_expire= 0; -static char *opt_servers= NULL; -static char *opt_username; -static char *opt_passwd; - -#define PROGRAM_NAME "memflush" -#define PROGRAM_DESCRIPTION "Erase all data in a server of memcached servers." - -/* Prototypes */ -void options_parse(int argc, char *argv[]); - -int main(int argc, char *argv[]) -{ - memcached_st *memc; - memcached_return_t rc; - memcached_server_st *servers; - - options_parse(argc, argv); - - if (!opt_servers) - { - char *temp; - - if ((temp= getenv("MEMCACHED_SERVERS"))) - opt_servers= strdup(temp); - else - { - fprintf(stderr, "No Servers provided\n"); - exit(1); - } - } - - memc= memcached_create(NULL); - - servers= memcached_servers_parse(opt_servers); - memcached_server_push(memc, servers); - memcached_server_list_free(servers); - memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, - (uint64_t) opt_binary); - - if (!initialize_sasl(memc, opt_username, opt_passwd)) - { - memcached_free(memc); - return EXIT_FAILURE; - } - - rc = memcached_flush(memc, opt_expire); - if (rc != MEMCACHED_SUCCESS) - { - fprintf(stderr, "memflush: memcache error %s", - memcached_strerror(memc, rc)); - if (memcached_last_error_errno(memc)) - fprintf(stderr, " system error %s", strerror(memcached_last_error_errno(memc))); - fprintf(stderr, "\n"); - } - - memcached_free(memc); - - free(opt_servers); - - shutdown_sasl(); - - return EXIT_SUCCESS; -} - - -void options_parse(int argc, char *argv[]) -{ - memcached_programs_help_st help_options[]= - { - {0}, - }; - - static struct option long_options[]= - { - {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION}, - {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP}, - {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE}, - {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG}, - {(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS}, - {(OPTIONSTRING)"expire", required_argument, NULL, OPT_EXPIRE}, - {(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY}, - {(OPTIONSTRING)"username", required_argument, NULL, OPT_USERNAME}, - {(OPTIONSTRING)"password", required_argument, NULL, OPT_PASSWD}, - {0, 0, 0, 0}, - }; - int option_index= 0; - int option_rv; - - while (1) - { - option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index); - if (option_rv == -1) break; - switch (option_rv) - { - case 0: - break; - case OPT_BINARY: - opt_binary = 1; - break; - case OPT_VERBOSE: /* --verbose or -v */ - opt_verbose = OPT_VERBOSE; - break; - case OPT_DEBUG: /* --debug or -d */ - opt_verbose = OPT_DEBUG; - break; - case OPT_VERSION: /* --version or -V */ - version_command(PROGRAM_NAME); - break; - case OPT_HELP: /* --help or -h */ - help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options); - break; - case OPT_SERVERS: /* --servers or -s */ - opt_servers= strdup(optarg); - break; - case OPT_EXPIRE: /* --expire */ - opt_expire= (time_t)strtoll(optarg, (char **)NULL, 10); - break; - case OPT_USERNAME: - opt_username= optarg; - break; - case OPT_PASSWD: - opt_passwd= optarg; - break; - case '?': - /* getopt_long already printed an error message. */ - exit(1); - default: - abort(); - } - } -} diff --git a/clients/memflush.cc b/clients/memflush.cc new file mode 100644 index 00000000..848bc1e7 --- /dev/null +++ b/clients/memflush.cc @@ -0,0 +1,154 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: + * + */ +#include "config.h" + +#include +#include +#include +#include +#include +#include "client_options.h" +#include "utilities.h" + +static int opt_binary= 0; +static int opt_verbose= 0; +static time_t opt_expire= 0; +static char *opt_servers= NULL; +static char *opt_username; +static char *opt_passwd; + +#define PROGRAM_NAME "memflush" +#define PROGRAM_DESCRIPTION "Erase all data in a server of memcached servers." + +/* Prototypes */ +void options_parse(int argc, char *argv[]); + +int main(int argc, char *argv[]) +{ + memcached_st *memc; + memcached_return_t rc; + memcached_server_st *servers; + + options_parse(argc, argv); + + if (!opt_servers) + { + char *temp; + + if ((temp= getenv("MEMCACHED_SERVERS"))) + opt_servers= strdup(temp); + else + { + fprintf(stderr, "No Servers provided\n"); + exit(1); + } + } + + memc= memcached_create(NULL); + + servers= memcached_servers_parse(opt_servers); + memcached_server_push(memc, servers); + memcached_server_list_free(servers); + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, + (uint64_t) opt_binary); + + if (!initialize_sasl(memc, opt_username, opt_passwd)) + { + memcached_free(memc); + return EXIT_FAILURE; + } + + rc = memcached_flush(memc, opt_expire); + if (rc != MEMCACHED_SUCCESS) + { + fprintf(stderr, "memflush: memcache error %s", + memcached_strerror(memc, rc)); + if (memcached_last_error_errno(memc)) + fprintf(stderr, " system error %s", strerror(memcached_last_error_errno(memc))); + fprintf(stderr, "\n"); + } + + memcached_free(memc); + + free(opt_servers); + + shutdown_sasl(); + + return EXIT_SUCCESS; +} + + +void options_parse(int argc, char *argv[]) +{ + memcached_programs_help_st help_options[]= + { + {0}, + }; + + static struct option long_options[]= + { + {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION}, + {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP}, + {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE}, + {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG}, + {(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS}, + {(OPTIONSTRING)"expire", required_argument, NULL, OPT_EXPIRE}, + {(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY}, + {(OPTIONSTRING)"username", required_argument, NULL, OPT_USERNAME}, + {(OPTIONSTRING)"password", required_argument, NULL, OPT_PASSWD}, + {0, 0, 0, 0}, + }; + int option_index= 0; + int option_rv; + + while (1) + { + option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index); + if (option_rv == -1) break; + switch (option_rv) + { + case 0: + break; + case OPT_BINARY: + opt_binary = 1; + break; + case OPT_VERBOSE: /* --verbose or -v */ + opt_verbose = OPT_VERBOSE; + break; + case OPT_DEBUG: /* --debug or -d */ + opt_verbose = OPT_DEBUG; + break; + case OPT_VERSION: /* --version or -V */ + version_command(PROGRAM_NAME); + break; + case OPT_HELP: /* --help or -h */ + help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options); + break; + case OPT_SERVERS: /* --servers or -s */ + opt_servers= strdup(optarg); + break; + case OPT_EXPIRE: /* --expire */ + opt_expire= (time_t)strtoll(optarg, (char **)NULL, 10); + break; + case OPT_USERNAME: + opt_username= optarg; + break; + case OPT_PASSWD: + opt_passwd= optarg; + break; + case '?': + /* getopt_long already printed an error message. */ + exit(1); + default: + abort(); + } + } +} diff --git a/clients/memrm.c b/clients/memrm.c deleted file mode 100644 index d4d93c2e..00000000 --- a/clients/memrm.c +++ /dev/null @@ -1,177 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: - * - */ -#include "config.h" - -#include -#include -#include -#include -#include -#include "client_options.h" -#include "utilities.h" - -static int opt_binary= 0; -static int opt_verbose= 0; -static time_t opt_expire= 0; -static char *opt_servers= NULL; -static char *opt_hash= NULL; -static char *opt_username; -static char *opt_passwd; - -#define PROGRAM_NAME "memrm" -#define PROGRAM_DESCRIPTION "Erase a key or set of keys from a memcached cluster." - -/* Prototypes */ -static void options_parse(int argc, char *argv[]); - -int main(int argc, char *argv[]) -{ - memcached_st *memc; - memcached_return_t rc; - memcached_server_st *servers; - - int return_code= 0; - - options_parse(argc, argv); - initialize_sockets(); - - if (!opt_servers) - { - char *temp; - - if ((temp= getenv("MEMCACHED_SERVERS"))) - opt_servers= strdup(temp); - else - { - fprintf(stderr, "No Servers provided\n"); - exit(1); - } - } - - memc= memcached_create(NULL); - process_hash_option(memc, opt_hash); - - servers= memcached_servers_parse(opt_servers); - memcached_server_push(memc, servers); - memcached_server_list_free(servers); - memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, - (uint64_t) opt_binary); - - if (!initialize_sasl(memc, opt_username, opt_passwd)) - { - memcached_free(memc); - return EXIT_FAILURE; - } - - while (optind < argc) - { - if (opt_verbose) - printf("key: %s\nexpires: %llu\n", argv[optind], (unsigned long long)opt_expire); - rc = memcached_delete(memc, argv[optind], strlen(argv[optind]), opt_expire); - - if (rc != MEMCACHED_SUCCESS) - { - fprintf(stderr, "memrm: %s: memcache error %s", - argv[optind], memcached_strerror(memc, rc)); - if (memcached_last_error_errno(memc)) - fprintf(stderr, " system error %s", strerror(memcached_last_error_errno(memc))); - fprintf(stderr, "\n"); - - return_code= -1; - } - - optind++; - } - - memcached_free(memc); - - if (opt_servers) - free(opt_servers); - - if (opt_hash) - free(opt_hash); - - shutdown_sasl(); - - return return_code; -} - - -static void options_parse(int argc, char *argv[]) -{ - memcached_programs_help_st help_options[]= - { - {0}, - }; - - static struct option long_options[]= - { - {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION}, - {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP}, - {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE}, - {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG}, - {(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS}, - {(OPTIONSTRING)"expire", required_argument, NULL, OPT_EXPIRE}, - {(OPTIONSTRING)"hash", required_argument, NULL, OPT_HASH}, - {(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY}, - {(OPTIONSTRING)"username", required_argument, NULL, OPT_USERNAME}, - {(OPTIONSTRING)"password", required_argument, NULL, OPT_PASSWD}, - {0, 0, 0, 0}, - }; - int option_index= 0; - int option_rv; - - while (1) - { - option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index); - if (option_rv == -1) break; - switch (option_rv) - { - case 0: - break; - case OPT_BINARY: - opt_binary = 1; - break; - case OPT_VERBOSE: /* --verbose or -v */ - opt_verbose = OPT_VERBOSE; - break; - case OPT_DEBUG: /* --debug or -d */ - opt_verbose = OPT_DEBUG; - break; - case OPT_VERSION: /* --version or -V */ - version_command(PROGRAM_NAME); - break; - case OPT_HELP: /* --help or -h */ - help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options); - break; - case OPT_SERVERS: /* --servers or -s */ - opt_servers= strdup(optarg); - break; - case OPT_EXPIRE: /* --expire */ - opt_expire= (time_t)strtoll(optarg, (char **)NULL, 10); - break; - case OPT_HASH: - opt_hash= strdup(optarg); - break; - case OPT_USERNAME: - opt_username= optarg; - break; - case OPT_PASSWD: - opt_passwd= optarg; - break; - case '?': - /* getopt_long already printed an error message. */ - exit(1); - default: - abort(); - } - } -} diff --git a/clients/memrm.cc b/clients/memrm.cc new file mode 100644 index 00000000..d4d93c2e --- /dev/null +++ b/clients/memrm.cc @@ -0,0 +1,177 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: + * + */ +#include "config.h" + +#include +#include +#include +#include +#include +#include "client_options.h" +#include "utilities.h" + +static int opt_binary= 0; +static int opt_verbose= 0; +static time_t opt_expire= 0; +static char *opt_servers= NULL; +static char *opt_hash= NULL; +static char *opt_username; +static char *opt_passwd; + +#define PROGRAM_NAME "memrm" +#define PROGRAM_DESCRIPTION "Erase a key or set of keys from a memcached cluster." + +/* Prototypes */ +static void options_parse(int argc, char *argv[]); + +int main(int argc, char *argv[]) +{ + memcached_st *memc; + memcached_return_t rc; + memcached_server_st *servers; + + int return_code= 0; + + options_parse(argc, argv); + initialize_sockets(); + + if (!opt_servers) + { + char *temp; + + if ((temp= getenv("MEMCACHED_SERVERS"))) + opt_servers= strdup(temp); + else + { + fprintf(stderr, "No Servers provided\n"); + exit(1); + } + } + + memc= memcached_create(NULL); + process_hash_option(memc, opt_hash); + + servers= memcached_servers_parse(opt_servers); + memcached_server_push(memc, servers); + memcached_server_list_free(servers); + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, + (uint64_t) opt_binary); + + if (!initialize_sasl(memc, opt_username, opt_passwd)) + { + memcached_free(memc); + return EXIT_FAILURE; + } + + while (optind < argc) + { + if (opt_verbose) + printf("key: %s\nexpires: %llu\n", argv[optind], (unsigned long long)opt_expire); + rc = memcached_delete(memc, argv[optind], strlen(argv[optind]), opt_expire); + + if (rc != MEMCACHED_SUCCESS) + { + fprintf(stderr, "memrm: %s: memcache error %s", + argv[optind], memcached_strerror(memc, rc)); + if (memcached_last_error_errno(memc)) + fprintf(stderr, " system error %s", strerror(memcached_last_error_errno(memc))); + fprintf(stderr, "\n"); + + return_code= -1; + } + + optind++; + } + + memcached_free(memc); + + if (opt_servers) + free(opt_servers); + + if (opt_hash) + free(opt_hash); + + shutdown_sasl(); + + return return_code; +} + + +static void options_parse(int argc, char *argv[]) +{ + memcached_programs_help_st help_options[]= + { + {0}, + }; + + static struct option long_options[]= + { + {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION}, + {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP}, + {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE}, + {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG}, + {(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS}, + {(OPTIONSTRING)"expire", required_argument, NULL, OPT_EXPIRE}, + {(OPTIONSTRING)"hash", required_argument, NULL, OPT_HASH}, + {(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY}, + {(OPTIONSTRING)"username", required_argument, NULL, OPT_USERNAME}, + {(OPTIONSTRING)"password", required_argument, NULL, OPT_PASSWD}, + {0, 0, 0, 0}, + }; + int option_index= 0; + int option_rv; + + while (1) + { + option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index); + if (option_rv == -1) break; + switch (option_rv) + { + case 0: + break; + case OPT_BINARY: + opt_binary = 1; + break; + case OPT_VERBOSE: /* --verbose or -v */ + opt_verbose = OPT_VERBOSE; + break; + case OPT_DEBUG: /* --debug or -d */ + opt_verbose = OPT_DEBUG; + break; + case OPT_VERSION: /* --version or -V */ + version_command(PROGRAM_NAME); + break; + case OPT_HELP: /* --help or -h */ + help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options); + break; + case OPT_SERVERS: /* --servers or -s */ + opt_servers= strdup(optarg); + break; + case OPT_EXPIRE: /* --expire */ + opt_expire= (time_t)strtoll(optarg, (char **)NULL, 10); + break; + case OPT_HASH: + opt_hash= strdup(optarg); + break; + case OPT_USERNAME: + opt_username= optarg; + break; + case OPT_PASSWD: + opt_passwd= optarg; + break; + case '?': + /* getopt_long already printed an error message. */ + exit(1); + default: + abort(); + } + } +} diff --git a/clients/memslap.c b/clients/memslap.c deleted file mode 100644 index 0d77fd6e..00000000 --- a/clients/memslap.c +++ /dev/null @@ -1,495 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * Copyright (C) 2006-2009 Brian Aker 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. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * 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. - * - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "client_options.h" -#include "utilities.h" -#include "generator.h" -#include "execute.h" - -#define DEFAULT_INITIAL_LOAD 10000 -#define DEFAULT_EXECUTE_NUMBER 10000 -#define DEFAULT_CONCURRENCY 1 - -#define PROGRAM_NAME "memslap" -#define PROGRAM_DESCRIPTION "Generates a load against a memcached custer of servers." - -/* Global Thread counter */ -volatile unsigned int thread_counter; -pthread_mutex_t counter_mutex; -pthread_cond_t count_threshhold; -volatile unsigned int master_wakeup; -pthread_mutex_t sleeper_mutex; -pthread_cond_t sleep_threshhold; - -void *run_task(void *p); - -/* Types */ -typedef struct conclusions_st conclusions_st; -typedef struct thread_context_st thread_context_st; -typedef enum { - SET_TEST, - GET_TEST, - MGET_TEST -} test_type; - -struct thread_context_st { - unsigned int key_count; - pairs_st *initial_pairs; - unsigned int initial_number; - pairs_st *execute_pairs; - unsigned int execute_number; - char **keys; - size_t *key_lengths; - test_type test; - memcached_st *memc; -}; - -struct conclusions_st { - long int load_time; - long int read_time; - unsigned int rows_loaded; - unsigned int rows_read; -}; - -/* Prototypes */ -void options_parse(int argc, char *argv[]); -void conclusions_print(conclusions_st *conclusion); -void scheduler(memcached_server_st *servers, conclusions_st *conclusion); -pairs_st *load_create_data(memcached_st *memc, unsigned int number_of, - unsigned int *actual_loaded); -void flush_all(memcached_st *memc); - -static int opt_binary= 0; -static int opt_verbose= 0; -static int opt_flush= 0; -static int opt_non_blocking_io= 0; -static int opt_tcp_nodelay= 0; -static unsigned int opt_execute_number= 0; -static unsigned int opt_createial_load= 0; -static unsigned int opt_concurrency= 0; -static int opt_displayflag= 0; -static char *opt_servers= NULL; -static int opt_udp_io= 0; -test_type opt_test= SET_TEST; - -int main(int argc, char *argv[]) -{ - conclusions_st conclusion; - memcached_server_st *servers; - - memset(&conclusion, 0, sizeof(conclusions_st)); - - srandom((unsigned int)time(NULL)); - options_parse(argc, argv); - - if (!opt_servers) - { - char *temp; - - if ((temp= getenv("MEMCACHED_SERVERS"))) - opt_servers= strdup(temp); - else - { - fprintf(stderr, "No Servers provided\n"); - exit(1); - } - } - - servers= memcached_servers_parse(opt_servers); - - pthread_mutex_init(&counter_mutex, NULL); - pthread_cond_init(&count_threshhold, NULL); - pthread_mutex_init(&sleeper_mutex, NULL); - pthread_cond_init(&sleep_threshhold, NULL); - - scheduler(servers, &conclusion); - - free(opt_servers); - - (void)pthread_mutex_destroy(&counter_mutex); - (void)pthread_cond_destroy(&count_threshhold); - (void)pthread_mutex_destroy(&sleeper_mutex); - (void)pthread_cond_destroy(&sleep_threshhold); - conclusions_print(&conclusion); - memcached_server_list_free(servers); - - return 0; -} - -void scheduler(memcached_server_st *servers, conclusions_st *conclusion) -{ - unsigned int actual_loaded= 0; /* Fix warning */ - memcached_st *memc; - - struct timeval start_time, end_time; - pthread_t mainthread; /* Thread descriptor */ - pthread_attr_t attr; /* Thread attributes */ - pairs_st *pairs= NULL; - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, - PTHREAD_CREATE_DETACHED); - - memc= memcached_create(NULL); - - /* We need to set udp behavior before adding servers to the client */ - if (opt_udp_io) - { - memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_USE_UDP, - (uint64_t)opt_udp_io); - for (uint32_t x= 0; x < memcached_server_list_count(servers); x++ ) - { - servers[x].type= MEMCACHED_CONNECTION_UDP; - } - } - memcached_server_push(memc, servers); - - memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, - (uint64_t)opt_binary); - - if (opt_flush) - flush_all(memc); - if (opt_createial_load) - pairs= load_create_data(memc, opt_createial_load, &actual_loaded); - - char **keys= calloc(actual_loaded, sizeof(char*)); - size_t *key_lengths= calloc(actual_loaded, sizeof(size_t)); - - if (keys == NULL || key_lengths == NULL) - { - free(keys); - free(key_lengths); - keys= NULL; - key_lengths= NULL; - } - else - { - for (uint32_t x= 0; x < actual_loaded; ++x) - { - keys[x]= pairs[x].key; - key_lengths[x]= pairs[x].key_length; - } - } - - /* We set this after we have loaded */ - { - if (opt_non_blocking_io) - memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NO_BLOCK, 1); - if (opt_tcp_nodelay) - memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, 1); - } - - pthread_mutex_lock(&counter_mutex); - thread_counter= 0; - - pthread_mutex_lock(&sleeper_mutex); - master_wakeup= 1; - pthread_mutex_unlock(&sleeper_mutex); - - for (uint32_t x= 0; x < opt_concurrency; x++) - { - thread_context_st *context; - context= (thread_context_st *)calloc(1, sizeof(thread_context_st)); - - context->memc= memcached_clone(NULL, memc); - context->test= opt_test; - - context->initial_pairs= pairs; - context->initial_number= actual_loaded; - context->keys= keys; - context->key_lengths= key_lengths; - - if (opt_test == SET_TEST) - { - context->execute_pairs= pairs_generate(opt_execute_number, 400); - context->execute_number= opt_execute_number; - } - - /* now you create the thread */ - if (pthread_create(&mainthread, &attr, run_task, - (void *)context) != 0) - { - fprintf(stderr,"Could not create thread\n"); - exit(1); - } - thread_counter++; - } - - pthread_mutex_unlock(&counter_mutex); - pthread_attr_destroy(&attr); - - pthread_mutex_lock(&sleeper_mutex); - master_wakeup= 0; - pthread_mutex_unlock(&sleeper_mutex); - pthread_cond_broadcast(&sleep_threshhold); - - gettimeofday(&start_time, NULL); - /* - We loop until we know that all children have cleaned up. - */ - pthread_mutex_lock(&counter_mutex); - while (thread_counter) - pthread_cond_wait(&count_threshhold, &counter_mutex); - pthread_mutex_unlock(&counter_mutex); - - gettimeofday(&end_time, NULL); - - conclusion->load_time= timedif(end_time, start_time); - conclusion->read_time= timedif(end_time, start_time); - free(keys); - free(key_lengths); - pairs_free(pairs); - memcached_free(memc); -} - -void options_parse(int argc, char *argv[]) -{ - memcached_programs_help_st help_options[]= - { - {0}, - }; - - static struct option long_options[]= - { - {(OPTIONSTRING)"concurrency", required_argument, NULL, OPT_SLAP_CONCURRENCY}, - {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG}, - {(OPTIONSTRING)"execute-number", required_argument, NULL, OPT_SLAP_EXECUTE_NUMBER}, - {(OPTIONSTRING)"flag", no_argument, &opt_displayflag, OPT_FLAG}, - {(OPTIONSTRING)"flush", no_argument, &opt_flush, OPT_FLUSH}, - {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP}, - {(OPTIONSTRING)"initial-load", required_argument, NULL, OPT_SLAP_INITIAL_LOAD}, /* Number to load initially */ - {(OPTIONSTRING)"non-blocking", no_argument, &opt_non_blocking_io, OPT_SLAP_NON_BLOCK}, - {(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS}, - {(OPTIONSTRING)"tcp-nodelay", no_argument, &opt_tcp_nodelay, OPT_SLAP_TCP_NODELAY}, - {(OPTIONSTRING)"test", required_argument, NULL, OPT_SLAP_TEST}, - {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE}, - {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION}, - {(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY}, - {(OPTIONSTRING)"udp", no_argument, NULL, OPT_UDP}, - {0, 0, 0, 0}, - }; - - int option_index= 0; - int option_rv; - - while (1) - { - option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index); - if (option_rv == -1) break; - switch (option_rv) - { - case 0: - break; - case OPT_UDP: - if (opt_test == GET_TEST) - { - fprintf(stderr, "You can not run a get test in UDP mode. UDP mode " - "does not currently support get ops.\n"); - exit(1); - } - opt_udp_io= 1; - break; - case OPT_BINARY: - opt_binary = 1; - break; - case OPT_VERBOSE: /* --verbose or -v */ - opt_verbose = OPT_VERBOSE; - break; - case OPT_DEBUG: /* --debug or -d */ - opt_verbose = OPT_DEBUG; - break; - case OPT_VERSION: /* --version or -V */ - version_command(PROGRAM_NAME); - break; - case OPT_HELP: /* --help or -h */ - help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options); - break; - case OPT_SERVERS: /* --servers or -s */ - opt_servers= strdup(optarg); - break; - case OPT_SLAP_TEST: - if (!strcmp(optarg, "get")) - { - if (opt_udp_io == 1) - { - fprintf(stderr, "You can not run a get test in UDP mode. UDP mode " - "does not currently support get ops.\n"); - exit(1); - } - opt_test= GET_TEST ; - } - else if (!strcmp(optarg, "set")) - opt_test= SET_TEST; - else if (!strcmp(optarg, "mget")) - { - opt_test= MGET_TEST; - } - else - { - fprintf(stderr, "Your test, %s, is not a known test\n", optarg); - exit(1); - } - break; - case OPT_SLAP_CONCURRENCY: - opt_concurrency= (unsigned int)strtoul(optarg, (char **)NULL, 10); - break; - case OPT_SLAP_EXECUTE_NUMBER: - opt_execute_number= (unsigned int)strtoul(optarg, (char **)NULL, 10); - break; - case OPT_SLAP_INITIAL_LOAD: - opt_createial_load= (unsigned int)strtoul(optarg, (char **)NULL, 10); - break; - case '?': - /* getopt_long already printed an error message. */ - exit(1); - default: - abort(); - } - } - - if ((opt_test == GET_TEST || opt_test == MGET_TEST) && opt_createial_load == 0) - opt_createial_load= DEFAULT_INITIAL_LOAD; - - if (opt_execute_number == 0) - opt_execute_number= DEFAULT_EXECUTE_NUMBER; - - if (opt_concurrency == 0) - opt_concurrency= DEFAULT_CONCURRENCY; -} - -void conclusions_print(conclusions_st *conclusion) -{ - printf("\tThreads connecting to servers %u\n", opt_concurrency); -#ifdef NOT_FINISHED - printf("\tLoaded %u rows\n", conclusion->rows_loaded); - printf("\tRead %u rows\n", conclusion->rows_read); -#endif - if (opt_test == SET_TEST) - printf("\tTook %ld.%03ld seconds to load data\n", conclusion->load_time / 1000, - conclusion->load_time % 1000); - else - printf("\tTook %ld.%03ld seconds to read data\n", conclusion->read_time / 1000, - conclusion->read_time % 1000); -} - -void *run_task(void *p) -{ - thread_context_st *context= (thread_context_st *)p; - memcached_st *memc; - - memc= context->memc; - - pthread_mutex_lock(&sleeper_mutex); - while (master_wakeup) - { - pthread_cond_wait(&sleep_threshhold, &sleeper_mutex); - } - pthread_mutex_unlock(&sleeper_mutex); - - /* Do Stuff */ - switch (context->test) - { - case SET_TEST: - assert(context->execute_pairs); - execute_set(memc, context->execute_pairs, context->execute_number); - break; - case GET_TEST: - execute_get(memc, context->initial_pairs, context->initial_number); - break; - case MGET_TEST: - execute_mget(memc, (const char*const*)context->keys, context->key_lengths, - context->initial_number); - break; - default: - WATCHPOINT_ASSERT(context->test); - break; - } - - memcached_free(memc); - - if (context->execute_pairs) - pairs_free(context->execute_pairs); - - free(context); - - pthread_mutex_lock(&counter_mutex); - thread_counter--; - pthread_cond_signal(&count_threshhold); - pthread_mutex_unlock(&counter_mutex); - - return NULL; -} - -void flush_all(memcached_st *memc) -{ - memcached_flush(memc, 0); -} - -pairs_st *load_create_data(memcached_st *memc, unsigned int number_of, - unsigned int *actual_loaded) -{ - memcached_st *memc_clone; - pairs_st *pairs; - - memc_clone= memcached_clone(NULL, memc); - /* We always used non-blocking IO for load since it is faster */ - memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NO_BLOCK, 0); - - pairs= pairs_generate(number_of, 400); - *actual_loaded= execute_set(memc_clone, pairs, number_of); - - memcached_free(memc_clone); - - return pairs; -} diff --git a/clients/memslap.cc b/clients/memslap.cc new file mode 100644 index 00000000..d8927e7e --- /dev/null +++ b/clients/memslap.cc @@ -0,0 +1,495 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2009 Brian Aker 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "client_options.h" +#include "utilities.h" +#include "generator.h" +#include "execute.h" + +#define DEFAULT_INITIAL_LOAD 10000 +#define DEFAULT_EXECUTE_NUMBER 10000 +#define DEFAULT_CONCURRENCY 1 + +#define PROGRAM_NAME "memslap" +#define PROGRAM_DESCRIPTION "Generates a load against a memcached custer of servers." + +/* Global Thread counter */ +volatile unsigned int thread_counter; +pthread_mutex_t counter_mutex; +pthread_cond_t count_threshhold; +volatile unsigned int master_wakeup; +pthread_mutex_t sleeper_mutex; +pthread_cond_t sleep_threshhold; + +void *run_task(void *p); + +/* Types */ +typedef struct conclusions_st conclusions_st; +typedef struct thread_context_st thread_context_st; +typedef enum { + SET_TEST, + GET_TEST, + MGET_TEST +} test_type; + +struct thread_context_st { + unsigned int key_count; + pairs_st *initial_pairs; + unsigned int initial_number; + pairs_st *execute_pairs; + unsigned int execute_number; + char **keys; + size_t *key_lengths; + test_type test; + memcached_st *memc; +}; + +struct conclusions_st { + long int load_time; + long int read_time; + unsigned int rows_loaded; + unsigned int rows_read; +}; + +/* Prototypes */ +void options_parse(int argc, char *argv[]); +void conclusions_print(conclusions_st *conclusion); +void scheduler(memcached_server_st *servers, conclusions_st *conclusion); +pairs_st *load_create_data(memcached_st *memc, unsigned int number_of, + unsigned int *actual_loaded); +void flush_all(memcached_st *memc); + +static int opt_binary= 0; +static int opt_verbose= 0; +static int opt_flush= 0; +static int opt_non_blocking_io= 0; +static int opt_tcp_nodelay= 0; +static unsigned int opt_execute_number= 0; +static unsigned int opt_createial_load= 0; +static unsigned int opt_concurrency= 0; +static int opt_displayflag= 0; +static char *opt_servers= NULL; +static int opt_udp_io= 0; +test_type opt_test= SET_TEST; + +int main(int argc, char *argv[]) +{ + conclusions_st conclusion; + memcached_server_st *servers; + + memset(&conclusion, 0, sizeof(conclusions_st)); + + srandom((unsigned int)time(NULL)); + options_parse(argc, argv); + + if (!opt_servers) + { + char *temp; + + if ((temp= getenv("MEMCACHED_SERVERS"))) + opt_servers= strdup(temp); + else + { + fprintf(stderr, "No Servers provided\n"); + exit(1); + } + } + + servers= memcached_servers_parse(opt_servers); + + pthread_mutex_init(&counter_mutex, NULL); + pthread_cond_init(&count_threshhold, NULL); + pthread_mutex_init(&sleeper_mutex, NULL); + pthread_cond_init(&sleep_threshhold, NULL); + + scheduler(servers, &conclusion); + + free(opt_servers); + + (void)pthread_mutex_destroy(&counter_mutex); + (void)pthread_cond_destroy(&count_threshhold); + (void)pthread_mutex_destroy(&sleeper_mutex); + (void)pthread_cond_destroy(&sleep_threshhold); + conclusions_print(&conclusion); + memcached_server_list_free(servers); + + return 0; +} + +void scheduler(memcached_server_st *servers, conclusions_st *conclusion) +{ + unsigned int actual_loaded= 0; /* Fix warning */ + memcached_st *memc; + + struct timeval start_time, end_time; + pthread_t mainthread; /* Thread descriptor */ + pthread_attr_t attr; /* Thread attributes */ + pairs_st *pairs= NULL; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, + PTHREAD_CREATE_DETACHED); + + memc= memcached_create(NULL); + + /* We need to set udp behavior before adding servers to the client */ + if (opt_udp_io) + { + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_USE_UDP, + (uint64_t)opt_udp_io); + for (uint32_t x= 0; x < memcached_server_list_count(servers); x++ ) + { + servers[x].type= MEMCACHED_CONNECTION_UDP; + } + } + memcached_server_push(memc, servers); + + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, + (uint64_t)opt_binary); + + if (opt_flush) + flush_all(memc); + if (opt_createial_load) + pairs= load_create_data(memc, opt_createial_load, &actual_loaded); + + char **keys= static_cast(calloc(actual_loaded, sizeof(char*))); + size_t *key_lengths= static_cast(calloc(actual_loaded, sizeof(size_t))); + + if (keys == NULL || key_lengths == NULL) + { + free(keys); + free(key_lengths); + keys= NULL; + key_lengths= NULL; + } + else + { + for (uint32_t x= 0; x < actual_loaded; ++x) + { + keys[x]= pairs[x].key; + key_lengths[x]= pairs[x].key_length; + } + } + + /* We set this after we have loaded */ + { + if (opt_non_blocking_io) + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NO_BLOCK, 1); + if (opt_tcp_nodelay) + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, 1); + } + + pthread_mutex_lock(&counter_mutex); + thread_counter= 0; + + pthread_mutex_lock(&sleeper_mutex); + master_wakeup= 1; + pthread_mutex_unlock(&sleeper_mutex); + + for (uint32_t x= 0; x < opt_concurrency; x++) + { + thread_context_st *context; + context= (thread_context_st *)calloc(1, sizeof(thread_context_st)); + + context->memc= memcached_clone(NULL, memc); + context->test= opt_test; + + context->initial_pairs= pairs; + context->initial_number= actual_loaded; + context->keys= keys; + context->key_lengths= key_lengths; + + if (opt_test == SET_TEST) + { + context->execute_pairs= pairs_generate(opt_execute_number, 400); + context->execute_number= opt_execute_number; + } + + /* now you create the thread */ + if (pthread_create(&mainthread, &attr, run_task, + (void *)context) != 0) + { + fprintf(stderr,"Could not create thread\n"); + exit(1); + } + thread_counter++; + } + + pthread_mutex_unlock(&counter_mutex); + pthread_attr_destroy(&attr); + + pthread_mutex_lock(&sleeper_mutex); + master_wakeup= 0; + pthread_mutex_unlock(&sleeper_mutex); + pthread_cond_broadcast(&sleep_threshhold); + + gettimeofday(&start_time, NULL); + /* + We loop until we know that all children have cleaned up. + */ + pthread_mutex_lock(&counter_mutex); + while (thread_counter) + pthread_cond_wait(&count_threshhold, &counter_mutex); + pthread_mutex_unlock(&counter_mutex); + + gettimeofday(&end_time, NULL); + + conclusion->load_time= timedif(end_time, start_time); + conclusion->read_time= timedif(end_time, start_time); + free(keys); + free(key_lengths); + pairs_free(pairs); + memcached_free(memc); +} + +void options_parse(int argc, char *argv[]) +{ + memcached_programs_help_st help_options[]= + { + {0}, + }; + + static struct option long_options[]= + { + {(OPTIONSTRING)"concurrency", required_argument, NULL, OPT_SLAP_CONCURRENCY}, + {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG}, + {(OPTIONSTRING)"execute-number", required_argument, NULL, OPT_SLAP_EXECUTE_NUMBER}, + {(OPTIONSTRING)"flag", no_argument, &opt_displayflag, OPT_FLAG}, + {(OPTIONSTRING)"flush", no_argument, &opt_flush, OPT_FLUSH}, + {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP}, + {(OPTIONSTRING)"initial-load", required_argument, NULL, OPT_SLAP_INITIAL_LOAD}, /* Number to load initially */ + {(OPTIONSTRING)"non-blocking", no_argument, &opt_non_blocking_io, OPT_SLAP_NON_BLOCK}, + {(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS}, + {(OPTIONSTRING)"tcp-nodelay", no_argument, &opt_tcp_nodelay, OPT_SLAP_TCP_NODELAY}, + {(OPTIONSTRING)"test", required_argument, NULL, OPT_SLAP_TEST}, + {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE}, + {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION}, + {(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY}, + {(OPTIONSTRING)"udp", no_argument, NULL, OPT_UDP}, + {0, 0, 0, 0}, + }; + + int option_index= 0; + int option_rv; + + while (1) + { + option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index); + if (option_rv == -1) break; + switch (option_rv) + { + case 0: + break; + case OPT_UDP: + if (opt_test == GET_TEST) + { + fprintf(stderr, "You can not run a get test in UDP mode. UDP mode " + "does not currently support get ops.\n"); + exit(1); + } + opt_udp_io= 1; + break; + case OPT_BINARY: + opt_binary = 1; + break; + case OPT_VERBOSE: /* --verbose or -v */ + opt_verbose = OPT_VERBOSE; + break; + case OPT_DEBUG: /* --debug or -d */ + opt_verbose = OPT_DEBUG; + break; + case OPT_VERSION: /* --version or -V */ + version_command(PROGRAM_NAME); + break; + case OPT_HELP: /* --help or -h */ + help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options); + break; + case OPT_SERVERS: /* --servers or -s */ + opt_servers= strdup(optarg); + break; + case OPT_SLAP_TEST: + if (!strcmp(optarg, "get")) + { + if (opt_udp_io == 1) + { + fprintf(stderr, "You can not run a get test in UDP mode. UDP mode " + "does not currently support get ops.\n"); + exit(1); + } + opt_test= GET_TEST ; + } + else if (!strcmp(optarg, "set")) + opt_test= SET_TEST; + else if (!strcmp(optarg, "mget")) + { + opt_test= MGET_TEST; + } + else + { + fprintf(stderr, "Your test, %s, is not a known test\n", optarg); + exit(1); + } + break; + case OPT_SLAP_CONCURRENCY: + opt_concurrency= (unsigned int)strtoul(optarg, (char **)NULL, 10); + break; + case OPT_SLAP_EXECUTE_NUMBER: + opt_execute_number= (unsigned int)strtoul(optarg, (char **)NULL, 10); + break; + case OPT_SLAP_INITIAL_LOAD: + opt_createial_load= (unsigned int)strtoul(optarg, (char **)NULL, 10); + break; + case '?': + /* getopt_long already printed an error message. */ + exit(1); + default: + abort(); + } + } + + if ((opt_test == GET_TEST || opt_test == MGET_TEST) && opt_createial_load == 0) + opt_createial_load= DEFAULT_INITIAL_LOAD; + + if (opt_execute_number == 0) + opt_execute_number= DEFAULT_EXECUTE_NUMBER; + + if (opt_concurrency == 0) + opt_concurrency= DEFAULT_CONCURRENCY; +} + +void conclusions_print(conclusions_st *conclusion) +{ + printf("\tThreads connecting to servers %u\n", opt_concurrency); +#ifdef NOT_FINISHED + printf("\tLoaded %u rows\n", conclusion->rows_loaded); + printf("\tRead %u rows\n", conclusion->rows_read); +#endif + if (opt_test == SET_TEST) + printf("\tTook %ld.%03ld seconds to load data\n", conclusion->load_time / 1000, + conclusion->load_time % 1000); + else + printf("\tTook %ld.%03ld seconds to read data\n", conclusion->read_time / 1000, + conclusion->read_time % 1000); +} + +void *run_task(void *p) +{ + thread_context_st *context= (thread_context_st *)p; + memcached_st *memc; + + memc= context->memc; + + pthread_mutex_lock(&sleeper_mutex); + while (master_wakeup) + { + pthread_cond_wait(&sleep_threshhold, &sleeper_mutex); + } + pthread_mutex_unlock(&sleeper_mutex); + + /* Do Stuff */ + switch (context->test) + { + case SET_TEST: + assert(context->execute_pairs); + execute_set(memc, context->execute_pairs, context->execute_number); + break; + case GET_TEST: + execute_get(memc, context->initial_pairs, context->initial_number); + break; + case MGET_TEST: + execute_mget(memc, (const char*const*)context->keys, context->key_lengths, + context->initial_number); + break; + default: + WATCHPOINT_ASSERT(context->test); + break; + } + + memcached_free(memc); + + if (context->execute_pairs) + pairs_free(context->execute_pairs); + + free(context); + + pthread_mutex_lock(&counter_mutex); + thread_counter--; + pthread_cond_signal(&count_threshhold); + pthread_mutex_unlock(&counter_mutex); + + return NULL; +} + +void flush_all(memcached_st *memc) +{ + memcached_flush(memc, 0); +} + +pairs_st *load_create_data(memcached_st *memc, unsigned int number_of, + unsigned int *actual_loaded) +{ + memcached_st *memc_clone; + pairs_st *pairs; + + memc_clone= memcached_clone(NULL, memc); + /* We always used non-blocking IO for load since it is faster */ + memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NO_BLOCK, 0); + + pairs= pairs_generate(number_of, 400); + *actual_loaded= execute_set(memc_clone, pairs, number_of); + + memcached_free(memc_clone); + + return pairs; +} diff --git a/clients/memstat.c b/clients/memstat.c deleted file mode 100644 index d3518458..00000000 --- a/clients/memstat.c +++ /dev/null @@ -1,347 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: - * - * Authors: - * Brian Aker - * Toru Maesaka - */ -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "client_options.h" -#include "utilities.h" - -#define PROGRAM_NAME "memstat" -#define PROGRAM_DESCRIPTION "Output the state of a memcached cluster." - -/* Prototypes */ -static void options_parse(int argc, char *argv[]); -static void run_analyzer(memcached_st *memc, memcached_stat_st *memc_stat); -static void print_analysis_report(memcached_st *memc, - memcached_analysis_st *report); - -static int opt_verbose= 0; -static int opt_displayflag= 0; -static int opt_analyze= 0; -static char *opt_servers= NULL; -static char *stat_args= NULL; -static char *analyze_mode= NULL; - -static struct option long_options[]= -{ - {(OPTIONSTRING)"args", required_argument, NULL, OPT_STAT_ARGS}, - {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION}, - {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP}, - {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE}, - {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG}, - {(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS}, - {(OPTIONSTRING)"flag", no_argument, &opt_displayflag, OPT_FLAG}, - {(OPTIONSTRING)"analyze", optional_argument, NULL, OPT_ANALYZE}, - {0, 0, 0, 0}, -}; - - -static memcached_return_t stat_printer(memcached_server_instance_st instance, - const char *key, size_t key_length, - const char *value, size_t value_length, - void *context) -{ - static memcached_server_instance_st last= NULL; - (void)context; - - if (last != instance) - { - printf("Server: %s (%u)\n", memcached_server_name(instance), - (uint32_t)memcached_server_port(instance)); - last= instance; - } - - printf("\t %.*s: %.*s\n", (int)key_length, key, (int)value_length, value); - - return MEMCACHED_SUCCESS; -} - -int main(int argc, char *argv[]) -{ - options_parse(argc, argv); - initialize_sockets(); - - if (! opt_servers) - { - char *temp; - - if ((temp= getenv("MEMCACHED_SERVERS"))) - opt_servers= strdup(temp); - else - { - fprintf(stderr, "No Servers provided\n\n"); - help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, 0); - exit(1); - } - } - - memcached_st *memc= memcached_create(NULL); - - memcached_server_st *servers= memcached_servers_parse(opt_servers); - free(opt_servers); - - memcached_return_t rc= memcached_server_push(memc, servers); - memcached_server_list_free(servers); - - if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_SOME_ERRORS) - { - printf("Failure to communicate with servers (%s)\n", - memcached_strerror(memc, rc)); - exit(1); - } - - if (opt_analyze) - { - memcached_stat_st *memc_stat; - - memc_stat= memcached_stat(memc, NULL, &rc); - - if (! memc_stat) - exit(-1); - - run_analyzer(memc, memc_stat); - - memcached_stat_free(memc, memc_stat); - } - else - { - rc= memcached_stat_execute(memc, stat_args, stat_printer, NULL); - } - - memcached_free(memc); - - return rc == MEMCACHED_SUCCESS ? EXIT_SUCCESS: EXIT_FAILURE; -} - -static void run_analyzer(memcached_st *memc, memcached_stat_st *memc_stat) -{ - memcached_return_t rc; - - if (analyze_mode == NULL) - { - memcached_analysis_st *report; - report= memcached_analyze(memc, memc_stat, &rc); - if (rc != MEMCACHED_SUCCESS || report == NULL) - { - printf("Failure to analyze servers (%s)\n", - memcached_strerror(memc, rc)); - exit(1); - } - print_analysis_report(memc, report); - free(report); - } - else if (strcmp(analyze_mode, "latency") == 0) - { - memcached_st **servers; - uint32_t flags, server_count= memcached_server_count(memc); - uint32_t num_of_tests= 32; - const char *test_key= "libmemcached_test_key"; - - servers= malloc(sizeof(memcached_st*) * server_count); - if (!servers) - { - fprintf(stderr, "Failed to allocate memory\n"); - return; - } - - for (uint32_t x= 0; x < server_count; x++) - { - memcached_server_instance_st instance= - memcached_server_instance_by_position(memc, x); - - if ((servers[x]= memcached_create(NULL)) == NULL) - { - fprintf(stderr, "Failed to memcached_create()\n"); - if (x > 0) - memcached_free(servers[0]); - x--; - for (; x > 0; x--) - memcached_free(servers[x]); - - free(servers); - return; - } - memcached_server_add(servers[x], - memcached_server_name(instance), - memcached_server_port(instance)); - } - - printf("Network Latency Test:\n\n"); - struct timeval start_time, end_time; - uint32_t slowest_server= 0; - long elapsed_time, slowest_time= 0; - - for (uint32_t x= 0; x < server_count; x++) - { - memcached_server_instance_st instance= - memcached_server_instance_by_position(memc, x); - gettimeofday(&start_time, NULL); - - for (uint32_t y= 0; y < num_of_tests; y++) - { - size_t vlen; - char *val= memcached_get(servers[x], test_key, strlen(test_key), - &vlen, &flags, &rc); - if (rc != MEMCACHED_NOTFOUND && rc != MEMCACHED_SUCCESS) - break; - free(val); - } - gettimeofday(&end_time, NULL); - - elapsed_time= (long) timedif(end_time, start_time); - elapsed_time /= (long) num_of_tests; - - if (elapsed_time > slowest_time) - { - slowest_server= x; - slowest_time= elapsed_time; - } - - if (rc != MEMCACHED_NOTFOUND && rc != MEMCACHED_SUCCESS) - { - printf("\t %s (%d) => failed to reach the server\n", - memcached_server_name(instance), - memcached_server_port(instance)); - } - else - { - printf("\t %s (%d) => %ld.%ld seconds\n", - memcached_server_name(instance), - memcached_server_port(instance), - elapsed_time / 1000, elapsed_time % 1000); - } - } - - if (server_count > 1 && slowest_time > 0) - { - memcached_server_instance_st slowest= - memcached_server_instance_by_position(memc, slowest_server); - - printf("---\n"); - printf("Slowest Server: %s (%d) => %ld.%ld seconds\n", - memcached_server_name(slowest), - memcached_server_port(slowest), - slowest_time / 1000, slowest_time % 1000); - } - printf("\n"); - - for (uint32_t x= 0; x < server_count; x++) - memcached_free(servers[x]); - - free(servers); - free(analyze_mode); - } - else - { - fprintf(stderr, "Invalid Analyzer Option provided\n"); - free(analyze_mode); - } -} - -static void print_analysis_report(memcached_st *memc, - memcached_analysis_st *report) - -{ - uint32_t server_count= memcached_server_count(memc); - memcached_server_instance_st most_consumed_server= memcached_server_instance_by_position(memc, report->most_consumed_server); - memcached_server_instance_st least_free_server= memcached_server_instance_by_position(memc, report->least_free_server); - memcached_server_instance_st oldest_server= memcached_server_instance_by_position(memc, report->oldest_server); - - printf("Memcached Cluster Analysis Report\n\n"); - - printf("\tNumber of Servers Analyzed : %u\n", server_count); - printf("\tAverage Item Size (incl/overhead) : %u bytes\n", - report->average_item_size); - - if (server_count == 1) - { - printf("\nFor a detailed report, you must supply multiple servers.\n"); - return; - } - - printf("\n"); - printf("\tNode with most memory consumption : %s:%u (%llu bytes)\n", - memcached_server_name(most_consumed_server), - (uint32_t)memcached_server_port(most_consumed_server), - (unsigned long long)report->most_used_bytes); - printf("\tNode with least free space : %s:%u (%llu bytes remaining)\n", - memcached_server_name(least_free_server), - (uint32_t)memcached_server_port(least_free_server), - (unsigned long long)report->least_remaining_bytes); - printf("\tNode with longest uptime : %s:%u (%us)\n", - memcached_server_name(oldest_server), - (uint32_t)memcached_server_port(oldest_server), - report->longest_uptime); - printf("\tPool-wide Hit Ratio : %1.f%%\n", report->pool_hit_ratio); - printf("\n"); -} - -static void options_parse(int argc, char *argv[]) -{ - memcached_programs_help_st help_options[]= - { - {0}, - }; - - int option_index= 0; - int option_rv; - - while (1) - { - option_rv= getopt_long(argc, argv, "Vhvds:a", long_options, &option_index); - if (option_rv == -1) break; - switch (option_rv) - { - case 0: - break; - case OPT_VERBOSE: /* --verbose or -v */ - opt_verbose = OPT_VERBOSE; - break; - case OPT_DEBUG: /* --debug or -d */ - opt_verbose = OPT_DEBUG; - break; - case OPT_VERSION: /* --version or -V */ - version_command(PROGRAM_NAME); - break; - case OPT_HELP: /* --help or -h */ - help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options); - break; - case OPT_SERVERS: /* --servers or -s */ - opt_servers= strdup(optarg); - break; - case OPT_STAT_ARGS: - stat_args= strdup(optarg); - break; - case OPT_ANALYZE: /* --analyze or -a */ - opt_analyze= OPT_ANALYZE; - analyze_mode= (optarg) ? strdup(optarg) : NULL; - break; - case '?': - /* getopt_long already printed an error message. */ - exit(1); - default: - abort(); - } - } -} diff --git a/clients/memstat.cc b/clients/memstat.cc new file mode 100644 index 00000000..a3b85108 --- /dev/null +++ b/clients/memstat.cc @@ -0,0 +1,349 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: + * + * Authors: + * Brian Aker + * Toru Maesaka + */ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "client_options.h" +#include "utilities.h" + +#define PROGRAM_NAME "memstat" +#define PROGRAM_DESCRIPTION "Output the state of a memcached cluster." + +/* Prototypes */ +static void options_parse(int argc, char *argv[]); +static void run_analyzer(memcached_st *memc, memcached_stat_st *memc_stat); +static void print_analysis_report(memcached_st *memc, + memcached_analysis_st *report); + +static int opt_verbose= 0; +static int opt_displayflag= 0; +static int opt_analyze= 0; +static char *opt_servers= NULL; +static char *stat_args= NULL; +static char *analyze_mode= NULL; + +static struct option long_options[]= +{ + {(OPTIONSTRING)"args", required_argument, NULL, OPT_STAT_ARGS}, + {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION}, + {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP}, + {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE}, + {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG}, + {(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS}, + {(OPTIONSTRING)"flag", no_argument, &opt_displayflag, OPT_FLAG}, + {(OPTIONSTRING)"analyze", optional_argument, NULL, OPT_ANALYZE}, + {0, 0, 0, 0}, +}; + + +static memcached_return_t stat_printer(memcached_server_instance_st instance, + const char *key, size_t key_length, + const char *value, size_t value_length, + void *context) +{ + static memcached_server_instance_st last= NULL; + (void)context; + + if (last != instance) + { + printf("Server: %s (%u)\n", memcached_server_name(instance), + (uint32_t)memcached_server_port(instance)); + last= instance; + } + + printf("\t %.*s: %.*s\n", (int)key_length, key, (int)value_length, value); + + return MEMCACHED_SUCCESS; +} + +int main(int argc, char *argv[]) +{ + options_parse(argc, argv); + initialize_sockets(); + + if (! opt_servers) + { + char *temp; + + if ((temp= getenv("MEMCACHED_SERVERS"))) + opt_servers= strdup(temp); + else + { + fprintf(stderr, "No Servers provided\n\n"); + help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, 0); + exit(1); + } + } + + memcached_st *memc= memcached_create(NULL); + + memcached_server_st *servers= memcached_servers_parse(opt_servers); + free(opt_servers); + + memcached_return_t rc= memcached_server_push(memc, servers); + memcached_server_list_free(servers); + + if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_SOME_ERRORS) + { + printf("Failure to communicate with servers (%s)\n", + memcached_strerror(memc, rc)); + exit(1); + } + + if (opt_analyze) + { + memcached_stat_st *memc_stat; + + memc_stat= memcached_stat(memc, NULL, &rc); + + if (! memc_stat) + exit(-1); + + run_analyzer(memc, memc_stat); + + memcached_stat_free(memc, memc_stat); + } + else + { + rc= memcached_stat_execute(memc, stat_args, stat_printer, NULL); + } + + memcached_free(memc); + + return rc == MEMCACHED_SUCCESS ? EXIT_SUCCESS: EXIT_FAILURE; +} + +static void run_analyzer(memcached_st *memc, memcached_stat_st *memc_stat) +{ + memcached_return_t rc; + + if (analyze_mode == NULL) + { + memcached_analysis_st *report; + report= memcached_analyze(memc, memc_stat, &rc); + if (rc != MEMCACHED_SUCCESS || report == NULL) + { + printf("Failure to analyze servers (%s)\n", + memcached_strerror(memc, rc)); + exit(1); + } + print_analysis_report(memc, report); + free(report); + } + else if (strcmp(analyze_mode, "latency") == 0) + { + uint32_t flags, server_count= memcached_server_count(memc); + uint32_t num_of_tests= 32; + const char *test_key= "libmemcached_test_key"; + + memcached_st **servers; + servers= static_cast(malloc(sizeof(memcached_st*) * server_count)); + if (not servers) + { + fprintf(stderr, "Failed to allocate memory\n"); + return; + } + + for (uint32_t x= 0; x < server_count; x++) + { + memcached_server_instance_st instance= + memcached_server_instance_by_position(memc, x); + + if ((servers[x]= memcached_create(NULL)) == NULL) + { + fprintf(stderr, "Failed to memcached_create()\n"); + if (x > 0) + memcached_free(servers[0]); + x--; + + for (; x > 0; x--) + memcached_free(servers[x]); + + free(servers); + return; + } + memcached_server_add(servers[x], + memcached_server_name(instance), + memcached_server_port(instance)); + } + + printf("Network Latency Test:\n\n"); + struct timeval start_time, end_time; + uint32_t slowest_server= 0; + long elapsed_time, slowest_time= 0; + + for (uint32_t x= 0; x < server_count; x++) + { + memcached_server_instance_st instance= + memcached_server_instance_by_position(memc, x); + gettimeofday(&start_time, NULL); + + for (uint32_t y= 0; y < num_of_tests; y++) + { + size_t vlen; + char *val= memcached_get(servers[x], test_key, strlen(test_key), + &vlen, &flags, &rc); + if (rc != MEMCACHED_NOTFOUND && rc != MEMCACHED_SUCCESS) + break; + free(val); + } + gettimeofday(&end_time, NULL); + + elapsed_time= (long) timedif(end_time, start_time); + elapsed_time /= (long) num_of_tests; + + if (elapsed_time > slowest_time) + { + slowest_server= x; + slowest_time= elapsed_time; + } + + if (rc != MEMCACHED_NOTFOUND && rc != MEMCACHED_SUCCESS) + { + printf("\t %s (%d) => failed to reach the server\n", + memcached_server_name(instance), + memcached_server_port(instance)); + } + else + { + printf("\t %s (%d) => %ld.%ld seconds\n", + memcached_server_name(instance), + memcached_server_port(instance), + elapsed_time / 1000, elapsed_time % 1000); + } + } + + if (server_count > 1 && slowest_time > 0) + { + memcached_server_instance_st slowest= + memcached_server_instance_by_position(memc, slowest_server); + + printf("---\n"); + printf("Slowest Server: %s (%d) => %ld.%ld seconds\n", + memcached_server_name(slowest), + memcached_server_port(slowest), + slowest_time / 1000, slowest_time % 1000); + } + printf("\n"); + + for (uint32_t x= 0; x < server_count; x++) + memcached_free(servers[x]); + + free(servers); + free(analyze_mode); + } + else + { + fprintf(stderr, "Invalid Analyzer Option provided\n"); + free(analyze_mode); + } +} + +static void print_analysis_report(memcached_st *memc, + memcached_analysis_st *report) + +{ + uint32_t server_count= memcached_server_count(memc); + memcached_server_instance_st most_consumed_server= memcached_server_instance_by_position(memc, report->most_consumed_server); + memcached_server_instance_st least_free_server= memcached_server_instance_by_position(memc, report->least_free_server); + memcached_server_instance_st oldest_server= memcached_server_instance_by_position(memc, report->oldest_server); + + printf("Memcached Cluster Analysis Report\n\n"); + + printf("\tNumber of Servers Analyzed : %u\n", server_count); + printf("\tAverage Item Size (incl/overhead) : %u bytes\n", + report->average_item_size); + + if (server_count == 1) + { + printf("\nFor a detailed report, you must supply multiple servers.\n"); + return; + } + + printf("\n"); + printf("\tNode with most memory consumption : %s:%u (%llu bytes)\n", + memcached_server_name(most_consumed_server), + (uint32_t)memcached_server_port(most_consumed_server), + (unsigned long long)report->most_used_bytes); + printf("\tNode with least free space : %s:%u (%llu bytes remaining)\n", + memcached_server_name(least_free_server), + (uint32_t)memcached_server_port(least_free_server), + (unsigned long long)report->least_remaining_bytes); + printf("\tNode with longest uptime : %s:%u (%us)\n", + memcached_server_name(oldest_server), + (uint32_t)memcached_server_port(oldest_server), + report->longest_uptime); + printf("\tPool-wide Hit Ratio : %1.f%%\n", report->pool_hit_ratio); + printf("\n"); +} + +static void options_parse(int argc, char *argv[]) +{ + memcached_programs_help_st help_options[]= + { + {0}, + }; + + int option_index= 0; + int option_rv; + + while (1) + { + option_rv= getopt_long(argc, argv, "Vhvds:a", long_options, &option_index); + if (option_rv == -1) break; + switch (option_rv) + { + case 0: + break; + case OPT_VERBOSE: /* --verbose or -v */ + opt_verbose = OPT_VERBOSE; + break; + case OPT_DEBUG: /* --debug or -d */ + opt_verbose = OPT_DEBUG; + break; + case OPT_VERSION: /* --version or -V */ + version_command(PROGRAM_NAME); + break; + case OPT_HELP: /* --help or -h */ + help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options); + break; + case OPT_SERVERS: /* --servers or -s */ + opt_servers= strdup(optarg); + break; + case OPT_STAT_ARGS: + stat_args= strdup(optarg); + break; + case OPT_ANALYZE: /* --analyze or -a */ + opt_analyze= OPT_ANALYZE; + analyze_mode= (optarg) ? strdup(optarg) : NULL; + break; + case '?': + /* getopt_long already printed an error message. */ + exit(1); + default: + abort(); + } + } +} diff --git a/clients/ms_conn.h b/clients/ms_conn.h index cf1e8c0f..b915888d 100644 --- a/clients/ms_conn.h +++ b/clients/ms_conn.h @@ -60,7 +60,7 @@ enum conn_states { conn_read, /* reading in a command line */ conn_write, /* writing out a simple response */ - conn_closing, /* closing this connection */ + conn_closing /* closing this connection */ }; /* returned states of memcached command */ @@ -78,7 +78,7 @@ enum mcd_ret MCD_NOTFOUND, /* server not find the object */ MCD_END, /* end of the response of get command */ MCD_DELETED, /* server delete the object successfully */ - MCD_STAT, /* response of stats command */ + MCD_STAT /* response of stats command */ }; /* used to store the current or previous running command state */ @@ -103,7 +103,7 @@ typedef struct udppkt enum protocol { ascii_prot = 3, /* ASCII protocol */ - binary_prot, /* binary protocol */ + binary_prot /* binary protocol */ }; /** diff --git a/clients/ms_memslap.h b/clients/ms_memslap.h index dc0844d8..1c1b29eb 100644 --- a/clients/ms_memslap.h +++ b/clients/ms_memslap.h @@ -55,7 +55,7 @@ typedef enum OPT_BINARY_PROTOCOL= 'B', OPT_OVERWRITE= 'o', OPT_TPS= 'P', - OPT_REP_WRITE_SRV= 'p', + OPT_REP_WRITE_SRV= 'p' } ms_options_t; /* global statistic of response time */ diff --git a/clients/ms_setting.h b/clients/ms_setting.h index d8ccf8bb..964dc400 100644 --- a/clients/ms_setting.h +++ b/clients/ms_setting.h @@ -88,7 +88,7 @@ typedef enum cmd_type { CMD_SET, CMD_GET, - CMD_NULL, + CMD_NULL } ms_cmd_type_t; /* types in the configuration file */ @@ -97,7 +97,7 @@ typedef enum conf_type CONF_KEY, CONF_VALUE, CONF_CMD, - CONF_NULL, + CONF_NULL } ms_conf_type_t; /* information of command distribution */ diff --git a/clients/utilities.c b/clients/utilities.c deleted file mode 100644 index 92987471..00000000 --- a/clients/utilities.c +++ /dev/null @@ -1,233 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: - * - */ -#include "config.h" - -#include -#include -#include -#include "utilities.h" - - -long int timedif(struct timeval a, struct timeval b) -{ - long us, s; - - us = (int)(a.tv_usec - b.tv_usec); - us /= 1000; - s = (int)(a.tv_sec - b.tv_sec); - s *= 1000; - return s + us; -} - -void version_command(const char *command_name) -{ - printf("%s v%u.%u\n", command_name, 1U, 0U); - exit(0); -} - -static const char *lookup_help(memcached_options option) -{ - switch (option) - { - case OPT_SERVERS: return("List which servers you wish to connect to."); - case OPT_VERSION: return("Display the version of the application and then exit."); - case OPT_HELP: return("Display this message and then exit."); - case OPT_VERBOSE: return("Give more details on the progression of the application."); - case OPT_DEBUG: return("Provide output only useful for debugging."); - case OPT_FLAG: return("Provide flag information for storage operation."); - case OPT_EXPIRE: return("Set the expire option for the object."); - case OPT_SET: return("Use set command with memcached when storing."); - case OPT_REPLACE: return("Use replace command with memcached when storing."); - case OPT_ADD: return("Use add command with memcached when storing."); - case OPT_SLAP_EXECUTE_NUMBER: return("Number of times to execute the given test."); - case OPT_SLAP_INITIAL_LOAD: return("Number of key pairs to load before executing tests."); - case OPT_SLAP_TEST: return("Test to run (currently \"get\" or \"set\")."); - case OPT_SLAP_CONCURRENCY: return("Number of users to simulate with load."); - case OPT_SLAP_NON_BLOCK: return("Set TCP up to use non-blocking IO."); - case OPT_SLAP_TCP_NODELAY: return("Set TCP socket up to use nodelay."); - case OPT_FLUSH: return("Flush servers before running tests."); - case OPT_HASH: return("Select hash type."); - case OPT_BINARY: return("Switch to binary protocol."); - case OPT_ANALYZE: return("Analyze the provided servers."); - case OPT_UDP: return("Use UDP protocol when communicating with server."); - case OPT_USERNAME: return "Username to use for SASL authentication"; - case OPT_PASSWD: return "Password to use for SASL authentication"; - case OPT_FILE: return "Path to file in which to save result"; - case OPT_STAT_ARGS: return "Argument for statistics"; - default: WATCHPOINT_ASSERT(0); - }; - - WATCHPOINT_ASSERT(0); - return "forgot to document this function :)"; -} - -void help_command(const char *command_name, const char *description, - const struct option *long_options, - memcached_programs_help_st *options) -{ - unsigned int x; - (void)options; - - printf("%s v%u.%u\n\n", command_name, 1U, 0U); - printf("\t%s\n\n", description); - printf("Current options. A '=' means the option takes a value.\n\n"); - - for (x= 0; long_options[x].name; x++) - { - const char *help_message; - - printf("\t --%s%c\n", long_options[x].name, - long_options[x].has_arg ? '=' : ' '); - if ((help_message= lookup_help(long_options[x].val))) - printf("\t\t%s\n", help_message); - } - - printf("\n"); - exit(0); -} - -void process_hash_option(memcached_st *memc, char *opt_hash) -{ - uint64_t set; - memcached_return_t rc; - - if (opt_hash == NULL) - return; - - set= MEMCACHED_HASH_DEFAULT; /* Just here to solve warning */ - if (!strcasecmp(opt_hash, "CRC")) - set= MEMCACHED_HASH_CRC; - else if (!strcasecmp(opt_hash, "FNV1_64")) - set= MEMCACHED_HASH_FNV1_64; - else if (!strcasecmp(opt_hash, "FNV1A_64")) - set= MEMCACHED_HASH_FNV1A_64; - else if (!strcasecmp(opt_hash, "FNV1_32")) - set= MEMCACHED_HASH_FNV1_32; - else if (!strcasecmp(opt_hash, "FNV1A_32")) - set= MEMCACHED_HASH_FNV1A_32; - else - { - fprintf(stderr, "hash: type not recognized %s\n", opt_hash); - exit(1); - } - - rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_HASH, set); - if (rc != MEMCACHED_SUCCESS) - { - fprintf(stderr, "hash: memcache error %s\n", memcached_strerror(memc, rc)); - exit(1); - } -} - -#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT -static char *username; -static char *passwd; - -static int get_username(void *context, int id, const char **result, - unsigned int *len) -{ - (void)context; - if (!result || (id != SASL_CB_USER && id != SASL_CB_AUTHNAME)) - return SASL_BADPARAM; - - *result= username; - if (len) - *len= (username == NULL) ? 0 : (unsigned int)strlen(username); - - return SASL_OK; -} - -static int get_password(sasl_conn_t *conn, void *context, int id, - sasl_secret_t **psecret) -{ - (void)context; - static sasl_secret_t* ptr; - - if (!conn || ! psecret || id != SASL_CB_PASS) - return SASL_BADPARAM; - - if (passwd == NULL) - { - *psecret= NULL; - return SASL_OK; - } - - size_t len= strlen(passwd); - ptr= malloc(sizeof(sasl_secret_t) + len +1); - if (! ptr) - return SASL_NOMEM; - - ptr->len= len; - memcpy(ptr->data, passwd, len); - ptr->data[len]= 0; - - *psecret= ptr; - return SASL_OK; -} - -/* callbacks we support */ -static sasl_callback_t sasl_callbacks[] = { - { - SASL_CB_USER, &get_username, NULL - }, { - SASL_CB_AUTHNAME, &get_username, NULL - }, { - SASL_CB_PASS, &get_password, NULL - }, { - SASL_CB_LIST_END, NULL, NULL - } -}; -#endif - -bool initialize_sasl(memcached_st *memc, char *user, char *password) -{ -#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT - if (user != NULL && password != NULL) - { - username= user; - passwd= password; - - if (sasl_client_init(NULL) != SASL_OK) - { - fprintf(stderr, "Failed to initialize sasl library!\n"); - return false; - } - memcached_set_sasl_callbacks(memc, sasl_callbacks); - } -#else - (void)memc; - (void)user; - (void)password; -#endif - - return true; -} - -void shutdown_sasl(void) -{ -#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT - if (username != NULL || passwd != NULL) - sasl_done(); -#endif -} - -void initialize_sockets(void) -{ - /* Define the function for all platforms to avoid #ifdefs in each program */ -#ifdef WIN32 - WSADATA wsaData; - if (WSAStartup(MAKEWORD(2,0), &wsaData) != 0) - { - fprintf(stderr, "Socket Initialization Error. Program aborted\n"); - exit(EXIT_FAILURE); - } -#endif -} diff --git a/clients/utilities.cc b/clients/utilities.cc new file mode 100644 index 00000000..ca109adc --- /dev/null +++ b/clients/utilities.cc @@ -0,0 +1,229 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: + * + */ +#include "config.h" + +#include +#include +#include +#include + + +long int timedif(struct timeval a, struct timeval b) +{ + long us, s; + + us = (int)(a.tv_usec - b.tv_usec); + us /= 1000; + s = (int)(a.tv_sec - b.tv_sec); + s *= 1000; + return s + us; +} + +void version_command(const char *command_name) +{ + printf("%s v%u.%u\n", command_name, 1U, 0U); + exit(0); +} + +static const char *lookup_help(memcached_options option) +{ + switch (option) + { + case OPT_SERVERS: return("List which servers you wish to connect to."); + case OPT_VERSION: return("Display the version of the application and then exit."); + case OPT_HELP: return("Display this message and then exit."); + case OPT_VERBOSE: return("Give more details on the progression of the application."); + case OPT_DEBUG: return("Provide output only useful for debugging."); + case OPT_FLAG: return("Provide flag information for storage operation."); + case OPT_EXPIRE: return("Set the expire option for the object."); + case OPT_SET: return("Use set command with memcached when storing."); + case OPT_REPLACE: return("Use replace command with memcached when storing."); + case OPT_ADD: return("Use add command with memcached when storing."); + case OPT_SLAP_EXECUTE_NUMBER: return("Number of times to execute the given test."); + case OPT_SLAP_INITIAL_LOAD: return("Number of key pairs to load before executing tests."); + case OPT_SLAP_TEST: return("Test to run (currently \"get\" or \"set\")."); + case OPT_SLAP_CONCURRENCY: return("Number of users to simulate with load."); + case OPT_SLAP_NON_BLOCK: return("Set TCP up to use non-blocking IO."); + case OPT_SLAP_TCP_NODELAY: return("Set TCP socket up to use nodelay."); + case OPT_FLUSH: return("Flush servers before running tests."); + case OPT_HASH: return("Select hash type."); + case OPT_BINARY: return("Switch to binary protocol."); + case OPT_ANALYZE: return("Analyze the provided servers."); + case OPT_UDP: return("Use UDP protocol when communicating with server."); + case OPT_USERNAME: return "Username to use for SASL authentication"; + case OPT_PASSWD: return "Password to use for SASL authentication"; + case OPT_FILE: return "Path to file in which to save result"; + case OPT_STAT_ARGS: return "Argument for statistics"; + default: WATCHPOINT_ASSERT(0); + }; + + WATCHPOINT_ASSERT(0); + return "forgot to document this function :)"; +} + +void help_command(const char *command_name, const char *description, + const struct option *long_options, + memcached_programs_help_st *options) +{ + unsigned int x; + (void)options; + + printf("%s v%u.%u\n\n", command_name, 1U, 0U); + printf("\t%s\n\n", description); + printf("Current options. A '=' means the option takes a value.\n\n"); + + for (x= 0; long_options[x].name; x++) + { + const char *help_message; + + printf("\t --%s%c\n", long_options[x].name, + long_options[x].has_arg ? '=' : ' '); + if ((help_message= lookup_help(memcached_options(long_options[x].val)))) + printf("\t\t%s\n", help_message); + } + + printf("\n"); + exit(0); +} + +void process_hash_option(memcached_st *memc, char *opt_hash) +{ + uint64_t set; + memcached_return_t rc; + + if (opt_hash == NULL) + return; + + set= MEMCACHED_HASH_DEFAULT; /* Just here to solve warning */ + if (!strcasecmp(opt_hash, "CRC")) + set= MEMCACHED_HASH_CRC; + else if (!strcasecmp(opt_hash, "FNV1_64")) + set= MEMCACHED_HASH_FNV1_64; + else if (!strcasecmp(opt_hash, "FNV1A_64")) + set= MEMCACHED_HASH_FNV1A_64; + else if (!strcasecmp(opt_hash, "FNV1_32")) + set= MEMCACHED_HASH_FNV1_32; + else if (!strcasecmp(opt_hash, "FNV1A_32")) + set= MEMCACHED_HASH_FNV1A_32; + else + { + fprintf(stderr, "hash: type not recognized %s\n", opt_hash); + exit(1); + } + + rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_HASH, set); + if (rc != MEMCACHED_SUCCESS) + { + fprintf(stderr, "hash: memcache error %s\n", memcached_strerror(memc, rc)); + exit(1); + } +} + +#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT +static char *username; +static char *passwd; + +static int get_username(void *context, int id, const char **result, unsigned int *len) +{ + (void)context; + if (!result || (id != SASL_CB_USER && id != SASL_CB_AUTHNAME)) + return SASL_BADPARAM; + + *result= username; + if (len) + *len= (username == NULL) ? 0 : (unsigned int)strlen(username); + + return SASL_OK; +} + +static int get_password(sasl_conn_t *conn, void *context, int id, + sasl_secret_t **psecret) +{ + (void)context; + static sasl_secret_t* ptr; + + if (!conn || ! psecret || id != SASL_CB_PASS) + return SASL_BADPARAM; + + if (passwd == NULL) + { + *psecret= NULL; + return SASL_OK; + } + + size_t len= strlen(passwd); + ptr= (sasl_secret_t *)malloc(sizeof(sasl_secret_t) + len +1); + if (not ptr) + return SASL_NOMEM; + + ptr->len= len; + memcpy(ptr->data, passwd, len); + ptr->data[len]= 0; + + *psecret= ptr; + return SASL_OK; +} + +typedef int (*local_sasl_fn)(void); + +/* callbacks we support */ +static sasl_callback_t sasl_callbacks[] = { + { SASL_CB_USER, (local_sasl_fn)get_username, NULL }, + { SASL_CB_AUTHNAME, (local_sasl_fn)get_username, NULL }, + { SASL_CB_PASS, (local_sasl_fn)get_password, NULL }, + { SASL_CB_LIST_END, NULL, NULL } +}; +#endif + +bool initialize_sasl(memcached_st *memc, char *user, char *password) +{ +#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT + if (user != NULL && password != NULL) + { + username= user; + passwd= password; + + if (sasl_client_init(NULL) != SASL_OK) + { + fprintf(stderr, "Failed to initialize sasl library!\n"); + return false; + } + memcached_set_sasl_callbacks(memc, sasl_callbacks); + } +#else + (void)memc; + (void)user; + (void)password; +#endif + + return true; +} + +void shutdown_sasl(void) +{ +#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT + if (username != NULL || passwd != NULL) + sasl_done(); +#endif +} + +void initialize_sockets(void) +{ + /* Define the function for all platforms to avoid #ifdefs in each program */ +#ifdef WIN32 + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2,0), &wsaData) != 0) + { + fprintf(stderr, "Socket Initialization Error. Program aborted\n"); + exit(EXIT_FAILURE); + } +#endif +} diff --git a/clients/utilities.h b/clients/utilities.h index a998773b..685be6a5 100644 --- a/clients/utilities.h +++ b/clients/utilities.h @@ -9,6 +9,8 @@ * */ +#pragma once + #include #include #include "libmemcached/watchpoint.h" @@ -41,6 +43,10 @@ struct memcached_programs_help_st char *not_used_yet; }; +#ifdef __cplusplus +extern "C" { +#endif + char *strdup_cleanup(const char *str); void cleanup(void); long int timedif(struct timeval a, struct timeval b); @@ -52,3 +58,7 @@ void process_hash_option(memcached_st *memc, char *opt_hash); bool initialize_sasl(memcached_st *memc, char *user, char *password); void shutdown_sasl(void); void initialize_sockets(void); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/configure.ac b/configure.ac index ea993358..9b8a82c5 100644 --- a/configure.ac +++ b/configure.ac @@ -9,7 +9,7 @@ AC_PREREQ(2.59) AC_INIT([libmemcached],[0.49],[http://libmemcached.org/]) -AC_CONFIG_SRCDIR([libmemcached/memcached.c]) +AC_CONFIG_SRCDIR([libmemcached/memcached.cc]) AC_CONFIG_AUX_DIR(config) PANDORA_CANONICAL_TARGET(no-vc-changelog) diff --git a/example/include.am b/example/include.am index ae9bae93..561eebc8 100644 --- a/example/include.am +++ b/example/include.am @@ -10,16 +10,13 @@ example_memcached_light_SOURCES= \ example/interface_v0.c \ example/interface_v1.c \ example/memcached_light.c \ + libmemcached/byteorder.cc \ example/memcached_light.h \ example/storage.h example_memcached_light_LDADD= libmemcached/libmemcachedprotocol.la \ $(LIBINNODB) $(LTLIBEVENT) -if BUILD_BYTEORDER -example_memcached_light_LDADD+= libmemcached/libbyteorder.la -endif - if HAVE_LIBINNODB example_memcached_light_SOURCES+= example/storage_innodb.c else diff --git a/libhashkit/algorithm.c b/libhashkit/algorithm.c deleted file mode 100644 index de00081d..00000000 --- a/libhashkit/algorithm.c +++ /dev/null @@ -1,69 +0,0 @@ -/* HashKit - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - */ - -#include - -uint32_t libhashkit_one_at_a_time(const char *key, size_t key_length) -{ - return hashkit_one_at_a_time(key, key_length, NULL); -} - -uint32_t libhashkit_fnv1_64(const char *key, size_t key_length) -{ - return hashkit_fnv1_64(key, key_length, NULL); -} - -uint32_t libhashkit_fnv1a_64(const char *key, size_t key_length) -{ - return hashkit_fnv1a_64(key, key_length, NULL); -} - -uint32_t libhashkit_fnv1_32(const char *key, size_t key_length) -{ - return hashkit_fnv1_32(key, key_length, NULL); -} - -uint32_t libhashkit_fnv1a_32(const char *key, size_t key_length) -{ - return hashkit_fnv1a_32(key, key_length, NULL); -} - -uint32_t libhashkit_crc32(const char *key, size_t key_length) -{ - return hashkit_crc32(key, key_length, NULL); -} - -#ifdef HAVE_HSIEH_HASH -uint32_t libhashkit_hsieh(const char *key, size_t key_length) -{ - return hashkit_hsieh(key, key_length, NULL); -} -#endif - -#ifdef HAVE_MURMUR_HASH -uint32_t libhashkit_murmur(const char *key, size_t key_length) -{ - return hashkit_murmur(key, key_length, NULL); -} -#endif - -uint32_t libhashkit_jenkins(const char *key, size_t key_length) -{ - return hashkit_jenkins(key, key_length, NULL); -} - -uint32_t libhashkit_md5(const char *key, size_t key_length) -{ - return hashkit_md5(key, key_length, NULL); -} - -void libhashkit_md5_signature(const unsigned char *key, size_t length, unsigned char *result) -{ - md5_signature(key, (uint32_t)length, result); -} - diff --git a/libhashkit/algorithm.cc b/libhashkit/algorithm.cc new file mode 100644 index 00000000..de00081d --- /dev/null +++ b/libhashkit/algorithm.cc @@ -0,0 +1,69 @@ +/* HashKit + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + */ + +#include + +uint32_t libhashkit_one_at_a_time(const char *key, size_t key_length) +{ + return hashkit_one_at_a_time(key, key_length, NULL); +} + +uint32_t libhashkit_fnv1_64(const char *key, size_t key_length) +{ + return hashkit_fnv1_64(key, key_length, NULL); +} + +uint32_t libhashkit_fnv1a_64(const char *key, size_t key_length) +{ + return hashkit_fnv1a_64(key, key_length, NULL); +} + +uint32_t libhashkit_fnv1_32(const char *key, size_t key_length) +{ + return hashkit_fnv1_32(key, key_length, NULL); +} + +uint32_t libhashkit_fnv1a_32(const char *key, size_t key_length) +{ + return hashkit_fnv1a_32(key, key_length, NULL); +} + +uint32_t libhashkit_crc32(const char *key, size_t key_length) +{ + return hashkit_crc32(key, key_length, NULL); +} + +#ifdef HAVE_HSIEH_HASH +uint32_t libhashkit_hsieh(const char *key, size_t key_length) +{ + return hashkit_hsieh(key, key_length, NULL); +} +#endif + +#ifdef HAVE_MURMUR_HASH +uint32_t libhashkit_murmur(const char *key, size_t key_length) +{ + return hashkit_murmur(key, key_length, NULL); +} +#endif + +uint32_t libhashkit_jenkins(const char *key, size_t key_length) +{ + return hashkit_jenkins(key, key_length, NULL); +} + +uint32_t libhashkit_md5(const char *key, size_t key_length) +{ + return hashkit_md5(key, key_length, NULL); +} + +void libhashkit_md5_signature(const unsigned char *key, size_t length, unsigned char *result) +{ + md5_signature(key, (uint32_t)length, result); +} + diff --git a/libhashkit/behavior.c b/libhashkit/behavior.c deleted file mode 100644 index ee0efcf3..00000000 --- a/libhashkit/behavior.c +++ /dev/null @@ -1,9 +0,0 @@ -/* HashKit - * Copyright (C) 2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - */ - -#include diff --git a/libhashkit/behavior.cc b/libhashkit/behavior.cc new file mode 100644 index 00000000..ee0efcf3 --- /dev/null +++ b/libhashkit/behavior.cc @@ -0,0 +1,9 @@ +/* HashKit + * Copyright (C) 2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + */ + +#include diff --git a/libhashkit/common.h b/libhashkit/common.h index 73b198f5..5cf8b9f5 100644 --- a/libhashkit/common.h +++ b/libhashkit/common.h @@ -6,8 +6,7 @@ * the COPYING file in the parent directory for full text. */ -#ifndef HASHKIT_COMMON_H -#define HASHKIT_COMMON_H +#pragma once #include @@ -32,5 +31,3 @@ int update_continuum(hashkit_st *hashkit); #ifdef __cplusplus } #endif - -#endif /* HASHKIT_COMMON_H */ diff --git a/libhashkit/crc32.c b/libhashkit/crc32.c deleted file mode 100644 index f07958ca..00000000 --- a/libhashkit/crc32.c +++ /dev/null @@ -1,86 +0,0 @@ -/* The crc32 functions and data was originally written by Spencer - * Garrett and was gleaned from the PostgreSQL source - * tree via the files contrib/ltree/crc32.[ch] and from FreeBSD at - * src/usr.bin/cksum/crc32.c. - */ - -#include - -static const uint32_t crc32tab[256] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, - 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, - 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, - 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, - 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, - 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, - 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, - 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, - 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, - 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, - 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, - 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, - 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, -}; - -uint32_t hashkit_crc32(const char *key, size_t key_length, void *context) -{ - uint64_t x; - uint32_t crc= UINT32_MAX; - (void)context; - - for (x= 0; x < key_length; x++) - crc= (crc >> 8) ^ crc32tab[(crc ^ (uint64_t)key[x]) & 0xff]; - - return ((~crc) >> 16) & 0x7fff; -} diff --git a/libhashkit/crc32.cc b/libhashkit/crc32.cc new file mode 100644 index 00000000..f07958ca --- /dev/null +++ b/libhashkit/crc32.cc @@ -0,0 +1,86 @@ +/* The crc32 functions and data was originally written by Spencer + * Garrett and was gleaned from the PostgreSQL source + * tree via the files contrib/ltree/crc32.[ch] and from FreeBSD at + * src/usr.bin/cksum/crc32.c. + */ + +#include + +static const uint32_t crc32tab[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; + +uint32_t hashkit_crc32(const char *key, size_t key_length, void *context) +{ + uint64_t x; + uint32_t crc= UINT32_MAX; + (void)context; + + for (x= 0; x < key_length; x++) + crc= (crc >> 8) ^ crc32tab[(crc ^ (uint64_t)key[x]) & 0xff]; + + return ((~crc) >> 16) & 0x7fff; +} diff --git a/libhashkit/digest.c b/libhashkit/digest.c deleted file mode 100644 index e1559819..00000000 --- a/libhashkit/digest.c +++ /dev/null @@ -1,60 +0,0 @@ -/* HashKit - * Copyright (C) 2010 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - */ - -#include - -uint32_t hashkit_digest(const hashkit_st *self, const char *key, size_t key_length) -{ - return self->base_hash.function(key, key_length, self->base_hash.context); -} - -uint32_t libhashkit_digest(const char *key, size_t key_length, hashkit_hash_algorithm_t hash_algorithm) -{ - switch (hash_algorithm) - { - case HASHKIT_HASH_DEFAULT: - return libhashkit_one_at_a_time(key, key_length); - case HASHKIT_HASH_MD5: - return libhashkit_md5(key, key_length); - case HASHKIT_HASH_CRC: - return libhashkit_crc32(key, key_length); - case HASHKIT_HASH_FNV1_64: - return libhashkit_fnv1_64(key, key_length); - case HASHKIT_HASH_FNV1A_64: - return libhashkit_fnv1a_64(key, key_length); - case HASHKIT_HASH_FNV1_32: - return libhashkit_fnv1_32(key, key_length); - case HASHKIT_HASH_FNV1A_32: - return libhashkit_fnv1a_32(key, key_length); - case HASHKIT_HASH_HSIEH: -#ifdef HAVE_HSIEH_HASH - return libhashkit_hsieh(key, key_length); -#else - return 1; -#endif - case HASHKIT_HASH_MURMUR: -#ifdef HAVE_MURMUR_HASH - return libhashkit_murmur(key, key_length); -#else - return 1; -#endif - case HASHKIT_HASH_JENKINS: - return libhashkit_jenkins(key, key_length); - case HASHKIT_HASH_CUSTOM: - case HASHKIT_HASH_MAX: - default: -#ifdef HAVE_DEBUG - fprintf(stderr, "hashkit_hash_t was extended but libhashkit_generate_value was not updated\n"); - fflush(stderr); - assert(0); -#endif - break; - } - - return 1; -} diff --git a/libhashkit/digest.cc b/libhashkit/digest.cc new file mode 100644 index 00000000..e1559819 --- /dev/null +++ b/libhashkit/digest.cc @@ -0,0 +1,60 @@ +/* HashKit + * Copyright (C) 2010 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + */ + +#include + +uint32_t hashkit_digest(const hashkit_st *self, const char *key, size_t key_length) +{ + return self->base_hash.function(key, key_length, self->base_hash.context); +} + +uint32_t libhashkit_digest(const char *key, size_t key_length, hashkit_hash_algorithm_t hash_algorithm) +{ + switch (hash_algorithm) + { + case HASHKIT_HASH_DEFAULT: + return libhashkit_one_at_a_time(key, key_length); + case HASHKIT_HASH_MD5: + return libhashkit_md5(key, key_length); + case HASHKIT_HASH_CRC: + return libhashkit_crc32(key, key_length); + case HASHKIT_HASH_FNV1_64: + return libhashkit_fnv1_64(key, key_length); + case HASHKIT_HASH_FNV1A_64: + return libhashkit_fnv1a_64(key, key_length); + case HASHKIT_HASH_FNV1_32: + return libhashkit_fnv1_32(key, key_length); + case HASHKIT_HASH_FNV1A_32: + return libhashkit_fnv1a_32(key, key_length); + case HASHKIT_HASH_HSIEH: +#ifdef HAVE_HSIEH_HASH + return libhashkit_hsieh(key, key_length); +#else + return 1; +#endif + case HASHKIT_HASH_MURMUR: +#ifdef HAVE_MURMUR_HASH + return libhashkit_murmur(key, key_length); +#else + return 1; +#endif + case HASHKIT_HASH_JENKINS: + return libhashkit_jenkins(key, key_length); + case HASHKIT_HASH_CUSTOM: + case HASHKIT_HASH_MAX: + default: +#ifdef HAVE_DEBUG + fprintf(stderr, "hashkit_hash_t was extended but libhashkit_generate_value was not updated\n"); + fflush(stderr); + assert(0); +#endif + break; + } + + return 1; +} diff --git a/libhashkit/fnv.c b/libhashkit/fnv.c deleted file mode 100644 index fffb94a4..00000000 --- a/libhashkit/fnv.c +++ /dev/null @@ -1,75 +0,0 @@ -/* HashKit - * Copyright (C) 2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - */ - -#include - -/* FNV hash'es lifted from Dustin Sallings work */ -static uint64_t FNV_64_INIT= UINT64_C(0xcbf29ce484222325); -static uint64_t FNV_64_PRIME= UINT64_C(0x100000001b3); -static uint32_t FNV_32_INIT= 2166136261UL; -static uint32_t FNV_32_PRIME= 16777619; - -uint32_t hashkit_fnv1_64(const char *key, size_t key_length, void *context) -{ - /* Thanks to pierre@demartines.com for the pointer */ - uint64_t hash= FNV_64_INIT; - (void)context; - - for (size_t x= 0; x < key_length; x++) - { - hash *= FNV_64_PRIME; - hash ^= (uint64_t)key[x]; - } - - return (uint32_t)hash; -} - -uint32_t hashkit_fnv1a_64(const char *key, size_t key_length, void *context) -{ - uint32_t hash= (uint32_t) FNV_64_INIT; - (void)context; - - for (size_t x= 0; x < key_length; x++) - { - uint32_t val= (uint32_t)key[x]; - hash ^= val; - hash *= (uint32_t) FNV_64_PRIME; - } - - return hash; -} - -uint32_t hashkit_fnv1_32(const char *key, size_t key_length, void *context) -{ - uint32_t hash= FNV_32_INIT; - (void)context; - - for (size_t x= 0; x < key_length; x++) - { - uint32_t val= (uint32_t)key[x]; - hash *= FNV_32_PRIME; - hash ^= val; - } - - return hash; -} - -uint32_t hashkit_fnv1a_32(const char *key, size_t key_length, void *context) -{ - uint32_t hash= FNV_32_INIT; - (void)context; - - for (size_t x= 0; x < key_length; x++) - { - uint32_t val= (uint32_t)key[x]; - hash ^= val; - hash *= FNV_32_PRIME; - } - - return hash; -} diff --git a/libhashkit/fnv.cc b/libhashkit/fnv.cc new file mode 100644 index 00000000..fffb94a4 --- /dev/null +++ b/libhashkit/fnv.cc @@ -0,0 +1,75 @@ +/* HashKit + * Copyright (C) 2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + */ + +#include + +/* FNV hash'es lifted from Dustin Sallings work */ +static uint64_t FNV_64_INIT= UINT64_C(0xcbf29ce484222325); +static uint64_t FNV_64_PRIME= UINT64_C(0x100000001b3); +static uint32_t FNV_32_INIT= 2166136261UL; +static uint32_t FNV_32_PRIME= 16777619; + +uint32_t hashkit_fnv1_64(const char *key, size_t key_length, void *context) +{ + /* Thanks to pierre@demartines.com for the pointer */ + uint64_t hash= FNV_64_INIT; + (void)context; + + for (size_t x= 0; x < key_length; x++) + { + hash *= FNV_64_PRIME; + hash ^= (uint64_t)key[x]; + } + + return (uint32_t)hash; +} + +uint32_t hashkit_fnv1a_64(const char *key, size_t key_length, void *context) +{ + uint32_t hash= (uint32_t) FNV_64_INIT; + (void)context; + + for (size_t x= 0; x < key_length; x++) + { + uint32_t val= (uint32_t)key[x]; + hash ^= val; + hash *= (uint32_t) FNV_64_PRIME; + } + + return hash; +} + +uint32_t hashkit_fnv1_32(const char *key, size_t key_length, void *context) +{ + uint32_t hash= FNV_32_INIT; + (void)context; + + for (size_t x= 0; x < key_length; x++) + { + uint32_t val= (uint32_t)key[x]; + hash *= FNV_32_PRIME; + hash ^= val; + } + + return hash; +} + +uint32_t hashkit_fnv1a_32(const char *key, size_t key_length, void *context) +{ + uint32_t hash= FNV_32_INIT; + (void)context; + + for (size_t x= 0; x < key_length; x++) + { + uint32_t val= (uint32_t)key[x]; + hash ^= val; + hash *= FNV_32_PRIME; + } + + return hash; +} diff --git a/libhashkit/function.c b/libhashkit/function.c deleted file mode 100644 index 3560abd7..00000000 --- a/libhashkit/function.c +++ /dev/null @@ -1,156 +0,0 @@ -/* HashKit - * Copyright (C) 2010 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - */ - -#include - -static hashkit_return_t _set_function(struct hashkit_function_st *self, hashkit_hash_algorithm_t hash_algorithm) -{ - switch (hash_algorithm) - { - case HASHKIT_HASH_DEFAULT: - self->function= hashkit_one_at_a_time; - break; - case HASHKIT_HASH_MD5: - self->function= hashkit_md5; - break; - case HASHKIT_HASH_CRC: - self->function= hashkit_crc32; - break; - case HASHKIT_HASH_FNV1_64: - self->function= hashkit_fnv1_64; - break; - case HASHKIT_HASH_FNV1A_64: - self->function= hashkit_fnv1a_64; - break; - case HASHKIT_HASH_FNV1_32: - self->function= hashkit_fnv1_32; - break; - case HASHKIT_HASH_FNV1A_32: - self->function= hashkit_fnv1a_32; - break; - case HASHKIT_HASH_HSIEH: -#ifdef HAVE_HSIEH_HASH - self->function= hashkit_hsieh; - break; -#else - return HASHKIT_FAILURE; -#endif - case HASHKIT_HASH_MURMUR: -#ifdef HAVE_MURMUR_HASH - self->function= hashkit_murmur; - break; -#else - return HASHKIT_FAILURE; -#endif - case HASHKIT_HASH_JENKINS: - self->function= hashkit_jenkins; - break; - case HASHKIT_HASH_CUSTOM: - return HASHKIT_INVALID_ARGUMENT; - case HASHKIT_HASH_MAX: - default: - return HASHKIT_INVALID_HASH; - } - - self->context= NULL; - - return HASHKIT_SUCCESS; -} - -hashkit_return_t hashkit_set_function(hashkit_st *self, hashkit_hash_algorithm_t hash_algorithm) -{ - return _set_function(&self->base_hash, hash_algorithm); -} - -hashkit_return_t hashkit_set_distribution_function(hashkit_st *self, hashkit_hash_algorithm_t hash_algorithm) -{ - return _set_function(&self->distribution_hash, hash_algorithm); -} - -static hashkit_return_t _set_custom_function(struct hashkit_function_st *self, hashkit_hash_fn function, void *context) -{ - if (function) - { - self->function= function; - self->context= context; - - return HASHKIT_SUCCESS; - } - - return HASHKIT_FAILURE; -} - -hashkit_return_t hashkit_set_custom_function(hashkit_st *self, hashkit_hash_fn function, void *context) -{ - return _set_custom_function(&self->base_hash, function, context); -} - -hashkit_return_t hashkit_set_custom_distribution_function(hashkit_st *self, hashkit_hash_fn function, void *context) -{ - return _set_custom_function(&self->distribution_hash, function, context); -} - -static hashkit_hash_algorithm_t get_function_type(const hashkit_hash_fn function) -{ - if (function == hashkit_one_at_a_time) - { - return HASHKIT_HASH_DEFAULT; - } - else if (function == hashkit_md5) - { - return HASHKIT_HASH_MD5; - } - else if (function == hashkit_crc32) - { - return HASHKIT_HASH_CRC; - } - else if (function == hashkit_fnv1_64) - { - return HASHKIT_HASH_FNV1_64; - } - else if (function == hashkit_fnv1a_64) - { - return HASHKIT_HASH_FNV1A_64; - } - else if (function == hashkit_fnv1_32) - { - return HASHKIT_HASH_FNV1_32; - } - else if (function == hashkit_fnv1a_32) - { - return HASHKIT_HASH_FNV1A_32; - } -#ifdef HAVE_HSIEH_HASH - else if (function == hashkit_hsieh) - { - return HASHKIT_HASH_HSIEH; - } -#endif -#ifdef HAVE_MURMUR_HASH - else if (function == hashkit_murmur) - { - return HASHKIT_HASH_MURMUR; - } -#endif - else if (function == hashkit_jenkins) - { - return HASHKIT_HASH_JENKINS; - } - - return HASHKIT_HASH_CUSTOM; -} - -hashkit_hash_algorithm_t hashkit_get_function(const hashkit_st *self) -{ - return get_function_type(self->base_hash.function); -} - -hashkit_hash_algorithm_t hashkit_get_distribution_function(const hashkit_st *self) -{ - return get_function_type(self->distribution_hash.function); -} diff --git a/libhashkit/function.cc b/libhashkit/function.cc new file mode 100644 index 00000000..7ac91007 --- /dev/null +++ b/libhashkit/function.cc @@ -0,0 +1,156 @@ +/* HashKit + * Copyright (C) 2010 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + */ + +#include + +static hashkit_return_t _set_function(struct hashkit_st::hashkit_function_st *self, hashkit_hash_algorithm_t hash_algorithm) +{ + switch (hash_algorithm) + { + case HASHKIT_HASH_DEFAULT: + self->function= hashkit_one_at_a_time; + break; + case HASHKIT_HASH_MD5: + self->function= hashkit_md5; + break; + case HASHKIT_HASH_CRC: + self->function= hashkit_crc32; + break; + case HASHKIT_HASH_FNV1_64: + self->function= hashkit_fnv1_64; + break; + case HASHKIT_HASH_FNV1A_64: + self->function= hashkit_fnv1a_64; + break; + case HASHKIT_HASH_FNV1_32: + self->function= hashkit_fnv1_32; + break; + case HASHKIT_HASH_FNV1A_32: + self->function= hashkit_fnv1a_32; + break; + case HASHKIT_HASH_HSIEH: +#ifdef HAVE_HSIEH_HASH + self->function= hashkit_hsieh; + break; +#else + return HASHKIT_FAILURE; +#endif + case HASHKIT_HASH_MURMUR: +#ifdef HAVE_MURMUR_HASH + self->function= hashkit_murmur; + break; +#else + return HASHKIT_FAILURE; +#endif + case HASHKIT_HASH_JENKINS: + self->function= hashkit_jenkins; + break; + case HASHKIT_HASH_CUSTOM: + return HASHKIT_INVALID_ARGUMENT; + case HASHKIT_HASH_MAX: + default: + return HASHKIT_INVALID_HASH; + } + + self->context= NULL; + + return HASHKIT_SUCCESS; +} + +hashkit_return_t hashkit_set_function(hashkit_st *self, hashkit_hash_algorithm_t hash_algorithm) +{ + return _set_function(&self->base_hash, hash_algorithm); +} + +hashkit_return_t hashkit_set_distribution_function(hashkit_st *self, hashkit_hash_algorithm_t hash_algorithm) +{ + return _set_function(&self->distribution_hash, hash_algorithm); +} + +static hashkit_return_t _set_custom_function(struct hashkit_st::hashkit_function_st *self, hashkit_hash_fn function, void *context) +{ + if (function) + { + self->function= function; + self->context= context; + + return HASHKIT_SUCCESS; + } + + return HASHKIT_FAILURE; +} + +hashkit_return_t hashkit_set_custom_function(hashkit_st *self, hashkit_hash_fn function, void *context) +{ + return _set_custom_function(&self->base_hash, function, context); +} + +hashkit_return_t hashkit_set_custom_distribution_function(hashkit_st *self, hashkit_hash_fn function, void *context) +{ + return _set_custom_function(&self->distribution_hash, function, context); +} + +static hashkit_hash_algorithm_t get_function_type(const hashkit_hash_fn function) +{ + if (function == hashkit_one_at_a_time) + { + return HASHKIT_HASH_DEFAULT; + } + else if (function == hashkit_md5) + { + return HASHKIT_HASH_MD5; + } + else if (function == hashkit_crc32) + { + return HASHKIT_HASH_CRC; + } + else if (function == hashkit_fnv1_64) + { + return HASHKIT_HASH_FNV1_64; + } + else if (function == hashkit_fnv1a_64) + { + return HASHKIT_HASH_FNV1A_64; + } + else if (function == hashkit_fnv1_32) + { + return HASHKIT_HASH_FNV1_32; + } + else if (function == hashkit_fnv1a_32) + { + return HASHKIT_HASH_FNV1A_32; + } +#ifdef HAVE_HSIEH_HASH + else if (function == hashkit_hsieh) + { + return HASHKIT_HASH_HSIEH; + } +#endif +#ifdef HAVE_MURMUR_HASH + else if (function == hashkit_murmur) + { + return HASHKIT_HASH_MURMUR; + } +#endif + else if (function == hashkit_jenkins) + { + return HASHKIT_HASH_JENKINS; + } + + return HASHKIT_HASH_CUSTOM; +} + +hashkit_hash_algorithm_t hashkit_get_function(const hashkit_st *self) +{ + return get_function_type(self->base_hash.function); +} + +hashkit_hash_algorithm_t hashkit_get_distribution_function(const hashkit_st *self) +{ + return get_function_type(self->distribution_hash.function); +} diff --git a/libhashkit/hashkit.c b/libhashkit/hashkit.c deleted file mode 100644 index 7214c144..00000000 --- a/libhashkit/hashkit.c +++ /dev/null @@ -1,108 +0,0 @@ -/* HashKit - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - */ - -#include - -static const hashkit_st global_default_hash= { - .base_hash= { - .function= hashkit_one_at_a_time, - .context= NULL - }, - .flags= { - .is_base_same_distributed= false, - } -}; - -static inline bool _hashkit_init(hashkit_st *self) -{ - self->base_hash= global_default_hash.base_hash; - self->distribution_hash= global_default_hash.base_hash; - self->flags= global_default_hash.flags; - - return true; -} - -static inline hashkit_st *_hashkit_create(hashkit_st *self) -{ - if (self == NULL) - { - self= (hashkit_st *)malloc(sizeof(hashkit_st)); - if (self == NULL) - { - return NULL; - } - - self->options.is_allocated= true; - } - else - { - self->options.is_allocated= false; - } - - return self; -} - -hashkit_st *hashkit_create(hashkit_st *self) -{ - self= _hashkit_create(self); - if (! self) - return self; - - if (! _hashkit_init(self)) - { - hashkit_free(self); - } - - return self; -} - - -void hashkit_free(hashkit_st *self) -{ - if (hashkit_is_allocated(self)) - { - free(self); - } -} - -hashkit_st *hashkit_clone(hashkit_st *destination, const hashkit_st *source) -{ - if (source == NULL) - { - return hashkit_create(destination); - } - - /* new_clone will be a pointer to destination */ - destination= _hashkit_create(destination); - - // Should only happen on allocation failure. - if (destination == NULL) - { - return NULL; - } - - destination->base_hash= source->base_hash; - destination->distribution_hash= source->distribution_hash; - destination->flags= source->flags; - - return destination; -} - -bool hashkit_compare(const hashkit_st *first, const hashkit_st *second) -{ - if (first->base_hash.function == second->base_hash.function && - first->base_hash.context == second->base_hash.context && - first->distribution_hash.function == second->distribution_hash.function && - first->distribution_hash.context == second->distribution_hash.context && - first->flags.is_base_same_distributed == second->flags.is_base_same_distributed) - { - return true; - } - - return false; -} diff --git a/libhashkit/hashkit.cc b/libhashkit/hashkit.cc new file mode 100644 index 00000000..201a6dff --- /dev/null +++ b/libhashkit/hashkit.cc @@ -0,0 +1,99 @@ +/* HashKit + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + */ + +#include + +static inline bool _hashkit_init(hashkit_st *self) +{ + self->base_hash.function= hashkit_one_at_a_time; + self->base_hash.context= NULL; + self->distribution_hash.function= self->base_hash.function; + self->flags.is_base_same_distributed= false; + + return true; +} + +static inline hashkit_st *_hashkit_create(hashkit_st *self) +{ + if (self == NULL) + { + self= (hashkit_st *)malloc(sizeof(hashkit_st)); + if (self == NULL) + { + return NULL; + } + + self->options.is_allocated= true; + } + else + { + self->options.is_allocated= false; + } + + return self; +} + +hashkit_st *hashkit_create(hashkit_st *self) +{ + self= _hashkit_create(self); + if (! self) + return self; + + if (! _hashkit_init(self)) + { + hashkit_free(self); + } + + return self; +} + + +void hashkit_free(hashkit_st *self) +{ + if (hashkit_is_allocated(self)) + { + free(self); + } +} + +hashkit_st *hashkit_clone(hashkit_st *destination, const hashkit_st *source) +{ + if (source == NULL) + { + return hashkit_create(destination); + } + + /* new_clone will be a pointer to destination */ + destination= _hashkit_create(destination); + + // Should only happen on allocation failure. + if (destination == NULL) + { + return NULL; + } + + destination->base_hash= source->base_hash; + destination->distribution_hash= source->distribution_hash; + destination->flags= source->flags; + + return destination; +} + +bool hashkit_compare(const hashkit_st *first, const hashkit_st *second) +{ + if (first->base_hash.function == second->base_hash.function && + first->base_hash.context == second->base_hash.context && + first->distribution_hash.function == second->distribution_hash.function && + first->distribution_hash.context == second->distribution_hash.context && + first->flags.is_base_same_distributed == second->flags.is_base_same_distributed) + { + return true; + } + + return false; +} diff --git a/libhashkit/hashkit.h b/libhashkit/hashkit.h index 2d8ad3a2..9b8761f1 100644 --- a/libhashkit/hashkit.h +++ b/libhashkit/hashkit.h @@ -1,13 +1,42 @@ -/* HashKit - * Copyright (C) 2009-2010 Brian Aker - * All rights reserved. +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * HashKit library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2009-2010 Brian Aker 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. */ -#ifndef HASHKIT_H -#define HASHKIT_H + +#pragma once #if !defined(__cplusplus) @@ -15,6 +44,7 @@ #endif #include #include + #include #include #include @@ -25,29 +55,6 @@ #include #include -#ifdef __cplusplus -extern "C" { -#endif - -HASHKIT_API -hashkit_st *hashkit_create(hashkit_st *hash); - -HASHKIT_API -hashkit_st *hashkit_clone(hashkit_st *destination, const hashkit_st *ptr); - -HASHKIT_API -bool hashkit_compare(const hashkit_st *first, const hashkit_st *second); - -HASHKIT_API -void hashkit_free(hashkit_st *hash); - -#define hashkit_is_allocated(__object) ((__object)->options.is_allocated) -#define hashkit_is_initialized(__object) ((__object)->options.is_initialized) - -#ifdef __cplusplus -} // extern "C" -#endif - struct hashkit_st { struct hashkit_function_st { @@ -65,65 +72,24 @@ struct hashkit_st }; #ifdef __cplusplus +extern "C" { +#endif -#include - -class Hashkit { - -public: - - Hashkit() - { - hashkit_create(&self); - } - - Hashkit(const Hashkit& source) - { - hashkit_clone(&self, &source.self); - } - - Hashkit& operator=(const Hashkit& source) - { - hashkit_free(&self); - hashkit_clone(&self, &source.self); - - return *this; - } - - friend bool operator==(const Hashkit &left, const Hashkit &right) - { - return hashkit_compare(&left.self, &right.self); - } - - uint32_t digest(std::string& str) - { - return hashkit_digest(&self, str.c_str(), str.length()); - } +HASHKIT_API +hashkit_st *hashkit_create(hashkit_st *hash); - uint32_t digest(const char *key, size_t key_length) - { - return hashkit_digest(&self, key, key_length); - } +HASHKIT_API +hashkit_st *hashkit_clone(hashkit_st *destination, const hashkit_st *ptr); - hashkit_return_t set_function(hashkit_hash_algorithm_t hash_algorithm) - { - return hashkit_set_function(&self, hash_algorithm); - } +HASHKIT_API +bool hashkit_compare(const hashkit_st *first, const hashkit_st *second); - hashkit_return_t set_distribution_function(hashkit_hash_algorithm_t hash_algorithm) - { - return hashkit_set_function(&self, hash_algorithm); - } +HASHKIT_API +void hashkit_free(hashkit_st *hash); - ~Hashkit() - { - hashkit_free(&self); - } -private: +#define hashkit_is_allocated(__object) ((__object)->options.is_allocated) +#define hashkit_is_initialized(__object) ((__object)->options.is_initialized) - hashkit_st self; -}; +#ifdef __cplusplus +} // extern "C" #endif - - -#endif /* HASHKIT_H */ diff --git a/libhashkit/hashkit.hpp b/libhashkit/hashkit.hpp new file mode 100644 index 00000000..7ead63d0 --- /dev/null +++ b/libhashkit/hashkit.hpp @@ -0,0 +1,97 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2009 Brian Aker 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + +#pragma once + +#include +#include + +class Hashkit { + +public: + + Hashkit() + { + hashkit_create(&self); + } + + Hashkit(const Hashkit& source) + { + hashkit_clone(&self, &source.self); + } + + Hashkit& operator=(const Hashkit& source) + { + hashkit_free(&self); + hashkit_clone(&self, &source.self); + + return *this; + } + + friend bool operator==(const Hashkit &left, const Hashkit &right) + { + return hashkit_compare(&left.self, &right.self); + } + + uint32_t digest(std::string& str) + { + return hashkit_digest(&self, str.c_str(), str.length()); + } + + uint32_t digest(const char *key, size_t key_length) + { + return hashkit_digest(&self, key, key_length); + } + + hashkit_return_t set_function(hashkit_hash_algorithm_t hash_algorithm) + { + return hashkit_set_function(&self, hash_algorithm); + } + + hashkit_return_t set_distribution_function(hashkit_hash_algorithm_t hash_algorithm) + { + return hashkit_set_function(&self, hash_algorithm); + } + + ~Hashkit() + { + hashkit_free(&self); + } +private: + + hashkit_st self; +}; diff --git a/libhashkit/hsieh.c b/libhashkit/hsieh.c deleted file mode 100644 index 35a2e209..00000000 --- a/libhashkit/hsieh.c +++ /dev/null @@ -1,70 +0,0 @@ -/* By Paul Hsieh (C) 2004, 2005. Covered under the Paul Hsieh - * derivative license. - * See: http://www.azillionmonkeys.com/qed/weblicense.html for license - * details. - * http://www.azillionmonkeys.com/qed/hash.html -*/ - -#include - -#undef get16bits -#if (defined(__GNUC__) && defined(__i386__)) -#define get16bits(d) (*((const uint16_t *) (d))) -#endif - -#if !defined (get16bits) -#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ - +(uint32_t)(((const uint8_t *)(d))[0]) ) -#endif - -uint32_t hashkit_hsieh(const char *key, size_t key_length, void *context __attribute__((unused))) -{ - uint32_t hash = 0, tmp; - int rem; - - if (key_length <= 0 || key == NULL) - return 0; - - rem = key_length & 3; - key_length >>= 2; - - /* Main loop */ - for (;key_length > 0; key_length--) - { - hash += get16bits (key); - tmp = (get16bits (key+2) << 11) ^ hash; - hash = (hash << 16) ^ tmp; - key += 2*sizeof (uint16_t); - hash += hash >> 11; - } - - /* Handle end cases */ - switch (rem) - { - case 3: hash += get16bits (key); - hash ^= hash << 16; - hash ^= (uint32_t)key[sizeof (uint16_t)] << 18; - hash += hash >> 11; - break; - case 2: hash += get16bits (key); - hash ^= hash << 11; - hash += hash >> 17; - break; - case 1: hash += (unsigned char)(*key); - hash ^= hash << 10; - hash += hash >> 1; - default: - break; - } - - /* Force "avalanching" of final 127 bits */ - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; - - return hash; -} - diff --git a/libhashkit/hsieh.cc b/libhashkit/hsieh.cc new file mode 100644 index 00000000..35a2e209 --- /dev/null +++ b/libhashkit/hsieh.cc @@ -0,0 +1,70 @@ +/* By Paul Hsieh (C) 2004, 2005. Covered under the Paul Hsieh + * derivative license. + * See: http://www.azillionmonkeys.com/qed/weblicense.html for license + * details. + * http://www.azillionmonkeys.com/qed/hash.html +*/ + +#include + +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif + +uint32_t hashkit_hsieh(const char *key, size_t key_length, void *context __attribute__((unused))) +{ + uint32_t hash = 0, tmp; + int rem; + + if (key_length <= 0 || key == NULL) + return 0; + + rem = key_length & 3; + key_length >>= 2; + + /* Main loop */ + for (;key_length > 0; key_length--) + { + hash += get16bits (key); + tmp = (get16bits (key+2) << 11) ^ hash; + hash = (hash << 16) ^ tmp; + key += 2*sizeof (uint16_t); + hash += hash >> 11; + } + + /* Handle end cases */ + switch (rem) + { + case 3: hash += get16bits (key); + hash ^= hash << 16; + hash ^= (uint32_t)key[sizeof (uint16_t)] << 18; + hash += hash >> 11; + break; + case 2: hash += get16bits (key); + hash ^= hash << 11; + hash += hash >> 17; + break; + case 1: hash += (unsigned char)(*key); + hash ^= hash << 10; + hash += hash >> 1; + default: + break; + } + + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return hash; +} + diff --git a/libhashkit/include.am b/libhashkit/include.am index 12575dfb..43300585 100644 --- a/libhashkit/include.am +++ b/libhashkit/include.am @@ -22,6 +22,7 @@ nobase_include_HEADERS+= \ libhashkit/digest.h \ libhashkit/function.h \ libhashkit/hashkit.h \ + libhashkit/hashkit.hpp \ libhashkit/strerror.h \ libhashkit/str_algorithm.h \ libhashkit/types.h \ @@ -31,31 +32,31 @@ noinst_HEADERS+= \ libhashkit/common.h libhashkit_libhashkit_la_SOURCES= \ - libhashkit/algorithm.c \ - libhashkit/behavior.c \ - libhashkit/crc32.c \ - libhashkit/digest.c \ - libhashkit/fnv.c \ - libhashkit/function.c \ - libhashkit/hashkit.c \ - libhashkit/jenkins.c \ - libhashkit/ketama.c \ - libhashkit/md5.c \ - libhashkit/one_at_a_time.c \ - libhashkit/str_algorithm.c \ - libhashkit/strerror.c + libhashkit/algorithm.cc \ + libhashkit/behavior.cc \ + libhashkit/crc32.cc \ + libhashkit/digest.cc \ + libhashkit/fnv.cc \ + libhashkit/function.cc \ + libhashkit/hashkit.cc \ + libhashkit/jenkins.cc \ + libhashkit/ketama.cc \ + libhashkit/md5.cc \ + libhashkit/one_at_a_time.cc \ + libhashkit/str_algorithm.cc \ + libhashkit/strerror.cc if INCLUDE_HSIEH_SRC -libhashkit_libhashkit_la_SOURCES+= libhashkit/hsieh.c +libhashkit_libhashkit_la_SOURCES+= libhashkit/hsieh.cc endif if INCLUDE_MURMUR_SRC -libhashkit_libhashkit_la_SOURCES+= libhashkit/murmur.c +libhashkit_libhashkit_la_SOURCES+= libhashkit/murmur.cc endif -libhashkit_libhashkit_la_CFLAGS= \ - ${AM_CFLAGS} \ - -DBUILDING_HASHKIT +libhashkit_libhashkit_la_CXXFLAGS= \ + ${AM_CXXFLAGS} \ + -DBUILDING_HASHKIT libhashkit_libhashkit_la_LDFLAGS= \ $(LIBM) \ diff --git a/libhashkit/jenkins.c b/libhashkit/jenkins.c deleted file mode 100644 index c2001cb5..00000000 --- a/libhashkit/jenkins.c +++ /dev/null @@ -1,214 +0,0 @@ -/* -* -* By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this -* code any way you wish, private, educational, or commercial. It's free. -* Use for hash table lookup, or anything where one collision in 2^^32 is -* acceptable. Do NOT use for cryptographic purposes. -* http://burtleburtle.net/bob/hash/index.html -* -* Modified by Brian Pontz for libmemcached -* TODO: -* Add big endian support -*/ - -#include - -#define hashsize(n) ((uint32_t)1<<(n)) -#define hashmask(n) (hashsize(n)-1) -#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) - -#define mix(a,b,c) \ -{ \ - a -= c; a ^= rot(c, 4); c += b; \ - b -= a; b ^= rot(a, 6); a += c; \ - c -= b; c ^= rot(b, 8); b += a; \ - a -= c; a ^= rot(c,16); c += b; \ - b -= a; b ^= rot(a,19); a += c; \ - c -= b; c ^= rot(b, 4); b += a; \ -} - -#define final(a,b,c) \ -{ \ - c ^= b; c -= rot(b,14); \ - a ^= c; a -= rot(c,11); \ - b ^= a; b -= rot(a,25); \ - c ^= b; c -= rot(b,16); \ - a ^= c; a -= rot(c,4); \ - b ^= a; b -= rot(a,14); \ - c ^= b; c -= rot(b,24); \ -} - -#define JENKINS_INITVAL 13 - -/* -jenkins_hash() -- hash a variable-length key into a 32-bit value - k : the key (the unaligned variable-length array of bytes) - length : the length of the key, counting by bytes - initval : can be any 4-byte value -Returns a 32-bit value. Every bit of the key affects every bit of -the return value. Two keys differing by one or two bits will have -totally different hash values. - -The best hash table sizes are powers of 2. There is no need to do -mod a prime (mod is sooo slow!). If you need less than 32 bits, -use a bitmask. For example, if you need only 10 bits, do - h = (h & hashmask(10)); -In which case, the hash table should have hashsize(10) elements. -*/ - -uint32_t hashkit_jenkins(const char *key, size_t length, void *context) -{ - uint32_t a,b,c; /* internal state */ - union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ - (void)context; - - /* Set up the internal state */ - a = b = c = 0xdeadbeef + ((uint32_t)length) + JENKINS_INITVAL; - - u.ptr = key; -#ifndef WORDS_BIGENDIAN - if ((u.i & 0x3) == 0) - { - const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ - - /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ - while (length > 12) - { - a += k[0]; - b += k[1]; - c += k[2]; - mix(a,b,c); - length -= 12; - k += 3; - } - - /*----------------------------- handle the last (probably partial) block */ - /* - * "k[2]&0xffffff" actually reads beyond the end of the string, but - * then masks off the part it's not allowed to read. Because the - * string is aligned, the masked-off tail is in the same word as the - * rest of the string. Every machine with memory protection I've seen - * does it on word boundaries, so is OK with this. But VALGRIND will - * still catch it and complain. The masking trick does make the hash - * noticably faster for short strings (like English words). - */ - switch(length) - { - case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; - case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; - case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; - case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; - case 8 : b+=k[1]; a+=k[0]; break; - case 7 : b+=k[1]&0xffffff; a+=k[0]; break; - case 6 : b+=k[1]&0xffff; a+=k[0]; break; - case 5 : b+=k[1]&0xff; a+=k[0]; break; - case 4 : a+=k[0]; break; - case 3 : a+=k[0]&0xffffff; break; - case 2 : a+=k[0]&0xffff; break; - case 1 : a+=k[0]&0xff; break; - case 0 : return c; /* zero length strings require no mixing */ - default: return c; - } - - } - else if ((u.i & 0x1) == 0) - { - const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ - const uint8_t *k8; - - /*--------------- all but last block: aligned reads and different mixing */ - while (length > 12) - { - a += k[0] + (((uint32_t)k[1])<<16); - b += k[2] + (((uint32_t)k[3])<<16); - c += k[4] + (((uint32_t)k[5])<<16); - mix(a,b,c); - length -= 12; - k += 6; - } - - /*----------------------------- handle the last (probably partial) block */ - k8 = (const uint8_t *)k; - switch(length) - { - case 12: c+=k[4]+(((uint32_t)k[5])<<16); - b+=k[2]+(((uint32_t)k[3])<<16); - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ - case 10: c+=k[4]; - b+=k[2]+(((uint32_t)k[3])<<16); - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 9 : c+=k8[8]; /* fall through */ - case 8 : b+=k[2]+(((uint32_t)k[3])<<16); - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ - case 6 : b+=k[2]; - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 5 : b+=k8[4]; /* fall through */ - case 4 : a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ - case 2 : a+=k[0]; - break; - case 1 : a+=k8[0]; - break; - case 0 : return c; /* zero length requires no mixing */ - default: return c; - } - - } - else - { /* need to read the key one byte at a time */ -#endif /* little endian */ - const uint8_t *k = (const uint8_t *)key; - - /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ - while (length > 12) - { - a += k[0]; - a += ((uint32_t)k[1])<<8; - a += ((uint32_t)k[2])<<16; - a += ((uint32_t)k[3])<<24; - b += k[4]; - b += ((uint32_t)k[5])<<8; - b += ((uint32_t)k[6])<<16; - b += ((uint32_t)k[7])<<24; - c += k[8]; - c += ((uint32_t)k[9])<<8; - c += ((uint32_t)k[10])<<16; - c += ((uint32_t)k[11])<<24; - mix(a,b,c); - length -= 12; - k += 12; - } - - /*-------------------------------- last block: affect all 32 bits of (c) */ - switch(length) /* all the case statements fall through */ - { - case 12: c+=((uint32_t)k[11])<<24; - case 11: c+=((uint32_t)k[10])<<16; - case 10: c+=((uint32_t)k[9])<<8; - case 9 : c+=k[8]; - case 8 : b+=((uint32_t)k[7])<<24; - case 7 : b+=((uint32_t)k[6])<<16; - case 6 : b+=((uint32_t)k[5])<<8; - case 5 : b+=k[4]; - case 4 : a+=((uint32_t)k[3])<<24; - case 3 : a+=((uint32_t)k[2])<<16; - case 2 : a+=((uint32_t)k[1])<<8; - case 1 : a+=k[0]; - break; - case 0 : return c; - default : return c; - } -#ifndef WORDS_BIGENDIAN - } -#endif - - final(a,b,c); - return c; -} diff --git a/libhashkit/jenkins.cc b/libhashkit/jenkins.cc new file mode 100644 index 00000000..c2001cb5 --- /dev/null +++ b/libhashkit/jenkins.cc @@ -0,0 +1,214 @@ +/* +* +* By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this +* code any way you wish, private, educational, or commercial. It's free. +* Use for hash table lookup, or anything where one collision in 2^^32 is +* acceptable. Do NOT use for cryptographic purposes. +* http://burtleburtle.net/bob/hash/index.html +* +* Modified by Brian Pontz for libmemcached +* TODO: +* Add big endian support +*/ + +#include + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +#define JENKINS_INITVAL 13 + +/* +jenkins_hash() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. +*/ + +uint32_t hashkit_jenkins(const char *key, size_t length, void *context) +{ + uint32_t a,b,c; /* internal state */ + union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ + (void)context; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + JENKINS_INITVAL; + + u.ptr = key; +#ifndef WORDS_BIGENDIAN + if ((u.i & 0x3) == 0) + { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + default: return c; + } + + } + else if ((u.i & 0x1) == 0) + { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + default: return c; + } + + } + else + { /* need to read the key one byte at a time */ +#endif /* little endian */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : return c; + default : return c; + } +#ifndef WORDS_BIGENDIAN + } +#endif + + final(a,b,c); + return c; +} diff --git a/libhashkit/ketama.c b/libhashkit/ketama.c deleted file mode 100644 index 45052c22..00000000 --- a/libhashkit/ketama.c +++ /dev/null @@ -1,164 +0,0 @@ -/* HashKit - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - */ - -#include -#include - -#if 0 -static uint32_t ketama_server_hash(const char *key, unsigned int key_length, int alignment) -{ - unsigned char results[16]; - - md5_signature((unsigned char*)key, key_length, results); - return ((uint32_t) (results[3 + alignment * 4] & 0xFF) << 24) - | ((uint32_t) (results[2 + alignment * 4] & 0xFF) << 16) - | ((uint32_t) (results[1 + alignment * 4] & 0xFF) << 8) - | (results[0 + alignment * 4] & 0xFF); -} - -static int continuum_points_cmp(const void *t1, const void *t2) -{ - hashkit_continuum_point_st *ct1= (hashkit_continuum_point_st *)t1; - hashkit_continuum_point_st *ct2= (hashkit_continuum_point_st *)t2; - - if (ct1->value == ct2->value) - return 0; - else if (ct1->value > ct2->value) - return 1; - else - return -1; -} - -int update_continuum(hashkit_st *hashkit) -{ - uint32_t count; - uint32_t continuum_index= 0; - uint32_t value; - uint32_t points_index; - uint32_t points_count= 0; - uint32_t points_per_server; - uint32_t points_per_hash; - uint64_t total_weight= 0; - uint32_t live_servers; - uint8_t *context; - - if (hashkit->active_fn != NULL || hashkit->weight_fn != NULL) - { - live_servers= 0; - - for (count= 0, context= hashkit->list; count < hashkit->list_size; - count++, context+= hashkit->context_size) - { - if (hashkit->active_fn != NULL) - { - if (hashkit->active_fn(context)) - live_servers++; - else - continue; - } - - if (hashkit->weight_fn != NULL) - total_weight+= hashkit->weight_fn(context); - } - } - - if (hashkit->active_fn == NULL) - live_servers= (uint32_t)hashkit->list_size; - - if (live_servers == 0) - return 0; - - if (hashkit->weight_fn == NULL) - { - points_per_server= HASHKIT_POINTS_PER_NODE; - points_per_hash= 1; - } - else - { - points_per_server= HASHKIT_POINTS_PER_NODE_WEIGHTED; - points_per_hash= 4; - } - - if (live_servers > hashkit->continuum_count) - { - hashkit_continuum_point_st *new_continuum; - - new_continuum= realloc(hashkit->continuum, - sizeof(hashkit_continuum_point_st) * - (live_servers + HASHKIT_CONTINUUM_ADDITION) * - points_per_server); - - if (new_continuum == NULL) - return ENOMEM; - - hashkit->continuum= new_continuum; - hashkit->continuum_count= live_servers + HASHKIT_CONTINUUM_ADDITION; - } - - for (count= 0, context= hashkit->list; count < hashkit->list_size; - count++, context+= hashkit->context_size) - { - if (hashkit->active_fn != NULL && hashkit->active_fn(context) == false) - continue; - - if (hashkit->weight_fn != NULL) - { - float pct = (float)hashkit->weight_fn(context) / (float)total_weight; - points_per_server= (uint32_t) ((floorf((float) (pct * HASHKIT_POINTS_PER_NODE_WEIGHTED / 4 * (float)live_servers + 0.0000000001))) * 4); - } - - for (points_index= 0; - points_index < points_per_server / points_per_hash; - points_index++) - { - char sort_host[HASHKIT_CONTINUUM_KEY_SIZE]= ""; - size_t sort_host_length; - - if (hashkit->continuum_key_fn == NULL) - { - sort_host_length= (size_t) snprintf(sort_host, HASHKIT_CONTINUUM_KEY_SIZE, "%u", - points_index); - } - else - { - sort_host_length= hashkit->continuum_key_fn(sort_host, HASHKIT_CONTINUUM_KEY_SIZE, - points_index, context); - } - - if (hashkit->weight_fn == NULL) - { - if (hashkit->continuum_hash_fn == NULL) - value= hashkit_default(sort_host, sort_host_length); - else - value= hashkit->continuum_hash_fn(sort_host, sort_host_length); - - hashkit->continuum[continuum_index].index= count; - hashkit->continuum[continuum_index++].value= value; - } - else - { - unsigned int i; - for (i = 0; i < points_per_hash; i++) - { - value= ketama_server_hash(sort_host, (uint32_t) sort_host_length, (int) i); - hashkit->continuum[continuum_index].index= count; - hashkit->continuum[continuum_index++].value= value; - } - } - } - - points_count+= points_per_server; - } - - hashkit->continuum_points_count= points_count; - qsort(hashkit->continuum, hashkit->continuum_points_count, sizeof(hashkit_continuum_point_st), - continuum_points_cmp); - - return 0; -} -#endif diff --git a/libhashkit/ketama.cc b/libhashkit/ketama.cc new file mode 100644 index 00000000..45052c22 --- /dev/null +++ b/libhashkit/ketama.cc @@ -0,0 +1,164 @@ +/* HashKit + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + */ + +#include +#include + +#if 0 +static uint32_t ketama_server_hash(const char *key, unsigned int key_length, int alignment) +{ + unsigned char results[16]; + + md5_signature((unsigned char*)key, key_length, results); + return ((uint32_t) (results[3 + alignment * 4] & 0xFF) << 24) + | ((uint32_t) (results[2 + alignment * 4] & 0xFF) << 16) + | ((uint32_t) (results[1 + alignment * 4] & 0xFF) << 8) + | (results[0 + alignment * 4] & 0xFF); +} + +static int continuum_points_cmp(const void *t1, const void *t2) +{ + hashkit_continuum_point_st *ct1= (hashkit_continuum_point_st *)t1; + hashkit_continuum_point_st *ct2= (hashkit_continuum_point_st *)t2; + + if (ct1->value == ct2->value) + return 0; + else if (ct1->value > ct2->value) + return 1; + else + return -1; +} + +int update_continuum(hashkit_st *hashkit) +{ + uint32_t count; + uint32_t continuum_index= 0; + uint32_t value; + uint32_t points_index; + uint32_t points_count= 0; + uint32_t points_per_server; + uint32_t points_per_hash; + uint64_t total_weight= 0; + uint32_t live_servers; + uint8_t *context; + + if (hashkit->active_fn != NULL || hashkit->weight_fn != NULL) + { + live_servers= 0; + + for (count= 0, context= hashkit->list; count < hashkit->list_size; + count++, context+= hashkit->context_size) + { + if (hashkit->active_fn != NULL) + { + if (hashkit->active_fn(context)) + live_servers++; + else + continue; + } + + if (hashkit->weight_fn != NULL) + total_weight+= hashkit->weight_fn(context); + } + } + + if (hashkit->active_fn == NULL) + live_servers= (uint32_t)hashkit->list_size; + + if (live_servers == 0) + return 0; + + if (hashkit->weight_fn == NULL) + { + points_per_server= HASHKIT_POINTS_PER_NODE; + points_per_hash= 1; + } + else + { + points_per_server= HASHKIT_POINTS_PER_NODE_WEIGHTED; + points_per_hash= 4; + } + + if (live_servers > hashkit->continuum_count) + { + hashkit_continuum_point_st *new_continuum; + + new_continuum= realloc(hashkit->continuum, + sizeof(hashkit_continuum_point_st) * + (live_servers + HASHKIT_CONTINUUM_ADDITION) * + points_per_server); + + if (new_continuum == NULL) + return ENOMEM; + + hashkit->continuum= new_continuum; + hashkit->continuum_count= live_servers + HASHKIT_CONTINUUM_ADDITION; + } + + for (count= 0, context= hashkit->list; count < hashkit->list_size; + count++, context+= hashkit->context_size) + { + if (hashkit->active_fn != NULL && hashkit->active_fn(context) == false) + continue; + + if (hashkit->weight_fn != NULL) + { + float pct = (float)hashkit->weight_fn(context) / (float)total_weight; + points_per_server= (uint32_t) ((floorf((float) (pct * HASHKIT_POINTS_PER_NODE_WEIGHTED / 4 * (float)live_servers + 0.0000000001))) * 4); + } + + for (points_index= 0; + points_index < points_per_server / points_per_hash; + points_index++) + { + char sort_host[HASHKIT_CONTINUUM_KEY_SIZE]= ""; + size_t sort_host_length; + + if (hashkit->continuum_key_fn == NULL) + { + sort_host_length= (size_t) snprintf(sort_host, HASHKIT_CONTINUUM_KEY_SIZE, "%u", + points_index); + } + else + { + sort_host_length= hashkit->continuum_key_fn(sort_host, HASHKIT_CONTINUUM_KEY_SIZE, + points_index, context); + } + + if (hashkit->weight_fn == NULL) + { + if (hashkit->continuum_hash_fn == NULL) + value= hashkit_default(sort_host, sort_host_length); + else + value= hashkit->continuum_hash_fn(sort_host, sort_host_length); + + hashkit->continuum[continuum_index].index= count; + hashkit->continuum[continuum_index++].value= value; + } + else + { + unsigned int i; + for (i = 0; i < points_per_hash; i++) + { + value= ketama_server_hash(sort_host, (uint32_t) sort_host_length, (int) i); + hashkit->continuum[continuum_index].index= count; + hashkit->continuum[continuum_index++].value= value; + } + } + } + + points_count+= points_per_server; + } + + hashkit->continuum_points_count= points_count; + qsort(hashkit->continuum, hashkit->continuum_points_count, sizeof(hashkit_continuum_point_st), + continuum_points_cmp); + + return 0; +} +#endif diff --git a/libhashkit/md5.c b/libhashkit/md5.c deleted file mode 100644 index 1af5e6c0..00000000 --- a/libhashkit/md5.c +++ /dev/null @@ -1,366 +0,0 @@ -/* - This Library has been modified from its original form by - Brian Aker (brian@tangent.org) - - See below for original Copyright. -*/ -/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm - */ - -/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. -*/ - -#include - -#include -#include - -/* POINTER defines a generic pointer type */ -typedef unsigned char *POINTER; - - -/* UINT4 defines a four byte word */ -typedef unsigned int UINT4; - - -/* MD5 context. */ -typedef struct { - UINT4 state[4]; /* state (ABCD) */ - UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ - unsigned char buffer[64]; /* input buffer */ -} MD5_CTX; - -static void MD5Init (MD5_CTX *context); /* context */ -static void MD5Update ( MD5_CTX *context, /* context */ - const unsigned char *input, /* input block */ - unsigned int inputLen); /* length of input block */ -static void MD5Final ( unsigned char digest[16], /* message digest */ - MD5_CTX *context); /* context */ - -/* Constants for MD5Transform routine. */ - -#define S11 7 -#define S12 12 -#define S13 17 -#define S14 22 -#define S21 5 -#define S22 9 -#define S23 14 -#define S24 20 -#define S31 4 -#define S32 11 -#define S33 16 -#define S34 23 -#define S41 6 -#define S42 10 -#define S43 15 -#define S44 21 - - -static void MD5Transform (UINT4 state[4], - unsigned char block[64]); -static void Encode (unsigned char *output, - UINT4 *input, - unsigned int len); -static void Decode(UINT4 *output, unsigned char *input, unsigned int len); - -static unsigned char PADDING[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* F, G, H and I are basic MD5 functions. - */ -#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) -#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) -#define H(x, y, z) ((x) ^ (y) ^ (z)) -#define I(x, y, z) ((y) ^ ((x) | (~z))) - -/* ROTATE_LEFT rotates x left n bits. - */ -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) - -/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. -Rotation is separate from addition to prevent recomputation. - */ -#define FF(a, b, c, d, x, s, ac) { \ - (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define GG(a, b, c, d, x, s, ac) { \ - (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define HH(a, b, c, d, x, s, ac) { \ - (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define II(a, b, c, d, x, s, ac) { \ - (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } - - -/* - Just a simple method for getting the signature - result must be == 16 -*/ -void md5_signature(const unsigned char *key, unsigned int length, unsigned char *result) -{ - MD5_CTX my_md5; - - MD5Init(&my_md5); - (void)MD5Update(&my_md5, key, length); - MD5Final(result, &my_md5); -} - -/* MD5 initialization. Begins an MD5 operation, writing a new context. - */ -static void MD5Init (MD5_CTX *context) /* context */ -{ - context->count[0] = context->count[1] = 0; - /* Load magic initialization constants. -*/ - context->state[0] = 0x67452301; - context->state[1] = 0xefcdab89; - context->state[2] = 0x98badcfe; - context->state[3] = 0x10325476; -} - -/* MD5 block update operation. Continues an MD5 message-digest - operation, processing another message block, and updating the - context. - */ - -static void MD5Update ( - MD5_CTX *context, /* context */ - const unsigned char *input, /* input block */ - unsigned int inputLen) /* length of input block */ -{ - unsigned int i, idx, partLen; - - /* Compute number of bytes mod 64 */ - idx = (unsigned int)((context->count[0] >> 3) & 0x3F); - - - /* Update number of bits */ - if ((context->count[0] += ((UINT4)inputLen << 3)) - < ((UINT4)inputLen << 3)) - context->count[1]++; - context->count[1] += ((UINT4)inputLen >> 29); - - partLen = 64 - idx; - - /* Transform as many times as possible. -*/ - if (inputLen >= partLen) { - memcpy((POINTER)&context->buffer[idx], (POINTER)input, partLen); - MD5Transform(context->state, context->buffer); - - for (i = partLen; i + 63 < inputLen; i += 64) - MD5Transform (context->state, (unsigned char *)&input[i]); - - idx = 0; - } - else - i = 0; - - /* Buffer remaining input */ - memcpy((POINTER)&context->buffer[idx], (POINTER)&input[i], - inputLen-i); -} - -/* MD5 finalization. Ends an MD5 message-digest operation, writing the - the message digest and zeroizing the context. - */ - -static void MD5Final ( - unsigned char digest[16], /* message digest */ - MD5_CTX *context) /* context */ -{ - unsigned char bits[8]; - unsigned int idx, padLen; - - /* Save number of bits */ - Encode (bits, context->count, 8); - - /* Pad out to 56 mod 64. -*/ - idx = (unsigned int)((context->count[0] >> 3) & 0x3f); - padLen = (idx < 56) ? (56 - idx) : (120 - idx); - MD5Update (context, PADDING, padLen); - - /* Append length (before padding) */ - MD5Update (context, bits, 8); - - /* Store state in digest */ - Encode (digest, context->state, 16); - - /* Zeroize sensitive information. -*/ - memset((POINTER)context, 0, sizeof (*context)); -} - -/* MD5 basic transformation. Transforms state based on block. - */ -static void MD5Transform ( - UINT4 state[4], - unsigned char block[64]) -{ - UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; - - Decode (x, block, 64); - - /* Round 1 */ - FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ - FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ - FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ - FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ - FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ - FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ - FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ - FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ - FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ - FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ - FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ - FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ - FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ - FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ - FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ - FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ - - /* Round 2 */ - GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ - GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ - GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ - GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ - GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ - GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ - GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ - GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ - GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ - GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ - GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ - GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ - GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ - GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ - GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ - GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ - - /* Round 3 */ - HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ - HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ - HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ - HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ - HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ - HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ - HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ - HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ - HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ - HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ - HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ - HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ - HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ - HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ - HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ - HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ - - /* Round 4 */ - II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ - II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ - II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ - II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ - II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ - II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ - II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ - II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ - II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ - II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ - II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ - II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ - II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ - II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ - II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ - II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ - - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - - /* Zeroize sensitive information. -*/ - memset((POINTER)x, 0, sizeof (x)); -} - -/* Encodes input (UINT4) into output (unsigned char). Assumes len is - a multiple of 4. - */ -static void Encode ( -unsigned char *output, -UINT4 *input, -unsigned int len) -{ - unsigned int i, j; - - for (i = 0, j = 0; j < len; i++, j += 4) { - output[j] = (unsigned char)(input[i] & 0xff); - output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); - output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); - output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); - } -} - - -/* Decodes input (unsigned char) into output (UINT4). Assumes len is - a multiple of 4. - */ -static void Decode ( -UINT4 *output, -unsigned char *input, -unsigned int len) -{ - unsigned int i, j; - - for (i = 0, j = 0; j < len; i++, j += 4) - output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | - (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); -} - -uint32_t hashkit_md5(const char *key, size_t key_length, void *context) -{ - unsigned char results[16]; - (void)context; - - md5_signature((unsigned char*)key, (unsigned int)key_length, results); - - return ((uint32_t) (results[3] & 0xFF) << 24) - | ((uint32_t) (results[2] & 0xFF) << 16) - | ((uint32_t) (results[1] & 0xFF) << 8) - | (results[0] & 0xFF); -} diff --git a/libhashkit/md5.cc b/libhashkit/md5.cc new file mode 100644 index 00000000..86822f54 --- /dev/null +++ b/libhashkit/md5.cc @@ -0,0 +1,367 @@ +/* + This Library has been modified from its original form by + Brian Aker (brian@tangent.org) + + See below for original Copyright. +*/ +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. +*/ + +#include + +#include +#include + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; +typedef const unsigned char *CONST_POINTER; + + +/* UINT4 defines a four byte word */ +typedef unsigned int UINT4; + + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +static void MD5Init (MD5_CTX *context); /* context */ +static void MD5Update ( MD5_CTX *context, /* context */ + const unsigned char *input, /* input block */ + unsigned int inputLen); /* length of input block */ +static void MD5Final ( unsigned char digest[16], /* message digest */ + MD5_CTX *context); /* context */ + +/* Constants for MD5Transform routine. */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + + +static void MD5Transform (UINT4 state[4], + const unsigned char block[64]); +static void Encode (unsigned char *output, + UINT4 *input, + unsigned int len); +static void Decode(UINT4 *output, const unsigned char *input, unsigned int len); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + + +/* + Just a simple method for getting the signature + result must be == 16 +*/ +void md5_signature(const unsigned char *key, unsigned int length, unsigned char *result) +{ + MD5_CTX my_md5; + + MD5Init(&my_md5); + (void)MD5Update(&my_md5, key, length); + MD5Final(result, &my_md5); +} + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +static void MD5Init (MD5_CTX *context) /* context */ +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. +*/ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ + +static void MD5Update ( + MD5_CTX *context, /* context */ + const unsigned char *input, /* input block */ + unsigned int inputLen) /* length of input block */ +{ + unsigned int i, idx, partLen; + + /* Compute number of bytes mod 64 */ + idx = (unsigned int)((context->count[0] >> 3) & 0x3F); + + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - idx; + + /* Transform as many times as possible. +*/ + if (inputLen >= partLen) { + memcpy((POINTER)&context->buffer[idx], (CONST_POINTER)input, partLen); + MD5Transform(context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, (CONST_POINTER)&input[i]); + + idx = 0; + } + else + i = 0; + + /* Buffer remaining input */ + memcpy((POINTER)&context->buffer[idx], (CONST_POINTER)&input[i], + inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ + +static void MD5Final ( + unsigned char digest[16], /* message digest */ + MD5_CTX *context) /* context */ +{ + unsigned char bits[8]; + unsigned int idx, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. +*/ + idx = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (idx < 56) ? (56 - idx) : (120 - idx); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. +*/ + memset((POINTER)context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. + */ +static void MD5Transform ( + UINT4 state[4], + const unsigned char block[64]) +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. +*/ + memset((POINTER)x, 0, sizeof (x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void Encode ( +unsigned char *output, +UINT4 *input, +unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void Decode ( + UINT4 *output, + const unsigned char *input, + unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +uint32_t hashkit_md5(const char *key, size_t key_length, void *context) +{ + unsigned char results[16]; + (void)context; + + md5_signature((unsigned char*)key, (unsigned int)key_length, results); + + return ((uint32_t) (results[3] & 0xFF) << 24) + | ((uint32_t) (results[2] & 0xFF) << 16) + | ((uint32_t) (results[1] & 0xFF) << 8) + | (results[0] & 0xFF); +} diff --git a/libhashkit/murmur.c b/libhashkit/murmur.c deleted file mode 100644 index a40b11b7..00000000 --- a/libhashkit/murmur.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - "Murmur" hash provided by Austin, tanjent@gmail.com - http://murmurhash.googlepages.com/ - - Note - This code makes a few assumptions about how your machine behaves - - - 1. We can read a 4-byte value from any address without crashing - 2. sizeof(int) == 4 - - And it has a few limitations - - 1. It will not work incrementally. - 2. It will not produce the same results on little-endian and big-endian - machines. - - Updated to murmur2 hash - BP -*/ - -#include - -uint32_t hashkit_murmur(const char *key, size_t length, void *context) -{ - /* - 'm' and 'r' are mixing constants generated offline. They're not - really 'magic', they just happen to work well. - */ - - const unsigned int m= 0x5bd1e995; - const uint32_t seed= (0xdeadbeef * (uint32_t)length); - const int r= 24; - - - // Initialize the hash to a 'random' value - - uint32_t h= seed ^ (uint32_t)length; - - // Mix 4 bytes at a time into the hash - - const unsigned char * data= (const unsigned char *)key; - (void)context; - - while(length >= 4) - { - unsigned int k = *(unsigned int *)data; - - k *= m; - k ^= k >> r; - k *= m; - - h *= m; - h ^= k; - - data += 4; - length -= 4; - } - - // Handle the last few bytes of the input array - - switch(length) - { - case 3: h ^= ((uint32_t)data[2]) << 16; - case 2: h ^= ((uint32_t)data[1]) << 8; - case 1: h ^= data[0]; - h *= m; - default: break; - }; - - /* - Do a few final mixes of the hash to ensure the last few bytes are - well-incorporated. - */ - - h ^= h >> 13; - h *= m; - h ^= h >> 15; - - return h; -} diff --git a/libhashkit/murmur.cc b/libhashkit/murmur.cc new file mode 100644 index 00000000..a40b11b7 --- /dev/null +++ b/libhashkit/murmur.cc @@ -0,0 +1,77 @@ +/* + "Murmur" hash provided by Austin, tanjent@gmail.com + http://murmurhash.googlepages.com/ + + Note - This code makes a few assumptions about how your machine behaves - + + 1. We can read a 4-byte value from any address without crashing + 2. sizeof(int) == 4 + + And it has a few limitations - + 1. It will not work incrementally. + 2. It will not produce the same results on little-endian and big-endian + machines. + + Updated to murmur2 hash - BP +*/ + +#include + +uint32_t hashkit_murmur(const char *key, size_t length, void *context) +{ + /* + 'm' and 'r' are mixing constants generated offline. They're not + really 'magic', they just happen to work well. + */ + + const unsigned int m= 0x5bd1e995; + const uint32_t seed= (0xdeadbeef * (uint32_t)length); + const int r= 24; + + + // Initialize the hash to a 'random' value + + uint32_t h= seed ^ (uint32_t)length; + + // Mix 4 bytes at a time into the hash + + const unsigned char * data= (const unsigned char *)key; + (void)context; + + while(length >= 4) + { + unsigned int k = *(unsigned int *)data; + + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + + data += 4; + length -= 4; + } + + // Handle the last few bytes of the input array + + switch(length) + { + case 3: h ^= ((uint32_t)data[2]) << 16; + case 2: h ^= ((uint32_t)data[1]) << 8; + case 1: h ^= data[0]; + h *= m; + default: break; + }; + + /* + Do a few final mixes of the hash to ensure the last few bytes are + well-incorporated. + */ + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} diff --git a/libhashkit/one_at_a_time.c b/libhashkit/one_at_a_time.c deleted file mode 100644 index aeb11b12..00000000 --- a/libhashkit/one_at_a_time.c +++ /dev/null @@ -1,34 +0,0 @@ -/* HashKit - * Copyright (C) 2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - */ - -/* - This has is Jenkin's "One at A time Hash". -http://en.wikipedia.org/wiki/Jenkins_hash_function -*/ - -#include - -uint32_t hashkit_one_at_a_time(const char *key, size_t key_length, void *context) -{ - const char *ptr= key; - uint32_t value= 0; - (void)context; - - while (key_length--) - { - uint32_t val= (uint32_t) *ptr++; - value += val; - value += (value << 10); - value ^= (value >> 6); - } - value += (value << 3); - value ^= (value >> 11); - value += (value << 15); - - return value; -} diff --git a/libhashkit/one_at_a_time.cc b/libhashkit/one_at_a_time.cc new file mode 100644 index 00000000..aeb11b12 --- /dev/null +++ b/libhashkit/one_at_a_time.cc @@ -0,0 +1,34 @@ +/* HashKit + * Copyright (C) 2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + */ + +/* + This has is Jenkin's "One at A time Hash". +http://en.wikipedia.org/wiki/Jenkins_hash_function +*/ + +#include + +uint32_t hashkit_one_at_a_time(const char *key, size_t key_length, void *context) +{ + const char *ptr= key; + uint32_t value= 0; + (void)context; + + while (key_length--) + { + uint32_t val= (uint32_t) *ptr++; + value += val; + value += (value << 10); + value ^= (value >> 6); + } + value += (value << 3); + value ^= (value >> 11); + value += (value << 15); + + return value; +} diff --git a/libhashkit/str_algorithm.c b/libhashkit/str_algorithm.c deleted file mode 100644 index 0a0613bc..00000000 --- a/libhashkit/str_algorithm.c +++ /dev/null @@ -1,57 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * HashKit - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * - * 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. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * 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. - * - */ - -#include - -const char * libhashkit_string_hash(hashkit_hash_algorithm_t type) -{ - switch(type) - { - case HASHKIT_HASH_DEFAULT: return "DEFAULT"; - case HASHKIT_HASH_MD5: return "MD5"; - case HASHKIT_HASH_CRC: return "CRC"; - case HASHKIT_HASH_FNV1_64: return "FNV1_64"; - case HASHKIT_HASH_FNV1A_64: return "FNV1A_64"; - case HASHKIT_HASH_FNV1_32: return "FNV1_32"; - case HASHKIT_HASH_FNV1A_32: return "FNV1A_32"; - case HASHKIT_HASH_HSIEH: return "HSIEH"; - case HASHKIT_HASH_MURMUR: return "MURMUR"; - case HASHKIT_HASH_JENKINS: return "JENKINS"; - case HASHKIT_HASH_CUSTOM: return "CUSTOM"; - default: - case HASHKIT_HASH_MAX: return "INVALID"; - } -} diff --git a/libhashkit/str_algorithm.cc b/libhashkit/str_algorithm.cc new file mode 100644 index 00000000..0a0613bc --- /dev/null +++ b/libhashkit/str_algorithm.cc @@ -0,0 +1,57 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * HashKit + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + +#include + +const char * libhashkit_string_hash(hashkit_hash_algorithm_t type) +{ + switch(type) + { + case HASHKIT_HASH_DEFAULT: return "DEFAULT"; + case HASHKIT_HASH_MD5: return "MD5"; + case HASHKIT_HASH_CRC: return "CRC"; + case HASHKIT_HASH_FNV1_64: return "FNV1_64"; + case HASHKIT_HASH_FNV1A_64: return "FNV1A_64"; + case HASHKIT_HASH_FNV1_32: return "FNV1_32"; + case HASHKIT_HASH_FNV1A_32: return "FNV1A_32"; + case HASHKIT_HASH_HSIEH: return "HSIEH"; + case HASHKIT_HASH_MURMUR: return "MURMUR"; + case HASHKIT_HASH_JENKINS: return "JENKINS"; + case HASHKIT_HASH_CUSTOM: return "CUSTOM"; + default: + case HASHKIT_HASH_MAX: return "INVALID"; + } +} diff --git a/libhashkit/strerror.c b/libhashkit/strerror.c deleted file mode 100644 index 8d7246c6..00000000 --- a/libhashkit/strerror.c +++ /dev/null @@ -1,25 +0,0 @@ -/* HashKit - * Copyright (C) 2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - */ - -#include - -const char *hashkit_strerror(hashkit_st *ptr, hashkit_return_t rc) -{ - (void)ptr; - switch (rc) - { - case HASHKIT_SUCCESS: return "SUCCESS"; - case HASHKIT_FAILURE: return "FAILURE"; - case HASHKIT_MEMORY_ALLOCATION_FAILURE: return "MEMORY ALLOCATION FAILURE"; - case HASHKIT_INVALID_ARGUMENT: return "INVALID ARGUMENT"; - case HASHKIT_INVALID_HASH: return "INVALID hashkit_hash_algorithm_t"; - case HASHKIT_MAXIMUM_RETURN: - default: - return "INVALID hashkit_return_t"; - } -} diff --git a/libhashkit/strerror.cc b/libhashkit/strerror.cc new file mode 100644 index 00000000..8d7246c6 --- /dev/null +++ b/libhashkit/strerror.cc @@ -0,0 +1,25 @@ +/* HashKit + * Copyright (C) 2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + */ + +#include + +const char *hashkit_strerror(hashkit_st *ptr, hashkit_return_t rc) +{ + (void)ptr; + switch (rc) + { + case HASHKIT_SUCCESS: return "SUCCESS"; + case HASHKIT_FAILURE: return "FAILURE"; + case HASHKIT_MEMORY_ALLOCATION_FAILURE: return "MEMORY ALLOCATION FAILURE"; + case HASHKIT_INVALID_ARGUMENT: return "INVALID ARGUMENT"; + case HASHKIT_INVALID_HASH: return "INVALID hashkit_hash_algorithm_t"; + case HASHKIT_MAXIMUM_RETURN: + default: + return "INVALID hashkit_return_t"; + } +} diff --git a/libhashkit/visibility.h b/libhashkit/visibility.h index 7691d4b5..b8f194c5 100644 --- a/libhashkit/visibility.h +++ b/libhashkit/visibility.h @@ -13,8 +13,7 @@ * @brief Visibility control macros */ -#ifndef HASHKIT_VISIBILITY_H -#define HASHKIT_VISIBILITY_H +#pragma once /** * @@ -47,5 +46,3 @@ # define HASHKIT_LOCAL # endif /* defined(_MSC_VER) */ #endif /* defined(BUILDING_HASHKIT) */ - -#endif /* HASHKIT_VISIBILITY_H */ diff --git a/libmemcached/allocators.c b/libmemcached/allocators.c deleted file mode 100644 index fe7b296c..00000000 --- a/libmemcached/allocators.c +++ /dev/null @@ -1,94 +0,0 @@ -#include "common.h" - -void _libmemcached_free(const memcached_st *ptr, void *mem, void *context) -{ - (void) ptr; - (void) context; - free(mem); -} - -void *_libmemcached_malloc(const memcached_st *ptr, size_t size, void *context) -{ - (void) ptr; - (void) context; - return malloc(size); -} - -void *_libmemcached_realloc(const memcached_st *ptr, void *mem, size_t size, void *context) -{ - (void) ptr; - (void) context; - return realloc(mem, size); -} - -void *_libmemcached_calloc(const memcached_st *ptr, size_t nelem, size_t size, void *context) -{ - if (ptr->allocators.malloc != _libmemcached_malloc) - { - void *ret = _libmemcached_malloc(ptr, nelem * size, context); - if (ret != NULL) - memset(ret, 0, nelem * size); - - return ret; - } - - return calloc(nelem, size); -} - -static const struct _allocators_st global_default_allocator= { - .calloc= _libmemcached_calloc, - .context= NULL, - .free= _libmemcached_free, - .malloc= _libmemcached_malloc, - .realloc= _libmemcached_realloc -}; - -struct _allocators_st memcached_allocators_return_default(void) -{ - return global_default_allocator; -} - -memcached_return_t memcached_set_memory_allocators(memcached_st *ptr, - memcached_malloc_fn mem_malloc, - memcached_free_fn mem_free, - memcached_realloc_fn mem_realloc, - memcached_calloc_fn mem_calloc, - void *context) -{ - /* All should be set, or none should be set */ - if (mem_malloc == NULL && mem_free == NULL && mem_realloc == NULL && mem_calloc == NULL) - { - ptr->allocators= memcached_allocators_return_default(); - } - else if (mem_malloc == NULL || mem_free == NULL || mem_realloc == NULL || mem_calloc == NULL) - { - return MEMCACHED_FAILURE; - } - else - { - ptr->allocators.malloc= mem_malloc; - ptr->allocators.free= mem_free; - ptr->allocators.realloc= mem_realloc; - ptr->allocators.calloc= mem_calloc; - ptr->allocators.context= context; - } - - return MEMCACHED_SUCCESS; -} - -void *memcached_get_memory_allocators_context(const memcached_st *ptr) -{ - return ptr->allocators.context; -} - -void memcached_get_memory_allocators(const memcached_st *ptr, - memcached_malloc_fn *mem_malloc, - memcached_free_fn *mem_free, - memcached_realloc_fn *mem_realloc, - memcached_calloc_fn *mem_calloc) -{ - *mem_malloc= ptr->allocators.malloc; - *mem_free= ptr->allocators.free; - *mem_realloc= ptr->allocators.realloc; - *mem_calloc= ptr->allocators.calloc; -} diff --git a/libmemcached/allocators.cc b/libmemcached/allocators.cc new file mode 100644 index 00000000..e716bee3 --- /dev/null +++ b/libmemcached/allocators.cc @@ -0,0 +1,87 @@ +#include "common.h" + +void _libmemcached_free(const memcached_st *ptr, void *mem, void *context) +{ + (void) ptr; + (void) context; + free(mem); +} + +void *_libmemcached_malloc(const memcached_st *ptr, size_t size, void *context) +{ + (void) ptr; + (void) context; + return malloc(size); +} + +void *_libmemcached_realloc(const memcached_st *ptr, void *mem, size_t size, void *context) +{ + (void) ptr; + (void) context; + return realloc(mem, size); +} + +void *_libmemcached_calloc(const memcached_st *ptr, size_t nelem, size_t size, void *context) +{ + if (ptr->allocators.malloc != _libmemcached_malloc) + { + void *ret = _libmemcached_malloc(ptr, nelem * size, context); + if (ret != NULL) + memset(ret, 0, nelem * size); + + return ret; + } + + return calloc(nelem, size); +} + +struct memcached_allocator_t memcached_allocators_return_default(void) +{ + static struct memcached_allocator_t global_default_allocator= { _libmemcached_calloc, _libmemcached_free, _libmemcached_malloc, _libmemcached_realloc, 0 }; + return global_default_allocator; +} + +memcached_return_t memcached_set_memory_allocators(memcached_st *ptr, + memcached_malloc_fn mem_malloc, + memcached_free_fn mem_free, + memcached_realloc_fn mem_realloc, + memcached_calloc_fn mem_calloc, + void *context) +{ + /* All should be set, or none should be set */ + if (mem_malloc == NULL && mem_free == NULL && mem_realloc == NULL && mem_calloc == NULL) + { + ptr->allocators= memcached_allocators_return_default(); + } + else if (mem_malloc == NULL || mem_free == NULL || mem_realloc == NULL || mem_calloc == NULL) + { + return MEMCACHED_FAILURE; + } + else + { + ptr->allocators.malloc= mem_malloc; + ptr->allocators.free= mem_free; + ptr->allocators.realloc= mem_realloc; + ptr->allocators.calloc= mem_calloc; + ptr->allocators.context= context; + } + + return MEMCACHED_SUCCESS; +} + +void *memcached_get_memory_allocators_context(const memcached_st *ptr) +{ + return ptr->allocators.context; +} + +void memcached_get_memory_allocators(const memcached_st *ptr, + memcached_malloc_fn *mem_malloc, + memcached_free_fn *mem_free, + memcached_realloc_fn *mem_realloc, + memcached_calloc_fn *mem_calloc) +{ + *mem_malloc= ptr->allocators.malloc; + *mem_free= ptr->allocators.free; + *mem_realloc= ptr->allocators.realloc; + *mem_calloc= ptr->allocators.calloc; +} diff --git a/libmemcached/allocators.h b/libmemcached/allocators.h index b525e901..6e4455f0 100644 --- a/libmemcached/allocators.h +++ b/libmemcached/allocators.h @@ -1,16 +1,49 @@ -/* LibMemcached - * Copyright (C) 2010 Brian Aker - * All rights reserved. +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker All rights reserved. * - * Summary: work with user defined memory allocators + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. * */ -#ifndef __LIBMEMCACHED_ALLOCATORS_H__ -#define __LIBMEMCACHED_ALLOCATORS_H__ +#pragma once + +struct memcached_allocator_t { + memcached_calloc_fn calloc; + memcached_free_fn free; + memcached_malloc_fn malloc; + memcached_realloc_fn realloc; + void *context; +}; #ifdef __cplusplus extern "C" { @@ -47,10 +80,8 @@ LIBMEMCACHED_LOCAL void *_libmemcached_calloc(const memcached_st *ptr, size_t nelem, size_t size, void *context); LIBMEMCACHED_LOCAL -struct _allocators_st memcached_allocators_return_default(void); +struct memcached_allocator_t memcached_allocators_return_default(void); #ifdef __cplusplus } #endif - -#endif /* __LIBMEMCACHED_ALLOCATORS_H__ */ diff --git a/libmemcached/analyze.c b/libmemcached/analyze.c deleted file mode 100644 index 7dcbf8cb..00000000 --- a/libmemcached/analyze.c +++ /dev/null @@ -1,107 +0,0 @@ -#include "common.h" - -static void calc_largest_consumption(memcached_analysis_st *result, - const uint32_t server_num, - const uint64_t nbytes) -{ - if (result->most_used_bytes < nbytes) - { - result->most_used_bytes= nbytes; - result->most_consumed_server= server_num; - } -} - -static void calc_oldest_node(memcached_analysis_st *result, - const uint32_t server_num, - const uint32_t uptime) -{ - if (result->longest_uptime < uptime) - { - result->longest_uptime= uptime; - result->oldest_server= server_num; - } -} - -static void calc_least_free_node(memcached_analysis_st *result, - const uint32_t server_num, - const uint64_t max_allowed_bytes, - const uint64_t used_bytes) -{ - uint64_t remaining_bytes= max_allowed_bytes - used_bytes; - - if (result->least_remaining_bytes == 0 || - remaining_bytes < result->least_remaining_bytes) - { - result->least_remaining_bytes= remaining_bytes; - result->least_free_server= server_num; - } -} - -static void calc_average_item_size(memcached_analysis_st *result, - const uint64_t total_items, - const uint64_t total_bytes) -{ - if (total_items > 0 && total_bytes > 0) - result->average_item_size= (uint32_t) (total_bytes / total_items); -} - -static void calc_hit_ratio(memcached_analysis_st *result, - const uint64_t total_get_hits, - const uint64_t total_get_cmds) -{ - if (total_get_hits == 0 || total_get_cmds == 0) - { - result->pool_hit_ratio= 0; - return; - } - - double temp= (double) (total_get_hits/total_get_cmds); - result->pool_hit_ratio= temp * 100; -} - -memcached_analysis_st *memcached_analyze(memcached_st *memc, - memcached_stat_st *memc_stat, - memcached_return_t *error) -{ - uint64_t total_items= 0, total_bytes= 0; - uint64_t total_get_cmds= 0, total_get_hits= 0; - uint32_t server_count, x; - memcached_analysis_st *result; - - *error= MEMCACHED_SUCCESS; - server_count= memcached_server_count(memc); - result= (memcached_analysis_st*)calloc(memcached_server_count(memc), - sizeof(memcached_analysis_st)); - - if (!result) - { - *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; - return NULL; - } - - result->root= memc; - - for (x= 0; x < server_count; x++) - { - calc_largest_consumption(result, x, memc_stat[x].bytes); - calc_oldest_node(result, x, memc_stat[x].uptime); - calc_least_free_node(result, x, - memc_stat[x].limit_maxbytes, - memc_stat[x].bytes); - - total_get_hits+= memc_stat[x].get_hits; - total_get_cmds+= memc_stat[x].cmd_get; - total_items+= memc_stat[x].curr_items; - total_bytes+= memc_stat[x].bytes; - } - - calc_average_item_size(result, total_items, total_bytes); - calc_hit_ratio(result, total_get_hits, total_get_cmds); - - return result; -} - -void memcached_analyze_free(memcached_analysis_st *ptr) -{ - free(ptr); -} diff --git a/libmemcached/analyze.cc b/libmemcached/analyze.cc new file mode 100644 index 00000000..7dcbf8cb --- /dev/null +++ b/libmemcached/analyze.cc @@ -0,0 +1,107 @@ +#include "common.h" + +static void calc_largest_consumption(memcached_analysis_st *result, + const uint32_t server_num, + const uint64_t nbytes) +{ + if (result->most_used_bytes < nbytes) + { + result->most_used_bytes= nbytes; + result->most_consumed_server= server_num; + } +} + +static void calc_oldest_node(memcached_analysis_st *result, + const uint32_t server_num, + const uint32_t uptime) +{ + if (result->longest_uptime < uptime) + { + result->longest_uptime= uptime; + result->oldest_server= server_num; + } +} + +static void calc_least_free_node(memcached_analysis_st *result, + const uint32_t server_num, + const uint64_t max_allowed_bytes, + const uint64_t used_bytes) +{ + uint64_t remaining_bytes= max_allowed_bytes - used_bytes; + + if (result->least_remaining_bytes == 0 || + remaining_bytes < result->least_remaining_bytes) + { + result->least_remaining_bytes= remaining_bytes; + result->least_free_server= server_num; + } +} + +static void calc_average_item_size(memcached_analysis_st *result, + const uint64_t total_items, + const uint64_t total_bytes) +{ + if (total_items > 0 && total_bytes > 0) + result->average_item_size= (uint32_t) (total_bytes / total_items); +} + +static void calc_hit_ratio(memcached_analysis_st *result, + const uint64_t total_get_hits, + const uint64_t total_get_cmds) +{ + if (total_get_hits == 0 || total_get_cmds == 0) + { + result->pool_hit_ratio= 0; + return; + } + + double temp= (double) (total_get_hits/total_get_cmds); + result->pool_hit_ratio= temp * 100; +} + +memcached_analysis_st *memcached_analyze(memcached_st *memc, + memcached_stat_st *memc_stat, + memcached_return_t *error) +{ + uint64_t total_items= 0, total_bytes= 0; + uint64_t total_get_cmds= 0, total_get_hits= 0; + uint32_t server_count, x; + memcached_analysis_st *result; + + *error= MEMCACHED_SUCCESS; + server_count= memcached_server_count(memc); + result= (memcached_analysis_st*)calloc(memcached_server_count(memc), + sizeof(memcached_analysis_st)); + + if (!result) + { + *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; + return NULL; + } + + result->root= memc; + + for (x= 0; x < server_count; x++) + { + calc_largest_consumption(result, x, memc_stat[x].bytes); + calc_oldest_node(result, x, memc_stat[x].uptime); + calc_least_free_node(result, x, + memc_stat[x].limit_maxbytes, + memc_stat[x].bytes); + + total_get_hits+= memc_stat[x].get_hits; + total_get_cmds+= memc_stat[x].cmd_get; + total_items+= memc_stat[x].curr_items; + total_bytes+= memc_stat[x].bytes; + } + + calc_average_item_size(result, total_items, total_bytes); + calc_hit_ratio(result, total_get_hits, total_get_cmds); + + return result; +} + +void memcached_analyze_free(memcached_analysis_st *ptr) +{ + free(ptr); +} diff --git a/libmemcached/auto.c b/libmemcached/auto.c deleted file mode 100644 index 325ddb6b..00000000 --- a/libmemcached/auto.c +++ /dev/null @@ -1,326 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Methods for adding or decrementing values from an object in memcached - * - */ - -#include "libmemcached/common.h" - -static memcached_return_t text_incr_decr(memcached_st *ptr, - const char *verb, - const char *group_key, size_t group_key_length, - const char *key, size_t key_length, - uint64_t offset, - uint64_t *value) -{ - memcached_return_t rc; - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - uint32_t server_key; - memcached_server_write_instance_st instance; - bool no_reply= ptr->flags.no_reply; - - if (memcached_server_count(ptr) == 0) - return memcached_set_error(ptr, MEMCACHED_NO_SERVERS, NULL); - - if (ptr->flags.verify_key && (memcached_key_test((const char **)&key, &key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED)) - return MEMCACHED_BAD_KEY_PROVIDED; - - server_key= memcached_generate_hash_with_redistribution(ptr, group_key, group_key_length); - instance= memcached_server_instance_fetch(ptr, server_key); - - int send_length; - send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "%s %.*s%.*s %" PRIu64 "%s\r\n", verb, - memcached_print_array(ptr->prefix_key), - (int)key_length, key, - offset, no_reply ? " noreply" : ""); - if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE || send_length < 0) - return MEMCACHED_WRITE_FAILURE; - - rc= memcached_do(instance, buffer, (size_t)send_length, true); - if (no_reply || rc != MEMCACHED_SUCCESS) - return rc; - - rc= memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); - - /* - So why recheck responce? Because the protocol is brain dead :) - The number returned might end up equaling one of the string - values. Less chance of a mistake with strncmp() so we will - use it. We still called memcached_response() though since it - worked its magic for non-blocking IO. - */ - if (! strncmp(buffer, "ERROR\r\n", 7)) - { - *value= 0; - rc= MEMCACHED_PROTOCOL_ERROR; - } - else if (! strncmp(buffer, "CLIENT_ERROR\r\n", 14)) - { - *value= 0; - rc= MEMCACHED_PROTOCOL_ERROR; - } - else if (!strncmp(buffer, "NOT_FOUND\r\n", 11)) - { - *value= 0; - rc= MEMCACHED_NOTFOUND; - } - else - { - *value= strtoull(buffer, (char **)NULL, 10); - rc= MEMCACHED_SUCCESS; - } - - return rc; -} - -static memcached_return_t binary_incr_decr(memcached_st *ptr, uint8_t cmd, - const char *group_key, size_t group_key_length, - const char *key, size_t key_length, - uint64_t offset, uint64_t initial, - uint32_t expiration, - uint64_t *value) -{ - uint32_t server_key; - memcached_server_write_instance_st instance; - bool no_reply= ptr->flags.no_reply; - - if (memcached_server_count(ptr) == 0) - return memcached_set_error(ptr, MEMCACHED_NO_SERVERS, NULL); - - server_key= memcached_generate_hash_with_redistribution(ptr, group_key, group_key_length); - instance= memcached_server_instance_fetch(ptr, server_key); - - if (no_reply) - { - if(cmd == PROTOCOL_BINARY_CMD_DECREMENT) - cmd= PROTOCOL_BINARY_CMD_DECREMENTQ; - if(cmd == PROTOCOL_BINARY_CMD_INCREMENT) - cmd= PROTOCOL_BINARY_CMD_INCREMENTQ; - } - protocol_binary_request_incr request= {.bytes= {0}}; - - request.message.header.request.magic= PROTOCOL_BINARY_REQ; - request.message.header.request.opcode= cmd; - request.message.header.request.keylen= htons((uint16_t)(key_length + memcached_array_size(ptr->prefix_key))); - request.message.header.request.extlen= 20; - request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; - request.message.header.request.bodylen= htonl((uint32_t)(key_length + memcached_array_size(ptr->prefix_key) +request.message.header.request.extlen)); - request.message.body.delta= htonll(offset); - request.message.body.initial= htonll(initial); - request.message.body.expiration= htonl((uint32_t) expiration); - - struct libmemcached_io_vector_st vector[]= - { - { .length= sizeof(request.bytes), .buffer= request.bytes }, - { .length= memcached_array_size(ptr->prefix_key), .buffer= ptr->prefix_key }, - { .length= key_length, .buffer= key } - }; - - memcached_return_t rc; - if ((rc= memcached_vdo(instance, vector, 3, true)) != MEMCACHED_SUCCESS) - { - memcached_io_reset(instance); - return (rc == MEMCACHED_SUCCESS) ? MEMCACHED_WRITE_FAILURE : rc; - } - - if (no_reply) - return MEMCACHED_SUCCESS; - return memcached_response(instance, (char*)value, sizeof(*value), NULL); -} - -memcached_return_t memcached_increment(memcached_st *ptr, - const char *key, size_t key_length, - uint32_t offset, - uint64_t *value) -{ - uint64_t local_value; - if (! value) - value= &local_value; - - return memcached_increment_by_key(ptr, key, key_length, key, key_length, offset, value); -} - -memcached_return_t memcached_decrement(memcached_st *ptr, - const char *key, size_t key_length, - uint32_t offset, - uint64_t *value) -{ - uint64_t local_value; - if (! value) - value= &local_value; - - return memcached_decrement_by_key(ptr, key, key_length, key, key_length, offset, value); -} - -memcached_return_t memcached_increment_by_key(memcached_st *ptr, - const char *group_key, size_t group_key_length, - const char *key, size_t key_length, - uint64_t offset, - uint64_t *value) -{ - memcached_return_t rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol); - unlikely (rc != MEMCACHED_SUCCESS) - return rc; - - uint64_t local_value; - if (! value) - value= &local_value; - - LIBMEMCACHED_MEMCACHED_INCREMENT_START(); - if (ptr->flags.binary_protocol) - { - rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_INCREMENT, - group_key, group_key_length, key, key_length, - (uint64_t)offset, 0, MEMCACHED_EXPIRATION_NOT_ADD, - value); - } - else - { - rc= text_incr_decr(ptr, "incr", group_key, group_key_length, key, key_length, offset, value); - } - - LIBMEMCACHED_MEMCACHED_INCREMENT_END(); - - return rc; -} - -memcached_return_t memcached_decrement_by_key(memcached_st *ptr, - const char *group_key, size_t group_key_length, - const char *key, size_t key_length, - uint64_t offset, - uint64_t *value) -{ - memcached_return_t rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol); - unlikely (rc != MEMCACHED_SUCCESS) - return rc; - - uint64_t local_value; - if (! value) - value= &local_value; - - LIBMEMCACHED_MEMCACHED_DECREMENT_START(); - if (ptr->flags.binary_protocol) - { - rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_DECREMENT, - group_key, group_key_length, key, key_length, - (uint64_t)offset, 0, MEMCACHED_EXPIRATION_NOT_ADD, - value); - } - else - { - rc= text_incr_decr(ptr, "decr", group_key, group_key_length, key, key_length, offset, value); - } - - LIBMEMCACHED_MEMCACHED_DECREMENT_END(); - - return rc; -} - -memcached_return_t memcached_increment_with_initial(memcached_st *ptr, - const char *key, - size_t key_length, - uint64_t offset, - uint64_t initial, - time_t expiration, - uint64_t *value) -{ - uint64_t local_value; - if (! value) - value= &local_value; - - return memcached_increment_with_initial_by_key(ptr, key, key_length, - key, key_length, - offset, initial, expiration, value); -} - -memcached_return_t memcached_increment_with_initial_by_key(memcached_st *ptr, - const char *group_key, - size_t group_key_length, - const char *key, - size_t key_length, - uint64_t offset, - uint64_t initial, - time_t expiration, - uint64_t *value) -{ - memcached_return_t rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol); - unlikely (rc != MEMCACHED_SUCCESS) - return rc; - - uint64_t local_value; - if (! value) - value= &local_value; - - LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_START(); - if (ptr->flags.binary_protocol) - rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_INCREMENT, - group_key, group_key_length, key, key_length, - offset, initial, (uint32_t)expiration, - value); - else - rc= MEMCACHED_PROTOCOL_ERROR; - - LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_END(); - - return rc; -} - -memcached_return_t memcached_decrement_with_initial(memcached_st *ptr, - const char *key, - size_t key_length, - uint64_t offset, - uint64_t initial, - time_t expiration, - uint64_t *value) -{ - uint64_t local_value; - if (! value) - value= &local_value; - - return memcached_decrement_with_initial_by_key(ptr, key, key_length, - key, key_length, - offset, initial, expiration, value); -} - -memcached_return_t memcached_decrement_with_initial_by_key(memcached_st *ptr, - const char *group_key, - size_t group_key_length, - const char *key, - size_t key_length, - uint64_t offset, - uint64_t initial, - time_t expiration, - uint64_t *value) -{ - memcached_return_t rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol); - unlikely (rc != MEMCACHED_SUCCESS) - return rc; - - uint64_t local_value; - if (! value) - value= &local_value; - - LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_START(); - if (ptr->flags.binary_protocol) - { - rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_DECREMENT, - group_key, group_key_length, key, key_length, - offset, initial, (uint32_t)expiration, - value); - } - else - { - rc= MEMCACHED_PROTOCOL_ERROR; - } - - LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_END(); - - return rc; -} - diff --git a/libmemcached/auto.cc b/libmemcached/auto.cc new file mode 100644 index 00000000..42c3b5f0 --- /dev/null +++ b/libmemcached/auto.cc @@ -0,0 +1,380 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2009 Brian Aker 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + +#include + +static memcached_return_t text_incr_decr(memcached_st *ptr, + const char *verb, + const char *group_key, size_t group_key_length, + const char *key, size_t key_length, + uint64_t offset, + uint64_t *value) +{ + memcached_return_t rc; + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + uint32_t server_key; + memcached_server_write_instance_st instance; + bool no_reply= ptr->flags.no_reply; + + if (ptr->flags.verify_key && (memcached_key_test((const char **)&key, &key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED)) + return MEMCACHED_BAD_KEY_PROVIDED; + + server_key= memcached_generate_hash_with_redistribution(ptr, group_key, group_key_length); + instance= memcached_server_instance_fetch(ptr, server_key); + + int send_length; + send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "%s %.*s%.*s %" PRIu64 "%s\r\n", verb, + memcached_print_array(ptr->prefix_key), + (int)key_length, key, + offset, no_reply ? " noreply" : ""); + if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE || send_length < 0) + return MEMCACHED_WRITE_FAILURE; + + rc= memcached_do(instance, buffer, (size_t)send_length, true); + if (no_reply || rc != MEMCACHED_SUCCESS) + return rc; + + rc= memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); + + /* + So why recheck responce? Because the protocol is brain dead :) + The number returned might end up equaling one of the string + values. Less chance of a mistake with strncmp() so we will + use it. We still called memcached_response() though since it + worked its magic for non-blocking IO. + */ + if (! strncmp(buffer, "ERROR\r\n", 7)) + { + *value= 0; + rc= MEMCACHED_PROTOCOL_ERROR; + } + else if (! strncmp(buffer, "CLIENT_ERROR\r\n", 14)) + { + *value= 0; + rc= MEMCACHED_PROTOCOL_ERROR; + } + else if (!strncmp(buffer, "NOT_FOUND\r\n", 11)) + { + *value= 0; + rc= MEMCACHED_NOTFOUND; + } + else + { + *value= strtoull(buffer, (char **)NULL, 10); + rc= MEMCACHED_SUCCESS; + } + + return rc; +} + +static memcached_return_t binary_incr_decr(memcached_st *ptr, uint8_t cmd, + const char *group_key, size_t group_key_length, + const char *key, size_t key_length, + uint64_t offset, uint64_t initial, + uint32_t expiration, + uint64_t *value) +{ + uint32_t server_key; + memcached_server_write_instance_st instance; + bool no_reply= ptr->flags.no_reply; + + if (memcached_server_count(ptr) == 0) + return memcached_set_error(ptr, MEMCACHED_NO_SERVERS, NULL); + + server_key= memcached_generate_hash_with_redistribution(ptr, group_key, group_key_length); + instance= memcached_server_instance_fetch(ptr, server_key); + + if (no_reply) + { + if(cmd == PROTOCOL_BINARY_CMD_DECREMENT) + cmd= PROTOCOL_BINARY_CMD_DECREMENTQ; + if(cmd == PROTOCOL_BINARY_CMD_INCREMENT) + cmd= PROTOCOL_BINARY_CMD_INCREMENTQ; + } + protocol_binary_request_incr request= {}; // = {.bytes= {0}}; + + request.message.header.request.magic= PROTOCOL_BINARY_REQ; + request.message.header.request.opcode= cmd; + request.message.header.request.keylen= htons((uint16_t)(key_length + memcached_array_size(ptr->prefix_key))); + request.message.header.request.extlen= 20; + request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; + request.message.header.request.bodylen= htonl((uint32_t)(key_length + memcached_array_size(ptr->prefix_key) +request.message.header.request.extlen)); + request.message.body.delta= htonll(offset); + request.message.body.initial= htonll(initial); + request.message.body.expiration= htonl((uint32_t) expiration); + + struct libmemcached_io_vector_st vector[]= + { + { sizeof(request.bytes), request.bytes }, + { memcached_array_size(ptr->prefix_key), ptr->prefix_key }, + { key_length, key } + }; + + memcached_return_t rc; + if ((rc= memcached_vdo(instance, vector, 3, true)) != MEMCACHED_SUCCESS) + { + memcached_io_reset(instance); + return (rc == MEMCACHED_SUCCESS) ? MEMCACHED_WRITE_FAILURE : rc; + } + + if (no_reply) + return MEMCACHED_SUCCESS; + + return memcached_response(instance, (char*)value, sizeof(*value), NULL); +} + +memcached_return_t memcached_increment(memcached_st *ptr, + const char *key, size_t key_length, + uint32_t offset, + uint64_t *value) +{ + uint64_t local_value; + if (! value) + value= &local_value; + + return memcached_increment_by_key(ptr, key, key_length, key, key_length, offset, value); +} + +memcached_return_t memcached_decrement(memcached_st *ptr, + const char *key, size_t key_length, + uint32_t offset, + uint64_t *value) +{ + uint64_t local_value; + if (! value) + value= &local_value; + + return memcached_decrement_by_key(ptr, key, key_length, key, key_length, offset, value); +} + +memcached_return_t memcached_increment_by_key(memcached_st *ptr, + const char *group_key, size_t group_key_length, + const char *key, size_t key_length, + uint64_t offset, + uint64_t *value) +{ + memcached_return_t rc; + uint64_t local_value; + if (not value) + value= &local_value; + + if (memcached_failed(rc= initialize_query(ptr))) + { + return rc; + } + + if (memcached_failed(rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol))) + { + return rc; + } + + LIBMEMCACHED_MEMCACHED_INCREMENT_START(); + if (ptr->flags.binary_protocol) + { + rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_INCREMENT, + group_key, group_key_length, key, key_length, + (uint64_t)offset, 0, MEMCACHED_EXPIRATION_NOT_ADD, + value); + } + else + { + rc= text_incr_decr(ptr, "incr", group_key, group_key_length, key, key_length, offset, value); + } + + LIBMEMCACHED_MEMCACHED_INCREMENT_END(); + + return rc; +} + +memcached_return_t memcached_decrement_by_key(memcached_st *ptr, + const char *group_key, size_t group_key_length, + const char *key, size_t key_length, + uint64_t offset, + uint64_t *value) +{ + uint64_t local_value; + if (not value) + value= &local_value; + + memcached_return_t rc; + if (memcached_failed(rc= initialize_query(ptr))) + { + return rc; + } + + if (memcached_failed(rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol))) + { + return rc; + } + + + LIBMEMCACHED_MEMCACHED_DECREMENT_START(); + if (ptr->flags.binary_protocol) + { + rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_DECREMENT, + group_key, group_key_length, key, key_length, + (uint64_t)offset, 0, MEMCACHED_EXPIRATION_NOT_ADD, + value); + } + else + { + rc= text_incr_decr(ptr, "decr", group_key, group_key_length, key, key_length, offset, value); + } + + LIBMEMCACHED_MEMCACHED_DECREMENT_END(); + + return rc; +} + +memcached_return_t memcached_increment_with_initial(memcached_st *ptr, + const char *key, + size_t key_length, + uint64_t offset, + uint64_t initial, + time_t expiration, + uint64_t *value) +{ + uint64_t local_value; + if (! value) + value= &local_value; + + return memcached_increment_with_initial_by_key(ptr, key, key_length, + key, key_length, + offset, initial, expiration, value); +} + +memcached_return_t memcached_increment_with_initial_by_key(memcached_st *ptr, + const char *group_key, + size_t group_key_length, + const char *key, + size_t key_length, + uint64_t offset, + uint64_t initial, + time_t expiration, + uint64_t *value) +{ + uint64_t local_value; + if (not value) + value= &local_value; + + memcached_return_t rc; + if (memcached_failed(rc= initialize_query(ptr))) + { + return rc; + } + + if (memcached_failed(rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol))) + { + return rc; + } + + LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_START(); + if (ptr->flags.binary_protocol) + rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_INCREMENT, + group_key, group_key_length, key, key_length, + offset, initial, (uint32_t)expiration, + value); + else + rc= MEMCACHED_PROTOCOL_ERROR; + + LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_END(); + + return rc; +} + +memcached_return_t memcached_decrement_with_initial(memcached_st *ptr, + const char *key, + size_t key_length, + uint64_t offset, + uint64_t initial, + time_t expiration, + uint64_t *value) +{ + uint64_t local_value; + if (! value) + value= &local_value; + + return memcached_decrement_with_initial_by_key(ptr, key, key_length, + key, key_length, + offset, initial, expiration, value); +} + +memcached_return_t memcached_decrement_with_initial_by_key(memcached_st *ptr, + const char *group_key, + size_t group_key_length, + const char *key, + size_t key_length, + uint64_t offset, + uint64_t initial, + time_t expiration, + uint64_t *value) +{ + uint64_t local_value; + if (not value) + value= &local_value; + + memcached_return_t rc; + if (memcached_failed(rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol))) + { + return rc; + } + + if (memcached_failed(rc= initialize_query(ptr))) + { + return rc; + } + + + LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_START(); + if (ptr->flags.binary_protocol) + { + rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_DECREMENT, + group_key, group_key_length, key, key_length, + offset, initial, (uint32_t)expiration, + value); + } + else + { + rc= MEMCACHED_PROTOCOL_ERROR; + } + + LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_END(); + + return rc; +} + diff --git a/libmemcached/auto.h b/libmemcached/auto.h index 14ee90b5..f37d50fc 100644 --- a/libmemcached/auto.h +++ b/libmemcached/auto.h @@ -1,16 +1,41 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2009 Brian Aker All rights reserved. * - * Summary: Change the behavior of the memcached connection. + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. * */ -#ifndef __LIBMEMCACHED_AUTO_H__ -#define __LIBMEMCACHED_AUTO_H__ +#pragma once #ifdef __cplusplus extern "C" { @@ -84,5 +109,3 @@ LIBMEMCACHED_API #ifdef __cplusplus } #endif - -#endif /* __LIBMEMCACHED_AUTO_H__ */ diff --git a/libmemcached/behavior.c b/libmemcached/behavior.c deleted file mode 100644 index 91e33a0b..00000000 --- a/libmemcached/behavior.c +++ /dev/null @@ -1,509 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Change the behavior of the memcached connection. - * - */ - -#include -#include - -#include -#include - -static bool set_flag(uint64_t data) -{ - // Wordy :) - return data ? true : false; -} - -/* - This function is used to modify the behavior of running client. - - We quit all connections so we can reset the sockets. -*/ - -memcached_return_t memcached_behavior_set(memcached_st *ptr, - const memcached_behavior_t flag, - uint64_t data) -{ - switch (flag) - { - case MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS: - ptr->number_of_replicas= (uint32_t)data; - break; - case MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK: - ptr->io_msg_watermark= (uint32_t) data; - break; - case MEMCACHED_BEHAVIOR_IO_BYTES_WATERMARK: - ptr->io_bytes_watermark= (uint32_t)data; - break; - case MEMCACHED_BEHAVIOR_IO_KEY_PREFETCH: - ptr->io_key_prefetch = (uint32_t)data; - break; - case MEMCACHED_BEHAVIOR_SND_TIMEOUT: - ptr->snd_timeout= (int32_t)data; - break; - case MEMCACHED_BEHAVIOR_RCV_TIMEOUT: - ptr->rcv_timeout= (int32_t)data; - break; - - case MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS: - ptr->flags.auto_eject_hosts= set_flag(data); - case MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT: - ptr->server_failure_limit= (uint32_t)data; - break; - - case MEMCACHED_BEHAVIOR_BINARY_PROTOCOL: - send_quit(ptr); // We need t shutdown all of the connections to make sure we do the correct protocol - if (data) - { - ptr->flags.verify_key= false; - } - ptr->flags.binary_protocol= set_flag(data); - break; - case MEMCACHED_BEHAVIOR_SUPPORT_CAS: - ptr->flags.support_cas= set_flag(data); - break; - case MEMCACHED_BEHAVIOR_NO_BLOCK: - ptr->flags.no_block= set_flag(data); - send_quit(ptr); - break; - case MEMCACHED_BEHAVIOR_BUFFER_REQUESTS: - ptr->flags.buffer_requests= set_flag(data); - send_quit(ptr); - break; - case MEMCACHED_BEHAVIOR_USE_UDP: - if (memcached_server_count(ptr)) - { - return MEMCACHED_FAILURE; - } - ptr->flags.use_udp= set_flag(data); - if (data) - { - ptr->flags.no_reply= set_flag(data); - } - break; - case MEMCACHED_BEHAVIOR_TCP_NODELAY: - ptr->flags.tcp_nodelay= set_flag(data); - send_quit(ptr); - break; - case MEMCACHED_BEHAVIOR_TCP_KEEPALIVE: - ptr->flags.tcp_keepalive= set_flag(data); - send_quit(ptr); - break; - case MEMCACHED_BEHAVIOR_DISTRIBUTION: - return memcached_behavior_set_distribution(ptr, (memcached_server_distribution_t)data); - case MEMCACHED_BEHAVIOR_KETAMA: - { - if (data) // Turn on - return memcached_behavior_set_distribution(ptr, MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA); - - return memcached_behavior_set_distribution(ptr, MEMCACHED_DISTRIBUTION_MODULA); - } - case MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED: - { - (void)memcached_behavior_set_key_hash(ptr, MEMCACHED_HASH_MD5); - (void)memcached_behavior_set_distribution_hash(ptr, MEMCACHED_HASH_MD5); - ptr->ketama.weighted= set_flag(data); - /** - @note We try to keep the same distribution going. This should be deprecated and rewritten. - */ - return memcached_behavior_set_distribution(ptr, MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA); - } - case MEMCACHED_BEHAVIOR_HASH: - return memcached_behavior_set_key_hash(ptr, (memcached_hash_t)(data)); - case MEMCACHED_BEHAVIOR_KETAMA_HASH: - return memcached_behavior_set_distribution_hash(ptr, (memcached_hash_t)(data)); - - case MEMCACHED_BEHAVIOR_CACHE_LOOKUPS: - return memcached_set_error_string(ptr, MEMCACHED_DEPRECATED, - memcached_string_with_size("MEMCACHED_BEHAVIOR_CACHE_LOOKUPS has been deprecated.")); - - case MEMCACHED_BEHAVIOR_VERIFY_KEY: - if (ptr->flags.binary_protocol) - return memcached_set_error_string(ptr, MEMCACHED_INVALID_ARGUMENTS, - memcached_string_with_size("MEMCACHED_BEHAVIOR_VERIFY_KEY if the binary protocol has been enabled.")); - ptr->flags.verify_key= set_flag(data); - break; - case MEMCACHED_BEHAVIOR_SORT_HOSTS: - { - ptr->flags.use_sort_hosts= set_flag(data); - run_distribution(ptr); - - break; - } - case MEMCACHED_BEHAVIOR_POLL_TIMEOUT: - ptr->poll_timeout= (int32_t)data; - break; - case MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT: - ptr->connect_timeout= (int32_t)data; - break; - case MEMCACHED_BEHAVIOR_RETRY_TIMEOUT: - ptr->retry_timeout= (int32_t)data; - break; - case MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE: - ptr->send_size= (int32_t)data; - send_quit(ptr); - break; - case MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE: - ptr->recv_size= (int32_t)data; - send_quit(ptr); - break; - case MEMCACHED_BEHAVIOR_TCP_KEEPIDLE: - ptr->tcp_keepidle= (uint32_t)data; - send_quit(ptr); - break; - case MEMCACHED_BEHAVIOR_USER_DATA: - return memcached_set_error_string(ptr, MEMCACHED_DEPRECATED, - memcached_string_with_size("MEMCACHED_BEHAVIOR_USER_DATA deprecated.")); - case MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY: - ptr->flags.hash_with_prefix_key= set_flag(data); - break; - case MEMCACHED_BEHAVIOR_NOREPLY: - ptr->flags.no_reply= set_flag(data); - break; - case MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS: - ptr->flags.auto_eject_hosts= set_flag(data); - break; - case MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ: - srandom((uint32_t) time(NULL)); - ptr->flags.randomize_replica_read= set_flag(data); - break; - case MEMCACHED_BEHAVIOR_CORK: - { - return memcached_set_error_string(ptr, MEMCACHED_DEPRECATED, - memcached_string_with_size("MEMCACHED_BEHAVIOR_CORK is now incorporated into the driver by default.")); - } - break; - case MEMCACHED_BEHAVIOR_LOAD_FROM_FILE: - return memcached_set_error_string(ptr, MEMCACHED_INVALID_ARGUMENTS, - memcached_string_with_size("MEMCACHED_BEHAVIOR_LOAD_FROM_FILE can not be set with memcached_behavior_set()")); - case MEMCACHED_BEHAVIOR_MAX: - default: - /* Shouldn't get here */ - WATCHPOINT_ASSERT(0); - return memcached_set_error_string(ptr, MEMCACHED_INVALID_ARGUMENTS, - memcached_string_with_size("Invalid behavior passed to memcached_behavior_set()")); - } - - return MEMCACHED_SUCCESS; -} - -bool _is_auto_eject_host(const memcached_st *ptr) -{ - return ptr->flags.auto_eject_hosts; -} - -uint64_t memcached_behavior_get(memcached_st *ptr, - const memcached_behavior_t flag) -{ - switch (flag) - { - case MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS: - return ptr->number_of_replicas; - case MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK: - return ptr->io_msg_watermark; - case MEMCACHED_BEHAVIOR_IO_BYTES_WATERMARK: - return ptr->io_bytes_watermark; - case MEMCACHED_BEHAVIOR_IO_KEY_PREFETCH: - return ptr->io_key_prefetch; - case MEMCACHED_BEHAVIOR_BINARY_PROTOCOL: - return ptr->flags.binary_protocol; - case MEMCACHED_BEHAVIOR_SUPPORT_CAS: - return ptr->flags.support_cas; - - case MEMCACHED_BEHAVIOR_CACHE_LOOKUPS: - return true; - - case MEMCACHED_BEHAVIOR_NO_BLOCK: - return ptr->flags.no_block; - case MEMCACHED_BEHAVIOR_BUFFER_REQUESTS: - return ptr->flags.buffer_requests; - case MEMCACHED_BEHAVIOR_USE_UDP: - return ptr->flags.use_udp; - case MEMCACHED_BEHAVIOR_TCP_NODELAY: - return ptr->flags.tcp_nodelay; - case MEMCACHED_BEHAVIOR_VERIFY_KEY: - return ptr->flags.verify_key; - case MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED: - return ptr->ketama.weighted; - case MEMCACHED_BEHAVIOR_DISTRIBUTION: - return ptr->distribution; - case MEMCACHED_BEHAVIOR_KETAMA: - return (ptr->distribution == MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA) ? (uint64_t) 1 : 0; - case MEMCACHED_BEHAVIOR_HASH: - return hashkit_get_function(&ptr->hashkit); - case MEMCACHED_BEHAVIOR_KETAMA_HASH: - return hashkit_get_function(&ptr->distribution_hashkit); - case MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS: - case MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT: - return ptr->server_failure_limit; - case MEMCACHED_BEHAVIOR_SORT_HOSTS: - return ptr->flags.use_sort_hosts; - case MEMCACHED_BEHAVIOR_POLL_TIMEOUT: - return (uint64_t)ptr->poll_timeout; - case MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT: - return (uint64_t)ptr->connect_timeout; - case MEMCACHED_BEHAVIOR_RETRY_TIMEOUT: - return (uint64_t)ptr->retry_timeout; - case MEMCACHED_BEHAVIOR_SND_TIMEOUT: - return (uint64_t)ptr->snd_timeout; - case MEMCACHED_BEHAVIOR_RCV_TIMEOUT: - return (uint64_t)ptr->rcv_timeout; - case MEMCACHED_BEHAVIOR_TCP_KEEPIDLE: - return (uint64_t)ptr->tcp_keepidle; - case MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE: - { - int sock_size= 0; - socklen_t sock_length= sizeof(int); - memcached_server_write_instance_st instance; - - if (ptr->send_size != -1) // If value is -1 then we are using the default - return (uint64_t) ptr->send_size; - - instance= memcached_server_instance_fetch(ptr, 0); - - if (instance) // If we have an instance we test, otherwise we just set and pray - { - /* REFACTOR */ - /* We just try the first host, and if it is down we return zero */ - if ((memcached_connect(instance)) != MEMCACHED_SUCCESS) - { - return 0; - } - - if (memcached_io_wait_for_write(instance) != MEMCACHED_SUCCESS) - { - return 0; - } - - if (getsockopt(instance->fd, SOL_SOCKET, SO_SNDBUF, &sock_size, &sock_length) < 0) - { - memcached_set_errno(ptr, errno, NULL); - return 0; /* Zero means error */ - } - } - - return (uint64_t) sock_size; - } - case MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE: - { - int sock_size= 0; - socklen_t sock_length= sizeof(int); - memcached_server_write_instance_st instance; - - if (ptr->recv_size != -1) // If value is -1 then we are using the default - return (uint64_t) ptr->recv_size; - - instance= memcached_server_instance_fetch(ptr, 0); - - /** - @note REFACTOR - */ - if (instance) - { - /* We just try the first host, and if it is down we return zero */ - if ((memcached_connect(instance)) != MEMCACHED_SUCCESS) - { - return 0; - } - - if (memcached_io_wait_for_write(instance) != MEMCACHED_SUCCESS) - { - return 0; - } - - if (getsockopt(instance->fd, SOL_SOCKET, SO_RCVBUF, &sock_size, &sock_length) < 0) - { - memcached_set_errno(ptr, errno, NULL); - return 0; /* Zero means error */ - } - - } - - return (uint64_t) sock_size; - } - case MEMCACHED_BEHAVIOR_USER_DATA: - memcached_set_error_string(ptr, MEMCACHED_DEPRECATED, - memcached_string_with_size("MEMCACHED_BEHAVIOR_USER_DATA deprecated.")); - return 0; - case MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY: - return ptr->flags.hash_with_prefix_key; - case MEMCACHED_BEHAVIOR_NOREPLY: - return ptr->flags.no_reply; - case MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS: - return ptr->flags.auto_eject_hosts; - case MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ: - return ptr->flags.randomize_replica_read; - case MEMCACHED_BEHAVIOR_CORK: -#ifdef HAVE_MSG_MORE - return true; -#else - return false; -#endif - case MEMCACHED_BEHAVIOR_TCP_KEEPALIVE: - return ptr->flags.tcp_keepalive; - case MEMCACHED_BEHAVIOR_LOAD_FROM_FILE: - return ptr->configure.filename ? true : false; - case MEMCACHED_BEHAVIOR_MAX: - default: - WATCHPOINT_ASSERT(0); /* Programming mistake if it gets this far */ - return 0; - } - - /* NOTREACHED */ -} - - -memcached_return_t memcached_behavior_set_distribution(memcached_st *ptr, memcached_server_distribution_t type) -{ - if (type < MEMCACHED_DISTRIBUTION_CONSISTENT_MAX) - { - if (MEMCACHED_DISTRIBUTION_CONSISTENT_WEIGHTED) - { - ptr->ketama.weighted= true; - } - else - { - ptr->ketama.weighted= false; - } - ptr->distribution= type; - run_distribution(ptr); - return MEMCACHED_SUCCESS; - } - - return memcached_set_error_string(ptr, MEMCACHED_INVALID_ARGUMENTS, - memcached_string_with_size("Invalid memcached_server_distribution_t")); -} - - -memcached_server_distribution_t memcached_behavior_get_distribution(memcached_st *ptr) -{ - return ptr->distribution; -} - -memcached_return_t memcached_behavior_set_key_hash(memcached_st *ptr, memcached_hash_t type) -{ - if (hashkit_set_function(&ptr->hashkit, (hashkit_hash_algorithm_t)type) == HASHKIT_SUCCESS) - return MEMCACHED_SUCCESS; - - return memcached_set_error_string(ptr, MEMCACHED_INVALID_ARGUMENTS, - memcached_string_with_size("Invalid memcached_hash_t()")); -} - -memcached_hash_t memcached_behavior_get_key_hash(memcached_st *ptr) -{ - return (memcached_hash_t)hashkit_get_function(&ptr->hashkit); -} - -memcached_return_t memcached_behavior_set_distribution_hash(memcached_st *ptr, memcached_hash_t type) -{ - if (hashkit_set_function(&ptr->distribution_hashkit, (hashkit_hash_algorithm_t)type) == HASHKIT_SUCCESS) - return MEMCACHED_SUCCESS; - - return memcached_set_error_string(ptr, MEMCACHED_INVALID_ARGUMENTS, - memcached_string_with_size("Invalid memcached_hash_t()")); -} - -memcached_hash_t memcached_behavior_get_distribution_hash(memcached_st *ptr) -{ - return (memcached_hash_t)hashkit_get_function(&ptr->distribution_hashkit); -} - -const char *libmemcached_string_behavior(const memcached_behavior_t flag) -{ - switch (flag) - { - case MEMCACHED_BEHAVIOR_NO_BLOCK: return "MEMCACHED_BEHAVIOR_NO_BLOCK"; - case MEMCACHED_BEHAVIOR_TCP_NODELAY: return "MEMCACHED_BEHAVIOR_TCP_NODELAY"; - case MEMCACHED_BEHAVIOR_HASH: return "MEMCACHED_BEHAVIOR_HASH"; - case MEMCACHED_BEHAVIOR_KETAMA: return "MEMCACHED_BEHAVIOR_KETAMA"; - case MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE: return "MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE"; - case MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE: return "MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE"; - case MEMCACHED_BEHAVIOR_CACHE_LOOKUPS: return "MEMCACHED_BEHAVIOR_CACHE_LOOKUPS"; - case MEMCACHED_BEHAVIOR_SUPPORT_CAS: return "MEMCACHED_BEHAVIOR_SUPPORT_CAS"; - case MEMCACHED_BEHAVIOR_POLL_TIMEOUT: return "MEMCACHED_BEHAVIOR_POLL_TIMEOUT"; - case MEMCACHED_BEHAVIOR_DISTRIBUTION: return "MEMCACHED_BEHAVIOR_DISTRIBUTION"; - case MEMCACHED_BEHAVIOR_BUFFER_REQUESTS: return "MEMCACHED_BEHAVIOR_BUFFER_REQUESTS"; - case MEMCACHED_BEHAVIOR_USER_DATA: return "MEMCACHED_BEHAVIOR_USER_DATA"; - case MEMCACHED_BEHAVIOR_SORT_HOSTS: return "MEMCACHED_BEHAVIOR_SORT_HOSTS"; - case MEMCACHED_BEHAVIOR_VERIFY_KEY: return "MEMCACHED_BEHAVIOR_VERIFY_KEY"; - case MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT: return "MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT"; - case MEMCACHED_BEHAVIOR_RETRY_TIMEOUT: return "MEMCACHED_BEHAVIOR_RETRY_TIMEOUT"; - case MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED: return "MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED"; - case MEMCACHED_BEHAVIOR_KETAMA_HASH: return "MEMCACHED_BEHAVIOR_KETAMA_HASH"; - case MEMCACHED_BEHAVIOR_BINARY_PROTOCOL: return "MEMCACHED_BEHAVIOR_BINARY_PROTOCOL"; - case MEMCACHED_BEHAVIOR_SND_TIMEOUT: return "MEMCACHED_BEHAVIOR_SND_TIMEOUT"; - case MEMCACHED_BEHAVIOR_RCV_TIMEOUT: return "MEMCACHED_BEHAVIOR_RCV_TIMEOUT"; - case MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT: return "MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT"; - case MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK: return "MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK"; - case MEMCACHED_BEHAVIOR_IO_BYTES_WATERMARK: return "MEMCACHED_BEHAVIOR_IO_BYTES_WATERMARK"; - case MEMCACHED_BEHAVIOR_IO_KEY_PREFETCH: return "MEMCACHED_BEHAVIOR_IO_KEY_PREFETCH"; - case MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY: return "MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY"; - case MEMCACHED_BEHAVIOR_NOREPLY: return "MEMCACHED_BEHAVIOR_NOREPLY"; - case MEMCACHED_BEHAVIOR_USE_UDP: return "MEMCACHED_BEHAVIOR_USE_UDP"; - case MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS: return "MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS"; - case MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS: return "MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS"; - case MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS: return "MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS"; - case MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ: return "MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ"; - case MEMCACHED_BEHAVIOR_CORK: return "MEMCACHED_BEHAVIOR_CORK"; - case MEMCACHED_BEHAVIOR_TCP_KEEPALIVE: return "MEMCACHED_BEHAVIOR_TCP_KEEPALIVE"; - case MEMCACHED_BEHAVIOR_TCP_KEEPIDLE: return "MEMCACHED_BEHAVIOR_TCP_KEEPIDLE"; - case MEMCACHED_BEHAVIOR_LOAD_FROM_FILE: return "MEMCACHED_BEHAVIOR_LOAD_FROM_FILE"; - default: - case MEMCACHED_BEHAVIOR_MAX: return "INVALID memcached_behavior_t"; - } -} - -const char *libmemcached_string_distribution(const memcached_server_distribution_t flag) -{ - switch (flag) - { - case MEMCACHED_DISTRIBUTION_MODULA: return "MEMCACHED_DISTRIBUTION_MODULA"; - case MEMCACHED_DISTRIBUTION_CONSISTENT: return "MEMCACHED_DISTRIBUTION_CONSISTENT"; - case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA: return "MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA"; - case MEMCACHED_DISTRIBUTION_RANDOM: return "MEMCACHED_DISTRIBUTION_RANDOM"; - case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY: return "MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY"; - case MEMCACHED_DISTRIBUTION_CONSISTENT_WEIGHTED: return "MEMCACHED_DISTRIBUTION_CONSISTENT_WEIGHTED"; - case MEMCACHED_DISTRIBUTION_VIRTUAL_BUCKET: return "MEMCACHED_DISTRIBUTION_VIRTUAL_BUCKET"; - default: - case MEMCACHED_DISTRIBUTION_CONSISTENT_MAX: return "INVALID memcached_server_distribution_t"; - } -} - -memcached_return_t memcached_bucket_set(memcached_st *self, - const uint32_t *host_map, - const uint32_t *forward_map, - const uint32_t buckets, - const uint32_t replicas) -{ - memcached_return_t rc; - - if (! self) - return MEMCACHED_INVALID_ARGUMENTS; - - if (! host_map) - return MEMCACHED_INVALID_ARGUMENTS; - - memcached_server_distribution_t old; - old= memcached_behavior_get_distribution(self); - - rc =memcached_behavior_set_distribution(self, MEMCACHED_DISTRIBUTION_VIRTUAL_BUCKET); - if (rc != MEMCACHED_SUCCESS) - { - return rc; - } - - rc= memcached_virtual_bucket_create(self, host_map, forward_map, buckets, replicas); - if (rc != MEMCACHED_SUCCESS) - { - memcached_behavior_set_distribution(self, old); - } - - return rc; -} diff --git a/libmemcached/behavior.cc b/libmemcached/behavior.cc new file mode 100644 index 00000000..91e33a0b --- /dev/null +++ b/libmemcached/behavior.cc @@ -0,0 +1,509 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Change the behavior of the memcached connection. + * + */ + +#include +#include + +#include +#include + +static bool set_flag(uint64_t data) +{ + // Wordy :) + return data ? true : false; +} + +/* + This function is used to modify the behavior of running client. + + We quit all connections so we can reset the sockets. +*/ + +memcached_return_t memcached_behavior_set(memcached_st *ptr, + const memcached_behavior_t flag, + uint64_t data) +{ + switch (flag) + { + case MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS: + ptr->number_of_replicas= (uint32_t)data; + break; + case MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK: + ptr->io_msg_watermark= (uint32_t) data; + break; + case MEMCACHED_BEHAVIOR_IO_BYTES_WATERMARK: + ptr->io_bytes_watermark= (uint32_t)data; + break; + case MEMCACHED_BEHAVIOR_IO_KEY_PREFETCH: + ptr->io_key_prefetch = (uint32_t)data; + break; + case MEMCACHED_BEHAVIOR_SND_TIMEOUT: + ptr->snd_timeout= (int32_t)data; + break; + case MEMCACHED_BEHAVIOR_RCV_TIMEOUT: + ptr->rcv_timeout= (int32_t)data; + break; + + case MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS: + ptr->flags.auto_eject_hosts= set_flag(data); + case MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT: + ptr->server_failure_limit= (uint32_t)data; + break; + + case MEMCACHED_BEHAVIOR_BINARY_PROTOCOL: + send_quit(ptr); // We need t shutdown all of the connections to make sure we do the correct protocol + if (data) + { + ptr->flags.verify_key= false; + } + ptr->flags.binary_protocol= set_flag(data); + break; + case MEMCACHED_BEHAVIOR_SUPPORT_CAS: + ptr->flags.support_cas= set_flag(data); + break; + case MEMCACHED_BEHAVIOR_NO_BLOCK: + ptr->flags.no_block= set_flag(data); + send_quit(ptr); + break; + case MEMCACHED_BEHAVIOR_BUFFER_REQUESTS: + ptr->flags.buffer_requests= set_flag(data); + send_quit(ptr); + break; + case MEMCACHED_BEHAVIOR_USE_UDP: + if (memcached_server_count(ptr)) + { + return MEMCACHED_FAILURE; + } + ptr->flags.use_udp= set_flag(data); + if (data) + { + ptr->flags.no_reply= set_flag(data); + } + break; + case MEMCACHED_BEHAVIOR_TCP_NODELAY: + ptr->flags.tcp_nodelay= set_flag(data); + send_quit(ptr); + break; + case MEMCACHED_BEHAVIOR_TCP_KEEPALIVE: + ptr->flags.tcp_keepalive= set_flag(data); + send_quit(ptr); + break; + case MEMCACHED_BEHAVIOR_DISTRIBUTION: + return memcached_behavior_set_distribution(ptr, (memcached_server_distribution_t)data); + case MEMCACHED_BEHAVIOR_KETAMA: + { + if (data) // Turn on + return memcached_behavior_set_distribution(ptr, MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA); + + return memcached_behavior_set_distribution(ptr, MEMCACHED_DISTRIBUTION_MODULA); + } + case MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED: + { + (void)memcached_behavior_set_key_hash(ptr, MEMCACHED_HASH_MD5); + (void)memcached_behavior_set_distribution_hash(ptr, MEMCACHED_HASH_MD5); + ptr->ketama.weighted= set_flag(data); + /** + @note We try to keep the same distribution going. This should be deprecated and rewritten. + */ + return memcached_behavior_set_distribution(ptr, MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA); + } + case MEMCACHED_BEHAVIOR_HASH: + return memcached_behavior_set_key_hash(ptr, (memcached_hash_t)(data)); + case MEMCACHED_BEHAVIOR_KETAMA_HASH: + return memcached_behavior_set_distribution_hash(ptr, (memcached_hash_t)(data)); + + case MEMCACHED_BEHAVIOR_CACHE_LOOKUPS: + return memcached_set_error_string(ptr, MEMCACHED_DEPRECATED, + memcached_string_with_size("MEMCACHED_BEHAVIOR_CACHE_LOOKUPS has been deprecated.")); + + case MEMCACHED_BEHAVIOR_VERIFY_KEY: + if (ptr->flags.binary_protocol) + return memcached_set_error_string(ptr, MEMCACHED_INVALID_ARGUMENTS, + memcached_string_with_size("MEMCACHED_BEHAVIOR_VERIFY_KEY if the binary protocol has been enabled.")); + ptr->flags.verify_key= set_flag(data); + break; + case MEMCACHED_BEHAVIOR_SORT_HOSTS: + { + ptr->flags.use_sort_hosts= set_flag(data); + run_distribution(ptr); + + break; + } + case MEMCACHED_BEHAVIOR_POLL_TIMEOUT: + ptr->poll_timeout= (int32_t)data; + break; + case MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT: + ptr->connect_timeout= (int32_t)data; + break; + case MEMCACHED_BEHAVIOR_RETRY_TIMEOUT: + ptr->retry_timeout= (int32_t)data; + break; + case MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE: + ptr->send_size= (int32_t)data; + send_quit(ptr); + break; + case MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE: + ptr->recv_size= (int32_t)data; + send_quit(ptr); + break; + case MEMCACHED_BEHAVIOR_TCP_KEEPIDLE: + ptr->tcp_keepidle= (uint32_t)data; + send_quit(ptr); + break; + case MEMCACHED_BEHAVIOR_USER_DATA: + return memcached_set_error_string(ptr, MEMCACHED_DEPRECATED, + memcached_string_with_size("MEMCACHED_BEHAVIOR_USER_DATA deprecated.")); + case MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY: + ptr->flags.hash_with_prefix_key= set_flag(data); + break; + case MEMCACHED_BEHAVIOR_NOREPLY: + ptr->flags.no_reply= set_flag(data); + break; + case MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS: + ptr->flags.auto_eject_hosts= set_flag(data); + break; + case MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ: + srandom((uint32_t) time(NULL)); + ptr->flags.randomize_replica_read= set_flag(data); + break; + case MEMCACHED_BEHAVIOR_CORK: + { + return memcached_set_error_string(ptr, MEMCACHED_DEPRECATED, + memcached_string_with_size("MEMCACHED_BEHAVIOR_CORK is now incorporated into the driver by default.")); + } + break; + case MEMCACHED_BEHAVIOR_LOAD_FROM_FILE: + return memcached_set_error_string(ptr, MEMCACHED_INVALID_ARGUMENTS, + memcached_string_with_size("MEMCACHED_BEHAVIOR_LOAD_FROM_FILE can not be set with memcached_behavior_set()")); + case MEMCACHED_BEHAVIOR_MAX: + default: + /* Shouldn't get here */ + WATCHPOINT_ASSERT(0); + return memcached_set_error_string(ptr, MEMCACHED_INVALID_ARGUMENTS, + memcached_string_with_size("Invalid behavior passed to memcached_behavior_set()")); + } + + return MEMCACHED_SUCCESS; +} + +bool _is_auto_eject_host(const memcached_st *ptr) +{ + return ptr->flags.auto_eject_hosts; +} + +uint64_t memcached_behavior_get(memcached_st *ptr, + const memcached_behavior_t flag) +{ + switch (flag) + { + case MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS: + return ptr->number_of_replicas; + case MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK: + return ptr->io_msg_watermark; + case MEMCACHED_BEHAVIOR_IO_BYTES_WATERMARK: + return ptr->io_bytes_watermark; + case MEMCACHED_BEHAVIOR_IO_KEY_PREFETCH: + return ptr->io_key_prefetch; + case MEMCACHED_BEHAVIOR_BINARY_PROTOCOL: + return ptr->flags.binary_protocol; + case MEMCACHED_BEHAVIOR_SUPPORT_CAS: + return ptr->flags.support_cas; + + case MEMCACHED_BEHAVIOR_CACHE_LOOKUPS: + return true; + + case MEMCACHED_BEHAVIOR_NO_BLOCK: + return ptr->flags.no_block; + case MEMCACHED_BEHAVIOR_BUFFER_REQUESTS: + return ptr->flags.buffer_requests; + case MEMCACHED_BEHAVIOR_USE_UDP: + return ptr->flags.use_udp; + case MEMCACHED_BEHAVIOR_TCP_NODELAY: + return ptr->flags.tcp_nodelay; + case MEMCACHED_BEHAVIOR_VERIFY_KEY: + return ptr->flags.verify_key; + case MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED: + return ptr->ketama.weighted; + case MEMCACHED_BEHAVIOR_DISTRIBUTION: + return ptr->distribution; + case MEMCACHED_BEHAVIOR_KETAMA: + return (ptr->distribution == MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA) ? (uint64_t) 1 : 0; + case MEMCACHED_BEHAVIOR_HASH: + return hashkit_get_function(&ptr->hashkit); + case MEMCACHED_BEHAVIOR_KETAMA_HASH: + return hashkit_get_function(&ptr->distribution_hashkit); + case MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS: + case MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT: + return ptr->server_failure_limit; + case MEMCACHED_BEHAVIOR_SORT_HOSTS: + return ptr->flags.use_sort_hosts; + case MEMCACHED_BEHAVIOR_POLL_TIMEOUT: + return (uint64_t)ptr->poll_timeout; + case MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT: + return (uint64_t)ptr->connect_timeout; + case MEMCACHED_BEHAVIOR_RETRY_TIMEOUT: + return (uint64_t)ptr->retry_timeout; + case MEMCACHED_BEHAVIOR_SND_TIMEOUT: + return (uint64_t)ptr->snd_timeout; + case MEMCACHED_BEHAVIOR_RCV_TIMEOUT: + return (uint64_t)ptr->rcv_timeout; + case MEMCACHED_BEHAVIOR_TCP_KEEPIDLE: + return (uint64_t)ptr->tcp_keepidle; + case MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE: + { + int sock_size= 0; + socklen_t sock_length= sizeof(int); + memcached_server_write_instance_st instance; + + if (ptr->send_size != -1) // If value is -1 then we are using the default + return (uint64_t) ptr->send_size; + + instance= memcached_server_instance_fetch(ptr, 0); + + if (instance) // If we have an instance we test, otherwise we just set and pray + { + /* REFACTOR */ + /* We just try the first host, and if it is down we return zero */ + if ((memcached_connect(instance)) != MEMCACHED_SUCCESS) + { + return 0; + } + + if (memcached_io_wait_for_write(instance) != MEMCACHED_SUCCESS) + { + return 0; + } + + if (getsockopt(instance->fd, SOL_SOCKET, SO_SNDBUF, &sock_size, &sock_length) < 0) + { + memcached_set_errno(ptr, errno, NULL); + return 0; /* Zero means error */ + } + } + + return (uint64_t) sock_size; + } + case MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE: + { + int sock_size= 0; + socklen_t sock_length= sizeof(int); + memcached_server_write_instance_st instance; + + if (ptr->recv_size != -1) // If value is -1 then we are using the default + return (uint64_t) ptr->recv_size; + + instance= memcached_server_instance_fetch(ptr, 0); + + /** + @note REFACTOR + */ + if (instance) + { + /* We just try the first host, and if it is down we return zero */ + if ((memcached_connect(instance)) != MEMCACHED_SUCCESS) + { + return 0; + } + + if (memcached_io_wait_for_write(instance) != MEMCACHED_SUCCESS) + { + return 0; + } + + if (getsockopt(instance->fd, SOL_SOCKET, SO_RCVBUF, &sock_size, &sock_length) < 0) + { + memcached_set_errno(ptr, errno, NULL); + return 0; /* Zero means error */ + } + + } + + return (uint64_t) sock_size; + } + case MEMCACHED_BEHAVIOR_USER_DATA: + memcached_set_error_string(ptr, MEMCACHED_DEPRECATED, + memcached_string_with_size("MEMCACHED_BEHAVIOR_USER_DATA deprecated.")); + return 0; + case MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY: + return ptr->flags.hash_with_prefix_key; + case MEMCACHED_BEHAVIOR_NOREPLY: + return ptr->flags.no_reply; + case MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS: + return ptr->flags.auto_eject_hosts; + case MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ: + return ptr->flags.randomize_replica_read; + case MEMCACHED_BEHAVIOR_CORK: +#ifdef HAVE_MSG_MORE + return true; +#else + return false; +#endif + case MEMCACHED_BEHAVIOR_TCP_KEEPALIVE: + return ptr->flags.tcp_keepalive; + case MEMCACHED_BEHAVIOR_LOAD_FROM_FILE: + return ptr->configure.filename ? true : false; + case MEMCACHED_BEHAVIOR_MAX: + default: + WATCHPOINT_ASSERT(0); /* Programming mistake if it gets this far */ + return 0; + } + + /* NOTREACHED */ +} + + +memcached_return_t memcached_behavior_set_distribution(memcached_st *ptr, memcached_server_distribution_t type) +{ + if (type < MEMCACHED_DISTRIBUTION_CONSISTENT_MAX) + { + if (MEMCACHED_DISTRIBUTION_CONSISTENT_WEIGHTED) + { + ptr->ketama.weighted= true; + } + else + { + ptr->ketama.weighted= false; + } + ptr->distribution= type; + run_distribution(ptr); + return MEMCACHED_SUCCESS; + } + + return memcached_set_error_string(ptr, MEMCACHED_INVALID_ARGUMENTS, + memcached_string_with_size("Invalid memcached_server_distribution_t")); +} + + +memcached_server_distribution_t memcached_behavior_get_distribution(memcached_st *ptr) +{ + return ptr->distribution; +} + +memcached_return_t memcached_behavior_set_key_hash(memcached_st *ptr, memcached_hash_t type) +{ + if (hashkit_set_function(&ptr->hashkit, (hashkit_hash_algorithm_t)type) == HASHKIT_SUCCESS) + return MEMCACHED_SUCCESS; + + return memcached_set_error_string(ptr, MEMCACHED_INVALID_ARGUMENTS, + memcached_string_with_size("Invalid memcached_hash_t()")); +} + +memcached_hash_t memcached_behavior_get_key_hash(memcached_st *ptr) +{ + return (memcached_hash_t)hashkit_get_function(&ptr->hashkit); +} + +memcached_return_t memcached_behavior_set_distribution_hash(memcached_st *ptr, memcached_hash_t type) +{ + if (hashkit_set_function(&ptr->distribution_hashkit, (hashkit_hash_algorithm_t)type) == HASHKIT_SUCCESS) + return MEMCACHED_SUCCESS; + + return memcached_set_error_string(ptr, MEMCACHED_INVALID_ARGUMENTS, + memcached_string_with_size("Invalid memcached_hash_t()")); +} + +memcached_hash_t memcached_behavior_get_distribution_hash(memcached_st *ptr) +{ + return (memcached_hash_t)hashkit_get_function(&ptr->distribution_hashkit); +} + +const char *libmemcached_string_behavior(const memcached_behavior_t flag) +{ + switch (flag) + { + case MEMCACHED_BEHAVIOR_NO_BLOCK: return "MEMCACHED_BEHAVIOR_NO_BLOCK"; + case MEMCACHED_BEHAVIOR_TCP_NODELAY: return "MEMCACHED_BEHAVIOR_TCP_NODELAY"; + case MEMCACHED_BEHAVIOR_HASH: return "MEMCACHED_BEHAVIOR_HASH"; + case MEMCACHED_BEHAVIOR_KETAMA: return "MEMCACHED_BEHAVIOR_KETAMA"; + case MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE: return "MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE"; + case MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE: return "MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE"; + case MEMCACHED_BEHAVIOR_CACHE_LOOKUPS: return "MEMCACHED_BEHAVIOR_CACHE_LOOKUPS"; + case MEMCACHED_BEHAVIOR_SUPPORT_CAS: return "MEMCACHED_BEHAVIOR_SUPPORT_CAS"; + case MEMCACHED_BEHAVIOR_POLL_TIMEOUT: return "MEMCACHED_BEHAVIOR_POLL_TIMEOUT"; + case MEMCACHED_BEHAVIOR_DISTRIBUTION: return "MEMCACHED_BEHAVIOR_DISTRIBUTION"; + case MEMCACHED_BEHAVIOR_BUFFER_REQUESTS: return "MEMCACHED_BEHAVIOR_BUFFER_REQUESTS"; + case MEMCACHED_BEHAVIOR_USER_DATA: return "MEMCACHED_BEHAVIOR_USER_DATA"; + case MEMCACHED_BEHAVIOR_SORT_HOSTS: return "MEMCACHED_BEHAVIOR_SORT_HOSTS"; + case MEMCACHED_BEHAVIOR_VERIFY_KEY: return "MEMCACHED_BEHAVIOR_VERIFY_KEY"; + case MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT: return "MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT"; + case MEMCACHED_BEHAVIOR_RETRY_TIMEOUT: return "MEMCACHED_BEHAVIOR_RETRY_TIMEOUT"; + case MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED: return "MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED"; + case MEMCACHED_BEHAVIOR_KETAMA_HASH: return "MEMCACHED_BEHAVIOR_KETAMA_HASH"; + case MEMCACHED_BEHAVIOR_BINARY_PROTOCOL: return "MEMCACHED_BEHAVIOR_BINARY_PROTOCOL"; + case MEMCACHED_BEHAVIOR_SND_TIMEOUT: return "MEMCACHED_BEHAVIOR_SND_TIMEOUT"; + case MEMCACHED_BEHAVIOR_RCV_TIMEOUT: return "MEMCACHED_BEHAVIOR_RCV_TIMEOUT"; + case MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT: return "MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT"; + case MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK: return "MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK"; + case MEMCACHED_BEHAVIOR_IO_BYTES_WATERMARK: return "MEMCACHED_BEHAVIOR_IO_BYTES_WATERMARK"; + case MEMCACHED_BEHAVIOR_IO_KEY_PREFETCH: return "MEMCACHED_BEHAVIOR_IO_KEY_PREFETCH"; + case MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY: return "MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY"; + case MEMCACHED_BEHAVIOR_NOREPLY: return "MEMCACHED_BEHAVIOR_NOREPLY"; + case MEMCACHED_BEHAVIOR_USE_UDP: return "MEMCACHED_BEHAVIOR_USE_UDP"; + case MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS: return "MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS"; + case MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS: return "MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS"; + case MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS: return "MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS"; + case MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ: return "MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ"; + case MEMCACHED_BEHAVIOR_CORK: return "MEMCACHED_BEHAVIOR_CORK"; + case MEMCACHED_BEHAVIOR_TCP_KEEPALIVE: return "MEMCACHED_BEHAVIOR_TCP_KEEPALIVE"; + case MEMCACHED_BEHAVIOR_TCP_KEEPIDLE: return "MEMCACHED_BEHAVIOR_TCP_KEEPIDLE"; + case MEMCACHED_BEHAVIOR_LOAD_FROM_FILE: return "MEMCACHED_BEHAVIOR_LOAD_FROM_FILE"; + default: + case MEMCACHED_BEHAVIOR_MAX: return "INVALID memcached_behavior_t"; + } +} + +const char *libmemcached_string_distribution(const memcached_server_distribution_t flag) +{ + switch (flag) + { + case MEMCACHED_DISTRIBUTION_MODULA: return "MEMCACHED_DISTRIBUTION_MODULA"; + case MEMCACHED_DISTRIBUTION_CONSISTENT: return "MEMCACHED_DISTRIBUTION_CONSISTENT"; + case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA: return "MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA"; + case MEMCACHED_DISTRIBUTION_RANDOM: return "MEMCACHED_DISTRIBUTION_RANDOM"; + case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY: return "MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY"; + case MEMCACHED_DISTRIBUTION_CONSISTENT_WEIGHTED: return "MEMCACHED_DISTRIBUTION_CONSISTENT_WEIGHTED"; + case MEMCACHED_DISTRIBUTION_VIRTUAL_BUCKET: return "MEMCACHED_DISTRIBUTION_VIRTUAL_BUCKET"; + default: + case MEMCACHED_DISTRIBUTION_CONSISTENT_MAX: return "INVALID memcached_server_distribution_t"; + } +} + +memcached_return_t memcached_bucket_set(memcached_st *self, + const uint32_t *host_map, + const uint32_t *forward_map, + const uint32_t buckets, + const uint32_t replicas) +{ + memcached_return_t rc; + + if (! self) + return MEMCACHED_INVALID_ARGUMENTS; + + if (! host_map) + return MEMCACHED_INVALID_ARGUMENTS; + + memcached_server_distribution_t old; + old= memcached_behavior_get_distribution(self); + + rc =memcached_behavior_set_distribution(self, MEMCACHED_DISTRIBUTION_VIRTUAL_BUCKET); + if (rc != MEMCACHED_SUCCESS) + { + return rc; + } + + rc= memcached_virtual_bucket_create(self, host_map, forward_map, buckets, replicas); + if (rc != MEMCACHED_SUCCESS) + { + memcached_behavior_set_distribution(self, old); + } + + return rc; +} diff --git a/libmemcached/byteorder.c b/libmemcached/byteorder.c deleted file mode 100644 index 97d14f2b..00000000 --- a/libmemcached/byteorder.c +++ /dev/null @@ -1,43 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: - * - */ - -#include "byteorder.h" - -/* Byte swap a 64-bit number. */ -#ifndef swap64 -static inline uint64_t swap64(uint64_t in) -{ -#ifndef WORDS_BIGENDIAN - /* Little endian, flip the bytes around until someone makes a faster/better - * way to do this. */ - uint64_t rv= 0; - for (uint8_t x= 0; x < 8; x++) - { - rv= (rv << 8) | (in & 0xff); - in >>= 8; - } - return rv; -#else - /* big-endian machines don't need byte swapping */ - return in; -#endif // WORDS_BIGENDIAN -} -#endif - -uint64_t memcached_ntohll(uint64_t value) -{ - return swap64(value); -} - -uint64_t memcached_htonll(uint64_t value) -{ - return swap64(value); -} diff --git a/libmemcached/byteorder.cc b/libmemcached/byteorder.cc new file mode 100644 index 00000000..ab35a9ef --- /dev/null +++ b/libmemcached/byteorder.cc @@ -0,0 +1,69 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2009 Brian Aker 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + +#include + +/* Byte swap a 64-bit number. */ +#ifndef swap64 +static inline uint64_t swap64(uint64_t in) +{ +#ifndef WORDS_BIGENDIAN + /* Little endian, flip the bytes around until someone makes a faster/better + * way to do this. */ + uint64_t rv= 0; + for (uint8_t x= 0; x < 8; x++) + { + rv= (rv << 8) | (in & 0xff); + in >>= 8; + } + return rv; +#else + /* big-endian machines don't need byte swapping */ + return in; +#endif // WORDS_BIGENDIAN +} +#endif + +uint64_t memcached_ntohll(uint64_t value) +{ + return swap64(value); +} + +uint64_t memcached_htonll(uint64_t value) +{ + return swap64(value); +} diff --git a/libmemcached/byteorder.h b/libmemcached/byteorder.h index 90a71ee4..f78790a8 100644 --- a/libmemcached/byteorder.h +++ b/libmemcached/byteorder.h @@ -1,39 +1,62 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2009 Brian Aker All rights reserved. * - * Summary: + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. * */ -#ifndef __LIBMEMCACHED_BYTEORDER_H__ -#define __LIBMEMCACHED_BYTEORDER_H__ - -#include "config.h" +#pragma once #if HAVE_SYS_TYPES_H #include #endif - -/* Define this here, which will turn on the visibilty controls while we're - * building libmemcached. - */ -#define BUILDING_LIBMEMCACHED 1 - -#include "libmemcached/memcached.h" - #ifndef HAVE_HTONLL #define ntohll(a) memcached_ntohll(a) #define htonll(a) memcached_htonll(a) +#ifdef __cplusplus +extern "C" { +#endif + LIBMEMCACHED_LOCAL uint64_t memcached_ntohll(uint64_t); LIBMEMCACHED_LOCAL uint64_t memcached_htonll(uint64_t); +#ifdef __cplusplus +} +#endif + #endif #ifdef linux @@ -47,5 +70,3 @@ uint64_t memcached_htonll(uint64_t); #undef htons #undef htonl #endif - -#endif /*__LIBMEMCACHED_BYTEORDER_H__ */ diff --git a/libmemcached/callback.c b/libmemcached/callback.c deleted file mode 100644 index dcb3ddeb..00000000 --- a/libmemcached/callback.c +++ /dev/null @@ -1,158 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Change any of the possible callbacks. - * - */ - -#include "libmemcached/common.h" -#include - -/* - These functions provide data and function callback support -*/ - -memcached_return_t memcached_callback_set(memcached_st *ptr, - const memcached_callback_t flag, - void *data) -{ - switch (flag) - { - case MEMCACHED_CALLBACK_PREFIX_KEY: - { - return memcached_set_prefix_key(ptr, (char*)data, data ? strlen((char*)data) : 0); - } - case MEMCACHED_CALLBACK_USER_DATA: - { - ptr->user_data= data; - break; - } - case MEMCACHED_CALLBACK_CLEANUP_FUNCTION: - { - memcached_cleanup_fn func= *(memcached_cleanup_fn *)&data; - ptr->on_cleanup= func; - break; - } - case MEMCACHED_CALLBACK_CLONE_FUNCTION: - { - memcached_clone_fn func= *(memcached_clone_fn *)&data; - ptr->on_clone= func; - break; - } -#ifdef MEMCACHED_ENABLE_DEPRECATED - case MEMCACHED_CALLBACK_MALLOC_FUNCTION: - { - memcached_malloc_function func= *(memcached_malloc_fn *)&data; - ptr->call_malloc= func; - break; - } - case MEMCACHED_CALLBACK_REALLOC_FUNCTION: - { - memcached_realloc_function func= *(memcached_realloc_fn *)&data; - ptr->call_realloc= func; - break; - } - case MEMCACHED_CALLBACK_FREE_FUNCTION: - { - memcached_free_function func= *(memcached_free_fn *)&data; - ptr->call_free= func; - break; - } -#endif - case MEMCACHED_CALLBACK_GET_FAILURE: - { - memcached_trigger_key_fn func= *(memcached_trigger_key_fn *)&data; - ptr->get_key_failure= func; - break; - } - case MEMCACHED_CALLBACK_DELETE_TRIGGER: - { - memcached_trigger_delete_key_fn func= *(memcached_trigger_delete_key_fn *)&data; - ptr->delete_trigger= func; - break; - } - case MEMCACHED_CALLBACK_MAX: - default: - return MEMCACHED_FAILURE; - } - - return MEMCACHED_SUCCESS; -} - -void *memcached_callback_get(memcached_st *ptr, - const memcached_callback_t flag, - memcached_return_t *error) -{ - memcached_return_t local_error; - - if (!error) - error = &local_error; - - switch (flag) - { - case MEMCACHED_CALLBACK_PREFIX_KEY: - { - if (ptr->prefix_key) - { - *error= MEMCACHED_SUCCESS; - return (void *)memcached_array_string(ptr->prefix_key); - } - else - { - *error= MEMCACHED_FAILURE; - return NULL; - } - } - case MEMCACHED_CALLBACK_USER_DATA: - { - *error= ptr->user_data ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; - return (void *)ptr->user_data; - } - case MEMCACHED_CALLBACK_CLEANUP_FUNCTION: - { - *error= ptr->on_cleanup ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; - return *(void **)&ptr->on_cleanup; - } - case MEMCACHED_CALLBACK_CLONE_FUNCTION: - { - *error= ptr->on_clone ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; - return *(void **)&ptr->on_clone; - } -#ifdef MEMCACHED_ENABLE_DEPRECATED - case MEMCACHED_CALLBACK_MALLOC_FUNCTION: - { - *error= ptr->call_malloc ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; - return *(void **)&ptr->call_malloc; - } - case MEMCACHED_CALLBACK_REALLOC_FUNCTION: - { - *error= ptr->call_realloc ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; - return *(void **)&ptr->call_realloc; - } - case MEMCACHED_CALLBACK_FREE_FUNCTION: - { - *error= ptr->call_free ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; - return *(void **)&ptr->call_free; - } -#endif - case MEMCACHED_CALLBACK_GET_FAILURE: - { - *error= ptr->get_key_failure ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; - return *(void **)&ptr->get_key_failure; - } - case MEMCACHED_CALLBACK_DELETE_TRIGGER: - { - *error= ptr->delete_trigger ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; - return *(void **)&ptr->delete_trigger; - } - case MEMCACHED_CALLBACK_MAX: - default: - WATCHPOINT_ASSERT(0); - *error= MEMCACHED_FAILURE; - return NULL; - } -} diff --git a/libmemcached/callback.cc b/libmemcached/callback.cc new file mode 100644 index 00000000..9818af4f --- /dev/null +++ b/libmemcached/callback.cc @@ -0,0 +1,160 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Change any of the possible callbacks. + * + */ + +#include +#include + +#pragma GCC diagnostic ignored "-Wstrict-aliasing" + +/* + These functions provide data and function callback support +*/ + +memcached_return_t memcached_callback_set(memcached_st *ptr, + const memcached_callback_t flag, + void *data) +{ + switch (flag) + { + case MEMCACHED_CALLBACK_PREFIX_KEY: + { + return memcached_set_prefix_key(ptr, (char*)data, data ? strlen((char*)data) : 0); + } + case MEMCACHED_CALLBACK_USER_DATA: + { + ptr->user_data= data; + break; + } + case MEMCACHED_CALLBACK_CLEANUP_FUNCTION: + { + memcached_cleanup_fn func= *(memcached_cleanup_fn *)&data; + ptr->on_cleanup= func; + break; + } + case MEMCACHED_CALLBACK_CLONE_FUNCTION: + { + memcached_clone_fn func= *(memcached_clone_fn *)&data; + ptr->on_clone= func; + break; + } +#ifdef MEMCACHED_ENABLE_DEPRECATED + case MEMCACHED_CALLBACK_MALLOC_FUNCTION: + { + memcached_malloc_function func= *(memcached_malloc_fn *)&data; + ptr->call_malloc= func; + break; + } + case MEMCACHED_CALLBACK_REALLOC_FUNCTION: + { + memcached_realloc_function func= *(memcached_realloc_fn *)&data; + ptr->call_realloc= func; + break; + } + case MEMCACHED_CALLBACK_FREE_FUNCTION: + { + memcached_free_function func= *(memcached_free_fn *)&data; + ptr->call_free= func; + break; + } +#endif + case MEMCACHED_CALLBACK_GET_FAILURE: + { + memcached_trigger_key_fn func= *(memcached_trigger_key_fn *)&data; + ptr->get_key_failure= func; + break; + } + case MEMCACHED_CALLBACK_DELETE_TRIGGER: + { + memcached_trigger_delete_key_fn func= *(memcached_trigger_delete_key_fn *)&data; + ptr->delete_trigger= func; + break; + } + case MEMCACHED_CALLBACK_MAX: + default: + return MEMCACHED_FAILURE; + } + + return MEMCACHED_SUCCESS; +} + +void *memcached_callback_get(memcached_st *ptr, + const memcached_callback_t flag, + memcached_return_t *error) +{ + memcached_return_t local_error; + + if (!error) + error = &local_error; + + switch (flag) + { + case MEMCACHED_CALLBACK_PREFIX_KEY: + { + if (ptr->prefix_key) + { + *error= MEMCACHED_SUCCESS; + return (void *)memcached_array_string(ptr->prefix_key); + } + else + { + *error= MEMCACHED_FAILURE; + return NULL; + } + } + case MEMCACHED_CALLBACK_USER_DATA: + { + *error= ptr->user_data ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; + return (void *)ptr->user_data; + } + case MEMCACHED_CALLBACK_CLEANUP_FUNCTION: + { + *error= ptr->on_cleanup ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; + return *(void **)&ptr->on_cleanup; + } + case MEMCACHED_CALLBACK_CLONE_FUNCTION: + { + *error= ptr->on_clone ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; + return *(void **)&ptr->on_clone; + } +#ifdef MEMCACHED_ENABLE_DEPRECATED + case MEMCACHED_CALLBACK_MALLOC_FUNCTION: + { + *error= ptr->call_malloc ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; + return *(void **)&ptr->call_malloc; + } + case MEMCACHED_CALLBACK_REALLOC_FUNCTION: + { + *error= ptr->call_realloc ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; + return *(void **)&ptr->call_realloc; + } + case MEMCACHED_CALLBACK_FREE_FUNCTION: + { + *error= ptr->call_free ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; + return *(void **)&ptr->call_free; + } +#endif + case MEMCACHED_CALLBACK_GET_FAILURE: + { + *error= ptr->get_key_failure ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; + return *(void **)&ptr->get_key_failure; + } + case MEMCACHED_CALLBACK_DELETE_TRIGGER: + { + *error= ptr->delete_trigger ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; + return *(void **)&ptr->delete_trigger; + } + case MEMCACHED_CALLBACK_MAX: + default: + WATCHPOINT_ASSERT(0); + *error= MEMCACHED_FAILURE; + return NULL; + } +} diff --git a/libmemcached/callback.h b/libmemcached/callback.h index 4c3a95f4..b4da8080 100644 --- a/libmemcached/callback.h +++ b/libmemcached/callback.h @@ -9,6 +9,7 @@ * */ +#pragma once #ifndef __LIBMEMCACHED_CALLBACK_H__ #define __LIBMEMCACHED_CALLBACK_H__ diff --git a/libmemcached/common.h b/libmemcached/common.h index d0a4ab4c..ec58fd8e 100644 --- a/libmemcached/common.h +++ b/libmemcached/common.h @@ -65,16 +65,14 @@ # endif #endif -/* Define this here, which will turn on the visibilty controls while we're - * building libmemcached. - */ -#define BUILDING_LIBMEMCACHED 1 - #include #include #include -#include + +#ifdef __cplusplus +extern "C" { +#endif typedef struct memcached_server_st * memcached_server_write_instance_st; @@ -87,6 +85,9 @@ LIBMEMCACHED_LOCAL memcached_return_t memcached_server_execute(memcached_st *ptr, memcached_server_execute_fn callback, void *context); +#ifdef __cplusplus +} // extern "C" +#endif /* These are private not to be installed headers */ diff --git a/libmemcached/connect.c b/libmemcached/connect.c deleted file mode 100644 index a7b17f0c..00000000 --- a/libmemcached/connect.c +++ /dev/null @@ -1,578 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2010 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Server IO, Not public! - * - */ - -#include -#include -#include -#include - -static memcached_return_t connect_poll(memcached_server_st *ptr) -{ - struct pollfd fds[1]; - fds[0].fd = ptr->fd; - fds[0].events = POLLOUT; - - int error; - size_t loop_max= 5; - - while (--loop_max) // Should only loop on cases of ERESTART or EINTR - { - error= poll(fds, 1, ptr->root->connect_timeout); - - switch (error) - { - case 1: - { - int err; - socklen_t len= sizeof (err); - (void)getsockopt(ptr->fd, SOL_SOCKET, SO_ERROR, &err, &len); - - // We check the value to see what happened wth the socket. - if (err == 0) - { - return MEMCACHED_SUCCESS; - } - else - { - ptr->cached_errno= errno; - - return MEMCACHED_ERRNO; - } - } - case 0: - return MEMCACHED_TIMEOUT; - default: // A real error occurred and we need to completely bail - WATCHPOINT_ERRNO(get_socket_errno()); - switch (get_socket_errno()) - { -#ifdef TARGET_OS_LINUX - case ERESTART: -#endif - case EINTR: - continue; - default: - if (fds[0].revents & POLLERR) - { - int err; - socklen_t len= sizeof (err); - (void)getsockopt(ptr->fd, SOL_SOCKET, SO_ERROR, &err, &len); - ptr->cached_errno= (err == 0) ? get_socket_errno() : err; - } - else - { - ptr->cached_errno= get_socket_errno(); - } - - (void)closesocket(ptr->fd); - ptr->fd= INVALID_SOCKET; - - return MEMCACHED_ERRNO; - } - } - } - - // This should only be possible from ERESTART or EINTR; - ptr->cached_errno= get_socket_errno(); - - return MEMCACHED_ERRNO; -} - -static memcached_return_t set_hostinfo(memcached_server_st *server) -{ - struct addrinfo hints; - char str_port[NI_MAXSERV]; - - assert(! server->address_info); // We cover the case where a programming mistake has been made. - if (server->address_info) - { - freeaddrinfo(server->address_info); - server->address_info= NULL; - server->address_info_next= NULL; - } - - int length= snprintf(str_port, NI_MAXSERV, "%u", (uint32_t)server->port); - if (length >= NI_MAXSERV || length < 0) - return MEMCACHED_FAILURE; - - memset(&hints, 0, sizeof(hints)); - -#if 0 - hints.ai_family= AF_INET; -#endif - if (server->type == MEMCACHED_CONNECTION_UDP) - { - hints.ai_protocol= IPPROTO_UDP; - hints.ai_socktype= SOCK_DGRAM; - } - else - { - hints.ai_socktype= SOCK_STREAM; - hints.ai_protocol= IPPROTO_TCP; - } - - uint32_t counter= 5; - while (--counter) - { - int e= getaddrinfo(server->hostname, str_port, &hints, &server->address_info); - - if (e == 0) - { - break; - } - else if (e == EAI_AGAIN) - { -#ifndef WIN32 - struct timespec dream, rem; - - dream.tv_nsec= 1000; - dream.tv_sec= 0; - - nanosleep(&dream, &rem); -#endif - continue; - } - else - { - WATCHPOINT_STRING(server->hostname); - WATCHPOINT_STRING(gai_strerror(e)); - return MEMCACHED_HOST_LOOKUP_FAILURE; - } - } - - server->address_info_next= server->address_info; - - return MEMCACHED_SUCCESS; -} - -static inline memcached_return_t set_socket_nonblocking(memcached_server_st *ptr) -{ -#ifdef WIN32 - u_long arg = 1; - if (ioctlsocket(ptr->fd, FIONBIO, &arg) == SOCKET_ERROR) - { - ptr->cached_errno= get_socket_errno(); - return MEMCACHED_CONNECTION_FAILURE; - } -#else - int flags; - - do - { - flags= fcntl(ptr->fd, F_GETFL, 0); - } - while (flags == -1 && (errno == EINTR || errno == EAGAIN)); - - unlikely (flags == -1) - { - ptr->cached_errno= errno; - return MEMCACHED_CONNECTION_FAILURE; - } - else if ((flags & O_NONBLOCK) == 0) - { - int rval; - - do - { - rval= fcntl(ptr->fd, F_SETFL, flags | O_NONBLOCK); - } - while (rval == -1 && (errno == EINTR || errno == EAGAIN)); - - unlikely (rval == -1) - { - ptr->cached_errno= errno; - return MEMCACHED_CONNECTION_FAILURE; - } - } -#endif - return MEMCACHED_SUCCESS; -} - -static memcached_return_t set_socket_options(memcached_server_st *ptr) -{ - WATCHPOINT_ASSERT(ptr->fd != -1); - - if (ptr->type == MEMCACHED_CONNECTION_UDP) - return MEMCACHED_SUCCESS; - -#ifdef HAVE_SNDTIMEO - if (ptr->root->snd_timeout) - { - int error; - struct timeval waittime; - - waittime.tv_sec= 0; - waittime.tv_usec= ptr->root->snd_timeout; - - error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDTIMEO, - &waittime, (socklen_t)sizeof(struct timeval)); - WATCHPOINT_ASSERT(error == 0); - if (error) - return MEMCACHED_FAILURE; - } -#endif - -#ifdef HAVE_RCVTIMEO - if (ptr->root->rcv_timeout) - { - int error; - struct timeval waittime; - - waittime.tv_sec= 0; - waittime.tv_usec= ptr->root->rcv_timeout; - - error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVTIMEO, - &waittime, (socklen_t)sizeof(struct timeval)); - WATCHPOINT_ASSERT(error == 0); - if (error) - return MEMCACHED_FAILURE; - } -#endif - - -#if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) - { - int set = 1; - int error= setsockopt(ptr->fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int)); - - // This is not considered a fatal error - if (error == -1) - { - WATCHPOINT_ERRNO(get_socket_errno()); - perror("setsockopt(SO_NOSIGPIPE)"); - } - } -#endif - - if (ptr->root->flags.no_block) - { - int error; - struct linger linger; - - linger.l_onoff= 1; - linger.l_linger= 0; /* By default on close() just drop the socket */ - error= setsockopt(ptr->fd, SOL_SOCKET, SO_LINGER, - &linger, (socklen_t)sizeof(struct linger)); - WATCHPOINT_ASSERT(error == 0); - if (error) - return MEMCACHED_FAILURE; - } - - if (ptr->root->flags.tcp_nodelay) - { - int flag= 1; - int error; - - error= setsockopt(ptr->fd, IPPROTO_TCP, TCP_NODELAY, - &flag, (socklen_t)sizeof(int)); - WATCHPOINT_ASSERT(error == 0); - if (error) - return MEMCACHED_FAILURE; - } - - if (ptr->root->flags.tcp_keepalive) - { - int flag= 1; - int error; - - error= setsockopt(ptr->fd, SOL_SOCKET, SO_KEEPALIVE, - &flag, (socklen_t)sizeof(int)); - WATCHPOINT_ASSERT(error == 0); - if (error) - return MEMCACHED_FAILURE; - } - -#ifdef TCP_KEEPIDLE - if (ptr->root->tcp_keepidle > 0) - { - int error; - - error= setsockopt(ptr->fd, IPPROTO_TCP, TCP_KEEPIDLE, - &ptr->root->tcp_keepidle, (socklen_t)sizeof(int)); - WATCHPOINT_ASSERT(error == 0); - if (error) - return MEMCACHED_FAILURE; - } -#endif - - if (ptr->root->send_size > 0) - { - int error; - - error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDBUF, - &ptr->root->send_size, (socklen_t)sizeof(int)); - WATCHPOINT_ASSERT(error == 0); - if (error) - return MEMCACHED_FAILURE; - } - - if (ptr->root->recv_size > 0) - { - int error; - - error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVBUF, - &ptr->root->recv_size, (socklen_t)sizeof(int)); - WATCHPOINT_ASSERT(error == 0); - if (error) - return MEMCACHED_FAILURE; - } - - - /* libmemcached will always use nonblocking IO to avoid write deadlocks */ - return set_socket_nonblocking(ptr); -} - -static memcached_return_t unix_socket_connect(memcached_server_st *ptr) -{ -#ifndef WIN32 - struct sockaddr_un servAddr; - - WATCHPOINT_ASSERT(ptr->fd == -1); - - if ((ptr->fd= socket(AF_UNIX, SOCK_STREAM, 0)) < 0) - { - ptr->cached_errno= errno; - return MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE; - } - - memset(&servAddr, 0, sizeof (struct sockaddr_un)); - servAddr.sun_family= AF_UNIX; - strncpy(servAddr.sun_path, ptr->hostname, sizeof(servAddr.sun_path)); /* Copy filename */ - -test_connect: - if (connect(ptr->fd, - (struct sockaddr *)&servAddr, - sizeof(servAddr)) < 0) - { - switch (errno) - { - case EINPROGRESS: - case EALREADY: - case EINTR: - goto test_connect; - case EISCONN: /* We were spinning waiting on connect */ - break; - default: - WATCHPOINT_ERRNO(errno); - ptr->cached_errno= errno; - return MEMCACHED_ERRNO; - } - } - - WATCHPOINT_ASSERT(ptr->fd != -1); - - return MEMCACHED_SUCCESS; -#else - (void)ptr; - return MEMCACHED_NOT_SUPPORTED; -#endif -} - -static memcached_return_t network_connect(memcached_server_st *ptr) -{ - bool timeout_error_occured= false; - - WATCHPOINT_ASSERT(ptr->fd == INVALID_SOCKET); - WATCHPOINT_ASSERT(ptr->cursor_active == 0); - - if (! ptr->address_info) - { - memcached_return_t rc= set_hostinfo(ptr); - if (rc != MEMCACHED_SUCCESS) - return rc; - } - - /* Create the socket */ - while (ptr->address_info_next && ptr->fd == INVALID_SOCKET) - { - /* Memcache server does not support IPV6 in udp mode, so skip if not ipv4 */ - if (ptr->type == MEMCACHED_CONNECTION_UDP && ptr->address_info_next->ai_family != AF_INET) - { - ptr->address_info_next= ptr->address_info_next->ai_next; - continue; - } - - if ((ptr->fd= socket(ptr->address_info_next->ai_family, - ptr->address_info_next->ai_socktype, - ptr->address_info_next->ai_protocol)) < 0) - { - ptr->cached_errno= get_socket_errno(); - WATCHPOINT_ERRNO(get_socket_errno()); - return MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE; - } - - (void)set_socket_options(ptr); - - /* connect to server */ - if ((connect(ptr->fd, ptr->address_info_next->ai_addr, ptr->address_info_next->ai_addrlen) != SOCKET_ERROR)) - { - break; // Success - } - - /* An error occurred */ - ptr->cached_errno= get_socket_errno(); - switch (ptr->cached_errno) - { - case EWOULDBLOCK: - case EINPROGRESS: // nonblocking mode - first return - case EALREADY: // nonblocking mode - subsequent returns - { - memcached_return_t rc; - rc= connect_poll(ptr); - - if (rc == MEMCACHED_TIMEOUT) - timeout_error_occured= true; - - if (rc == MEMCACHED_SUCCESS) - break; - } - - case EISCONN: // we are connected :-) - break; - - case EINTR: // Special case, we retry ai_addr - (void)closesocket(ptr->fd); - ptr->fd= INVALID_SOCKET; - continue; - - default: - (void)closesocket(ptr->fd); - ptr->fd= INVALID_SOCKET; - ptr->address_info_next= ptr->address_info_next->ai_next; - break; - } - } - - if (ptr->fd == INVALID_SOCKET) - { - WATCHPOINT_STRING("Never got a good file descriptor"); - - /* Failed to connect. schedule next retry */ - if (ptr->root->retry_timeout) - { - struct timeval next_time; - - if (gettimeofday(&next_time, NULL) == 0) - ptr->next_retry= next_time.tv_sec + ptr->root->retry_timeout; - } - - if (timeout_error_occured) - return MEMCACHED_TIMEOUT; - - return MEMCACHED_ERRNO; /* The last error should be from connect() */ - } - - return MEMCACHED_SUCCESS; /* The last error should be from connect() */ -} - -void set_last_disconnected_host(memcached_server_write_instance_st ptr) -{ - // const_cast - memcached_st *root= (memcached_st *)ptr->root; - -#if 0 - WATCHPOINT_STRING(ptr->hostname); - WATCHPOINT_NUMBER(ptr->port); - WATCHPOINT_ERRNO(ptr->cached_errno); -#endif - if (root->last_disconnected_server) - memcached_server_free(root->last_disconnected_server); - root->last_disconnected_server= memcached_server_clone(NULL, ptr); -} - -memcached_return_t memcached_connect(memcached_server_write_instance_st ptr) -{ - memcached_return_t rc= MEMCACHED_NO_SERVERS; - - if (ptr->fd != INVALID_SOCKET) - return MEMCACHED_SUCCESS; - - LIBMEMCACHED_MEMCACHED_CONNECT_START(); - - /* both retry_timeout and server_failure_limit must be set in order to delay retrying a server on error. */ - WATCHPOINT_ASSERT(ptr->root); - if (ptr->root->retry_timeout && ptr->next_retry) - { - struct timeval curr_time; - - gettimeofday(&curr_time, NULL); - - // We should optimize this to remove the allocation if the server was - // the last server to die - if (ptr->next_retry > curr_time.tv_sec) - { - set_last_disconnected_host(ptr); - - return MEMCACHED_SERVER_MARKED_DEAD; - } - } - - // If we are over the counter failure, we just fail. Reject host only - // works if you have a set number of failures. - if (ptr->root->server_failure_limit && ptr->server_failure_counter >= ptr->root->server_failure_limit) - { - set_last_disconnected_host(ptr); - - // @todo fix this by fixing behavior to no longer make use of - // memcached_st - if (_is_auto_eject_host(ptr->root)) - { - run_distribution((memcached_st *)ptr->root); - } - - return MEMCACHED_SERVER_MARKED_DEAD; - } - - /* We need to clean up the multi startup piece */ - switch (ptr->type) - { - case MEMCACHED_CONNECTION_UNKNOWN: - WATCHPOINT_ASSERT(0); - rc= MEMCACHED_NOT_SUPPORTED; - break; - case MEMCACHED_CONNECTION_UDP: - case MEMCACHED_CONNECTION_TCP: - rc= network_connect(ptr); -#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT - if (ptr->fd != INVALID_SOCKET && ptr->root->sasl.callbacks) - { - rc= memcached_sasl_authenticate_connection(ptr); - if (rc != MEMCACHED_SUCCESS) - { - (void)closesocket(ptr->fd); - ptr->fd= INVALID_SOCKET; - } - } -#endif - break; - case MEMCACHED_CONNECTION_UNIX_SOCKET: - rc= unix_socket_connect(ptr); - break; - case MEMCACHED_CONNECTION_MAX: - default: - WATCHPOINT_ASSERT(0); - } - - if (rc == MEMCACHED_SUCCESS) - { - ptr->server_failure_counter= 0; - ptr->next_retry= 0; - } - else - { - ptr->server_failure_counter++; - - set_last_disconnected_host(ptr); - } - - LIBMEMCACHED_MEMCACHED_CONNECT_END(); - - return rc; -} diff --git a/libmemcached/connect.cc b/libmemcached/connect.cc new file mode 100644 index 00000000..1914bc0f --- /dev/null +++ b/libmemcached/connect.cc @@ -0,0 +1,605 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2010 Brian Aker 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + + +#include +#include +#include +#include + +static memcached_return_t connect_poll(memcached_server_st *ptr) +{ + struct pollfd fds[1]; + fds[0].fd = ptr->fd; + fds[0].events = POLLOUT; + + int error; + size_t loop_max= 5; + + while (--loop_max) // Should only loop on cases of ERESTART or EINTR + { + error= poll(fds, 1, ptr->root->connect_timeout); + + switch (error) + { + case 1: + { + int err; + socklen_t len= sizeof (err); + (void)getsockopt(ptr->fd, SOL_SOCKET, SO_ERROR, &err, &len); + + // We check the value to see what happened wth the socket. + if (err == 0) + { + return MEMCACHED_SUCCESS; + } + else + { + ptr->cached_errno= errno; + + return MEMCACHED_ERRNO; + } + } + case 0: + return MEMCACHED_TIMEOUT; + default: // A real error occurred and we need to completely bail + WATCHPOINT_ERRNO(get_socket_errno()); + switch (get_socket_errno()) + { +#ifdef TARGET_OS_LINUX + case ERESTART: +#endif + case EINTR: + continue; + default: + if (fds[0].revents & POLLERR) + { + int err; + socklen_t len= sizeof (err); + (void)getsockopt(ptr->fd, SOL_SOCKET, SO_ERROR, &err, &len); + ptr->cached_errno= (err == 0) ? get_socket_errno() : err; + } + else + { + ptr->cached_errno= get_socket_errno(); + } + + (void)closesocket(ptr->fd); + ptr->fd= INVALID_SOCKET; + + return MEMCACHED_ERRNO; + } + } + } + + // This should only be possible from ERESTART or EINTR; + ptr->cached_errno= get_socket_errno(); + + return MEMCACHED_ERRNO; +} + +static memcached_return_t set_hostinfo(memcached_server_st *server) +{ + char str_port[NI_MAXSERV]; + + assert(! server->address_info); // We cover the case where a programming mistake has been made. + if (server->address_info) + { + freeaddrinfo(server->address_info); + server->address_info= NULL; + server->address_info_next= NULL; + } + + int length= snprintf(str_port, NI_MAXSERV, "%u", (uint32_t)server->port); + if (length >= NI_MAXSERV || length < 0) + return MEMCACHED_FAILURE; + + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + +#if 0 + hints.ai_family= AF_INET; +#endif + if (server->type == MEMCACHED_CONNECTION_UDP) + { + hints.ai_protocol= IPPROTO_UDP; + hints.ai_socktype= SOCK_DGRAM; + } + else + { + hints.ai_socktype= SOCK_STREAM; + hints.ai_protocol= IPPROTO_TCP; + } + + uint32_t counter= 5; + while (--counter) + { + int e= getaddrinfo(server->hostname, str_port, &hints, &server->address_info); + + if (e == 0) + { + break; + } + else if (e == EAI_AGAIN) + { +#ifndef WIN32 + struct timespec dream, rem; + + dream.tv_nsec= 1000; + dream.tv_sec= 0; + + nanosleep(&dream, &rem); +#endif + continue; + } + else + { + WATCHPOINT_STRING(server->hostname); + WATCHPOINT_STRING(gai_strerror(e)); + return MEMCACHED_HOST_LOOKUP_FAILURE; + } + } + + server->address_info_next= server->address_info; + + return MEMCACHED_SUCCESS; +} + +static inline memcached_return_t set_socket_nonblocking(memcached_server_st *ptr) +{ +#ifdef WIN32 + u_long arg = 1; + if (ioctlsocket(ptr->fd, FIONBIO, &arg) == SOCKET_ERROR) + { + ptr->cached_errno= get_socket_errno(); + return MEMCACHED_CONNECTION_FAILURE; + } +#else + int flags; + + do + { + flags= fcntl(ptr->fd, F_GETFL, 0); + } + while (flags == -1 && (errno == EINTR || errno == EAGAIN)); + + unlikely (flags == -1) + { + ptr->cached_errno= errno; + return MEMCACHED_CONNECTION_FAILURE; + } + else if ((flags & O_NONBLOCK) == 0) + { + int rval; + + do + { + rval= fcntl(ptr->fd, F_SETFL, flags | O_NONBLOCK); + } + while (rval == -1 && (errno == EINTR || errno == EAGAIN)); + + unlikely (rval == -1) + { + ptr->cached_errno= errno; + return MEMCACHED_CONNECTION_FAILURE; + } + } +#endif + return MEMCACHED_SUCCESS; +} + +static memcached_return_t set_socket_options(memcached_server_st *ptr) +{ + WATCHPOINT_ASSERT(ptr->fd != -1); + + if (ptr->type == MEMCACHED_CONNECTION_UDP) + return MEMCACHED_SUCCESS; + +#ifdef HAVE_SNDTIMEO + if (ptr->root->snd_timeout) + { + int error; + struct timeval waittime; + + waittime.tv_sec= 0; + waittime.tv_usec= ptr->root->snd_timeout; + + error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDTIMEO, + &waittime, (socklen_t)sizeof(struct timeval)); + WATCHPOINT_ASSERT(error == 0); + if (error) + return MEMCACHED_FAILURE; + } +#endif + +#ifdef HAVE_RCVTIMEO + if (ptr->root->rcv_timeout) + { + int error; + struct timeval waittime; + + waittime.tv_sec= 0; + waittime.tv_usec= ptr->root->rcv_timeout; + + error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVTIMEO, + &waittime, (socklen_t)sizeof(struct timeval)); + WATCHPOINT_ASSERT(error == 0); + if (error) + return MEMCACHED_FAILURE; + } +#endif + + +#if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) + { + int set = 1; + int error= setsockopt(ptr->fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int)); + + // This is not considered a fatal error + if (error == -1) + { + WATCHPOINT_ERRNO(get_socket_errno()); + perror("setsockopt(SO_NOSIGPIPE)"); + } + } +#endif + + if (ptr->root->flags.no_block) + { + int error; + struct linger linger; + + linger.l_onoff= 1; + linger.l_linger= 0; /* By default on close() just drop the socket */ + error= setsockopt(ptr->fd, SOL_SOCKET, SO_LINGER, + &linger, (socklen_t)sizeof(struct linger)); + WATCHPOINT_ASSERT(error == 0); + if (error) + return MEMCACHED_FAILURE; + } + + if (ptr->root->flags.tcp_nodelay) + { + int flag= 1; + int error; + + error= setsockopt(ptr->fd, IPPROTO_TCP, TCP_NODELAY, + &flag, (socklen_t)sizeof(int)); + WATCHPOINT_ASSERT(error == 0); + if (error) + return MEMCACHED_FAILURE; + } + + if (ptr->root->flags.tcp_keepalive) + { + int flag= 1; + int error; + + error= setsockopt(ptr->fd, SOL_SOCKET, SO_KEEPALIVE, + &flag, (socklen_t)sizeof(int)); + WATCHPOINT_ASSERT(error == 0); + if (error) + return MEMCACHED_FAILURE; + } + +#ifdef TCP_KEEPIDLE + if (ptr->root->tcp_keepidle > 0) + { + int error; + + error= setsockopt(ptr->fd, IPPROTO_TCP, TCP_KEEPIDLE, + &ptr->root->tcp_keepidle, (socklen_t)sizeof(int)); + WATCHPOINT_ASSERT(error == 0); + if (error) + return MEMCACHED_FAILURE; + } +#endif + + if (ptr->root->send_size > 0) + { + int error; + + error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDBUF, + &ptr->root->send_size, (socklen_t)sizeof(int)); + WATCHPOINT_ASSERT(error == 0); + if (error) + return MEMCACHED_FAILURE; + } + + if (ptr->root->recv_size > 0) + { + int error; + + error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVBUF, + &ptr->root->recv_size, (socklen_t)sizeof(int)); + WATCHPOINT_ASSERT(error == 0); + if (error) + return MEMCACHED_FAILURE; + } + + + /* libmemcached will always use nonblocking IO to avoid write deadlocks */ + return set_socket_nonblocking(ptr); +} + +static memcached_return_t unix_socket_connect(memcached_server_st *ptr) +{ +#ifndef WIN32 + WATCHPOINT_ASSERT(ptr->fd == -1); + + if ((ptr->fd= socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + { + ptr->cached_errno= errno; + return MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE; + } + + struct sockaddr_un servAddr; + + memset(&servAddr, 0, sizeof (struct sockaddr_un)); + servAddr.sun_family= AF_UNIX; + strncpy(servAddr.sun_path, ptr->hostname, sizeof(servAddr.sun_path)); /* Copy filename */ + +test_connect: + if (connect(ptr->fd, + (struct sockaddr *)&servAddr, + sizeof(servAddr)) < 0) + { + switch (errno) + { + case EINPROGRESS: + case EALREADY: + case EINTR: + goto test_connect; + case EISCONN: /* We were spinning waiting on connect */ + break; + default: + WATCHPOINT_ERRNO(errno); + ptr->cached_errno= errno; + return MEMCACHED_ERRNO; + } + } + + WATCHPOINT_ASSERT(ptr->fd != -1); + + return MEMCACHED_SUCCESS; +#else + (void)ptr; + return MEMCACHED_NOT_SUPPORTED; +#endif +} + +static memcached_return_t network_connect(memcached_server_st *ptr) +{ + bool timeout_error_occured= false; + + WATCHPOINT_ASSERT(ptr->fd == INVALID_SOCKET); + WATCHPOINT_ASSERT(ptr->cursor_active == 0); + + if (! ptr->address_info) + { + memcached_return_t rc= set_hostinfo(ptr); + if (rc != MEMCACHED_SUCCESS) + return rc; + } + + /* Create the socket */ + while (ptr->address_info_next && ptr->fd == INVALID_SOCKET) + { + /* Memcache server does not support IPV6 in udp mode, so skip if not ipv4 */ + if (ptr->type == MEMCACHED_CONNECTION_UDP && ptr->address_info_next->ai_family != AF_INET) + { + ptr->address_info_next= ptr->address_info_next->ai_next; + continue; + } + + if ((ptr->fd= socket(ptr->address_info_next->ai_family, + ptr->address_info_next->ai_socktype, + ptr->address_info_next->ai_protocol)) < 0) + { + ptr->cached_errno= get_socket_errno(); + WATCHPOINT_ERRNO(get_socket_errno()); + return MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE; + } + + (void)set_socket_options(ptr); + + /* connect to server */ + if ((connect(ptr->fd, ptr->address_info_next->ai_addr, ptr->address_info_next->ai_addrlen) != SOCKET_ERROR)) + { + break; // Success + } + + /* An error occurred */ + ptr->cached_errno= get_socket_errno(); + switch (ptr->cached_errno) + { + case EWOULDBLOCK: + case EINPROGRESS: // nonblocking mode - first return + case EALREADY: // nonblocking mode - subsequent returns + { + memcached_return_t rc; + rc= connect_poll(ptr); + + if (rc == MEMCACHED_TIMEOUT) + timeout_error_occured= true; + + if (rc == MEMCACHED_SUCCESS) + break; + } + + case EISCONN: // we are connected :-) + break; + + case EINTR: // Special case, we retry ai_addr + (void)closesocket(ptr->fd); + ptr->fd= INVALID_SOCKET; + continue; + + default: + (void)closesocket(ptr->fd); + ptr->fd= INVALID_SOCKET; + ptr->address_info_next= ptr->address_info_next->ai_next; + break; + } + } + + if (ptr->fd == INVALID_SOCKET) + { + WATCHPOINT_STRING("Never got a good file descriptor"); + + /* Failed to connect. schedule next retry */ + if (ptr->root->retry_timeout) + { + struct timeval next_time; + + if (gettimeofday(&next_time, NULL) == 0) + ptr->next_retry= next_time.tv_sec + ptr->root->retry_timeout; + } + + if (timeout_error_occured) + return MEMCACHED_TIMEOUT; + + return MEMCACHED_ERRNO; /* The last error should be from connect() */ + } + + return MEMCACHED_SUCCESS; /* The last error should be from connect() */ +} + +void set_last_disconnected_host(memcached_server_write_instance_st ptr) +{ + // const_cast + memcached_st *root= (memcached_st *)ptr->root; + +#if 0 + WATCHPOINT_STRING(ptr->hostname); + WATCHPOINT_NUMBER(ptr->port); + WATCHPOINT_ERRNO(ptr->cached_errno); +#endif + if (root->last_disconnected_server) + memcached_server_free(root->last_disconnected_server); + root->last_disconnected_server= memcached_server_clone(NULL, ptr); +} + +memcached_return_t memcached_connect(memcached_server_write_instance_st ptr) +{ + memcached_return_t rc= MEMCACHED_NO_SERVERS; + + if (ptr->fd != INVALID_SOCKET) + return MEMCACHED_SUCCESS; + + LIBMEMCACHED_MEMCACHED_CONNECT_START(); + + /* both retry_timeout and server_failure_limit must be set in order to delay retrying a server on error. */ + WATCHPOINT_ASSERT(ptr->root); + if (ptr->root->retry_timeout && ptr->next_retry) + { + struct timeval curr_time; + + gettimeofday(&curr_time, NULL); + + // We should optimize this to remove the allocation if the server was + // the last server to die + if (ptr->next_retry > curr_time.tv_sec) + { + set_last_disconnected_host(ptr); + + return MEMCACHED_SERVER_MARKED_DEAD; + } + } + + // If we are over the counter failure, we just fail. Reject host only + // works if you have a set number of failures. + if (ptr->root->server_failure_limit && ptr->server_failure_counter >= ptr->root->server_failure_limit) + { + set_last_disconnected_host(ptr); + + // @todo fix this by fixing behavior to no longer make use of + // memcached_st + if (_is_auto_eject_host(ptr->root)) + { + run_distribution((memcached_st *)ptr->root); + } + + return MEMCACHED_SERVER_MARKED_DEAD; + } + + /* We need to clean up the multi startup piece */ + switch (ptr->type) + { + case MEMCACHED_CONNECTION_UNKNOWN: + WATCHPOINT_ASSERT(0); + rc= MEMCACHED_NOT_SUPPORTED; + break; + case MEMCACHED_CONNECTION_UDP: + case MEMCACHED_CONNECTION_TCP: + rc= network_connect(ptr); +#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT + if (ptr->fd != INVALID_SOCKET && ptr->root->sasl.callbacks) + { + rc= memcached_sasl_authenticate_connection(ptr); + if (rc != MEMCACHED_SUCCESS) + { + (void)closesocket(ptr->fd); + ptr->fd= INVALID_SOCKET; + } + } +#endif + break; + case MEMCACHED_CONNECTION_UNIX_SOCKET: + rc= unix_socket_connect(ptr); + break; + case MEMCACHED_CONNECTION_MAX: + default: + WATCHPOINT_ASSERT(0); + } + + if (rc == MEMCACHED_SUCCESS) + { + ptr->server_failure_counter= 0; + ptr->next_retry= 0; + } + else + { + ptr->server_failure_counter++; + + set_last_disconnected_host(ptr); + } + + LIBMEMCACHED_MEMCACHED_CONNECT_END(); + + return rc; +} diff --git a/libmemcached/delete.c b/libmemcached/delete.c deleted file mode 100644 index 78f08f26..00000000 --- a/libmemcached/delete.c +++ /dev/null @@ -1,260 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * Copyright (C) 2006-2009 Brian Aker 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. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * 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. - * - */ - -#include -#include - -memcached_return_t memcached_delete(memcached_st *ptr, const char *key, size_t key_length, - time_t expiration) -{ - return memcached_delete_by_key(ptr, key, key_length, - key, key_length, expiration); -} - -static inline memcached_return_t binary_delete(memcached_st *ptr, - uint32_t server_key, - const char *key, - size_t key_length, - bool flush); - -memcached_return_t memcached_delete_by_key(memcached_st *ptr, - const char *group_key, size_t group_key_length, - const char *key, size_t key_length, - time_t expiration) -{ - bool to_write; - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - uint32_t server_key; - memcached_server_write_instance_st instance; - - LIBMEMCACHED_MEMCACHED_DELETE_START(); - - memcached_return_t rc; - if ((rc= initialize_query(ptr)) != MEMCACHED_SUCCESS) - { - return rc; - } - - rc= memcached_validate_key_length(key_length, - ptr->flags.binary_protocol); - unlikely (rc != MEMCACHED_SUCCESS) - return rc; - - unlikely (memcached_server_count(ptr) == 0) - return MEMCACHED_NO_SERVERS; - - server_key= memcached_generate_hash_with_redistribution(ptr, group_key, group_key_length); - instance= memcached_server_instance_fetch(ptr, server_key); - - to_write= (ptr->flags.buffer_requests) ? false : true; - - bool no_reply= (ptr->flags.no_reply); - - if (ptr->flags.binary_protocol) - { - likely (! expiration) - { - rc= binary_delete(ptr, server_key, key, key_length, to_write); - } - else - { - rc= MEMCACHED_INVALID_ARGUMENTS; - } - } - else - { - int send_length; - - unlikely (expiration) - { - if ((instance->major_version == 1 && - instance->minor_version > 2) || - instance->major_version > 1) - { - rc= MEMCACHED_INVALID_ARGUMENTS; - goto error; - } - else - { - /* ensure that we are connected, otherwise we might bump the - * command counter before connection */ - if ((rc= memcached_connect(instance)) != MEMCACHED_SUCCESS) - { - WATCHPOINT_ERROR(rc); - return rc; - } - - if (instance->minor_version == 0) - { - if (no_reply || ! to_write) - { - /* We might get out of sync with the server if we - * send this command to a server newer than 1.2.x.. - * disable no_reply and buffered mode. - */ - to_write= true; - if (no_reply) - memcached_server_response_increment(instance); - no_reply= false; - } - } - send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "delete %.*s%.*s %u%s\r\n", - memcached_print_array(ptr->prefix_key), - (int) key_length, key, - (uint32_t)expiration, - no_reply ? " noreply" :"" ); - } - } - else - { - send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "delete %.*s%.*s%s\r\n", - memcached_print_array(ptr->prefix_key), - (int)key_length, key, no_reply ? " noreply" :""); - } - - if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE || send_length < 0) - { - rc= MEMCACHED_WRITE_FAILURE; - goto error; - } - - if (ptr->flags.use_udp && ! to_write) - { - if (send_length > MAX_UDP_DATAGRAM_LENGTH - UDP_DATAGRAM_HEADER_LENGTH) - return MEMCACHED_WRITE_FAILURE; - if (send_length + instance->write_buffer_offset > MAX_UDP_DATAGRAM_LENGTH) - memcached_io_write(instance, NULL, 0, true); - } - - rc= memcached_do(instance, buffer, (size_t)send_length, to_write); - } - - if (rc != MEMCACHED_SUCCESS) - goto error; - - if (! to_write) - { - rc= MEMCACHED_BUFFERED; - } - else if (!no_reply) - { - rc= memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); - if (rc == MEMCACHED_DELETED) - rc= MEMCACHED_SUCCESS; - } - - if (rc == MEMCACHED_SUCCESS && ptr->delete_trigger) - ptr->delete_trigger(ptr, key, key_length); - -error: - LIBMEMCACHED_MEMCACHED_DELETE_END(); - return rc; -} - -static inline memcached_return_t binary_delete(memcached_st *ptr, - uint32_t server_key, - const char *key, - size_t key_length, - bool flush) -{ - memcached_server_write_instance_st instance; - protocol_binary_request_delete request= {.bytes= {0}}; - - instance= memcached_server_instance_fetch(ptr, server_key); - - request.message.header.request.magic= PROTOCOL_BINARY_REQ; - if (ptr->flags.no_reply) - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_DELETEQ; - else - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_DELETE; - request.message.header.request.keylen= htons((uint16_t)(key_length + memcached_array_size(ptr->prefix_key))); - request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; - request.message.header.request.bodylen= htonl((uint32_t)(key_length + memcached_array_size(ptr->prefix_key))); - - if (ptr->flags.use_udp && ! flush) - { - size_t cmd_size= sizeof(request.bytes) + key_length; - if (cmd_size > MAX_UDP_DATAGRAM_LENGTH - UDP_DATAGRAM_HEADER_LENGTH) - return MEMCACHED_WRITE_FAILURE; - if (cmd_size + instance->write_buffer_offset > MAX_UDP_DATAGRAM_LENGTH) - memcached_io_write(instance, NULL, 0, true); - } - - struct libmemcached_io_vector_st vector[]= - { - { .length= sizeof(request.bytes), .buffer= request.bytes}, - { .length= memcached_array_size(ptr->prefix_key), .buffer= memcached_array_string(ptr->prefix_key) }, - { .length= key_length, .buffer= key }, - }; - - memcached_return_t rc= MEMCACHED_SUCCESS; - - if ((rc= memcached_vdo(instance, vector, 3, flush)) != MEMCACHED_SUCCESS) - { - memcached_io_reset(instance); - rc= (rc == MEMCACHED_SUCCESS) ? MEMCACHED_WRITE_FAILURE : rc; - } - - unlikely (ptr->number_of_replicas > 0) - { - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_DELETEQ; - - for (uint32_t x= 0; x < ptr->number_of_replicas; ++x) - { - memcached_server_write_instance_st replica; - - ++server_key; - if (server_key == memcached_server_count(ptr)) - server_key= 0; - - replica= memcached_server_instance_fetch(ptr, server_key); - - if (memcached_vdo(replica, vector, 3, flush) != MEMCACHED_SUCCESS) - { - memcached_io_reset(replica); - } - else - { - memcached_server_response_decrement(replica); - } - } - } - - return rc; -} diff --git a/libmemcached/delete.cc b/libmemcached/delete.cc new file mode 100644 index 00000000..1005cb90 --- /dev/null +++ b/libmemcached/delete.cc @@ -0,0 +1,264 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2009 Brian Aker 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + +#include +#include + +memcached_return_t memcached_delete(memcached_st *ptr, const char *key, size_t key_length, + time_t expiration) +{ + return memcached_delete_by_key(ptr, key, key_length, + key, key_length, expiration); +} + +static inline memcached_return_t binary_delete(memcached_st *ptr, + uint32_t server_key, + const char *key, + size_t key_length, + bool flush); + +memcached_return_t memcached_delete_by_key(memcached_st *ptr, + const char *group_key, size_t group_key_length, + const char *key, size_t key_length, + time_t expiration) +{ + bool to_write; + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + uint32_t server_key; + memcached_server_write_instance_st instance; + + LIBMEMCACHED_MEMCACHED_DELETE_START(); + + memcached_return_t rc; + if ((rc= initialize_query(ptr)) != MEMCACHED_SUCCESS) + { + return rc; + } + + rc= memcached_validate_key_length(key_length, + ptr->flags.binary_protocol); + unlikely (rc != MEMCACHED_SUCCESS) + return rc; + + unlikely (memcached_server_count(ptr) == 0) + return MEMCACHED_NO_SERVERS; + + server_key= memcached_generate_hash_with_redistribution(ptr, group_key, group_key_length); + instance= memcached_server_instance_fetch(ptr, server_key); + + to_write= (ptr->flags.buffer_requests) ? false : true; + + bool no_reply= (ptr->flags.no_reply); + + if (ptr->flags.binary_protocol) + { + likely (! expiration) + { + rc= binary_delete(ptr, server_key, key, key_length, to_write); + } + else + { + rc= MEMCACHED_INVALID_ARGUMENTS; + } + } + else + { + int send_length; + + unlikely (expiration) + { + if ((instance->major_version == 1 && + instance->minor_version > 2) || + instance->major_version > 1) + { + rc= MEMCACHED_INVALID_ARGUMENTS; + goto error; + } + else + { + /* ensure that we are connected, otherwise we might bump the + * command counter before connection */ + if ((rc= memcached_connect(instance)) != MEMCACHED_SUCCESS) + { + WATCHPOINT_ERROR(rc); + return rc; + } + + if (instance->minor_version == 0) + { + if (no_reply || ! to_write) + { + /* We might get out of sync with the server if we + * send this command to a server newer than 1.2.x.. + * disable no_reply and buffered mode. + */ + to_write= true; + if (no_reply) + memcached_server_response_increment(instance); + no_reply= false; + } + } + send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "delete %.*s%.*s %u%s\r\n", + memcached_print_array(ptr->prefix_key), + (int) key_length, key, + (uint32_t)expiration, + no_reply ? " noreply" :"" ); + } + } + else + { + send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "delete %.*s%.*s%s\r\n", + memcached_print_array(ptr->prefix_key), + (int)key_length, key, no_reply ? " noreply" :""); + } + + if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE || send_length < 0) + { + rc= MEMCACHED_WRITE_FAILURE; + goto error; + } + + if (ptr->flags.use_udp && ! to_write) + { + if (send_length > MAX_UDP_DATAGRAM_LENGTH - UDP_DATAGRAM_HEADER_LENGTH) + return MEMCACHED_WRITE_FAILURE; + if (send_length + instance->write_buffer_offset > MAX_UDP_DATAGRAM_LENGTH) + memcached_io_write(instance, NULL, 0, true); + } + + rc= memcached_do(instance, buffer, (size_t)send_length, to_write); + } + + if (rc != MEMCACHED_SUCCESS) + goto error; + + if (! to_write) + { + rc= MEMCACHED_BUFFERED; + } + else if (!no_reply) + { + rc= memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); + if (rc == MEMCACHED_DELETED) + rc= MEMCACHED_SUCCESS; + } + + if (rc == MEMCACHED_SUCCESS && ptr->delete_trigger) + ptr->delete_trigger(ptr, key, key_length); + +error: + LIBMEMCACHED_MEMCACHED_DELETE_END(); + return rc; +} + +static inline memcached_return_t binary_delete(memcached_st *ptr, + uint32_t server_key, + const char *key, + size_t key_length, + bool flush) +{ + memcached_server_write_instance_st instance; + protocol_binary_request_delete request= {}; + + instance= memcached_server_instance_fetch(ptr, server_key); + + request.message.header.request.magic= PROTOCOL_BINARY_REQ; + if (ptr->flags.no_reply) + { + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_DELETEQ; + } + else + { + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_DELETE; + } + request.message.header.request.keylen= htons((uint16_t)(key_length + memcached_array_size(ptr->prefix_key))); + request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; + request.message.header.request.bodylen= htonl((uint32_t)(key_length + memcached_array_size(ptr->prefix_key))); + + if (ptr->flags.use_udp && ! flush) + { + size_t cmd_size= sizeof(request.bytes) + key_length; + if (cmd_size > MAX_UDP_DATAGRAM_LENGTH - UDP_DATAGRAM_HEADER_LENGTH) + return MEMCACHED_WRITE_FAILURE; + if (cmd_size + instance->write_buffer_offset > MAX_UDP_DATAGRAM_LENGTH) + memcached_io_write(instance, NULL, 0, true); + } + + struct libmemcached_io_vector_st vector[]= + { + { sizeof(request.bytes), request.bytes}, + { memcached_array_size(ptr->prefix_key), memcached_array_string(ptr->prefix_key) }, + { key_length, key }, + }; + + memcached_return_t rc= MEMCACHED_SUCCESS; + + if ((rc= memcached_vdo(instance, vector, 3, flush)) != MEMCACHED_SUCCESS) + { + memcached_io_reset(instance); + rc= (rc == MEMCACHED_SUCCESS) ? MEMCACHED_WRITE_FAILURE : rc; + } + + unlikely (ptr->number_of_replicas > 0) + { + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_DELETEQ; + + for (uint32_t x= 0; x < ptr->number_of_replicas; ++x) + { + memcached_server_write_instance_st replica; + + ++server_key; + if (server_key == memcached_server_count(ptr)) + server_key= 0; + + replica= memcached_server_instance_fetch(ptr, server_key); + + if (memcached_vdo(replica, vector, 3, flush) != MEMCACHED_SUCCESS) + { + memcached_io_reset(replica); + } + else + { + memcached_server_response_decrement(replica); + } + } + } + + return rc; +} diff --git a/libmemcached/delete.h b/libmemcached/delete.h index 8d0d7eda..617d5857 100644 --- a/libmemcached/delete.h +++ b/libmemcached/delete.h @@ -1,16 +1,42 @@ -/* LibMemcached - * Copyright (C) 2010 Brian Aker - * All rights reserved. +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker All rights reserved. * - * Summary: Delete a key from the server. + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. * */ -#ifndef __LIBMEMCACHED_DELETE_H__ -#define __LIBMEMCACHED_DELETE_H__ + +#pragma once #ifdef __cplusplus extern "C" { @@ -29,5 +55,3 @@ memcached_return_t memcached_delete_by_key(memcached_st *ptr, #ifdef __cplusplus } #endif - -#endif /* __LIBMEMCACHED_DELETE_H__ */ diff --git a/libmemcached/do.c b/libmemcached/do.c deleted file mode 100644 index 14824a64..00000000 --- a/libmemcached/do.c +++ /dev/null @@ -1,99 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2010 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: - * - */ - -#include "common.h" - -memcached_return_t memcached_do(memcached_server_write_instance_st ptr, const void *command, - size_t command_length, bool with_flush) -{ - memcached_return_t rc; - ssize_t sent_length; - - WATCHPOINT_ASSERT(command_length); - WATCHPOINT_ASSERT(command); - - if ((rc= memcached_connect(ptr)) != MEMCACHED_SUCCESS) - { - WATCHPOINT_ERROR(rc); - return rc; - } - - /* - ** Since non buffering ops in UDP mode dont check to make sure they will fit - ** before they start writing, if there is any data in buffer, clear it out, - ** otherwise we might get a partial write. - **/ - if (ptr->type == MEMCACHED_CONNECTION_UDP && with_flush && ptr->write_buffer_offset > UDP_DATAGRAM_HEADER_LENGTH) - { - memcached_io_write(ptr, NULL, 0, true); - } - - sent_length= memcached_io_write(ptr, command, command_length, with_flush); - - if (sent_length == -1 || (size_t)sent_length != command_length) - { - rc= MEMCACHED_WRITE_FAILURE; - } - else if ((ptr->root->flags.no_reply) == 0) - { - memcached_server_response_increment(ptr); - } - - return rc; -} - -memcached_return_t memcached_vdo(memcached_server_write_instance_st ptr, - const struct libmemcached_io_vector_st *vector, size_t count, - bool with_flush) -{ - memcached_return_t rc; - ssize_t sent_length; - - WATCHPOINT_ASSERT(count); - WATCHPOINT_ASSERT(vector); - - if ((rc= memcached_connect(ptr)) != MEMCACHED_SUCCESS) - { - WATCHPOINT_ERROR(rc); - return rc; - } - - /* - ** Since non buffering ops in UDP mode dont check to make sure they will fit - ** before they start writing, if there is any data in buffer, clear it out, - ** otherwise we might get a partial write. - **/ - if (ptr->type == MEMCACHED_CONNECTION_UDP && with_flush && ptr->write_buffer_offset > UDP_DATAGRAM_HEADER_LENGTH) - { - memcached_io_write(ptr, NULL, 0, true); - } - - sent_length= memcached_io_writev(ptr, vector, count, with_flush); - - size_t command_length= 0; - for (uint32_t x= 0; x < count; ++x, vector++) - { - command_length+= vector->length; - } - - if (sent_length == -1 || (size_t)sent_length != command_length) - { - rc= MEMCACHED_WRITE_FAILURE; - WATCHPOINT_ERROR(rc); - WATCHPOINT_ERRNO(errno); - } - else if ((ptr->root->flags.no_reply) == 0) - { - memcached_server_response_increment(ptr); - } - - return rc; -} diff --git a/libmemcached/do.cc b/libmemcached/do.cc new file mode 100644 index 00000000..14824a64 --- /dev/null +++ b/libmemcached/do.cc @@ -0,0 +1,99 @@ +/* LibMemcached + * Copyright (C) 2006-2010 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: + * + */ + +#include "common.h" + +memcached_return_t memcached_do(memcached_server_write_instance_st ptr, const void *command, + size_t command_length, bool with_flush) +{ + memcached_return_t rc; + ssize_t sent_length; + + WATCHPOINT_ASSERT(command_length); + WATCHPOINT_ASSERT(command); + + if ((rc= memcached_connect(ptr)) != MEMCACHED_SUCCESS) + { + WATCHPOINT_ERROR(rc); + return rc; + } + + /* + ** Since non buffering ops in UDP mode dont check to make sure they will fit + ** before they start writing, if there is any data in buffer, clear it out, + ** otherwise we might get a partial write. + **/ + if (ptr->type == MEMCACHED_CONNECTION_UDP && with_flush && ptr->write_buffer_offset > UDP_DATAGRAM_HEADER_LENGTH) + { + memcached_io_write(ptr, NULL, 0, true); + } + + sent_length= memcached_io_write(ptr, command, command_length, with_flush); + + if (sent_length == -1 || (size_t)sent_length != command_length) + { + rc= MEMCACHED_WRITE_FAILURE; + } + else if ((ptr->root->flags.no_reply) == 0) + { + memcached_server_response_increment(ptr); + } + + return rc; +} + +memcached_return_t memcached_vdo(memcached_server_write_instance_st ptr, + const struct libmemcached_io_vector_st *vector, size_t count, + bool with_flush) +{ + memcached_return_t rc; + ssize_t sent_length; + + WATCHPOINT_ASSERT(count); + WATCHPOINT_ASSERT(vector); + + if ((rc= memcached_connect(ptr)) != MEMCACHED_SUCCESS) + { + WATCHPOINT_ERROR(rc); + return rc; + } + + /* + ** Since non buffering ops in UDP mode dont check to make sure they will fit + ** before they start writing, if there is any data in buffer, clear it out, + ** otherwise we might get a partial write. + **/ + if (ptr->type == MEMCACHED_CONNECTION_UDP && with_flush && ptr->write_buffer_offset > UDP_DATAGRAM_HEADER_LENGTH) + { + memcached_io_write(ptr, NULL, 0, true); + } + + sent_length= memcached_io_writev(ptr, vector, count, with_flush); + + size_t command_length= 0; + for (uint32_t x= 0; x < count; ++x, vector++) + { + command_length+= vector->length; + } + + if (sent_length == -1 || (size_t)sent_length != command_length) + { + rc= MEMCACHED_WRITE_FAILURE; + WATCHPOINT_ERROR(rc); + WATCHPOINT_ERRNO(errno); + } + else if ((ptr->root->flags.no_reply) == 0) + { + memcached_server_response_increment(ptr); + } + + return rc; +} diff --git a/libmemcached/dump.c b/libmemcached/dump.c deleted file mode 100644 index 18c15974..00000000 --- a/libmemcached/dump.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - We use this to dump all keys. - - At this point we only support a callback method. This could be optimized by first - calling items and finding active slabs. For the moment though we just loop through - all slabs on servers and "grab" the keys. -*/ - -#include "common.h" -static memcached_return_t ascii_dump(memcached_st *ptr, memcached_dump_fn *callback, void *context, uint32_t number_of_callbacks) -{ - memcached_return_t rc= MEMCACHED_SUCCESS; - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - uint32_t server_key; - uint32_t x; - - unlikely (memcached_server_count(ptr) == 0) - return MEMCACHED_NO_SERVERS; - - for (server_key= 0; server_key < memcached_server_count(ptr); server_key++) - { - memcached_server_write_instance_st instance; - instance= memcached_server_instance_fetch(ptr, server_key); - - /* 256 I BELIEVE is the upper limit of slabs */ - for (x= 0; x < 256; x++) - { - int send_length; - send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "stats cachedump %u 0 0\r\n", x); - - if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE || send_length < 0) - { - return MEMCACHED_FAILURE; - } - - rc= memcached_do(instance, buffer, (size_t)send_length, true); - - unlikely (rc != MEMCACHED_SUCCESS) - goto error; - - while (1) - { - uint32_t callback_counter; - rc= memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); - - if (rc == MEMCACHED_ITEM) - { - char *string_ptr, *end_ptr; - char *key; - - string_ptr= buffer; - string_ptr+= 5; /* Move past ITEM */ - for (end_ptr= string_ptr; isgraph(*end_ptr); end_ptr++); - key= string_ptr; - key[(size_t)(end_ptr-string_ptr)]= 0; - for (callback_counter= 0; callback_counter < number_of_callbacks; callback_counter++) - { - rc= (*callback[callback_counter])(ptr, key, (size_t)(end_ptr-string_ptr), context); - if (rc != MEMCACHED_SUCCESS) - break; - } - } - else if (rc == MEMCACHED_END) - break; - else if (rc == MEMCACHED_SERVER_ERROR || rc == MEMCACHED_CLIENT_ERROR) - { - /* If we try to request stats cachedump for a slab class that is too big - * the server will return an incorrect error message: - * "MEMCACHED_SERVER_ERROR failed to allocate memory" - * This isn't really a fatal error, so let's just skip it. I want to - * fix the return value from the memcached server to a CLIENT_ERROR, - * so let's add support for that as well right now. - */ - rc= MEMCACHED_END; - break; - } - else - goto error; - } - } - } - -error: - if (rc == MEMCACHED_END) - return MEMCACHED_SUCCESS; - else - return rc; -} - -memcached_return_t memcached_dump(memcached_st *ptr, memcached_dump_fn *callback, void *context, uint32_t number_of_callbacks) -{ - memcached_return_t rc; - if ((rc= initialize_query(ptr)) != MEMCACHED_SUCCESS) - { - return rc; - } - - /* - No support for Binary protocol yet - @todo Fix this so that we just flush, switch to ascii, and then go back to binary. - */ - if (ptr->flags.binary_protocol) - return MEMCACHED_FAILURE; - - return ascii_dump(ptr, callback, context, number_of_callbacks); -} diff --git a/libmemcached/dump.cc b/libmemcached/dump.cc new file mode 100644 index 00000000..18c15974 --- /dev/null +++ b/libmemcached/dump.cc @@ -0,0 +1,107 @@ +/* + We use this to dump all keys. + + At this point we only support a callback method. This could be optimized by first + calling items and finding active slabs. For the moment though we just loop through + all slabs on servers and "grab" the keys. +*/ + +#include "common.h" +static memcached_return_t ascii_dump(memcached_st *ptr, memcached_dump_fn *callback, void *context, uint32_t number_of_callbacks) +{ + memcached_return_t rc= MEMCACHED_SUCCESS; + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + uint32_t server_key; + uint32_t x; + + unlikely (memcached_server_count(ptr) == 0) + return MEMCACHED_NO_SERVERS; + + for (server_key= 0; server_key < memcached_server_count(ptr); server_key++) + { + memcached_server_write_instance_st instance; + instance= memcached_server_instance_fetch(ptr, server_key); + + /* 256 I BELIEVE is the upper limit of slabs */ + for (x= 0; x < 256; x++) + { + int send_length; + send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "stats cachedump %u 0 0\r\n", x); + + if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE || send_length < 0) + { + return MEMCACHED_FAILURE; + } + + rc= memcached_do(instance, buffer, (size_t)send_length, true); + + unlikely (rc != MEMCACHED_SUCCESS) + goto error; + + while (1) + { + uint32_t callback_counter; + rc= memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); + + if (rc == MEMCACHED_ITEM) + { + char *string_ptr, *end_ptr; + char *key; + + string_ptr= buffer; + string_ptr+= 5; /* Move past ITEM */ + for (end_ptr= string_ptr; isgraph(*end_ptr); end_ptr++); + key= string_ptr; + key[(size_t)(end_ptr-string_ptr)]= 0; + for (callback_counter= 0; callback_counter < number_of_callbacks; callback_counter++) + { + rc= (*callback[callback_counter])(ptr, key, (size_t)(end_ptr-string_ptr), context); + if (rc != MEMCACHED_SUCCESS) + break; + } + } + else if (rc == MEMCACHED_END) + break; + else if (rc == MEMCACHED_SERVER_ERROR || rc == MEMCACHED_CLIENT_ERROR) + { + /* If we try to request stats cachedump for a slab class that is too big + * the server will return an incorrect error message: + * "MEMCACHED_SERVER_ERROR failed to allocate memory" + * This isn't really a fatal error, so let's just skip it. I want to + * fix the return value from the memcached server to a CLIENT_ERROR, + * so let's add support for that as well right now. + */ + rc= MEMCACHED_END; + break; + } + else + goto error; + } + } + } + +error: + if (rc == MEMCACHED_END) + return MEMCACHED_SUCCESS; + else + return rc; +} + +memcached_return_t memcached_dump(memcached_st *ptr, memcached_dump_fn *callback, void *context, uint32_t number_of_callbacks) +{ + memcached_return_t rc; + if ((rc= initialize_query(ptr)) != MEMCACHED_SUCCESS) + { + return rc; + } + + /* + No support for Binary protocol yet + @todo Fix this so that we just flush, switch to ascii, and then go back to binary. + */ + if (ptr->flags.binary_protocol) + return MEMCACHED_FAILURE; + + return ascii_dump(ptr, callback, context, number_of_callbacks); +} diff --git a/libmemcached/error.cc b/libmemcached/error.cc index ef207a95..02169a28 100644 --- a/libmemcached/error.cc +++ b/libmemcached/error.cc @@ -139,7 +139,7 @@ void memcached_error_print(const memcached_st *self) static void _error_free(memcached_error_t *error) { - if (! error) + if (not error) return; _error_free(error->next); @@ -156,10 +156,11 @@ static void _error_free(memcached_error_t *error) void memcached_error_free(memcached_st *self) { - if (! self) + if (not self) return; _error_free(self->error_messages); + self->error_messages= NULL; } const char *memcached_last_error_message(memcached_st *memc) diff --git a/libmemcached/flush.c b/libmemcached/flush.c deleted file mode 100644 index 8da6b5b5..00000000 --- a/libmemcached/flush.c +++ /dev/null @@ -1,113 +0,0 @@ -#include "common.h" - -static memcached_return_t memcached_flush_binary(memcached_st *ptr, - time_t expiration); -static memcached_return_t memcached_flush_textual(memcached_st *ptr, - time_t expiration); - -memcached_return_t memcached_flush(memcached_st *ptr, time_t expiration) -{ - memcached_return_t rc; - if ((rc= initialize_query(ptr)) != MEMCACHED_SUCCESS) - { - return rc; - } - - LIBMEMCACHED_MEMCACHED_FLUSH_START(); - if (ptr->flags.binary_protocol) - rc= memcached_flush_binary(ptr, expiration); - else - rc= memcached_flush_textual(ptr, expiration); - LIBMEMCACHED_MEMCACHED_FLUSH_END(); - return rc; -} - -static memcached_return_t memcached_flush_textual(memcached_st *ptr, - time_t expiration) -{ - unlikely (memcached_server_count(ptr) == 0) - return MEMCACHED_NO_SERVERS; - - for (unsigned int x= 0; x < memcached_server_count(ptr); x++) - { - memcached_return_t rc; - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - - bool no_reply= ptr->flags.no_reply; - memcached_server_write_instance_st instance= - memcached_server_instance_fetch(ptr, x); - - int send_length; - if (expiration) - { - send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "flush_all %llu%s\r\n", - (unsigned long long)expiration, no_reply ? " noreply" : ""); - } - else - { - send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "flush_all%s\r\n", no_reply ? " noreply" : ""); - } - - if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE || send_length < 0) - { - return MEMCACHED_FAILURE; - } - - rc= memcached_do(instance, buffer, (size_t)send_length, true); - - if (rc == MEMCACHED_SUCCESS && !no_reply) - (void)memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); - } - - return MEMCACHED_SUCCESS; -} - -static memcached_return_t memcached_flush_binary(memcached_st *ptr, - time_t expiration) -{ - protocol_binary_request_flush request= {.bytes= {0}}; - - unlikely (memcached_server_count(ptr) == 0) - return MEMCACHED_NO_SERVERS; - - request.message.header.request.magic= (uint8_t)PROTOCOL_BINARY_REQ; - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_FLUSH; - request.message.header.request.extlen= 4; - request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; - request.message.header.request.bodylen= htonl(request.message.header.request.extlen); - request.message.body.expiration= htonl((uint32_t) expiration); - - for (uint32_t x= 0; x < memcached_server_count(ptr); x++) - { - memcached_server_write_instance_st instance= - memcached_server_instance_fetch(ptr, x); - - if (ptr->flags.no_reply) - { - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_FLUSHQ; - } - else - { - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_FLUSH; - } - - if (memcached_do(instance, request.bytes, sizeof(request.bytes), true) != MEMCACHED_SUCCESS) - { - memcached_io_reset(instance); - return MEMCACHED_WRITE_FAILURE; - } - } - - for (uint32_t x= 0; x < memcached_server_count(ptr); x++) - { - memcached_server_write_instance_st instance= - memcached_server_instance_fetch(ptr, x); - - if (memcached_server_response_count(instance) > 0) - (void)memcached_response(instance, NULL, 0, NULL); - } - - return MEMCACHED_SUCCESS; -} diff --git a/libmemcached/flush.cc b/libmemcached/flush.cc new file mode 100644 index 00000000..6a1364c2 --- /dev/null +++ b/libmemcached/flush.cc @@ -0,0 +1,149 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + +#include + +static memcached_return_t memcached_flush_binary(memcached_st *ptr, + time_t expiration); +static memcached_return_t memcached_flush_textual(memcached_st *ptr, + time_t expiration); + +memcached_return_t memcached_flush(memcached_st *ptr, time_t expiration) +{ + memcached_return_t rc; + if (memcached_failed(rc= initialize_query(ptr))) + { + return rc; + } + + LIBMEMCACHED_MEMCACHED_FLUSH_START(); + if (ptr->flags.binary_protocol) + rc= memcached_flush_binary(ptr, expiration); + else + rc= memcached_flush_textual(ptr, expiration); + LIBMEMCACHED_MEMCACHED_FLUSH_END(); + return rc; +} + +static memcached_return_t memcached_flush_textual(memcached_st *ptr, + time_t expiration) +{ + unlikely (memcached_server_count(ptr) == 0) + return MEMCACHED_NO_SERVERS; + + for (unsigned int x= 0; x < memcached_server_count(ptr); x++) + { + memcached_return_t rc; + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + + bool no_reply= ptr->flags.no_reply; + memcached_server_write_instance_st instance= + memcached_server_instance_fetch(ptr, x); + + int send_length; + if (expiration) + { + send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "flush_all %llu%s\r\n", + (unsigned long long)expiration, no_reply ? " noreply" : ""); + } + else + { + send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "flush_all%s\r\n", no_reply ? " noreply" : ""); + } + + if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE || send_length < 0) + { + return MEMCACHED_FAILURE; + } + + rc= memcached_do(instance, buffer, (size_t)send_length, true); + + if (rc == MEMCACHED_SUCCESS && !no_reply) + (void)memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); + } + + return MEMCACHED_SUCCESS; +} + +static memcached_return_t memcached_flush_binary(memcached_st *ptr, + time_t expiration) +{ + protocol_binary_request_flush request= {}; + + unlikely (memcached_server_count(ptr) == 0) + return MEMCACHED_NO_SERVERS; + + request.message.header.request.magic= (uint8_t)PROTOCOL_BINARY_REQ; + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_FLUSH; + request.message.header.request.extlen= 4; + request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; + request.message.header.request.bodylen= htonl(request.message.header.request.extlen); + request.message.body.expiration= htonl((uint32_t) expiration); + + for (uint32_t x= 0; x < memcached_server_count(ptr); x++) + { + memcached_server_write_instance_st instance= + memcached_server_instance_fetch(ptr, x); + + if (ptr->flags.no_reply) + { + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_FLUSHQ; + } + else + { + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_FLUSH; + } + + if (memcached_do(instance, request.bytes, sizeof(request.bytes), true) != MEMCACHED_SUCCESS) + { + memcached_io_reset(instance); + return MEMCACHED_WRITE_FAILURE; + } + } + + for (uint32_t x= 0; x < memcached_server_count(ptr); x++) + { + memcached_server_write_instance_st instance= + memcached_server_instance_fetch(ptr, x); + + if (memcached_server_response_count(instance) > 0) + (void)memcached_response(instance, NULL, 0, NULL); + } + + return MEMCACHED_SUCCESS; +} diff --git a/libmemcached/flush.h b/libmemcached/flush.h index 36c07598..820a98e7 100644 --- a/libmemcached/flush.h +++ b/libmemcached/flush.h @@ -1,16 +1,41 @@ -/* LibMemcached - * Copyright (C) 2010 Brian Aker - * All rights reserved. +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker All rights reserved. * - * Summary: Flush connections. + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. * */ -#ifndef __LIBMEMCACHED_FLUSH_H__ -#define __LIBMEMCACHED_FLUSH_H__ +#pragma once #ifdef __cplusplus extern "C" { @@ -22,5 +47,3 @@ memcached_return_t memcached_flush(memcached_st *ptr, time_t expiration); #ifdef __cplusplus } #endif - -#endif /* __LIBMEMCACHED_FLUSH_H__ */ diff --git a/libmemcached/flush_buffers.c b/libmemcached/flush_buffers.c deleted file mode 100644 index 649db983..00000000 --- a/libmemcached/flush_buffers.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "common.h" - -memcached_return_t memcached_flush_buffers(memcached_st *memc) -{ - memcached_return_t ret= MEMCACHED_SUCCESS; - - for (uint32_t x= 0; x < memcached_server_count(memc); ++x) - { - memcached_server_write_instance_st instance= - memcached_server_instance_fetch(memc, x); - - if (instance->write_buffer_offset != 0) - { - if (instance->fd == -1 && - (ret= memcached_connect(instance)) != MEMCACHED_SUCCESS) - { - WATCHPOINT_ERROR(ret); - return ret; - } - - if (memcached_io_write(instance, NULL, 0, true) == -1) - { - ret= MEMCACHED_SOME_ERRORS; - } - } - } - - return ret; -} diff --git a/libmemcached/flush_buffers.cc b/libmemcached/flush_buffers.cc new file mode 100644 index 00000000..bb3c4dec --- /dev/null +++ b/libmemcached/flush_buffers.cc @@ -0,0 +1,66 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + +#include + +memcached_return_t memcached_flush_buffers(memcached_st *memc) +{ + memcached_return_t ret= MEMCACHED_SUCCESS; + + for (uint32_t x= 0; x < memcached_server_count(memc); ++x) + { + memcached_server_write_instance_st instance= + memcached_server_instance_fetch(memc, x); + + if (instance->write_buffer_offset != 0) + { + if (instance->fd == -1 && + (ret= memcached_connect(instance)) != MEMCACHED_SUCCESS) + { + WATCHPOINT_ERROR(ret); + return ret; + } + + if (memcached_io_write(instance, NULL, 0, true) == -1) + { + ret= MEMCACHED_SOME_ERRORS; + } + } + } + + return ret; +} diff --git a/libmemcached/flush_buffers.h b/libmemcached/flush_buffers.h index 88d8a81c..31b58687 100644 --- a/libmemcached/flush_buffers.h +++ b/libmemcached/flush_buffers.h @@ -1,16 +1,41 @@ -/* LibMemcached - * Copyright (C) 2010 Brian Aker - * All rights reserved. +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker All rights reserved. * - * Summary: Work with fetching results + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. * */ -#ifndef __LIBMEMCACHED_FLUSH_BUFFERS_H__ -#define __LIBMEMCACHED_FLUSH_BUFFERS_H__ +#pragma once #ifdef __cplusplus extern "C" { @@ -22,5 +47,3 @@ memcached_return_t memcached_flush_buffers(memcached_st *mem); #ifdef __cplusplus } #endif - -#endif /* __LIBMEMCACHED_FLUSH_BUFFERS_H__ */ diff --git a/libmemcached/get.c b/libmemcached/get.c deleted file mode 100644 index 46b6319c..00000000 --- a/libmemcached/get.c +++ /dev/null @@ -1,648 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Get functions for libmemcached - * - */ - -#include "common.h" - -/* - What happens if no servers exist? -*/ -char *memcached_get(memcached_st *ptr, const char *key, - size_t key_length, - size_t *value_length, - uint32_t *flags, - memcached_return_t *error) -{ - return memcached_get_by_key(ptr, NULL, 0, key, key_length, value_length, - flags, error); -} - -static memcached_return_t memcached_mget_by_key_real(memcached_st *ptr, - const char *group_key, - size_t group_key_length, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys, - bool mget_mode); - -char *memcached_get_by_key(memcached_st *ptr, - const char *group_key, - size_t group_key_length, - const char *key, size_t key_length, - size_t *value_length, - uint32_t *flags, - memcached_return_t *error) -{ - char *value; - size_t dummy_length; - uint32_t dummy_flags; - memcached_return_t dummy_error; - - unlikely (ptr->flags.use_udp) - { - *error= MEMCACHED_NOT_SUPPORTED; - return NULL; - } - - /* Request the key */ - *error= memcached_mget_by_key_real(ptr, group_key, group_key_length, - (const char * const *)&key, - &key_length, 1, false); - - value= memcached_fetch(ptr, NULL, NULL, - value_length, flags, error); - /* This is for historical reasons */ - if (*error == MEMCACHED_END) - *error= MEMCACHED_NOTFOUND; - - if (value == NULL) - { - if (ptr->get_key_failure && *error == MEMCACHED_NOTFOUND) - { - memcached_return_t rc; - - memcached_result_reset(&ptr->result); - rc= ptr->get_key_failure(ptr, key, key_length, &ptr->result); - - /* On all failure drop to returning NULL */ - if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED) - { - if (rc == MEMCACHED_BUFFERED) - { - uint64_t latch; /* We use latch to track the state of the original socket */ - latch= memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS); - if (latch == 0) - memcached_behavior_set(ptr, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 1); - - rc= memcached_set(ptr, key, key_length, - (memcached_result_value(&ptr->result)), - (memcached_result_length(&ptr->result)), - 0, - (memcached_result_flags(&ptr->result))); - - if (rc == MEMCACHED_BUFFERED && latch == 0) - memcached_behavior_set(ptr, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 0); - } - else - { - rc= memcached_set(ptr, key, key_length, - (memcached_result_value(&ptr->result)), - (memcached_result_length(&ptr->result)), - 0, - (memcached_result_flags(&ptr->result))); - } - - if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED) - { - *error= rc; - *value_length= memcached_result_length(&ptr->result); - *flags= memcached_result_flags(&ptr->result); - return memcached_string_c_copy(&ptr->result.value); - } - } - } - - return NULL; - } - - (void)memcached_fetch(ptr, NULL, NULL, - &dummy_length, &dummy_flags, - &dummy_error); - WATCHPOINT_ASSERT(dummy_length == 0); - - return value; -} - -memcached_return_t memcached_mget(memcached_st *ptr, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys) -{ - return memcached_mget_by_key(ptr, NULL, 0, keys, key_length, number_of_keys); -} - -static memcached_return_t binary_mget_by_key(memcached_st *ptr, - uint32_t master_server_key, - bool is_group_key_set, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys, - bool mget_mode); - -static memcached_return_t memcached_mget_by_key_real(memcached_st *ptr, - const char *group_key, - size_t group_key_length, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys, - bool mget_mode) -{ - bool failures_occured_in_sending= false; - const char *get_command= "get "; - uint8_t get_command_length= 4; - unsigned int master_server_key= (unsigned int)-1; /* 0 is a valid server id! */ - bool is_group_key_set= false; - - memcached_return_t rc; - if ((rc= initialize_query(ptr)) != MEMCACHED_SUCCESS) - { - return rc; - } - - unlikely (ptr->flags.use_udp) - return MEMCACHED_NOT_SUPPORTED; - - LIBMEMCACHED_MEMCACHED_MGET_START(); - - if (number_of_keys == 0) - return MEMCACHED_NOTFOUND; - - if (ptr->flags.verify_key && (memcached_key_test(keys, key_length, number_of_keys) == MEMCACHED_BAD_KEY_PROVIDED)) - return MEMCACHED_BAD_KEY_PROVIDED; - - if (group_key && group_key_length) - { - if (ptr->flags.verify_key && (memcached_key_test((const char * const *)&group_key, &group_key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED)) - return MEMCACHED_BAD_KEY_PROVIDED; - master_server_key= memcached_generate_hash_with_redistribution(ptr, group_key, group_key_length); - is_group_key_set= true; - } - - /* - Here is where we pay for the non-block API. We need to remove any data sitting - in the queue before we start our get. - - It might be optimum to bounce the connection if count > some number. - */ - for (uint32_t x= 0; x < memcached_server_count(ptr); x++) - { - memcached_server_write_instance_st instance= - memcached_server_instance_fetch(ptr, x); - - if (memcached_server_response_count(instance)) - { - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - - if (ptr->flags.no_block) - (void)memcached_io_write(instance, NULL, 0, true); - - while(memcached_server_response_count(instance)) - (void)memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, &ptr->result); - } - } - - if (ptr->flags.binary_protocol) - { - return binary_mget_by_key(ptr, master_server_key, is_group_key_set, keys, - key_length, number_of_keys, mget_mode); - } - - if (ptr->flags.support_cas) - { - get_command= "gets "; - get_command_length= 5; - } - - /* - If a server fails we warn about errors and start all over with sending keys - to the server. - */ - WATCHPOINT_ASSERT(rc == MEMCACHED_SUCCESS); - size_t hosts_connected= 0; - for (uint32_t x= 0; x < number_of_keys; x++) - { - memcached_server_write_instance_st instance; - uint32_t server_key; - - if (is_group_key_set) - { - server_key= master_server_key; - } - else - { - server_key= memcached_generate_hash_with_redistribution(ptr, keys[x], key_length[x]); - } - - instance= memcached_server_instance_fetch(ptr, server_key); - - struct libmemcached_io_vector_st vector[]= - { - { .length= get_command_length, .buffer= get_command }, - { .length= memcached_array_size(ptr->prefix_key), .buffer= memcached_array_string(ptr->prefix_key) }, - { .length= key_length[x], .buffer= keys[x] }, - { .length= 1, .buffer= " " } - }; - - - if (memcached_server_response_count(instance) == 0) - { - rc= memcached_connect(instance); - - if (rc != MEMCACHED_SUCCESS) - { - continue; - } - hosts_connected++; - - if ((memcached_io_writev(instance, vector, 4, false)) == -1) - { - failures_occured_in_sending= true; - continue; - } - WATCHPOINT_ASSERT(instance->cursor_active == 0); - memcached_server_response_increment(instance); - WATCHPOINT_ASSERT(instance->cursor_active == 1); - } - else - { - if ((memcached_io_writev(instance, (vector + 1), 3, false)) == -1) - { - memcached_server_response_reset(instance); - failures_occured_in_sending= true; - continue; - } - } - } - - if (hosts_connected == 0) - { - LIBMEMCACHED_MEMCACHED_MGET_END(); - - if (rc != MEMCACHED_SUCCESS) - return rc; - - return MEMCACHED_NO_SERVERS; - } - - - /* - Should we muddle on if some servers are dead? - */ - bool success_happened= false; - for (uint32_t x= 0; x < memcached_server_count(ptr); x++) - { - memcached_server_write_instance_st instance= - memcached_server_instance_fetch(ptr, x); - - if (memcached_server_response_count(instance)) - { - /* We need to do something about non-connnected hosts in the future */ - if ((memcached_io_write(instance, "\r\n", 2, true)) == -1) - { - failures_occured_in_sending= true; - } - else - { - success_happened= true; - } - } - } - - LIBMEMCACHED_MEMCACHED_MGET_END(); - - if (failures_occured_in_sending && success_happened) - return MEMCACHED_SOME_ERRORS; - - if (success_happened) - return MEMCACHED_SUCCESS; - - return MEMCACHED_FAILURE; -} - -memcached_return_t memcached_mget_by_key(memcached_st *ptr, - const char *group_key, - size_t group_key_length, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys) -{ - return memcached_mget_by_key_real(ptr, group_key, group_key_length, keys, - key_length, number_of_keys, true); -} - -memcached_return_t memcached_mget_execute(memcached_st *ptr, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys, - memcached_execute_fn *callback, - void *context, - unsigned int number_of_callbacks) -{ - return memcached_mget_execute_by_key(ptr, NULL, 0, keys, key_length, - number_of_keys, callback, - context, number_of_callbacks); -} - -memcached_return_t memcached_mget_execute_by_key(memcached_st *ptr, - const char *group_key, - size_t group_key_length, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys, - memcached_execute_fn *callback, - void *context, - unsigned int number_of_callbacks) -{ - if ((ptr->flags.binary_protocol) == 0) - return MEMCACHED_NOT_SUPPORTED; - - memcached_return_t rc; - memcached_callback_st *original_callbacks= ptr->callbacks; - memcached_callback_st cb= { - .callback= callback, - .context= context, - .number_of_callback= number_of_callbacks - }; - - ptr->callbacks= &cb; - rc= memcached_mget_by_key(ptr, group_key, group_key_length, keys, - key_length, number_of_keys); - ptr->callbacks= original_callbacks; - return rc; -} - -static memcached_return_t simple_binary_mget(memcached_st *ptr, - uint32_t master_server_key, - bool is_group_key_set, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys, bool mget_mode) -{ - memcached_return_t rc= MEMCACHED_NOTFOUND; - - bool flush= (number_of_keys == 1); - - /* - If a server fails we warn about errors and start all over with sending keys - to the server. - */ - for (uint32_t x= 0; x < number_of_keys; ++x) - { - uint32_t server_key; - memcached_server_write_instance_st instance; - - if (is_group_key_set) - { - server_key= master_server_key; - } - else - { - server_key= memcached_generate_hash_with_redistribution(ptr, keys[x], key_length[x]); - } - - instance= memcached_server_instance_fetch(ptr, server_key); - - if (memcached_server_response_count(instance) == 0) - { - rc= memcached_connect(instance); - if (rc != MEMCACHED_SUCCESS) - continue; - } - - protocol_binary_request_getk request= {.bytes= {0}}; - request.message.header.request.magic= PROTOCOL_BINARY_REQ; - if (mget_mode) - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETKQ; - else - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETK; - - memcached_return_t vk; - vk= memcached_validate_key_length(key_length[x], - ptr->flags.binary_protocol); - unlikely (vk != MEMCACHED_SUCCESS) - { - if (x > 0) - { - memcached_io_reset(instance); - } - - return vk; - } - - request.message.header.request.keylen= htons((uint16_t)(key_length[x] + memcached_array_size(ptr->prefix_key))); - request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; - request.message.header.request.bodylen= htonl((uint32_t)( key_length[x] + memcached_array_size(ptr->prefix_key))); - - struct libmemcached_io_vector_st vector[]= - { - { .length= sizeof(request.bytes), .buffer= request.bytes }, - { .length= memcached_array_size(ptr->prefix_key), .buffer= memcached_array_string(ptr->prefix_key) }, - { .length= key_length[x], .buffer= keys[x] } - }; - - if (memcached_io_writev(instance, vector, 3, flush) == -1) - { - memcached_server_response_reset(instance); - rc= MEMCACHED_SOME_ERRORS; - continue; - } - - /* We just want one pending response per server */ - memcached_server_response_reset(instance); - memcached_server_response_increment(instance); - if ((x > 0 && x == ptr->io_key_prefetch) && memcached_flush_buffers(ptr) != MEMCACHED_SUCCESS) - { - rc= MEMCACHED_SOME_ERRORS; - } - } - - if (mget_mode) - { - /* - * Send a noop command to flush the buffers - */ - protocol_binary_request_noop request= {.bytes= {0}}; - request.message.header.request.magic= PROTOCOL_BINARY_REQ; - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_NOOP; - request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; - - for (uint32_t x= 0; x < memcached_server_count(ptr); ++x) - { - memcached_server_write_instance_st instance= - memcached_server_instance_fetch(ptr, x); - - if (memcached_server_response_count(instance)) - { - if (memcached_io_write(instance, NULL, 0, true) == -1) - { - memcached_server_response_reset(instance); - memcached_io_reset(instance); - rc= MEMCACHED_SOME_ERRORS; - } - - if (memcached_io_write(instance, request.bytes, - sizeof(request.bytes), true) == -1) - { - memcached_server_response_reset(instance); - memcached_io_reset(instance); - rc= MEMCACHED_SOME_ERRORS; - } - } - } - } - - - return rc; -} - -static memcached_return_t replication_binary_mget(memcached_st *ptr, - uint32_t* hash, - bool* dead_servers, - const char *const *keys, - const size_t *key_length, - size_t number_of_keys) -{ - memcached_return_t rc= MEMCACHED_NOTFOUND; - uint32_t start= 0; - uint64_t randomize_read= memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ); - - if (randomize_read) - start= (uint32_t)random() % (uint32_t)(ptr->number_of_replicas + 1); - - /* Loop for each replica */ - for (uint32_t replica= 0; replica <= ptr->number_of_replicas; ++replica) - { - bool success= true; - - for (uint32_t x= 0; x < number_of_keys; ++x) - { - memcached_server_write_instance_st instance; - - if (hash[x] == memcached_server_count(ptr)) - continue; /* Already successfully sent */ - - uint32_t server= hash[x] + replica; - - /* In case of randomized reads */ - if (randomize_read && ((server + start) <= (hash[x] + ptr->number_of_replicas))) - server += start; - - while (server >= memcached_server_count(ptr)) - server -= memcached_server_count(ptr); - - if (dead_servers[server]) - continue; - - instance= memcached_server_instance_fetch(ptr, server); - - if (memcached_server_response_count(instance) == 0) - { - rc= memcached_connect(instance); - if (rc != MEMCACHED_SUCCESS) - { - memcached_io_reset(instance); - dead_servers[server]= true; - success= false; - continue; - } - } - - protocol_binary_request_getk request= { - .message.header.request= { - .magic= PROTOCOL_BINARY_REQ, - .opcode= PROTOCOL_BINARY_CMD_GETK, - .keylen= htons((uint16_t)(key_length[x] + memcached_array_size(ptr->prefix_key))), - .datatype= PROTOCOL_BINARY_RAW_BYTES, - .bodylen= htonl((uint32_t)(key_length[x] + memcached_array_size(ptr->prefix_key))) - } - }; - - /* - * We need to disable buffering to actually know that the request was - * successfully sent to the server (so that we should expect a result - * back). It would be nice to do this in buffered mode, but then it - * would be complex to handle all error situations if we got to send - * some of the messages, and then we failed on writing out some others - * and we used the callback interface from memcached_mget_execute so - * that we might have processed some of the responses etc. For now, - * just make sure we work _correctly_ - */ - struct libmemcached_io_vector_st vector[]= - { - { .length= sizeof(request.bytes), .buffer= request.bytes }, - { .length= memcached_array_size(ptr->prefix_key), .buffer= memcached_array_string(ptr->prefix_key) }, - { .length= key_length[x], .buffer= keys[x] } - }; - - if (memcached_io_writev(instance, vector, 3, true) == -1) - { - memcached_io_reset(instance); - dead_servers[server]= true; - success= false; - continue; - } - - memcached_server_response_increment(instance); - hash[x]= memcached_server_count(ptr); - } - - if (success) - break; - } - - return rc; -} - -static memcached_return_t binary_mget_by_key(memcached_st *ptr, - uint32_t master_server_key, - bool is_group_key_set, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys, - bool mget_mode) -{ - memcached_return_t rc; - - if (ptr->number_of_replicas == 0) - { - rc= simple_binary_mget(ptr, master_server_key, is_group_key_set, - keys, key_length, number_of_keys, mget_mode); - } - else - { - uint32_t* hash; - bool* dead_servers; - - hash= libmemcached_malloc(ptr, sizeof(uint32_t) * number_of_keys); - dead_servers= libmemcached_calloc(ptr, memcached_server_count(ptr), sizeof(bool)); - - if (hash == NULL || dead_servers == NULL) - { - libmemcached_free(ptr, hash); - libmemcached_free(ptr, dead_servers); - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - } - - if (is_group_key_set) - { - for (size_t x= 0; x < number_of_keys; x++) - { - hash[x]= master_server_key; - } - } - else - { - for (size_t x= 0; x < number_of_keys; x++) - { - hash[x]= memcached_generate_hash_with_redistribution(ptr, keys[x], key_length[x]); - } - } - - rc= replication_binary_mget(ptr, hash, dead_servers, keys, - key_length, number_of_keys); - - libmemcached_free(ptr, hash); - libmemcached_free(ptr, dead_servers); - - return MEMCACHED_SUCCESS; - } - - return rc; -} diff --git a/libmemcached/get.cc b/libmemcached/get.cc new file mode 100644 index 00000000..29d01bc8 --- /dev/null +++ b/libmemcached/get.cc @@ -0,0 +1,648 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Get functions for libmemcached + * + */ + +#include "common.h" + +/* + What happens if no servers exist? +*/ +char *memcached_get(memcached_st *ptr, const char *key, + size_t key_length, + size_t *value_length, + uint32_t *flags, + memcached_return_t *error) +{ + return memcached_get_by_key(ptr, NULL, 0, key, key_length, value_length, + flags, error); +} + +static memcached_return_t memcached_mget_by_key_real(memcached_st *ptr, + const char *group_key, + size_t group_key_length, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys, + bool mget_mode); + +char *memcached_get_by_key(memcached_st *ptr, + const char *group_key, + size_t group_key_length, + const char *key, size_t key_length, + size_t *value_length, + uint32_t *flags, + memcached_return_t *error) +{ + char *value; + size_t dummy_length; + uint32_t dummy_flags; + memcached_return_t dummy_error; + + unlikely (ptr->flags.use_udp) + { + *error= MEMCACHED_NOT_SUPPORTED; + return NULL; + } + + /* Request the key */ + *error= memcached_mget_by_key_real(ptr, group_key, group_key_length, + (const char * const *)&key, + &key_length, 1, false); + + value= memcached_fetch(ptr, NULL, NULL, + value_length, flags, error); + /* This is for historical reasons */ + if (*error == MEMCACHED_END) + *error= MEMCACHED_NOTFOUND; + + if (value == NULL) + { + if (ptr->get_key_failure && *error == MEMCACHED_NOTFOUND) + { + memcached_return_t rc; + + memcached_result_reset(&ptr->result); + rc= ptr->get_key_failure(ptr, key, key_length, &ptr->result); + + /* On all failure drop to returning NULL */ + if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED) + { + if (rc == MEMCACHED_BUFFERED) + { + uint64_t latch; /* We use latch to track the state of the original socket */ + latch= memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS); + if (latch == 0) + memcached_behavior_set(ptr, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 1); + + rc= memcached_set(ptr, key, key_length, + (memcached_result_value(&ptr->result)), + (memcached_result_length(&ptr->result)), + 0, + (memcached_result_flags(&ptr->result))); + + if (rc == MEMCACHED_BUFFERED && latch == 0) + memcached_behavior_set(ptr, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 0); + } + else + { + rc= memcached_set(ptr, key, key_length, + (memcached_result_value(&ptr->result)), + (memcached_result_length(&ptr->result)), + 0, + (memcached_result_flags(&ptr->result))); + } + + if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED) + { + *error= rc; + *value_length= memcached_result_length(&ptr->result); + *flags= memcached_result_flags(&ptr->result); + return memcached_string_c_copy(&ptr->result.value); + } + } + } + + return NULL; + } + + (void)memcached_fetch(ptr, NULL, NULL, + &dummy_length, &dummy_flags, + &dummy_error); + WATCHPOINT_ASSERT(dummy_length == 0); + + return value; +} + +memcached_return_t memcached_mget(memcached_st *ptr, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys) +{ + return memcached_mget_by_key(ptr, NULL, 0, keys, key_length, number_of_keys); +} + +static memcached_return_t binary_mget_by_key(memcached_st *ptr, + uint32_t master_server_key, + bool is_group_key_set, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys, + bool mget_mode); + +static memcached_return_t memcached_mget_by_key_real(memcached_st *ptr, + const char *group_key, + size_t group_key_length, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys, + bool mget_mode) +{ + bool failures_occured_in_sending= false; + const char *get_command= "get "; + uint8_t get_command_length= 4; + unsigned int master_server_key= (unsigned int)-1; /* 0 is a valid server id! */ + bool is_group_key_set= false; + + memcached_return_t rc; + if (memcached_failed(rc= initialize_query(ptr))) + { + return rc; + } + + unlikely (ptr->flags.use_udp) + return MEMCACHED_NOT_SUPPORTED; + + LIBMEMCACHED_MEMCACHED_MGET_START(); + + if (number_of_keys == 0) + return MEMCACHED_NOTFOUND; + + if (ptr->flags.verify_key && (memcached_key_test(keys, key_length, number_of_keys) == MEMCACHED_BAD_KEY_PROVIDED)) + { + return MEMCACHED_BAD_KEY_PROVIDED; + } + + if (group_key && group_key_length) + { + if (ptr->flags.verify_key and (memcached_key_test((const char * const *)&group_key, &group_key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED)) + return MEMCACHED_BAD_KEY_PROVIDED; + + master_server_key= memcached_generate_hash_with_redistribution(ptr, group_key, group_key_length); + is_group_key_set= true; + } + + /* + Here is where we pay for the non-block API. We need to remove any data sitting + in the queue before we start our get. + + It might be optimum to bounce the connection if count > some number. + */ + for (uint32_t x= 0; x < memcached_server_count(ptr); x++) + { + memcached_server_write_instance_st instance= + memcached_server_instance_fetch(ptr, x); + + if (memcached_server_response_count(instance)) + { + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + + if (ptr->flags.no_block) + (void)memcached_io_write(instance, NULL, 0, true); + + while(memcached_server_response_count(instance)) + (void)memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, &ptr->result); + } + } + + if (ptr->flags.binary_protocol) + { + return binary_mget_by_key(ptr, master_server_key, is_group_key_set, keys, + key_length, number_of_keys, mget_mode); + } + + if (ptr->flags.support_cas) + { + get_command= "gets "; + get_command_length= 5; + } + + /* + If a server fails we warn about errors and start all over with sending keys + to the server. + */ + WATCHPOINT_ASSERT(rc == MEMCACHED_SUCCESS); + size_t hosts_connected= 0; + for (uint32_t x= 0; x < number_of_keys; x++) + { + memcached_server_write_instance_st instance; + uint32_t server_key; + + if (is_group_key_set) + { + server_key= master_server_key; + } + else + { + server_key= memcached_generate_hash_with_redistribution(ptr, keys[x], key_length[x]); + } + + instance= memcached_server_instance_fetch(ptr, server_key); + + struct libmemcached_io_vector_st vector[]= + { + { get_command_length, get_command }, + { memcached_array_size(ptr->prefix_key), memcached_array_string(ptr->prefix_key) }, + { key_length[x], keys[x] }, + { 1, " " } + }; + + + if (memcached_server_response_count(instance) == 0) + { + rc= memcached_connect(instance); + + if (rc != MEMCACHED_SUCCESS) + { + continue; + } + hosts_connected++; + + if ((memcached_io_writev(instance, vector, 4, false)) == -1) + { + failures_occured_in_sending= true; + continue; + } + WATCHPOINT_ASSERT(instance->cursor_active == 0); + memcached_server_response_increment(instance); + WATCHPOINT_ASSERT(instance->cursor_active == 1); + } + else + { + if ((memcached_io_writev(instance, (vector + 1), 3, false)) == -1) + { + memcached_server_response_reset(instance); + failures_occured_in_sending= true; + continue; + } + } + } + + if (hosts_connected == 0) + { + LIBMEMCACHED_MEMCACHED_MGET_END(); + + if (rc != MEMCACHED_SUCCESS) + return rc; + + return MEMCACHED_NO_SERVERS; + } + + + /* + Should we muddle on if some servers are dead? + */ + bool success_happened= false; + for (uint32_t x= 0; x < memcached_server_count(ptr); x++) + { + memcached_server_write_instance_st instance= + memcached_server_instance_fetch(ptr, x); + + if (memcached_server_response_count(instance)) + { + /* We need to do something about non-connnected hosts in the future */ + if ((memcached_io_write(instance, "\r\n", 2, true)) == -1) + { + failures_occured_in_sending= true; + } + else + { + success_happened= true; + } + } + } + + LIBMEMCACHED_MEMCACHED_MGET_END(); + + if (failures_occured_in_sending && success_happened) + return MEMCACHED_SOME_ERRORS; + + if (success_happened) + return MEMCACHED_SUCCESS; + + return MEMCACHED_FAILURE; +} + +memcached_return_t memcached_mget_by_key(memcached_st *ptr, + const char *group_key, + size_t group_key_length, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys) +{ + return memcached_mget_by_key_real(ptr, group_key, group_key_length, keys, + key_length, number_of_keys, true); +} + +memcached_return_t memcached_mget_execute(memcached_st *ptr, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys, + memcached_execute_fn *callback, + void *context, + unsigned int number_of_callbacks) +{ + return memcached_mget_execute_by_key(ptr, NULL, 0, keys, key_length, + number_of_keys, callback, + context, number_of_callbacks); +} + +memcached_return_t memcached_mget_execute_by_key(memcached_st *ptr, + const char *group_key, + size_t group_key_length, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys, + memcached_execute_fn *callback, + void *context, + unsigned int number_of_callbacks) +{ + if ((ptr->flags.binary_protocol) == 0) + return MEMCACHED_NOT_SUPPORTED; + + memcached_return_t rc; + memcached_callback_st *original_callbacks= ptr->callbacks; + memcached_callback_st cb= { + callback, + context, + number_of_callbacks + }; + + ptr->callbacks= &cb; + rc= memcached_mget_by_key(ptr, group_key, group_key_length, keys, + key_length, number_of_keys); + ptr->callbacks= original_callbacks; + return rc; +} + +static memcached_return_t simple_binary_mget(memcached_st *ptr, + uint32_t master_server_key, + bool is_group_key_set, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys, bool mget_mode) +{ + memcached_return_t rc= MEMCACHED_NOTFOUND; + + bool flush= (number_of_keys == 1); + + /* + If a server fails we warn about errors and start all over with sending keys + to the server. + */ + for (uint32_t x= 0; x < number_of_keys; ++x) + { + uint32_t server_key; + memcached_server_write_instance_st instance; + + if (is_group_key_set) + { + server_key= master_server_key; + } + else + { + server_key= memcached_generate_hash_with_redistribution(ptr, keys[x], key_length[x]); + } + + instance= memcached_server_instance_fetch(ptr, server_key); + + if (memcached_server_response_count(instance) == 0) + { + rc= memcached_connect(instance); + if (rc != MEMCACHED_SUCCESS) + continue; + } + + protocol_binary_request_getk request= { }; //= {.bytes= {0}}; + request.message.header.request.magic= PROTOCOL_BINARY_REQ; + if (mget_mode) + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETKQ; + else + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETK; + + memcached_return_t vk; + vk= memcached_validate_key_length(key_length[x], + ptr->flags.binary_protocol); + unlikely (vk != MEMCACHED_SUCCESS) + { + if (x > 0) + { + memcached_io_reset(instance); + } + + return vk; + } + + request.message.header.request.keylen= htons((uint16_t)(key_length[x] + memcached_array_size(ptr->prefix_key))); + request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; + request.message.header.request.bodylen= htonl((uint32_t)( key_length[x] + memcached_array_size(ptr->prefix_key))); + + struct libmemcached_io_vector_st vector[]= + { + { sizeof(request.bytes), request.bytes }, + { memcached_array_size(ptr->prefix_key), memcached_array_string(ptr->prefix_key) }, + { key_length[x], keys[x] } + }; + + if (memcached_io_writev(instance, vector, 3, flush) == -1) + { + memcached_server_response_reset(instance); + rc= MEMCACHED_SOME_ERRORS; + continue; + } + + /* We just want one pending response per server */ + memcached_server_response_reset(instance); + memcached_server_response_increment(instance); + if ((x > 0 && x == ptr->io_key_prefetch) && memcached_flush_buffers(ptr) != MEMCACHED_SUCCESS) + { + rc= MEMCACHED_SOME_ERRORS; + } + } + + if (mget_mode) + { + /* + Send a noop command to flush the buffers + */ + protocol_binary_request_noop request= {}; //= {.bytes= {0}}; + request.message.header.request.magic= PROTOCOL_BINARY_REQ; + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_NOOP; + request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; + + for (uint32_t x= 0; x < memcached_server_count(ptr); ++x) + { + memcached_server_write_instance_st instance= + memcached_server_instance_fetch(ptr, x); + + if (memcached_server_response_count(instance)) + { + if (memcached_io_write(instance, NULL, 0, true) == -1) + { + memcached_server_response_reset(instance); + memcached_io_reset(instance); + rc= MEMCACHED_SOME_ERRORS; + } + + if (memcached_io_write(instance, request.bytes, + sizeof(request.bytes), true) == -1) + { + memcached_server_response_reset(instance); + memcached_io_reset(instance); + rc= MEMCACHED_SOME_ERRORS; + } + } + } + } + + + return rc; +} + +static memcached_return_t replication_binary_mget(memcached_st *ptr, + uint32_t* hash, + bool* dead_servers, + const char *const *keys, + const size_t *key_length, + size_t number_of_keys) +{ + memcached_return_t rc= MEMCACHED_NOTFOUND; + uint32_t start= 0; + uint64_t randomize_read= memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ); + + if (randomize_read) + start= (uint32_t)random() % (uint32_t)(ptr->number_of_replicas + 1); + + /* Loop for each replica */ + for (uint32_t replica= 0; replica <= ptr->number_of_replicas; ++replica) + { + bool success= true; + + for (uint32_t x= 0; x < number_of_keys; ++x) + { + memcached_server_write_instance_st instance; + + if (hash[x] == memcached_server_count(ptr)) + continue; /* Already successfully sent */ + + uint32_t server= hash[x] + replica; + + /* In case of randomized reads */ + if (randomize_read && ((server + start) <= (hash[x] + ptr->number_of_replicas))) + server += start; + + while (server >= memcached_server_count(ptr)) + server -= memcached_server_count(ptr); + + if (dead_servers[server]) + continue; + + instance= memcached_server_instance_fetch(ptr, server); + + if (memcached_server_response_count(instance) == 0) + { + rc= memcached_connect(instance); + if (rc != MEMCACHED_SUCCESS) + { + memcached_io_reset(instance); + dead_servers[server]= true; + success= false; + continue; + } + } + + protocol_binary_request_getk request= {}; + request.message.header.request.magic= PROTOCOL_BINARY_REQ; + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETK; + request.message.header.request.keylen= htons((uint16_t)(key_length[x] + memcached_array_size(ptr->prefix_key))); + request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; + request.message.header.request.bodylen= htonl((uint32_t)(key_length[x] + memcached_array_size(ptr->prefix_key))); + + /* + * We need to disable buffering to actually know that the request was + * successfully sent to the server (so that we should expect a result + * back). It would be nice to do this in buffered mode, but then it + * would be complex to handle all error situations if we got to send + * some of the messages, and then we failed on writing out some others + * and we used the callback interface from memcached_mget_execute so + * that we might have processed some of the responses etc. For now, + * just make sure we work _correctly_ + */ + struct libmemcached_io_vector_st vector[]= + { + { sizeof(request.bytes), request.bytes }, + { memcached_array_size(ptr->prefix_key), memcached_array_string(ptr->prefix_key) }, + { key_length[x], keys[x] } + }; + + if (memcached_io_writev(instance, vector, 3, true) == -1) + { + memcached_io_reset(instance); + dead_servers[server]= true; + success= false; + continue; + } + + memcached_server_response_increment(instance); + hash[x]= memcached_server_count(ptr); + } + + if (success) + break; + } + + return rc; +} + +static memcached_return_t binary_mget_by_key(memcached_st *ptr, + uint32_t master_server_key, + bool is_group_key_set, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys, + bool mget_mode) +{ + memcached_return_t rc; + + if (ptr->number_of_replicas == 0) + { + rc= simple_binary_mget(ptr, master_server_key, is_group_key_set, + keys, key_length, number_of_keys, mget_mode); + } + else + { + uint32_t* hash; + bool* dead_servers; + + hash= static_cast(libmemcached_malloc(ptr, sizeof(uint32_t) * number_of_keys)); + dead_servers= static_cast(libmemcached_calloc(ptr, memcached_server_count(ptr), sizeof(bool))); + + if (hash == NULL || dead_servers == NULL) + { + libmemcached_free(ptr, hash); + libmemcached_free(ptr, dead_servers); + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + } + + if (is_group_key_set) + { + for (size_t x= 0; x < number_of_keys; x++) + { + hash[x]= master_server_key; + } + } + else + { + for (size_t x= 0; x < number_of_keys; x++) + { + hash[x]= memcached_generate_hash_with_redistribution(ptr, keys[x], key_length[x]); + } + } + + rc= replication_binary_mget(ptr, hash, dead_servers, keys, + key_length, number_of_keys); + + libmemcached_free(ptr, hash); + libmemcached_free(ptr, dead_servers); + + return MEMCACHED_SUCCESS; + } + + return rc; +} diff --git a/libmemcached/hash.c b/libmemcached/hash.c deleted file mode 100644 index e5f87a75..00000000 --- a/libmemcached/hash.c +++ /dev/null @@ -1,176 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * Copyright (C) 2006-2009 Brian Aker 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. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * 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. - * - */ - - -#include -#include - - -uint32_t memcached_generate_hash_value(const char *key, size_t key_length, memcached_hash_t hash_algorithm) -{ - return libhashkit_digest(key, key_length, (hashkit_hash_algorithm_t)hash_algorithm); -} - -static inline uint32_t generate_hash(const memcached_st *ptr, const char *key, size_t key_length) -{ - return hashkit_digest(&ptr->hashkit, key, key_length); -} - -static uint32_t dispatch_host(const memcached_st *ptr, uint32_t hash) -{ - switch (ptr->distribution) - { - case MEMCACHED_DISTRIBUTION_CONSISTENT: - case MEMCACHED_DISTRIBUTION_CONSISTENT_WEIGHTED: - case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA: - case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY: - { - uint32_t num= ptr->ketama.continuum_points_counter; - WATCHPOINT_ASSERT(ptr->continuum); - - hash= hash; - memcached_continuum_item_st *begin, *end, *left, *right, *middle; - begin= left= ptr->ketama.continuum; - end= right= ptr->ketama.continuum + num; - - while (left < right) - { - middle= left + (right - left) / 2; - if (middle->value < hash) - left= middle + 1; - else - right= middle; - } - if (right == end) - right= begin; - return right->index; - } - case MEMCACHED_DISTRIBUTION_MODULA: - return hash % memcached_server_count(ptr); - case MEMCACHED_DISTRIBUTION_RANDOM: - return (uint32_t) random() % memcached_server_count(ptr); - case MEMCACHED_DISTRIBUTION_VIRTUAL_BUCKET: - { - return memcached_virtual_bucket_get(ptr, hash); - } - default: - case MEMCACHED_DISTRIBUTION_CONSISTENT_MAX: - WATCHPOINT_ASSERT(0); /* We have added a distribution without extending the logic */ - return hash % memcached_server_count(ptr); - } - /* NOTREACHED */ -} - -/* - One version is public and will not modify the distribution hash, the other will. -*/ -static inline uint32_t _generate_hash_wrapper(const memcached_st *ptr, const char *key, size_t key_length) -{ - WATCHPOINT_ASSERT(memcached_server_count(ptr)); - - if (memcached_server_count(ptr) == 1) - return 0; - - if (ptr->flags.hash_with_prefix_key) - { - size_t temp_length= memcached_array_size(ptr->prefix_key) + key_length; - char temp[temp_length]; - - if (temp_length > MEMCACHED_MAX_KEY -1) - return 0; - - strncpy(temp, memcached_array_string(ptr->prefix_key), memcached_array_size(ptr->prefix_key)); - strncpy(temp + memcached_array_size(ptr->prefix_key), key, key_length); - - return generate_hash(ptr, temp, temp_length); - } - else - { - return generate_hash(ptr, key, key_length); - } -} - -static inline void _regen_for_auto_eject(memcached_st *ptr) -{ - if (_is_auto_eject_host(ptr) && ptr->ketama.next_distribution_rebuild) - { - struct timeval now; - - if (gettimeofday(&now, NULL) == 0 && - now.tv_sec > ptr->ketama.next_distribution_rebuild) - { - run_distribution(ptr); - } - } -} - -void memcached_autoeject(memcached_st *ptr) -{ - _regen_for_auto_eject(ptr); -} - -uint32_t memcached_generate_hash_with_redistribution(memcached_st *ptr, const char *key, size_t key_length) -{ - uint32_t hash= _generate_hash_wrapper(ptr, key, key_length); - - _regen_for_auto_eject(ptr); - - return dispatch_host(ptr, hash); -} - -uint32_t memcached_generate_hash(const memcached_st *ptr, const char *key, size_t key_length) -{ - return dispatch_host(ptr, _generate_hash_wrapper(ptr, key, key_length)); -} - -const hashkit_st *memcached_get_hashkit(const memcached_st *ptr) -{ - return &ptr->hashkit; -} - -memcached_return_t memcached_set_hashkit(memcached_st *self, hashkit_st *hashk) -{ - hashkit_free(&self->hashkit); - hashkit_clone(&self->hashkit, hashk); - - return MEMCACHED_SUCCESS; -} - -const char * libmemcached_string_hash(memcached_hash_t type) -{ - return libhashkit_string_hash((hashkit_hash_algorithm_t)type); -} diff --git a/libmemcached/hash.cc b/libmemcached/hash.cc new file mode 100644 index 00000000..0e6295b8 --- /dev/null +++ b/libmemcached/hash.cc @@ -0,0 +1,176 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2009 Brian Aker 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + + +#include +#include + + +uint32_t memcached_generate_hash_value(const char *key, size_t key_length, memcached_hash_t hash_algorithm) +{ + return libhashkit_digest(key, key_length, (hashkit_hash_algorithm_t)hash_algorithm); +} + +static inline uint32_t generate_hash(const memcached_st *ptr, const char *key, size_t key_length) +{ + return hashkit_digest(&ptr->hashkit, key, key_length); +} + +static uint32_t dispatch_host(const memcached_st *ptr, uint32_t hash) +{ + switch (ptr->distribution) + { + case MEMCACHED_DISTRIBUTION_CONSISTENT: + case MEMCACHED_DISTRIBUTION_CONSISTENT_WEIGHTED: + case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA: + case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY: + { + uint32_t num= ptr->ketama.continuum_points_counter; + WATCHPOINT_ASSERT(ptr->continuum); + + hash= hash; + memcached_continuum_item_st *begin, *end, *left, *right, *middle; + begin= left= ptr->ketama.continuum; + end= right= ptr->ketama.continuum + num; + + while (left < right) + { + middle= left + (right - left) / 2; + if (middle->value < hash) + left= middle + 1; + else + right= middle; + } + if (right == end) + right= begin; + return right->index; + } + case MEMCACHED_DISTRIBUTION_MODULA: + return hash % memcached_server_count(ptr); + case MEMCACHED_DISTRIBUTION_RANDOM: + return (uint32_t) random() % memcached_server_count(ptr); + case MEMCACHED_DISTRIBUTION_VIRTUAL_BUCKET: + { + return memcached_virtual_bucket_get(ptr, hash); + } + default: + case MEMCACHED_DISTRIBUTION_CONSISTENT_MAX: + WATCHPOINT_ASSERT(0); /* We have added a distribution without extending the logic */ + return hash % memcached_server_count(ptr); + } + /* NOTREACHED */ +} + +/* + One version is public and will not modify the distribution hash, the other will. +*/ +static inline uint32_t _generate_hash_wrapper(const memcached_st *ptr, const char *key, size_t key_length) +{ + WATCHPOINT_ASSERT(memcached_server_count(ptr)); + + if (memcached_server_count(ptr) == 1) + return 0; + + if (ptr->flags.hash_with_prefix_key) + { + size_t temp_length= memcached_array_size(ptr->prefix_key) + key_length; + char temp[MEMCACHED_MAX_KEY]; + + if (temp_length > MEMCACHED_MAX_KEY -1) + return 0; + + strncpy(temp, memcached_array_string(ptr->prefix_key), memcached_array_size(ptr->prefix_key)); + strncpy(temp + memcached_array_size(ptr->prefix_key), key, key_length); + + return generate_hash(ptr, temp, temp_length); + } + else + { + return generate_hash(ptr, key, key_length); + } +} + +static inline void _regen_for_auto_eject(memcached_st *ptr) +{ + if (_is_auto_eject_host(ptr) && ptr->ketama.next_distribution_rebuild) + { + struct timeval now; + + if (gettimeofday(&now, NULL) == 0 && + now.tv_sec > ptr->ketama.next_distribution_rebuild) + { + run_distribution(ptr); + } + } +} + +void memcached_autoeject(memcached_st *ptr) +{ + _regen_for_auto_eject(ptr); +} + +uint32_t memcached_generate_hash_with_redistribution(memcached_st *ptr, const char *key, size_t key_length) +{ + uint32_t hash= _generate_hash_wrapper(ptr, key, key_length); + + _regen_for_auto_eject(ptr); + + return dispatch_host(ptr, hash); +} + +uint32_t memcached_generate_hash(const memcached_st *ptr, const char *key, size_t key_length) +{ + return dispatch_host(ptr, _generate_hash_wrapper(ptr, key, key_length)); +} + +const hashkit_st *memcached_get_hashkit(const memcached_st *ptr) +{ + return &ptr->hashkit; +} + +memcached_return_t memcached_set_hashkit(memcached_st *self, hashkit_st *hashk) +{ + hashkit_free(&self->hashkit); + hashkit_clone(&self->hashkit, hashk); + + return MEMCACHED_SUCCESS; +} + +const char * libmemcached_string_hash(memcached_hash_t type) +{ + return libhashkit_string_hash((hashkit_hash_algorithm_t)type); +} diff --git a/libmemcached/hosts.c b/libmemcached/hosts.c deleted file mode 100644 index 7e687667..00000000 --- a/libmemcached/hosts.c +++ /dev/null @@ -1,451 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2010 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: - * - */ - -#include "common.h" -#include - -/* Protoypes (static) */ -static memcached_return_t server_add(memcached_st *ptr, const char *hostname, - in_port_t port, - uint32_t weight, - memcached_connection_t type); - -static memcached_return_t update_continuum(memcached_st *ptr); - -static int compare_servers(const void *p1, const void *p2) -{ - int return_value; - memcached_server_instance_st a= (memcached_server_instance_st)p1; - memcached_server_instance_st b= (memcached_server_instance_st)p2; - - return_value= strcmp(a->hostname, b->hostname); - - if (return_value == 0) - { - return_value= (int) (a->port - b->port); - } - - return return_value; -} - -static void sort_hosts(memcached_st *ptr) -{ - if (memcached_server_count(ptr)) - { - memcached_server_write_instance_st instance; - - qsort(memcached_server_list(ptr), memcached_server_count(ptr), sizeof(memcached_server_st), compare_servers); - instance= memcached_server_instance_fetch(ptr, 0); - instance->number_of_hosts= memcached_server_count(ptr); - } -} - - -memcached_return_t run_distribution(memcached_st *ptr) -{ - if (ptr->flags.use_sort_hosts) - sort_hosts(ptr); - - switch (ptr->distribution) - { - case MEMCACHED_DISTRIBUTION_CONSISTENT: - case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA: - case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY: - case MEMCACHED_DISTRIBUTION_CONSISTENT_WEIGHTED: - return update_continuum(ptr); - case MEMCACHED_DISTRIBUTION_VIRTUAL_BUCKET: - case MEMCACHED_DISTRIBUTION_MODULA: - break; - case MEMCACHED_DISTRIBUTION_RANDOM: - srandom((uint32_t) time(NULL)); - break; - case MEMCACHED_DISTRIBUTION_CONSISTENT_MAX: - default: - WATCHPOINT_ASSERT(0); /* We have added a distribution without extending the logic */ - } - - return MEMCACHED_SUCCESS; -} - -static uint32_t ketama_server_hash(const char *key, size_t key_length, uint32_t alignment) -{ - unsigned char results[16]; - - libhashkit_md5_signature((unsigned char*)key, key_length, results); - - return ((uint32_t) (results[3 + alignment * 4] & 0xFF) << 24) - | ((uint32_t) (results[2 + alignment * 4] & 0xFF) << 16) - | ((uint32_t) (results[1 + alignment * 4] & 0xFF) << 8) - | (results[0 + alignment * 4] & 0xFF); -} - -static int continuum_item_cmp(const void *t1, const void *t2) -{ - memcached_continuum_item_st *ct1= (memcached_continuum_item_st *)t1; - memcached_continuum_item_st *ct2= (memcached_continuum_item_st *)t2; - - /* Why 153? Hmmm... */ - WATCHPOINT_ASSERT(ct1->value != 153); - if (ct1->value == ct2->value) - return 0; - else if (ct1->value > ct2->value) - return 1; - else - return -1; -} - -static memcached_return_t update_continuum(memcached_st *ptr) -{ - uint32_t continuum_index= 0; - memcached_server_st *list; - uint32_t pointer_counter= 0; - uint32_t pointer_per_server= MEMCACHED_POINTS_PER_SERVER; - uint32_t pointer_per_hash= 1; - uint32_t live_servers= 0; - struct timeval now; - - if (gettimeofday(&now, NULL) != 0) - { - memcached_set_errno(ptr, errno, NULL); - return MEMCACHED_ERRNO; - } - - list= memcached_server_list(ptr); - - /* count live servers (those without a retry delay set) */ - bool is_auto_ejecting= _is_auto_eject_host(ptr); - if (is_auto_ejecting) - { - live_servers= 0; - ptr->ketama.next_distribution_rebuild= 0; - for (uint32_t host_index= 0; host_index < memcached_server_count(ptr); ++host_index) - { - if (list[host_index].next_retry <= now.tv_sec) - live_servers++; - else - { - if (ptr->ketama.next_distribution_rebuild == 0 || list[host_index].next_retry < ptr->ketama.next_distribution_rebuild) - ptr->ketama.next_distribution_rebuild= list[host_index].next_retry; - } - } - } - else - { - live_servers= memcached_server_count(ptr); - } - - uint64_t is_ketama_weighted= memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED); - uint32_t points_per_server= (uint32_t) (is_ketama_weighted ? MEMCACHED_POINTS_PER_SERVER_KETAMA : MEMCACHED_POINTS_PER_SERVER); - - if (live_servers == 0) - return MEMCACHED_SUCCESS; - - if (live_servers > ptr->ketama.continuum_count) - { - memcached_continuum_item_st *new_ptr; - - new_ptr= libmemcached_realloc(ptr, ptr->ketama.continuum, - sizeof(memcached_continuum_item_st) * (live_servers + MEMCACHED_CONTINUUM_ADDITION) * points_per_server); - - if (new_ptr == 0) - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - - ptr->ketama.continuum= new_ptr; - ptr->ketama.continuum_count= live_servers + MEMCACHED_CONTINUUM_ADDITION; - } - - uint64_t total_weight= 0; - if (is_ketama_weighted) - { - for (uint32_t host_index = 0; host_index < memcached_server_count(ptr); ++host_index) - { - if (! is_auto_ejecting || list[host_index].next_retry <= now.tv_sec) - { - total_weight += list[host_index].weight; - } - } - } - - for (uint32_t host_index= 0; host_index < memcached_server_count(ptr); ++host_index) - { - if (is_auto_ejecting && list[host_index].next_retry > now.tv_sec) - continue; - - if (is_ketama_weighted) - { - float pct = (float)list[host_index].weight / (float)total_weight; - pointer_per_server= (uint32_t) ((floorf((float) (pct * MEMCACHED_POINTS_PER_SERVER_KETAMA / 4 * (float)live_servers + 0.0000000001))) * 4); - pointer_per_hash= 4; -#ifdef DEBUG - printf("ketama_weighted:%s|%d|%llu|%u\n", - list[host_index].hostname, - list[host_index].port, - (unsigned long long)list[host_index].weight, - pointer_per_server); -#endif - } - - - if (ptr->distribution == MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY) - { - for (uint32_t pointer_index= 0; - pointer_index < pointer_per_server / pointer_per_hash; - pointer_index++) - { - char sort_host[MEMCACHED_MAX_HOST_SORT_LENGTH]= ""; - int sort_host_length; - - // Spymemcached ketema key format is: hostname/ip:port-index - // If hostname is not available then: /ip:port-index - sort_host_length= snprintf(sort_host, MEMCACHED_MAX_HOST_SORT_LENGTH, - "/%s:%u-%u", - list[host_index].hostname, - (uint32_t)list[host_index].port, - pointer_index); - - if (sort_host_length >= MEMCACHED_MAX_HOST_SORT_LENGTH || sort_host_length < 0) - { - return MEMCACHED_FAILURE; - } -#ifdef DEBUG - printf("update_continuum: key is %s\n", sort_host); -#endif - - WATCHPOINT_ASSERT(sort_host_length); - - if (is_ketama_weighted) - { - for (uint32_t x= 0; x < pointer_per_hash; x++) - { - uint32_t value= ketama_server_hash(sort_host, (size_t)sort_host_length, x); - ptr->ketama.continuum[continuum_index].index= host_index; - ptr->ketama.continuum[continuum_index++].value= value; - } - } - else - { - uint32_t value= hashkit_digest(&ptr->distribution_hashkit, sort_host, (size_t)sort_host_length); - ptr->ketama.continuum[continuum_index].index= host_index; - ptr->ketama.continuum[continuum_index++].value= value; - } - } - } - else - { - for (uint32_t pointer_index= 1; - pointer_index <= pointer_per_server / pointer_per_hash; - pointer_index++) - { - char sort_host[MEMCACHED_MAX_HOST_SORT_LENGTH]= ""; - int sort_host_length; - - if (list[host_index].port == MEMCACHED_DEFAULT_PORT) - { - sort_host_length= snprintf(sort_host, MEMCACHED_MAX_HOST_SORT_LENGTH, - "%s-%u", - list[host_index].hostname, - pointer_index - 1); - } - else - { - sort_host_length= snprintf(sort_host, MEMCACHED_MAX_HOST_SORT_LENGTH, - "%s:%u-%u", - list[host_index].hostname, - (uint32_t)list[host_index].port, - pointer_index - 1); - } - - if (sort_host_length >= MEMCACHED_MAX_HOST_SORT_LENGTH || sort_host_length < 0) - { - return MEMCACHED_FAILURE; - } - - WATCHPOINT_ASSERT(sort_host_length); - - if (is_ketama_weighted) - { - for (uint32_t x = 0; x < pointer_per_hash; x++) - { - uint32_t value= ketama_server_hash(sort_host, (size_t)sort_host_length, x); - ptr->ketama.continuum[continuum_index].index= host_index; - ptr->ketama.continuum[continuum_index++].value= value; - } - } - else - { - uint32_t value= hashkit_digest(&ptr->distribution_hashkit, sort_host, (size_t)sort_host_length); - ptr->ketama.continuum[continuum_index].index= host_index; - ptr->ketama.continuum[continuum_index++].value= value; - } - } - } - - pointer_counter+= pointer_per_server; - } - - WATCHPOINT_ASSERT(ptr); - WATCHPOINT_ASSERT(ptr->continuum); - WATCHPOINT_ASSERT(memcached_server_count(ptr) * MEMCACHED_POINTS_PER_SERVER <= MEMCACHED_CONTINUUM_SIZE); - ptr->ketama.continuum_points_counter= pointer_counter; - qsort(ptr->ketama.continuum, ptr->ketama.continuum_points_counter, sizeof(memcached_continuum_item_st), continuum_item_cmp); - -#ifdef DEBUG - for (uint32_t pointer_index= 0; memcached_server_count(ptr) && pointer_index < ((live_servers * MEMCACHED_POINTS_PER_SERVER) - 1); pointer_index++) - { - WATCHPOINT_ASSERT(ptr->continuum[pointer_index].value <= ptr->continuum[pointer_index + 1].value); - } -#endif - - return MEMCACHED_SUCCESS; -} - - -memcached_return_t memcached_server_push(memcached_st *ptr, const memcached_server_list_st list) -{ - uint32_t count; - memcached_server_st *new_host_list; - - if (! list) - return MEMCACHED_SUCCESS; - - count= memcached_server_list_count(list); - new_host_list= libmemcached_realloc(ptr, memcached_server_list(ptr), - sizeof(memcached_server_st) * (count + memcached_server_count(ptr))); - - if (! new_host_list) - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - - memcached_server_list_set(ptr, new_host_list); - - for (uint32_t x= 0; x < count; x++) - { - memcached_server_write_instance_st instance; - - if ((ptr->flags.use_udp && list[x].type != MEMCACHED_CONNECTION_UDP) - || ((list[x].type == MEMCACHED_CONNECTION_UDP) - && ! (ptr->flags.use_udp)) ) - { - return MEMCACHED_INVALID_HOST_PROTOCOL; - } - - WATCHPOINT_ASSERT(list[x].hostname[0] != 0); - - instance= memcached_server_instance_fetch(ptr, memcached_server_count(ptr)); - WATCHPOINT_ASSERT(instance); - - /* TODO check return type */ - instance= memcached_server_create_with(ptr, instance, list[x].hostname, - list[x].port, list[x].weight, list[x].type); - if (! instance) - { - return memcached_set_error(ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, NULL); - } - ptr->number_of_hosts++; - } - - // Provides backwards compatibility with server list. - { - memcached_server_write_instance_st instance; - instance= memcached_server_instance_fetch(ptr, 0); - instance->number_of_hosts= memcached_server_count(ptr); - } - - return run_distribution(ptr); -} - -memcached_return_t memcached_server_add_unix_socket(memcached_st *ptr, - const char *filename) -{ - return memcached_server_add_unix_socket_with_weight(ptr, filename, 0); -} - -memcached_return_t memcached_server_add_unix_socket_with_weight(memcached_st *ptr, - const char *filename, - uint32_t weight) -{ - if (! filename) - return MEMCACHED_FAILURE; - - return server_add(ptr, filename, 0, weight, MEMCACHED_CONNECTION_UNIX_SOCKET); -} - -memcached_return_t memcached_server_add_udp(memcached_st *ptr, - const char *hostname, - in_port_t port) -{ - return memcached_server_add_udp_with_weight(ptr, hostname, port, 0); -} - -memcached_return_t memcached_server_add_udp_with_weight(memcached_st *ptr, - const char *hostname, - in_port_t port, - uint32_t weight) -{ - if (! port) - port= MEMCACHED_DEFAULT_PORT; - - if (! hostname) - hostname= "localhost"; - - return server_add(ptr, hostname, port, weight, MEMCACHED_CONNECTION_UDP); -} - -memcached_return_t memcached_server_add(memcached_st *ptr, - const char *hostname, - in_port_t port) -{ - return memcached_server_add_with_weight(ptr, hostname, port, 0); -} - -memcached_return_t memcached_server_add_with_weight(memcached_st *ptr, - const char *hostname, - in_port_t port, - uint32_t weight) -{ - if (! port) - port= MEMCACHED_DEFAULT_PORT; - - if (! hostname) - hostname= "localhost"; - - return server_add(ptr, hostname, port, weight, MEMCACHED_CONNECTION_TCP); -} - -static memcached_return_t server_add(memcached_st *ptr, const char *hostname, - in_port_t port, - uint32_t weight, - memcached_connection_t type) -{ - memcached_server_st *new_host_list; - memcached_server_write_instance_st instance; - - if ( (ptr->flags.use_udp && type != MEMCACHED_CONNECTION_UDP) - || ( (type == MEMCACHED_CONNECTION_UDP) && (! ptr->flags.use_udp) ) ) - return MEMCACHED_INVALID_HOST_PROTOCOL; - - new_host_list= libmemcached_realloc(ptr, memcached_server_list(ptr), - sizeof(memcached_server_st) * (ptr->number_of_hosts + 1)); - - if (new_host_list == NULL) - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - - memcached_server_list_set(ptr, new_host_list); - - /* TODO: Check return type */ - instance= memcached_server_instance_fetch(ptr, memcached_server_count(ptr)); - (void)memcached_server_create_with(ptr, instance, hostname, port, weight, type); - ptr->number_of_hosts++; - - instance= memcached_server_instance_fetch(ptr, 0); - memcached_servers_set_count(instance, memcached_server_count(ptr)); - - return run_distribution(ptr); -} diff --git a/libmemcached/hosts.cc b/libmemcached/hosts.cc new file mode 100644 index 00000000..14b45589 --- /dev/null +++ b/libmemcached/hosts.cc @@ -0,0 +1,450 @@ +/* LibMemcached + * Copyright (C) 2006-2010 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: + * + */ + +#include "common.h" +#include + +/* Protoypes (static) */ +static memcached_return_t server_add(memcached_st *ptr, const char *hostname, + in_port_t port, + uint32_t weight, + memcached_connection_t type); + +static memcached_return_t update_continuum(memcached_st *ptr); + +static int compare_servers(const void *p1, const void *p2) +{ + int return_value; + memcached_server_instance_st a= (memcached_server_instance_st)p1; + memcached_server_instance_st b= (memcached_server_instance_st)p2; + + return_value= strcmp(a->hostname, b->hostname); + + if (return_value == 0) + { + return_value= (int) (a->port - b->port); + } + + return return_value; +} + +static void sort_hosts(memcached_st *ptr) +{ + if (memcached_server_count(ptr)) + { + memcached_server_write_instance_st instance; + + qsort(memcached_server_list(ptr), memcached_server_count(ptr), sizeof(memcached_server_st), compare_servers); + instance= memcached_server_instance_fetch(ptr, 0); + instance->number_of_hosts= memcached_server_count(ptr); + } +} + + +memcached_return_t run_distribution(memcached_st *ptr) +{ + if (ptr->flags.use_sort_hosts) + sort_hosts(ptr); + + switch (ptr->distribution) + { + case MEMCACHED_DISTRIBUTION_CONSISTENT: + case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA: + case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY: + case MEMCACHED_DISTRIBUTION_CONSISTENT_WEIGHTED: + return update_continuum(ptr); + case MEMCACHED_DISTRIBUTION_VIRTUAL_BUCKET: + case MEMCACHED_DISTRIBUTION_MODULA: + break; + case MEMCACHED_DISTRIBUTION_RANDOM: + srandom((uint32_t) time(NULL)); + break; + case MEMCACHED_DISTRIBUTION_CONSISTENT_MAX: + default: + WATCHPOINT_ASSERT(0); /* We have added a distribution without extending the logic */ + } + + return MEMCACHED_SUCCESS; +} + +static uint32_t ketama_server_hash(const char *key, size_t key_length, uint32_t alignment) +{ + unsigned char results[16]; + + libhashkit_md5_signature((unsigned char*)key, key_length, results); + + return ((uint32_t) (results[3 + alignment * 4] & 0xFF) << 24) + | ((uint32_t) (results[2 + alignment * 4] & 0xFF) << 16) + | ((uint32_t) (results[1 + alignment * 4] & 0xFF) << 8) + | (results[0 + alignment * 4] & 0xFF); +} + +static int continuum_item_cmp(const void *t1, const void *t2) +{ + memcached_continuum_item_st *ct1= (memcached_continuum_item_st *)t1; + memcached_continuum_item_st *ct2= (memcached_continuum_item_st *)t2; + + /* Why 153? Hmmm... */ + WATCHPOINT_ASSERT(ct1->value != 153); + if (ct1->value == ct2->value) + return 0; + else if (ct1->value > ct2->value) + return 1; + else + return -1; +} + +static memcached_return_t update_continuum(memcached_st *ptr) +{ + uint32_t continuum_index= 0; + memcached_server_st *list; + uint32_t pointer_counter= 0; + uint32_t pointer_per_server= MEMCACHED_POINTS_PER_SERVER; + uint32_t pointer_per_hash= 1; + uint32_t live_servers= 0; + struct timeval now; + + if (gettimeofday(&now, NULL) != 0) + { + memcached_set_errno(ptr, errno, NULL); + return MEMCACHED_ERRNO; + } + + list= memcached_server_list(ptr); + + /* count live servers (those without a retry delay set) */ + bool is_auto_ejecting= _is_auto_eject_host(ptr); + if (is_auto_ejecting) + { + live_servers= 0; + ptr->ketama.next_distribution_rebuild= 0; + for (uint32_t host_index= 0; host_index < memcached_server_count(ptr); ++host_index) + { + if (list[host_index].next_retry <= now.tv_sec) + live_servers++; + else + { + if (ptr->ketama.next_distribution_rebuild == 0 || list[host_index].next_retry < ptr->ketama.next_distribution_rebuild) + ptr->ketama.next_distribution_rebuild= list[host_index].next_retry; + } + } + } + else + { + live_servers= memcached_server_count(ptr); + } + + uint64_t is_ketama_weighted= memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED); + uint32_t points_per_server= (uint32_t) (is_ketama_weighted ? MEMCACHED_POINTS_PER_SERVER_KETAMA : MEMCACHED_POINTS_PER_SERVER); + + if (live_servers == 0) + return MEMCACHED_SUCCESS; + + if (live_servers > ptr->ketama.continuum_count) + { + memcached_continuum_item_st *new_ptr; + + new_ptr= static_cast(libmemcached_realloc(ptr, ptr->ketama.continuum, + sizeof(memcached_continuum_item_st) * (live_servers + MEMCACHED_CONTINUUM_ADDITION) * points_per_server)); + + if (new_ptr == 0) + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + + ptr->ketama.continuum= new_ptr; + ptr->ketama.continuum_count= live_servers + MEMCACHED_CONTINUUM_ADDITION; + } + + uint64_t total_weight= 0; + if (is_ketama_weighted) + { + for (uint32_t host_index = 0; host_index < memcached_server_count(ptr); ++host_index) + { + if (! is_auto_ejecting || list[host_index].next_retry <= now.tv_sec) + { + total_weight += list[host_index].weight; + } + } + } + + for (uint32_t host_index= 0; host_index < memcached_server_count(ptr); ++host_index) + { + if (is_auto_ejecting && list[host_index].next_retry > now.tv_sec) + continue; + + if (is_ketama_weighted) + { + float pct = (float)list[host_index].weight / (float)total_weight; + pointer_per_server= (uint32_t) ((floorf((float) (pct * MEMCACHED_POINTS_PER_SERVER_KETAMA / 4 * (float)live_servers + 0.0000000001))) * 4); + pointer_per_hash= 4; +#ifdef DEBUG + printf("ketama_weighted:%s|%d|%llu|%u\n", + list[host_index].hostname, + list[host_index].port, + (unsigned long long)list[host_index].weight, + pointer_per_server); +#endif + } + + + if (ptr->distribution == MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY) + { + for (uint32_t pointer_index= 0; + pointer_index < pointer_per_server / pointer_per_hash; + pointer_index++) + { + char sort_host[MEMCACHED_MAX_HOST_SORT_LENGTH]= ""; + int sort_host_length; + + // Spymemcached ketema key format is: hostname/ip:port-index + // If hostname is not available then: /ip:port-index + sort_host_length= snprintf(sort_host, MEMCACHED_MAX_HOST_SORT_LENGTH, + "/%s:%u-%u", + list[host_index].hostname, + (uint32_t)list[host_index].port, + pointer_index); + + if (sort_host_length >= MEMCACHED_MAX_HOST_SORT_LENGTH || sort_host_length < 0) + { + return MEMCACHED_FAILURE; + } +#ifdef DEBUG + printf("update_continuum: key is %s\n", sort_host); +#endif + + WATCHPOINT_ASSERT(sort_host_length); + + if (is_ketama_weighted) + { + for (uint32_t x= 0; x < pointer_per_hash; x++) + { + uint32_t value= ketama_server_hash(sort_host, (size_t)sort_host_length, x); + ptr->ketama.continuum[continuum_index].index= host_index; + ptr->ketama.continuum[continuum_index++].value= value; + } + } + else + { + uint32_t value= hashkit_digest(&ptr->distribution_hashkit, sort_host, (size_t)sort_host_length); + ptr->ketama.continuum[continuum_index].index= host_index; + ptr->ketama.continuum[continuum_index++].value= value; + } + } + } + else + { + for (uint32_t pointer_index= 1; + pointer_index <= pointer_per_server / pointer_per_hash; + pointer_index++) + { + char sort_host[MEMCACHED_MAX_HOST_SORT_LENGTH]= ""; + int sort_host_length; + + if (list[host_index].port == MEMCACHED_DEFAULT_PORT) + { + sort_host_length= snprintf(sort_host, MEMCACHED_MAX_HOST_SORT_LENGTH, + "%s-%u", + list[host_index].hostname, + pointer_index - 1); + } + else + { + sort_host_length= snprintf(sort_host, MEMCACHED_MAX_HOST_SORT_LENGTH, + "%s:%u-%u", + list[host_index].hostname, + (uint32_t)list[host_index].port, + pointer_index - 1); + } + + if (sort_host_length >= MEMCACHED_MAX_HOST_SORT_LENGTH || sort_host_length < 0) + { + return MEMCACHED_FAILURE; + } + + WATCHPOINT_ASSERT(sort_host_length); + + if (is_ketama_weighted) + { + for (uint32_t x = 0; x < pointer_per_hash; x++) + { + uint32_t value= ketama_server_hash(sort_host, (size_t)sort_host_length, x); + ptr->ketama.continuum[continuum_index].index= host_index; + ptr->ketama.continuum[continuum_index++].value= value; + } + } + else + { + uint32_t value= hashkit_digest(&ptr->distribution_hashkit, sort_host, (size_t)sort_host_length); + ptr->ketama.continuum[continuum_index].index= host_index; + ptr->ketama.continuum[continuum_index++].value= value; + } + } + } + + pointer_counter+= pointer_per_server; + } + + WATCHPOINT_ASSERT(ptr); + WATCHPOINT_ASSERT(ptr->continuum); + WATCHPOINT_ASSERT(memcached_server_count(ptr) * MEMCACHED_POINTS_PER_SERVER <= MEMCACHED_CONTINUUM_SIZE); + ptr->ketama.continuum_points_counter= pointer_counter; + qsort(ptr->ketama.continuum, ptr->ketama.continuum_points_counter, sizeof(memcached_continuum_item_st), continuum_item_cmp); + +#ifdef DEBUG + for (uint32_t pointer_index= 0; memcached_server_count(ptr) && pointer_index < ((live_servers * MEMCACHED_POINTS_PER_SERVER) - 1); pointer_index++) + { + WATCHPOINT_ASSERT(ptr->continuum[pointer_index].value <= ptr->continuum[pointer_index + 1].value); + } +#endif + + return MEMCACHED_SUCCESS; +} + + +memcached_return_t memcached_server_push(memcached_st *ptr, const memcached_server_list_st list) +{ + if (not list) + return MEMCACHED_SUCCESS; + + uint32_t count= memcached_server_list_count(list); + + memcached_server_st *new_host_list; + new_host_list= static_cast(libmemcached_realloc(ptr, memcached_server_list(ptr), + sizeof(memcached_server_st) * (count + memcached_server_count(ptr)))); + + if (not new_host_list) + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + + memcached_server_list_set(ptr, new_host_list); + + for (uint32_t x= 0; x < count; x++) + { + memcached_server_write_instance_st instance; + + if ((ptr->flags.use_udp && list[x].type != MEMCACHED_CONNECTION_UDP) + || ((list[x].type == MEMCACHED_CONNECTION_UDP) + && ! (ptr->flags.use_udp)) ) + { + return MEMCACHED_INVALID_HOST_PROTOCOL; + } + + WATCHPOINT_ASSERT(list[x].hostname[0] != 0); + + instance= memcached_server_instance_fetch(ptr, memcached_server_count(ptr)); + WATCHPOINT_ASSERT(instance); + + /* TODO check return type */ + instance= memcached_server_create_with(ptr, instance, list[x].hostname, + list[x].port, list[x].weight, list[x].type); + if (! instance) + { + return memcached_set_error(ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, NULL); + } + ptr->number_of_hosts++; + } + + // Provides backwards compatibility with server list. + { + memcached_server_write_instance_st instance; + instance= memcached_server_instance_fetch(ptr, 0); + instance->number_of_hosts= memcached_server_count(ptr); + } + + return run_distribution(ptr); +} + +memcached_return_t memcached_server_add_unix_socket(memcached_st *ptr, + const char *filename) +{ + return memcached_server_add_unix_socket_with_weight(ptr, filename, 0); +} + +memcached_return_t memcached_server_add_unix_socket_with_weight(memcached_st *ptr, + const char *filename, + uint32_t weight) +{ + if (! filename) + return MEMCACHED_FAILURE; + + return server_add(ptr, filename, 0, weight, MEMCACHED_CONNECTION_UNIX_SOCKET); +} + +memcached_return_t memcached_server_add_udp(memcached_st *ptr, + const char *hostname, + in_port_t port) +{ + return memcached_server_add_udp_with_weight(ptr, hostname, port, 0); +} + +memcached_return_t memcached_server_add_udp_with_weight(memcached_st *ptr, + const char *hostname, + in_port_t port, + uint32_t weight) +{ + if (! port) + port= MEMCACHED_DEFAULT_PORT; + + if (! hostname) + hostname= "localhost"; + + return server_add(ptr, hostname, port, weight, MEMCACHED_CONNECTION_UDP); +} + +memcached_return_t memcached_server_add(memcached_st *ptr, + const char *hostname, + in_port_t port) +{ + return memcached_server_add_with_weight(ptr, hostname, port, 0); +} + +memcached_return_t memcached_server_add_with_weight(memcached_st *ptr, + const char *hostname, + in_port_t port, + uint32_t weight) +{ + if (! port) + port= MEMCACHED_DEFAULT_PORT; + + if (! hostname) + hostname= "localhost"; + + return server_add(ptr, hostname, port, weight, MEMCACHED_CONNECTION_TCP); +} + +static memcached_return_t server_add(memcached_st *ptr, const char *hostname, + in_port_t port, + uint32_t weight, + memcached_connection_t type) +{ + memcached_server_st *new_host_list; + memcached_server_write_instance_st instance; + + if ( (ptr->flags.use_udp && type != MEMCACHED_CONNECTION_UDP) + || ( (type == MEMCACHED_CONNECTION_UDP) && (! ptr->flags.use_udp) ) ) + return MEMCACHED_INVALID_HOST_PROTOCOL; + + new_host_list= static_cast(libmemcached_realloc(ptr, memcached_server_list(ptr), + sizeof(memcached_server_st) * (ptr->number_of_hosts + 1))); + + if (new_host_list == NULL) + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + + memcached_server_list_set(ptr, new_host_list); + + /* TODO: Check return type */ + instance= memcached_server_instance_fetch(ptr, memcached_server_count(ptr)); + (void)memcached_server_create_with(ptr, instance, hostname, port, weight, type); + ptr->number_of_hosts++; + + instance= memcached_server_instance_fetch(ptr, 0); + memcached_servers_set_count(instance, memcached_server_count(ptr)); + + return run_distribution(ptr); +} diff --git a/libmemcached/include.am b/libmemcached/include.am index f243f12f..28c5513a 100644 --- a/libmemcached/include.am +++ b/libmemcached/include.am @@ -71,104 +71,73 @@ nobase_include_HEADERS+= \ libmemcached/visibility.h \ libmemcached/watchpoint.h -lib_LTLIBRARIES+= libmemcached/libmemcachedprotocol.la -libmemcached_libmemcachedprotocol_la_SOURCES = \ - libmemcached/protocol/ascii_handler.c \ - libmemcached/protocol/binary_handler.c \ - libmemcached/protocol/cache.c \ - libmemcached/protocol/pedantic.c \ - libmemcached/protocol/protocol_handler.c - -libmemcached_libmemcachedprotocol_la_CFLAGS= ${AM_CFLAGS} ${NO_CONVERSION} ${PTHREAD_CFLAGS} -libmemcached_libmemcachedprotocol_la_LDFLAGS= ${AM_LDFLAGS} ${PTHREAD_LIBS} -version-info ${MEMCACHED_PROTOCAL_LIBRARY_VERSION} - -noinst_LTLIBRARIES+= \ - libmemcached/libmemcachedcallbacks.la - -libmemcached_libmemcachedcallbacks_la_CFLAGS = ${AM_CFLAGS} ${NO_STRICT_ALIASING} -libmemcached_libmemcachedcallbacks_la_SOURCES = libmemcached/callback.c - # This noinst lib contains things we want to be ABI private but still want to # either use in client programs or be able to test in test cases # These symbols will not be exposed in the shipped .so noinst_LTLIBRARIES+= libmemcached/libmemcachedinternal.la libmemcached_libmemcachedinternal_la_SOURCES= \ libmemcached/error.cc \ - libmemcached/string.c + libmemcached/string.cc lib_LTLIBRARIES+= libmemcached/libmemcached.la -libmemcached_libmemcached_la_CFLAGS= ${AM_CFLAGS} ${NO_CONVERSION} +libmemcached_libmemcached_la_CFLAGS= \ + ${AM_CFLAGS} \ + ${NO_CONVERSION} \ + -DBUILDING_LIBMEMCACHED + +libmemcached_libmemcached_la_CXXFLAGS= \ + ${AM_CXXFLAGS} \ + ${NO_CONVERSION} \ + -DBUILDING_LIBMEMCACHED + libmemcached_libmemcached_la_SOURCES+= \ - libmemcached/allocators.c \ - libmemcached/analyze.c \ + ${libhashkit_libhashkit_la_SOURCES} \ + libmemcached/allocators.cc \ + libmemcached/analyze.cc \ libmemcached/array.c \ - libmemcached/auto.c \ - libmemcached/behavior.c \ - libmemcached/connect.c \ - libmemcached/delete.c \ - libmemcached/do.c \ - libmemcached/dump.c \ + libmemcached/auto.cc \ + libmemcached/behavior.cc \ + libmemcached/byteorder.cc \ + libmemcached/callback.cc \ + libmemcached/connect.cc \ + libmemcached/delete.cc \ + libmemcached/do.cc \ + libmemcached/dump.cc \ + libmemcached/error.cc \ libmemcached/fetch.c \ - libmemcached/flush.c \ - libmemcached/flush_buffers.c \ - libmemcached/get.c \ - libmemcached/hash.c \ - libmemcached/hosts.c \ + libmemcached/flush.cc \ + libmemcached/flush_buffers.cc \ + libmemcached/get.cc \ + libmemcached/hash.cc \ + libmemcached/hosts.cc \ libmemcached/initialize_query.cc \ - libmemcached/io.c \ - libmemcached/key.c \ - libmemcached/memcached.c \ + libmemcached/io.cc \ + libmemcached/key.cc \ + libmemcached/memcached.cc \ libmemcached/options.cc \ - libmemcached/parse.c \ + libmemcached/parse.cc \ libmemcached/prefix_key.cc \ - libmemcached/purge.c \ - libmemcached/quit.c \ - libmemcached/response.c \ - libmemcached/result.c \ - libmemcached/server.c \ - libmemcached/server_list.c \ - libmemcached/stats.c \ - libmemcached/storage.c \ - libmemcached/strerror.c \ - libmemcached/verbosity.c \ - libmemcached/version.c \ + libmemcached/purge.cc \ + libmemcached/quit.cc \ + libmemcached/response.cc \ + libmemcached/result.cc \ + libmemcached/server.cc \ + libmemcached/server_list.cc \ + libmemcached/stats.cc \ + libmemcached/storage.cc \ + libmemcached/strerror.cc \ + libmemcached/string.cc \ + libmemcached/verbosity.cc \ + libmemcached/version.cc \ libmemcached/virtual_bucket.c libmemcached/options.cc: libmemcached/options/parser.h -libmemcached_libmemcached_la_DEPENDENCIES= libmemcached/libmemcachedcallbacks.la libmemcached/libmemcachedinternal.la libhashkit/libhashkitinc.la -libmemcached_libmemcached_la_LIBADD= $(LIBM) libmemcached/libmemcachedcallbacks.la libmemcached/libmemcachedinternal.la libhashkit/libhashkitinc.la +libmemcached_libmemcached_la_DEPENDENCIES= +libmemcached_libmemcached_la_LIBADD= $(LIBM) libmemcached_libmemcached_la_LDFLAGS= ${AM_LDFLAGS} -version-info ${MEMCACHED_LIBRARY_VERSION} -if BUILD_LIBMEMCACHEDUTIL -nobase_include_HEADERS+= \ - libmemcached/memcached_util.h \ - libmemcached/util.h \ - libmemcached/util/ping.h \ - libmemcached/util/pool.h \ - libmemcached/util/version.h -lib_LTLIBRARIES+= libmemcached/libmemcachedutil.la -endif - -libmemcached_libmemcachedutil_la_SOURCES= \ - libmemcached/util/ping.cc \ - libmemcached/util/pool.cc \ - libmemcached/util/version.cc -libmemcached_libmemcachedutil_la_CFLAGS= ${AM_CFLAGS} ${NO_CONVERSION} ${PTHREAD_CFLAGS} -libmemcached_libmemcachedutil_la_LIBADD= libmemcached/libmemcached.la -libmemcached_libmemcachedutil_la_LDFLAGS= ${AM_LDFLAGS} ${PTHREAD_LIBS} -version-info ${MEMCACHED_UTIL_LIBRARY_VERSION} -libmemcached_libmemcachedutil_la_DEPENDENCIES= libmemcached/libmemcached.la - -if BUILD_BYTEORDER -noinst_LTLIBRARIES += libmemcached/libbyteorder.la -libmemcached_libbyteorder_la_SOURCES= libmemcached/byteorder.c -libmemcached_libmemcached_la_LIBADD += libmemcached/libbyteorder.la -libmemcached_libmemcached_la_DEPENDENCIES+= libmemcached/libbyteorder.la -libmemcached_libmemcachedprotocol_la_LIBADD=libmemcached/libbyteorder.la -libmemcached_libmemcachedprotocol_la_DEPENDENCIES=libmemcached/libbyteorder.la -endif - if HAVE_SASL libmemcached_libmemcached_la_LDFLAGS+= $(LTLIBSASL) $(LTLIBSASL2) libmemcached_libmemcached_la_SOURCES += libmemcached/sasl.c diff --git a/libmemcached/initialize_query.cc b/libmemcached/initialize_query.cc index abc8dfdc..31a15fbc 100644 --- a/libmemcached/initialize_query.cc +++ b/libmemcached/initialize_query.cc @@ -35,13 +35,14 @@ */ #include -#include memcached_return_t initialize_query(memcached_st *self) { - if (! self) + if (not self) return MEMCACHED_INVALID_ARGUMENTS; + self->query_id++; + if (self->state.is_time_for_rebuild) { memcached_reset(self); @@ -52,15 +53,12 @@ memcached_return_t initialize_query(memcached_st *self) return memcached_set_error(self, MEMCACHED_NO_SERVERS, NULL); } - - self->query_id++; - return MEMCACHED_SUCCESS; } memcached_return_t initialize_const_query(const memcached_st *self) { - if (! self) + if (not self) return MEMCACHED_INVALID_ARGUMENTS; if (memcached_server_count(self) == 0) diff --git a/libmemcached/internal.h b/libmemcached/internal.h index 743bf870..67ae67d5 100644 --- a/libmemcached/internal.h +++ b/libmemcached/internal.h @@ -1,18 +1,41 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2009 Brian Aker All rights reserved. * - * Summary: Internal functions used by the library. Not for public use! + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. * */ -#ifndef __LIBMEMCACHED_INTERNAL_H__ -#define __LIBMEMCACHED_INTERNAL_H__ - -#if defined(BUILDING_LIBMEMCACHED) +#pragma once #ifdef __cplusplus extern "C" { @@ -21,6 +44,3 @@ extern "C" { #ifdef __cplusplus } #endif - -#endif /* BUILDING_LIBMEMCACHED */ -#endif /* __LIBMEMCACHED_INTERNAL_H__ */ diff --git a/libmemcached/io.c b/libmemcached/io.c deleted file mode 100644 index 5acbbd52..00000000 --- a/libmemcached/io.c +++ /dev/null @@ -1,806 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * LibMemcached - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * Copyright (C) 2006-2009 Brian Aker - * 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. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * 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. - * - */ - - -#include "libmemcached/common.h" - -typedef enum { - MEM_READ, - MEM_WRITE -} memc_read_or_write; - -static ssize_t io_flush(memcached_server_write_instance_st ptr, - const bool with_flush, - memcached_return_t *error); -static void increment_udp_message_id(memcached_server_write_instance_st ptr); - -static memcached_return_t io_wait(memcached_server_write_instance_st ptr, - memc_read_or_write read_or_write) -{ - struct pollfd fds= { - .fd= ptr->fd, - .events = POLLIN - }; - int error; - - if (read_or_write == MEM_WRITE) /* write */ - { - fds.events= POLLOUT; - WATCHPOINT_SET(ptr->io_wait_count.write++); - } - else - { - WATCHPOINT_SET(ptr->io_wait_count.read++); - } - - /* - ** We are going to block on write, but at least on Solaris we might block - ** on write if we haven't read anything from our input buffer.. - ** Try to purge the input buffer if we don't do any flow control in the - ** application layer (just sending a lot of data etc) - ** The test is moved down in the purge function to avoid duplication of - ** the test. - */ - if (read_or_write == MEM_WRITE) - { - memcached_return_t rc= memcached_purge(ptr); - if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_STORED) - return MEMCACHED_FAILURE; - } - - size_t loop_max= 5; - while (--loop_max) // While loop is for ERESTART or EINTR - { - error= poll(&fds, 1, ptr->root->poll_timeout); - - switch (error) - { - case 1: // Success! - WATCHPOINT_IF_LABELED_NUMBER(read_or_write && loop_max < 4, "read() times we had to loop, decremented down from 5", loop_max); - WATCHPOINT_IF_LABELED_NUMBER(!read_or_write && loop_max < 4, "write() times we had to loop, decremented down from 5", loop_max); - - return MEMCACHED_SUCCESS; - case 0: // Timeout occured, we let the while() loop do its thing. - return MEMCACHED_TIMEOUT; - default: - WATCHPOINT_ERRNO(get_socket_errno()); - switch (get_socket_errno()) - { -#ifdef TARGET_OS_LINUX - case ERESTART: -#endif - case EINTR: - break; - default: - if (fds.revents & POLLERR) - { - int err; - socklen_t len= sizeof (err); - (void)getsockopt(ptr->fd, SOL_SOCKET, SO_ERROR, &err, &len); - ptr->cached_errno= (err == 0) ? get_socket_errno() : err; - } - else - { - ptr->cached_errno= get_socket_errno(); - } - memcached_quit_server(ptr, true); - - return MEMCACHED_FAILURE; - } - } - } - - /* Imposssible for anything other then -1 */ - WATCHPOINT_ASSERT(error == -1); - ptr->cached_errno= get_socket_errno(); - memcached_quit_server(ptr, true); - - return MEMCACHED_FAILURE; -} - -memcached_return_t memcached_io_wait_for_write(memcached_server_write_instance_st ptr) -{ - return io_wait(ptr, MEM_WRITE); -} - -/** - * Try to fill the input buffer for a server with as much - * data as possible. - * - * @param ptr the server to pack - */ -static bool repack_input_buffer(memcached_server_write_instance_st ptr) -{ - if (ptr->read_ptr != ptr->read_buffer) - { - /* Move all of the data to the beginning of the buffer so - ** that we can fit more data into the buffer... - */ - memmove(ptr->read_buffer, ptr->read_ptr, ptr->read_buffer_length); - ptr->read_ptr= ptr->read_buffer; - ptr->read_data_length= ptr->read_buffer_length; - } - - /* There is room in the buffer, try to fill it! */ - if (ptr->read_buffer_length != MEMCACHED_MAX_BUFFER) - { - /* Just try a single read to grab what's available */ - ssize_t nr= recv(ptr->fd, - ptr->read_ptr + ptr->read_data_length, - MEMCACHED_MAX_BUFFER - ptr->read_data_length, - 0); - - if (nr > 0) - { - ptr->read_data_length+= (size_t)nr; - ptr->read_buffer_length+= (size_t)nr; - return true; - } - } - return false; -} - -/** - * If the we have callbacks connected to this server structure - * we may start process the input queue and fire the callbacks - * for the incomming messages. This function is _only_ called - * when the input buffer is full, so that we _know_ that we have - * at least _one_ message to process. - * - * @param ptr the server to star processing iput messages for - * @return true if we processed anything, false otherwise - */ -static bool process_input_buffer(memcached_server_write_instance_st ptr) -{ - /* - ** We might be able to process some of the response messages if we - ** have a callback set up - */ - if (ptr->root->callbacks != NULL && ptr->root->flags.use_udp == false) - { - /* - * We might have responses... try to read them out and fire - * callbacks - */ - memcached_callback_st cb= *ptr->root->callbacks; - - memcached_set_processing_input((memcached_st *)ptr->root, true); - - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - memcached_return_t error; - memcached_st *root= (memcached_st *)ptr->root; - error= memcached_response(ptr, buffer, sizeof(buffer), - &root->result); - - memcached_set_processing_input(root, false); - - if (error == MEMCACHED_SUCCESS) - { - for (unsigned int x= 0; x < cb.number_of_callback; x++) - { - error= (*cb.callback[x])(ptr->root, &root->result, cb.context); - if (error != MEMCACHED_SUCCESS) - break; - } - - /* @todo what should I do with the error message??? */ - } - /* @todo what should I do with other error messages?? */ - return true; - } - - return false; -} - -#if 0 // Dead code, this should be removed. -void memcached_io_preread(memcached_st *ptr) -{ - unsigned int x; - - return; - - for (x= 0; x < memcached_server_count(ptr); x++) - { - if (memcached_server_response_count(ptr, x) && - ptr->hosts[x].read_data_length < MEMCACHED_MAX_BUFFER ) - { - size_t data_read; - - data_read= recv(ptr->hosts[x].fd, - ptr->hosts[x].read_ptr + ptr->hosts[x].read_data_length, - MEMCACHED_MAX_BUFFER - ptr->hosts[x].read_data_length, 0); - if (data_read == SOCKET_ERROR) - continue; - - ptr->hosts[x].read_buffer_length+= data_read; - ptr->hosts[x].read_data_length+= data_read; - } - } -} -#endif - -memcached_return_t memcached_io_read(memcached_server_write_instance_st ptr, - void *buffer, size_t length, ssize_t *nread) -{ - char *buffer_ptr; - - buffer_ptr= buffer; - - while (length) - { - if (!ptr->read_buffer_length) - { - ssize_t data_read; - - while (1) - { - data_read= recv(ptr->fd, ptr->read_buffer, MEMCACHED_MAX_BUFFER, 0); - if (data_read > 0) - { - break; - } - else if (data_read == SOCKET_ERROR) - { - ptr->cached_errno= get_socket_errno(); - memcached_return_t rc= MEMCACHED_ERRNO; - switch (get_socket_errno()) - { - case EWOULDBLOCK: -#ifdef USE_EAGAIN - case EAGAIN: -#endif - case EINTR: -#ifdef TARGET_OS_LINUX - case ERESTART: -#endif - if ((rc= io_wait(ptr, MEM_READ)) == MEMCACHED_SUCCESS) - continue; - /* fall through */ - - default: - { - memcached_quit_server(ptr, true); - *nread= -1; - return rc; - } - } - } - else - { - /* - EOF. Any data received so far is incomplete - so discard it. This always reads by byte in case of TCP - and protocol enforcement happens at memcached_response() - looking for '\n'. We do not care for UDB which requests 8 bytes - at once. Generally, this means that connection went away. Since - for blocking I/O we do not return 0 and for non-blocking case - it will return EGAIN if data is not immediatly available. - */ - WATCHPOINT_STRING("We had a zero length recv()"); - memcached_quit_server(ptr, true); - *nread= -1; - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - } - - ptr->io_bytes_sent = 0; - ptr->read_data_length= (size_t) data_read; - ptr->read_buffer_length= (size_t) data_read; - ptr->read_ptr= ptr->read_buffer; - } - - if (length > 1) - { - size_t difference; - - difference= (length > ptr->read_buffer_length) ? ptr->read_buffer_length : length; - - memcpy(buffer_ptr, ptr->read_ptr, difference); - length -= difference; - ptr->read_ptr+= difference; - ptr->read_buffer_length-= difference; - buffer_ptr+= difference; - } - else - { - *buffer_ptr= *ptr->read_ptr; - ptr->read_ptr++; - ptr->read_buffer_length--; - buffer_ptr++; - break; - } - } - - ptr->server_failure_counter= 0; - *nread = (ssize_t)(buffer_ptr - (char*)buffer); - return MEMCACHED_SUCCESS; -} - -static ssize_t _io_write(memcached_server_write_instance_st ptr, - const void *buffer, size_t length, bool with_flush) -{ - size_t original_length; - const char* buffer_ptr; - - WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); - - original_length= length; - buffer_ptr= buffer; - - while (length) - { - char *write_ptr; - size_t should_write; - size_t buffer_end; - - if (ptr->type == MEMCACHED_CONNECTION_UDP) - { - //UDP does not support partial writes - buffer_end= MAX_UDP_DATAGRAM_LENGTH; - should_write= length; - if (ptr->write_buffer_offset + should_write > buffer_end) - { - return -1; - } - } - else - { - buffer_end= MEMCACHED_MAX_BUFFER; - should_write= buffer_end - ptr->write_buffer_offset; - should_write= (should_write < length) ? should_write : length; - } - - write_ptr= ptr->write_buffer + ptr->write_buffer_offset; - memcpy(write_ptr, buffer_ptr, should_write); - ptr->write_buffer_offset+= should_write; - buffer_ptr+= should_write; - length-= should_write; - - if (ptr->write_buffer_offset == buffer_end && ptr->type != MEMCACHED_CONNECTION_UDP) - { - memcached_return_t rc; - ssize_t sent_length; - - WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); - sent_length= io_flush(ptr, with_flush, &rc); - if (sent_length == -1) - { - return -1; - } - - /* If io_flush calls memcached_purge, sent_length may be 0 */ - unlikely (sent_length != 0) - { - WATCHPOINT_ASSERT(sent_length == (ssize_t)buffer_end); - } - } - } - - if (with_flush) - { - memcached_return_t rc; - WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); - if (io_flush(ptr, with_flush, &rc) == -1) - { - return -1; - } - } - - return (ssize_t) original_length; -} - -ssize_t memcached_io_write(memcached_server_write_instance_st ptr, - const void *buffer, size_t length, bool with_flush) -{ - return _io_write(ptr, buffer, length, with_flush); -} - -ssize_t memcached_io_writev(memcached_server_write_instance_st ptr, - const struct libmemcached_io_vector_st *vector, - size_t number_of, bool with_flush) -{ - ssize_t total= 0; - - for (size_t x= 0; x < number_of; x++, vector++) - { - ssize_t returnable; - - if ((returnable= _io_write(ptr, vector->buffer, vector->length, false)) == -1) - { - return -1; - } - total+= returnable; - } - - if (with_flush) - { - if (memcached_io_write(ptr, NULL, 0, true) == -1) - { - return -1; - } - } - - return total; -} - - -memcached_return_t memcached_io_close(memcached_server_write_instance_st ptr) -{ - if (ptr->fd == INVALID_SOCKET) - { - return MEMCACHED_SUCCESS; - } - - /* in case of death shutdown to avoid blocking at close() */ - if (shutdown(ptr->fd, SHUT_RDWR) == SOCKET_ERROR && get_socket_errno() != ENOTCONN) - { - WATCHPOINT_NUMBER(ptr->fd); - WATCHPOINT_ERRNO(get_socket_errno()); - WATCHPOINT_ASSERT(get_socket_errno()); - } - - if (closesocket(ptr->fd) == SOCKET_ERROR) - { - WATCHPOINT_ERRNO(get_socket_errno()); - } - - return MEMCACHED_SUCCESS; -} - -memcached_server_write_instance_st memcached_io_get_readable_server(memcached_st *memc) -{ -#define MAX_SERVERS_TO_POLL 100 - struct pollfd fds[MAX_SERVERS_TO_POLL]; - unsigned int host_index= 0; - - for (uint32_t x= 0; - x< memcached_server_count(memc) && host_index < MAX_SERVERS_TO_POLL; - ++x) - { - memcached_server_write_instance_st instance= - memcached_server_instance_fetch(memc, x); - - if (instance->read_buffer_length > 0) /* I have data in the buffer */ - return instance; - - if (memcached_server_response_count(instance) > 0) - { - fds[host_index].events = POLLIN; - fds[host_index].revents = 0; - fds[host_index].fd = instance->fd; - ++host_index; - } - } - - if (host_index < 2) - { - /* We have 0 or 1 server with pending events.. */ - for (uint32_t x= 0; x< memcached_server_count(memc); ++x) - { - memcached_server_write_instance_st instance= - memcached_server_instance_fetch(memc, x); - - if (memcached_server_response_count(instance) > 0) - { - return instance; - } - } - - return NULL; - } - - int err= poll(fds, host_index, memc->poll_timeout); - switch (err) { - case -1: - memcached_set_errno(memc, get_socket_errno(), NULL); - /* FALLTHROUGH */ - case 0: - break; - default: - for (size_t x= 0; x < host_index; ++x) - { - if (fds[x].revents & POLLIN) - { - for (uint32_t y= 0; y < memcached_server_count(memc); ++y) - { - memcached_server_write_instance_st instance= - memcached_server_instance_fetch(memc, y); - - if (instance->fd == fds[x].fd) - return instance; - } - } - } - } - - return NULL; -} - -static ssize_t io_flush(memcached_server_write_instance_st ptr, - const bool with_flush, - memcached_return_t *error) -{ - /* - ** We might want to purge the input buffer if we haven't consumed - ** any output yet... The test for the limits is the purge is inline - ** in the purge function to avoid duplicating the logic.. - */ - { - memcached_return_t rc; - WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); - rc= memcached_purge(ptr); - - if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_STORED) - { - return -1; - } - } - ssize_t sent_length; - size_t return_length; - char *local_write_ptr= ptr->write_buffer; - size_t write_length= ptr->write_buffer_offset; - - *error= MEMCACHED_SUCCESS; - - WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); - - // UDP Sanity check, make sure that we are not sending somthing too big - if (ptr->type == MEMCACHED_CONNECTION_UDP && write_length > MAX_UDP_DATAGRAM_LENGTH) - { - return -1; - } - - if (ptr->write_buffer_offset == 0 || (ptr->type == MEMCACHED_CONNECTION_UDP - && ptr->write_buffer_offset == UDP_DATAGRAM_HEADER_LENGTH)) - return 0; - - /* Looking for memory overflows */ -#if defined(DEBUG) - if (write_length == MEMCACHED_MAX_BUFFER) - WATCHPOINT_ASSERT(ptr->write_buffer == local_write_ptr); - WATCHPOINT_ASSERT((ptr->write_buffer + MEMCACHED_MAX_BUFFER) >= (local_write_ptr + write_length)); -#endif - - return_length= 0; - while (write_length) - { - WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); - WATCHPOINT_ASSERT(write_length > 0); - sent_length= 0; - if (ptr->type == MEMCACHED_CONNECTION_UDP) - increment_udp_message_id(ptr); - - WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); - if (with_flush) - { - sent_length= send(ptr->fd, local_write_ptr, write_length, MSG_NOSIGNAL|MSG_DONTWAIT); - } - else - { - sent_length= send(ptr->fd, local_write_ptr, write_length, MSG_NOSIGNAL|MSG_DONTWAIT|MSG_MORE); - } - - if (sent_length == SOCKET_ERROR) - { - ptr->cached_errno= get_socket_errno(); -#if 0 // @todo I should look at why we hit this bit of code hard frequently - WATCHPOINT_ERRNO(get_socket_errno()); - WATCHPOINT_NUMBER(get_socket_errno()); -#endif - switch (get_socket_errno()) - { - case ENOBUFS: - continue; - case EWOULDBLOCK: -#ifdef USE_EAGAIN - case EAGAIN: -#endif - { - /* - * We may be blocked on write because the input buffer - * is full. Let's check if we have room in our input - * buffer for more data and retry the write before - * waiting.. - */ - if (repack_input_buffer(ptr) || - process_input_buffer(ptr)) - continue; - - memcached_return_t rc; - rc= io_wait(ptr, MEM_WRITE); - - if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_TIMEOUT) - continue; - - memcached_quit_server(ptr, true); - return -1; - } - case ENOTCONN: - case EPIPE: - default: - memcached_quit_server(ptr, true); - *error= MEMCACHED_ERRNO; - WATCHPOINT_ASSERT(ptr->fd == -1); - return -1; - } - } - - if (ptr->type == MEMCACHED_CONNECTION_UDP && - (size_t)sent_length != write_length) - { - memcached_quit_server(ptr, true); - return -1; - } - - ptr->io_bytes_sent += (uint32_t) sent_length; - - local_write_ptr+= sent_length; - write_length-= (uint32_t) sent_length; - return_length+= (uint32_t) sent_length; - } - - WATCHPOINT_ASSERT(write_length == 0); - // Need to study this assert() WATCHPOINT_ASSERT(return_length == - // ptr->write_buffer_offset); - - // if we are a udp server, the begining of the buffer is reserverd for - // the upd frame header - if (ptr->type == MEMCACHED_CONNECTION_UDP) - ptr->write_buffer_offset= UDP_DATAGRAM_HEADER_LENGTH; - else - ptr->write_buffer_offset= 0; - - return (ssize_t) return_length; -} - -/* - Eventually we will just kill off the server with the problem. -*/ -void memcached_io_reset(memcached_server_write_instance_st ptr) -{ - memcached_quit_server(ptr, true); -} - -/** - * Read a given number of bytes from the server and place it into a specific - * buffer. Reset the IO channel on this server if an error occurs. - */ -memcached_return_t memcached_safe_read(memcached_server_write_instance_st ptr, - void *dta, - size_t size) -{ - size_t offset= 0; - char *data= dta; - - while (offset < size) - { - ssize_t nread; - memcached_return_t rc= memcached_io_read(ptr, data + offset, size - offset, - &nread); - if (rc != MEMCACHED_SUCCESS) - return rc; - - offset+= (size_t) nread; - } - - return MEMCACHED_SUCCESS; -} - -memcached_return_t memcached_io_readline(memcached_server_write_instance_st ptr, - char *buffer_ptr, - size_t size) -{ - bool line_complete= false; - size_t total_nr= 0; - - while (!line_complete) - { - if (ptr->read_buffer_length == 0) - { - /* - * We don't have any data in the buffer, so let's fill the read - * buffer. Call the standard read function to avoid duplicating - * the logic. - */ - ssize_t nread; - memcached_return_t rc= memcached_io_read(ptr, buffer_ptr, 1, &nread); - if (rc != MEMCACHED_SUCCESS) - return rc; - - if (*buffer_ptr == '\n') - line_complete= true; - - ++buffer_ptr; - ++total_nr; - } - - /* Now let's look in the buffer and copy as we go! */ - while (ptr->read_buffer_length && total_nr < size && !line_complete) - { - *buffer_ptr = *ptr->read_ptr; - if (*buffer_ptr == '\n') - line_complete = true; - --ptr->read_buffer_length; - ++ptr->read_ptr; - ++total_nr; - ++buffer_ptr; - } - - if (total_nr == size) - return MEMCACHED_PROTOCOL_ERROR; - } - - return MEMCACHED_SUCCESS; -} - -/* - * The udp request id consists of two seperate sections - * 1) The thread id - * 2) The message number - * The thread id should only be set when the memcached_st struct is created - * and should not be changed. - * - * The message num is incremented for each new message we send, this function - * extracts the message number from message_id, increments it and then - * writes the new value back into the header - */ -static void increment_udp_message_id(memcached_server_write_instance_st ptr) -{ - struct udp_datagram_header_st *header= (struct udp_datagram_header_st *)ptr->write_buffer; - uint16_t cur_req= get_udp_datagram_request_id(header); - int msg_num= get_msg_num_from_request_id(cur_req); - int thread_id= get_thread_id_from_request_id(cur_req); - - if (((++msg_num) & UDP_REQUEST_ID_THREAD_MASK) != 0) - msg_num= 0; - - header->request_id= htons((uint16_t) (thread_id | msg_num)); -} - -memcached_return_t memcached_io_init_udp_header(memcached_server_write_instance_st ptr, uint16_t thread_id) -{ - if (thread_id > UDP_REQUEST_ID_MAX_THREAD_ID) - return MEMCACHED_FAILURE; - - struct udp_datagram_header_st *header= (struct udp_datagram_header_st *)ptr->write_buffer; - header->request_id= htons((uint16_t) (generate_udp_request_thread_id(thread_id))); - header->num_datagrams= htons(1); - header->sequence_number= htons(0); - - return MEMCACHED_SUCCESS; -} diff --git a/libmemcached/io.cc b/libmemcached/io.cc new file mode 100644 index 00000000..74aa4a23 --- /dev/null +++ b/libmemcached/io.cc @@ -0,0 +1,806 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * LibMemcached + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2009 Brian Aker + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + + +#include "libmemcached/common.h" + +typedef enum { + MEM_READ, + MEM_WRITE +} memc_read_or_write; + +static ssize_t io_flush(memcached_server_write_instance_st ptr, + const bool with_flush, + memcached_return_t *error); +static void increment_udp_message_id(memcached_server_write_instance_st ptr); + +static memcached_return_t io_wait(memcached_server_write_instance_st ptr, + memc_read_or_write read_or_write) +{ + struct pollfd fds; + fds.fd= ptr->fd; + fds.events= POLLIN; + + int error; + + if (read_or_write == MEM_WRITE) /* write */ + { + fds.events= POLLOUT; + WATCHPOINT_SET(ptr->io_wait_count.write++); + } + else + { + WATCHPOINT_SET(ptr->io_wait_count.read++); + } + + /* + ** We are going to block on write, but at least on Solaris we might block + ** on write if we haven't read anything from our input buffer.. + ** Try to purge the input buffer if we don't do any flow control in the + ** application layer (just sending a lot of data etc) + ** The test is moved down in the purge function to avoid duplication of + ** the test. + */ + if (read_or_write == MEM_WRITE) + { + memcached_return_t rc= memcached_purge(ptr); + if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_STORED) + return MEMCACHED_FAILURE; + } + + size_t loop_max= 5; + while (--loop_max) // While loop is for ERESTART or EINTR + { + error= poll(&fds, 1, ptr->root->poll_timeout); + + switch (error) + { + case 1: // Success! + WATCHPOINT_IF_LABELED_NUMBER(read_or_write && loop_max < 4, "read() times we had to loop, decremented down from 5", loop_max); + WATCHPOINT_IF_LABELED_NUMBER(!read_or_write && loop_max < 4, "write() times we had to loop, decremented down from 5", loop_max); + + return MEMCACHED_SUCCESS; + case 0: // Timeout occured, we let the while() loop do its thing. + return MEMCACHED_TIMEOUT; + default: + WATCHPOINT_ERRNO(get_socket_errno()); + switch (get_socket_errno()) + { +#ifdef TARGET_OS_LINUX + case ERESTART: +#endif + case EINTR: + break; + default: + if (fds.revents & POLLERR) + { + int err; + socklen_t len= sizeof (err); + (void)getsockopt(ptr->fd, SOL_SOCKET, SO_ERROR, &err, &len); + ptr->cached_errno= (err == 0) ? get_socket_errno() : err; + } + else + { + ptr->cached_errno= get_socket_errno(); + } + memcached_quit_server(ptr, true); + + return MEMCACHED_FAILURE; + } + } + } + + /* Imposssible for anything other then -1 */ + WATCHPOINT_ASSERT(error == -1); + ptr->cached_errno= get_socket_errno(); + memcached_quit_server(ptr, true); + + return MEMCACHED_FAILURE; +} + +memcached_return_t memcached_io_wait_for_write(memcached_server_write_instance_st ptr) +{ + return io_wait(ptr, MEM_WRITE); +} + +/** + * Try to fill the input buffer for a server with as much + * data as possible. + * + * @param ptr the server to pack + */ +static bool repack_input_buffer(memcached_server_write_instance_st ptr) +{ + if (ptr->read_ptr != ptr->read_buffer) + { + /* Move all of the data to the beginning of the buffer so + ** that we can fit more data into the buffer... + */ + memmove(ptr->read_buffer, ptr->read_ptr, ptr->read_buffer_length); + ptr->read_ptr= ptr->read_buffer; + ptr->read_data_length= ptr->read_buffer_length; + } + + /* There is room in the buffer, try to fill it! */ + if (ptr->read_buffer_length != MEMCACHED_MAX_BUFFER) + { + /* Just try a single read to grab what's available */ + ssize_t nr= recv(ptr->fd, + ptr->read_ptr + ptr->read_data_length, + MEMCACHED_MAX_BUFFER - ptr->read_data_length, + 0); + + if (nr > 0) + { + ptr->read_data_length+= (size_t)nr; + ptr->read_buffer_length+= (size_t)nr; + return true; + } + } + return false; +} + +/** + * If the we have callbacks connected to this server structure + * we may start process the input queue and fire the callbacks + * for the incomming messages. This function is _only_ called + * when the input buffer is full, so that we _know_ that we have + * at least _one_ message to process. + * + * @param ptr the server to star processing iput messages for + * @return true if we processed anything, false otherwise + */ +static bool process_input_buffer(memcached_server_write_instance_st ptr) +{ + /* + ** We might be able to process some of the response messages if we + ** have a callback set up + */ + if (ptr->root->callbacks != NULL && ptr->root->flags.use_udp == false) + { + /* + * We might have responses... try to read them out and fire + * callbacks + */ + memcached_callback_st cb= *ptr->root->callbacks; + + memcached_set_processing_input((memcached_st *)ptr->root, true); + + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + memcached_return_t error; + memcached_st *root= (memcached_st *)ptr->root; + error= memcached_response(ptr, buffer, sizeof(buffer), + &root->result); + + memcached_set_processing_input(root, false); + + if (error == MEMCACHED_SUCCESS) + { + for (unsigned int x= 0; x < cb.number_of_callback; x++) + { + error= (*cb.callback[x])(ptr->root, &root->result, cb.context); + if (error != MEMCACHED_SUCCESS) + break; + } + + /* @todo what should I do with the error message??? */ + } + /* @todo what should I do with other error messages?? */ + return true; + } + + return false; +} + +#if 0 // Dead code, this should be removed. +void memcached_io_preread(memcached_st *ptr) +{ + unsigned int x; + + return; + + for (x= 0; x < memcached_server_count(ptr); x++) + { + if (memcached_server_response_count(ptr, x) && + ptr->hosts[x].read_data_length < MEMCACHED_MAX_BUFFER ) + { + size_t data_read; + + data_read= recv(ptr->hosts[x].fd, + ptr->hosts[x].read_ptr + ptr->hosts[x].read_data_length, + MEMCACHED_MAX_BUFFER - ptr->hosts[x].read_data_length, 0); + if (data_read == SOCKET_ERROR) + continue; + + ptr->hosts[x].read_buffer_length+= data_read; + ptr->hosts[x].read_data_length+= data_read; + } + } +} +#endif + +memcached_return_t memcached_io_read(memcached_server_write_instance_st ptr, + void *buffer, size_t length, ssize_t *nread) +{ + char *buffer_ptr; + + buffer_ptr= static_cast(buffer); + + while (length) + { + if (not ptr->read_buffer_length) + { + ssize_t data_read; + + while (1) + { + data_read= recv(ptr->fd, ptr->read_buffer, MEMCACHED_MAX_BUFFER, 0); + if (data_read > 0) + { + break; + } + else if (data_read == SOCKET_ERROR) + { + ptr->cached_errno= get_socket_errno(); + memcached_return_t rc= MEMCACHED_ERRNO; + switch (get_socket_errno()) + { + case EWOULDBLOCK: +#ifdef USE_EAGAIN + case EAGAIN: +#endif + case EINTR: +#ifdef TARGET_OS_LINUX + case ERESTART: +#endif + if ((rc= io_wait(ptr, MEM_READ)) == MEMCACHED_SUCCESS) + continue; + /* fall through */ + + default: + { + memcached_quit_server(ptr, true); + *nread= -1; + return rc; + } + } + } + else + { + /* + EOF. Any data received so far is incomplete + so discard it. This always reads by byte in case of TCP + and protocol enforcement happens at memcached_response() + looking for '\n'. We do not care for UDB which requests 8 bytes + at once. Generally, this means that connection went away. Since + for blocking I/O we do not return 0 and for non-blocking case + it will return EGAIN if data is not immediatly available. + */ + WATCHPOINT_STRING("We had a zero length recv()"); + memcached_quit_server(ptr, true); + *nread= -1; + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + } + + ptr->io_bytes_sent = 0; + ptr->read_data_length= (size_t) data_read; + ptr->read_buffer_length= (size_t) data_read; + ptr->read_ptr= ptr->read_buffer; + } + + if (length > 1) + { + size_t difference; + + difference= (length > ptr->read_buffer_length) ? ptr->read_buffer_length : length; + + memcpy(buffer_ptr, ptr->read_ptr, difference); + length -= difference; + ptr->read_ptr+= difference; + ptr->read_buffer_length-= difference; + buffer_ptr+= difference; + } + else + { + *buffer_ptr= *ptr->read_ptr; + ptr->read_ptr++; + ptr->read_buffer_length--; + buffer_ptr++; + break; + } + } + + ptr->server_failure_counter= 0; + *nread = (ssize_t)(buffer_ptr - (char*)buffer); + return MEMCACHED_SUCCESS; +} + +static ssize_t _io_write(memcached_server_write_instance_st ptr, + const void *buffer, size_t length, bool with_flush) +{ + size_t original_length; + const char* buffer_ptr; + + WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); + + original_length= length; + buffer_ptr= static_cast(buffer); + + while (length) + { + char *write_ptr; + size_t should_write; + size_t buffer_end; + + if (ptr->type == MEMCACHED_CONNECTION_UDP) + { + //UDP does not support partial writes + buffer_end= MAX_UDP_DATAGRAM_LENGTH; + should_write= length; + if (ptr->write_buffer_offset + should_write > buffer_end) + { + return -1; + } + } + else + { + buffer_end= MEMCACHED_MAX_BUFFER; + should_write= buffer_end - ptr->write_buffer_offset; + should_write= (should_write < length) ? should_write : length; + } + + write_ptr= ptr->write_buffer + ptr->write_buffer_offset; + memcpy(write_ptr, buffer_ptr, should_write); + ptr->write_buffer_offset+= should_write; + buffer_ptr+= should_write; + length-= should_write; + + if (ptr->write_buffer_offset == buffer_end && ptr->type != MEMCACHED_CONNECTION_UDP) + { + memcached_return_t rc; + ssize_t sent_length; + + WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); + sent_length= io_flush(ptr, with_flush, &rc); + if (sent_length == -1) + { + return -1; + } + + /* If io_flush calls memcached_purge, sent_length may be 0 */ + unlikely (sent_length != 0) + { + WATCHPOINT_ASSERT(sent_length == (ssize_t)buffer_end); + } + } + } + + if (with_flush) + { + memcached_return_t rc; + WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); + if (io_flush(ptr, with_flush, &rc) == -1) + { + return -1; + } + } + + return (ssize_t) original_length; +} + +ssize_t memcached_io_write(memcached_server_write_instance_st ptr, + const void *buffer, size_t length, bool with_flush) +{ + return _io_write(ptr, buffer, length, with_flush); +} + +ssize_t memcached_io_writev(memcached_server_write_instance_st ptr, + const struct libmemcached_io_vector_st *vector, + size_t number_of, bool with_flush) +{ + ssize_t total= 0; + + for (size_t x= 0; x < number_of; x++, vector++) + { + ssize_t returnable; + + if ((returnable= _io_write(ptr, vector->buffer, vector->length, false)) == -1) + { + return -1; + } + total+= returnable; + } + + if (with_flush) + { + if (memcached_io_write(ptr, NULL, 0, true) == -1) + { + return -1; + } + } + + return total; +} + + +memcached_return_t memcached_io_close(memcached_server_write_instance_st ptr) +{ + if (ptr->fd == INVALID_SOCKET) + { + return MEMCACHED_SUCCESS; + } + + /* in case of death shutdown to avoid blocking at close() */ + if (shutdown(ptr->fd, SHUT_RDWR) == SOCKET_ERROR && get_socket_errno() != ENOTCONN) + { + WATCHPOINT_NUMBER(ptr->fd); + WATCHPOINT_ERRNO(get_socket_errno()); + WATCHPOINT_ASSERT(get_socket_errno()); + } + + if (closesocket(ptr->fd) == SOCKET_ERROR) + { + WATCHPOINT_ERRNO(get_socket_errno()); + } + + return MEMCACHED_SUCCESS; +} + +memcached_server_write_instance_st memcached_io_get_readable_server(memcached_st *memc) +{ +#define MAX_SERVERS_TO_POLL 100 + struct pollfd fds[MAX_SERVERS_TO_POLL]; + unsigned int host_index= 0; + + for (uint32_t x= 0; + x< memcached_server_count(memc) && host_index < MAX_SERVERS_TO_POLL; + ++x) + { + memcached_server_write_instance_st instance= + memcached_server_instance_fetch(memc, x); + + if (instance->read_buffer_length > 0) /* I have data in the buffer */ + return instance; + + if (memcached_server_response_count(instance) > 0) + { + fds[host_index].events = POLLIN; + fds[host_index].revents = 0; + fds[host_index].fd = instance->fd; + ++host_index; + } + } + + if (host_index < 2) + { + /* We have 0 or 1 server with pending events.. */ + for (uint32_t x= 0; x< memcached_server_count(memc); ++x) + { + memcached_server_write_instance_st instance= + memcached_server_instance_fetch(memc, x); + + if (memcached_server_response_count(instance) > 0) + { + return instance; + } + } + + return NULL; + } + + int err= poll(fds, host_index, memc->poll_timeout); + switch (err) { + case -1: + memcached_set_errno(memc, get_socket_errno(), NULL); + /* FALLTHROUGH */ + case 0: + break; + default: + for (size_t x= 0; x < host_index; ++x) + { + if (fds[x].revents & POLLIN) + { + for (uint32_t y= 0; y < memcached_server_count(memc); ++y) + { + memcached_server_write_instance_st instance= + memcached_server_instance_fetch(memc, y); + + if (instance->fd == fds[x].fd) + return instance; + } + } + } + } + + return NULL; +} + +static ssize_t io_flush(memcached_server_write_instance_st ptr, + const bool with_flush, + memcached_return_t *error) +{ + /* + ** We might want to purge the input buffer if we haven't consumed + ** any output yet... The test for the limits is the purge is inline + ** in the purge function to avoid duplicating the logic.. + */ + { + memcached_return_t rc; + WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); + rc= memcached_purge(ptr); + + if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_STORED) + { + return -1; + } + } + ssize_t sent_length; + size_t return_length; + char *local_write_ptr= ptr->write_buffer; + size_t write_length= ptr->write_buffer_offset; + + *error= MEMCACHED_SUCCESS; + + WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); + + // UDP Sanity check, make sure that we are not sending somthing too big + if (ptr->type == MEMCACHED_CONNECTION_UDP && write_length > MAX_UDP_DATAGRAM_LENGTH) + { + return -1; + } + + if (ptr->write_buffer_offset == 0 || (ptr->type == MEMCACHED_CONNECTION_UDP + && ptr->write_buffer_offset == UDP_DATAGRAM_HEADER_LENGTH)) + return 0; + + /* Looking for memory overflows */ +#if defined(DEBUG) + if (write_length == MEMCACHED_MAX_BUFFER) + WATCHPOINT_ASSERT(ptr->write_buffer == local_write_ptr); + WATCHPOINT_ASSERT((ptr->write_buffer + MEMCACHED_MAX_BUFFER) >= (local_write_ptr + write_length)); +#endif + + return_length= 0; + while (write_length) + { + WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); + WATCHPOINT_ASSERT(write_length > 0); + sent_length= 0; + if (ptr->type == MEMCACHED_CONNECTION_UDP) + increment_udp_message_id(ptr); + + WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET); + if (with_flush) + { + sent_length= send(ptr->fd, local_write_ptr, write_length, MSG_NOSIGNAL|MSG_DONTWAIT); + } + else + { + sent_length= send(ptr->fd, local_write_ptr, write_length, MSG_NOSIGNAL|MSG_DONTWAIT|MSG_MORE); + } + + if (sent_length == SOCKET_ERROR) + { + ptr->cached_errno= get_socket_errno(); +#if 0 // @todo I should look at why we hit this bit of code hard frequently + WATCHPOINT_ERRNO(get_socket_errno()); + WATCHPOINT_NUMBER(get_socket_errno()); +#endif + switch (get_socket_errno()) + { + case ENOBUFS: + continue; + case EWOULDBLOCK: +#ifdef USE_EAGAIN + case EAGAIN: +#endif + { + /* + * We may be blocked on write because the input buffer + * is full. Let's check if we have room in our input + * buffer for more data and retry the write before + * waiting.. + */ + if (repack_input_buffer(ptr) || + process_input_buffer(ptr)) + continue; + + memcached_return_t rc; + rc= io_wait(ptr, MEM_WRITE); + + if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_TIMEOUT) + continue; + + memcached_quit_server(ptr, true); + return -1; + } + case ENOTCONN: + case EPIPE: + default: + memcached_quit_server(ptr, true); + *error= MEMCACHED_ERRNO; + WATCHPOINT_ASSERT(ptr->fd == -1); + return -1; + } + } + + if (ptr->type == MEMCACHED_CONNECTION_UDP && + (size_t)sent_length != write_length) + { + memcached_quit_server(ptr, true); + return -1; + } + + ptr->io_bytes_sent += (uint32_t) sent_length; + + local_write_ptr+= sent_length; + write_length-= (uint32_t) sent_length; + return_length+= (uint32_t) sent_length; + } + + WATCHPOINT_ASSERT(write_length == 0); + // Need to study this assert() WATCHPOINT_ASSERT(return_length == + // ptr->write_buffer_offset); + + // if we are a udp server, the begining of the buffer is reserverd for + // the upd frame header + if (ptr->type == MEMCACHED_CONNECTION_UDP) + ptr->write_buffer_offset= UDP_DATAGRAM_HEADER_LENGTH; + else + ptr->write_buffer_offset= 0; + + return (ssize_t) return_length; +} + +/* + Eventually we will just kill off the server with the problem. +*/ +void memcached_io_reset(memcached_server_write_instance_st ptr) +{ + memcached_quit_server(ptr, true); +} + +/** + * Read a given number of bytes from the server and place it into a specific + * buffer. Reset the IO channel on this server if an error occurs. + */ +memcached_return_t memcached_safe_read(memcached_server_write_instance_st ptr, + void *dta, + size_t size) +{ + size_t offset= 0; + char *data= static_cast(dta); + + while (offset < size) + { + ssize_t nread; + memcached_return_t rc= memcached_io_read(ptr, data + offset, size - offset, + &nread); + if (rc != MEMCACHED_SUCCESS) + return rc; + + offset+= (size_t) nread; + } + + return MEMCACHED_SUCCESS; +} + +memcached_return_t memcached_io_readline(memcached_server_write_instance_st ptr, + char *buffer_ptr, + size_t size) +{ + bool line_complete= false; + size_t total_nr= 0; + + while (!line_complete) + { + if (ptr->read_buffer_length == 0) + { + /* + * We don't have any data in the buffer, so let's fill the read + * buffer. Call the standard read function to avoid duplicating + * the logic. + */ + ssize_t nread; + memcached_return_t rc= memcached_io_read(ptr, buffer_ptr, 1, &nread); + if (rc != MEMCACHED_SUCCESS) + return rc; + + if (*buffer_ptr == '\n') + line_complete= true; + + ++buffer_ptr; + ++total_nr; + } + + /* Now let's look in the buffer and copy as we go! */ + while (ptr->read_buffer_length && total_nr < size && !line_complete) + { + *buffer_ptr = *ptr->read_ptr; + if (*buffer_ptr == '\n') + line_complete = true; + --ptr->read_buffer_length; + ++ptr->read_ptr; + ++total_nr; + ++buffer_ptr; + } + + if (total_nr == size) + return MEMCACHED_PROTOCOL_ERROR; + } + + return MEMCACHED_SUCCESS; +} + +/* + * The udp request id consists of two seperate sections + * 1) The thread id + * 2) The message number + * The thread id should only be set when the memcached_st struct is created + * and should not be changed. + * + * The message num is incremented for each new message we send, this function + * extracts the message number from message_id, increments it and then + * writes the new value back into the header + */ +static void increment_udp_message_id(memcached_server_write_instance_st ptr) +{ + struct udp_datagram_header_st *header= (struct udp_datagram_header_st *)ptr->write_buffer; + uint16_t cur_req= get_udp_datagram_request_id(header); + int msg_num= get_msg_num_from_request_id(cur_req); + int thread_id= get_thread_id_from_request_id(cur_req); + + if (((++msg_num) & UDP_REQUEST_ID_THREAD_MASK) != 0) + msg_num= 0; + + header->request_id= htons((uint16_t) (thread_id | msg_num)); +} + +memcached_return_t memcached_io_init_udp_header(memcached_server_write_instance_st ptr, uint16_t thread_id) +{ + if (thread_id > UDP_REQUEST_ID_MAX_THREAD_ID) + return MEMCACHED_FAILURE; + + struct udp_datagram_header_st *header= (struct udp_datagram_header_st *)ptr->write_buffer; + header->request_id= htons((uint16_t) (generate_udp_request_thread_id(thread_id))); + header->num_datagrams= htons(1); + header->sequence_number= htons(0); + + return MEMCACHED_SUCCESS; +} diff --git a/libmemcached/io.h b/libmemcached/io.h index 85d56b16..a5d34474 100644 --- a/libmemcached/io.h +++ b/libmemcached/io.h @@ -36,12 +36,9 @@ * */ -#ifndef __LIBMEMCACHED_IO_H__ -#define __LIBMEMCACHED_IO_H__ +#pragma once -#if defined(BUILDING_LIBMEMCACHED) - -#include "libmemcached/memcached.h" +#include #define MAX_UDP_DATAGRAM_LENGTH 1400 #define UDP_DATAGRAM_HEADER_LENGTH 8 @@ -117,7 +114,3 @@ memcached_server_write_instance_st memcached_io_get_readable_server(memcached_st #ifdef __cplusplus } #endif - -#endif /* BUILDING_LIBMEMCACHED */ - -#endif /* __LIBMEMCACHED_IO_H__ */ diff --git a/libmemcached/key.c b/libmemcached/key.c deleted file mode 100644 index 76a7d8ed..00000000 --- a/libmemcached/key.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "common.h" - -memcached_return_t memcached_key_test(const char * const *keys, - const size_t *key_length, - size_t number_of_keys) -{ - uint32_t x; - memcached_return_t rc; - - for (x= 0; x < number_of_keys; x++) - { - size_t y; - - rc= memcached_validate_key_length(*(key_length + x), false); - if (rc != MEMCACHED_SUCCESS) - return rc; - - for (y= 0; y < *(key_length + x); y++) - { - if ((isgraph(keys[x][y])) == 0) - return MEMCACHED_BAD_KEY_PROVIDED; - } - } - - return MEMCACHED_SUCCESS; -} - diff --git a/libmemcached/key.cc b/libmemcached/key.cc new file mode 100644 index 00000000..c2c2b14b --- /dev/null +++ b/libmemcached/key.cc @@ -0,0 +1,23 @@ +#include "common.h" + +memcached_return_t memcached_key_test(const char * const *keys, + const size_t *key_length, + size_t number_of_keys) +{ + for (uint32_t x= 0; x < number_of_keys; x++) + { + memcached_return_t rc; + rc= memcached_validate_key_length(*(key_length + x), false); + if (rc != MEMCACHED_SUCCESS) + return rc; + + for (size_t y= 0; y < *(key_length + x); y++) + { + if ((isgraph(keys[x][y])) == 0) + return MEMCACHED_BAD_KEY_PROVIDED; + } + } + + return MEMCACHED_SUCCESS; +} + diff --git a/libmemcached/memcached.c b/libmemcached/memcached.c deleted file mode 100644 index c57b2577..00000000 --- a/libmemcached/memcached.c +++ /dev/null @@ -1,438 +0,0 @@ -/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * - * Libmemcached library - * - * Copyright (C) 2011 Data Differential, http://datadifferential.com/ - * Copyright (C) 2006-2009 Brian Aker 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. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * The names of its contributors may not be used to endorse or - * promote products derived from this software without specific prior - * written permission. - * - * 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. - * - */ - -#include -#include - -static const memcached_st global_copy= { - .state= { - .is_purging= false, - .is_processing_input= false, - .is_time_for_rebuild= false, - }, - .flags= { - .auto_eject_hosts= false, - .binary_protocol= false, - .buffer_requests= false, - .hash_with_prefix_key= false, - .no_block= false, - .no_reply= false, - .randomize_replica_read= false, - .support_cas= false, - .tcp_nodelay= false, - .use_sort_hosts= false, - .use_udp= false, - .verify_key= false, - .tcp_keepalive= false, - }, -}; - -static inline bool _memcached_init(memcached_st *self) -{ - self->state= global_copy.state; - self->flags= global_copy.flags; - self->virtual_bucket= NULL; - - self->distribution= MEMCACHED_DISTRIBUTION_MODULA; - - hashkit_st *hash_ptr; - hash_ptr= hashkit_create(&self->hashkit); - if (! hash_ptr) - return false; - - self->ketama.continuum= NULL; - self->ketama.continuum_count= 0; - self->ketama.continuum_points_counter= 0; - self->ketama.next_distribution_rebuild= 0; - self->ketama.weighted= false; - - self->number_of_hosts= 0; - self->servers= NULL; - self->last_disconnected_server= NULL; - - self->snd_timeout= 0; - self->rcv_timeout= 0; - self->server_failure_limit= 0; - self->query_id= 0; - - /* TODO, Document why we picked these defaults */ - self->io_msg_watermark= 500; - self->io_bytes_watermark= 65 * 1024; - - self->tcp_keepidle= 0; - - self->io_key_prefetch= 0; - self->poll_timeout= MEMCACHED_DEFAULT_TIMEOUT; - self->connect_timeout= MEMCACHED_DEFAULT_CONNECT_TIMEOUT; - self->retry_timeout= 0; - - self->send_size= -1; - self->recv_size= -1; - - self->user_data= NULL; - self->number_of_replicas= 0; - hash_ptr= hashkit_create(&self->distribution_hashkit); - if (! hash_ptr) - return false; - - self->allocators= memcached_allocators_return_default(); - - self->on_clone= NULL; - self->on_cleanup= NULL; - self->get_key_failure= NULL; - self->delete_trigger= NULL; - self->callbacks= NULL; - self->sasl.callbacks= NULL; - self->sasl.is_allocated= false; - - self->error_messages= NULL; - self->prefix_key= NULL; - self->configure.initial_pool_size= 1; - self->configure.max_pool_size= 1; - self->configure.version= -1; - self->configure.filename= NULL; - - return true; -} - -static void _free(memcached_st *ptr, bool release_st) -{ - /* If we have anything open, lets close it now */ - send_quit(ptr); - memcached_server_list_free(memcached_server_list(ptr)); - memcached_result_free(&ptr->result); - - memcached_virtual_bucket_free(ptr); - - if (ptr->last_disconnected_server) - memcached_server_free(ptr->last_disconnected_server); - - if (ptr->on_cleanup) - ptr->on_cleanup(ptr); - - if (ptr->ketama.continuum) - libmemcached_free(ptr, ptr->ketama.continuum); - - memcached_array_free(ptr->prefix_key); - ptr->prefix_key= NULL; - - memcached_error_free(ptr); - - if (ptr->sasl.callbacks) - { -#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT - memcached_destroy_sasl_auth_data(ptr); -#endif - } - - if (release_st) - { - memcached_array_free(ptr->configure.filename); - ptr->configure.filename= NULL; - } - - if (memcached_is_allocated(ptr) && release_st) - { - libmemcached_free(ptr, ptr); - } -} - -memcached_st *memcached_create(memcached_st *ptr) -{ - if (ptr == NULL) - { - ptr= (memcached_st *)malloc(sizeof(memcached_st)); - - if (! ptr) - { - return NULL; /* MEMCACHED_MEMORY_ALLOCATION_FAILURE */ - } - - ptr->options.is_allocated= true; - } - else - { - ptr->options.is_allocated= false; - } - -#if 0 - memcached_set_purging(ptr, false); - memcached_set_processing_input(ptr, false); -#endif - - if (! _memcached_init(ptr)) - { - memcached_free(ptr); - return NULL; - } - - if (! memcached_result_create(ptr, &ptr->result)) - { - memcached_free(ptr); - return NULL; - } - - WATCHPOINT_ASSERT_INITIALIZED(&ptr->result); - - return ptr; -} - -memcached_st *memcached(const char *string, size_t length) -{ - if (! length || ! string) - { - errno= EINVAL; - return NULL; - } - - memcached_st *self= memcached_create(NULL); - if (! self) - { - errno= ENOMEM; - return NULL; - } - - memcached_return_t rc; - rc= memcached_parse_configuration(self, string, length); - - if (rc == MEMCACHED_SUCCESS && memcached_parse_filename(self)) - { - rc= memcached_parse_configure_file(self, memcached_parse_filename(self), memcached_parse_filename_length(self)); - } - - if (rc != MEMCACHED_SUCCESS) - { - memcached_free(self); - errno= EINVAL; - return NULL; - } - - errno= 0; - - return self; -} - -memcached_return_t memcached_reset(memcached_st *ptr) -{ - WATCHPOINT_ASSERT(ptr); - if (! ptr) - return MEMCACHED_INVALID_ARGUMENTS; - - bool stored_is_allocated= memcached_is_allocated(ptr); - uint64_t query_id= ptr->query_id; - _free(ptr, false); - memcached_create(ptr); - memcached_set_allocated(ptr, stored_is_allocated); - ptr->query_id= query_id; - - if (ptr->configure.filename) - { - return memcached_parse_configure_file(ptr, memcached_param_array(ptr->configure.filename)); - } - - return MEMCACHED_SUCCESS; -} - -void memcached_servers_reset(memcached_st *ptr) -{ - if (! ptr) - return; - - memcached_server_list_free(memcached_server_list(ptr)); - - memcached_server_list_set(ptr, NULL); - ptr->number_of_hosts= 0; - if (ptr->last_disconnected_server) - { - memcached_server_free(ptr->last_disconnected_server); - } - ptr->last_disconnected_server= NULL; - ptr->server_failure_limit= 0; -} - -void memcached_reset_last_disconnected_server(memcached_st *ptr) -{ - if (! ptr) - return; - - if (ptr->last_disconnected_server) - { - memcached_server_free(ptr->last_disconnected_server); - ptr->last_disconnected_server= NULL; - } -} - -void memcached_free(memcached_st *ptr) -{ - if (! ptr) - return; - - _free(ptr, true); -} - -/* - clone is the destination, while source is the structure to clone. - If source is NULL the call is the same as if a memcached_create() was - called. -*/ -memcached_st *memcached_clone(memcached_st *clone, const memcached_st *source) -{ - memcached_return_t rc= MEMCACHED_SUCCESS; - memcached_st *new_clone; - - if (source == NULL) - return memcached_create(clone); - - if (clone && memcached_is_allocated(clone)) - { - return NULL; - } - - new_clone= memcached_create(clone); - - if (new_clone == NULL) - return NULL; - - new_clone->flags= source->flags; - new_clone->send_size= source->send_size; - new_clone->recv_size= source->recv_size; - new_clone->poll_timeout= source->poll_timeout; - new_clone->connect_timeout= source->connect_timeout; - new_clone->retry_timeout= source->retry_timeout; - new_clone->distribution= source->distribution; - - hashkit_st *hash_ptr; - - hash_ptr= hashkit_clone(&new_clone->hashkit, &source->hashkit); - if (! hash_ptr) - { - memcached_free(new_clone); - return NULL; - } - - hash_ptr= hashkit_clone(&new_clone->distribution_hashkit, &source->distribution_hashkit); - if (! hash_ptr) - { - memcached_free(new_clone); - return NULL; - } - - new_clone->user_data= source->user_data; - - new_clone->snd_timeout= source->snd_timeout; - new_clone->rcv_timeout= source->rcv_timeout; - - new_clone->on_clone= source->on_clone; - new_clone->on_cleanup= source->on_cleanup; - - new_clone->allocators= source->allocators; - - new_clone->get_key_failure= source->get_key_failure; - new_clone->delete_trigger= source->delete_trigger; - new_clone->server_failure_limit= source->server_failure_limit; - new_clone->io_msg_watermark= source->io_msg_watermark; - new_clone->io_bytes_watermark= source->io_bytes_watermark; - new_clone->io_key_prefetch= source->io_key_prefetch; - new_clone->number_of_replicas= source->number_of_replicas; - new_clone->tcp_keepidle= source->tcp_keepidle; - - if (memcached_server_count(source)) - rc= memcached_push(new_clone, source); - - if (rc != MEMCACHED_SUCCESS) - { - memcached_free(new_clone); - - return NULL; - } - - - new_clone->prefix_key= memcached_array_clone(new_clone, source->prefix_key); - -#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT - if (source->sasl.callbacks) - { - if (memcached_clone_sasl(new_clone, source) != MEMCACHED_SUCCESS) - { - memcached_free(new_clone); - return NULL; - } - } -#endif - - rc= run_distribution(new_clone); - - if (rc != MEMCACHED_SUCCESS) - { - memcached_free(new_clone); - - return NULL; - } - - if (source->on_clone) - source->on_clone(new_clone, source); - - return new_clone; -} - -void *memcached_get_user_data(const memcached_st *ptr) -{ - return ptr->user_data; -} - -void *memcached_set_user_data(memcached_st *ptr, void *data) -{ - void *ret= ptr->user_data; - ptr->user_data= data; - - return ret; -} - -memcached_return_t memcached_push(memcached_st *destination, const memcached_st *source) -{ - return memcached_server_push(destination, source->servers); -} - -memcached_server_write_instance_st memcached_server_instance_fetch(memcached_st *ptr, uint32_t server_key) -{ - return &ptr->servers[server_key]; -} - -memcached_server_instance_st memcached_server_instance_by_position(const memcached_st *ptr, uint32_t server_key) -{ - return &ptr->servers[server_key]; -} diff --git a/libmemcached/memcached.cc b/libmemcached/memcached.cc new file mode 100644 index 00000000..1197dd5d --- /dev/null +++ b/libmemcached/memcached.cc @@ -0,0 +1,464 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2009 Brian Aker 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + +#include +#include + +#if 0 +static const memcached_st global_copy= { + .state= { + .is_purging= false, // .is_purging + .is_processing_input= false, // is_processing_input + .is_time_for_rebuild= false, + }, + .flags= { + .auto_eject_hosts= false, + .binary_protocol= false, + .buffer_requests= false, + .hash_with_prefix_key= false, + .no_block= false, + .no_reply= false, + .randomize_replica_read= false, + .support_cas= false, + .tcp_nodelay= false, + .use_sort_hosts= false, + .use_udp= false, + .verify_key= false, + .tcp_keepalive= false, + }, +}; +#endif + +static inline bool _memcached_init(memcached_st *self) +{ + self->state.is_purging= false; + self->state.is_processing_input= false; + self->state.is_time_for_rebuild= false; + + self->flags.auto_eject_hosts= false; + self->flags.binary_protocol= false; + self->flags.buffer_requests= false; + self->flags.hash_with_prefix_key= false; + self->flags.no_block= false; + self->flags.no_reply= false; + self->flags.randomize_replica_read= false; + self->flags.support_cas= false; + self->flags.tcp_nodelay= false; + self->flags.use_sort_hosts= false; + self->flags.use_udp= false; + self->flags.verify_key= false; + self->flags.tcp_keepalive= false; + + self->virtual_bucket= NULL; + + self->distribution= MEMCACHED_DISTRIBUTION_MODULA; + + hashkit_st *hash_ptr; + hash_ptr= hashkit_create(&self->hashkit); + if (! hash_ptr) + return false; + + self->ketama.continuum= NULL; + self->ketama.continuum_count= 0; + self->ketama.continuum_points_counter= 0; + self->ketama.next_distribution_rebuild= 0; + self->ketama.weighted= false; + + self->number_of_hosts= 0; + self->servers= NULL; + self->last_disconnected_server= NULL; + + self->snd_timeout= 0; + self->rcv_timeout= 0; + self->server_failure_limit= 0; + self->query_id= 1; // 0 is considered invalid + + /* TODO, Document why we picked these defaults */ + self->io_msg_watermark= 500; + self->io_bytes_watermark= 65 * 1024; + + self->tcp_keepidle= 0; + + self->io_key_prefetch= 0; + self->poll_timeout= MEMCACHED_DEFAULT_TIMEOUT; + self->connect_timeout= MEMCACHED_DEFAULT_CONNECT_TIMEOUT; + self->retry_timeout= 0; + + self->send_size= -1; + self->recv_size= -1; + + self->user_data= NULL; + self->number_of_replicas= 0; + hash_ptr= hashkit_create(&self->distribution_hashkit); + if (! hash_ptr) + return false; + + self->allocators= memcached_allocators_return_default(); + + self->on_clone= NULL; + self->on_cleanup= NULL; + self->get_key_failure= NULL; + self->delete_trigger= NULL; + self->callbacks= NULL; + self->sasl.callbacks= NULL; + self->sasl.is_allocated= false; + + self->error_messages= NULL; + self->prefix_key= NULL; + self->configure.initial_pool_size= 1; + self->configure.max_pool_size= 1; + self->configure.version= -1; + self->configure.filename= NULL; + + return true; +} + +static void _free(memcached_st *ptr, bool release_st) +{ + /* If we have anything open, lets close it now */ + send_quit(ptr); + memcached_server_list_free(memcached_server_list(ptr)); + memcached_result_free(&ptr->result); + + memcached_virtual_bucket_free(ptr); + + if (ptr->last_disconnected_server) + memcached_server_free(ptr->last_disconnected_server); + + if (ptr->on_cleanup) + ptr->on_cleanup(ptr); + + if (ptr->ketama.continuum) + libmemcached_free(ptr, ptr->ketama.continuum); + + memcached_array_free(ptr->prefix_key); + ptr->prefix_key= NULL; + + memcached_error_free(ptr); + + if (ptr->sasl.callbacks) + { +#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT + memcached_destroy_sasl_auth_data(ptr); +#endif + } + + if (release_st) + { + memcached_array_free(ptr->configure.filename); + ptr->configure.filename= NULL; + } + + if (memcached_is_allocated(ptr) && release_st) + { + libmemcached_free(ptr, ptr); + } +} + +memcached_st *memcached_create(memcached_st *ptr) +{ + if (ptr == NULL) + { + ptr= (memcached_st *)malloc(sizeof(memcached_st)); + + if (! ptr) + { + return NULL; /* MEMCACHED_MEMORY_ALLOCATION_FAILURE */ + } + + ptr->options.is_allocated= true; + } + else + { + ptr->options.is_allocated= false; + } + +#if 0 + memcached_set_purging(ptr, false); + memcached_set_processing_input(ptr, false); +#endif + + if (! _memcached_init(ptr)) + { + memcached_free(ptr); + return NULL; + } + + if (! memcached_result_create(ptr, &ptr->result)) + { + memcached_free(ptr); + return NULL; + } + + WATCHPOINT_ASSERT_INITIALIZED(&ptr->result); + + return ptr; +} + +memcached_st *memcached(const char *string, size_t length) +{ + if (! length || ! string) + { + errno= EINVAL; + return NULL; + } + + memcached_st *self= memcached_create(NULL); + if (! self) + { + errno= ENOMEM; + return NULL; + } + + memcached_return_t rc; + rc= memcached_parse_configuration(self, string, length); + + if (rc == MEMCACHED_SUCCESS && memcached_parse_filename(self)) + { + rc= memcached_parse_configure_file(self, memcached_parse_filename(self), memcached_parse_filename_length(self)); + } + + if (rc != MEMCACHED_SUCCESS) + { + memcached_free(self); + errno= EINVAL; + return NULL; + } + + errno= 0; + + return self; +} + +memcached_return_t memcached_reset(memcached_st *ptr) +{ + WATCHPOINT_ASSERT(ptr); + if (not ptr) + return MEMCACHED_INVALID_ARGUMENTS; + + bool stored_is_allocated= memcached_is_allocated(ptr); + uint64_t query_id= ptr->query_id; + _free(ptr, false); + memcached_create(ptr); + memcached_set_allocated(ptr, stored_is_allocated); + ptr->query_id= query_id; + + if (ptr->configure.filename) + { + return memcached_parse_configure_file(ptr, memcached_param_array(ptr->configure.filename)); + } + + return MEMCACHED_SUCCESS; +} + +void memcached_servers_reset(memcached_st *ptr) +{ + if (! ptr) + return; + + memcached_server_list_free(memcached_server_list(ptr)); + + memcached_server_list_set(ptr, NULL); + ptr->number_of_hosts= 0; + if (ptr->last_disconnected_server) + { + memcached_server_free(ptr->last_disconnected_server); + } + ptr->last_disconnected_server= NULL; + ptr->server_failure_limit= 0; +} + +void memcached_reset_last_disconnected_server(memcached_st *ptr) +{ + if (! ptr) + return; + + if (ptr->last_disconnected_server) + { + memcached_server_free(ptr->last_disconnected_server); + ptr->last_disconnected_server= NULL; + } +} + +void memcached_free(memcached_st *ptr) +{ + if (! ptr) + return; + + _free(ptr, true); +} + +/* + clone is the destination, while source is the structure to clone. + If source is NULL the call is the same as if a memcached_create() was + called. +*/ +memcached_st *memcached_clone(memcached_st *clone, const memcached_st *source) +{ + memcached_return_t rc= MEMCACHED_SUCCESS; + memcached_st *new_clone; + + if (source == NULL) + return memcached_create(clone); + + if (clone && memcached_is_allocated(clone)) + { + return NULL; + } + + new_clone= memcached_create(clone); + + if (new_clone == NULL) + return NULL; + + new_clone->flags= source->flags; + new_clone->send_size= source->send_size; + new_clone->recv_size= source->recv_size; + new_clone->poll_timeout= source->poll_timeout; + new_clone->connect_timeout= source->connect_timeout; + new_clone->retry_timeout= source->retry_timeout; + new_clone->distribution= source->distribution; + + hashkit_st *hash_ptr; + + hash_ptr= hashkit_clone(&new_clone->hashkit, &source->hashkit); + if (! hash_ptr) + { + memcached_free(new_clone); + return NULL; + } + + hash_ptr= hashkit_clone(&new_clone->distribution_hashkit, &source->distribution_hashkit); + if (! hash_ptr) + { + memcached_free(new_clone); + return NULL; + } + + new_clone->user_data= source->user_data; + + new_clone->snd_timeout= source->snd_timeout; + new_clone->rcv_timeout= source->rcv_timeout; + + new_clone->on_clone= source->on_clone; + new_clone->on_cleanup= source->on_cleanup; + + new_clone->allocators= source->allocators; + + new_clone->get_key_failure= source->get_key_failure; + new_clone->delete_trigger= source->delete_trigger; + new_clone->server_failure_limit= source->server_failure_limit; + new_clone->io_msg_watermark= source->io_msg_watermark; + new_clone->io_bytes_watermark= source->io_bytes_watermark; + new_clone->io_key_prefetch= source->io_key_prefetch; + new_clone->number_of_replicas= source->number_of_replicas; + new_clone->tcp_keepidle= source->tcp_keepidle; + + if (memcached_server_count(source)) + rc= memcached_push(new_clone, source); + + if (rc != MEMCACHED_SUCCESS) + { + memcached_free(new_clone); + + return NULL; + } + + + new_clone->prefix_key= memcached_array_clone(new_clone, source->prefix_key); + +#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT + if (source->sasl.callbacks) + { + if (memcached_clone_sasl(new_clone, source) != MEMCACHED_SUCCESS) + { + memcached_free(new_clone); + return NULL; + } + } +#endif + + rc= run_distribution(new_clone); + + if (rc != MEMCACHED_SUCCESS) + { + memcached_free(new_clone); + + return NULL; + } + + if (source->on_clone) + source->on_clone(new_clone, source); + + return new_clone; +} + +void *memcached_get_user_data(const memcached_st *ptr) +{ + return ptr->user_data; +} + +void *memcached_set_user_data(memcached_st *ptr, void *data) +{ + void *ret= ptr->user_data; + ptr->user_data= data; + + return ret; +} + +memcached_return_t memcached_push(memcached_st *destination, const memcached_st *source) +{ + return memcached_server_push(destination, source->servers); +} + +memcached_server_write_instance_st memcached_server_instance_fetch(memcached_st *ptr, uint32_t server_key) +{ + return &ptr->servers[server_key]; +} + +memcached_server_instance_st memcached_server_instance_by_position(const memcached_st *ptr, uint32_t server_key) +{ + return &ptr->servers[server_key]; +} + +uint64_t memcached_query_id(const memcached_st *self) +{ + if (not self) + return 0; + + return self->query_id; +} diff --git a/libmemcached/memcached.h b/libmemcached/memcached.h index 1baab961..f32fe0e7 100644 --- a/libmemcached/memcached.h +++ b/libmemcached/memcached.h @@ -57,6 +57,7 @@ #include #include #include + // Everything above this line must be in the order specified. #include #include @@ -142,13 +143,7 @@ struct memcached_st { struct memcached_virtual_bucket_t *virtual_bucket; - struct _allocators_st { - memcached_calloc_fn calloc; - memcached_free_fn free; - memcached_malloc_fn malloc; - memcached_realloc_fn realloc; - void *context; - } allocators; + struct memcached_allocator_t allocators; memcached_clone_fn on_clone; memcached_cleanup_fn on_cleanup; @@ -210,6 +205,9 @@ memcached_server_instance_st memcached_server_instance_by_position(const memcach LIBMEMCACHED_API uint32_t memcached_server_count(const memcached_st *); +LIBMEMCACHED_API +uint64_t memcached_query_id(const memcached_st *); + #ifdef __cplusplus } // extern "C" #endif diff --git a/libmemcached/memcached.hpp b/libmemcached/memcached.hpp index 55f96c7e..b0985715 100644 --- a/libmemcached/memcached.hpp +++ b/libmemcached/memcached.hpp @@ -13,8 +13,6 @@ */ #pragma once -#ifndef LIBMEMCACHEDPP_H -#define LIBMEMCACHEDPP_H #include #include @@ -37,8 +35,7 @@ class Memcache { public: - Memcache() - : + Memcache() : servers_list(), memc(), result() @@ -261,17 +258,12 @@ public: * this vector * @return true on success; false otherwise */ - bool get(const std::string &key, - std::vector &ret_val) throw (Error) + bool get(const std::string &key, std::vector &ret_val) { uint32_t flags= 0; memcached_return_t rc; size_t value_length= 0; - if (key.empty()) - { - throw(Error("the key supplied is empty!", false)); - } char *value= memcached_get(&memc, key.c_str(), key.length(), &value_length, &flags, &rc); if (value != NULL && ret_val.empty()) @@ -298,16 +290,12 @@ public: */ bool getByKey(const std::string &master_key, const std::string &key, - std::vector &ret_val) throw(Error) + std::vector &ret_val) { uint32_t flags= 0; memcached_return_t rc; size_t value_length= 0; - if (master_key.empty() || key.empty()) - { - throw(Error("the master key or key supplied is empty!", false)); - } char *value= memcached_get_by_key(&memc, master_key.c_str(), master_key.length(), key.c_str(), key.length(), @@ -379,12 +367,8 @@ public: bool set(const std::string &key, const std::vector &value, time_t expiration, - uint32_t flags) throw(Error) + uint32_t flags) { - if (key.empty() || value.empty()) - { - throw(Error("the key or value supplied is empty!", false)); - } memcached_return_t rc= memcached_set(&memc, key.c_str(), key.length(), &value[0], value.size(), @@ -407,14 +391,8 @@ public: const std::string &key, const std::vector &value, time_t expiration, - uint32_t flags) throw(Error) + uint32_t flags) { - if (master_key.empty() || - key.empty() || - value.empty()) - { - throw(Error("the key or value supplied is empty!", false)); - } memcached_return_t rc= memcached_set_by_key(&memc, master_key.c_str(), master_key.length(), key.c_str(), key.length(), @@ -437,12 +415,8 @@ public: bool setAll(std::vector &keys, std::vector< std::vector *> &values, time_t expiration, - uint32_t flags) throw(Error) + uint32_t flags) { - if (keys.size() != values.size()) - { - throw(Error("The number of keys and values do not match!", false)); - } bool retval= true; std::vector::iterator key_it= keys.begin(); std::vector< std::vector *>::iterator val_it= values.begin(); @@ -470,23 +444,18 @@ public: */ bool setAll(std::map > &key_value_map, time_t expiration, - uint32_t flags) throw(Error) + uint32_t flags) { - if (key_value_map.empty()) - { - throw(Error("The key/values are not properly set!", false)); - } bool retval= true; - std::map >::iterator it= - key_value_map.begin(); + std::map >::iterator it= key_value_map.begin(); + while (it != key_value_map.end()) { retval= set(it->first, it->second, expiration, flags); if (retval == false) { - std::string err_buff("There was an error setting the key "); - err_buff.append(it->first); - throw(Error(err_buff, false)); + // We should tell the user what the key that failed was + return false; } ++it; } @@ -503,12 +472,8 @@ public: * @param[out] value store the result of the increment here * @return true on success; false otherwise */ - bool increment(const std::string &key, uint32_t offset, uint64_t *value) throw(Error) + bool increment(const std::string &key, uint32_t offset, uint64_t *value) { - if (key.empty()) - { - throw(Error("the key supplied is empty!", false)); - } memcached_return_t rc= memcached_increment(&memc, key.c_str(), key.length(), offset, value); return (rc == MEMCACHED_SUCCESS); @@ -525,12 +490,7 @@ public: * @return true on success; false otherwise */ bool decrement(const std::string &key, uint32_t offset, uint64_t *value) - throw(Error) { - if (key.empty()) - { - throw(Error("the key supplied is empty!", false)); - } memcached_return_t rc= memcached_decrement(&memc, key.c_str(), key.length(), offset, value); @@ -547,12 +507,7 @@ public: * @return true on success; false otherwise */ bool add(const std::string &key, const std::vector &value) - throw(Error) { - if (key.empty() || value.empty()) - { - throw(Error("the key or value supplied is empty!", false)); - } memcached_return_t rc= memcached_add(&memc, key.c_str(), key.length(), &value[0], value.size(), 0, 0); return (rc == MEMCACHED_SUCCESS); @@ -570,14 +525,8 @@ public: */ bool addByKey(const std::string &master_key, const std::string &key, - const std::vector &value) throw(Error) + const std::vector &value) { - if (master_key.empty() || - key.empty() || - value.empty()) - { - throw(Error("the master key or key supplied is empty!", false)); - } memcached_return_t rc= memcached_add_by_key(&memc, master_key.c_str(), master_key.length(), @@ -597,13 +546,8 @@ public: * @param[in[ value value to replace object with * @return true on success; false otherwise */ - bool replace(const std::string &key, const std::vector &value) throw(Error) + bool replace(const std::string &key, const std::vector &value) { - if (key.empty() || - value.empty()) - { - throw(Error("the key or value supplied is empty!", false)); - } memcached_return_t rc= memcached_replace(&memc, key.c_str(), key.length(), &value[0], value.size(), 0, 0); @@ -624,12 +568,6 @@ public: const std::string &key, const std::vector &value) { - if (master_key.empty() || - key.empty() || - value.empty()) - { - throw(Error("the master key or key supplied is empty!", false)); - } memcached_return_t rc= memcached_replace_by_key(&memc, master_key.c_str(), master_key.length(), @@ -649,12 +587,7 @@ public: * @return true on success; false otherwise */ bool prepend(const std::string &key, const std::vector &value) - throw(Error) { - if (key.empty() || value.empty()) - { - throw(Error("the key or value supplied is empty!", false)); - } memcached_return_t rc= memcached_prepend(&memc, key.c_str(), key.length(), &value[0], value.size(), 0, 0); return (rc == MEMCACHED_SUCCESS); @@ -673,14 +606,7 @@ public: bool prependByKey(const std::string &master_key, const std::string &key, const std::vector &value) - throw(Error) { - if (master_key.empty() || - key.empty() || - value.empty()) - { - throw(Error("the master key or key supplied is empty!", false)); - } memcached_return_t rc= memcached_prepend_by_key(&memc, master_key.c_str(), master_key.length(), @@ -701,18 +627,13 @@ public: * @return true on success; false otherwise */ bool append(const std::string &key, const std::vector &value) - throw(Error) { - if (key.empty() || value.empty()) - { - throw(Error("the key or value supplied is empty!", false)); - } memcached_return_t rc= memcached_append(&memc, - key.c_str(), - key.length(), - &value[0], - value.size(), - 0, 0); + key.c_str(), + key.length(), + &value[0], + value.size(), + 0, 0); return (rc == MEMCACHED_SUCCESS); } @@ -729,22 +650,15 @@ public: bool appendByKey(const std::string &master_key, const std::string &key, const std::vector &value) - throw(Error) { - if (master_key.empty() || - key.empty() || - value.empty()) - { - throw(Error("the master key or key supplied is empty!", false)); - } memcached_return_t rc= memcached_append_by_key(&memc, - master_key.c_str(), - master_key.length(), - key.c_str(), - key.length(), - &value[0], - value.size(), - 0, 0); + master_key.c_str(), + master_key.length(), + key.c_str(), + key.length(), + &value[0], + value.size(), + 0, 0); return (rc == MEMCACHED_SUCCESS); } @@ -758,12 +672,8 @@ public: */ bool cas(const std::string &key, const std::vector &value, - uint64_t cas_arg) throw(Error) + uint64_t cas_arg) { - if (key.empty() || value.empty()) - { - throw(Error("the key or value supplied is empty!", false)); - } memcached_return_t rc= memcached_cas(&memc, key.c_str(), key.length(), &value[0], value.size(), 0, 0, cas_arg); @@ -783,14 +693,8 @@ public: bool casByKey(const std::string &master_key, const std::string &key, const std::vector &value, - uint64_t cas_arg) throw(Error) + uint64_t cas_arg) { - if (master_key.empty() || - key.empty() || - value.empty()) - { - throw(Error("the master key, key or value supplied is empty!", false)); - } memcached_return_t rc= memcached_cas_by_key(&memc, master_key.c_str(), master_key.length(), @@ -808,12 +712,8 @@ public: * @param[in] key key of object to delete * @return true on success; false otherwise */ - bool remove(const std::string &key) throw(Error) + bool remove(const std::string &key) { - if (key.empty()) - { - throw(Error("the key supplied is empty!", false)); - } memcached_return_t rc= memcached_delete(&memc, key.c_str(), key.length(), 0); return (rc == MEMCACHED_SUCCESS); } @@ -825,13 +725,8 @@ public: * @param[in] expiration time to delete the object after * @return true on success; false otherwise */ - bool remove(const std::string &key, - time_t expiration) throw(Error) + bool remove(const std::string &key, time_t expiration) { - if (key.empty()) - { - throw(Error("the key supplied is empty!", false)); - } memcached_return_t rc= memcached_delete(&memc, key.c_str(), key.length(), @@ -847,12 +742,8 @@ public: * @return true on success; false otherwise */ bool removeByKey(const std::string &master_key, - const std::string &key) throw(Error) + const std::string &key) { - if (master_key.empty() || key.empty()) - { - throw(Error("the master key or key supplied is empty!", false)); - } memcached_return_t rc= memcached_delete_by_key(&memc, master_key.c_str(), master_key.length(), @@ -872,12 +763,8 @@ public: */ bool removeByKey(const std::string &master_key, const std::string &key, - time_t expiration) throw(Error) + time_t expiration) { - if (master_key.empty() || key.empty()) - { - throw(Error("the master key or key supplied is empty!", false)); - } memcached_return_t rc= memcached_delete_by_key(&memc, master_key.c_str(), master_key.length(), @@ -900,27 +787,6 @@ public: return (rc == MEMCACHED_SUCCESS); } - /** - * Callback function for result sets. It passes the result - * sets to the list of functions provided. - * - * @param[in] callback list of callback functions - * @param[in] context pointer to memory reference that is - * supplied to the calling function - * @param[in] num_of_callbacks number of callback functions - * @return true on success; false otherwise - */ - bool fetchExecute(memcached_execute_fn *callback, - void *context, - uint32_t num_of_callbacks) - { - memcached_return_t rc= memcached_fetch_execute(&memc, - callback, - context, - num_of_callbacks); - return (rc == MEMCACHED_SUCCESS); - } - /** * Get the library version string. * @return std::string containing a copy of the library version string. @@ -996,5 +862,3 @@ private: }; } - -#endif /* LIBMEMCACHEDPP_H */ diff --git a/libmemcached/memcached/protocol_binary.h b/libmemcached/memcached/protocol_binary.h index 4509bbcb..7a253ae7 100644 --- a/libmemcached/memcached/protocol_binary.h +++ b/libmemcached/memcached/protocol_binary.h @@ -218,12 +218,13 @@ extern "C" /** * Definition of a request-packet containing no extras */ - typedef union { + union protocol_binary_request_no_extras { struct { protocol_binary_request_header header; } message; uint8_t bytes[sizeof(protocol_binary_request_header)]; - } protocol_binary_request_no_extras; + }; + typedef union protocol_binary_request_no_extras protocol_binary_request_no_extras; /** * Definition of a response-packet containing no extras diff --git a/libmemcached/options/context.h b/libmemcached/options/context.h index bbca66c8..226284d9 100644 --- a/libmemcached/options/context.h +++ b/libmemcached/options/context.h @@ -90,7 +90,7 @@ public: const char *set_hostname(const char *str, size_t size) { - size_t copy_length= std::min((size_t)NI_MAXHOST, size); + size_t copy_length= (size_t)NI_MAXHOST > size ? size : (size_t)NI_MAXHOST; memcpy(_hostname, str, copy_length); _hostname[copy_length]= 0; diff --git a/libmemcached/parse.c b/libmemcached/parse.c deleted file mode 100644 index 85860ef3..00000000 --- a/libmemcached/parse.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - I debated about putting this in the client library since it does an - action I don't really believe belongs in the library. - - Frankly its too damn useful not to be here though. -*/ - -#include "common.h" - -memcached_server_list_st memcached_servers_parse(const char *server_strings) -{ - char *string; - const char *begin_ptr; - const char *end_ptr; - memcached_server_st *servers= NULL; - memcached_return_t rc; - - WATCHPOINT_ASSERT(server_strings); - - end_ptr= server_strings + strlen(server_strings); - - for (begin_ptr= server_strings, string= index(server_strings, ','); - begin_ptr != end_ptr; - string= index(begin_ptr, ',')) - { - char buffer[HUGE_STRING_LEN]; - char *ptr, *ptr2; - uint32_t weight= 0; - - if (string) - { - memcpy(buffer, begin_ptr, (size_t) (string - begin_ptr)); - buffer[(unsigned int)(string - begin_ptr)]= 0; - begin_ptr= string+1; - } - else - { - size_t length= strlen(begin_ptr); - memcpy(buffer, begin_ptr, length); - buffer[length]= 0; - begin_ptr= end_ptr; - } - - ptr= index(buffer, ':'); - - in_port_t port= 0; - if (ptr) - { - ptr[0]= 0; - - ptr++; - - port= (in_port_t) strtoul(ptr, (char **)NULL, 10); - - ptr2= index(ptr, ' '); - if (! ptr2) - ptr2= index(ptr, ':'); - - if (ptr2) - { - ptr2++; - weight = (uint32_t) strtoul(ptr2, (char **)NULL, 10); - } - } - - servers= memcached_server_list_append_with_weight(servers, buffer, port, weight, &rc); - - if (isspace(*begin_ptr)) - begin_ptr++; - } - - return servers; -} diff --git a/libmemcached/parse.cc b/libmemcached/parse.cc new file mode 100644 index 00000000..3284a4f6 --- /dev/null +++ b/libmemcached/parse.cc @@ -0,0 +1,110 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2009 Brian Aker 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + +/* + I debated about putting this in the client library since it does an + action I don't really believe belongs in the library. + + Frankly its too damn useful not to be here though. +*/ + +#include + +memcached_server_list_st memcached_servers_parse(const char *server_strings) +{ + char *string; + const char *begin_ptr; + const char *end_ptr; + memcached_server_st *servers= NULL; + memcached_return_t rc; + + WATCHPOINT_ASSERT(server_strings); + + end_ptr= server_strings + strlen(server_strings); + + for (begin_ptr= server_strings, string= (char *)index(server_strings, ','); + begin_ptr != end_ptr; + string= (char *)index(begin_ptr, ',')) + { + char buffer[HUGE_STRING_LEN]; + char *ptr, *ptr2; + uint32_t weight= 0; + + if (string) + { + memcpy(buffer, begin_ptr, (size_t) (string - begin_ptr)); + buffer[(unsigned int)(string - begin_ptr)]= 0; + begin_ptr= string+1; + } + else + { + size_t length= strlen(begin_ptr); + memcpy(buffer, begin_ptr, length); + buffer[length]= 0; + begin_ptr= end_ptr; + } + + ptr= index(buffer, ':'); + + in_port_t port= 0; + if (ptr) + { + ptr[0]= 0; + + ptr++; + + port= (in_port_t) strtoul(ptr, (char **)NULL, 10); + + ptr2= index(ptr, ' '); + if (! ptr2) + ptr2= index(ptr, ':'); + + if (ptr2) + { + ptr2++; + weight = (uint32_t) strtoul(ptr2, (char **)NULL, 10); + } + } + + servers= memcached_server_list_append_with_weight(servers, buffer, port, weight, &rc); + + if (isspace(*begin_ptr)) + begin_ptr++; + } + + return servers; +} diff --git a/libmemcached/protocol/ascii_handler.c b/libmemcached/protocol/ascii_handler.c index 465b7396..5e7307ae 100644 --- a/libmemcached/protocol/ascii_handler.c +++ b/libmemcached/protocol/ascii_handler.c @@ -1,10 +1,48 @@ -/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ -#include "libmemcached/protocol/common.h" +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + +#include "config.h" + +#include +#include #include -#include -#include #include +#include +#include /** * Try to parse a key from the string. diff --git a/libmemcached/protocol/ascii_handler.h b/libmemcached/protocol/ascii_handler.h index 02a1a82d..02f8831e 100644 --- a/libmemcached/protocol/ascii_handler.h +++ b/libmemcached/protocol/ascii_handler.h @@ -1,8 +1,40 @@ -/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ -#ifndef LIBMEMCACHED_PROTOCOL_ASCII_HANDLER_H -#define LIBMEMCACHED_PROTOCOL_ASCII_HANDLER_H +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + +#pragma once LIBMEMCACHED_LOCAL memcached_protocol_event_t memcached_ascii_protocol_process_data(memcached_protocol_client_st *client, ssize_t *length, void **endptr); - -#endif diff --git a/libmemcached/protocol/binary_handler.c b/libmemcached/protocol/binary_handler.c index a9e4ce95..93fb3162 100644 --- a/libmemcached/protocol/binary_handler.c +++ b/libmemcached/protocol/binary_handler.c @@ -1,5 +1,41 @@ -/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ -#include "libmemcached/protocol/common.h" +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + +#include +#include #include #include diff --git a/libmemcached/protocol/binary_handler.h b/libmemcached/protocol/binary_handler.h index a21165b2..d5a74e78 100644 --- a/libmemcached/protocol/binary_handler.h +++ b/libmemcached/protocol/binary_handler.h @@ -1,6 +1,40 @@ -/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ -#ifndef LIBMEMCACHED_PROTOCOL_BINARY_HANDLER_H -#define LIBMEMCACHED_PROTOCOL_BINARY_HANDLER_H +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + +#pragma once LIBMEMCACHED_LOCAL bool memcached_binary_protocol_pedantic_check_request(const protocol_binary_request_header *request); @@ -11,5 +45,3 @@ bool memcached_binary_protocol_pedantic_check_response(const protocol_binary_req LIBMEMCACHED_LOCAL memcached_protocol_event_t memcached_binary_protocol_process_data(memcached_protocol_client_st *client, ssize_t *length, void **endptr); - -#endif diff --git a/libmemcached/protocol/common.h b/libmemcached/protocol/common.h index 185aef0b..808a6086 100644 --- a/libmemcached/protocol/common.h +++ b/libmemcached/protocol/common.h @@ -1,6 +1,40 @@ -/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ -#ifndef LIBMEMCACHED_PROTOCOL_COMMON_H -#define LIBMEMCACHED_PROTOCOL_COMMON_H +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + +#pragma once #include "config.h" #if !defined(__cplusplus) @@ -8,12 +42,7 @@ #endif #include -/* Define this here, which will turn on the visibilty controls while we're - * building libmemcached. - */ -#define BUILDING_LIBMEMCACHED 1 - -#include +#include #include #include @@ -132,5 +161,3 @@ struct memcached_protocol_client_st { #include "ascii_handler.h" #include "binary_handler.h" - -#endif diff --git a/libmemcached/protocol/include.am b/libmemcached/protocol/include.am new file mode 100644 index 00000000..9c4c1bc2 --- /dev/null +++ b/libmemcached/protocol/include.am @@ -0,0 +1,26 @@ +# vim:ft=automake +# included from Top Level Makefile.am +# All paths should be given relative to the root + + +lib_LTLIBRARIES+= libmemcached/libmemcachedprotocol.la +libmemcached_libmemcachedprotocol_la_SOURCES= \ + libmemcached/byteorder.cc \ + libmemcached/protocol/ascii_handler.c \ + libmemcached/protocol/binary_handler.c \ + libmemcached/protocol/cache.c \ + libmemcached/protocol/pedantic.c \ + libmemcached/protocol/protocol_handler.c + +libmemcached_libmemcachedprotocol_la_CFLAGS= \ + ${AM_CFLAGS} \ + ${NO_CONVERSION} \ + ${PTHREAD_CFLAGS} \ + -DBUILDING_LIBMEMCACHED + +libmemcached_libmemcachedprotocol_la_CXXFLAGS= \ + ${AM_CXXFLAGS} \ + ${PTHREAD_CFLAGS} \ + -DBUILDING_LIBMEMCACHED + +libmemcached_libmemcachedprotocol_la_LDFLAGS= ${AM_LDFLAGS} ${PTHREAD_LIBS} -version-info ${MEMCACHED_PROTOCAL_LIBRARY_VERSION} diff --git a/libmemcached/purge.c b/libmemcached/purge.c deleted file mode 100644 index 07cd135f..00000000 --- a/libmemcached/purge.c +++ /dev/null @@ -1,90 +0,0 @@ -#include "common.h" - -memcached_return_t memcached_purge(memcached_server_write_instance_st ptr) -{ - uint32_t x; - memcached_return_t ret= MEMCACHED_SUCCESS; - memcached_st *root= (memcached_st *)ptr->root; - - if (memcached_is_purging(ptr->root) || /* already purging */ - (memcached_server_response_count(ptr) < ptr->root->io_msg_watermark && - ptr->io_bytes_sent < ptr->root->io_bytes_watermark) || - (ptr->io_bytes_sent >= ptr->root->io_bytes_watermark && - memcached_server_response_count(ptr) < 2)) - { - return MEMCACHED_SUCCESS; - } - - /* memcached_io_write and memcached_response may call memcached_purge - so we need to be able stop any recursion.. */ - memcached_set_purging(root, true); - - WATCHPOINT_ASSERT(ptr->fd != -1); - /* Force a flush of the buffer to ensure that we don't have the n-1 pending - requests buffered up.. */ - if (memcached_io_write(ptr, NULL, 0, true) == -1) - { - memcached_set_purging(root, true); - - return MEMCACHED_WRITE_FAILURE; - } - WATCHPOINT_ASSERT(ptr->fd != -1); - - uint32_t no_msg= memcached_server_response_count(ptr) - 1; - if (no_msg > 0) - { - memcached_result_st result; - memcached_result_st *result_ptr; - char buffer[SMALL_STRING_LEN]; - - /* - * We need to increase the timeout, because we might be waiting for - * data to be sent from the server (the commands was in the output buffer - * and just flushed - */ - const int32_t timeo= ptr->root->poll_timeout; - root->poll_timeout= 2000; - - result_ptr= memcached_result_create(root, &result); - WATCHPOINT_ASSERT(result_ptr); - - for (x= 0; x < no_msg; x++) - { - memcached_result_reset(result_ptr); - memcached_return_t rc= memcached_read_one_response(ptr, buffer, - sizeof (buffer), - result_ptr); - /* - * Purge doesn't care for what kind of command results that is received. - * The only kind of errors I care about if is I'm out of sync with the - * protocol or have problems reading data from the network.. - */ - if (rc== MEMCACHED_PROTOCOL_ERROR || rc == MEMCACHED_UNKNOWN_READ_FAILURE) - { - WATCHPOINT_ERROR(rc); - ret = rc; - memcached_io_reset(ptr); - } - - if (ptr->root->callbacks != NULL) - { - memcached_callback_st cb = *ptr->root->callbacks; - if (rc == MEMCACHED_SUCCESS) - { - for (uint32_t y= 0; y < cb.number_of_callback; y++) - { - rc = (*cb.callback[y])(ptr->root, result_ptr, cb.context); - if (rc != MEMCACHED_SUCCESS) - break; - } - } - } - } - - memcached_result_free(result_ptr); - root->poll_timeout= timeo; - } - memcached_set_purging(root, false); - - return ret; -} diff --git a/libmemcached/purge.cc b/libmemcached/purge.cc new file mode 100644 index 00000000..07cd135f --- /dev/null +++ b/libmemcached/purge.cc @@ -0,0 +1,90 @@ +#include "common.h" + +memcached_return_t memcached_purge(memcached_server_write_instance_st ptr) +{ + uint32_t x; + memcached_return_t ret= MEMCACHED_SUCCESS; + memcached_st *root= (memcached_st *)ptr->root; + + if (memcached_is_purging(ptr->root) || /* already purging */ + (memcached_server_response_count(ptr) < ptr->root->io_msg_watermark && + ptr->io_bytes_sent < ptr->root->io_bytes_watermark) || + (ptr->io_bytes_sent >= ptr->root->io_bytes_watermark && + memcached_server_response_count(ptr) < 2)) + { + return MEMCACHED_SUCCESS; + } + + /* memcached_io_write and memcached_response may call memcached_purge + so we need to be able stop any recursion.. */ + memcached_set_purging(root, true); + + WATCHPOINT_ASSERT(ptr->fd != -1); + /* Force a flush of the buffer to ensure that we don't have the n-1 pending + requests buffered up.. */ + if (memcached_io_write(ptr, NULL, 0, true) == -1) + { + memcached_set_purging(root, true); + + return MEMCACHED_WRITE_FAILURE; + } + WATCHPOINT_ASSERT(ptr->fd != -1); + + uint32_t no_msg= memcached_server_response_count(ptr) - 1; + if (no_msg > 0) + { + memcached_result_st result; + memcached_result_st *result_ptr; + char buffer[SMALL_STRING_LEN]; + + /* + * We need to increase the timeout, because we might be waiting for + * data to be sent from the server (the commands was in the output buffer + * and just flushed + */ + const int32_t timeo= ptr->root->poll_timeout; + root->poll_timeout= 2000; + + result_ptr= memcached_result_create(root, &result); + WATCHPOINT_ASSERT(result_ptr); + + for (x= 0; x < no_msg; x++) + { + memcached_result_reset(result_ptr); + memcached_return_t rc= memcached_read_one_response(ptr, buffer, + sizeof (buffer), + result_ptr); + /* + * Purge doesn't care for what kind of command results that is received. + * The only kind of errors I care about if is I'm out of sync with the + * protocol or have problems reading data from the network.. + */ + if (rc== MEMCACHED_PROTOCOL_ERROR || rc == MEMCACHED_UNKNOWN_READ_FAILURE) + { + WATCHPOINT_ERROR(rc); + ret = rc; + memcached_io_reset(ptr); + } + + if (ptr->root->callbacks != NULL) + { + memcached_callback_st cb = *ptr->root->callbacks; + if (rc == MEMCACHED_SUCCESS) + { + for (uint32_t y= 0; y < cb.number_of_callback; y++) + { + rc = (*cb.callback[y])(ptr->root, result_ptr, cb.context); + if (rc != MEMCACHED_SUCCESS) + break; + } + } + } + } + + memcached_result_free(result_ptr); + root->poll_timeout= timeo; + } + memcached_set_purging(root, false); + + return ret; +} diff --git a/libmemcached/quit.c b/libmemcached/quit.c deleted file mode 100644 index 6d72906c..00000000 --- a/libmemcached/quit.c +++ /dev/null @@ -1,105 +0,0 @@ -#include "common.h" - -/* - This closes all connections (forces flush of input as well). - - Maybe add a host specific, or key specific version? - - The reason we send "quit" is that in case we have buffered IO, this - will force data to be completed. -*/ - -void memcached_quit_server(memcached_server_st *ptr, bool io_death) -{ - if (ptr->fd != INVALID_SOCKET) - { - if (io_death == false && ptr->type != MEMCACHED_CONNECTION_UDP && ptr->options.is_shutting_down == false) - { - memcached_return_t rc; - char buffer[MEMCACHED_MAX_BUFFER]; - - ptr->options.is_shutting_down= true; - - if (ptr->root->flags.binary_protocol) - { - protocol_binary_request_quit request = {.bytes= {0}}; - request.message.header.request.magic = PROTOCOL_BINARY_REQ; - request.message.header.request.opcode = PROTOCOL_BINARY_CMD_QUIT; - request.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; - rc= memcached_do(ptr, request.bytes, sizeof(request.bytes), true); - } - else - { - rc= memcached_do(ptr, "quit\r\n", sizeof("quit\r\n") -1, true); - } - - WATCHPOINT_ASSERT(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_FETCH_NOTFINISHED); - (void)rc; // Shut up ICC - - /* read until socket is closed, or there is an error - * closing the socket before all data is read - * results in server throwing away all data which is - * not read - * - * In .40 we began to only do this if we had been doing buffered - * requests of had replication enabled. - */ - if (ptr->root->flags.buffer_requests || ptr->root->number_of_replicas) - { - ssize_t nread; - while (memcached_io_read(ptr, buffer, sizeof(buffer)/sizeof(*buffer), - &nread) == MEMCACHED_SUCCESS); - } - - - /* - * memcached_io_read may call memcached_quit_server with io_death if - * it encounters problems, but we don't care about those occurences. - * The intention of that loop is to drain the data sent from the - * server to ensure that the server processed all of the data we - * sent to the server. - */ - ptr->server_failure_counter= 0; - } - memcached_io_close(ptr); - } - - ptr->fd= INVALID_SOCKET; - ptr->io_bytes_sent= 0; - ptr->write_buffer_offset= (size_t) ((ptr->type == MEMCACHED_CONNECTION_UDP) ? UDP_DATAGRAM_HEADER_LENGTH : 0); - ptr->read_buffer_length= 0; - ptr->read_ptr= ptr->read_buffer; - ptr->options.is_shutting_down= false; - memcached_server_response_reset(ptr); - - // We reset the version so that if we end up talking to a different server - // we don't have stale server version information. - ptr->major_version= ptr->minor_version= ptr->micro_version= UINT8_MAX; - - if (io_death) - { - ptr->server_failure_counter++; - set_last_disconnected_host(ptr); - } -} - -void send_quit(memcached_st *ptr) -{ - for (uint32_t x= 0; x < memcached_server_count(ptr); x++) - { - memcached_server_write_instance_st instance= - memcached_server_instance_fetch(ptr, x); - - memcached_quit_server(instance, false); - } -} - -void memcached_quit(memcached_st *ptr) -{ - if (initialize_query(ptr) != MEMCACHED_SUCCESS) - { - return; - } - - send_quit(ptr); -} diff --git a/libmemcached/quit.cc b/libmemcached/quit.cc new file mode 100644 index 00000000..2efe909b --- /dev/null +++ b/libmemcached/quit.cc @@ -0,0 +1,142 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + +#include + +/* + This closes all connections (forces flush of input as well). + + Maybe add a host specific, or key specific version? + + The reason we send "quit" is that in case we have buffered IO, this + will force data to be completed. +*/ + +void memcached_quit_server(memcached_server_st *ptr, bool io_death) +{ + if (ptr->fd != INVALID_SOCKET) + { + if (io_death == false && ptr->type != MEMCACHED_CONNECTION_UDP && ptr->options.is_shutting_down == false) + { + memcached_return_t rc; + char buffer[MEMCACHED_MAX_BUFFER]; + + ptr->options.is_shutting_down= true; + + if (ptr->root->flags.binary_protocol) + { + protocol_binary_request_quit request= {}; // = {.bytes= {0}}; + request.message.header.request.magic = PROTOCOL_BINARY_REQ; + request.message.header.request.opcode = PROTOCOL_BINARY_CMD_QUIT; + request.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; + rc= memcached_do(ptr, request.bytes, sizeof(request.bytes), true); + } + else + { + rc= memcached_do(ptr, "quit\r\n", sizeof("quit\r\n") -1, true); + } + + WATCHPOINT_ASSERT(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_FETCH_NOTFINISHED); + (void)rc; // Shut up ICC + + /* read until socket is closed, or there is an error + * closing the socket before all data is read + * results in server throwing away all data which is + * not read + * + * In .40 we began to only do this if we had been doing buffered + * requests of had replication enabled. + */ + if (ptr->root->flags.buffer_requests || ptr->root->number_of_replicas) + { + ssize_t nread; + while (memcached_io_read(ptr, buffer, sizeof(buffer)/sizeof(*buffer), + &nread) == MEMCACHED_SUCCESS); + } + + + /* + * memcached_io_read may call memcached_quit_server with io_death if + * it encounters problems, but we don't care about those occurences. + * The intention of that loop is to drain the data sent from the + * server to ensure that the server processed all of the data we + * sent to the server. + */ + ptr->server_failure_counter= 0; + } + memcached_io_close(ptr); + } + + ptr->fd= INVALID_SOCKET; + ptr->io_bytes_sent= 0; + ptr->write_buffer_offset= (size_t) ((ptr->type == MEMCACHED_CONNECTION_UDP) ? UDP_DATAGRAM_HEADER_LENGTH : 0); + ptr->read_buffer_length= 0; + ptr->read_ptr= ptr->read_buffer; + ptr->options.is_shutting_down= false; + memcached_server_response_reset(ptr); + + // We reset the version so that if we end up talking to a different server + // we don't have stale server version information. + ptr->major_version= ptr->minor_version= ptr->micro_version= UINT8_MAX; + + if (io_death) + { + ptr->server_failure_counter++; + set_last_disconnected_host(ptr); + } +} + +void send_quit(memcached_st *ptr) +{ + for (uint32_t x= 0; x < memcached_server_count(ptr); x++) + { + memcached_server_write_instance_st instance= + memcached_server_instance_fetch(ptr, x); + + memcached_quit_server(instance, false); + } +} + +void memcached_quit(memcached_st *ptr) +{ + if (initialize_query(ptr) != MEMCACHED_SUCCESS) + { + return; + } + + send_quit(ptr); +} diff --git a/libmemcached/quit.h b/libmemcached/quit.h index e640020a..0338eaf0 100644 --- a/libmemcached/quit.h +++ b/libmemcached/quit.h @@ -1,16 +1,41 @@ -/* LibMemcached - * Copyright (C) 2010 Brian Aker - * All rights reserved. +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker All rights reserved. * - * Summary: returns a human readable string for the error message + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. * */ -#ifndef __LIBMEMCACHED_QUIT_H__ -#define __LIBMEMCACHED_QUIT_H__ +#pragma once #ifdef __cplusplus extern "C" { @@ -28,5 +53,3 @@ void send_quit(memcached_st *ptr); #ifdef __cplusplus } #endif - -#endif /* __LIBMEMCACHED_QUIT_H__ */ diff --git a/libmemcached/response.c b/libmemcached/response.c deleted file mode 100644 index 49825fb1..00000000 --- a/libmemcached/response.c +++ /dev/null @@ -1,584 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: memcached_response() is used to determine the return result from an issued command. - * -*/ - -#include "common.h" - -static memcached_return_t textual_read_one_response(memcached_server_write_instance_st ptr, - char *buffer, size_t buffer_length, - memcached_result_st *result); -static memcached_return_t binary_read_one_response(memcached_server_write_instance_st ptr, - char *buffer, size_t buffer_length, - memcached_result_st *result); - -memcached_return_t memcached_read_one_response(memcached_server_write_instance_st ptr, - char *buffer, size_t buffer_length, - memcached_result_st *result) -{ - memcached_server_response_decrement(ptr); - - if (result == NULL) - { - memcached_st *root= (memcached_st *)ptr->root; - result = &root->result; - } - - memcached_return_t rc; - if (ptr->root->flags.binary_protocol) - rc= binary_read_one_response(ptr, buffer, buffer_length, result); - else - rc= textual_read_one_response(ptr, buffer, buffer_length, result); - - unlikely(rc == MEMCACHED_UNKNOWN_READ_FAILURE || - rc == MEMCACHED_PROTOCOL_ERROR || - rc == MEMCACHED_CLIENT_ERROR || - rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE) - memcached_io_reset(ptr); - - return rc; -} - -memcached_return_t memcached_response(memcached_server_write_instance_st ptr, - char *buffer, size_t buffer_length, - memcached_result_st *result) -{ - /* We may have old commands in the buffer not set, first purge */ - if ((ptr->root->flags.no_block) && (memcached_is_processing_input(ptr->root) == false)) - { - (void)memcached_io_write(ptr, NULL, 0, true); - } - - /* - * The previous implementation purged all pending requests and just - * returned the last one. Purge all pending messages to ensure backwards - * compatibility. - */ - if (ptr->root->flags.binary_protocol == false) - { - while (memcached_server_response_count(ptr) > 1) - { - memcached_return_t rc= memcached_read_one_response(ptr, buffer, buffer_length, result); - - unlikely (rc != MEMCACHED_END && - rc != MEMCACHED_STORED && - rc != MEMCACHED_SUCCESS && - rc != MEMCACHED_STAT && - rc != MEMCACHED_DELETED && - rc != MEMCACHED_NOTFOUND && - rc != MEMCACHED_NOTSTORED && - rc != MEMCACHED_DATA_EXISTS) - return rc; - } - } - - return memcached_read_one_response(ptr, buffer, buffer_length, result); -} - -static memcached_return_t textual_value_fetch(memcached_server_write_instance_st ptr, - char *buffer, - memcached_result_st *result) -{ - memcached_return_t rc= MEMCACHED_SUCCESS; - char *string_ptr; - char *end_ptr; - char *next_ptr; - size_t value_length; - size_t to_read; - char *value_ptr; - ssize_t read_length= 0; - memcached_return_t rrc; - - if (ptr->root->flags.use_udp) - return MEMCACHED_NOT_SUPPORTED; - - WATCHPOINT_ASSERT(ptr->root); - end_ptr= buffer + MEMCACHED_DEFAULT_COMMAND_SIZE; - - memcached_result_reset(result); - - string_ptr= buffer; - string_ptr+= 6; /* "VALUE " */ - - - /* We load the key */ - { - char *key; - size_t prefix_length; - - key= result->item_key; - result->key_length= 0; - - for (prefix_length= memcached_array_size(ptr->root->prefix_key); !(iscntrl(*string_ptr) || isspace(*string_ptr)) ; string_ptr++) - { - if (prefix_length == 0) - { - *key= *string_ptr; - key++; - result->key_length++; - } - else - prefix_length--; - } - result->item_key[result->key_length]= 0; - } - - if (end_ptr == string_ptr) - goto read_error; - - /* Flags fetch move past space */ - string_ptr++; - if (end_ptr == string_ptr) - goto read_error; - for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++); - result->item_flags= (uint32_t) strtoul(next_ptr, &string_ptr, 10); - - if (end_ptr == string_ptr) - goto read_error; - - /* Length fetch move past space*/ - string_ptr++; - if (end_ptr == string_ptr) - goto read_error; - - for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++); - value_length= (size_t)strtoull(next_ptr, &string_ptr, 10); - - if (end_ptr == string_ptr) - goto read_error; - - /* Skip spaces */ - if (*string_ptr == '\r') - { - /* Skip past the \r\n */ - string_ptr+= 2; - } - else - { - string_ptr++; - for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++); - result->item_cas= strtoull(next_ptr, &string_ptr, 10); - } - - if (end_ptr < string_ptr) - goto read_error; - - /* We add two bytes so that we can walk the \r\n */ - rc= memcached_string_check(&result->value, value_length+2); - if (rc != MEMCACHED_SUCCESS) - { - value_length= 0; - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - } - - value_ptr= memcached_string_value_mutable(&result->value); - /* - We read the \r\n into the string since not doing so is more - cycles then the waster of memory to do so. - - We are null terminating through, which will most likely make - some people lazy about using the return length. - */ - to_read= (value_length) + 2; - rrc= memcached_io_read(ptr, value_ptr, to_read, &read_length); - if (rrc != MEMCACHED_SUCCESS) - return rrc; - - if (read_length != (ssize_t)(value_length + 2)) - { - goto read_error; - } - - /* This next bit blows the API, but this is internal....*/ - { - char *char_ptr; - char_ptr= memcached_string_value_mutable(&result->value);; - char_ptr[value_length]= 0; - char_ptr[value_length + 1]= 0; - memcached_string_set_length(&result->value, value_length); - } - - return MEMCACHED_SUCCESS; - -read_error: - memcached_io_reset(ptr); - - return MEMCACHED_PARTIAL_READ; -} - -static memcached_return_t textual_read_one_response(memcached_server_write_instance_st ptr, - char *buffer, size_t buffer_length, - memcached_result_st *result) -{ - memcached_return_t rc= memcached_io_readline(ptr, buffer, buffer_length); - if (rc != MEMCACHED_SUCCESS) - return rc; - - switch(buffer[0]) - { - case 'V': /* VALUE || VERSION */ - if (buffer[1] == 'A') /* VALUE */ - { - /* We add back in one because we will need to search for END */ - memcached_server_response_increment(ptr); - return textual_value_fetch(ptr, buffer, result); - } - else if (buffer[1] == 'E') /* VERSION */ - { - return MEMCACHED_SUCCESS; - } - else - { - WATCHPOINT_STRING(buffer); - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - case 'O': /* OK */ - return MEMCACHED_SUCCESS; - case 'S': /* STORED STATS SERVER_ERROR */ - { - if (buffer[2] == 'A') /* STORED STATS */ - { - memcached_server_response_increment(ptr); - return MEMCACHED_STAT; - } - else if (buffer[1] == 'E') /* SERVER_ERROR */ - { - char *rel_ptr; - char *startptr= buffer + 13, *endptr= startptr; - - while (*endptr != '\r' && *endptr != '\n') endptr++; - - /* - Yes, we could make this "efficent" but to do that we would need - to maintain more state for the size of the buffer. Why waste - memory in the struct, which is important, for something that - rarely should happen? - */ - rel_ptr= (char *)libmemcached_realloc(ptr->root, - ptr->cached_server_error, - (size_t) (endptr - startptr + 1)); - - if (rel_ptr == NULL) - { - /* If we happened to have some memory, we just null it since we don't know the size */ - if (ptr->cached_server_error) - ptr->cached_server_error[0]= 0; - return MEMCACHED_SERVER_ERROR; - } - ptr->cached_server_error= rel_ptr; - - memcpy(ptr->cached_server_error, startptr, (size_t) (endptr - startptr)); - ptr->cached_server_error[endptr - startptr]= 0; - return MEMCACHED_SERVER_ERROR; - } - else if (buffer[1] == 'T') - return MEMCACHED_STORED; - else - { - WATCHPOINT_STRING(buffer); - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - } - case 'D': /* DELETED */ - return MEMCACHED_DELETED; - case 'N': /* NOT_FOUND */ - { - if (buffer[4] == 'F') - return MEMCACHED_NOTFOUND; - else if (buffer[4] == 'S') - return MEMCACHED_NOTSTORED; - else - { - WATCHPOINT_STRING(buffer); - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - } - case 'E': /* PROTOCOL ERROR or END */ - { - if (buffer[1] == 'N') - return MEMCACHED_END; - else if (buffer[1] == 'R') - return MEMCACHED_PROTOCOL_ERROR; - else if (buffer[1] == 'X') - return MEMCACHED_DATA_EXISTS; - else - { - WATCHPOINT_STRING(buffer); - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - - } - case 'I': /* CLIENT ERROR */ - /* We add back in one because we will need to search for END */ - memcached_server_response_increment(ptr); - return MEMCACHED_ITEM; - case 'C': /* CLIENT ERROR */ - return MEMCACHED_CLIENT_ERROR; - default: - { - unsigned long long auto_return_value; - - if (sscanf(buffer, "%llu", &auto_return_value) == 1) - return MEMCACHED_SUCCESS; - - WATCHPOINT_STRING(buffer); - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - } - - /* NOTREACHED */ -} - -static memcached_return_t binary_read_one_response(memcached_server_write_instance_st ptr, - char *buffer, size_t buffer_length, - memcached_result_st *result) -{ - memcached_return_t rc; - protocol_binary_response_header header; - - if ((rc= memcached_safe_read(ptr, &header.bytes, sizeof(header.bytes))) != MEMCACHED_SUCCESS) - { - WATCHPOINT_ERROR(rc); - return rc; - } - - if (header.response.magic != PROTOCOL_BINARY_RES) - { - return MEMCACHED_PROTOCOL_ERROR; - } - - /* - ** Convert the header to host local endian! - */ - header.response.keylen= ntohs(header.response.keylen); - header.response.status= ntohs(header.response.status); - header.response.bodylen= ntohl(header.response.bodylen); - header.response.cas= ntohll(header.response.cas); - uint32_t bodylen= header.response.bodylen; - - if (header.response.status == PROTOCOL_BINARY_RESPONSE_SUCCESS || - header.response.status == PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE) - { - switch (header.response.opcode) - { - case PROTOCOL_BINARY_CMD_GETKQ: - /* - * We didn't increment the response counter for the GETKQ packet - * (only the final NOOP), so we need to increment the counter again. - */ - memcached_server_response_increment(ptr); - /* FALLTHROUGH */ - case PROTOCOL_BINARY_CMD_GETK: - { - uint16_t keylen= header.response.keylen; - memcached_result_reset(result); - result->item_cas= header.response.cas; - - if ((rc= memcached_safe_read(ptr, &result->item_flags, sizeof (result->item_flags))) != MEMCACHED_SUCCESS) - { - WATCHPOINT_ERROR(rc); - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - - result->item_flags= ntohl(result->item_flags); - bodylen -= header.response.extlen; - - result->key_length= keylen; - if ((rc= memcached_safe_read(ptr, result->item_key, keylen)) != MEMCACHED_SUCCESS) - { - WATCHPOINT_ERROR(rc); - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - - bodylen -= keylen; - if (memcached_string_check(&result->value, - bodylen) != MEMCACHED_SUCCESS) - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - - char *vptr= memcached_string_value_mutable(&result->value); - if ((rc= memcached_safe_read(ptr, vptr, bodylen)) != MEMCACHED_SUCCESS) - { - WATCHPOINT_ERROR(rc); - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - - memcached_string_set_length(&result->value, bodylen); - } - break; - case PROTOCOL_BINARY_CMD_INCREMENT: - case PROTOCOL_BINARY_CMD_DECREMENT: - { - if (bodylen != sizeof(uint64_t) || buffer_length != sizeof(uint64_t)) - return MEMCACHED_PROTOCOL_ERROR; - - WATCHPOINT_ASSERT(bodylen == buffer_length); - uint64_t val; - if ((rc= memcached_safe_read(ptr, &val, sizeof(val))) != MEMCACHED_SUCCESS) - { - WATCHPOINT_ERROR(rc); - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - - val= ntohll(val); - memcpy(buffer, &val, sizeof(val)); - } - break; - case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS: - case PROTOCOL_BINARY_CMD_VERSION: - { - memset(buffer, 0, buffer_length); - if (bodylen >= buffer_length) - { - /* not enough space in buffer.. should not happen... */ - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - else if ((rc= memcached_safe_read(ptr, buffer, bodylen)) != MEMCACHED_SUCCESS) - { - WATCHPOINT_ERROR(rc); - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - } - break; - case PROTOCOL_BINARY_CMD_FLUSH: - case PROTOCOL_BINARY_CMD_QUIT: - case PROTOCOL_BINARY_CMD_SET: - case PROTOCOL_BINARY_CMD_ADD: - case PROTOCOL_BINARY_CMD_REPLACE: - case PROTOCOL_BINARY_CMD_APPEND: - case PROTOCOL_BINARY_CMD_PREPEND: - case PROTOCOL_BINARY_CMD_DELETE: - { - WATCHPOINT_ASSERT(bodylen == 0); - return MEMCACHED_SUCCESS; - } - case PROTOCOL_BINARY_CMD_NOOP: - { - WATCHPOINT_ASSERT(bodylen == 0); - return MEMCACHED_END; - } - case PROTOCOL_BINARY_CMD_STAT: - { - if (bodylen == 0) - { - return MEMCACHED_END; - } - else if (bodylen + 1 > buffer_length) - { - /* not enough space in buffer.. should not happen... */ - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - else - { - size_t keylen= header.response.keylen; - memset(buffer, 0, buffer_length); - if ((rc= memcached_safe_read(ptr, buffer, keylen)) != MEMCACHED_SUCCESS || - (rc= memcached_safe_read(ptr, buffer + keylen + 1, bodylen - keylen)) != MEMCACHED_SUCCESS) - { - WATCHPOINT_ERROR(rc); - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - } - } - break; - - case PROTOCOL_BINARY_CMD_SASL_AUTH: - case PROTOCOL_BINARY_CMD_SASL_STEP: - { - memcached_result_reset(result); - result->item_cas= header.response.cas; - - if (memcached_string_check(&result->value, - bodylen) != MEMCACHED_SUCCESS) - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - - char *vptr= memcached_string_value_mutable(&result->value); - if ((rc= memcached_safe_read(ptr, vptr, bodylen)) != MEMCACHED_SUCCESS) - { - WATCHPOINT_ERROR(rc); - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - - memcached_string_set_length(&result->value, bodylen); - } - break; - default: - { - /* Command not implemented yet! */ - WATCHPOINT_ASSERT(0); - return MEMCACHED_PROTOCOL_ERROR; - } - } - } - else if (header.response.bodylen) - { - /* What should I do with the error message??? just discard it for now */ - char hole[SMALL_STRING_LEN]; - while (bodylen > 0) - { - size_t nr= (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen; - if ((rc= memcached_safe_read(ptr, hole, nr)) != MEMCACHED_SUCCESS) - { - WATCHPOINT_ERROR(rc); - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - bodylen-= (uint32_t) nr; - } - - /* This might be an error from one of the quiet commands.. if - * so, just throw it away and get the next one. What about creating - * a callback to the user with the error information? - */ - switch (header.response.opcode) - { - case PROTOCOL_BINARY_CMD_SETQ: - case PROTOCOL_BINARY_CMD_ADDQ: - case PROTOCOL_BINARY_CMD_REPLACEQ: - case PROTOCOL_BINARY_CMD_APPENDQ: - case PROTOCOL_BINARY_CMD_PREPENDQ: - return binary_read_one_response(ptr, buffer, buffer_length, result); - default: - break; - } - } - - rc= MEMCACHED_SUCCESS; - unlikely(header.response.status != 0) - switch (header.response.status) - { - case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT: - rc= MEMCACHED_NOTFOUND; - break; - case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS: - rc= MEMCACHED_DATA_EXISTS; - break; - case PROTOCOL_BINARY_RESPONSE_NOT_STORED: - rc= MEMCACHED_NOTSTORED; - break; - case PROTOCOL_BINARY_RESPONSE_E2BIG: - rc= MEMCACHED_E2BIG; - break; - case PROTOCOL_BINARY_RESPONSE_ENOMEM: - rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE; - break; - case PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE: - rc= MEMCACHED_AUTH_CONTINUE; - break; - case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR: - rc= MEMCACHED_AUTH_FAILURE; - break; - case PROTOCOL_BINARY_RESPONSE_EINVAL: - case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND: - default: - /* @todo fix the error mappings */ - rc= MEMCACHED_PROTOCOL_ERROR; - break; - } - - return rc; -} diff --git a/libmemcached/response.cc b/libmemcached/response.cc new file mode 100644 index 00000000..572aef58 --- /dev/null +++ b/libmemcached/response.cc @@ -0,0 +1,610 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2009 Brian Aker 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + +#include + +static memcached_return_t textual_read_one_response(memcached_server_write_instance_st ptr, + char *buffer, size_t buffer_length, + memcached_result_st *result); +static memcached_return_t binary_read_one_response(memcached_server_write_instance_st ptr, + char *buffer, size_t buffer_length, + memcached_result_st *result); + +memcached_return_t memcached_read_one_response(memcached_server_write_instance_st ptr, + char *buffer, size_t buffer_length, + memcached_result_st *result) +{ + memcached_server_response_decrement(ptr); + + if (result == NULL) + { + memcached_st *root= (memcached_st *)ptr->root; + result = &root->result; + } + + memcached_return_t rc; + if (ptr->root->flags.binary_protocol) + rc= binary_read_one_response(ptr, buffer, buffer_length, result); + else + rc= textual_read_one_response(ptr, buffer, buffer_length, result); + + unlikely(rc == MEMCACHED_UNKNOWN_READ_FAILURE || + rc == MEMCACHED_PROTOCOL_ERROR || + rc == MEMCACHED_CLIENT_ERROR || + rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE) + memcached_io_reset(ptr); + + return rc; +} + +memcached_return_t memcached_response(memcached_server_write_instance_st ptr, + char *buffer, size_t buffer_length, + memcached_result_st *result) +{ + /* We may have old commands in the buffer not set, first purge */ + if ((ptr->root->flags.no_block) && (memcached_is_processing_input(ptr->root) == false)) + { + (void)memcached_io_write(ptr, NULL, 0, true); + } + + /* + * The previous implementation purged all pending requests and just + * returned the last one. Purge all pending messages to ensure backwards + * compatibility. + */ + if (ptr->root->flags.binary_protocol == false) + { + while (memcached_server_response_count(ptr) > 1) + { + memcached_return_t rc= memcached_read_one_response(ptr, buffer, buffer_length, result); + + unlikely (rc != MEMCACHED_END && + rc != MEMCACHED_STORED && + rc != MEMCACHED_SUCCESS && + rc != MEMCACHED_STAT && + rc != MEMCACHED_DELETED && + rc != MEMCACHED_NOTFOUND && + rc != MEMCACHED_NOTSTORED && + rc != MEMCACHED_DATA_EXISTS) + return rc; + } + } + + return memcached_read_one_response(ptr, buffer, buffer_length, result); +} + +static memcached_return_t textual_value_fetch(memcached_server_write_instance_st ptr, + char *buffer, + memcached_result_st *result) +{ + memcached_return_t rc= MEMCACHED_SUCCESS; + char *string_ptr; + char *end_ptr; + char *next_ptr; + size_t value_length; + size_t to_read; + char *value_ptr; + ssize_t read_length= 0; + memcached_return_t rrc; + + if (ptr->root->flags.use_udp) + return MEMCACHED_NOT_SUPPORTED; + + WATCHPOINT_ASSERT(ptr->root); + end_ptr= buffer + MEMCACHED_DEFAULT_COMMAND_SIZE; + + memcached_result_reset(result); + + string_ptr= buffer; + string_ptr+= 6; /* "VALUE " */ + + + /* We load the key */ + { + char *key; + size_t prefix_length; + + key= result->item_key; + result->key_length= 0; + + for (prefix_length= memcached_array_size(ptr->root->prefix_key); !(iscntrl(*string_ptr) || isspace(*string_ptr)) ; string_ptr++) + { + if (prefix_length == 0) + { + *key= *string_ptr; + key++; + result->key_length++; + } + else + prefix_length--; + } + result->item_key[result->key_length]= 0; + } + + if (end_ptr == string_ptr) + goto read_error; + + /* Flags fetch move past space */ + string_ptr++; + if (end_ptr == string_ptr) + goto read_error; + for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++); + result->item_flags= (uint32_t) strtoul(next_ptr, &string_ptr, 10); + + if (end_ptr == string_ptr) + goto read_error; + + /* Length fetch move past space*/ + string_ptr++; + if (end_ptr == string_ptr) + goto read_error; + + for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++); + value_length= (size_t)strtoull(next_ptr, &string_ptr, 10); + + if (end_ptr == string_ptr) + goto read_error; + + /* Skip spaces */ + if (*string_ptr == '\r') + { + /* Skip past the \r\n */ + string_ptr+= 2; + } + else + { + string_ptr++; + for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++); + result->item_cas= strtoull(next_ptr, &string_ptr, 10); + } + + if (end_ptr < string_ptr) + goto read_error; + + /* We add two bytes so that we can walk the \r\n */ + rc= memcached_string_check(&result->value, value_length+2); + if (rc != MEMCACHED_SUCCESS) + { + value_length= 0; + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + } + + value_ptr= memcached_string_value_mutable(&result->value); + /* + We read the \r\n into the string since not doing so is more + cycles then the waster of memory to do so. + + We are null terminating through, which will most likely make + some people lazy about using the return length. + */ + to_read= (value_length) + 2; + rrc= memcached_io_read(ptr, value_ptr, to_read, &read_length); + if (rrc != MEMCACHED_SUCCESS) + return rrc; + + if (read_length != (ssize_t)(value_length + 2)) + { + goto read_error; + } + + /* This next bit blows the API, but this is internal....*/ + { + char *char_ptr; + char_ptr= memcached_string_value_mutable(&result->value);; + char_ptr[value_length]= 0; + char_ptr[value_length + 1]= 0; + memcached_string_set_length(&result->value, value_length); + } + + return MEMCACHED_SUCCESS; + +read_error: + memcached_io_reset(ptr); + + return MEMCACHED_PARTIAL_READ; +} + +static memcached_return_t textual_read_one_response(memcached_server_write_instance_st ptr, + char *buffer, size_t buffer_length, + memcached_result_st *result) +{ + memcached_return_t rc= memcached_io_readline(ptr, buffer, buffer_length); + if (rc != MEMCACHED_SUCCESS) + return rc; + + switch(buffer[0]) + { + case 'V': /* VALUE || VERSION */ + if (buffer[1] == 'A') /* VALUE */ + { + /* We add back in one because we will need to search for END */ + memcached_server_response_increment(ptr); + return textual_value_fetch(ptr, buffer, result); + } + else if (buffer[1] == 'E') /* VERSION */ + { + return MEMCACHED_SUCCESS; + } + else + { + WATCHPOINT_STRING(buffer); + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + case 'O': /* OK */ + return MEMCACHED_SUCCESS; + case 'S': /* STORED STATS SERVER_ERROR */ + { + if (buffer[2] == 'A') /* STORED STATS */ + { + memcached_server_response_increment(ptr); + return MEMCACHED_STAT; + } + else if (buffer[1] == 'E') /* SERVER_ERROR */ + { + char *rel_ptr; + char *startptr= buffer + 13, *endptr= startptr; + + while (*endptr != '\r' && *endptr != '\n') endptr++; + + /* + Yes, we could make this "efficent" but to do that we would need + to maintain more state for the size of the buffer. Why waste + memory in the struct, which is important, for something that + rarely should happen? + */ + rel_ptr= (char *)libmemcached_realloc(ptr->root, + ptr->cached_server_error, + (size_t) (endptr - startptr + 1)); + + if (rel_ptr == NULL) + { + /* If we happened to have some memory, we just null it since we don't know the size */ + if (ptr->cached_server_error) + ptr->cached_server_error[0]= 0; + return MEMCACHED_SERVER_ERROR; + } + ptr->cached_server_error= rel_ptr; + + memcpy(ptr->cached_server_error, startptr, (size_t) (endptr - startptr)); + ptr->cached_server_error[endptr - startptr]= 0; + return MEMCACHED_SERVER_ERROR; + } + else if (buffer[1] == 'T') + return MEMCACHED_STORED; + else + { + WATCHPOINT_STRING(buffer); + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + } + case 'D': /* DELETED */ + return MEMCACHED_DELETED; + case 'N': /* NOT_FOUND */ + { + if (buffer[4] == 'F') + return MEMCACHED_NOTFOUND; + else if (buffer[4] == 'S') + return MEMCACHED_NOTSTORED; + else + { + WATCHPOINT_STRING(buffer); + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + } + case 'E': /* PROTOCOL ERROR or END */ + { + if (buffer[1] == 'N') + return MEMCACHED_END; + else if (buffer[1] == 'R') + return MEMCACHED_PROTOCOL_ERROR; + else if (buffer[1] == 'X') + return MEMCACHED_DATA_EXISTS; + else + { + WATCHPOINT_STRING(buffer); + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + + } + case 'I': /* CLIENT ERROR */ + /* We add back in one because we will need to search for END */ + memcached_server_response_increment(ptr); + return MEMCACHED_ITEM; + case 'C': /* CLIENT ERROR */ + return MEMCACHED_CLIENT_ERROR; + default: + { + unsigned long long auto_return_value; + + if (sscanf(buffer, "%llu", &auto_return_value) == 1) + return MEMCACHED_SUCCESS; + + WATCHPOINT_STRING(buffer); + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + } + + /* NOTREACHED */ +} + +static memcached_return_t binary_read_one_response(memcached_server_write_instance_st ptr, + char *buffer, size_t buffer_length, + memcached_result_st *result) +{ + memcached_return_t rc; + protocol_binary_response_header header; + + if ((rc= memcached_safe_read(ptr, &header.bytes, sizeof(header.bytes))) != MEMCACHED_SUCCESS) + { + WATCHPOINT_ERROR(rc); + return rc; + } + + if (header.response.magic != PROTOCOL_BINARY_RES) + { + return MEMCACHED_PROTOCOL_ERROR; + } + + /* + ** Convert the header to host local endian! + */ + header.response.keylen= ntohs(header.response.keylen); + header.response.status= ntohs(header.response.status); + header.response.bodylen= ntohl(header.response.bodylen); + header.response.cas= ntohll(header.response.cas); + uint32_t bodylen= header.response.bodylen; + + if (header.response.status == PROTOCOL_BINARY_RESPONSE_SUCCESS || + header.response.status == PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE) + { + switch (header.response.opcode) + { + case PROTOCOL_BINARY_CMD_GETKQ: + /* + * We didn't increment the response counter for the GETKQ packet + * (only the final NOOP), so we need to increment the counter again. + */ + memcached_server_response_increment(ptr); + /* FALLTHROUGH */ + case PROTOCOL_BINARY_CMD_GETK: + { + uint16_t keylen= header.response.keylen; + memcached_result_reset(result); + result->item_cas= header.response.cas; + + if ((rc= memcached_safe_read(ptr, &result->item_flags, sizeof (result->item_flags))) != MEMCACHED_SUCCESS) + { + WATCHPOINT_ERROR(rc); + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + + result->item_flags= ntohl(result->item_flags); + bodylen -= header.response.extlen; + + result->key_length= keylen; + if ((rc= memcached_safe_read(ptr, result->item_key, keylen)) != MEMCACHED_SUCCESS) + { + WATCHPOINT_ERROR(rc); + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + + bodylen -= keylen; + if (memcached_string_check(&result->value, + bodylen) != MEMCACHED_SUCCESS) + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + + char *vptr= memcached_string_value_mutable(&result->value); + if ((rc= memcached_safe_read(ptr, vptr, bodylen)) != MEMCACHED_SUCCESS) + { + WATCHPOINT_ERROR(rc); + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + + memcached_string_set_length(&result->value, bodylen); + } + break; + case PROTOCOL_BINARY_CMD_INCREMENT: + case PROTOCOL_BINARY_CMD_DECREMENT: + { + if (bodylen != sizeof(uint64_t) || buffer_length != sizeof(uint64_t)) + return MEMCACHED_PROTOCOL_ERROR; + + WATCHPOINT_ASSERT(bodylen == buffer_length); + uint64_t val; + if ((rc= memcached_safe_read(ptr, &val, sizeof(val))) != MEMCACHED_SUCCESS) + { + WATCHPOINT_ERROR(rc); + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + + val= ntohll(val); + memcpy(buffer, &val, sizeof(val)); + } + break; + case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS: + case PROTOCOL_BINARY_CMD_VERSION: + { + memset(buffer, 0, buffer_length); + if (bodylen >= buffer_length) + { + /* not enough space in buffer.. should not happen... */ + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + else if ((rc= memcached_safe_read(ptr, buffer, bodylen)) != MEMCACHED_SUCCESS) + { + WATCHPOINT_ERROR(rc); + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + } + break; + case PROTOCOL_BINARY_CMD_FLUSH: + case PROTOCOL_BINARY_CMD_QUIT: + case PROTOCOL_BINARY_CMD_SET: + case PROTOCOL_BINARY_CMD_ADD: + case PROTOCOL_BINARY_CMD_REPLACE: + case PROTOCOL_BINARY_CMD_APPEND: + case PROTOCOL_BINARY_CMD_PREPEND: + case PROTOCOL_BINARY_CMD_DELETE: + { + WATCHPOINT_ASSERT(bodylen == 0); + return MEMCACHED_SUCCESS; + } + case PROTOCOL_BINARY_CMD_NOOP: + { + WATCHPOINT_ASSERT(bodylen == 0); + return MEMCACHED_END; + } + case PROTOCOL_BINARY_CMD_STAT: + { + if (bodylen == 0) + { + return MEMCACHED_END; + } + else if (bodylen + 1 > buffer_length) + { + /* not enough space in buffer.. should not happen... */ + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + else + { + size_t keylen= header.response.keylen; + memset(buffer, 0, buffer_length); + if ((rc= memcached_safe_read(ptr, buffer, keylen)) != MEMCACHED_SUCCESS || + (rc= memcached_safe_read(ptr, buffer + keylen + 1, bodylen - keylen)) != MEMCACHED_SUCCESS) + { + WATCHPOINT_ERROR(rc); + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + } + } + break; + + case PROTOCOL_BINARY_CMD_SASL_AUTH: + case PROTOCOL_BINARY_CMD_SASL_STEP: + { + memcached_result_reset(result); + result->item_cas= header.response.cas; + + if (memcached_string_check(&result->value, + bodylen) != MEMCACHED_SUCCESS) + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + + char *vptr= memcached_string_value_mutable(&result->value); + if ((rc= memcached_safe_read(ptr, vptr, bodylen)) != MEMCACHED_SUCCESS) + { + WATCHPOINT_ERROR(rc); + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + + memcached_string_set_length(&result->value, bodylen); + } + break; + default: + { + /* Command not implemented yet! */ + WATCHPOINT_ASSERT(0); + return MEMCACHED_PROTOCOL_ERROR; + } + } + } + else if (header.response.bodylen) + { + /* What should I do with the error message??? just discard it for now */ + char hole[SMALL_STRING_LEN]; + while (bodylen > 0) + { + size_t nr= (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen; + if ((rc= memcached_safe_read(ptr, hole, nr)) != MEMCACHED_SUCCESS) + { + WATCHPOINT_ERROR(rc); + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + bodylen-= (uint32_t) nr; + } + + /* This might be an error from one of the quiet commands.. if + * so, just throw it away and get the next one. What about creating + * a callback to the user with the error information? + */ + switch (header.response.opcode) + { + case PROTOCOL_BINARY_CMD_SETQ: + case PROTOCOL_BINARY_CMD_ADDQ: + case PROTOCOL_BINARY_CMD_REPLACEQ: + case PROTOCOL_BINARY_CMD_APPENDQ: + case PROTOCOL_BINARY_CMD_PREPENDQ: + return binary_read_one_response(ptr, buffer, buffer_length, result); + default: + break; + } + } + + rc= MEMCACHED_SUCCESS; + unlikely(header.response.status != 0) + switch (header.response.status) + { + case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT: + rc= MEMCACHED_NOTFOUND; + break; + case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS: + rc= MEMCACHED_DATA_EXISTS; + break; + case PROTOCOL_BINARY_RESPONSE_NOT_STORED: + rc= MEMCACHED_NOTSTORED; + break; + case PROTOCOL_BINARY_RESPONSE_E2BIG: + rc= MEMCACHED_E2BIG; + break; + case PROTOCOL_BINARY_RESPONSE_ENOMEM: + rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE; + break; + case PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE: + rc= MEMCACHED_AUTH_CONTINUE; + break; + case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR: + rc= MEMCACHED_AUTH_FAILURE; + break; + case PROTOCOL_BINARY_RESPONSE_EINVAL: + case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND: + default: + /* @todo fix the error mappings */ + rc= MEMCACHED_PROTOCOL_ERROR; + break; + } + + return rc; +} diff --git a/libmemcached/response.h b/libmemcached/response.h index da6f2b42..51f09998 100644 --- a/libmemcached/response.h +++ b/libmemcached/response.h @@ -1,16 +1,41 @@ -/* LibMemcached - * Copyright (C) 2010 Brian Aker - * All rights reserved. +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker All rights reserved. * - * Summary: Change the behavior of the memcached connection. + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. * */ -#ifndef __LIBMEMCACHED_RESPONSE_H__ -#define __LIBMEMCACHED_RESPONSE_H__ +#pragma once #ifdef __cplusplus extern "C" { @@ -30,5 +55,3 @@ memcached_return_t memcached_response(memcached_server_write_instance_st ptr, #ifdef __cplusplus } #endif - -#endif /* __LIBMEMCACHED_RESPONSE_H__ */ diff --git a/libmemcached/result.c b/libmemcached/result.c deleted file mode 100644 index 6e58eebd..00000000 --- a/libmemcached/result.c +++ /dev/null @@ -1,144 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Functions to manipulate the result structure. - * - */ - -/* - memcached_result_st are used to internally represent the return values from - memcached. We use a structure so that long term as identifiers are added - to memcached we will be able to absorb new attributes without having - to addjust the entire API. -*/ -#include "common.h" - -static inline void _result_init(memcached_result_st *self, - memcached_st *memc) -{ - self->item_flags= 0; - self->item_expiration= 0; - self->key_length= 0; - self->item_cas= 0; - self->root= memc; - self->item_key[0]= 0; -} - -memcached_result_st *memcached_result_create(const memcached_st *memc, - memcached_result_st *ptr) -{ - WATCHPOINT_ASSERT(memc); - - /* Saving malloc calls :) */ - if (ptr) - { - ptr->options.is_allocated= false; - } - else - { - ptr= libmemcached_malloc(memc, sizeof(memcached_result_st)); - - if (ptr == NULL) - return NULL; - - ptr->options.is_allocated= true; - } - - ptr->options.is_initialized= true; - - _result_init(ptr, (memcached_st *)memc); - - WATCHPOINT_SET(ptr->value.options.is_initialized= false); - memcached_string_create(memc, &ptr->value, 0); - WATCHPOINT_ASSERT_INITIALIZED(&ptr->value); - WATCHPOINT_ASSERT(ptr->value.string == NULL); - - return ptr; -} - -void memcached_result_reset(memcached_result_st *ptr) -{ - ptr->key_length= 0; - memcached_string_reset(&ptr->value); - ptr->item_flags= 0; - ptr->item_cas= 0; - ptr->item_expiration= 0; -} - -void memcached_result_free(memcached_result_st *ptr) -{ - if (ptr == NULL) - return; - - memcached_string_free(&ptr->value); - - if (memcached_is_allocated(ptr)) - { - WATCHPOINT_ASSERT(ptr->root); // Without a root, that means that result was not properly initialized. - libmemcached_free(ptr->root, ptr); - } - else - { - ptr->options.is_initialized= false; - } -} - -memcached_return_t memcached_result_set_value(memcached_result_st *ptr, - const char *value, - size_t length) -{ - memcached_return_t rc= memcached_string_append(&ptr->value, value, length); - - if (rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE) - { - memcached_set_errno(ptr->root, errno, NULL); - } - - return rc; -} - -const char *memcached_result_key_value(const memcached_result_st *self) -{ - return self->key_length ? self->item_key : NULL; -} - -size_t memcached_result_key_length(const memcached_result_st *self) -{ - return self->key_length; -} - -const char *memcached_result_value(const memcached_result_st *self) -{ - const memcached_string_st *sptr= &self->value; - return memcached_string_value(sptr); -} - -size_t memcached_result_length(const memcached_result_st *self) -{ - const memcached_string_st *sptr= &self->value; - return memcached_string_length(sptr); -} - -uint32_t memcached_result_flags(const memcached_result_st *self) -{ - return self->item_flags; -} - -uint64_t memcached_result_cas(const memcached_result_st *self) -{ - return self->item_cas; -} - -void memcached_result_set_flags(memcached_result_st *self, uint32_t flags) -{ - self->item_flags= flags; -} - -void memcached_result_set_expiration(memcached_result_st *self, time_t expiration) -{ - self->item_expiration= expiration; -} diff --git a/libmemcached/result.cc b/libmemcached/result.cc new file mode 100644 index 00000000..1d0b763d --- /dev/null +++ b/libmemcached/result.cc @@ -0,0 +1,171 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2009 Brian Aker 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + + +/* + memcached_result_st are used to internally represent the return values from + memcached. We use a structure so that long term as identifiers are added + to memcached we will be able to absorb new attributes without having + to addjust the entire API. +*/ +#include + +static inline void _result_init(memcached_result_st *self, + memcached_st *memc) +{ + self->item_flags= 0; + self->item_expiration= 0; + self->key_length= 0; + self->item_cas= 0; + self->root= memc; + self->item_key[0]= 0; +} + +memcached_result_st *memcached_result_create(const memcached_st *memc, + memcached_result_st *ptr) +{ + WATCHPOINT_ASSERT(memc); + + /* Saving malloc calls :) */ + if (ptr) + { + ptr->options.is_allocated= false; + } + else + { + ptr= static_cast(libmemcached_malloc(memc, sizeof(memcached_result_st))); + + if (ptr == NULL) + return NULL; + + ptr->options.is_allocated= true; + } + + ptr->options.is_initialized= true; + + _result_init(ptr, (memcached_st *)memc); + + WATCHPOINT_SET(ptr->value.options.is_initialized= false); + memcached_string_create(memc, &ptr->value, 0); + WATCHPOINT_ASSERT_INITIALIZED(&ptr->value); + WATCHPOINT_ASSERT(ptr->value.string == NULL); + + return ptr; +} + +void memcached_result_reset(memcached_result_st *ptr) +{ + ptr->key_length= 0; + memcached_string_reset(&ptr->value); + ptr->item_flags= 0; + ptr->item_cas= 0; + ptr->item_expiration= 0; +} + +void memcached_result_free(memcached_result_st *ptr) +{ + if (ptr == NULL) + return; + + memcached_string_free(&ptr->value); + + if (memcached_is_allocated(ptr)) + { + WATCHPOINT_ASSERT(ptr->root); // Without a root, that means that result was not properly initialized. + libmemcached_free(ptr->root, ptr); + } + else + { + ptr->options.is_initialized= false; + } +} + +memcached_return_t memcached_result_set_value(memcached_result_st *ptr, + const char *value, + size_t length) +{ + memcached_return_t rc= memcached_string_append(&ptr->value, value, length); + + if (rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE) + { + memcached_set_errno(ptr->root, errno, NULL); + } + + return rc; +} + +const char *memcached_result_key_value(const memcached_result_st *self) +{ + return self->key_length ? self->item_key : NULL; +} + +size_t memcached_result_key_length(const memcached_result_st *self) +{ + return self->key_length; +} + +const char *memcached_result_value(const memcached_result_st *self) +{ + const memcached_string_st *sptr= &self->value; + return memcached_string_value(sptr); +} + +size_t memcached_result_length(const memcached_result_st *self) +{ + const memcached_string_st *sptr= &self->value; + return memcached_string_length(sptr); +} + +uint32_t memcached_result_flags(const memcached_result_st *self) +{ + return self->item_flags; +} + +uint64_t memcached_result_cas(const memcached_result_st *self) +{ + return self->item_cas; +} + +void memcached_result_set_flags(memcached_result_st *self, uint32_t flags) +{ + self->item_flags= flags; +} + +void memcached_result_set_expiration(memcached_result_st *self, time_t expiration) +{ + self->item_expiration= expiration; +} diff --git a/libmemcached/return.h b/libmemcached/return.h index c7aa6003..048c0670 100644 --- a/libmemcached/return.h +++ b/libmemcached/return.h @@ -90,4 +90,5 @@ enum memcached_return_t { typedef enum memcached_return_t memcached_return_t; #endif +#define memcached_failed(A) (A) != MEMCACHED_SUCCESS ? true : false diff --git a/libmemcached/server.c b/libmemcached/server.c deleted file mode 100644 index 4fe676b0..00000000 --- a/libmemcached/server.c +++ /dev/null @@ -1,348 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: String structure used for libmemcached. - * - */ - -/* - This is a partial implementation for fetching/creating memcached_server_st objects. -*/ -#include - -static inline void _server_init(memcached_server_st *self, const memcached_st *root, - const char *hostname, in_port_t port, - uint32_t weight, memcached_connection_t type) -{ - self->options.is_shutting_down= false; - self->options.is_dead= false; - self->number_of_hosts= 0; - self->cursor_active= 0; - self->port= port; - self->cached_errno= 0; - self->fd= -1; - self->io_bytes_sent= 0; - self->server_failure_counter= 0; - self->weight= weight ? weight : 1; // 1 is the default weight value - WATCHPOINT_SET(self->io_wait_count.read= 0); - WATCHPOINT_SET(self->io_wait_count.write= 0); - self->major_version= UINT8_MAX; - self->micro_version= UINT8_MAX; - self->minor_version= UINT8_MAX; - self->type= type; - self->read_ptr= self->read_buffer; - self->cached_server_error= NULL; - self->read_buffer_length= 0; - self->read_data_length= 0; - self->write_buffer_offset= 0; - self->address_info= NULL; - self->address_info_next= NULL; - - if (root) - { - self->next_retry= root->retry_timeout; - } - else - { - self->next_retry= 0; - } - - if (self->weight > 1 && root) - { - ((memcached_st *)root)->ketama.weighted= true; - } - - self->root= root; - self->limit_maxbytes= 0; - if (hostname == NULL) - { - self->hostname[0]= 0; - } - else - { - strncpy(self->hostname, hostname, NI_MAXHOST - 1); - } -} - -static memcached_server_st *_server_create(memcached_server_st *self, const memcached_st *memc) -{ - if (self == NULL) - { - self= (memcached_server_st *)libmemcached_malloc(memc, sizeof(memcached_server_st)); - - if (! self) - return NULL; /* MEMCACHED_MEMORY_ALLOCATION_FAILURE */ - - self->options.is_allocated= true; - } - else - { - self->options.is_allocated= false; - } - - self->options.is_initialized= true; - - return self; -} - -memcached_server_st *memcached_server_create_with(const memcached_st *memc, - memcached_server_write_instance_st self, - const char *hostname, in_port_t port, - uint32_t weight, memcached_connection_t type) -{ - self= _server_create(self, memc); - - if (self == NULL) - return NULL; - - _server_init(self, memc, hostname, port, weight, type); - - - if (type == MEMCACHED_CONNECTION_UDP) - { - self->write_buffer_offset= UDP_DATAGRAM_HEADER_LENGTH; - memcached_io_init_udp_header(self, 0); - } - - return self; -} - -void memcached_server_free(memcached_server_st *self) -{ - memcached_quit_server(self, false); - - if (self->cached_server_error) - free(self->cached_server_error); - - if (self->address_info) - freeaddrinfo(self->address_info); - - if (memcached_is_allocated(self)) - { - if (self->root) - { - libmemcached_free(self->root, self); - } - else - { - free(self); - } - } - else - { - self->options.is_initialized= false; - } -} - -/* - If we do not have a valid object to clone from, we toss an error. -*/ -memcached_server_st *memcached_server_clone(memcached_server_st *destination, - const memcached_server_st *source) -{ - /* We just do a normal create if source is missing */ - if (source == NULL) - return NULL; - - destination= memcached_server_create_with(source->root, destination, - source->hostname, source->port, source->weight, - source->type); - if (destination != NULL) - { - destination->cached_errno= source->cached_errno; - - if (source->cached_server_error) - destination->cached_server_error= strdup(source->cached_server_error); - } - - return destination; - -} - -memcached_return_t memcached_server_cursor(const memcached_st *ptr, - const memcached_server_fn *callback, - void *context, - uint32_t number_of_callbacks) -{ - memcached_return_t rc; - if ((rc= initialize_const_query(ptr)) != MEMCACHED_SUCCESS) - { - return rc; - } - - for (uint32_t x= 0; x < memcached_server_count(ptr); x++) - { - memcached_server_instance_st instance= - memcached_server_instance_by_position(ptr, x); - - for (uint32_t y= 0; y < number_of_callbacks; y++) - { - unsigned int iferror; - - iferror= (*callback[y])(ptr, instance, context); - - if (iferror) - continue; - } - } - - return MEMCACHED_SUCCESS; -} - -memcached_return_t memcached_server_execute(memcached_st *ptr, - memcached_server_execute_fn callback, - void *context) -{ - for (uint32_t x= 0; x < memcached_server_count(ptr); x++) - { - memcached_server_write_instance_st instance= - memcached_server_instance_fetch(ptr, x); - - unsigned int iferror; - - iferror= (*callback)(ptr, instance, context); - - if (iferror) - continue; - } - - return MEMCACHED_SUCCESS; -} - -memcached_server_instance_st memcached_server_by_key(const memcached_st *ptr, - const char *key, - size_t key_length, - memcached_return_t *error) -{ - uint32_t server_key; - memcached_server_instance_st instance; - - memcached_return_t rc; - if ((rc= initialize_const_query(ptr)) != MEMCACHED_SUCCESS) - { - if (error) - *error= rc; - - return NULL; - } - - if ((rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol)) != MEMCACHED_SUCCESS) - { - if (error) - *error= rc; - - return NULL; - } - - if (ptr->flags.verify_key && (memcached_key_test((const char **)&key, &key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED)) - { - *error= MEMCACHED_BAD_KEY_PROVIDED; - return NULL; - } - - server_key= memcached_generate_hash(ptr, key, key_length); - instance= memcached_server_instance_by_position(ptr, server_key); - - return instance; - -} - -void memcached_server_error_reset(memcached_server_st *self) -{ - WATCHPOINT_ASSERT(self); - if (! self) - return; - - self->cached_server_error[0]= 0; -} - -memcached_server_instance_st memcached_server_get_last_disconnect(const memcached_st *self) -{ - WATCHPOINT_ASSERT(self); - if (! self) - return 0; - - return self->last_disconnected_server; -} - -void memcached_server_list_free(memcached_server_list_st self) -{ - if (self == NULL) - return; - - for (uint32_t x= 0; x < memcached_server_list_count(self); x++) - { - if (self[x].address_info) - { - freeaddrinfo(self[x].address_info); - self[x].address_info= NULL; - } - } - - const memcached_st *root= self->root; - if (root) - { - libmemcached_free(root, self); - } - else - { - free(self); - } -} - -uint32_t memcached_servers_set_count(memcached_server_st *servers, uint32_t count) -{ - WATCHPOINT_ASSERT(servers); - if (! servers) - return 0; - - return servers->number_of_hosts= count; -} - -uint32_t memcached_server_count(const memcached_st *self) -{ - WATCHPOINT_ASSERT(self); - if (! self) - return 0; - - return self->number_of_hosts; -} - -const char *memcached_server_name(memcached_server_instance_st self) -{ - WATCHPOINT_ASSERT(self); - if (! self) - return NULL; - - return self->hostname; -} - -in_port_t memcached_server_port(memcached_server_instance_st self) -{ - WATCHPOINT_ASSERT(self); - if (! self) - return 0; - - return self->port; -} - -uint32_t memcached_server_response_count(memcached_server_instance_st self) -{ - WATCHPOINT_ASSERT(self); - if (! self) - return 0; - - return self->cursor_active; -} - -const char *memcached_server_error(memcached_server_instance_st ptr) -{ - return ptr - ? ptr->cached_server_error - : NULL; -} - diff --git a/libmemcached/server.cc b/libmemcached/server.cc new file mode 100644 index 00000000..1eb50ee4 --- /dev/null +++ b/libmemcached/server.cc @@ -0,0 +1,342 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: String structure used for libmemcached. + * + */ + +/* + This is a partial implementation for fetching/creating memcached_server_st objects. +*/ +#include + +static inline void _server_init(memcached_server_st *self, const memcached_st *root, + const char *hostname, in_port_t port, + uint32_t weight, memcached_connection_t type) +{ + self->options.is_shutting_down= false; + self->options.is_dead= false; + self->number_of_hosts= 0; + self->cursor_active= 0; + self->port= port; + self->cached_errno= 0; + self->fd= -1; + self->io_bytes_sent= 0; + self->server_failure_counter= 0; + self->weight= weight ? weight : 1; // 1 is the default weight value + WATCHPOINT_SET(self->io_wait_count.read= 0); + WATCHPOINT_SET(self->io_wait_count.write= 0); + self->major_version= UINT8_MAX; + self->micro_version= UINT8_MAX; + self->minor_version= UINT8_MAX; + self->type= type; + self->read_ptr= self->read_buffer; + self->cached_server_error= NULL; + self->read_buffer_length= 0; + self->read_data_length= 0; + self->write_buffer_offset= 0; + self->address_info= NULL; + self->address_info_next= NULL; + + if (root) + { + self->next_retry= root->retry_timeout; + } + else + { + self->next_retry= 0; + } + + if (self->weight > 1 && root) + { + ((memcached_st *)root)->ketama.weighted= true; + } + + self->root= root; + self->limit_maxbytes= 0; + if (hostname == NULL) + { + self->hostname[0]= 0; + } + else + { + strncpy(self->hostname, hostname, NI_MAXHOST - 1); + } +} + +static memcached_server_st *_server_create(memcached_server_st *self, const memcached_st *memc) +{ + if (self == NULL) + { + self= (memcached_server_st *)libmemcached_malloc(memc, sizeof(memcached_server_st)); + + if (! self) + return NULL; /* MEMCACHED_MEMORY_ALLOCATION_FAILURE */ + + self->options.is_allocated= true; + } + else + { + self->options.is_allocated= false; + } + + self->options.is_initialized= true; + + return self; +} + +memcached_server_st *memcached_server_create_with(const memcached_st *memc, + memcached_server_write_instance_st self, + const char *hostname, in_port_t port, + uint32_t weight, memcached_connection_t type) +{ + self= _server_create(self, memc); + + if (self == NULL) + return NULL; + + _server_init(self, memc, hostname, port, weight, type); + + + if (type == MEMCACHED_CONNECTION_UDP) + { + self->write_buffer_offset= UDP_DATAGRAM_HEADER_LENGTH; + memcached_io_init_udp_header(self, 0); + } + + return self; +} + +void memcached_server_free(memcached_server_st *self) +{ + memcached_quit_server(self, false); + + if (self->cached_server_error) + free(self->cached_server_error); + + if (self->address_info) + freeaddrinfo(self->address_info); + + if (memcached_is_allocated(self)) + { + if (self->root) + { + libmemcached_free(self->root, self); + } + else + { + free(self); + } + } + else + { + self->options.is_initialized= false; + } +} + +/* + If we do not have a valid object to clone from, we toss an error. +*/ +memcached_server_st *memcached_server_clone(memcached_server_st *destination, + const memcached_server_st *source) +{ + /* We just do a normal create if source is missing */ + if (source == NULL) + return NULL; + + destination= memcached_server_create_with(source->root, destination, + source->hostname, source->port, source->weight, + source->type); + if (destination != NULL) + { + destination->cached_errno= source->cached_errno; + + if (source->cached_server_error) + destination->cached_server_error= strdup(source->cached_server_error); + } + + return destination; + +} + +memcached_return_t memcached_server_cursor(const memcached_st *ptr, + const memcached_server_fn *callback, + void *context, + uint32_t number_of_callbacks) +{ + memcached_return_t rc; + if ((rc= initialize_const_query(ptr)) != MEMCACHED_SUCCESS) + { + return rc; + } + + for (uint32_t x= 0; x < memcached_server_count(ptr); x++) + { + memcached_server_instance_st instance= + memcached_server_instance_by_position(ptr, x); + + for (uint32_t y= 0; y < number_of_callbacks; y++) + { + unsigned int iferror; + + iferror= (*callback[y])(ptr, instance, context); + + if (iferror) + continue; + } + } + + return MEMCACHED_SUCCESS; +} + +memcached_return_t memcached_server_execute(memcached_st *ptr, + memcached_server_execute_fn callback, + void *context) +{ + for (uint32_t x= 0; x < memcached_server_count(ptr); x++) + { + memcached_server_write_instance_st instance= + memcached_server_instance_fetch(ptr, x); + + unsigned int iferror; + + iferror= (*callback)(ptr, instance, context); + + if (iferror) + continue; + } + + return MEMCACHED_SUCCESS; +} + +memcached_server_instance_st memcached_server_by_key(const memcached_st *ptr, + const char *key, + size_t key_length, + memcached_return_t *error) +{ + memcached_return_t rc; + if (memcached_failed(rc= initialize_const_query(ptr))) + { + if (error) + *error= rc; + + return NULL; + } + + if (memcached_failed(rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol))) + { + if (error) + *error= rc; + + return NULL; + } + + if (ptr->flags.verify_key && (memcached_key_test((const char **)&key, &key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED)) + { + if (error) + *error= MEMCACHED_BAD_KEY_PROVIDED; + return NULL; + } + + uint32_t server_key= memcached_generate_hash(ptr, key, key_length); + return memcached_server_instance_by_position(ptr, server_key); + +} + +void memcached_server_error_reset(memcached_server_st *self) +{ + WATCHPOINT_ASSERT(self); + if (not self) + return; + + self->cached_server_error[0]= 0; +} + +memcached_server_instance_st memcached_server_get_last_disconnect(const memcached_st *self) +{ + WATCHPOINT_ASSERT(self); + if (not self) + return 0; + + return self->last_disconnected_server; +} + +void memcached_server_list_free(memcached_server_list_st self) +{ + if (not self) + return; + + for (uint32_t x= 0; x < memcached_server_list_count(self); x++) + { + if (self[x].address_info) + { + freeaddrinfo(self[x].address_info); + self[x].address_info= NULL; + } + } + + const memcached_st *root= self->root; + if (root) + { + libmemcached_free(root, self); + } + else + { + free(self); + } +} + +uint32_t memcached_servers_set_count(memcached_server_st *servers, uint32_t count) +{ + WATCHPOINT_ASSERT(servers); + if (not servers) + return 0; + + return servers->number_of_hosts= count; +} + +uint32_t memcached_server_count(const memcached_st *self) +{ + WATCHPOINT_ASSERT(self); + if (not self) + return 0; + + return self->number_of_hosts; +} + +const char *memcached_server_name(memcached_server_instance_st self) +{ + WATCHPOINT_ASSERT(self); + if (not self) + return NULL; + + return self->hostname; +} + +in_port_t memcached_server_port(memcached_server_instance_st self) +{ + WATCHPOINT_ASSERT(self); + if (not self) + return 0; + + return self->port; +} + +uint32_t memcached_server_response_count(memcached_server_instance_st self) +{ + WATCHPOINT_ASSERT(self); + if (not self) + return 0; + + return self->cursor_active; +} + +const char *memcached_server_error(memcached_server_instance_st ptr) +{ + return ptr ? ptr->cached_server_error : NULL; +} + diff --git a/libmemcached/server_list.c b/libmemcached/server_list.c deleted file mode 100644 index ca37f7f9..00000000 --- a/libmemcached/server_list.c +++ /dev/null @@ -1,83 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2010 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: - * - */ - - -#include "common.h" - -memcached_server_list_st -memcached_server_list_append_with_weight(memcached_server_list_st ptr, - const char *hostname, in_port_t port, - uint32_t weight, - memcached_return_t *error) -{ - uint32_t count; - memcached_server_list_st new_host_list; - - if (hostname == NULL || error == NULL) - return NULL; - - if (hostname[0] == '/') - port = 0; - else if (! port) - port= MEMCACHED_DEFAULT_PORT; - - /* Increment count for hosts */ - count= 1; - if (ptr != NULL) - { - count+= memcached_server_list_count(ptr); - } - - new_host_list= (memcached_server_write_instance_st)realloc(ptr, sizeof(memcached_server_st) * count); - if (!new_host_list) - { - ptr->cached_errno= errno; - *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; - return NULL; - } - - /* @todo Check return type */ - memcached_server_create_with(NULL, &new_host_list[count-1], hostname, port, weight, port ? MEMCACHED_CONNECTION_TCP : MEMCACHED_CONNECTION_UNIX_SOCKET); - - // Handset allocated since - new_host_list->options.is_allocated= true; - - /* Backwards compatibility hack */ - memcached_servers_set_count(new_host_list, count); - - *error= MEMCACHED_SUCCESS; - return new_host_list; -} - -memcached_server_list_st -memcached_server_list_append(memcached_server_list_st ptr, - const char *hostname, in_port_t port, - memcached_return_t *error) -{ - return memcached_server_list_append_with_weight(ptr, hostname, port, 0, error); -} - -uint32_t memcached_server_list_count(const memcached_server_list_st self) -{ - return (self == NULL) - ? 0 - : self->number_of_hosts; -} - -memcached_server_st *memcached_server_list(const memcached_st *self) -{ - return self->servers; -} - -void memcached_server_list_set(memcached_st *self, memcached_server_st *list) -{ - self->servers= list; -} diff --git a/libmemcached/server_list.cc b/libmemcached/server_list.cc new file mode 100644 index 00000000..ca37f7f9 --- /dev/null +++ b/libmemcached/server_list.cc @@ -0,0 +1,83 @@ +/* LibMemcached + * Copyright (C) 2006-2010 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: + * + */ + + +#include "common.h" + +memcached_server_list_st +memcached_server_list_append_with_weight(memcached_server_list_st ptr, + const char *hostname, in_port_t port, + uint32_t weight, + memcached_return_t *error) +{ + uint32_t count; + memcached_server_list_st new_host_list; + + if (hostname == NULL || error == NULL) + return NULL; + + if (hostname[0] == '/') + port = 0; + else if (! port) + port= MEMCACHED_DEFAULT_PORT; + + /* Increment count for hosts */ + count= 1; + if (ptr != NULL) + { + count+= memcached_server_list_count(ptr); + } + + new_host_list= (memcached_server_write_instance_st)realloc(ptr, sizeof(memcached_server_st) * count); + if (!new_host_list) + { + ptr->cached_errno= errno; + *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; + return NULL; + } + + /* @todo Check return type */ + memcached_server_create_with(NULL, &new_host_list[count-1], hostname, port, weight, port ? MEMCACHED_CONNECTION_TCP : MEMCACHED_CONNECTION_UNIX_SOCKET); + + // Handset allocated since + new_host_list->options.is_allocated= true; + + /* Backwards compatibility hack */ + memcached_servers_set_count(new_host_list, count); + + *error= MEMCACHED_SUCCESS; + return new_host_list; +} + +memcached_server_list_st +memcached_server_list_append(memcached_server_list_st ptr, + const char *hostname, in_port_t port, + memcached_return_t *error) +{ + return memcached_server_list_append_with_weight(ptr, hostname, port, 0, error); +} + +uint32_t memcached_server_list_count(const memcached_server_list_st self) +{ + return (self == NULL) + ? 0 + : self->number_of_hosts; +} + +memcached_server_st *memcached_server_list(const memcached_st *self) +{ + return self->servers; +} + +void memcached_server_list_set(memcached_st *self, memcached_server_st *list) +{ + self->servers= list; +} diff --git a/libmemcached/stats.c b/libmemcached/stats.c deleted file mode 100644 index 45b530e4..00000000 --- a/libmemcached/stats.c +++ /dev/null @@ -1,582 +0,0 @@ -/* -*/ - -#include "common.h" - -static const char *memcached_stat_keys[] = { - "pid", - "uptime", - "time", - "version", - "pointer_size", - "rusage_user", - "rusage_system", - "curr_items", - "total_items", - "bytes", - "curr_connections", - "total_connections", - "connection_structures", - "cmd_get", - "cmd_set", - "get_hits", - "get_misses", - "evictions", - "bytes_read", - "bytes_written", - "limit_maxbytes", - "threads", - NULL -}; - -struct local_context -{ - memcached_stat_fn func; - void *context; - const char *args; -}; - - -static memcached_return_t set_data(memcached_stat_st *memc_stat, char *key, char *value) -{ - - if (strlen(key) < 1) - { - WATCHPOINT_STRING(key); - return MEMCACHED_UNKNOWN_STAT_KEY; - } - else if (!strcmp("pid", key)) - { - memc_stat->pid= (uint32_t) strtol(value, (char **)NULL, 10); - } - else if (!strcmp("uptime", key)) - { - memc_stat->uptime= (uint32_t) strtol(value, (char **)NULL, 10); - } - else if (!strcmp("time", key)) - { - memc_stat->time= (uint32_t) strtol(value, (char **)NULL, 10); - } - else if (!strcmp("version", key)) - { - memcpy(memc_stat->version, value, strlen(value)); - memc_stat->version[strlen(value)]= 0; - } - else if (!strcmp("pointer_size", key)) - { - memc_stat->pointer_size= (uint32_t) strtol(value, (char **)NULL, 10); - } - else if (!strcmp("rusage_user", key)) - { - char *walk_ptr; - for (walk_ptr= value; (!ispunct(*walk_ptr)); walk_ptr++); - *walk_ptr= 0; - walk_ptr++; - memc_stat->rusage_user_seconds= (uint32_t) strtol(value, (char **)NULL, 10); - memc_stat->rusage_user_microseconds= (uint32_t) strtol(walk_ptr, (char **)NULL, 10); - } - else if (!strcmp("rusage_system", key)) - { - char *walk_ptr; - for (walk_ptr= value; (!ispunct(*walk_ptr)); walk_ptr++); - *walk_ptr= 0; - walk_ptr++; - memc_stat->rusage_system_seconds= (uint32_t) strtol(value, (char **)NULL, 10); - memc_stat->rusage_system_microseconds= (uint32_t) strtol(walk_ptr, (char **)NULL, 10); - } - else if (!strcmp("curr_items", key)) - { - memc_stat->curr_items= (uint32_t) strtol(value, (char **)NULL, 10); - } - else if (!strcmp("total_items", key)) - { - memc_stat->total_items= (uint32_t) strtol(value, (char **)NULL, 10); - } - else if (!strcmp("bytes_read", key)) - { - memc_stat->bytes_read= (uint32_t) strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("bytes_written", key)) - { - memc_stat->bytes_written= (uint32_t) strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("bytes", key)) - { - memc_stat->bytes= (uint32_t) strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("curr_connections", key)) - { - memc_stat->curr_connections= (uint32_t) strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("total_connections", key)) - { - memc_stat->total_connections= (uint32_t) strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("connection_structures", key)) - { - memc_stat->connection_structures= (uint32_t) strtol(value, (char **)NULL, 10); - } - else if (!strcmp("cmd_get", key)) - { - memc_stat->cmd_get= (uint64_t) strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("cmd_set", key)) - { - memc_stat->cmd_set= (uint64_t) strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("get_hits", key)) - { - memc_stat->get_hits= (uint64_t) strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("get_misses", key)) - { - memc_stat->get_misses= (uint64_t)strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("evictions", key)) - { - memc_stat->evictions= (uint64_t)strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("limit_maxbytes", key)) - { - memc_stat->limit_maxbytes= (uint64_t) strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("threads", key)) - { - memc_stat->threads= (uint32_t) strtol(value, (char **)NULL, 10); - } - else if (!(strcmp("delete_misses", key) == 0 ||/* New stats in the 1.3 beta */ - strcmp("delete_hits", key) == 0 ||/* Just swallow them for now.. */ - strcmp("incr_misses", key) == 0 || - strcmp("incr_hits", key) == 0 || - strcmp("decr_misses", key) == 0 || - strcmp("decr_hits", key) == 0 || - strcmp("cas_misses", key) == 0 || - strcmp("cas_hits", key) == 0 || - strcmp("cas_badval", key) == 0 || - strcmp("cmd_flush", key) == 0 || - strcmp("accepting_conns", key) == 0 || - strcmp("listen_disabled_num", key) == 0 || - strcmp("conn_yields", key) == 0 || - strcmp("auth_cmds", key) == 0 || - strcmp("auth_errors", key) == 0 || - strcmp("reclaimed", key) == 0)) - { - WATCHPOINT_STRING(key); - /* return MEMCACHED_UNKNOWN_STAT_KEY; */ - return MEMCACHED_SUCCESS; - } - - return MEMCACHED_SUCCESS; -} - -char *memcached_stat_get_value(const memcached_st *ptr, memcached_stat_st *memc_stat, - const char *key, memcached_return_t *error) -{ - char buffer[SMALL_STRING_LEN]; - int length; - char *ret; - - *error= MEMCACHED_SUCCESS; - - if (!memcmp("pid", key, sizeof("pid") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->pid); - else if (!memcmp("uptime", key, sizeof("uptime") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->uptime); - else if (!memcmp("time", key, sizeof("time") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->time); - else if (!memcmp("version", key, sizeof("version") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%s", memc_stat->version); - else if (!memcmp("pointer_size", key, sizeof("pointer_size") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->pointer_size); - else if (!memcmp("rusage_user", key, sizeof("rusage_user") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%u.%u", memc_stat->rusage_user_seconds, memc_stat->rusage_user_microseconds); - else if (!memcmp("rusage_system", key, sizeof("rusage_system") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%u.%u", memc_stat->rusage_system_seconds, memc_stat->rusage_system_microseconds); - else if (!memcmp("curr_items", key, sizeof("curr_items") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->curr_items); - else if (!memcmp("total_items", key, sizeof("total_items") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->total_items); - else if (!memcmp("curr_connections", key, sizeof("curr_connections") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->curr_connections); - else if (!memcmp("total_connections", key, sizeof("total_connections") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->total_connections); - else if (!memcmp("connection_structures", key, sizeof("connection_structures") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->connection_structures); - else if (!memcmp("cmd_get", key, sizeof("cmd_get") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->cmd_get); - else if (!memcmp("cmd_set", key, sizeof("cmd_set") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->cmd_set); - else if (!memcmp("get_hits", key, sizeof("get_hits") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->get_hits); - else if (!memcmp("get_misses", key, sizeof("get_misses") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->get_misses); - else if (!memcmp("evictions", key, sizeof("evictions") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->evictions); - else if (!memcmp("bytes_read", key, sizeof("bytes_read") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_read); - else if (!memcmp("bytes_written", key, sizeof("bytes_written") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_written); - else if (!memcmp("bytes", key, sizeof("bytes") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes); - else if (!memcmp("limit_maxbytes", key, sizeof("limit_maxbytes") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->limit_maxbytes); - else if (! memcmp("threads", key, sizeof("threads") -1)) - length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->threads); - else - { - *error= MEMCACHED_NOTFOUND; - return NULL; - } - - if (length >= SMALL_STRING_LEN || length < 0) - { - *error= MEMCACHED_FAILURE; - return NULL; - } - - ret= libmemcached_malloc(ptr, (size_t) (length + 1)); - memcpy(ret, buffer, (size_t) length); - ret[length]= '\0'; - - return ret; -} - -static memcached_return_t binary_stats_fetch(memcached_stat_st *memc_stat, - const char *args, - memcached_server_write_instance_st instance, - struct local_context *check) -{ - memcached_return_t rc; - - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - protocol_binary_request_stats request= {.bytes= {0}}; - request.message.header.request.magic= PROTOCOL_BINARY_REQ; - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_STAT; - request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; - - if (args != NULL) - { - size_t len= strlen(args); - - rc= memcached_validate_key_length(len, true); - unlikely (rc != MEMCACHED_SUCCESS) - return rc; - - request.message.header.request.keylen= htons((uint16_t)len); - request.message.header.request.bodylen= htonl((uint32_t) len); - - struct libmemcached_io_vector_st vector[]= - { - { .length= sizeof(request.bytes), .buffer= request.bytes }, - { .length= len, .buffer= args } - }; - - if (memcached_vdo(instance, vector, 2, true) != MEMCACHED_SUCCESS) - { - memcached_io_reset(instance); - return MEMCACHED_WRITE_FAILURE; - } - } - else - { - if (memcached_do(instance, request.bytes, - sizeof(request.bytes), true) != MEMCACHED_SUCCESS) - { - memcached_io_reset(instance); - return MEMCACHED_WRITE_FAILURE; - } - } - - memcached_server_response_decrement(instance); - do - { - rc= memcached_response(instance, buffer, sizeof(buffer), NULL); - - if (rc == MEMCACHED_END) - break; - - unlikely (rc != MEMCACHED_SUCCESS) - { - memcached_io_reset(instance); - return rc; - } - - if (memc_stat) - { - unlikely((set_data(memc_stat, buffer, buffer + strlen(buffer) + 1)) == MEMCACHED_UNKNOWN_STAT_KEY) - { - WATCHPOINT_ERROR(MEMCACHED_UNKNOWN_STAT_KEY); - WATCHPOINT_ASSERT(0); - } - } - - if (check && check->func) - { - size_t key_length= strlen(buffer); - - check->func(instance, - buffer, key_length, - buffer+key_length+1, strlen(buffer+key_length+1), - check->context); - } - } while (1); - - /* shit... memcached_response will decrement the counter, so I need to - ** reset it.. todo: look at this and try to find a better solution. - */ - instance->cursor_active= 0; - - return MEMCACHED_SUCCESS; -} - -static memcached_return_t ascii_stats_fetch(memcached_stat_st *memc_stat, - const char *args, - memcached_server_write_instance_st instance, - struct local_context *check) -{ - memcached_return_t rc; - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - int send_length; - - if (args) - send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "stats %s\r\n", args); - else - send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "stats\r\n"); - - if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE || send_length < 0) - return MEMCACHED_WRITE_FAILURE; - - rc= memcached_do(instance, buffer, (size_t)send_length, true); - if (rc != MEMCACHED_SUCCESS) - goto error; - - while (1) - { - rc= memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); - - if (rc == MEMCACHED_STAT) - { - char *string_ptr, *end_ptr; - char *key, *value; - - string_ptr= buffer; - string_ptr+= 5; /* Move past STAT */ - for (end_ptr= string_ptr; isgraph(*end_ptr); end_ptr++); - key= string_ptr; - key[(size_t)(end_ptr-string_ptr)]= 0; - - string_ptr= end_ptr + 1; - for (end_ptr= string_ptr; !(isspace(*end_ptr)); end_ptr++); - value= string_ptr; - value[(size_t)(end_ptr-string_ptr)]= 0; - string_ptr= end_ptr + 2; - if (memc_stat) - { - unlikely((set_data(memc_stat, key, value)) == MEMCACHED_UNKNOWN_STAT_KEY) - { - WATCHPOINT_ERROR(MEMCACHED_UNKNOWN_STAT_KEY); - WATCHPOINT_ASSERT(0); - } - } - - if (check && check->func) - { - check->func(instance, - key, strlen(key), - value, strlen(value), - check->context); - } - } - else - break; - } - -error: - if (rc == MEMCACHED_END) - return MEMCACHED_SUCCESS; - else - return rc; -} - -memcached_stat_st *memcached_stat(memcached_st *self, char *args, memcached_return_t *error) -{ - memcached_return_t rc; - memcached_stat_st *stats; - - if ((rc= initialize_query(self)) != MEMCACHED_SUCCESS) - { - if (error) - *error= rc; - - return NULL; - } - - WATCHPOINT_ASSERT(error); - - unlikely (self->flags.use_udp) - { - if (error) - *error= MEMCACHED_NOT_SUPPORTED; - - return NULL; - } - - stats= libmemcached_calloc(self, memcached_server_count(self), sizeof(memcached_stat_st)); - - if (! stats) - { - if (error) - *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; - - return NULL; - } - - WATCHPOINT_ASSERT(rc == MEMCACHED_SUCCESS); - rc= MEMCACHED_SUCCESS; - for (uint32_t x= 0; x < memcached_server_count(self); x++) - { - memcached_return_t temp_return; - memcached_server_write_instance_st instance; - memcached_stat_st *stat_instance; - - stat_instance= stats + x; - - stat_instance->root= self; - - instance= memcached_server_instance_fetch(self, x); - - if (self->flags.binary_protocol) - { - temp_return= binary_stats_fetch(stat_instance, args, instance, NULL); - } - else - { - temp_return= ascii_stats_fetch(stat_instance, args, instance, NULL); - } - - if (temp_return != MEMCACHED_SUCCESS) - rc= MEMCACHED_SOME_ERRORS; - } - - if (error) - *error= rc; - - return stats; -} - -memcached_return_t memcached_stat_servername(memcached_stat_st *memc_stat, char *args, - const char *hostname, in_port_t port) -{ - memcached_st memc; - memcached_st *memc_ptr; - memcached_server_write_instance_st instance; - - memset(memc_stat, 0, sizeof(memcached_stat_st)); - - memc_ptr= memcached_create(&memc); - if (! memc_ptr) - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - - memcached_server_add(&memc, hostname, port); - - memcached_return_t rc; - if ((rc= initialize_query(memc_ptr)) != MEMCACHED_SUCCESS) - { - return rc; - } - - instance= memcached_server_instance_fetch(memc_ptr, 0); - - if (memc.flags.binary_protocol) - { - rc= binary_stats_fetch(memc_stat, args, instance, NULL); - } - else - { - rc= ascii_stats_fetch(memc_stat, args, instance, NULL); - } - - memcached_free(&memc); - - return rc; -} - -/* - We make a copy of the keys since at some point in the not so distant future - we will add support for "found" keys. -*/ -char ** memcached_stat_get_keys(const memcached_st *ptr, - memcached_stat_st *memc_stat, - memcached_return_t *error) -{ - char **list; - size_t length= sizeof(memcached_stat_keys); - - (void)memc_stat; - - list= libmemcached_malloc(ptr, length); - - if (! list) - { - *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; - return NULL; - } - - memcpy(list, memcached_stat_keys, sizeof(memcached_stat_keys)); - - *error= MEMCACHED_SUCCESS; - - return list; -} - -void memcached_stat_free(const memcached_st *ptr, memcached_stat_st *memc_stat) -{ - if (memc_stat == NULL) - { - WATCHPOINT_ASSERT(0); /* Be polite, but when debugging catch this as an error */ - return; - } - - if (memc_stat->root) - { - libmemcached_free(memc_stat->root, memc_stat); - } - else if (ptr) - { - libmemcached_free(ptr, memc_stat); - } - else - { - free(memc_stat); - } -} - -static memcached_return_t call_stat_fn(memcached_st *ptr, - memcached_server_write_instance_st instance, - void *context) -{ - memcached_return_t rc; - struct local_context *check= (struct local_context *)context; - - if (ptr->flags.binary_protocol) - { - rc= binary_stats_fetch(NULL, check->args, instance, check); - } - else - { - rc= ascii_stats_fetch(NULL, check->args, instance, check); - } - - return rc; -} - -memcached_return_t memcached_stat_execute(memcached_st *memc, const char *args, memcached_stat_fn func, void *context) -{ - memcached_version(memc); - - struct local_context check= { .func= func, .context= context, .args= args }; - - return memcached_server_execute(memc, call_stat_fn, (void *)&check); -} diff --git a/libmemcached/stats.cc b/libmemcached/stats.cc new file mode 100644 index 00000000..08934ba6 --- /dev/null +++ b/libmemcached/stats.cc @@ -0,0 +1,590 @@ +/* +*/ + +#include "common.h" + +static const char *memcached_stat_keys[] = { + "pid", + "uptime", + "time", + "version", + "pointer_size", + "rusage_user", + "rusage_system", + "curr_items", + "total_items", + "bytes", + "curr_connections", + "total_connections", + "connection_structures", + "cmd_get", + "cmd_set", + "get_hits", + "get_misses", + "evictions", + "bytes_read", + "bytes_written", + "limit_maxbytes", + "threads", + NULL +}; + +struct local_context +{ + memcached_stat_fn func; + void *context; + const char *args; + + local_context(memcached_stat_fn func_arg, + void *context_arg, + const char *args_arg) : + func(func_arg), + context(context_arg), + args(args_arg) + { } +}; + + +static memcached_return_t set_data(memcached_stat_st *memc_stat, char *key, char *value) +{ + + if (strlen(key) < 1) + { + WATCHPOINT_STRING(key); + return MEMCACHED_UNKNOWN_STAT_KEY; + } + else if (!strcmp("pid", key)) + { + memc_stat->pid= (uint32_t) strtol(value, (char **)NULL, 10); + } + else if (!strcmp("uptime", key)) + { + memc_stat->uptime= (uint32_t) strtol(value, (char **)NULL, 10); + } + else if (!strcmp("time", key)) + { + memc_stat->time= (uint32_t) strtol(value, (char **)NULL, 10); + } + else if (!strcmp("version", key)) + { + memcpy(memc_stat->version, value, strlen(value)); + memc_stat->version[strlen(value)]= 0; + } + else if (!strcmp("pointer_size", key)) + { + memc_stat->pointer_size= (uint32_t) strtol(value, (char **)NULL, 10); + } + else if (!strcmp("rusage_user", key)) + { + char *walk_ptr; + for (walk_ptr= value; (!ispunct(*walk_ptr)); walk_ptr++); + *walk_ptr= 0; + walk_ptr++; + memc_stat->rusage_user_seconds= (uint32_t) strtol(value, (char **)NULL, 10); + memc_stat->rusage_user_microseconds= (uint32_t) strtol(walk_ptr, (char **)NULL, 10); + } + else if (!strcmp("rusage_system", key)) + { + char *walk_ptr; + for (walk_ptr= value; (!ispunct(*walk_ptr)); walk_ptr++); + *walk_ptr= 0; + walk_ptr++; + memc_stat->rusage_system_seconds= (uint32_t) strtol(value, (char **)NULL, 10); + memc_stat->rusage_system_microseconds= (uint32_t) strtol(walk_ptr, (char **)NULL, 10); + } + else if (!strcmp("curr_items", key)) + { + memc_stat->curr_items= (uint32_t) strtol(value, (char **)NULL, 10); + } + else if (!strcmp("total_items", key)) + { + memc_stat->total_items= (uint32_t) strtol(value, (char **)NULL, 10); + } + else if (!strcmp("bytes_read", key)) + { + memc_stat->bytes_read= (uint32_t) strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("bytes_written", key)) + { + memc_stat->bytes_written= (uint32_t) strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("bytes", key)) + { + memc_stat->bytes= (uint32_t) strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("curr_connections", key)) + { + memc_stat->curr_connections= (uint32_t) strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("total_connections", key)) + { + memc_stat->total_connections= (uint32_t) strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("connection_structures", key)) + { + memc_stat->connection_structures= (uint32_t) strtol(value, (char **)NULL, 10); + } + else if (!strcmp("cmd_get", key)) + { + memc_stat->cmd_get= (uint64_t) strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("cmd_set", key)) + { + memc_stat->cmd_set= (uint64_t) strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("get_hits", key)) + { + memc_stat->get_hits= (uint64_t) strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("get_misses", key)) + { + memc_stat->get_misses= (uint64_t)strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("evictions", key)) + { + memc_stat->evictions= (uint64_t)strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("limit_maxbytes", key)) + { + memc_stat->limit_maxbytes= (uint64_t) strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("threads", key)) + { + memc_stat->threads= (uint32_t) strtol(value, (char **)NULL, 10); + } + else if (!(strcmp("delete_misses", key) == 0 ||/* New stats in the 1.3 beta */ + strcmp("delete_hits", key) == 0 ||/* Just swallow them for now.. */ + strcmp("incr_misses", key) == 0 || + strcmp("incr_hits", key) == 0 || + strcmp("decr_misses", key) == 0 || + strcmp("decr_hits", key) == 0 || + strcmp("cas_misses", key) == 0 || + strcmp("cas_hits", key) == 0 || + strcmp("cas_badval", key) == 0 || + strcmp("cmd_flush", key) == 0 || + strcmp("accepting_conns", key) == 0 || + strcmp("listen_disabled_num", key) == 0 || + strcmp("conn_yields", key) == 0 || + strcmp("auth_cmds", key) == 0 || + strcmp("auth_errors", key) == 0 || + strcmp("reclaimed", key) == 0)) + { + WATCHPOINT_STRING(key); + /* return MEMCACHED_UNKNOWN_STAT_KEY; */ + return MEMCACHED_SUCCESS; + } + + return MEMCACHED_SUCCESS; +} + +char *memcached_stat_get_value(const memcached_st *ptr, memcached_stat_st *memc_stat, + const char *key, memcached_return_t *error) +{ + char buffer[SMALL_STRING_LEN]; + int length; + char *ret; + + *error= MEMCACHED_SUCCESS; + + if (!memcmp("pid", key, sizeof("pid") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->pid); + else if (!memcmp("uptime", key, sizeof("uptime") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->uptime); + else if (!memcmp("time", key, sizeof("time") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->time); + else if (!memcmp("version", key, sizeof("version") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%s", memc_stat->version); + else if (!memcmp("pointer_size", key, sizeof("pointer_size") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->pointer_size); + else if (!memcmp("rusage_user", key, sizeof("rusage_user") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%u.%u", memc_stat->rusage_user_seconds, memc_stat->rusage_user_microseconds); + else if (!memcmp("rusage_system", key, sizeof("rusage_system") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%u.%u", memc_stat->rusage_system_seconds, memc_stat->rusage_system_microseconds); + else if (!memcmp("curr_items", key, sizeof("curr_items") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->curr_items); + else if (!memcmp("total_items", key, sizeof("total_items") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->total_items); + else if (!memcmp("curr_connections", key, sizeof("curr_connections") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->curr_connections); + else if (!memcmp("total_connections", key, sizeof("total_connections") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->total_connections); + else if (!memcmp("connection_structures", key, sizeof("connection_structures") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->connection_structures); + else if (!memcmp("cmd_get", key, sizeof("cmd_get") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->cmd_get); + else if (!memcmp("cmd_set", key, sizeof("cmd_set") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->cmd_set); + else if (!memcmp("get_hits", key, sizeof("get_hits") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->get_hits); + else if (!memcmp("get_misses", key, sizeof("get_misses") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->get_misses); + else if (!memcmp("evictions", key, sizeof("evictions") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->evictions); + else if (!memcmp("bytes_read", key, sizeof("bytes_read") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_read); + else if (!memcmp("bytes_written", key, sizeof("bytes_written") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_written); + else if (!memcmp("bytes", key, sizeof("bytes") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes); + else if (!memcmp("limit_maxbytes", key, sizeof("limit_maxbytes") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->limit_maxbytes); + else if (! memcmp("threads", key, sizeof("threads") -1)) + length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->threads); + else + { + *error= MEMCACHED_NOTFOUND; + return NULL; + } + + if (length >= SMALL_STRING_LEN || length < 0) + { + *error= MEMCACHED_FAILURE; + return NULL; + } + + ret= static_cast(libmemcached_malloc(ptr, (size_t) (length + 1))); + memcpy(ret, buffer, (size_t) length); + ret[length]= '\0'; + + return ret; +} + +static memcached_return_t binary_stats_fetch(memcached_stat_st *memc_stat, + const char *args, + memcached_server_write_instance_st instance, + struct local_context *check) +{ + memcached_return_t rc; + + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + protocol_binary_request_stats request= {}; // = {.bytes= {0}}; + request.message.header.request.magic= PROTOCOL_BINARY_REQ; + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_STAT; + request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; + + if (args != NULL) + { + size_t len= strlen(args); + + rc= memcached_validate_key_length(len, true); + unlikely (rc != MEMCACHED_SUCCESS) + return rc; + + request.message.header.request.keylen= htons((uint16_t)len); + request.message.header.request.bodylen= htonl((uint32_t) len); + + struct libmemcached_io_vector_st vector[]= + { + { sizeof(request.bytes), request.bytes }, + { len, args } + }; + + if (memcached_vdo(instance, vector, 2, true) != MEMCACHED_SUCCESS) + { + memcached_io_reset(instance); + return MEMCACHED_WRITE_FAILURE; + } + } + else + { + if (memcached_do(instance, request.bytes, + sizeof(request.bytes), true) != MEMCACHED_SUCCESS) + { + memcached_io_reset(instance); + return MEMCACHED_WRITE_FAILURE; + } + } + + memcached_server_response_decrement(instance); + do + { + rc= memcached_response(instance, buffer, sizeof(buffer), NULL); + + if (rc == MEMCACHED_END) + break; + + unlikely (rc != MEMCACHED_SUCCESS) + { + memcached_io_reset(instance); + return rc; + } + + if (memc_stat) + { + unlikely((set_data(memc_stat, buffer, buffer + strlen(buffer) + 1)) == MEMCACHED_UNKNOWN_STAT_KEY) + { + WATCHPOINT_ERROR(MEMCACHED_UNKNOWN_STAT_KEY); + WATCHPOINT_ASSERT(0); + } + } + + if (check && check->func) + { + size_t key_length= strlen(buffer); + + check->func(instance, + buffer, key_length, + buffer+key_length+1, strlen(buffer+key_length+1), + check->context); + } + } while (1); + + /* shit... memcached_response will decrement the counter, so I need to + ** reset it.. todo: look at this and try to find a better solution. + */ + instance->cursor_active= 0; + + return MEMCACHED_SUCCESS; +} + +static memcached_return_t ascii_stats_fetch(memcached_stat_st *memc_stat, + const char *args, + memcached_server_write_instance_st instance, + struct local_context *check) +{ + memcached_return_t rc; + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + int send_length; + + if (args) + send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "stats %s\r\n", args); + else + send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "stats\r\n"); + + if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE || send_length < 0) + return MEMCACHED_WRITE_FAILURE; + + rc= memcached_do(instance, buffer, (size_t)send_length, true); + if (rc != MEMCACHED_SUCCESS) + goto error; + + while (1) + { + rc= memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); + + if (rc == MEMCACHED_STAT) + { + char *string_ptr, *end_ptr; + char *key, *value; + + string_ptr= buffer; + string_ptr+= 5; /* Move past STAT */ + for (end_ptr= string_ptr; isgraph(*end_ptr); end_ptr++); + key= string_ptr; + key[(size_t)(end_ptr-string_ptr)]= 0; + + string_ptr= end_ptr + 1; + for (end_ptr= string_ptr; !(isspace(*end_ptr)); end_ptr++); + value= string_ptr; + value[(size_t)(end_ptr-string_ptr)]= 0; + string_ptr= end_ptr + 2; + if (memc_stat) + { + unlikely((set_data(memc_stat, key, value)) == MEMCACHED_UNKNOWN_STAT_KEY) + { + WATCHPOINT_ERROR(MEMCACHED_UNKNOWN_STAT_KEY); + WATCHPOINT_ASSERT(0); + } + } + + if (check && check->func) + { + check->func(instance, + key, strlen(key), + value, strlen(value), + check->context); + } + } + else + break; + } + +error: + if (rc == MEMCACHED_END) + return MEMCACHED_SUCCESS; + else + return rc; +} + +memcached_stat_st *memcached_stat(memcached_st *self, char *args, memcached_return_t *error) +{ + memcached_return_t rc; + memcached_stat_st *stats; + + if ((rc= initialize_query(self)) != MEMCACHED_SUCCESS) + { + if (error) + *error= rc; + + return NULL; + } + + WATCHPOINT_ASSERT(error); + + unlikely (self->flags.use_udp) + { + if (error) + *error= MEMCACHED_NOT_SUPPORTED; + + return NULL; + } + + stats= static_cast(libmemcached_calloc(self, memcached_server_count(self), sizeof(memcached_stat_st))); + + if (! stats) + { + if (error) + *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; + + return NULL; + } + + WATCHPOINT_ASSERT(rc == MEMCACHED_SUCCESS); + rc= MEMCACHED_SUCCESS; + for (uint32_t x= 0; x < memcached_server_count(self); x++) + { + memcached_return_t temp_return; + memcached_server_write_instance_st instance; + memcached_stat_st *stat_instance; + + stat_instance= stats + x; + + stat_instance->root= self; + + instance= memcached_server_instance_fetch(self, x); + + if (self->flags.binary_protocol) + { + temp_return= binary_stats_fetch(stat_instance, args, instance, NULL); + } + else + { + temp_return= ascii_stats_fetch(stat_instance, args, instance, NULL); + } + + if (temp_return != MEMCACHED_SUCCESS) + rc= MEMCACHED_SOME_ERRORS; + } + + if (error) + *error= rc; + + return stats; +} + +memcached_return_t memcached_stat_servername(memcached_stat_st *memc_stat, char *args, + const char *hostname, in_port_t port) +{ + memcached_st memc; + memcached_st *memc_ptr; + memcached_server_write_instance_st instance; + + memset(memc_stat, 0, sizeof(memcached_stat_st)); + + memc_ptr= memcached_create(&memc); + if (! memc_ptr) + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + + memcached_server_add(&memc, hostname, port); + + memcached_return_t rc; + if ((rc= initialize_query(memc_ptr)) != MEMCACHED_SUCCESS) + { + return rc; + } + + instance= memcached_server_instance_fetch(memc_ptr, 0); + + if (memc.flags.binary_protocol) + { + rc= binary_stats_fetch(memc_stat, args, instance, NULL); + } + else + { + rc= ascii_stats_fetch(memc_stat, args, instance, NULL); + } + + memcached_free(&memc); + + return rc; +} + +/* + We make a copy of the keys since at some point in the not so distant future + we will add support for "found" keys. +*/ +char ** memcached_stat_get_keys(const memcached_st *ptr, + memcached_stat_st *memc_stat, + memcached_return_t *error) +{ + char **list; + size_t length= sizeof(memcached_stat_keys); + + (void)memc_stat; + + list= static_cast(libmemcached_malloc(ptr, length)); + + if (not list) + { + *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; + return NULL; + } + + memcpy(list, memcached_stat_keys, sizeof(memcached_stat_keys)); + + *error= MEMCACHED_SUCCESS; + + return list; +} + +void memcached_stat_free(const memcached_st *ptr, memcached_stat_st *memc_stat) +{ + if (memc_stat == NULL) + { + WATCHPOINT_ASSERT(0); /* Be polite, but when debugging catch this as an error */ + return; + } + + if (memc_stat->root) + { + libmemcached_free(memc_stat->root, memc_stat); + } + else if (ptr) + { + libmemcached_free(ptr, memc_stat); + } + else + { + free(memc_stat); + } +} + +static memcached_return_t call_stat_fn(memcached_st *ptr, + memcached_server_write_instance_st instance, + void *context) +{ + memcached_return_t rc; + struct local_context *check= (struct local_context *)context; + + if (ptr->flags.binary_protocol) + { + rc= binary_stats_fetch(NULL, check->args, instance, check); + } + else + { + rc= ascii_stats_fetch(NULL, check->args, instance, check); + } + + return rc; +} + +memcached_return_t memcached_stat_execute(memcached_st *memc, const char *args, memcached_stat_fn func, void *context) +{ + memcached_version(memc); + + struct local_context check(func, context, args); + + return memcached_server_execute(memc, call_stat_fn, (void *)&check); +} diff --git a/libmemcached/storage.c b/libmemcached/storage.c deleted file mode 100644 index 135def30..00000000 --- a/libmemcached/storage.c +++ /dev/null @@ -1,570 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Storage related functions, aka set, replace,.. - * - */ - -#include "common.h" - -typedef enum { - SET_OP, - REPLACE_OP, - ADD_OP, - PREPEND_OP, - APPEND_OP, - CAS_OP, -} memcached_storage_action_t; - -/* Inline this */ -static inline const char *storage_op_string(memcached_storage_action_t verb) -{ - switch (verb) - { - case SET_OP: - return "set "; - case REPLACE_OP: - return "replace "; - case ADD_OP: - return "add "; - case PREPEND_OP: - return "prepend "; - case APPEND_OP: - return "append "; - case CAS_OP: - return "cas "; - default: - return "tosserror"; /* This is impossible, fixes issue for compiler warning in VisualStudio */ - } - - /* NOTREACHED */ -} - -static memcached_return_t memcached_send_binary(memcached_st *ptr, - memcached_server_write_instance_st server, - uint32_t server_key, - const char *key, - size_t key_length, - const char *value, - size_t value_length, - time_t expiration, - uint32_t flags, - uint64_t cas, - memcached_storage_action_t verb); - -static inline memcached_return_t memcached_send(memcached_st *ptr, - const char *group_key, size_t group_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags, - uint64_t cas, - memcached_storage_action_t verb) -{ - bool to_write; - size_t write_length; - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - uint32_t server_key; - memcached_server_write_instance_st instance; - - WATCHPOINT_ASSERT(!(value == NULL && value_length > 0)); - - memcached_return_t rc; - if ((rc= initialize_query(ptr)) != MEMCACHED_SUCCESS) - { - return rc; - } - - rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol); - unlikely (rc != MEMCACHED_SUCCESS) - return rc; - - if (ptr->flags.verify_key && (memcached_key_test((const char **)&key, &key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED)) - return MEMCACHED_BAD_KEY_PROVIDED; - - server_key= memcached_generate_hash_with_redistribution(ptr, group_key, group_key_length); - instance= memcached_server_instance_fetch(ptr, server_key); - - WATCHPOINT_SET(instance->io_wait_count.read= 0); - WATCHPOINT_SET(instance->io_wait_count.write= 0); - - if (ptr->flags.binary_protocol) - { - rc= memcached_send_binary(ptr, instance, server_key, - key, key_length, - value, value_length, expiration, - flags, cas, verb); - WATCHPOINT_IF_LABELED_NUMBER(instance->io_wait_count.read > 2, "read IO_WAIT", instance->io_wait_count.read); - WATCHPOINT_IF_LABELED_NUMBER(instance->io_wait_count.write > 2, "write_IO_WAIT", instance->io_wait_count.write); - } - else - { - - if (cas) - { - int check_length; - check_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "%s %.*s%.*s %u %llu %lu %llu%s\r\n", - storage_op_string(verb), - memcached_print_array(ptr->prefix_key), - (int)key_length, key, flags, - (unsigned long long)expiration, (unsigned long)value_length, - (unsigned long long)cas, - (ptr->flags.no_reply) ? " noreply" : ""); - if (check_length >= MEMCACHED_DEFAULT_COMMAND_SIZE || check_length < 0) - { - rc= MEMCACHED_WRITE_FAILURE; - memcached_io_reset(instance); - - return rc; - } - write_length= check_length; - } - else - { - char *buffer_ptr= buffer; - const char *command= storage_op_string(verb); - - /* Copy in the command, no space needed, we handle that in the command function*/ - memcpy(buffer_ptr, command, strlen(command)); - - /* Copy in the key prefix, switch to the buffer_ptr */ - buffer_ptr= memcpy((buffer_ptr + strlen(command)), memcached_array_string(ptr->prefix_key), memcached_array_size(ptr->prefix_key)); - - /* Copy in the key, adjust point if a key prefix was used. */ - buffer_ptr= memcpy(buffer_ptr + memcached_array_size(ptr->prefix_key), - key, key_length); - buffer_ptr+= key_length; - buffer_ptr[0]= ' '; - buffer_ptr++; - - write_length= (size_t)(buffer_ptr - buffer); - int check_length; - check_length= snprintf(buffer_ptr, MEMCACHED_DEFAULT_COMMAND_SIZE -(size_t)(buffer_ptr - buffer), - "%u %llu %lu%s\r\n", - flags, - (unsigned long long)expiration, (unsigned long)value_length, - ptr->flags.no_reply ? " noreply" : ""); - if ((size_t)check_length >= MEMCACHED_DEFAULT_COMMAND_SIZE -(size_t)(buffer_ptr - buffer) || check_length < 0) - { - rc= MEMCACHED_WRITE_FAILURE; - memcached_io_reset(instance); - - return rc; - } - - write_length+= (size_t)check_length; - WATCHPOINT_ASSERT(write_length < MEMCACHED_DEFAULT_COMMAND_SIZE); - } - - if (ptr->flags.use_udp && ptr->flags.buffer_requests) - { - size_t cmd_size= write_length + value_length + 2; - if (cmd_size > MAX_UDP_DATAGRAM_LENGTH - UDP_DATAGRAM_HEADER_LENGTH) - return MEMCACHED_WRITE_FAILURE; - if (cmd_size + instance->write_buffer_offset > MAX_UDP_DATAGRAM_LENGTH) - memcached_io_write(instance, NULL, 0, true); - } - - if (write_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) - { - rc= MEMCACHED_WRITE_FAILURE; - } - else - { - struct libmemcached_io_vector_st vector[]= - { - { .length= write_length, .buffer= buffer }, - { .length= value_length, .buffer= value }, - { .length= 2, .buffer= "\r\n" } - }; - - if (ptr->flags.buffer_requests && verb == SET_OP) - { - to_write= false; - } - else - { - to_write= true; - } - - /* Send command header */ - rc= memcached_vdo(instance, vector, 3, to_write); - if (rc == MEMCACHED_SUCCESS) - { - - if (ptr->flags.no_reply) - { - rc= (to_write == false) ? MEMCACHED_BUFFERED : MEMCACHED_SUCCESS; - } - else if (to_write == false) - { - rc= MEMCACHED_BUFFERED; - } - else - { - rc= memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); - - if (rc == MEMCACHED_STORED) - rc= MEMCACHED_SUCCESS; - } - } - } - - if (rc == MEMCACHED_WRITE_FAILURE) - memcached_io_reset(instance); - } - - WATCHPOINT_IF_LABELED_NUMBER(instance->io_wait_count.read > 2, "read IO_WAIT", instance->io_wait_count.read); - WATCHPOINT_IF_LABELED_NUMBER(instance->io_wait_count.write > 2, "write_IO_WAIT", instance->io_wait_count.write); - - return rc; -} - - -memcached_return_t memcached_set(memcached_st *ptr, const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - LIBMEMCACHED_MEMCACHED_SET_START(); - rc= memcached_send(ptr, key, key_length, - key, key_length, value, value_length, - expiration, flags, 0, SET_OP); - LIBMEMCACHED_MEMCACHED_SET_END(); - return rc; -} - -memcached_return_t memcached_add(memcached_st *ptr, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - LIBMEMCACHED_MEMCACHED_ADD_START(); - rc= memcached_send(ptr, key, key_length, - key, key_length, value, value_length, - expiration, flags, 0, ADD_OP); - LIBMEMCACHED_MEMCACHED_ADD_END(); - return rc; -} - -memcached_return_t memcached_replace(memcached_st *ptr, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - LIBMEMCACHED_MEMCACHED_REPLACE_START(); - rc= memcached_send(ptr, key, key_length, - key, key_length, value, value_length, - expiration, flags, 0, REPLACE_OP); - LIBMEMCACHED_MEMCACHED_REPLACE_END(); - return rc; -} - -memcached_return_t memcached_prepend(memcached_st *ptr, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - rc= memcached_send(ptr, key, key_length, - key, key_length, value, value_length, - expiration, flags, 0, PREPEND_OP); - return rc; -} - -memcached_return_t memcached_append(memcached_st *ptr, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - rc= memcached_send(ptr, key, key_length, - key, key_length, value, value_length, - expiration, flags, 0, APPEND_OP); - return rc; -} - -memcached_return_t memcached_cas(memcached_st *ptr, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags, - uint64_t cas) -{ - memcached_return_t rc; - rc= memcached_send(ptr, key, key_length, - key, key_length, value, value_length, - expiration, flags, cas, CAS_OP); - return rc; -} - -memcached_return_t memcached_set_by_key(memcached_st *ptr, - const char *group_key, - size_t group_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - LIBMEMCACHED_MEMCACHED_SET_START(); - rc= memcached_send(ptr, group_key, group_key_length, - key, key_length, value, value_length, - expiration, flags, 0, SET_OP); - LIBMEMCACHED_MEMCACHED_SET_END(); - return rc; -} - -memcached_return_t memcached_add_by_key(memcached_st *ptr, - const char *group_key, size_t group_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - LIBMEMCACHED_MEMCACHED_ADD_START(); - rc= memcached_send(ptr, group_key, group_key_length, - key, key_length, value, value_length, - expiration, flags, 0, ADD_OP); - LIBMEMCACHED_MEMCACHED_ADD_END(); - return rc; -} - -memcached_return_t memcached_replace_by_key(memcached_st *ptr, - const char *group_key, size_t group_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - LIBMEMCACHED_MEMCACHED_REPLACE_START(); - rc= memcached_send(ptr, group_key, group_key_length, - key, key_length, value, value_length, - expiration, flags, 0, REPLACE_OP); - LIBMEMCACHED_MEMCACHED_REPLACE_END(); - return rc; -} - -memcached_return_t memcached_prepend_by_key(memcached_st *ptr, - const char *group_key, size_t group_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - rc= memcached_send(ptr, group_key, group_key_length, - key, key_length, value, value_length, - expiration, flags, 0, PREPEND_OP); - return rc; -} - -memcached_return_t memcached_append_by_key(memcached_st *ptr, - const char *group_key, size_t group_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - rc= memcached_send(ptr, group_key, group_key_length, - key, key_length, value, value_length, - expiration, flags, 0, APPEND_OP); - return rc; -} - -memcached_return_t memcached_cas_by_key(memcached_st *ptr, - const char *group_key, size_t group_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags, - uint64_t cas) -{ - memcached_return_t rc; - rc= memcached_send(ptr, group_key, group_key_length, - key, key_length, value, value_length, - expiration, flags, cas, CAS_OP); - return rc; -} - -static inline uint8_t get_com_code(memcached_storage_action_t verb, bool noreply) -{ - /* 0 isn't a value we want, but GCC 4.2 seems to think ret can otherwise - * be used uninitialized in this function. FAIL */ - uint8_t ret= 0; - - if (noreply) - switch (verb) - { - case SET_OP: - ret=PROTOCOL_BINARY_CMD_SETQ; - break; - case ADD_OP: - ret=PROTOCOL_BINARY_CMD_ADDQ; - break; - case CAS_OP: /* FALLTHROUGH */ - case REPLACE_OP: - ret=PROTOCOL_BINARY_CMD_REPLACEQ; - break; - case APPEND_OP: - ret=PROTOCOL_BINARY_CMD_APPENDQ; - break; - case PREPEND_OP: - ret=PROTOCOL_BINARY_CMD_PREPENDQ; - break; - default: - WATCHPOINT_ASSERT(verb); - break; - } - else - switch (verb) - { - case SET_OP: - ret=PROTOCOL_BINARY_CMD_SET; - break; - case ADD_OP: - ret=PROTOCOL_BINARY_CMD_ADD; - break; - case CAS_OP: /* FALLTHROUGH */ - case REPLACE_OP: - ret=PROTOCOL_BINARY_CMD_REPLACE; - break; - case APPEND_OP: - ret=PROTOCOL_BINARY_CMD_APPEND; - break; - case PREPEND_OP: - ret=PROTOCOL_BINARY_CMD_PREPEND; - break; - default: - WATCHPOINT_ASSERT(verb); - break; - } - - return ret; -} - - - -static memcached_return_t memcached_send_binary(memcached_st *ptr, - memcached_server_write_instance_st server, - uint32_t server_key, - const char *key, - size_t key_length, - const char *value, - size_t value_length, - time_t expiration, - uint32_t flags, - uint64_t cas, - memcached_storage_action_t verb) -{ - bool flush; - protocol_binary_request_set request= {.bytes= {0}}; - size_t send_length= sizeof(request.bytes); - - bool noreply= server->root->flags.no_reply; - - request.message.header.request.magic= PROTOCOL_BINARY_REQ; - request.message.header.request.opcode= get_com_code(verb, noreply); - request.message.header.request.keylen= htons((uint16_t)(key_length + memcached_array_size(ptr->prefix_key))); - request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; - if (verb == APPEND_OP || verb == PREPEND_OP) - send_length -= 8; /* append & prepend does not contain extras! */ - else - { - request.message.header.request.extlen= 8; - request.message.body.flags= htonl(flags); - request.message.body.expiration= htonl((uint32_t)expiration); - } - - request.message.header.request.bodylen= htonl((uint32_t) (key_length + memcached_array_size(ptr->prefix_key) + value_length + - request.message.header.request.extlen)); - - if (cas) - request.message.header.request.cas= htonll(cas); - - flush= (bool) ((server->root->flags.buffer_requests && verb == SET_OP) ? 0 : 1); - - if (server->root->flags.use_udp && ! flush) - { - size_t cmd_size= send_length + key_length + value_length; - - if (cmd_size > MAX_UDP_DATAGRAM_LENGTH - UDP_DATAGRAM_HEADER_LENGTH) - { - return MEMCACHED_WRITE_FAILURE; - } - if (cmd_size + server->write_buffer_offset > MAX_UDP_DATAGRAM_LENGTH) - { - memcached_io_write(server, NULL, 0, true); - } - } - - struct libmemcached_io_vector_st vector[]= - { - { .length= send_length, .buffer= request.bytes }, - { .length= memcached_array_size(ptr->prefix_key), .buffer= memcached_array_string(ptr->prefix_key) }, - { .length= key_length, .buffer= key }, - { .length= value_length, .buffer= value } - }; - - /* write the header */ - memcached_return_t rc; - if ((rc= memcached_vdo(server, vector, 4, flush)) != MEMCACHED_SUCCESS) - { - memcached_io_reset(server); - return (rc == MEMCACHED_SUCCESS) ? MEMCACHED_WRITE_FAILURE : rc; - } - - if (verb == SET_OP && ptr->number_of_replicas > 0) - { - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_SETQ; - WATCHPOINT_STRING("replicating"); - - for (uint32_t x= 0; x < ptr->number_of_replicas; x++) - { - memcached_server_write_instance_st instance; - - ++server_key; - if (server_key == memcached_server_count(ptr)) - server_key= 0; - - instance= memcached_server_instance_fetch(ptr, server_key); - - if (memcached_vdo(instance, vector, 4, false) != MEMCACHED_SUCCESS) - { - memcached_io_reset(instance); - } - else - { - memcached_server_response_decrement(instance); - } - } - } - - if (flush == false) - { - return MEMCACHED_BUFFERED; - } - - if (noreply) - { - return MEMCACHED_SUCCESS; - } - - return memcached_response(server, NULL, 0, NULL); -} - diff --git a/libmemcached/storage.cc b/libmemcached/storage.cc new file mode 100644 index 00000000..006393c4 --- /dev/null +++ b/libmemcached/storage.cc @@ -0,0 +1,569 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Storage related functions, aka set, replace,.. + * + */ + +#include + +enum memcached_storage_action_t { + SET_OP, + REPLACE_OP, + ADD_OP, + PREPEND_OP, + APPEND_OP, + CAS_OP +}; + +/* Inline this */ +static inline const char *storage_op_string(memcached_storage_action_t verb) +{ + switch (verb) + { + case SET_OP: + return "set "; + case REPLACE_OP: + return "replace "; + case ADD_OP: + return "add "; + case PREPEND_OP: + return "prepend "; + case APPEND_OP: + return "append "; + case CAS_OP: + return "cas "; + default: + return "tosserror"; /* This is impossible, fixes issue for compiler warning in VisualStudio */ + } + + /* NOTREACHED */ +} + +static memcached_return_t memcached_send_binary(memcached_st *ptr, + memcached_server_write_instance_st server, + uint32_t server_key, + const char *key, + size_t key_length, + const char *value, + size_t value_length, + time_t expiration, + uint32_t flags, + uint64_t cas, + memcached_storage_action_t verb); + +static inline memcached_return_t memcached_send(memcached_st *ptr, + const char *group_key, size_t group_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags, + uint64_t cas, + memcached_storage_action_t verb) +{ + bool to_write; + size_t write_length; + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + uint32_t server_key; + memcached_server_write_instance_st instance; + + WATCHPOINT_ASSERT(!(value == NULL && value_length > 0)); + + memcached_return_t rc; + if (memcached_failed(rc= initialize_query(ptr))) + { + return rc; + } + + if (memcached_failed(rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol))) + return rc; + + if (ptr->flags.verify_key && (memcached_key_test((const char **)&key, &key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED)) + return MEMCACHED_BAD_KEY_PROVIDED; + + server_key= memcached_generate_hash_with_redistribution(ptr, group_key, group_key_length); + instance= memcached_server_instance_fetch(ptr, server_key); + + WATCHPOINT_SET(instance->io_wait_count.read= 0); + WATCHPOINT_SET(instance->io_wait_count.write= 0); + + if (ptr->flags.binary_protocol) + { + rc= memcached_send_binary(ptr, instance, server_key, + key, key_length, + value, value_length, expiration, + flags, cas, verb); + WATCHPOINT_IF_LABELED_NUMBER(instance->io_wait_count.read > 2, "read IO_WAIT", instance->io_wait_count.read); + WATCHPOINT_IF_LABELED_NUMBER(instance->io_wait_count.write > 2, "write_IO_WAIT", instance->io_wait_count.write); + } + else + { + + if (cas) + { + int check_length; + check_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "%s %.*s%.*s %u %llu %lu %llu%s\r\n", + storage_op_string(verb), + memcached_print_array(ptr->prefix_key), + (int)key_length, key, flags, + (unsigned long long)expiration, (unsigned long)value_length, + (unsigned long long)cas, + (ptr->flags.no_reply) ? " noreply" : ""); + if (check_length >= MEMCACHED_DEFAULT_COMMAND_SIZE || check_length < 0) + { + rc= MEMCACHED_WRITE_FAILURE; + memcached_io_reset(instance); + + return rc; + } + write_length= check_length; + } + else + { + char *buffer_ptr= buffer; + const char *command= storage_op_string(verb); + + /* Copy in the command, no space needed, we handle that in the command function*/ + memcpy(buffer_ptr, command, strlen(command)); + + /* Copy in the key prefix, switch to the buffer_ptr */ + buffer_ptr= (char *)memcpy((char *)(buffer_ptr + strlen(command)), (char *)memcached_array_string(ptr->prefix_key), memcached_array_size(ptr->prefix_key)); + + /* Copy in the key, adjust point if a key prefix was used. */ + buffer_ptr= (char *)memcpy(buffer_ptr + memcached_array_size(ptr->prefix_key), + key, key_length); + buffer_ptr+= key_length; + buffer_ptr[0]= ' '; + buffer_ptr++; + + write_length= (size_t)(buffer_ptr - buffer); + int check_length; + check_length= snprintf(buffer_ptr, MEMCACHED_DEFAULT_COMMAND_SIZE -(size_t)(buffer_ptr - buffer), + "%u %llu %lu%s\r\n", + flags, + (unsigned long long)expiration, (unsigned long)value_length, + ptr->flags.no_reply ? " noreply" : ""); + if ((size_t)check_length >= MEMCACHED_DEFAULT_COMMAND_SIZE -(size_t)(buffer_ptr - buffer) || check_length < 0) + { + rc= MEMCACHED_WRITE_FAILURE; + memcached_io_reset(instance); + + return rc; + } + + write_length+= (size_t)check_length; + WATCHPOINT_ASSERT(write_length < MEMCACHED_DEFAULT_COMMAND_SIZE); + } + + if (ptr->flags.use_udp && ptr->flags.buffer_requests) + { + size_t cmd_size= write_length + value_length + 2; + if (cmd_size > MAX_UDP_DATAGRAM_LENGTH - UDP_DATAGRAM_HEADER_LENGTH) + return MEMCACHED_WRITE_FAILURE; + if (cmd_size + instance->write_buffer_offset > MAX_UDP_DATAGRAM_LENGTH) + memcached_io_write(instance, NULL, 0, true); + } + + if (write_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) + { + rc= MEMCACHED_WRITE_FAILURE; + } + else + { + struct libmemcached_io_vector_st vector[]= + { + { write_length, buffer }, + { value_length, value }, + { 2, "\r\n" } + }; + + if (ptr->flags.buffer_requests && verb == SET_OP) + { + to_write= false; + } + else + { + to_write= true; + } + + /* Send command header */ + rc= memcached_vdo(instance, vector, 3, to_write); + if (rc == MEMCACHED_SUCCESS) + { + + if (ptr->flags.no_reply) + { + rc= (to_write == false) ? MEMCACHED_BUFFERED : MEMCACHED_SUCCESS; + } + else if (to_write == false) + { + rc= MEMCACHED_BUFFERED; + } + else + { + rc= memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); + + if (rc == MEMCACHED_STORED) + rc= MEMCACHED_SUCCESS; + } + } + } + + if (rc == MEMCACHED_WRITE_FAILURE) + memcached_io_reset(instance); + } + + WATCHPOINT_IF_LABELED_NUMBER(instance->io_wait_count.read > 2, "read IO_WAIT", instance->io_wait_count.read); + WATCHPOINT_IF_LABELED_NUMBER(instance->io_wait_count.write > 2, "write_IO_WAIT", instance->io_wait_count.write); + + return rc; +} + + +memcached_return_t memcached_set(memcached_st *ptr, const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + LIBMEMCACHED_MEMCACHED_SET_START(); + rc= memcached_send(ptr, key, key_length, + key, key_length, value, value_length, + expiration, flags, 0, SET_OP); + LIBMEMCACHED_MEMCACHED_SET_END(); + return rc; +} + +memcached_return_t memcached_add(memcached_st *ptr, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + LIBMEMCACHED_MEMCACHED_ADD_START(); + rc= memcached_send(ptr, key, key_length, + key, key_length, value, value_length, + expiration, flags, 0, ADD_OP); + LIBMEMCACHED_MEMCACHED_ADD_END(); + return rc; +} + +memcached_return_t memcached_replace(memcached_st *ptr, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + LIBMEMCACHED_MEMCACHED_REPLACE_START(); + rc= memcached_send(ptr, key, key_length, + key, key_length, value, value_length, + expiration, flags, 0, REPLACE_OP); + LIBMEMCACHED_MEMCACHED_REPLACE_END(); + return rc; +} + +memcached_return_t memcached_prepend(memcached_st *ptr, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + rc= memcached_send(ptr, key, key_length, + key, key_length, value, value_length, + expiration, flags, 0, PREPEND_OP); + return rc; +} + +memcached_return_t memcached_append(memcached_st *ptr, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + rc= memcached_send(ptr, key, key_length, + key, key_length, value, value_length, + expiration, flags, 0, APPEND_OP); + return rc; +} + +memcached_return_t memcached_cas(memcached_st *ptr, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags, + uint64_t cas) +{ + memcached_return_t rc; + rc= memcached_send(ptr, key, key_length, + key, key_length, value, value_length, + expiration, flags, cas, CAS_OP); + return rc; +} + +memcached_return_t memcached_set_by_key(memcached_st *ptr, + const char *group_key, + size_t group_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + LIBMEMCACHED_MEMCACHED_SET_START(); + rc= memcached_send(ptr, group_key, group_key_length, + key, key_length, value, value_length, + expiration, flags, 0, SET_OP); + LIBMEMCACHED_MEMCACHED_SET_END(); + return rc; +} + +memcached_return_t memcached_add_by_key(memcached_st *ptr, + const char *group_key, size_t group_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + LIBMEMCACHED_MEMCACHED_ADD_START(); + rc= memcached_send(ptr, group_key, group_key_length, + key, key_length, value, value_length, + expiration, flags, 0, ADD_OP); + LIBMEMCACHED_MEMCACHED_ADD_END(); + return rc; +} + +memcached_return_t memcached_replace_by_key(memcached_st *ptr, + const char *group_key, size_t group_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + LIBMEMCACHED_MEMCACHED_REPLACE_START(); + rc= memcached_send(ptr, group_key, group_key_length, + key, key_length, value, value_length, + expiration, flags, 0, REPLACE_OP); + LIBMEMCACHED_MEMCACHED_REPLACE_END(); + return rc; +} + +memcached_return_t memcached_prepend_by_key(memcached_st *ptr, + const char *group_key, size_t group_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + rc= memcached_send(ptr, group_key, group_key_length, + key, key_length, value, value_length, + expiration, flags, 0, PREPEND_OP); + return rc; +} + +memcached_return_t memcached_append_by_key(memcached_st *ptr, + const char *group_key, size_t group_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + rc= memcached_send(ptr, group_key, group_key_length, + key, key_length, value, value_length, + expiration, flags, 0, APPEND_OP); + return rc; +} + +memcached_return_t memcached_cas_by_key(memcached_st *ptr, + const char *group_key, size_t group_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags, + uint64_t cas) +{ + memcached_return_t rc; + rc= memcached_send(ptr, group_key, group_key_length, + key, key_length, value, value_length, + expiration, flags, cas, CAS_OP); + return rc; +} + +static inline uint8_t get_com_code(memcached_storage_action_t verb, bool noreply) +{ + /* 0 isn't a value we want, but GCC 4.2 seems to think ret can otherwise + * be used uninitialized in this function. FAIL */ + uint8_t ret= 0; + + if (noreply) + switch (verb) + { + case SET_OP: + ret=PROTOCOL_BINARY_CMD_SETQ; + break; + case ADD_OP: + ret=PROTOCOL_BINARY_CMD_ADDQ; + break; + case CAS_OP: /* FALLTHROUGH */ + case REPLACE_OP: + ret=PROTOCOL_BINARY_CMD_REPLACEQ; + break; + case APPEND_OP: + ret=PROTOCOL_BINARY_CMD_APPENDQ; + break; + case PREPEND_OP: + ret=PROTOCOL_BINARY_CMD_PREPENDQ; + break; + default: + WATCHPOINT_ASSERT(verb); + break; + } + else + switch (verb) + { + case SET_OP: + ret=PROTOCOL_BINARY_CMD_SET; + break; + case ADD_OP: + ret=PROTOCOL_BINARY_CMD_ADD; + break; + case CAS_OP: /* FALLTHROUGH */ + case REPLACE_OP: + ret=PROTOCOL_BINARY_CMD_REPLACE; + break; + case APPEND_OP: + ret=PROTOCOL_BINARY_CMD_APPEND; + break; + case PREPEND_OP: + ret=PROTOCOL_BINARY_CMD_PREPEND; + break; + default: + WATCHPOINT_ASSERT(verb); + break; + } + + return ret; +} + + + +static memcached_return_t memcached_send_binary(memcached_st *ptr, + memcached_server_write_instance_st server, + uint32_t server_key, + const char *key, + size_t key_length, + const char *value, + size_t value_length, + time_t expiration, + uint32_t flags, + uint64_t cas, + memcached_storage_action_t verb) +{ + bool flush; + protocol_binary_request_set request= {}; + size_t send_length= sizeof(request.bytes); + + bool noreply= server->root->flags.no_reply; + + request.message.header.request.magic= PROTOCOL_BINARY_REQ; + request.message.header.request.opcode= get_com_code(verb, noreply); + request.message.header.request.keylen= htons((uint16_t)(key_length + memcached_array_size(ptr->prefix_key))); + request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; + if (verb == APPEND_OP || verb == PREPEND_OP) + send_length -= 8; /* append & prepend does not contain extras! */ + else + { + request.message.header.request.extlen= 8; + request.message.body.flags= htonl(flags); + request.message.body.expiration= htonl((uint32_t)expiration); + } + + request.message.header.request.bodylen= htonl((uint32_t) (key_length + memcached_array_size(ptr->prefix_key) + value_length + + request.message.header.request.extlen)); + + if (cas) + request.message.header.request.cas= htonll(cas); + + flush= (bool) ((server->root->flags.buffer_requests && verb == SET_OP) ? 0 : 1); + + if (server->root->flags.use_udp && ! flush) + { + size_t cmd_size= send_length + key_length + value_length; + + if (cmd_size > MAX_UDP_DATAGRAM_LENGTH - UDP_DATAGRAM_HEADER_LENGTH) + { + return MEMCACHED_WRITE_FAILURE; + } + if (cmd_size + server->write_buffer_offset > MAX_UDP_DATAGRAM_LENGTH) + { + memcached_io_write(server, NULL, 0, true); + } + } + + struct libmemcached_io_vector_st vector[]= + { + { send_length, request.bytes }, + { memcached_array_size(ptr->prefix_key), memcached_array_string(ptr->prefix_key) }, + { key_length, key }, + { value_length, value } + }; + + /* write the header */ + memcached_return_t rc; + if ((rc= memcached_vdo(server, vector, 4, flush)) != MEMCACHED_SUCCESS) + { + memcached_io_reset(server); + return (rc == MEMCACHED_SUCCESS) ? MEMCACHED_WRITE_FAILURE : rc; + } + + if (verb == SET_OP && ptr->number_of_replicas > 0) + { + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_SETQ; + WATCHPOINT_STRING("replicating"); + + for (uint32_t x= 0; x < ptr->number_of_replicas; x++) + { + memcached_server_write_instance_st instance; + + ++server_key; + if (server_key == memcached_server_count(ptr)) + server_key= 0; + + instance= memcached_server_instance_fetch(ptr, server_key); + + if (memcached_vdo(instance, vector, 4, false) != MEMCACHED_SUCCESS) + { + memcached_io_reset(instance); + } + else + { + memcached_server_response_decrement(instance); + } + } + } + + if (flush == false) + { + return MEMCACHED_BUFFERED; + } + + if (noreply) + { + return MEMCACHED_SUCCESS; + } + + return memcached_response(server, NULL, 0, NULL); +} + diff --git a/libmemcached/strerror.c b/libmemcached/strerror.c deleted file mode 100644 index b3ac2b75..00000000 --- a/libmemcached/strerror.c +++ /dev/null @@ -1,105 +0,0 @@ -#include "common.h" - -const char *memcached_strerror(memcached_st *ptr, memcached_return_t rc) -{ - (void)ptr; - switch (rc) - { - case MEMCACHED_SUCCESS: - return "SUCCESS"; - case MEMCACHED_FAILURE: - return "FAILURE"; - case MEMCACHED_HOST_LOOKUP_FAILURE: - return "HOSTNAME LOOKUP FAILURE"; - case MEMCACHED_CONNECTION_FAILURE: - return "CONNECTION FAILURE"; - case MEMCACHED_CONNECTION_BIND_FAILURE: - return "CONNECTION BIND FAILURE"; - case MEMCACHED_READ_FAILURE: - return "READ FAILURE"; - case MEMCACHED_UNKNOWN_READ_FAILURE: - return "UNKNOWN READ FAILURE"; - case MEMCACHED_PROTOCOL_ERROR: - return "PROTOCOL ERROR"; - case MEMCACHED_CLIENT_ERROR: - return "CLIENT ERROR"; - case MEMCACHED_SERVER_ERROR: - return "SERVER ERROR"; - case MEMCACHED_WRITE_FAILURE: - return "WRITE FAILURE"; - case MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE: - return "CONNECTION SOCKET CREATE FAILURE"; - case MEMCACHED_DATA_EXISTS: - return "CONNECTION DATA EXISTS"; - case MEMCACHED_DATA_DOES_NOT_EXIST: - return "CONNECTION DATA DOES NOT EXIST"; - case MEMCACHED_NOTSTORED: - return "NOT STORED"; - case MEMCACHED_STORED: - return "STORED"; - case MEMCACHED_NOTFOUND: - return "NOT FOUND"; - case MEMCACHED_MEMORY_ALLOCATION_FAILURE: - return "MEMORY ALLOCATION FAILURE"; - case MEMCACHED_PARTIAL_READ: - return "PARTIAL READ"; - case MEMCACHED_SOME_ERRORS: - return "SOME ERRORS WERE REPORTED"; - case MEMCACHED_NO_SERVERS: - return "NO SERVERS DEFINED"; - case MEMCACHED_END: - return "SERVER END"; - case MEMCACHED_DELETED: - return "SERVER DELETE"; - case MEMCACHED_VALUE: - return "SERVER VALUE"; - case MEMCACHED_STAT: - return "STAT VALUE"; - case MEMCACHED_ITEM: - return "ITEM VALUE"; - case MEMCACHED_ERRNO: - return "SYSTEM ERROR"; - case MEMCACHED_FAIL_UNIX_SOCKET: - return "COULD NOT OPEN UNIX SOCKET"; - case MEMCACHED_NOT_SUPPORTED: - return "ACTION NOT SUPPORTED"; - case MEMCACHED_FETCH_NOTFINISHED: - return "FETCH WAS NOT COMPLETED"; - case MEMCACHED_NO_KEY_PROVIDED: - return "A KEY LENGTH OF ZERO WAS PROVIDED"; - case MEMCACHED_BUFFERED: - return "ACTION QUEUED"; - case MEMCACHED_TIMEOUT: - return "A TIMEOUT OCCURRED"; - case MEMCACHED_BAD_KEY_PROVIDED: - return "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE"; - case MEMCACHED_INVALID_HOST_PROTOCOL: - return "THE HOST TRANSPORT PROTOCOL DOES NOT MATCH THAT OF THE CLIENT"; - case MEMCACHED_SERVER_MARKED_DEAD: - return "SERVER IS MARKED DEAD"; - case MEMCACHED_UNKNOWN_STAT_KEY: - return "ENCOUNTERED AN UNKNOWN STAT KEY"; - case MEMCACHED_E2BIG: - return "ITEM TOO BIG"; - case MEMCACHED_INVALID_ARGUMENTS: - return "INVALID ARGUMENTS"; - case MEMCACHED_KEY_TOO_BIG: - return "KEY RETURNED FROM SERVER WAS TOO LARGE"; - case MEMCACHED_AUTH_PROBLEM: - return "FAILED TO SEND AUTHENTICATION TO SERVER"; - case MEMCACHED_AUTH_FAILURE: - return "AUTHENTICATION FAILURE"; - case MEMCACHED_AUTH_CONTINUE: - return "CONTINUE AUTHENTICATION"; - case MEMCACHED_PARSE_ERROR: - return "ERROR OCCURED WHILE PARSING"; - case MEMCACHED_PARSE_USER_ERROR: - return "USER INITIATED ERROR OCCURED WHILE PARSING"; - case MEMCACHED_DEPRECATED: - return "DEPRECATED"; - case MEMCACHED_MAXIMUM_RETURN: - return "Gibberish returned!"; - default: - return "Gibberish returned!"; - } -} diff --git a/libmemcached/strerror.cc b/libmemcached/strerror.cc new file mode 100644 index 00000000..b3ac2b75 --- /dev/null +++ b/libmemcached/strerror.cc @@ -0,0 +1,105 @@ +#include "common.h" + +const char *memcached_strerror(memcached_st *ptr, memcached_return_t rc) +{ + (void)ptr; + switch (rc) + { + case MEMCACHED_SUCCESS: + return "SUCCESS"; + case MEMCACHED_FAILURE: + return "FAILURE"; + case MEMCACHED_HOST_LOOKUP_FAILURE: + return "HOSTNAME LOOKUP FAILURE"; + case MEMCACHED_CONNECTION_FAILURE: + return "CONNECTION FAILURE"; + case MEMCACHED_CONNECTION_BIND_FAILURE: + return "CONNECTION BIND FAILURE"; + case MEMCACHED_READ_FAILURE: + return "READ FAILURE"; + case MEMCACHED_UNKNOWN_READ_FAILURE: + return "UNKNOWN READ FAILURE"; + case MEMCACHED_PROTOCOL_ERROR: + return "PROTOCOL ERROR"; + case MEMCACHED_CLIENT_ERROR: + return "CLIENT ERROR"; + case MEMCACHED_SERVER_ERROR: + return "SERVER ERROR"; + case MEMCACHED_WRITE_FAILURE: + return "WRITE FAILURE"; + case MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE: + return "CONNECTION SOCKET CREATE FAILURE"; + case MEMCACHED_DATA_EXISTS: + return "CONNECTION DATA EXISTS"; + case MEMCACHED_DATA_DOES_NOT_EXIST: + return "CONNECTION DATA DOES NOT EXIST"; + case MEMCACHED_NOTSTORED: + return "NOT STORED"; + case MEMCACHED_STORED: + return "STORED"; + case MEMCACHED_NOTFOUND: + return "NOT FOUND"; + case MEMCACHED_MEMORY_ALLOCATION_FAILURE: + return "MEMORY ALLOCATION FAILURE"; + case MEMCACHED_PARTIAL_READ: + return "PARTIAL READ"; + case MEMCACHED_SOME_ERRORS: + return "SOME ERRORS WERE REPORTED"; + case MEMCACHED_NO_SERVERS: + return "NO SERVERS DEFINED"; + case MEMCACHED_END: + return "SERVER END"; + case MEMCACHED_DELETED: + return "SERVER DELETE"; + case MEMCACHED_VALUE: + return "SERVER VALUE"; + case MEMCACHED_STAT: + return "STAT VALUE"; + case MEMCACHED_ITEM: + return "ITEM VALUE"; + case MEMCACHED_ERRNO: + return "SYSTEM ERROR"; + case MEMCACHED_FAIL_UNIX_SOCKET: + return "COULD NOT OPEN UNIX SOCKET"; + case MEMCACHED_NOT_SUPPORTED: + return "ACTION NOT SUPPORTED"; + case MEMCACHED_FETCH_NOTFINISHED: + return "FETCH WAS NOT COMPLETED"; + case MEMCACHED_NO_KEY_PROVIDED: + return "A KEY LENGTH OF ZERO WAS PROVIDED"; + case MEMCACHED_BUFFERED: + return "ACTION QUEUED"; + case MEMCACHED_TIMEOUT: + return "A TIMEOUT OCCURRED"; + case MEMCACHED_BAD_KEY_PROVIDED: + return "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE"; + case MEMCACHED_INVALID_HOST_PROTOCOL: + return "THE HOST TRANSPORT PROTOCOL DOES NOT MATCH THAT OF THE CLIENT"; + case MEMCACHED_SERVER_MARKED_DEAD: + return "SERVER IS MARKED DEAD"; + case MEMCACHED_UNKNOWN_STAT_KEY: + return "ENCOUNTERED AN UNKNOWN STAT KEY"; + case MEMCACHED_E2BIG: + return "ITEM TOO BIG"; + case MEMCACHED_INVALID_ARGUMENTS: + return "INVALID ARGUMENTS"; + case MEMCACHED_KEY_TOO_BIG: + return "KEY RETURNED FROM SERVER WAS TOO LARGE"; + case MEMCACHED_AUTH_PROBLEM: + return "FAILED TO SEND AUTHENTICATION TO SERVER"; + case MEMCACHED_AUTH_FAILURE: + return "AUTHENTICATION FAILURE"; + case MEMCACHED_AUTH_CONTINUE: + return "CONTINUE AUTHENTICATION"; + case MEMCACHED_PARSE_ERROR: + return "ERROR OCCURED WHILE PARSING"; + case MEMCACHED_PARSE_USER_ERROR: + return "USER INITIATED ERROR OCCURED WHILE PARSING"; + case MEMCACHED_DEPRECATED: + return "DEPRECATED"; + case MEMCACHED_MAXIMUM_RETURN: + return "Gibberish returned!"; + default: + return "Gibberish returned!"; + } +} diff --git a/libmemcached/string.c b/libmemcached/string.c deleted file mode 100644 index b5badc5e..00000000 --- a/libmemcached/string.c +++ /dev/null @@ -1,214 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: String structure used for libmemcached. - * - */ - -#include "common.h" - -inline static memcached_return_t _string_check(memcached_string_st *string, size_t need) -{ - if (need && need > (size_t)(string->current_size - (size_t)(string->end - string->string))) - { - size_t current_offset= (size_t) (string->end - string->string); - char *new_value; - size_t adjust; - size_t new_size; - - /* This is the block multiplier. To keep it larger and surive division errors we must round it up */ - adjust= (need - (size_t)(string->current_size - (size_t)(string->end - string->string))) / MEMCACHED_BLOCK_SIZE; - adjust++; - - new_size= sizeof(char) * (size_t)((adjust * MEMCACHED_BLOCK_SIZE) + string->current_size); - /* Test for overflow */ - if (new_size < need) - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - - new_value= libmemcached_realloc(string->root, string->string, new_size); - - if (new_value == NULL) - { - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - } - - string->string= new_value; - string->end= string->string + current_offset; - - string->current_size+= (MEMCACHED_BLOCK_SIZE * adjust); - } - - return MEMCACHED_SUCCESS; -} - -static inline void _init_string(memcached_string_st *self) -{ - self->current_size= 0; - self->end= self->string= NULL; -} - -memcached_string_st *memcached_string_create(const memcached_st *memc, memcached_string_st *self, size_t initial_size) -{ - memcached_return_t rc; - - WATCHPOINT_ASSERT(memc); - - /* Saving malloc calls :) */ - if (self) - { - WATCHPOINT_ASSERT(self->options.is_initialized == false); - - self->options.is_allocated= false; - } - else - { - self= libmemcached_malloc(memc, sizeof(memcached_string_st)); - - if (self == NULL) - { - return NULL; - } - - self->options.is_allocated= true; - } - self->root= (memcached_st *)memc; - - _init_string(self); - - rc= _string_check(self, initial_size); - if (rc != MEMCACHED_SUCCESS) - { - if (rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE) - { - memcached_set_errno(self->root, errno, NULL); - } - libmemcached_free(memc, self); - - return NULL; - } - - self->options.is_initialized= true; - - WATCHPOINT_ASSERT(self->string == self->end); - - return self; -} - -memcached_return_t memcached_string_append_character(memcached_string_st *string, - char character) -{ - memcached_return_t rc; - - rc= _string_check(string, 1); - - if (rc != MEMCACHED_SUCCESS) - { - return rc; - } - - *string->end= character; - string->end++; - - return MEMCACHED_SUCCESS; -} - -memcached_return_t memcached_string_append(memcached_string_st *string, - const char *value, size_t length) -{ - memcached_return_t rc; - - rc= _string_check(string, length); - - if (rc != MEMCACHED_SUCCESS) - { - return rc; - } - - WATCHPOINT_ASSERT(length <= string->current_size); - WATCHPOINT_ASSERT(string->string); - WATCHPOINT_ASSERT(string->end >= string->string); - - memcpy(string->end, value, length); - string->end+= length; - - return MEMCACHED_SUCCESS; -} - -char *memcached_string_c_copy(memcached_string_st *string) -{ - char *c_ptr; - - if (memcached_string_length(string) == 0) - return NULL; - - c_ptr= libmemcached_malloc(string->root, (memcached_string_length(string)+1) * sizeof(char)); - - if (c_ptr == NULL) - return NULL; - - memcpy(c_ptr, memcached_string_value(string), memcached_string_length(string)); - c_ptr[memcached_string_length(string)]= 0; - - return c_ptr; -} - -memcached_return_t memcached_string_reset(memcached_string_st *string) -{ - string->end= string->string; - - return MEMCACHED_SUCCESS; -} - -void memcached_string_free(memcached_string_st *ptr) -{ - if (ptr == NULL) - return; - - if (ptr->string) - { - libmemcached_free(ptr->root, ptr->string); - } - - if (memcached_is_allocated(ptr)) - { - libmemcached_free(ptr->root, ptr); - } - else - { - ptr->options.is_initialized= false; - } -} - -memcached_return_t memcached_string_check(memcached_string_st *string, size_t need) -{ - return _string_check(string, need); -} - -size_t memcached_string_length(const memcached_string_st *self) -{ - return (size_t)(self->end - self->string); -} - -size_t memcached_string_size(const memcached_string_st *self) -{ - return self->current_size; -} - -const char *memcached_string_value(const memcached_string_st *self) -{ - return self->string; -} - -char *memcached_string_value_mutable(const memcached_string_st *self) -{ - return self->string; -} - -void memcached_string_set_length(memcached_string_st *self, size_t length) -{ - self->end= self->string + length; -} diff --git a/libmemcached/string.cc b/libmemcached/string.cc new file mode 100644 index 00000000..4f012795 --- /dev/null +++ b/libmemcached/string.cc @@ -0,0 +1,241 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2009 Brian Aker 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + + +#include + +inline static memcached_return_t _string_check(memcached_string_st *string, size_t need) +{ + if (need && need > (size_t)(string->current_size - (size_t)(string->end - string->string))) + { + size_t current_offset= (size_t) (string->end - string->string); + char *new_value; + size_t adjust; + size_t new_size; + + /* This is the block multiplier. To keep it larger and surive division errors we must round it up */ + adjust= (need - (size_t)(string->current_size - (size_t)(string->end - string->string))) / MEMCACHED_BLOCK_SIZE; + adjust++; + + new_size= sizeof(char) * (size_t)((adjust * MEMCACHED_BLOCK_SIZE) + string->current_size); + /* Test for overflow */ + if (new_size < need) + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + + new_value= static_cast(libmemcached_realloc(string->root, string->string, new_size)); + + if (new_value == NULL) + { + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + } + + string->string= new_value; + string->end= string->string + current_offset; + + string->current_size+= (MEMCACHED_BLOCK_SIZE * adjust); + } + + return MEMCACHED_SUCCESS; +} + +static inline void _init_string(memcached_string_st *self) +{ + self->current_size= 0; + self->end= self->string= NULL; +} + +memcached_string_st *memcached_string_create(const memcached_st *memc, memcached_string_st *self, size_t initial_size) +{ + memcached_return_t rc; + + WATCHPOINT_ASSERT(memc); + + /* Saving malloc calls :) */ + if (self) + { + WATCHPOINT_ASSERT(self->options.is_initialized == false); + + self->options.is_allocated= false; + } + else + { + self= static_cast(libmemcached_malloc(memc, sizeof(memcached_string_st))); + + if (self == NULL) + { + return NULL; + } + + self->options.is_allocated= true; + } + self->root= const_cast(memc); + + _init_string(self); + + rc= _string_check(self, initial_size); + if (rc != MEMCACHED_SUCCESS) + { + if (rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE) + { + memcached_set_errno(self->root, errno, NULL); + } + libmemcached_free(memc, self); + + return NULL; + } + + self->options.is_initialized= true; + + WATCHPOINT_ASSERT(self->string == self->end); + + return self; +} + +memcached_return_t memcached_string_append_character(memcached_string_st *string, + char character) +{ + memcached_return_t rc; + + rc= _string_check(string, 1); + + if (rc != MEMCACHED_SUCCESS) + { + return rc; + } + + *string->end= character; + string->end++; + + return MEMCACHED_SUCCESS; +} + +memcached_return_t memcached_string_append(memcached_string_st *string, + const char *value, size_t length) +{ + memcached_return_t rc; + + rc= _string_check(string, length); + + if (rc != MEMCACHED_SUCCESS) + { + return rc; + } + + WATCHPOINT_ASSERT(length <= string->current_size); + WATCHPOINT_ASSERT(string->string); + WATCHPOINT_ASSERT(string->end >= string->string); + + memcpy(string->end, value, length); + string->end+= length; + + return MEMCACHED_SUCCESS; +} + +char *memcached_string_c_copy(memcached_string_st *string) +{ + char *c_ptr; + + if (memcached_string_length(string) == 0) + return NULL; + + c_ptr= static_cast(libmemcached_malloc(string->root, (memcached_string_length(string)+1) * sizeof(char))); + + if (c_ptr == NULL) + return NULL; + + memcpy(c_ptr, memcached_string_value(string), memcached_string_length(string)); + c_ptr[memcached_string_length(string)]= 0; + + return c_ptr; +} + +memcached_return_t memcached_string_reset(memcached_string_st *string) +{ + string->end= string->string; + + return MEMCACHED_SUCCESS; +} + +void memcached_string_free(memcached_string_st *ptr) +{ + if (ptr == NULL) + return; + + if (ptr->string) + { + libmemcached_free(ptr->root, ptr->string); + } + + if (memcached_is_allocated(ptr)) + { + libmemcached_free(ptr->root, ptr); + } + else + { + ptr->options.is_initialized= false; + } +} + +memcached_return_t memcached_string_check(memcached_string_st *string, size_t need) +{ + return _string_check(string, need); +} + +size_t memcached_string_length(const memcached_string_st *self) +{ + return (size_t)(self->end - self->string); +} + +size_t memcached_string_size(const memcached_string_st *self) +{ + return self->current_size; +} + +const char *memcached_string_value(const memcached_string_st *self) +{ + return self->string; +} + +char *memcached_string_value_mutable(const memcached_string_st *self) +{ + return self->string; +} + +void memcached_string_set_length(memcached_string_st *self, size_t length) +{ + self->end= self->string + length; +} diff --git a/libmemcached/string.h b/libmemcached/string.h index ca3dad14..8c57c8b0 100644 --- a/libmemcached/string.h +++ b/libmemcached/string.h @@ -1,17 +1,41 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2009 Brian Aker All rights reserved. * - * Summary: String structure used for libmemcached. + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. * */ #pragma once -#ifndef __LIBMEMCACHED_STRING_H__ -#define __LIBMEMCACHED_STRING_H__ /** Strings are always under our control so we make some assumptions @@ -97,5 +121,3 @@ void memcached_string_set_length(memcached_string_st *self, size_t length); #define memcached_string_make_from_cstr(X) (X), ((X) ? strlen(X) : 0) #endif - -#endif /* __LIBMEMCACHED_STRING_H__ */ diff --git a/libmemcached/types.h b/libmemcached/types.h index 2ebb8c00..b3d8f479 100644 --- a/libmemcached/types.h +++ b/libmemcached/types.h @@ -9,6 +9,7 @@ * */ +#pragma once #ifndef __LIBMEMCACHED_TYPES_H__ #define __LIBMEMCACHED_TYPES_H__ diff --git a/libmemcached/util/include.am b/libmemcached/util/include.am new file mode 100644 index 00000000..2c452f52 --- /dev/null +++ b/libmemcached/util/include.am @@ -0,0 +1,32 @@ +# vim:ft=automake +# included from Top Level Makefile.am +# All paths should be given relative to the root + +if BUILD_LIBMEMCACHEDUTIL +nobase_include_HEADERS+= \ + libmemcached/memcached_util.h \ + libmemcached/util.h \ + libmemcached/util/ping.h \ + libmemcached/util/pool.h \ + libmemcached/util/version.h +lib_LTLIBRARIES+= libmemcached/libmemcachedutil.la +endif + +libmemcached_libmemcachedutil_la_SOURCES= \ + libmemcached/util/ping.cc \ + libmemcached/util/pool.cc \ + libmemcached/util/version.cc +libmemcached_libmemcachedutil_la_CFLAGS= \ + ${AM_CFLAGS} \ + ${NO_CONVERSION} \ + ${PTHREAD_CFLAGS} \ + -DBUILDING_LIBMEMCACHED +libmemcached_libmemcachedutil_la_CXXFLAGS= \ + ${AM_CXXFLAGS} \ + ${NO_CONVERSION} \ + ${PTHREAD_CFLAGS} \ + -DBUILDING_LIBMEMCACHED +libmemcached_libmemcachedutil_la_LIBADD= libmemcached/libmemcached.la +libmemcached_libmemcachedutil_la_LDFLAGS= ${AM_LDFLAGS} ${PTHREAD_LIBS} -version-info ${MEMCACHED_UTIL_LIBRARY_VERSION} +libmemcached_libmemcachedutil_la_DEPENDENCIES= libmemcached/libmemcached.la + diff --git a/libmemcached/util/version.cc b/libmemcached/util/version.cc index 761cbbf5..a8208298 100644 --- a/libmemcached/util/version.cc +++ b/libmemcached/util/version.cc @@ -1,17 +1,43 @@ -/* LibMemcached - * Copyright (C) 2010 Brian Aker - * All rights reserved. +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker All rights reserved. * - * Summary: connect to all hosts, and make sure they meet a minimum version + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. * */ -/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ -#include "libmemcached/common.h" -#include "libmemcached/memcached_util.h" + +#include +#include struct local_context { @@ -48,14 +74,12 @@ bool libmemcached_util_version_check(memcached_st *memc, uint8_t minor_version, uint8_t micro_version) { - memcached_server_fn callbacks[1]; - memcached_return_t rc= memcached_version(memc); - - if (rc != MEMCACHED_SUCCESS) + if (memcached_version(memc) != MEMCACHED_SUCCESS) return false; struct local_context check= { major_version, minor_version, micro_version, true }; + memcached_server_fn callbacks[1]; callbacks[0]= check_server_version; memcached_server_cursor(memc, callbacks, (void *)&check, 1); diff --git a/libmemcached/verbosity.c b/libmemcached/verbosity.c deleted file mode 100644 index d71fced7..00000000 --- a/libmemcached/verbosity.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "common.h" - -struct context_st -{ - size_t length; - const char *buffer; -}; - -static memcached_return_t _set_verbosity(const memcached_st *ptr, - const memcached_server_st *server, - void *context) -{ - memcached_return_t rc; - memcached_st local_memc; - memcached_st *memc_ptr; - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - - struct context_st *execute= (struct context_st *)context; - (void)ptr; - - memc_ptr= memcached_create(&local_memc); - - rc= memcached_server_add(memc_ptr, memcached_server_name(server), memcached_server_port(server)); - - if (rc == MEMCACHED_SUCCESS) - { - memcached_server_write_instance_st instance= - memcached_server_instance_fetch(memc_ptr, 0); - - rc= memcached_do(instance, execute->buffer, execute->length, true); - - if (rc == MEMCACHED_SUCCESS) - { - rc= memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); - } - } - - memcached_free(memc_ptr); - - return rc; -} - -memcached_return_t memcached_verbosity(memcached_st *ptr, uint32_t verbosity) -{ - int send_length; - memcached_server_fn callbacks[1]; - - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - - send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "verbosity %u\r\n", verbosity); - if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE || send_length < 0) - return MEMCACHED_WRITE_FAILURE; - - struct context_st context = { .length= (size_t)send_length, .buffer= buffer }; - - callbacks[0]= _set_verbosity; - - return memcached_server_cursor(ptr, callbacks, &context, 1); -} diff --git a/libmemcached/verbosity.cc b/libmemcached/verbosity.cc new file mode 100644 index 00000000..ec00b8de --- /dev/null +++ b/libmemcached/verbosity.cc @@ -0,0 +1,97 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ + +#include + +struct context_st +{ + size_t length; + const char *buffer; +}; + +static memcached_return_t _set_verbosity(const memcached_st *ptr, + const memcached_server_st *server, + void *context) +{ + memcached_return_t rc; + memcached_st local_memc; + memcached_st *memc_ptr; + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + + struct context_st *execute= (struct context_st *)context; + (void)ptr; + + memc_ptr= memcached_create(&local_memc); + + rc= memcached_server_add(memc_ptr, memcached_server_name(server), memcached_server_port(server)); + + if (rc == MEMCACHED_SUCCESS) + { + memcached_server_write_instance_st instance= + memcached_server_instance_fetch(memc_ptr, 0); + + rc= memcached_do(instance, execute->buffer, execute->length, true); + + if (rc == MEMCACHED_SUCCESS) + { + rc= memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); + } + } + + memcached_free(memc_ptr); + + return rc; +} + +memcached_return_t memcached_verbosity(memcached_st *ptr, uint32_t verbosity) +{ + int send_length; + memcached_server_fn callbacks[1]; + + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + + send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "verbosity %u\r\n", verbosity); + if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE || send_length < 0) + return MEMCACHED_WRITE_FAILURE; + + struct context_st context = { (size_t)send_length, buffer }; + + callbacks[0]= _set_verbosity; + + return memcached_server_cursor(ptr, callbacks, &context, 1); +} diff --git a/libmemcached/verbosity.h b/libmemcached/verbosity.h index b28458e4..29946486 100644 --- a/libmemcached/verbosity.h +++ b/libmemcached/verbosity.h @@ -1,16 +1,41 @@ -/* LibMemcached - * Copyright (C) 2010 Brian Aker - * All rights reserved. +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker All rights reserved. * - * Summary: Change the verbository level of the memcached server + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. * */ -#ifndef __LIBMEMCACHED_VERBOSITY_H__ -#define __LIBMEMCACHED_VERBOSITY_H__ +#pragma once #ifdef __cplusplus extern "C" { @@ -23,5 +48,3 @@ memcached_return_t memcached_verbosity(memcached_st *ptr, uint32_t verbosity); #ifdef __cplusplus } #endif - -#endif /* __LIBMEMCACHED_VERBOSITY_H__ */ diff --git a/libmemcached/version.c b/libmemcached/version.c deleted file mode 100644 index 82de87d3..00000000 --- a/libmemcached/version.c +++ /dev/null @@ -1,178 +0,0 @@ -#include "common.h" - -const char * memcached_lib_version(void) -{ - return LIBMEMCACHED_VERSION_STRING; -} - -static inline memcached_return_t memcached_version_binary(memcached_st *ptr); -static inline memcached_return_t memcached_version_textual(memcached_st *ptr); - -memcached_return_t memcached_version(memcached_st *ptr) -{ - if (ptr->flags.use_udp) - return MEMCACHED_NOT_SUPPORTED; - - memcached_return_t rc; - - if (ptr->flags.binary_protocol) - rc= memcached_version_binary(ptr); - else - rc= memcached_version_textual(ptr); - - return rc; -} - -static inline memcached_return_t memcached_version_textual(memcached_st *ptr) -{ - size_t send_length; - memcached_return_t rc; - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - char *response_ptr; - const char *command= "version\r\n"; - - send_length= sizeof("version\r\n") -1; - - rc= MEMCACHED_SUCCESS; - for (uint32_t x= 0; x < memcached_server_count(ptr); x++) - { - memcached_return_t rrc; - memcached_server_write_instance_st instance= - memcached_server_instance_fetch(ptr, x); - - // Optimization, we only fetch version once. - if (instance->major_version != UINT8_MAX) - continue; - - rrc= memcached_do(instance, command, send_length, true); - if (rrc != MEMCACHED_SUCCESS) - { - instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX; - rc= MEMCACHED_SOME_ERRORS; - continue; - } - - rrc= memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); - if (rrc != MEMCACHED_SUCCESS) - { - instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX; - rc= MEMCACHED_SOME_ERRORS; - continue; - } - - /* Find the space, and then move one past it to copy version */ - response_ptr= index(buffer, ' '); - response_ptr++; - - instance->major_version= (uint8_t)strtol(response_ptr, (char **)NULL, 10); - if (errno == ERANGE) - { - instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX; - rc= MEMCACHED_SOME_ERRORS; - continue; - } - - response_ptr= index(response_ptr, '.'); - response_ptr++; - - instance->minor_version= (uint8_t)strtol(response_ptr, (char **)NULL, 10); - if (errno == ERANGE) - { - instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX; - rc= MEMCACHED_SOME_ERRORS; - continue; - } - - response_ptr= index(response_ptr, '.'); - response_ptr++; - instance->micro_version= (uint8_t)strtol(response_ptr, (char **)NULL, 10); - if (errno == ERANGE) - { - instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX; - rc= MEMCACHED_SOME_ERRORS; - continue; - } - } - - return rc; -} - -static inline memcached_return_t memcached_version_binary(memcached_st *ptr) -{ - memcached_return_t rc; - protocol_binary_request_version request= { .bytes= {0}}; - request.message.header.request.magic= PROTOCOL_BINARY_REQ; - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_VERSION; - request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; - - rc= MEMCACHED_SUCCESS; - for (uint32_t x= 0; x < memcached_server_count(ptr); x++) - { - memcached_return_t rrc; - - memcached_server_write_instance_st instance= - memcached_server_instance_fetch(ptr, x); - - if (instance->major_version != UINT8_MAX) - continue; - - rrc= memcached_do(instance, request.bytes, sizeof(request.bytes), true); - if (rrc != MEMCACHED_SUCCESS) - { - memcached_io_reset(instance); - rc= MEMCACHED_SOME_ERRORS; - continue; - } - } - - for (uint32_t x= 0; x < memcached_server_count(ptr); x++) - { - memcached_server_write_instance_st instance= - memcached_server_instance_fetch(ptr, x); - - if (instance->major_version != UINT8_MAX) - continue; - - if (memcached_server_response_count(instance) > 0) - { - memcached_return_t rrc; - char buffer[32]; - char *p; - - rrc= memcached_response(instance, buffer, sizeof(buffer), NULL); - if (rrc != MEMCACHED_SUCCESS) - { - memcached_io_reset(instance); - rc= MEMCACHED_SOME_ERRORS; - continue; - } - - instance->major_version= (uint8_t)strtol(buffer, &p, 10); - if (errno == ERANGE) - { - instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX; - rc= MEMCACHED_SOME_ERRORS; - continue; - } - - instance->minor_version= (uint8_t)strtol(p + 1, &p, 10); - if (errno == ERANGE) - { - instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX; - rc= MEMCACHED_SOME_ERRORS; - continue; - } - - instance->micro_version= (uint8_t)strtol(p + 1, NULL, 10); - if (errno == ERANGE) - { - instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX; - rc= MEMCACHED_SOME_ERRORS; - continue; - } - - } - } - - return rc; -} diff --git a/libmemcached/version.cc b/libmemcached/version.cc new file mode 100644 index 00000000..abb72005 --- /dev/null +++ b/libmemcached/version.cc @@ -0,0 +1,214 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. + * + */ +#include + +const char * memcached_lib_version(void) +{ + return LIBMEMCACHED_VERSION_STRING; +} + +static inline memcached_return_t memcached_version_binary(memcached_st *ptr); +static inline memcached_return_t memcached_version_textual(memcached_st *ptr); + +memcached_return_t memcached_version(memcached_st *ptr) +{ + if (ptr->flags.use_udp) + return MEMCACHED_NOT_SUPPORTED; + + memcached_return_t rc; + + if (ptr->flags.binary_protocol) + rc= memcached_version_binary(ptr); + else + rc= memcached_version_textual(ptr); + + return rc; +} + +static inline memcached_return_t memcached_version_textual(memcached_st *ptr) +{ + size_t send_length; + memcached_return_t rc; + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + char *response_ptr; + const char *command= "version\r\n"; + + send_length= sizeof("version\r\n") -1; + + rc= MEMCACHED_SUCCESS; + for (uint32_t x= 0; x < memcached_server_count(ptr); x++) + { + memcached_return_t rrc; + memcached_server_write_instance_st instance= + memcached_server_instance_fetch(ptr, x); + + // Optimization, we only fetch version once. + if (instance->major_version != UINT8_MAX) + continue; + + rrc= memcached_do(instance, command, send_length, true); + if (rrc != MEMCACHED_SUCCESS) + { + instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX; + rc= MEMCACHED_SOME_ERRORS; + continue; + } + + rrc= memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); + if (rrc != MEMCACHED_SUCCESS) + { + instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX; + rc= MEMCACHED_SOME_ERRORS; + continue; + } + + /* Find the space, and then move one past it to copy version */ + response_ptr= index(buffer, ' '); + response_ptr++; + + instance->major_version= (uint8_t)strtol(response_ptr, (char **)NULL, 10); + if (errno == ERANGE) + { + instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX; + rc= MEMCACHED_SOME_ERRORS; + continue; + } + + response_ptr= index(response_ptr, '.'); + response_ptr++; + + instance->minor_version= (uint8_t)strtol(response_ptr, (char **)NULL, 10); + if (errno == ERANGE) + { + instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX; + rc= MEMCACHED_SOME_ERRORS; + continue; + } + + response_ptr= index(response_ptr, '.'); + response_ptr++; + instance->micro_version= (uint8_t)strtol(response_ptr, (char **)NULL, 10); + if (errno == ERANGE) + { + instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX; + rc= MEMCACHED_SOME_ERRORS; + continue; + } + } + + return rc; +} + +static inline memcached_return_t memcached_version_binary(memcached_st *ptr) +{ + memcached_return_t rc; + protocol_binary_request_version request= {}; + request.message.header.request.magic= PROTOCOL_BINARY_REQ; + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_VERSION; + request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; + + rc= MEMCACHED_SUCCESS; + for (uint32_t x= 0; x < memcached_server_count(ptr); x++) + { + memcached_return_t rrc; + + memcached_server_write_instance_st instance= + memcached_server_instance_fetch(ptr, x); + + if (instance->major_version != UINT8_MAX) + continue; + + rrc= memcached_do(instance, request.bytes, sizeof(request.bytes), true); + if (rrc != MEMCACHED_SUCCESS) + { + memcached_io_reset(instance); + rc= MEMCACHED_SOME_ERRORS; + continue; + } + } + + for (uint32_t x= 0; x < memcached_server_count(ptr); x++) + { + memcached_server_write_instance_st instance= + memcached_server_instance_fetch(ptr, x); + + if (instance->major_version != UINT8_MAX) + continue; + + if (memcached_server_response_count(instance) > 0) + { + memcached_return_t rrc; + char buffer[32]; + char *p; + + rrc= memcached_response(instance, buffer, sizeof(buffer), NULL); + if (rrc != MEMCACHED_SUCCESS) + { + memcached_io_reset(instance); + rc= MEMCACHED_SOME_ERRORS; + continue; + } + + instance->major_version= (uint8_t)strtol(buffer, &p, 10); + if (errno == ERANGE) + { + instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX; + rc= MEMCACHED_SOME_ERRORS; + continue; + } + + instance->minor_version= (uint8_t)strtol(p + 1, &p, 10); + if (errno == ERANGE) + { + instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX; + rc= MEMCACHED_SOME_ERRORS; + continue; + } + + instance->micro_version= (uint8_t)strtol(p + 1, NULL, 10); + if (errno == ERANGE) + { + instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX; + rc= MEMCACHED_SOME_ERRORS; + continue; + } + + } + } + + return rc; +} diff --git a/libmemcached/version.h b/libmemcached/version.h index 7e6f607a..c443accb 100644 --- a/libmemcached/version.h +++ b/libmemcached/version.h @@ -1,16 +1,41 @@ -/* LibMemcached - * Copyright (C) 2010 Brian Aker - * All rights reserved. +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Libmemcached library * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2010 Brian Aker All rights reserved. * - * Summary: Find version information + * 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. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * 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. * */ -#ifndef __LIBMEMCACHED_VERSION_H__ -#define __LIBMEMCACHED_VERSION_H__ +#pragma once #ifdef __cplusplus extern "C" { @@ -25,5 +50,3 @@ const char * memcached_lib_version(void); #ifdef __cplusplus } #endif - -#endif /* __LIBMEMCACHED_VERSION_H__ */ diff --git a/libmemcached/visibility.h b/libmemcached/visibility.h index 7d9af058..646806ac 100644 --- a/libmemcached/visibility.h +++ b/libmemcached/visibility.h @@ -16,8 +16,7 @@ * @brief Visibility control macros */ -#ifndef __LIBMEMCACHED_VISIBILITY_H__ -#define __LIBMEMCACHED_VISIBILITY_H__ +#pragma once /** * @@ -50,5 +49,3 @@ # define LIBMEMCACHED_LOCAL # endif /* defined(_MSC_VER) */ #endif /* defined(BUILDING_LIBMEMCACHED) */ - -#endif /* __LIBMEMCACHED_VISIBILITY_H__ */ diff --git a/libtest/test.c b/libtest/test.c index 9a79e4c5..afb2b965 100644 --- a/libtest/test.c +++ b/libtest/test.c @@ -24,8 +24,6 @@ #include #include -#include - #include #include @@ -370,9 +368,5 @@ cleanup: world_stats_print(&stats); -#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT - sasl_done(); -#endif - return stats.failed == 0 ? 0 : 1; } diff --git a/libtest/test.h b/libtest/test.h index 58d24373..656be169 100644 --- a/libtest/test.h +++ b/libtest/test.h @@ -217,6 +217,17 @@ do \ } \ } while (0) +#define test_compare(A,B) \ +do \ +{ \ + if ((A) != (B)) \ + { \ + fprintf(stderr, "\n%s:%d: Expected %lu == %lu\n", __FILE__, __LINE__, (unsigned long)(A), (unsigned long)(B)); \ + create_core(); \ + return TEST_FAILURE; \ + } \ +} while (0) + #define test_false(A) \ do \ { \ @@ -248,6 +259,16 @@ do \ } \ } while (0) +#define test_memcmp(A,B,C) \ +do \ +{ \ + if (memcmp((A), (B), (C))) \ + { \ + fprintf(stderr, "\n%s:%d: %.*s -> %.*s\n", __FILE__, __LINE__, (int)(C), (char *)(A), (int)(C), (char *)(B)); \ + create_core(); \ + return TEST_FAILURE; \ + } \ +} while (0) #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) diff --git a/tests/atomsmasher.c b/tests/atomsmasher.c index afdf0830..8563e4ec 100644 --- a/tests/atomsmasher.c +++ b/tests/atomsmasher.c @@ -26,8 +26,8 @@ #include #include #include -#include "../clients/generator.h" -#include "../clients/execute.h" +#include +#include #include #include diff --git a/tests/hash_plus.cc b/tests/hash_plus.cc index 140d28fc..20cf79db 100644 --- a/tests/hash_plus.cc +++ b/tests/hash_plus.cc @@ -1,13 +1,16 @@ /* C++ to libhashkit */ + +#include + #include #include #include #include -#include +#include #include "hash_results.h" diff --git a/tests/include.am b/tests/include.am index 2a3e5cb8..44954b11 100644 --- a/tests/include.am +++ b/tests/include.am @@ -67,18 +67,23 @@ tests_testapp_SOURCES= \ tests_testapp_DEPENDENCIES= \ $(BUILT_SOURCES) \ + $(TESTS_LDADDS) \ clients/libgenexec.la \ - libmemcached/libmemcachedinternal.la \ - $(TESTS_LDADDS) + libhashkit/libhashkit.la \ + libmemcached/libmemcachedinternal.la -tests_testapp_LDADD= clients/libgenexec.la \ - libmemcached/libmemcachedinternal.la \ - $(TESTS_LDADDS) $(LIBSASL) +tests_testapp_LDADD= \ + $(LIBSASL) \ + $(TESTS_LDADDS) \ + clients/libgenexec.la \ + libhashkit/libhashkit.la \ + libmemcached/libmemcachedinternal.la tests_testplus_SOURCES= tests/plus.cpp tests_testplus_CXXFLAGS = $(AM_CXXFLAGS) $(NO_EFF_CXX) tests_testplus_DEPENDENCIES= $(TESTS_LDADDS) tests_testplus_LDADD= $(tests_testplus_DEPENDENCIES) $(LIBSASL) +check_PROGRAMS+= tests/testplus tests_atomsmasher_SOURCES= tests/atomsmasher.c tests_atomsmasher_DEPENDENCIES= \ @@ -101,12 +106,12 @@ tests_startservers_LDADD= $(tests_startservers_DEPENDENCIES) $(LIBSASL) tests_testhashkit_SOURCES = tests/hashkit_functions.c tests_testhashkit_DEPENDENCIES = libtest/libtest.la libhashkit/libhashkit.la -tests_testhashkit_LDADD = $(tests_testhashkit_DEPENDENCIES) $(LIBSASL) +tests_testhashkit_LDADD = $(tests_testhashkit_DEPENDENCIES) -tests_hash_plus_SOURCES = tests/hash_plus.cc -tests_hash_plus_CXXFLAGS = $(AM_CXXFLAGS) $(NO_EFF_CXX) -tests_hash_plus_DEPENDENCIES = $(tests_testhashkit_DEPENDENCIES) -tests_hash_plus_LDADD = $(tests_testhashkit_DEPENDENCIES) $(LIBSASL) +tests_hash_plus_SOURCES= tests/hash_plus.cc +tests_hash_plus_CXXFLAGS= $(AM_CXXFLAGS) $(NO_EFF_CXX) +tests_hash_plus_DEPENDENCIES= $(tests_testhashkit_DEPENDENCIES) +tests_hash_plus_LDADD= $(tests_testhashkit_DEPENDENCIES) check_PROGRAMS+= tests/hash_plus test: check diff --git a/tests/libmemcached_world.h b/tests/libmemcached_world.h index 5a5e15b5..4793569c 100644 --- a/tests/libmemcached_world.h +++ b/tests/libmemcached_world.h @@ -133,6 +133,10 @@ test_return_t world_destroy(libmemcached_test_container_st *container) server_shutdown(construct); +#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT + sasl_done(); +#endif + return TEST_SUCCESS; } diff --git a/tests/mem_functions.c b/tests/mem_functions.c index 1ab8d6a3..0400ce5a 100644 --- a/tests/mem_functions.c +++ b/tests/mem_functions.c @@ -767,8 +767,10 @@ static test_return_t flush_test(memcached_st *memc) { memcached_return_t rc; + uint64_t query_id= memcached_query_id(memc); rc= memcached_flush(memc, 0); - test_true(rc == MEMCACHED_SUCCESS); + test_compare(rc, MEMCACHED_SUCCESS); + test_compare(query_id +1, memcached_query_id(memc)); return TEST_SUCCESS; } @@ -806,18 +808,23 @@ static test_return_t bad_key_test(memcached_st *memc) size_t max_keylen= 0xffff; // Just skip if we are in binary mode. + uint64_t query_id= memcached_query_id(memc); if (memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL)) return TEST_SKIPPED; + test_compare(query_id, memcached_query_id(memc)); // We should not increase the query_id for memcached_behavior_get() memc_clone= memcached_clone(NULL, memc); test_true(memc_clone); + query_id= memcached_query_id(memc_clone); rc= memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_VERIFY_KEY, set); test_true(rc == MEMCACHED_SUCCESS); + test_compare(query_id, memcached_query_id(memc_clone)); // We should not increase the query_id for memcached_behavior_set() /* All keys are valid in the binary protocol (except for length) */ if (memcached_behavior_get(memc_clone, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL) == 0) { + query_id= memcached_query_id(memc_clone); string= memcached_get(memc_clone, key, strlen(key), &string_length, &flags, &rc); test_true(rc == MEMCACHED_BAD_KEY_PROVIDED); @@ -825,7 +832,9 @@ static test_return_t bad_key_test(memcached_st *memc) test_true(!string); set= 0; + query_id= memcached_query_id(memc_clone); rc= memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_VERIFY_KEY, set); + test_compare(query_id, memcached_query_id(memc_clone)); // We should not increase the query_id for memcached_behavior_set() test_true(rc == MEMCACHED_SUCCESS); string= memcached_get(memc_clone, key, strlen(key), &string_length, &flags, &rc); @@ -837,14 +846,20 @@ static test_return_t bad_key_test(memcached_st *memc) const char *keys[] = { "GoodKey", "Bad Key", "NotMine" }; size_t key_lengths[] = { 7, 7, 7 }; set= 1; + query_id= memcached_query_id(memc_clone); rc= memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_VERIFY_KEY, set); test_true(rc == MEMCACHED_SUCCESS); + test_compare(query_id, memcached_query_id(memc_clone)); + query_id= memcached_query_id(memc_clone); rc= memcached_mget(memc_clone, keys, key_lengths, 3); test_true(rc == MEMCACHED_BAD_KEY_PROVIDED); + test_compare(query_id +1, memcached_query_id(memc_clone)); + query_id= memcached_query_id(memc_clone); rc= memcached_mget_by_key(memc_clone, "foo daddy", 9, keys, key_lengths, 1); test_true(rc == MEMCACHED_BAD_KEY_PROVIDED); + test_compare(query_id +1, memcached_query_id(memc_clone)); max_keylen= 250; @@ -970,8 +985,10 @@ static test_return_t get_test(memcached_st *memc) size_t string_length; uint32_t flags; + uint64_t query_id= memcached_query_id(memc); rc= memcached_delete(memc, key, strlen(key), (time_t)0); test_true(rc == MEMCACHED_BUFFERED || rc == MEMCACHED_NOTFOUND); + test_compare(query_id +1, memcached_query_id(memc)); string= memcached_get(memc, key, strlen(key), &string_length, &flags, &rc); @@ -992,18 +1009,22 @@ static test_return_t get_test2(memcached_st *memc) size_t string_length; uint32_t flags; + uint64_t query_id= memcached_query_id(memc); rc= memcached_set(memc, key, strlen(key), value, strlen(value), (time_t)0, (uint32_t)0); test_true(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED); + test_compare(query_id +1, memcached_query_id(memc)); + query_id= memcached_query_id(memc); string= memcached_get(memc, key, strlen(key), &string_length, &flags, &rc); + test_compare(query_id +1, memcached_query_id(memc)); test_true(string); test_true(rc == MEMCACHED_SUCCESS); test_true(string_length == strlen(value)); - test_true(!memcmp(string, value, string_length)); + test_memcmp(string, value, string_length); free(string); @@ -1034,25 +1055,26 @@ static test_return_t set_test3(memcached_st *memc) memcached_return_t rc; char *value; size_t value_length= 8191; - unsigned int x; value = (char*)malloc(value_length); test_true(value); - for (x= 0; x < value_length; x++) + for (uint32_t x= 0; x < value_length; x++) value[x] = (char) (x % 127); /* The dump test relies on there being at least 32 items in memcached */ - for (x= 0; x < 32; x++) + for (uint32_t x= 0; x < 32; x++) { char key[16]; snprintf(key, sizeof(key), "foo%u", x); + uint64_t query_id= memcached_query_id(memc); rc= memcached_set(memc, key, strlen(key), value, value_length, (time_t)0, (uint32_t)0); test_true(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED); + test_compare(query_id +1, memcached_query_id(memc)); } free(value); @@ -1720,8 +1742,10 @@ static test_return_t mget_execute(memcached_st *memc) key_length[x]= (size_t)snprintf(k, sizeof(k), "0200%lu", (unsigned long)x); keys[x]= strdup(k); test_true(keys[x] != NULL); + uint64_t query_id= memcached_query_id(memc); rc= memcached_add(memc, keys[x], key_length[x], blob, sizeof(blob), 0, 0); test_true(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED); + test_compare(query_id +1, memcached_query_id(memc)); } /* Try to get all of them with a large multiget */ @@ -1733,8 +1757,10 @@ static test_return_t mget_execute(memcached_st *memc) if (rc == MEMCACHED_SUCCESS) { test_true(binary); + uint64_t query_id= memcached_query_id(memc); rc= memcached_fetch_execute(memc, callbacks, (void *)&counter, 1); test_true(rc == MEMCACHED_END); + test_compare(query_id, memcached_query_id(memc)); /* Verify that we got all of the items */ test_true(counter == max_keys); @@ -2257,7 +2283,7 @@ static test_return_t user_supplied_bug4(memcached_st *memc) /* We need to empty the server before continueing test */ rc= memcached_flush(memc, 0); - test_true(rc == MEMCACHED_NO_SERVERS); + test_compare(rc, MEMCACHED_NO_SERVERS); rc= memcached_mget(memc, keys, key_length, 3); test_true(rc == MEMCACHED_NO_SERVERS); @@ -2267,7 +2293,7 @@ static test_return_t user_supplied_bug4(memcached_st *memc) { test_true(return_value); } - test_true(!return_value); + test_false(return_value); test_true(return_value_length == 0); test_true(rc == MEMCACHED_NO_SERVERS); @@ -2289,7 +2315,7 @@ static test_return_t user_supplied_bug4(memcached_st *memc) test_true(return_value); test_true(rc == MEMCACHED_SUCCESS); test_true(return_key_length == return_value_length); - test_true(!memcmp(return_value, return_key, return_value_length)); + test_memcmp(return_value, return_key, return_value_length); free(return_value); x++; } @@ -6295,7 +6321,7 @@ collection_st collection[] ={ {0, 0, 0, 0} }; -#include "libmemcached_world.h" +#include "tests/libmemcached_world.h" void get_world(world_st *world) { diff --git a/tests/plus.cpp b/tests/plus.cpp index c0c6bb95..c4b67115 100644 --- a/tests/plus.cpp +++ b/tests/plus.cpp @@ -1,17 +1,16 @@ /* C++ interface test */ -#include "libmemcached/memcached.hpp" +#include -#include -#include -#include -#include +#include +#include +#include #include #include #include #include -#include +#include #include @@ -44,7 +43,7 @@ static void populate_vector(vector &vec, const string &str) static void copy_vec_to_string(vector &vec, string &str) { str.clear(); - if (! vec.empty()) + if (not vec.empty()) { str.assign(vec.begin(), vec.end()); } @@ -59,31 +58,18 @@ test_return_t basic_test(memcached_st *memc) populate_vector(value, value_set); - foo.set("mine", value, 0, 0); - foo.get("mine", test_value); + test_true(foo.set("mine", value, 0, 0)); + test_true(foo.get("mine", test_value)); - assert((memcmp(&test_value[0], &value[0], test_value.size()) == 0)); + test_memcmp(&test_value[0], &value[0], test_value.size()); + test_false(foo.set("", value, 0, 0)); - /* - * Simple test of the exceptions here...this should throw an exception - * saying that the key is empty. - */ - try - { - foo.set("", value, 0, 0); - } - catch (Error &err) - { - return TEST_SUCCESS; - } - - return TEST_FAILURE; + return TEST_SUCCESS; } -test_return_t increment_test(memcached_st *memc) +test_return_t increment_test(memcached_st *original) { - Memcache mcach(memc); - bool rc; + Memcache mcach(original); const string key("blah"); const string inc_value("1"); std::vector inc_val; @@ -94,40 +80,31 @@ test_return_t increment_test(memcached_st *memc) populate_vector(inc_val, inc_value); - rc= mcach.set(key, inc_val, 0, 0); - if (rc == false) - { - return TEST_FAILURE; - } - mcach.get(key, ret_value); - if (ret_value.empty()) - { - return TEST_FAILURE; - } + test_true(mcach.set(key, inc_val, 0, 0)); + + test_true(mcach.get(key, ret_value)); + test_false(ret_value.empty()); copy_vec_to_string(ret_value, ret_string); int_inc_value= uint64_t(atol(inc_value.c_str())); int_ret_value= uint64_t(atol(ret_string.c_str())); - assert(int_ret_value == int_inc_value); + test_compare(int_inc_value, int_ret_value); - rc= mcach.increment(key, 1, &int_ret_value); - assert(rc == true); - assert(int_ret_value == 2); + test_true(mcach.increment(key, 1, &int_ret_value)); + test_compare(2, int_ret_value); - rc= mcach.increment(key, 1, &int_ret_value); - assert(rc == true); - assert(int_ret_value == 3); + test_true(mcach.increment(key, 1, &int_ret_value)); + test_compare(3, int_ret_value); - rc= mcach.increment(key, 5, &int_ret_value); - assert(rc == true); - assert(int_ret_value == 8); + test_true(mcach.increment(key, 5, &int_ret_value)); + test_compare(8, int_ret_value); return TEST_SUCCESS; } -test_return_t basic_master_key_test(memcached_st *memc) +test_return_t basic_master_key_test(memcached_st *original) { - Memcache foo(memc); + Memcache foo(original); const string value_set("Data for server A"); vector value; vector test_value; @@ -140,12 +117,12 @@ test_return_t basic_master_key_test(memcached_st *memc) foo.setByKey(master_key_a, key, value, 0, 0); foo.getByKey(master_key_a, key, test_value); - assert((memcmp(&value[0], &test_value[0], value.size()) == 0)); + test_true((memcmp(&value[0], &test_value[0], value.size()) == 0)); test_value.clear(); foo.getByKey(master_key_b, key, test_value); - assert((memcmp(&value[0], &test_value[0], value.size()) == 0)); + test_true((memcmp(&value[0], &test_value[0], value.size()) == 0)); return TEST_SUCCESS; } @@ -162,53 +139,9 @@ memcached_return_t callback_counter(const memcached_st *, return MEMCACHED_SUCCESS; } -test_return_t mget_result_function(memcached_st *memc) -{ - Memcache mc(memc); - bool rc; - string key1("fudge"); - string key2("son"); - string key3("food"); - vector keys; - vector< vector *> values; - vector val1; - vector val2; - vector val3; - populate_vector(val1, key1); - populate_vector(val2, key2); - populate_vector(val3, key3); - keys.reserve(3); - keys.push_back(key1); - keys.push_back(key2); - keys.push_back(key3); - values.reserve(3); - values.push_back(&val1); - values.push_back(&val2); - values.push_back(&val3); - unsigned int counter; - memcached_execute_fn callbacks[1]; - - /* We need to empty the server before we continue the test */ - rc= mc.flush(0); - rc= mc.setAll(keys, values, 50, 9); - assert(rc == true); - - rc= mc.mget(keys); - assert(rc == true); - - callbacks[0]= &callback_counter; - counter= 0; - rc= mc.fetchExecute(callbacks, static_cast(&counter), 1); - - assert(counter == 3); - - return TEST_SUCCESS; -} - -test_return_t mget_test(memcached_st *memc) +test_return_t mget_test(memcached_st *original) { - Memcache mc(memc); - bool rc; + Memcache memc(original); memcached_return_t mc_rc; vector keys; vector< vector *> values; @@ -231,43 +164,37 @@ test_return_t mget_test(memcached_st *memc) vector return_value; /* We need to empty the server before we continue the test */ - rc= mc.flush(0); - assert(rc == true); + test_true(memc.flush(0)); - rc= mc.mget(keys); - assert(rc == true); + test_true(memc.mget(keys)); - while ((mc_rc= mc.fetch(return_key, return_value)) != MEMCACHED_END) + while ((mc_rc= memc.fetch(return_key, return_value)) != MEMCACHED_END) { - assert(return_value.size() != 0); + test_true(return_value.size()); return_value.clear(); } - assert(mc_rc == MEMCACHED_END); + test_compare(mc_rc, MEMCACHED_END); - rc= mc.setAll(keys, values, 50, 9); - assert(rc == true); + test_true(memc.setAll(keys, values, 50, 9)); - rc= mc.mget(keys); - assert(rc == true); + test_true(memc.mget(keys)); - while ((mc_rc= mc.fetch(return_key, return_value)) != MEMCACHED_END) + while ((mc_rc= memc.fetch(return_key, return_value)) != MEMCACHED_END) { - assert(return_key.length() == return_value.size()); - assert(!memcmp(&return_value[0], return_key.c_str(), return_value.size())); + test_compare(return_key.length(), return_value.size()); + test_memcmp(&return_value[0], return_key.c_str(), return_value.size()); } return TEST_SUCCESS; } -test_return_t basic_behavior(memcached_st *memc) +test_return_t basic_behavior(memcached_st *original) { - Memcache mc(memc); - bool rc; - uint64_t value = 1; - rc = mc.setBehavior(MEMCACHED_BEHAVIOR_VERIFY_KEY, value); - assert(rc); - uint64_t behavior = mc.getBehavior(MEMCACHED_BEHAVIOR_VERIFY_KEY); - assert(behavior == value); + Memcache memc(original); + uint64_t value= 1; + test_true(memc.setBehavior(MEMCACHED_BEHAVIOR_VERIFY_KEY, value)); + uint64_t behavior= memc.getBehavior(MEMCACHED_BEHAVIOR_VERIFY_KEY); + test_compare(behavior, value); return TEST_SUCCESS; } @@ -281,8 +208,6 @@ test_st tests[] ={ reinterpret_cast(increment_test) }, { "mget", 1, reinterpret_cast(mget_test) }, - { "mget_result_function", 1, - reinterpret_cast(mget_result_function) }, { "basic_behavior", 0, reinterpret_cast(basic_behavior) }, {0, 0, 0} diff --git a/tests/string.cc b/tests/string.cc index 35d6eb41..aa8a7d6f 100644 --- a/tests/string.cc +++ b/tests/string.cc @@ -35,9 +35,11 @@ * */ -#include "libmemcached/common.h" -#include "libmemcached/error.h" -#include "tests/string.h" +#define BUILDING_LIBMEMCACHED + +#include +#include +#include test_return_t string_static_null(memcached_st *memc) {