Merge in code changes for all of the new parser.
authorBrian Aker <brian@tangent.org>
Fri, 25 Mar 2011 05:22:45 +0000 (22:22 -0700)
committerBrian Aker <brian@tangent.org>
Fri, 25 Mar 2011 05:22:45 +0000 (22:22 -0700)
71 files changed:
.bzrignore
ChangeLog
Makefile.am
clients/include.am
clients/memparse.cc [new file with mode: 0644]
configure.ac
libhashkit/function.c
libhashkit/strerror.c
libhashkit/types.h
libmemcached/array.c [new file with mode: 0644]
libmemcached/array.h [new file with mode: 0644]
libmemcached/auto.c
libmemcached/auto.h
libmemcached/behavior.c
libmemcached/behavior.h
libmemcached/callback.c
libmemcached/common.h
libmemcached/constants.h
libmemcached/delete.c
libmemcached/error.c [new file with mode: 0644]
libmemcached/error.h [new file with mode: 0644]
libmemcached/get.c
libmemcached/hash.c
libmemcached/hash.h
libmemcached/hosts.c
libmemcached/include.am
libmemcached/is.h [new file with mode: 0644]
libmemcached/memcached.c
libmemcached/memcached.h
libmemcached/options.cc [new file with mode: 0644]
libmemcached/options.h [new file with mode: 0644]
libmemcached/options/context.h [new file with mode: 0644]
libmemcached/options/include.am [new file with mode: 0644]
libmemcached/options/lex_string.h [new file with mode: 0644]
libmemcached/options/parser.yy [new file with mode: 0644]
libmemcached/options/scanner.l [new file with mode: 0644]
libmemcached/options/server.h [new file with mode: 0644]
libmemcached/options/string.h [new file with mode: 0644]
libmemcached/options/symbol.h [new file with mode: 0644]
libmemcached/parse.h
libmemcached/response.c
libmemcached/server.h
libmemcached/stats.c
libmemcached/storage.c
libmemcached/strerror.c
libmemcached/string.c
libmemcached/string.h
libmemcached/types.h
libtest/failed.cc [new file with mode: 0644]
libtest/failed.h [new file with mode: 0644]
libtest/include.am
libtest/server.c
libtest/test.c
libtest/test.h
libtest/visibility.h [new file with mode: 0644]
m4/pandora_warnings.m4
support/example.cnf [new file with mode: 0644]
support/libmemcached.spec.in
tests/basic.cc [new file with mode: 0644]
tests/basic.h [new file with mode: 0644]
tests/error_conditions.cc [new file with mode: 0644]
tests/error_conditions.h [new file with mode: 0644]
tests/hashkit_functions.c
tests/include.am
tests/mem_functions.c
tests/parser.cc [new file with mode: 0644]
tests/parser.h [new file with mode: 0644]
tests/print.cc [new file with mode: 0644]
tests/print.h [new file with mode: 0644]
tests/replication.cc [new file with mode: 0644]
tests/replication.h [new file with mode: 0644]

index 87578c4bc01c3468780834952bfb83eb0bd25a10..e7ff2911aa8964063c72901bc0cd7ed1c8b03a95 100644 (file)
@@ -1,6 +1,7 @@
 *.exe
 *.lo
 *.pop
+*.rpm
 */*.l[oa]
 */*/*.l[oa]
 */*/.deps
@@ -40,6 +41,7 @@ config/ltmain.sh
 config/missing
 config/pandora_vc_revinfo
 config/plugin.ac
+config/top.h
 configure
 docs/*.[13]
 docs/*.html
@@ -73,7 +75,16 @@ libmemcached-?.??/
 libmemcached.pop
 libmemcached/configure.h
 libmemcached/dtrace_probes.h
+libmemcached/generated_probes.h
 libmemcached/memcached_configure.h
+libmemcached/options/parser.cc
+libmemcached/options/parser.h
+libmemcached/options/parser.output
+libmemcached/options/parsers.cc
+libmemcached/options/parsers.h
+libmemcached/options/parsers.output
+libmemcached/options/scanner.cc
+libmemcached/options/scanner.h
 libmemcached_examples.pop
 libmemcachedutil.pop
 libtool
@@ -185,5 +196,4 @@ tests/testhashkit
 tests/testplus
 tests/testudp
 unittests/unittests
-config/top.h
-libmemcached/generated_probes.h
+clients/memparse
index 538463c4f3b65279247fbb5dc7f3f4105e83ad50..9c0bea9ccff6f3769ce1fad28d73f381faa11560 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+0.49
+  * Fix calls to auto methods so that if value is not passed in nothing bad happens.
+  * New parser calls for generating memcached_st objects.
+  * New error system.
+
 0.48 Tue Mar 15 23:05:18 PDT 2011
   * Fix memory leak in server parse.
   * Move test framework out to be its own library (easier to work with Gearman).
index 31585120cae1d8ff2ec87602823017247202e72e..7a09b51f35a9fff850a171aa7e13cc33eb20430e 100644 (file)
@@ -15,6 +15,7 @@ nobase_include_HEADERS =
 check_PROGRAMS =
 EXTRA_HEADERS =
 BUILT_SOURCES=
+DISTCLEANFILES=
 EXTRA_DIST= \
            ${srcdir}/m4/pandora_*.m4 \
            .quickly \
index a709e38df6c979e6f28a4657e500a8a4b6bfd6b7..f7503754649028eb6ae48ef96911e808dcb2bade 100644 (file)
@@ -18,6 +18,7 @@ bin_PROGRAMS+= \
        clients/memdump \
        clients/memerror \
        clients/memflush \
+       clients/memparse \
        clients/memrm \
        clients/memstat
 
@@ -50,6 +51,9 @@ clients_libgenexec_la_SOURCES= clients/generator.c clients/execute.c
 clients_memcat_SOURCES= clients/memcat.c
 clients_memcat_LDADD= $(CLIENTS_LDADDS)
 
+clients_memparse_SOURCES= clients/memparse.cc
+clients_memparse_LDADD= $(CLIENTS_LDADDS)
+
 clients_memcp_SOURCES= clients/memcp.c
 clients_memcp_LDADD= $(CLIENTS_LDADDS)
 
diff --git a/clients/memparse.cc b/clients/memparse.cc
new file mode 100644 (file)
index 0000000..2c80fcd
--- /dev/null
@@ -0,0 +1,68 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Libmemcached library
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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 <config.h>
+
+#include <iostream>
+
+#include <libmemcached/memcached.h>
+
+int main(int argc, char *argv[])
+{
+
+  if (argc < 2)
+  {
+    std::cerr << "No arguments provided." << std::endl;
+    return EXIT_FAILURE;
+  }
+
+  for (int x= 1; x < argc; x++)
+  {
+    char buffer[BUFSIZ];
+    memcached_return_t rc;
+    rc= libmemcached_check_configuration(argv[x], strlen(argv[x]), buffer, sizeof(buffer));
+
+    if (rc != MEMCACHED_SUCCESS)
+    {
+      std::cerr << "Failed to parse argument #" << x << " " << argv[x] << std::endl;
+      std::cerr << "Error message from parser was:\t" << buffer << std::endl;
+      return EXIT_FAILURE;
+    }
+  }
+
+  return EXIT_SUCCESS;
+}
index c3b4300f7a23fce36d8e51050d1b94efff08615c..9349b1f4756ad22974c6e36660975a1fea840024 100644 (file)
@@ -1,5 +1,6 @@
 #!/usr/bin/env bash
-# libmemcached
+# LibmemcacheD
+# Copyright (C) 2011 Data Differential, http://datadifferential.com/
 # Copyright (C) 2006-2010 Brian Aker, Monty Taylor, Trond Norbye
 # All rights reserved.
 #
@@ -12,6 +13,8 @@ AC_CONFIG_SRCDIR([libmemcached/memcached.c])
 AC_CONFIG_AUX_DIR(config)
 
 PANDORA_CANONICAL_TARGET(no-vc-changelog)
+AC_CHECK_PROGS([YACC], ['bison'], [:])
+AC_CHECK_PROGS([LEX], ['flex'], [:])
 
 #shared library versioning
 MEMCACHED_UTIL_LIBRARY_VERSION=1:0:0
@@ -32,7 +35,7 @@ AC_SUBST(MEMCACHED_PROTOCAL_LIBRARY_VERSION)
 AC_SUBST(MEMCACHED_LIBRARY_VERSION)
 
 
-HASHKIT_LIBRARY_VERSION=0:0:0
+HASHKIT_LIBRARY_VERSION=1:0:0
 AC_SUBST(HASHKIT_LIBRARY_VERSION)
 
 AH_TOP([
@@ -102,7 +105,6 @@ my_saved_libs="$LIBS"
 LIBS=
 PANDORA_REQUIRE_PTHREAD
 LIBS="$my_saved_libs"
-PANDORA_CXX_DEMANGLE
 
 dnl Specialty checks
 DETECT_BYTEORDER
index 2e68b58365c8506413cfa0010b7043ae2002c1fe..a779bb63ff51b003515c07216e4e02c74780db52 100644 (file)
@@ -51,9 +51,10 @@ static hashkit_return_t _set_function(struct hashkit_function_st *self, hashkit_
     self->function= hashkit_jenkins;
     break;    
   case HASHKIT_HASH_CUSTOM:
+    return HASHKIT_INVALID_ARGUMENT;
   case HASHKIT_HASH_MAX:
   default:
-    return HASHKIT_FAILURE;
+    return HASHKIT_INVALID_HASH;
   }
 
   self->context= NULL;
index ac51f99650833ba0ebb418e95680c05a86f39e28..50532e1dbffc5d08bd8f9fb89b230927e83424ab 100644 (file)
@@ -13,15 +13,13 @@ 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_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:
-    return "Gibberish returned!";
   default:
-    return "Gibberish returned!";
+    return "INVALID hashkit_return_t";
   }
 }
index 8d39696227d61f1ca97f8acfe8236e60fc50d178..255620b838ed529a4fcc1ddce12370c4183a6785 100644 (file)
@@ -19,6 +19,8 @@ typedef enum {
   HASHKIT_SUCCESS,
   HASHKIT_FAILURE,
   HASHKIT_MEMORY_ALLOCATION_FAILURE,
+  HASHKIT_INVALID_HASH,
+  HASHKIT_INVALID_ARGUMENT,
   HASHKIT_MAXIMUM_RETURN /* Always add new error code before */
 } hashkit_return_t;
 
diff --git a/libmemcached/array.c b/libmemcached/array.c
new file mode 100644 (file)
index 0000000..2412369
--- /dev/null
@@ -0,0 +1,109 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  LibMemcached
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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"
+
+struct memcached_array_st
+{
+  memcached_st *root;
+  size_t size;
+  char c_str[];
+};
+
+memcached_array_st *memcached_array_clone(memcached_st *memc, const memcached_array_st *original)
+{
+  if (! original)
+    return NULL;
+
+  return memcached_strcpy(memc, original->c_str, original->size);
+}
+
+memcached_array_st *memcached_strcpy(memcached_st *memc, const char *str, size_t str_length)
+{
+  memcached_array_st *array= (struct memcached_array_st *)libmemcached_malloc(memc, sizeof(struct memcached_array_st) +str_length +1);
+
+  if (! array)
+    return NULL;
+
+  array->root= memc;
+  array->size= str_length -1; // We don't count the NULL ending
+  memcpy(array->c_str, str, str_length);
+  array->c_str[str_length]= 0;
+
+  return array;
+}
+
+memcached_string_t memcached_array_to_string(memcached_array_st *array)
+{
+  memcached_string_t tmp;
+  tmp.c_str= array->c_str;
+  tmp.size= array->size;
+
+  return tmp;
+}
+
+void memcached_array_free(memcached_array_st *array)
+{
+  if (! array)
+    return;
+
+  WATCHPOINT_ASSERT(array->root);
+  if (array && array->root)
+  {
+    libmemcached_free(array->root, array);
+  }
+  else if (array)
+  {
+    free(array);
+  }
+}
+
+size_t memcached_array_size(memcached_array_st *array)
+{
+  if (! array)
+    return 0;
+
+  return array->size;
+}
+
+const char *memcached_array_string(memcached_array_st *array)
+{
+  if (! array)
+    return NULL;
+
+  return array->c_str;
+}
diff --git a/libmemcached/array.h b/libmemcached/array.h
new file mode 100644 (file)
index 0000000..ddc97d5
--- /dev/null
@@ -0,0 +1,72 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  LibMemcached
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LIBMEMCACHED_LOCAL
+memcached_array_st *memcached_array_clone(memcached_st *memc, const memcached_array_st *original);
+
+LIBMEMCACHED_LOCAL
+memcached_array_st *memcached_strcpy(memcached_st *memc, const char *str, size_t str_length);
+
+LIBMEMCACHED_LOCAL
+void memcached_array_free(memcached_array_st *array);
+
+LIBMEMCACHED_LOCAL
+size_t memcached_array_size(memcached_array_st *array);
+
+LIBMEMCACHED_LOCAL
+const char *memcached_array_string(memcached_array_st *array);
+
+LIBMEMCACHED_LOCAL
+memcached_string_t memcached_array_to_string(memcached_array_st *array);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#ifdef __cplusplus
+#define memcached_print_array(X) static_cast<int>(memcached_array_size(X)), memcached_array_string(X)
+#define memcached_param_array(X) memcached_array_string(X), memcached_array_size(X)
+#else
+#define memcached_print_array(X) (int)memcached_array_size((X)), memcached_array_string((X))
+#define memcached_param_array(X) memcached_array_string(X), memcached_array_size(X)
+#endif
index a221144f06a75851e39d2779a1c4cad1ddf09d4e..7bd0cc12518c100521466635c4f85f3901e967df 100644 (file)
@@ -9,7 +9,7 @@
  *
  */
 
-#include "common.h"
+#include "libmemcached/common.h"
 
 static memcached_return_t text_incr_decr(memcached_st *ptr,
                                          const char *verb,
@@ -24,8 +24,8 @@ static memcached_return_t text_incr_decr(memcached_st *ptr,
   memcached_server_write_instance_st instance;
   bool no_reply= ptr->flags.no_reply;
 
-  unlikely (memcached_server_count(ptr) == 0)
-    return MEMCACHED_NO_SERVERS;
+  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;
@@ -36,8 +36,7 @@ static memcached_return_t text_incr_decr(memcached_st *ptr,
   int send_length;
   send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE,
                         "%s %.*s%.*s %" PRIu64 "%s\r\n", verb,
-                        (int)ptr->prefix_key_length,
-                        ptr->prefix_key,
+                        memcached_print_array(ptr->prefix_key),
                         (int)key_length, key,
                         offset, no_reply ? " noreply" : "");
   if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE || send_length < 0)
@@ -91,8 +90,8 @@ static memcached_return_t binary_incr_decr(memcached_st *ptr, uint8_t cmd,
   memcached_server_write_instance_st instance;
   bool no_reply= ptr->flags.no_reply;
 
-  unlikely (memcached_server_count(ptr) == 0)
-    return MEMCACHED_NO_SERVERS;
+  if (memcached_server_count(ptr) == 0)
+    return memcached_set_error(ptr, MEMCACHED_NO_SERVERS, NULL);
 
   server_key= memcached_generate_hash_with_redistribution(ptr, master_key, master_key_length);
   instance= memcached_server_instance_fetch(ptr, server_key);
@@ -108,10 +107,10 @@ static memcached_return_t binary_incr_decr(memcached_st *ptr, uint8_t cmd,
 
   request.message.header.request.magic= PROTOCOL_BINARY_REQ;
   request.message.header.request.opcode= cmd;
-  request.message.header.request.keylen= htons((uint16_t)(key_length + ptr->prefix_key_length));
+  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 + ptr->prefix_key_length +  request.message.header.request.extlen));
+  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);
@@ -119,7 +118,7 @@ static memcached_return_t binary_incr_decr(memcached_st *ptr, uint8_t cmd,
   struct libmemcached_io_vector_st vector[]=
   {
     { .length= sizeof(request.bytes), .buffer= request.bytes },
-    { .length= ptr->prefix_key_length, .buffer= ptr->prefix_key },
+    { .length= memcached_array_size(ptr->prefix_key), .buffer= ptr->prefix_key },
     { .length= key_length, .buffer= key }
   };
 
@@ -140,6 +139,10 @@ memcached_return_t memcached_increment(memcached_st *ptr,
                                        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);
 }
 
@@ -148,6 +151,10 @@ memcached_return_t memcached_decrement(memcached_st *ptr,
                                        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);
 }
 
@@ -161,6 +168,10 @@ memcached_return_t memcached_increment_by_key(memcached_st *ptr,
   unlikely (rc != MEMCACHED_SUCCESS)
     return rc;
 
+  uint64_t local_value;
+  if (! value)
+    value= &local_value;
+
   LIBMEMCACHED_MEMCACHED_INCREMENT_START();
   if (ptr->flags.binary_protocol)
   {
@@ -189,6 +200,10 @@ memcached_return_t memcached_decrement_by_key(memcached_st *ptr,
   unlikely (rc != MEMCACHED_SUCCESS)
     return rc;
 
+  uint64_t local_value;
+  if (! value)
+    value= &local_value;
+
   LIBMEMCACHED_MEMCACHED_DECREMENT_START();
   if (ptr->flags.binary_protocol)
   {
@@ -215,6 +230,10 @@ memcached_return_t memcached_increment_with_initial(memcached_st *ptr,
                                                     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);
@@ -234,6 +253,10 @@ memcached_return_t memcached_increment_with_initial_by_key(memcached_st *ptr,
   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,
@@ -256,6 +279,10 @@ memcached_return_t memcached_decrement_with_initial(memcached_st *ptr,
                                                     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);
@@ -275,6 +302,10 @@ memcached_return_t memcached_decrement_with_initial_by_key(memcached_st *ptr,
   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)
   {
index 227be9df040930199a100cf2bae62f8b9d384ef8..aa1b23dc8fd2075053ed841393df1e0011007969 100644 (file)
@@ -17,66 +17,69 @@ extern "C" {
 #endif
 
 LIBMEMCACHED_API
-memcached_return_t memcached_increment(memcached_st *ptr,
-                                     const char *key, size_t key_length,
-                                     uint32_t offset,
-                                     uint64_t *value);
+  memcached_return_t memcached_increment(memcached_st *ptr,
+                                         const char *key, size_t key_length,
+                                         uint32_t offset,
+                                         uint64_t *value);
 LIBMEMCACHED_API
-memcached_return_t memcached_decrement(memcached_st *ptr,
-                                     const char *key, size_t key_length,
-                                     uint32_t offset,
-                                     uint64_t *value);
+  memcached_return_t memcached_decrement(memcached_st *ptr,
+                                         const char *key, size_t key_length,
+                                         uint32_t offset,
+                                         uint64_t *value);
 
 LIBMEMCACHED_API
-memcached_return_t memcached_increment_by_key(memcached_st *ptr,
-                                            const char *master_key, size_t master_key_length,
-                                            const char *key, size_t key_length,
-                                            uint64_t offset,
-                                            uint64_t *value);
+  memcached_return_t memcached_increment_by_key(memcached_st *ptr,
+                                                const char *master_key, size_t master_key_length,
+                                                const char *key, size_t key_length,
+                                                uint64_t offset,
+                                                uint64_t *value);
 
 LIBMEMCACHED_API
-memcached_return_t memcached_decrement_by_key(memcached_st *ptr,
-                                            const char *master_key, size_t master_key_length,
-                                            const char *key, size_t key_length,
-                                            uint64_t offset,
-                                            uint64_t *value);
+  memcached_return_t memcached_decrement_by_key(memcached_st *ptr,
+                                                const char *master_key, size_t master_key_length,
+                                                const char *key, size_t key_length,
+                                                uint64_t offset,
+                                                uint64_t *value);
 
 LIBMEMCACHED_API
-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);
+  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);
+
 LIBMEMCACHED_API
-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);
+  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);
+
 LIBMEMCACHED_API
-memcached_return_t memcached_increment_with_initial_by_key(memcached_st *ptr,
-                                                         const char *master_key,
-                                                         size_t master_key_length,
-                                                         const char *key,
-                                                         size_t key_length,
-                                                         uint64_t offset,
-                                                         uint64_t initial,
-                                                         time_t expiration,
-                                                         uint64_t *value);
+  memcached_return_t memcached_increment_with_initial_by_key(memcached_st *ptr,
+                                                             const char *master_key,
+                                                             size_t master_key_length,
+                                                             const char *key,
+                                                             size_t key_length,
+                                                             uint64_t offset,
+                                                             uint64_t initial,
+                                                             time_t expiration,
+                                                             uint64_t *value);
+
 LIBMEMCACHED_API
-memcached_return_t memcached_decrement_with_initial_by_key(memcached_st *ptr,
-                                                         const char *master_key,
-                                                         size_t master_key_length,
-                                                         const char *key,
-                                                         size_t key_length,
-                                                         uint64_t offset,
-                                                         uint64_t initial,
-                                                         time_t expiration,
-                                                         uint64_t *value);
+  memcached_return_t memcached_decrement_with_initial_by_key(memcached_st *ptr,
+                                                             const char *master_key,
+                                                             size_t master_key_length,
+                                                             const char *key,
+                                                             size_t key_length,
+                                                             uint64_t offset,
+                                                             uint64_t initial,
+                                                             time_t expiration,
+                                                             uint64_t *value);
 
 #ifdef __cplusplus
 }
index 2b6ccdf9112fda235bb2dc163497b9b49fd712f4..138e0f17c98613473b57302f2bb4336c7246e296 100644 (file)
@@ -129,7 +129,8 @@ memcached_return_t memcached_behavior_set(memcached_st *ptr,
     break;
   case MEMCACHED_BEHAVIOR_VERIFY_KEY:
     if (ptr->flags.binary_protocol)
-      return MEMCACHED_FAILURE;
+      return memcached_set_error_string(ptr, MEMCACHED_FAILURE, 
+                                        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:
@@ -161,7 +162,8 @@ memcached_return_t memcached_behavior_set(memcached_st *ptr,
     memcached_quit(ptr);
     break;
   case MEMCACHED_BEHAVIOR_USER_DATA:
-    return MEMCACHED_FAILURE;
+    return memcached_set_error_string(ptr, MEMCACHED_FAILURE, 
+                                      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;
@@ -223,15 +225,20 @@ memcached_return_t memcached_behavior_set(memcached_st *ptr,
           break;
         case MEM_NOT:
         default:
-          return MEMCACHED_NOT_SUPPORTED;
+          return memcached_set_error_string(ptr, MEMCACHED_NOT_SUPPORTED, 
+                                            memcached_string_with_size("MEMCACHED_BEHAVIOR_CORK is not supported on this platform."));
         }
       }
       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_FAILURE;
+    return memcached_set_error_string(ptr, MEMCACHED_INVALID_ARGUMENTS, 
+                                      memcached_string_with_size("Invalid behavior passed to memcached_behavior_set()"));
   }
 
   return MEMCACHED_SUCCESS;
@@ -369,7 +376,8 @@ uint64_t memcached_behavior_get(memcached_st *ptr,
       return (uint64_t) sock_size;
     }
   case MEMCACHED_BEHAVIOR_USER_DATA:
-    return MEMCACHED_FAILURE;
+    return memcached_set_error_string(ptr, MEMCACHED_FAILURE, 
+                                      memcached_string_with_size("MEMCACHED_BEHAVIOR_USER_DATA deprecated."));
   case MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY:
     return ptr->flags.hash_with_prefix_key;
   case MEMCACHED_BEHAVIOR_NOREPLY:
@@ -382,6 +390,8 @@ uint64_t memcached_behavior_get(memcached_st *ptr,
     return ptr->flags.cork;
   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 */
@@ -398,13 +408,11 @@ memcached_return_t memcached_behavior_set_distribution(memcached_st *ptr, memcac
   {
     ptr->distribution= type;
     run_distribution(ptr);
-  }
-  else
-  {
-    return MEMCACHED_FAILURE;
+    return MEMCACHED_SUCCESS;
   }
 
-  return MEMCACHED_SUCCESS;
+  return memcached_set_error_string(ptr, MEMCACHED_INVALID_ARGUMENTS,
+                                    memcached_string_with_size("Invalid memcached_server_distribution_t"));
 }
 
 
@@ -415,10 +423,11 @@ memcached_server_distribution_t memcached_behavior_get_distribution(memcached_st
 
 memcached_return_t memcached_behavior_set_key_hash(memcached_st *ptr, memcached_hash_t type)
 {
-  hashkit_return_t rc;
-  rc= hashkit_set_function(&ptr->hashkit, (hashkit_hash_algorithm_t)type);
+  if (hashkit_set_function(&ptr->hashkit, (hashkit_hash_algorithm_t)type) == HASHKIT_SUCCESS)
+    return MEMCACHED_SUCCESS;
 
-  return rc == HASHKIT_SUCCESS ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE;
+  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)
@@ -428,13 +437,72 @@ memcached_hash_t memcached_behavior_get_key_hash(memcached_st *ptr)
 
 memcached_return_t memcached_behavior_set_distribution_hash(memcached_st *ptr, memcached_hash_t type)
 {
-  hashkit_return_t rc;
-  rc= hashkit_set_function(&ptr->distribution_hashkit, (hashkit_hash_algorithm_t)type);
+  if (hashkit_set_function(&ptr->distribution_hashkit, (hashkit_hash_algorithm_t)type) == HASHKIT_SUCCESS)
+    return MEMCACHED_SUCCESS;
 
-  return rc == HASHKIT_SUCCESS ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE;
+  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_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";
+  default:
+  case MEMCACHED_DISTRIBUTION_CONSISTENT_MAX: return "INVALID memcached_server_distribution_t";
+  }
+}
index 39a430d7fa526f470476c14c38386c3fc18bf8cc..1c8d42e235eed96e149bc426b45a49bbc932eb5d 100644 (file)
@@ -43,6 +43,11 @@ memcached_hash_t memcached_behavior_get_distribution_hash(memcached_st *ptr);
 LIBMEMCACHED_LOCAL
 bool _is_auto_eject_host(const memcached_st *ptr);
 
+LIBMEMCACHED_LOCAL
+  const char *libmemcached_string_behavior(const memcached_behavior_t flag);
+
+LIBMEMCACHED_LOCAL
+  const char *libmemcached_string_distribution(const memcached_server_distribution_t flag);
 
 #ifdef __cplusplus
 }
index aba1a383670afd4cf0d297b9f54de423a259dc6f..6d5285a5fe1416170de6381b64b1571e3f28efb3 100644 (file)
@@ -31,25 +31,21 @@ memcached_return_t memcached_callback_set(memcached_st *ptr,
         size_t key_length= strlen(key);
 
         if (memcached_key_test((const char **)&key, &key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED)
-        {
-          return MEMCACHED_BAD_KEY_PROVIDED;
-        }
+          return memcached_set_error(ptr, MEMCACHED_BAD_KEY_PROVIDED, NULL);
 
-        if ((key_length > MEMCACHED_PREFIX_KEY_MAX_SIZE -1)
-            || (strncpy(ptr->prefix_key, key, MEMCACHED_PREFIX_KEY_MAX_SIZE) == NULL))
-        {
-          ptr->prefix_key_length= 0;
-          return MEMCACHED_BAD_KEY_PROVIDED;
-        }
-        else
-        {
-          ptr->prefix_key_length= key_length;
-        }
+        if ((key_length > MEMCACHED_PREFIX_KEY_MAX_SIZE -1))
+          return memcached_set_error(ptr, MEMCACHED_KEY_TOO_BIG, NULL);
+
+        memcached_array_free(ptr->prefix_key);
+        ptr->prefix_key= memcached_strcpy(ptr, (const char *)data, strlen((const char*)data));
+
+        if (! ptr->prefix_key)
+          return memcached_set_error(ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, NULL);
       }
       else
       {
-        ptr->prefix_key[0]= 0;
-        ptr->prefix_key_length= 0;
+        memcached_array_free(ptr->prefix_key);
+        ptr->prefix_key= NULL;
       }
 
       break;
@@ -124,10 +120,10 @@ void *memcached_callback_get(memcached_st *ptr,
   {
   case MEMCACHED_CALLBACK_PREFIX_KEY:
     {
-      if (ptr->prefix_key_length)
+      if (ptr->prefix_key)
       {
         *error= MEMCACHED_SUCCESS;
-        return (void *)ptr->prefix_key;
+        return (void *)memcached_array_string(ptr->prefix_key);
       }
       else
       {
index a7d95af71617c0b827f7156cae634d6cf4bbf94e..e6f845eacafb01b1d877c4db92d29983f9317646 100644 (file)
@@ -16,7 +16,7 @@
 #ifndef __LIBMEMCACHED_COMMON_H__
 #define __LIBMEMCACHED_COMMON_H__
 
-#include "config.h"
+#include <config.h>
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -47,6 +47,7 @@
 
 #include "libmemcached/memcached.h"
 #include "libmemcached/watchpoint.h"
+#include "libmemcached/is.h"
 
 typedef struct memcached_server_st * memcached_server_write_instance_st;
 
@@ -65,6 +66,7 @@ memcached_return_t memcached_server_execute(memcached_st *ptr,
 #include "libmemcached/io.h"
 #include "libmemcached/do.h"
 #include "libmemcached/internal.h"
+#include "libmemcached/array.h"
 #include "libmemcached/libmemcached_probes.h"
 #include "libmemcached/memcached/protocol_binary.h"
 #include "libmemcached/byteorder.h"
@@ -115,16 +117,6 @@ memcached_return_t run_distribution(memcached_st *ptr);
 #define memcached_server_response_decrement(A) (A)->cursor_active--
 #define memcached_server_response_reset(A) (A)->cursor_active=0
 
-// These are private 
-#define memcached_is_allocated(__object) ((__object)->options.is_allocated)
-#define memcached_is_initialized(__object) ((__object)->options.is_initialized)
-#define memcached_is_purging(__object) ((__object)->state.is_purging)
-#define memcached_is_processing_input(__object) ((__object)->state.is_processing_input)
-#define memcached_set_purging(__object, __value) ((__object)->state.is_purging= (__value))
-#define memcached_set_processing_input(__object, __value) ((__object)->state.is_processing_input= (__value))
-#define memcached_set_initialized(__object, __value) ((__object)->options.is_initialized(= (__value))
-#define memcached_set_allocated(__object, __value) ((__object)->options.is_allocated(= (__value))
-
 LIBMEMCACHED_LOCAL
 void set_last_disconnected_host(memcached_server_write_instance_st ptr);
 
index b12a3890ff62a6cafcf7e4a57d158ef1863d0084..e01623c8e663680d02a54c0f0af6e0d4a5e351dd 100644 (file)
@@ -9,6 +9,7 @@
  *
  */
 
+#pragma once
 #ifndef __LIBMEMCACHED_CONSTANTS_H__
 #define __LIBMEMCACHED_CONSTANTS_H__
 
@@ -73,6 +74,8 @@ typedef enum {
   MEMCACHED_AUTH_PROBLEM,
   MEMCACHED_AUTH_FAILURE,
   MEMCACHED_AUTH_CONTINUE,
+  MEMCACHED_PARSE_ERROR,
+  MEMCACHED_PARSE_USER_ERROR,
   MEMCACHED_MAXIMUM_RETURN /* Always add new error code before */
 } memcached_return_t;
 
@@ -121,6 +124,7 @@ typedef enum {
   MEMCACHED_BEHAVIOR_CORK,
   MEMCACHED_BEHAVIOR_TCP_KEEPALIVE,
   MEMCACHED_BEHAVIOR_TCP_KEEPIDLE,
+  MEMCACHED_BEHAVIOR_LOAD_FROM_FILE,
   MEMCACHED_BEHAVIOR_MAX
 } memcached_behavior_t;
 
index ef1fa6da328e499e94d931e608f88db2ac6e5c81..2f205cd6f293b42d0a148bdf30995018a0a3d810 100644 (file)
@@ -92,8 +92,7 @@ memcached_return_t memcached_delete_by_key(memcached_st *ptr,
           }
           send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE,
                                 "delete %.*s%.*s %u%s\r\n",
-                                (int)ptr->prefix_key_length,
-                                ptr->prefix_key,
+                                memcached_print_array(ptr->prefix_key),
                                 (int) key_length, key,
                                 (uint32_t)expiration,
                                 no_reply ? " noreply" :"" );
@@ -103,8 +102,7 @@ memcached_return_t memcached_delete_by_key(memcached_st *ptr,
     {
       send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE,
                             "delete %.*s%.*s%s\r\n",
-                            (int)ptr->prefix_key_length,
-                            ptr->prefix_key,
+                            memcached_print_array(ptr->prefix_key),
                             (int)key_length, key, no_reply ? " noreply" :"");
     }
 
@@ -163,9 +161,9 @@ static inline memcached_return_t binary_delete(memcached_st *ptr,
     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 + ptr->prefix_key_length));
+  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 + ptr->prefix_key_length));
+  request.message.header.request.bodylen= htonl((uint32_t)(key_length + memcached_array_size(ptr->prefix_key)));
 
   if (ptr->flags.use_udp && ! flush)
   {
@@ -179,7 +177,7 @@ static inline memcached_return_t binary_delete(memcached_st *ptr,
   struct libmemcached_io_vector_st vector[]=
   {
     { .length= sizeof(request.bytes), .buffer= request.bytes},
-    { .length= ptr->prefix_key_length, .buffer= ptr->prefix_key },
+    { .length= memcached_array_size(ptr->prefix_key), .buffer= memcached_array_string(ptr->prefix_key) },
     { .length= key_length, .buffer= key },
   };
 
diff --git a/libmemcached/error.c b/libmemcached/error.c
new file mode 100644 (file)
index 0000000..81f6504
--- /dev/null
@@ -0,0 +1,203 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  LibMemcached
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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"
+
+struct memcached_error_st
+{
+  memcached_st *root;
+  struct memcached_error_st *next;
+  memcached_return_t rc;
+  int local_errno;
+  size_t size;
+  char c_str[];
+};
+
+static memcached_error_st *_set(memcached_st *memc, memcached_string_t *str)
+{
+  if (! memc)
+    return NULL;
+
+  memcached_error_st *error;
+  error= (struct memcached_error_st *)libmemcached_malloc(memc, sizeof(struct memcached_error_st) +(str ? str->size :0) +1); 
+
+  if (! error)
+    return NULL;
+
+  error->root= memc;
+
+  if (str)
+  {
+    error->size= str->size;
+    memcpy(error->c_str, str->c_str, str->size);
+    error->c_str[str->size]= 0;
+  }
+  else
+  {
+    error->size= 0;
+  }
+
+  error->next= memc->error_messages;
+  memc->error_messages= error;
+
+  return error;
+}
+
+memcached_return_t memcached_set_error_string(memcached_st *memc, memcached_return_t rc, const char *str, size_t length)
+{
+  memcached_string_t tmp;
+  tmp.c_str= str;
+  tmp.size= length;
+  return memcached_set_error(memc, rc, &tmp);
+}
+
+memcached_return_t memcached_set_error(memcached_st *memc, memcached_return_t rc, memcached_string_t *str)
+{
+  if (rc == MEMCACHED_SUCCESS)
+    return MEMCACHED_SUCCESS;
+
+  memcached_error_st *error= _set(memc, str);
+
+  if (error)
+  {
+    error->local_errno= 0;
+    error->rc= rc;
+  }
+
+  return rc;
+}
+
+memcached_return_t memcached_set_errno(memcached_st *memc, int local_errno, memcached_string_t *str)
+{
+  memcached_error_st *error= _set(memc, str);
+
+  if (error)
+  {
+    error->local_errno= local_errno;
+    error->rc= MEMCACHED_ERRNO;
+  }
+
+  return error->rc;
+}
+
+static void _error_print(const memcached_error_st *error)
+{
+  if (! error)
+    return;
+
+  if (! error->size)
+  {
+    fprintf(stderr, "%s\n", memcached_strerror(NULL, error->rc) );
+  }
+  else
+  {
+    fprintf(stderr, "%s %s\n", memcached_strerror(NULL, error->rc), error->c_str);
+  }
+
+  _error_print(error->next);
+}
+
+void memcached_error_print(const memcached_st *self)
+{
+  if (! self)
+    return;
+
+  _error_print(self->error_messages);
+}
+
+static void _error_free(memcached_error_st *error)
+{
+  if (! error)
+    return;
+
+  _error_free(error->next);
+
+  if (error && error->root)
+  {
+    libmemcached_free(error->root, error);
+  }
+  else if (error)
+  {
+    free(error);
+  }
+}
+
+void memcached_error_free(memcached_st *self)
+{
+  if (! self)
+    return;
+
+  _error_free(self->error_messages);
+}
+
+const char *memcached_last_error_message(memcached_st *memc)
+{
+  if (! memc)
+    return memcached_strerror(memc, MEMCACHED_INVALID_ARGUMENTS);
+
+  if (! memc->error_messages)
+    return memcached_strerror(memc, MEMCACHED_SUCCESS);
+
+  if (! memc->error_messages->size)
+  {
+    return memcached_strerror(memc, memc->error_messages->rc);
+  }
+
+  return memc->error_messages->c_str;
+}
+
+memcached_return_t memcached_last_error(memcached_st *memc)
+{
+  if (! memc)
+    return MEMCACHED_INVALID_ARGUMENTS;
+
+  if (! memc->error_messages)
+    return MEMCACHED_SUCCESS;
+
+  return memc->error_messages->rc;
+}
+
+memcached_return_t memcached_last_error_errno(memcached_st *memc)
+{
+  if (! memc)
+    return MEMCACHED_INVALID_ARGUMENTS;
+
+  if (! memc->error_messages)
+    return MEMCACHED_SUCCESS;
+
+  return memc->error_messages->local_errno;
+}
diff --git a/libmemcached/error.h b/libmemcached/error.h
new file mode 100644 (file)
index 0000000..d9a08dd
--- /dev/null
@@ -0,0 +1,71 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  LibMemcached
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LIBMEMCACHED_LOCAL
+  memcached_return_t memcached_set_error(memcached_st *memc, memcached_return_t rc, memcached_string_t *str);
+
+LIBMEMCACHED_LOCAL
+  memcached_return_t memcached_set_error_string(memcached_st *memc, memcached_return_t rc, const char *str, size_t length);
+
+LIBMEMCACHED_LOCAL
+  memcached_return_t memcached_set_errno(memcached_st *memc, int local_errno, memcached_string_t *str);
+
+LIBMEMCACHED_LOCAL
+  void memcached_error_free(memcached_st *error);
+
+LIBMEMCACHED_API
+  const char *memcached_last_error_message(memcached_st *memc);
+
+LIBMEMCACHED_API
+  void memcached_error_print(const memcached_st *self);
+
+LIBMEMCACHED_API
+  memcached_return_t memcached_last_error(memcached_st *memc);
+
+LIBMEMCACHED_API
+  memcached_return_t memcached_last_error_errno(memcached_st *memc);
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
index aa4e6f53de49eb2fba940dc08046f50c9ef6909e..13fe60799418e2bc8cc451f7d6009319553fe60a 100644 (file)
@@ -232,7 +232,7 @@ static memcached_return_t memcached_mget_by_key_real(memcached_st *ptr,
     struct libmemcached_io_vector_st vector[]=
     {
       { .length= get_command_length, .buffer= get_command },
-      { .length= ptr->prefix_key_length, .buffer= ptr->prefix_key },
+      { .length= memcached_array_size(ptr->prefix_key), .buffer= memcached_array_string(ptr->prefix_key) },
       { .length= key_length[x], .buffer= keys[x] },
       { .length= 1, .buffer= " " }
     };
@@ -423,14 +423,14 @@ static memcached_return_t simple_binary_mget(memcached_st *ptr,
       return vk;
     }
 
-    request.message.header.request.keylen= htons((uint16_t)(key_length[x] + ptr->prefix_key_length));
+    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] + ptr->prefix_key_length));
+    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= ptr->prefix_key_length, .buffer= ptr->prefix_key },
+      { .length= memcached_array_size(ptr->prefix_key), .buffer= memcached_array_string(ptr->prefix_key) },
       { .length= key_length[x], .buffer= keys[x] }
     };
 
@@ -545,9 +545,9 @@ static memcached_return_t replication_binary_mget(memcached_st *ptr,
         .message.header.request= {
           .magic= PROTOCOL_BINARY_REQ,
           .opcode= PROTOCOL_BINARY_CMD_GETK,
-          .keylen= htons((uint16_t)(key_length[x] + ptr->prefix_key_length)),
+          .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] + ptr->prefix_key_length))
+          .bodylen= htonl((uint32_t)(key_length[x] + memcached_array_size(ptr->prefix_key)))
         }
       };
 
@@ -564,7 +564,7 @@ static memcached_return_t replication_binary_mget(memcached_st *ptr,
       struct libmemcached_io_vector_st vector[]=
       {
         { .length= sizeof(request.bytes), .buffer= request.bytes },
-        { .length= ptr->prefix_key_length, .buffer= ptr->prefix_key },
+        { .length= memcached_array_size(ptr->prefix_key), .buffer= memcached_array_string(ptr->prefix_key) },
         { .length= key_length[x], .buffer= keys[x] }
       };
 
index dda303952a5c86c2e6cc76de52c5a440474795eb..ce47a44833ff4e5b5deaf7f28111974df405d714 100644 (file)
@@ -74,14 +74,14 @@ static inline uint32_t _generate_hash_wrapper(const memcached_st *ptr, const cha
 
   if (ptr->flags.hash_with_prefix_key)
   {
-    size_t temp_length= ptr->prefix_key_length + key_length;
+    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, ptr->prefix_key, ptr->prefix_key_length);
-    strncpy(temp + ptr->prefix_key_length, key, key_length);
+    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);
   }
@@ -136,3 +136,23 @@ memcached_return_t memcached_set_hashkit(memcached_st *self, hashkit_st *hashk)
 
   return MEMCACHED_SUCCESS;
 }
+
+const char * libmemcached_string_hash(memcached_hash_t type)
+{
+  switch (type)
+  {
+  case MEMCACHED_HASH_DEFAULT: return "MEMCACHED_HASH_DEFAULT";
+  case MEMCACHED_HASH_MD5: return "MEMCACHED_HASH_MD5";
+  case MEMCACHED_HASH_CRC: return "MEMCACHED_HASH_CRC";
+  case MEMCACHED_HASH_FNV1_64: return "MEMCACHED_HASH_FNV1_64";
+  case MEMCACHED_HASH_FNV1A_64: return "MEMCACHED_HASH_FNV1A_64";
+  case MEMCACHED_HASH_FNV1_32: return "MEMCACHED_HASH_FNV1_32";
+  case MEMCACHED_HASH_FNV1A_32: return "MEMCACHED_HASH_FNV1A_32";
+  case MEMCACHED_HASH_HSIEH: return "MEMCACHED_HASH_HSIEH";
+  case MEMCACHED_HASH_MURMUR: return "MEMCACHED_HASH_MURMUR";
+  case MEMCACHED_HASH_JENKINS: return "MEMCACHED_HASH_JENKINS";
+  case MEMCACHED_HASH_CUSTOM: return "MEMCACHED_HASH_CUSTOM";
+  default:
+  case MEMCACHED_HASH_MAX: return "INVALID memcached_hash_t";
+  }
+}
index 0e66426680b8b90320c81836983309ac199bc9b9..1480c3b924632f0732d4229e07c3887dc74e3a7f 100644 (file)
@@ -35,6 +35,9 @@ uint32_t memcached_generate_hash_with_redistribution(memcached_st *ptr, const ch
 LIBMEMCACHED_API
 void memcached_autoeject(memcached_st *ptr);
 
+LIBMEMCACHED_API
+  const char * libmemcached_string_hash(memcached_hash_t type);
+
 #ifdef __cplusplus
 }
 #endif
index 9723f8b628ad8c49b42d3d1d6c4388ce63e8a22d..992bd763d0b98e1afe59aa32f9d88740e840a897 100644 (file)
@@ -17,6 +17,7 @@ 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)
@@ -449,3 +450,20 @@ static memcached_return_t server_add(memcached_st *ptr, const char *hostname,
 
   return run_distribution(ptr);
 }
+
+memcached_return_t memcached_server_add_parsed(memcached_st *ptr,
+                                               const char *hostname,
+                                               size_t hostname_length,
+                                               in_port_t port,
+                                               uint32_t weight)
+{
+  char buffer[NI_MAXHOST];
+
+  memcpy(buffer, hostname, hostname_length);
+  buffer[hostname_length]= 0;
+
+  return server_add(ptr, buffer,
+                    port,
+                    weight,
+                    MEMCACHED_CONNECTION_TCP);
+}
index 7d4841f8fb03a8273727b5eb97a4158ae863bb48..b0d0731938c4d670e4c2836e65866e95ce29085c 100644 (file)
@@ -2,6 +2,10 @@
 # included from Top Level Makefile.am
 # All paths should be given relative to the root
 
+libmemcached_libmemcached_la_SOURCES =
+
+include libmemcached/options/include.am
+
 EXTRA_DIST+= \
             libmemcached/configure.h.in \
             libmemcached/libmemcached_probes.d \
@@ -13,6 +17,7 @@ noinst_HEADERS+= \
                 libmemcached/do.h \
                 libmemcached/internal.h \
                 libmemcached/io.h \
+                libmemcached/is.h \
                 libmemcached/libmemcached_probes.h \
                 libmemcached/protocol/ascii_handler.h \
                 libmemcached/protocol/binary_handler.h \
@@ -22,6 +27,7 @@ noinst_HEADERS+= \
 nobase_include_HEADERS+= \
                         libmemcached/allocators.h \
                         libmemcached/analyze.h \
+                        libmemcached/array.h \
                         libmemcached/auto.h \
                         libmemcached/behavior.h \
                         libmemcached/callback.h \
@@ -29,6 +35,7 @@ nobase_include_HEADERS+= \
                         libmemcached/constants.h \
                         libmemcached/delete.h \
                         libmemcached/dump.h \
+                        libmemcached/error.h \
                         libmemcached/exception.hpp \
                         libmemcached/fetch.h \
                         libmemcached/flush.h \
@@ -38,6 +45,7 @@ nobase_include_HEADERS+= \
                         libmemcached/memcached.h \
                         libmemcached/memcached.hpp \
                         libmemcached/memcached/protocol_binary.h \
+                        libmemcached/options.h \
                         libmemcached/parse.h \
                          libmemcached/platform.h \
                         libmemcached/protocol/cache.h \
@@ -84,15 +92,17 @@ libmemcached_libmemcachedinternal_la_SOURCES= \
 
 lib_LTLIBRARIES+= libmemcached/libmemcached.la
 libmemcached_libmemcached_la_CFLAGS= ${AM_CFLAGS} ${NO_CONVERSION}
-libmemcached_libmemcached_la_SOURCES = \
+libmemcached_libmemcached_la_SOURCES+= \
                                       libmemcached/allocators.c \
                                       libmemcached/analyze.c \
+                                      libmemcached/array.c \
                                       libmemcached/auto.c \
                                       libmemcached/behavior.c \
                                       libmemcached/connect.c \
                                       libmemcached/delete.c \
                                       libmemcached/do.c \
                                       libmemcached/dump.c \
+                                      libmemcached/error.c \
                                       libmemcached/fetch.c \
                                       libmemcached/flush.c \
                                       libmemcached/flush_buffers.c \
@@ -102,6 +112,7 @@ libmemcached_libmemcached_la_SOURCES = \
                                       libmemcached/io.c \
                                       libmemcached/key.c \
                                       libmemcached/memcached.c \
+                                      libmemcached/options.cc \
                                       libmemcached/parse.c \
                                       libmemcached/purge.c \
                                       libmemcached/quit.c \
@@ -115,6 +126,8 @@ libmemcached_libmemcached_la_SOURCES = \
                                       libmemcached/verbosity.c \
                                       libmemcached/version.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
diff --git a/libmemcached/is.h b/libmemcached/is.h
new file mode 100644 (file)
index 0000000..f155334
--- /dev/null
@@ -0,0 +1,48 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  LibMemcached
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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
+
+/* These are private */ 
+#define memcached_is_allocated(__object) ((__object)->options.is_allocated)
+#define memcached_is_initialized(__object) ((__object)->options.is_initialized)
+#define memcached_is_purging(__object) ((__object)->state.is_purging)
+#define memcached_is_processing_input(__object) ((__object)->state.is_processing_input)
+#define memcached_set_purging(__object, __value) ((__object)->state.is_purging= (__value))
+#define memcached_set_processing_input(__object, __value) ((__object)->state.is_processing_input= (__value))
+#define memcached_set_initialized(__object, __value) ((__object)->options.is_initialized(= (__value))
+#define memcached_set_allocated(__object, __value) ((__object)->options.is_allocated= (__value))
index 81895afab67b19102b9e4418ee1890663cf4128c..bc335dd8bf6139d96b27f853643bf57e0dce88de 100644 (file)
@@ -34,7 +34,8 @@ static const memcached_st global_copy= {
     .use_sort_hosts= false,
     .use_udp= false,
     .verify_key= false,
-    .tcp_keepalive= false
+    .tcp_keepalive= false,
+    .ping_service= false
   }
 };
 
@@ -78,7 +79,6 @@ static inline bool _memcached_init(memcached_st *self)
 
   self->user_data= NULL;
   self->next_distribution_rebuild= 0;
-  self->prefix_key_length= 0;
   self->number_of_replicas= 0;
   hash_ptr= hashkit_create(&self->distribution_hashkit);
   if (! hash_ptr)
@@ -95,9 +95,53 @@ static inline bool _memcached_init(memcached_st *self)
   self->sasl.callbacks= NULL;
   self->sasl.is_allocated= false;
 
+  self->error_messages= NULL;
+  self->prefix_key= NULL;
+  self->configure.filename= NULL;
+
   return true;
 }
 
+static void _free(memcached_st *ptr, bool release_st)
+{
+  /* If we have anything open, lets close it now */
+  memcached_quit(ptr);
+  memcached_server_list_free(memcached_server_list(ptr));
+  memcached_result_free(&ptr->result);
+
+  if (ptr->last_disconnected_server)
+    memcached_server_free(ptr->last_disconnected_server);
+
+  if (ptr->on_cleanup)
+    ptr->on_cleanup(ptr);
+
+  if (ptr->continuum)
+    libmemcached_free(ptr, ptr->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)
@@ -138,6 +182,46 @@ memcached_st *memcached_create(memcached_st *ptr)
   return ptr;
 }
 
+memcached_st *memcached_create_with_options(const char *string, size_t length)
+{
+  memcached_st *self= memcached_create(NULL);
+
+  if (! self)
+    return NULL;
+
+  memcached_return_t rc;
+  if ((rc= memcached_parse_configuration(self, string, length)) != MEMCACHED_SUCCESS)
+  {
+    return self;
+  }
+
+  if (memcached_parse_filename(self))
+  {
+    rc= memcached_parse_configure_file(self, memcached_parse_filename(self), memcached_parse_filename_length(self));
+  }
+
+  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);
+  _free(ptr, false);
+  memcached_create(ptr);
+  memcached_set_allocated(ptr, stored_is_allocated);
+
+  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)
 {
   memcached_server_list_free(memcached_server_list(ptr));
@@ -163,31 +247,7 @@ void memcached_reset_last_disconnected_server(memcached_st *ptr)
 
 void memcached_free(memcached_st *ptr)
 {
-  /* If we have anything open, lets close it now */
-  memcached_quit(ptr);
-  memcached_server_list_free(memcached_server_list(ptr));
-  memcached_result_free(&ptr->result);
-
-  if (ptr->last_disconnected_server)
-    memcached_server_free(ptr->last_disconnected_server);
-
-  if (ptr->on_cleanup)
-    ptr->on_cleanup(ptr);
-
-  if (ptr->continuum)
-    libmemcached_free(ptr, ptr->continuum);
-
-  if (ptr->sasl.callbacks)
-  {
-#ifdef LIBMEMCACHED_WITH_SASL_SUPPORT
-    memcached_destroy_sasl_auth_data(ptr);
-#endif
-  }
-
-  if (memcached_is_allocated(ptr))
-  {
-    libmemcached_free(ptr, ptr);
-  }
+  _free(ptr, true);
 }
 
 /*
@@ -267,11 +327,7 @@ memcached_st *memcached_clone(memcached_st *clone, const memcached_st *source)
   }
 
 
-  if (source->prefix_key_length)
-  {
-    strcpy(new_clone->prefix_key, source->prefix_key);
-    new_clone->prefix_key_length= source->prefix_key_length;
-  }
+  new_clone->prefix_key= memcached_array_clone(new_clone, source->prefix_key);
 
 #ifdef LIBMEMCACHED_WITH_SASL_SUPPORT
   if (source->sasl.callbacks)
index c099d76e2b984f56e0b0e826149b457e13af8201..193eb33c723f8300c5102e962b7f0f31ada1b73a 100644 (file)
@@ -28,6 +28,8 @@
 #include <libmemcached/constants.h>
 #include <libmemcached/types.h>
 #include <libmemcached/string.h>
+#include <libmemcached/array.h>
+#include <libmemcached/error.h>
 #include <libmemcached/stats.h>
 #include <libhashkit/hashkit.h>
 // Everything above this line must be in the order specified.
@@ -43,6 +45,7 @@
 #include <libmemcached/flush_buffers.h>
 #include <libmemcached/get.h>
 #include <libmemcached/hash.h>
+#include <libmemcached/options.h>
 #include <libmemcached/parse.h>
 #include <libmemcached/quit.h>
 #include <libmemcached/result.h>
@@ -82,6 +85,7 @@ struct memcached_st {
     bool use_udp:1;
     bool verify_key:1;
     bool tcp_keepalive:1;
+    bool ping_service:1;
   } flags;
   memcached_server_distribution_t distribution;
   hashkit_st hashkit;
@@ -105,7 +109,6 @@ struct memcached_st {
   int recv_size;
   void *user_data;
   time_t next_distribution_rebuild; // Ketama
-  size_t prefix_key_length;
   uint32_t number_of_replicas;
   hashkit_st distribution_hashkit;
   memcached_result_st result;
@@ -125,7 +128,11 @@ struct memcached_st {
   memcached_trigger_delete_key_fn delete_trigger;
   memcached_callback_st *callbacks;
   struct memcached_sasl_st sasl;
-  char prefix_key[MEMCACHED_PREFIX_KEY_MAX_SIZE];
+  struct memcached_error_st *error_messages;
+  struct memcached_array_st *prefix_key;
+  struct {
+    struct memcached_array_st *filename;
+  } configure;
   struct {
     bool is_allocated:1;
   } options;
@@ -142,9 +149,15 @@ void memcached_servers_reset(memcached_st *ptr);
 LIBMEMCACHED_API
 memcached_st *memcached_create(memcached_st *ptr);
 
+LIBMEMCACHED_API
+memcached_st *memcached_create_with_options(const char *string, size_t length);
+
 LIBMEMCACHED_API
 void memcached_free(memcached_st *ptr);
 
+LIBMEMCACHED_API
+memcached_return_t memcached_reset(memcached_st *ptr);
+
 LIBMEMCACHED_API
 void memcached_reset_last_disconnected_server(memcached_st *ptr);
 
diff --git a/libmemcached/options.cc b/libmemcached/options.cc
new file mode 100644 (file)
index 0000000..02b885f
--- /dev/null
@@ -0,0 +1,146 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Libmemcached library
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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 "common.h"
+
+#include <iostream>
+
+#include <libmemcached/options/context.h>
+
+const char *memcached_parse_filename(memcached_st *memc)
+{
+  return memcached_array_string(memc->configure.filename);
+}
+
+size_t memcached_parse_filename_length(memcached_st *memc)
+{
+  return memcached_array_size(memc->configure.filename);
+}
+
+static memcached_return_t _parse_file_options(memcached_st *self, memcached_string_t *filename)
+{
+  std::string real_name(filename->c_str, filename->size);
+  FILE *fp= fopen(real_name.c_str(), "r");
+  if (! fp)
+  {
+    memcached_string_t tmp;
+    tmp.c_str= real_name.c_str();
+    tmp.size= real_name.size();
+    return memcached_set_errno(self, errno, &tmp);
+  }
+
+  char buffer[BUFSIZ];
+  memcached_return_t rc= MEMCACHED_INVALID_ARGUMENTS;
+  while (fgets(buffer, sizeof(buffer), fp))
+  {
+    size_t length= strlen(buffer);
+    
+    if (length == 1 and buffer[0] == '\n')
+      continue;
+
+    rc= memcached_parse_configuration(self, buffer, length);
+    if (rc != MEMCACHED_SUCCESS)
+      break;
+  }
+  fclose(fp);
+
+  return rc;
+}
+
+memcached_return_t libmemcached_check_configuration(const char *option_string, size_t length, char *error_buffer, size_t error_buffer_size)
+{
+  memcached_st memc, *memc_ptr;
+
+  if (! (memc_ptr= memcached_create(&memc)))
+    return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
+
+  memcached_return_t rc= memcached_parse_configuration(memc_ptr, option_string, length);
+  if (rc != MEMCACHED_SUCCESS && error_buffer && error_buffer_size)
+  {
+    strncpy(error_buffer, memcached_last_error_message(memc_ptr), error_buffer_size);
+  }
+
+  if (rc== MEMCACHED_SUCCESS && memcached_behavior_get(memc_ptr, MEMCACHED_BEHAVIOR_LOAD_FROM_FILE))
+  {
+    memcached_string_t filename= memcached_array_to_string(memc_ptr->configure.filename);
+    rc= _parse_file_options(memc_ptr, &filename);
+
+    if (rc != MEMCACHED_SUCCESS && error_buffer && error_buffer_size)
+    {
+      strncpy(error_buffer, memcached_last_error_message(memc_ptr), error_buffer_size);
+    }
+  }
+
+  memcached_free(memc_ptr);
+
+  return rc;
+}
+
+memcached_return_t memcached_parse_configuration(memcached_st *self, char const *option_string, size_t length)
+{
+  WATCHPOINT_ASSERT(self);
+  if (! self)
+    return memcached_set_error(self, MEMCACHED_INVALID_ARGUMENTS, NULL);
+
+  memcached_return_t rc;
+  Context context(option_string, length, self, rc);
+
+  context.start();
+
+  return rc;
+}
+
+void memcached_set_configuration_file(memcached_st *self, const char *filename, size_t filename_length)
+{
+  memcached_array_free(self->configure.filename);
+  self->configure.filename= memcached_strcpy(self, filename, filename_length);
+}
+
+memcached_return_t memcached_parse_configure_file(memcached_st *self, const char *filename, size_t filename_length)
+{
+  if (! self)
+    return memcached_set_error(self, MEMCACHED_INVALID_ARGUMENTS, NULL);
+
+  if (! filename || filename_length == 0)
+    return memcached_set_error(self, MEMCACHED_INVALID_ARGUMENTS, NULL);
+  
+  memcached_string_t tmp;
+  tmp.c_str= filename;
+  tmp.size= filename_length;
+
+  return _parse_file_options(self, &tmp);
+}
diff --git a/libmemcached/options.h b/libmemcached/options.h
new file mode 100644 (file)
index 0000000..0a27be9
--- /dev/null
@@ -0,0 +1,64 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Libmemcached library
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LIBMEMCACHED_API
+  memcached_return_t libmemcached_check_configuration(const char *option_string, size_t length, char *error_buffer, size_t error_buffer_size);
+
+LIBMEMCACHED_API
+  memcached_return_t memcached_parse_configuration(memcached_st *ptr, const char *option_string, size_t length);
+
+LIBMEMCACHED_API
+  memcached_return_t memcached_parse_configure_file(memcached_st *ptr, const char *filename, size_t filename_length);
+
+LIBMEMCACHED_API
+  void memcached_set_configuration_file(memcached_st *self, const char *filename, size_t filename_length);
+
+LIBMEMCACHED_API
+  const char *memcached_parse_filename(memcached_st *memc);
+
+LIBMEMCACHED_LOCAL
+  size_t memcached_parse_filename_length(memcached_st *memc);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/libmemcached/options/context.h b/libmemcached/options/context.h
new file mode 100644 (file)
index 0000000..9e4ecb3
--- /dev/null
@@ -0,0 +1,93 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Libmemcached library
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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 <libmemcached/memcached.h>
+
+class Context
+{
+public:
+  Context(const char *option_string, size_t option_string_length, memcached_st *memc_arg,
+          memcached_return_t &rc_arg) :
+    scanner(NULL),
+    begin(NULL),
+    pos(0),
+    memc(NULL),
+    rc(rc_arg),
+    _end(false)
+  {
+    buf= option_string;
+    length= option_string_length;
+    memc= memc_arg;
+    init_scanner();
+    rc= MEMCACHED_SUCCESS;
+  }
+
+  bool end()
+  {
+    return _end;
+  }
+
+  void start();
+
+  void set_end()
+  {
+    rc= MEMCACHED_SUCCESS;
+    _end= true;
+  }
+
+  ~Context()
+  {
+    destroy_scanner();
+  }
+
+  void *scanner;
+  const char *buf;
+  const char *begin;
+  size_t pos;
+  size_t length;
+  memcached_st *memc;
+  memcached_return_t &rc;
+
+protected:
+  void init_scanner();   
+  void destroy_scanner();
+
+private:
+  bool _end;
+}; 
diff --git a/libmemcached/options/include.am b/libmemcached/options/include.am
new file mode 100644 (file)
index 0000000..d110a94
--- /dev/null
@@ -0,0 +1,37 @@
+# vim:ft=automake
+# included from Top Level Makefile.am
+# All paths should be given relative to the root
+
+DISTCLEANFILES+= \
+                libmemcached/options/parser.output \
+                libmemcached/options/parser.cc \
+                libmemcached/options/parser.h \
+                libmemcached/options/scanner.cc \
+                libmemcached/options/scanner.h
+
+EXTRA_DIST+= \
+            libmemcached/options/scanner.l \
+            libmemcached/options/parser.yy
+
+noinst_HEADERS+= \
+                libmemcached/options/context.h \
+                libmemcached/options/parser.h \
+                libmemcached/options/scanner.h \
+                libmemcached/options/server.h \
+                libmemcached/options/string.h \
+                libmemcached/options/symbol.h
+
+libmemcached_libmemcached_la_SOURCES+= \
+                                      libmemcached/options/parser.cc \
+                                      libmemcached/options/scanner.cc
+
+libmemcached/options/parser.h: libmemcached/options/parser.cc
+
+libmemcached/options/parser.cc: libmemcached/options/parser.yy libmemcached/options/scanner.l libmemcached/options/scanner.h
+       $(AM_V_YACC)$(am__skipyacc) $(YACC) $(YLFLAGS) $(AM_YFLAGS) -o $@ $<
+
+libmemcached/options/scanner.h: libmemcached/options/scanner.cc
+
+libmemcached/options/scanner.cc: libmemcached/options/scanner.l libmemcached/options/parser.yy
+       $(AM_V_GEN)$(LEX) $<
+
diff --git a/libmemcached/options/lex_string.h b/libmemcached/options/lex_string.h
new file mode 100644 (file)
index 0000000..991acba
--- /dev/null
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <stdlib.h>
+#include <iostream>
+
+struct string_t
+{
+  const char *c_str;
+  size_t length;
+};
+
+inline std::ostream& operator<<(std::ostream& output, const string_t& arg)
+{
+  output << arg.c_str;
+  return output;
+}
diff --git a/libmemcached/options/parser.yy b/libmemcached/options/parser.yy
new file mode 100644 (file)
index 0000000..e7b95bd
--- /dev/null
@@ -0,0 +1,530 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ *
+ *  Libmemcached Scanner and Parser
+ *
+ *  Copyright (C) 2011 DataDifferental, http://datadifferential.com
+ *  
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Affero General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Affero General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU Affero General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+%error-verbose
+%debug
+%defines
+%expect 0
+%output "libmemcached/options/parser.cc"
+%defines "libmemcached/options/parser.h"
+%lex-param { yyscan_t *scanner }
+%name-prefix="libmemcached_"
+%parse-param { Context *context }
+%parse-param { yyscan_t *scanner }
+%locations
+%pure-parser
+%require "2.2"
+%start begin
+%verbose
+
+%{
+
+#include <config.h>
+
+#include <stdint.h>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <libmemcached/options/context.h>
+#include <libmemcached/options/string.h>
+#include <libmemcached/options/symbol.h>
+
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+#include <libmemcached/options/scanner.h>
+
+int libmemcached_lex(YYSTYPE* lvalp, YYLTYPE* llocp, void* scanner);
+
+#define parser_abort(A, B) do { parser_abort_func((A), (B)); YYABORT; } while (0) 
+
+inline void parser_abort_func(Context *context, const char *error)
+{
+  (void)error;
+  if (context->rc == MEMCACHED_SUCCESS)
+    context->rc= MEMCACHED_PARSE_ERROR;
+
+  std::string error_message;
+  error_message+= "Error occured while parsing: ";
+  error_message+= context->begin;
+  error_message+= " (";
+  if (context->rc == MEMCACHED_PARSE_ERROR and error)
+  {
+    error_message+= error;
+  }
+  else
+  {
+    error_message+= memcached_strerror(NULL, context->rc);
+  }
+  error_message+= ")";
+
+  memcached_set_error_string(context->memc, context->rc, error_message.c_str(), error_message.size());
+}
+
+inline void libmemcached_error(YYLTYPE *locp, Context *context, yyscan_t *scanner, const char *error)
+{
+  if (not context->end())
+    parser_abort_func(context, error);
+}
+
+int libmemcached_parse(Context*, yyscan_t *);
+void Context::start() 
+{
+  libmemcached_parse(this, scanner);
+}
+
+%}
+
+%token COMMENT
+%token END
+%token ERROR
+%token RESET
+%token PARSER_DEBUG
+%token INCLUDE
+%token CONFIGURE_FILE
+%token EMPTY_LINE
+%token SERVER
+%token SERVERS
+%token SERVERS_OPTION
+%token UNKNOWN_OPTION
+%token UNKNOWN
+
+/* All behavior options */
+%token AUTO_EJECT_HOSTS
+%token BINARY_PROTOCOL
+%token BUFFER_REQUESTS
+%token CACHE_LOOKUPS
+%token CONNECT_TIMEOUT
+%token _CORK
+%token DISTRIBUTION
+%token HASH
+%token HASH_WITH_PREFIX_KEY
+%token IO_BYTES_WATERMARK
+%token IO_KEY_PREFETCH
+%token IO_MSG_WATERMARK
+%token KETAMA
+%token KETAMA_HASH
+%token KETAMA_WEIGHTED
+%token NOREPLY
+%token NUMBER_OF_REPLICAS
+%token POLL_TIMEOUT
+%token RANDOMIZE_REPLICA_READ
+%token RCV_TIMEOUT
+%token RETRY_TIMEOUT
+%token SERVER_FAILURE_LIMIT
+%token SND_TIMEOUT
+%token SOCKET_RECV_SIZE
+%token SOCKET_SEND_SIZE
+%token SORT_HOSTS
+%token SUPPORT_CAS
+%token _TCP_NODELAY
+%token _TCP_KEEPALIVE
+%token _TCP_KEEPIDLE
+%token USER_DATA
+%token USE_UDP
+%token VERIFY_KEY
+
+/* Callbacks */
+%token PREFIX_KEY
+
+/* Hash types */
+%token MD5
+%token CRC
+%token FNV1_64
+%token FNV1A_64
+%token FNV1_32
+%token FNV1A_32
+%token HSIEH
+%token MURMUR
+%token JENKINS
+
+/* Distributions */
+%token CONSISTENT
+%token MODULA
+%token RANDOM
+
+/* Boolean values */
+%token <boolean> TRUE
+%token <boolean> FALSE
+
+%nonassoc ','
+%nonassoc '='
+
+%token <number> NUMBER
+%token <number> FLOAT
+%token <string> HOSTNAME
+%token <string> HOSTNAME_WITH_PORT
+%token <string> IPADDRESS
+%token <string> IPADDRESS_WITH_PORT
+%token <string> STRING
+%token <string> QUOTED_STRING
+%token <string> FILE_PATH
+
+%type <server> server
+%type <string> string
+%type <distribution> distribution
+%type <hash> hash
+%type <behavior> behavior_boolean
+%type <behavior> behavior_number
+
+%%
+
+begin:
+          statement
+        | begin ' ' statement
+        ;
+
+statement:
+         expression
+          { }
+        | COMMENT
+          { }
+        | EMPTY_LINE
+          { }
+        | END
+          {
+            context->set_end();
+            YYACCEPT;
+          }
+        | ERROR
+          {
+            context->rc= MEMCACHED_PARSE_USER_ERROR;
+            parser_abort(context, NULL);
+          }
+        | RESET
+          {
+            memcached_reset(context->memc);
+          }
+        | PARSER_DEBUG
+          {
+            yydebug= 1;
+          }
+        | INCLUDE ' ' string
+          {
+            if ((context->rc= memcached_parse_configure_file(context->memc, $3.c_str, $3.length)) != MEMCACHED_SUCCESS)
+            {
+              parser_abort(context, NULL);
+            }
+          }
+        ;
+
+
+expression:
+          SERVER '=' server
+          { 
+            if ((context->rc= memcached_server_add_parsed(context->memc, $3.c_str, $3.length, $3.port, 0)) != MEMCACHED_SUCCESS)
+            {
+              parser_abort(context, NULL);
+            }
+          }
+        | SERVERS_OPTION '=' server_list
+          {
+          }
+        | CONFIGURE_FILE '=' string
+          {
+            memcached_set_configuration_file(context->memc, $3.c_str, $3.length);
+          }
+        | behaviors
+        ;
+
+behaviors:
+          PREFIX_KEY '=' string
+          {
+            if ((context->rc= memcached_callback_set(context->memc, MEMCACHED_CALLBACK_PREFIX_KEY, std::string($3.c_str, $3.length).c_str())) != MEMCACHED_SUCCESS)
+            {
+              parser_abort(context, NULL);;
+            }
+          }
+        | DISTRIBUTION '=' distribution
+          {
+            if ((context->rc= memcached_behavior_set(context->memc, MEMCACHED_BEHAVIOR_DISTRIBUTION, $3)) != MEMCACHED_SUCCESS)
+            {
+              parser_abort(context, NULL);;
+            }
+          }
+        | HASH '=' hash
+          {
+            if ((context->rc= memcached_behavior_set(context->memc, MEMCACHED_BEHAVIOR_HASH, $3)) != MEMCACHED_SUCCESS)
+            {
+              parser_abort(context, NULL);; 
+            }
+          }
+        | KETAMA_HASH '=' hash
+          {
+            if ((context->rc= memcached_behavior_set(context->memc, MEMCACHED_BEHAVIOR_KETAMA_HASH, $3)) != MEMCACHED_SUCCESS)
+            {
+              parser_abort(context, NULL);;
+            }
+          }
+        | behavior_number '=' NUMBER
+          {
+            if ((context->rc= memcached_behavior_set(context->memc, $1, $3)) != MEMCACHED_SUCCESS)
+            {
+              parser_abort(context, NULL);;
+            }
+          }
+        | behavior_boolean
+          {
+            if ((context->rc= memcached_behavior_set(context->memc, $1, true)) != MEMCACHED_SUCCESS)
+            {
+              parser_abort(context, NULL);;
+            }
+          }
+        |  USER_DATA
+          {
+          }
+        ;
+
+behavior_number:
+          CONNECT_TIMEOUT
+          {
+            $$= MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT;
+          }
+        | IO_MSG_WATERMARK
+          {
+            $$= MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK;
+          }
+        | IO_BYTES_WATERMARK
+          {
+            $$= MEMCACHED_BEHAVIOR_IO_BYTES_WATERMARK;
+          }
+        | IO_KEY_PREFETCH
+          {
+            $$= MEMCACHED_BEHAVIOR_IO_KEY_PREFETCH;
+          }
+        | NUMBER_OF_REPLICAS
+          {
+            $$= MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS;
+          }
+        | POLL_TIMEOUT
+          {
+            $$= MEMCACHED_BEHAVIOR_POLL_TIMEOUT;
+          }
+        |  RCV_TIMEOUT
+          {
+            $$= MEMCACHED_BEHAVIOR_RCV_TIMEOUT;
+          }
+        |  RETRY_TIMEOUT
+          {
+            $$= MEMCACHED_BEHAVIOR_RETRY_TIMEOUT;
+          }
+        |  SERVER_FAILURE_LIMIT
+          {
+            $$= MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT;
+          }
+        |  SND_TIMEOUT
+          {
+            $$= MEMCACHED_BEHAVIOR_SND_TIMEOUT;
+          }
+        |  SOCKET_RECV_SIZE
+          {
+            $$= MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE;
+          }
+        |  SOCKET_SEND_SIZE
+          {
+            $$= MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE;
+          }
+        ;
+
+behavior_boolean: 
+          AUTO_EJECT_HOSTS
+          {
+            $$= MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS;
+          }
+        | BINARY_PROTOCOL
+          {
+            $$= MEMCACHED_BEHAVIOR_BINARY_PROTOCOL;
+          }
+        | BUFFER_REQUESTS
+          {
+            $$= MEMCACHED_BEHAVIOR_BUFFER_REQUESTS;
+          }
+        | CACHE_LOOKUPS
+          {
+            $$= MEMCACHED_BEHAVIOR_CACHE_LOOKUPS;
+          }
+        | _CORK
+          {
+            $$= MEMCACHED_BEHAVIOR_CORK;
+          }
+        | HASH_WITH_PREFIX_KEY
+          {
+            $$= MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY;
+          }
+        | KETAMA
+          {
+            $$= MEMCACHED_BEHAVIOR_KETAMA;
+          }
+        | KETAMA_WEIGHTED
+          {
+            $$= MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED;
+          }
+        | NOREPLY
+          {
+            $$= MEMCACHED_BEHAVIOR_NOREPLY;
+          }
+        |  RANDOMIZE_REPLICA_READ
+          {
+            $$= MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ;
+          }
+        |  SORT_HOSTS
+          {
+            $$= MEMCACHED_BEHAVIOR_SORT_HOSTS;
+          }
+        |  SUPPORT_CAS
+          {
+            $$= MEMCACHED_BEHAVIOR_SUPPORT_CAS;
+          }
+        |  _TCP_NODELAY
+          {
+            $$= MEMCACHED_BEHAVIOR_TCP_NODELAY;
+          }
+        |  _TCP_KEEPALIVE
+          {
+            $$= MEMCACHED_BEHAVIOR_TCP_KEEPALIVE;
+          }
+        |  _TCP_KEEPIDLE
+          {
+            $$= MEMCACHED_BEHAVIOR_TCP_KEEPIDLE;
+          }
+        |  USE_UDP
+          {
+            $$= MEMCACHED_BEHAVIOR_USE_UDP;
+          }
+        |  VERIFY_KEY
+          {
+            $$= MEMCACHED_BEHAVIOR_VERIFY_KEY;
+          }
+
+
+server_list:
+           server
+          {
+            if ((context->rc= memcached_server_add_parsed(context->memc, $1.c_str, $1.length, $1.port, 0)) != MEMCACHED_SUCCESS)
+            {
+              parser_abort(context, NULL);;
+            }
+          }
+        | server_list ',' server
+          {
+            if ((context->rc= memcached_server_add_parsed(context->memc, $3.c_str, $3.length, $3.port, 0)) != MEMCACHED_SUCCESS)
+            {
+              parser_abort(context, NULL);;
+            }
+          }
+        ;
+
+server:
+          HOSTNAME_WITH_PORT NUMBER
+          {
+            $$.c_str= $1.c_str;
+            $$.length= $1.length -1; // -1 to remove :
+            $$.port= $2;
+          }
+        | HOSTNAME
+          {
+            $$.c_str= $1.c_str;
+            $$.length= $1.length;
+            $$.port= MEMCACHED_DEFAULT_PORT;
+          }
+        | STRING /* a match can be against "localhost" which is just a string */
+          {
+            $$.c_str= $1.c_str;
+            $$.length= $1.length;
+            $$.port= MEMCACHED_DEFAULT_PORT;
+          }
+        | IPADDRESS_WITH_PORT NUMBER
+          {
+            $$.c_str= $1.c_str;
+            $$.length= $1.length -1; // -1 to remove :
+            $$.port= $2;
+          }
+        | IPADDRESS
+          {
+            $$.c_str= $1.c_str;
+            $$.length= $1.length;
+            $$.port= MEMCACHED_DEFAULT_PORT;
+          }
+        ;
+
+hash:
+          MD5
+          {
+            $$= MEMCACHED_HASH_MD5;
+          }
+        | CRC
+          {
+            $$= MEMCACHED_HASH_CRC;
+          }
+        | FNV1_64
+          {
+            $$= MEMCACHED_HASH_FNV1_64;
+          }
+        | FNV1A_64
+          {
+            $$= MEMCACHED_HASH_FNV1A_64;
+          }
+        | FNV1_32
+          {
+            $$= MEMCACHED_HASH_FNV1_32;
+          }
+        | FNV1A_32
+          {
+            $$= MEMCACHED_HASH_FNV1A_32;
+          }
+        | HSIEH
+          {
+            $$= MEMCACHED_HASH_HSIEH;
+          }
+        | MURMUR
+          {
+            $$= MEMCACHED_HASH_MURMUR;
+          }
+        | JENKINS
+          {
+            $$= MEMCACHED_HASH_JENKINS;
+          }
+        ;
+
+string:
+          STRING
+          {
+            $$= $1;
+          }
+        | QUOTED_STRING
+          {
+            $$.c_str= $1.c_str +1; // +1 to move use passed the initial quote
+            $$.length= $1.length -1; // -1 removes the end quote
+          }
+        ;
+
+distribution:
+          CONSISTENT
+          {
+            $$= MEMCACHED_DISTRIBUTION_CONSISTENT;
+          }
+        | MODULA
+          {
+            $$= MEMCACHED_DISTRIBUTION_MODULA;
+          }
+        | RANDOM
+          {
+            $$= MEMCACHED_DISTRIBUTION_RANDOM;
+          }
+        ;
diff --git a/libmemcached/options/scanner.l b/libmemcached/options/scanner.l
new file mode 100644 (file)
index 0000000..71ec83d
--- /dev/null
@@ -0,0 +1,245 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ *
+ *  Libmemcached Scanner and Parser
+ *
+ *  Copyright (C) 2011 DataDifferental, http://datadifferential.com
+ *  
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Affero General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Affero General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU Affero General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+%top{
+
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-fpermissive"
+
+#include <iostream>
+
+#include <libmemcached/options/context.h>
+#include <libmemcached/options/parser.h>
+#include <libmemcached/options/string.h>
+#include <libmemcached/options/symbol.h>
+
+#define YY_EXTRA_TYPE Context*
+#define YY_USER_ACTION yylloc->first_line = yylineno;
+
+}
+
+
+%{
+#include <cstdlib>
+#include <cstring>
+
+#define PARAM yyget_extra(yyscanner)
+
+static void get_lex_chars(char* buffer, int& result, int max_size, Context *context)
+{
+  if (context->pos >= context->length)
+  {
+    result= YY_NULL;
+  }
+  else
+  {
+    result= context->length - context->pos;
+    result > (int)max_size ? result = max_size : 0;
+    memcpy(buffer, context->buf + context->pos, result);
+    context->pos += result;
+  }
+}
+
+
+#define YY_INPUT(buffer, result, max_size) get_lex_chars(buffer, result, max_size, PARAM)
+
+%}
+
+%option bison-bridge
+%option bison-locations
+%option case-insensitive
+%option debug
+%option nounput
+%option noyywrap
+%option yylineno
+%option outfile="libmemcached/options/scanner.cc" header-file="libmemcached/options/scanner.h"
+%option perf-report
+%option prefix="libmemcached_"
+%option reentrant
+
+%%
+
+
+=|,|[ ]       { return yytext[0];}
+
+
+[[:digit:]]+ { yylval->number = atoi(yytext); return (NUMBER); }
+
+[\t\r\n] ; /* skip whitespace */
+
+^#.*$ {
+      return COMMENT;
+    }
+
+"--SERVER"                          { yyextra->begin= yytext; return SERVER; }
+"--SERVERS"                        { yyextra->begin= yytext; return SERVERS_OPTION; }
+
+"--VERIFY_KEY"                      { yyextra->begin= yytext; return VERIFY_KEY; }
+"--VERIFY-KEY"                      { yyextra->begin= yytext; return VERIFY_KEY; }
+"--AUTO_EJECT_HOSTS"           { yyextra->begin= yytext; return AUTO_EJECT_HOSTS; }
+"--AUTO-EJECT_HOSTS"           { yyextra->begin= yytext; return AUTO_EJECT_HOSTS; }
+"--BINARY_PROTOCOL"                    { yyextra->begin= yytext; return BINARY_PROTOCOL; }
+"--BINARY-PROTOCOL"                    { yyextra->begin= yytext; return BINARY_PROTOCOL; }
+"--BUFFER_REQUESTS"                    { yyextra->begin= yytext; return BUFFER_REQUESTS; }
+"--BUFFER-REQUESTS"                    { yyextra->begin= yytext; return BUFFER_REQUESTS; }
+"--CACHE_LOOKUPS"                      { yyextra->begin= yytext; return CACHE_LOOKUPS; }
+"--CACHE-LOOKUPS"                      { yyextra->begin= yytext; return CACHE_LOOKUPS; }
+"--CONFIGURE_FILE"                     { yyextra->begin= yytext; return CONFIGURE_FILE; }
+"--CONFIGURE-FILE"                     { yyextra->begin= yytext; return CONFIGURE_FILE; }
+"--CONNECT_TIMEOUT"                    { yyextra->begin= yytext; return CONNECT_TIMEOUT; }
+"--CONNECT-TIMEOUT"                    { yyextra->begin= yytext; return CONNECT_TIMEOUT; }
+"--CORK"                               { yyextra->begin= yytext; return _CORK; }
+"--DISTRIBUTION"                       { yyextra->begin= yytext; return DISTRIBUTION; }
+"--HASH"                               { yyextra->begin= yytext; return HASH; }
+"--HASH_WITH_PREFIX_KEY"               { yyextra->begin= yytext; return HASH_WITH_PREFIX_KEY; }
+"--HASH-WITH-PREFIX_KEY"               { yyextra->begin= yytext; return HASH_WITH_PREFIX_KEY; }
+"--IO_BYTES_WATERMARK"         { yyextra->begin= yytext; return IO_BYTES_WATERMARK; }
+"--IO-BYTES-WATERMARK"         { yyextra->begin= yytext; return IO_BYTES_WATERMARK; }
+"--IO_KEY_PREFETCH"                    { yyextra->begin= yytext; return IO_KEY_PREFETCH; }
+"--IO-KEY-PREFETCH"                    { yyextra->begin= yytext; return IO_KEY_PREFETCH; }
+"--IO_MSG_WATERMARK"           { yyextra->begin= yytext; return IO_MSG_WATERMARK; }
+"--IO-MSG-WATERMARK"           { yyextra->begin= yytext; return IO_MSG_WATERMARK; }
+"--KETAMA"                             { yyextra->begin= yytext; return KETAMA; }
+"--KETAMA_HASH"                        { yyextra->begin= yytext; return KETAMA_HASH; }
+"--KETAMA-HASH"                        { yyextra->begin= yytext; return KETAMA_HASH; }
+"--KETAMA_WEIGHTED"                    { yyextra->begin= yytext; return KETAMA_WEIGHTED; }
+"--KETAMA-WEIGHTED"                    { yyextra->begin= yytext; return KETAMA_WEIGHTED; }
+"--NOREPLY"                         { yyextra->begin= yytext; return NOREPLY; }
+"--NUMBER_OF_REPLICAS"         { yyextra->begin= yytext; return NUMBER_OF_REPLICAS; }
+"--NUMBER-OF-REPLICAS"         { yyextra->begin= yytext; return NUMBER_OF_REPLICAS; }
+"--POLL_TIMEOUT"                       { yyextra->begin= yytext; return POLL_TIMEOUT; }
+"--POLL-TIMEOUT"                       { yyextra->begin= yytext; return POLL_TIMEOUT; }
+"--RANDOMIZE_REPLICA_READ"             { yyextra->begin= yytext; return RANDOMIZE_REPLICA_READ; }
+"--RANDOMIZE-REPLICA-READ"             { yyextra->begin= yytext; return RANDOMIZE_REPLICA_READ; }
+"--RCV_TIMEOUT"                        { yyextra->begin= yytext; return RCV_TIMEOUT; }
+"--RCV-TIMEOUT"                        { yyextra->begin= yytext; return RCV_TIMEOUT; }
+"--RETRY_TIMEOUT"                      { yyextra->begin= yytext; return RETRY_TIMEOUT; }
+"--RETRY-TIMEOUT"                      { yyextra->begin= yytext; return RETRY_TIMEOUT; }
+"--SERVER_FAILURE_LIMIT"               { yyextra->begin= yytext; return SERVER_FAILURE_LIMIT; }
+"--SERVER-FAILURE-LIMIT"               { yyextra->begin= yytext; return SERVER_FAILURE_LIMIT; }
+"--SND_TIMEOUT"                        { yyextra->begin= yytext; return SND_TIMEOUT; }
+"--SND-TIMEOUT"                        { yyextra->begin= yytext; return SND_TIMEOUT; }
+"--SOCKET_RECV_SIZE"           { yyextra->begin= yytext; return SOCKET_RECV_SIZE; }
+"--SOCKET-RECV-SIZE"           { yyextra->begin= yytext; return SOCKET_RECV_SIZE; }
+"--SOCKET_SEND_SIZE"           { yyextra->begin= yytext; return SOCKET_SEND_SIZE; }
+"--SOCKET-SEND-SIZE"           { yyextra->begin= yytext; return SOCKET_SEND_SIZE; }
+"--SORT_HOSTS"                 { yyextra->begin= yytext; return SORT_HOSTS; }
+"--SORT-HOSTS"                 { yyextra->begin= yytext; return SORT_HOSTS; }
+"--SUPPORT_CAS"                        { yyextra->begin= yytext; return SUPPORT_CAS; }
+"--SUPPORT-CAS"                        { yyextra->begin= yytext; return SUPPORT_CAS; }
+"--TCP_NODELAY"                        { yyextra->begin= yytext; return _TCP_NODELAY; }
+"--TCP-NODELAY"                        { yyextra->begin= yytext; return _TCP_NODELAY; }
+"--TCP_KEEPALIVE"                      { yyextra->begin= yytext; return _TCP_KEEPALIVE; }
+"--TCP-KEEPALIVE"                      { yyextra->begin= yytext; return _TCP_KEEPALIVE; }
+"--TCP_KEEPIDLE"                       { yyextra->begin= yytext; return _TCP_KEEPIDLE; }
+"--TCP-KEEPIDLE"                       { yyextra->begin= yytext; return _TCP_KEEPIDLE; }
+"--USER_DATA"                  { yyextra->begin= yytext; return USER_DATA; }
+"--USER-DATA"                  { yyextra->begin= yytext; return USER_DATA; }
+"--USE_UDP"                            { yyextra->begin= yytext; return USE_UDP; }
+"--USE-UDP"                            { yyextra->begin= yytext; return USE_UDP; }
+
+"--PREFIX-KEY"                         { yyextra->begin= yytext; return PREFIX_KEY; }
+"--PREFIX_KEY"                         { yyextra->begin= yytext; return PREFIX_KEY; }
+
+INCLUDE           { yyextra->begin= yytext; return INCLUDE; }
+RESET           { yyextra->begin= yytext; return RESET; }
+DEBUG           { yyextra->begin= yytext; return PARSER_DEBUG; }
+SERVERS           { yyextra->begin= yytext; return SERVERS; }
+END           { yyextra->begin= yytext; return END; }
+ERROR           { yyextra->begin= yytext; return ERROR; }
+
+TRUE           { return TRUE; }
+FALSE           { return FALSE; }
+
+
+"--"[[:alnum:]]*   {
+      yyextra->begin= yytext;
+      return UNKNOWN_OPTION;
+    }
+
+CONSISTENT      { return CONSISTENT; }
+MODULA          { return MODULA; }
+RANDOM          { return RANDOM; }
+
+MD5                    { return MD5; }
+CRC                    { return CRC; }
+FNV1_64                        { return FNV1_64; }
+FNV1A_64                       { return FNV1A_64; }
+FNV1_32                        { return FNV1_32; }
+FNV1A_32                       { return FNV1A_32; }
+HSIEH                  { return HSIEH; }
+MURMUR                 { return MURMUR; }
+JENKINS                        { return JENKINS; }
+
+[[:alnum:]][[:alnum:].]*[[:alpha:]]: { 
+      yylval->string.c_str = yytext;
+      yylval->string.length = yyleng;
+      return HOSTNAME_WITH_PORT;
+    }
+
+[[:alnum:]]+"."[[:alpha:].]+[[:alnum:]] { 
+      yylval->string.c_str = yytext;
+      yylval->string.length = yyleng;
+      return HOSTNAME;
+    }
+
+(([[:digit:]]{1,3}"."){3}([[:digit:]]{1,3})): {
+      yylval->string.c_str = yytext;
+      yylval->string.length = yyleng;
+      return IPADDRESS_WITH_PORT;
+    }
+
+(([[:digit:]]{1,3}"."){3}([[:digit:]]{1,3})) {
+      yylval->string.c_str = yytext;
+      yylval->string.length = yyleng;
+      return IPADDRESS;
+    }
+
+[[:alnum:]]+ { 
+      yylval->string.c_str = yytext;
+      yylval->string.length = yyleng;
+      return STRING;
+    }
+
+(\".*\") {
+      yylval->string.c_str = yytext;
+      yylval->string.length = yyleng;
+      return QUOTED_STRING;
+    }
+
+.   {
+      yyextra->begin= yytext;
+      return UNKNOWN;
+    }
+
+%%
+
+void Context::init_scanner()
+{
+  yylex_init(&scanner);
+  yyset_extra(this, scanner);
+}
+
+void Context::destroy_scanner()
+{
+  yylex_destroy(scanner);
+}
+
diff --git a/libmemcached/options/server.h b/libmemcached/options/server.h
new file mode 100644 (file)
index 0000000..af6e0f3
--- /dev/null
@@ -0,0 +1,56 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Libmemcached library
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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 <cstdlib>
+#include <iostream>
+#include <arpa/inet.h>
+
+struct server_t
+{
+  const char *c_str;
+  size_t length;
+  in_port_t port;
+};
+
+inline std::ostream& operator<<(std::ostream& output, const server_t& arg)
+{
+  output.write(arg.c_str, arg.length);
+  output << ':' << arg.port;
+  return output;
+}
diff --git a/libmemcached/options/string.h b/libmemcached/options/string.h
new file mode 100644 (file)
index 0000000..82d60ac
--- /dev/null
@@ -0,0 +1,53 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Libmemcached library
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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 <cstdlib>
+#include <iostream>
+
+struct string_t
+{
+  const char *c_str;
+  size_t length;
+};
+
+inline std::ostream& operator<<(std::ostream& output, const string_t& arg)
+{
+  output.write(arg.c_str, arg.length);
+  return output;
+}
diff --git a/libmemcached/options/symbol.h b/libmemcached/options/symbol.h
new file mode 100644 (file)
index 0000000..b14eb33
--- /dev/null
@@ -0,0 +1,57 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Libmemcached library
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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 <libmemcached/constants.h>
+#include <libmemcached/options/string.h>
+#include <libmemcached/options/server.h>
+
+union YYSTYPE
+{
+  long long number;  
+  string_t string;
+  string_t option;
+  server_t server;
+  double double_number;
+  memcached_server_distribution_t distribution;
+  memcached_hash_t hash;
+  memcached_behavior_t behavior;
+  bool boolean;
+};
+
+typedef union YYSTYPE YYSTYPE;
index 45a5d746946eb95d9409893bb3eb2c7dff3b09f8..a91f769d1b0191c03e98d47af6e103eebd1c0027 100644 (file)
@@ -9,8 +9,7 @@
  *
  */
 
-#ifndef __LIBMEMCACHED_PARSE_H__
-#define __LIBMEMCACHED_PARSE_H__
+#pragma once
 
 #ifdef __cplusplus
 extern "C" {
@@ -22,5 +21,3 @@ memcached_server_list_st memcached_servers_parse(const char *server_strings);
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* __LIBMEMCACHED_PARSE_H__ */
index 867a999ef039b16f190dce2f04c49166e722aa06..49825fb1881feae3517c6f21a2ff0a3f4d6c8ad2 100644 (file)
@@ -115,7 +115,7 @@ static memcached_return_t textual_value_fetch(memcached_server_write_instance_st
     key= result->item_key;
     result->key_length= 0;
 
-    for (prefix_length= ptr->root->prefix_key_length; !(iscntrl(*string_ptr) || isspace(*string_ptr)) ; string_ptr++)
+    for (prefix_length= memcached_array_size(ptr->root->prefix_key); !(iscntrl(*string_ptr) || isspace(*string_ptr)) ; string_ptr++)
     {
       if (prefix_length == 0)
       {
index 4248e143f7c47e68d0a3c6c82d0a83b2e15458a9..aadd690fe0714e1b72e77b1081c4827ed074ca46 100644 (file)
@@ -96,6 +96,13 @@ LIBMEMCACHED_API
 memcached_return_t memcached_server_add(memcached_st *ptr,
                                         const char *hostname, in_port_t port);
 
+LIBMEMCACHED_LOCAL
+  memcached_return_t memcached_server_add_parsed(memcached_st *ptr,
+                                                 const char *hostname,
+                                                 size_t hostname_length,
+                                                 in_port_t port,
+                                                 uint32_t weight);
+
 LIBMEMCACHED_API
 memcached_return_t memcached_server_add_udp_with_weight(memcached_st *ptr,
                                                         const char *hostname,
index 5a37dfd9130fdd528e0481e6c404b9c730b90e04..83d342a1f096559cc66d5a66be5e124415783d7c 100644 (file)
@@ -400,21 +400,21 @@ error:
     return rc;
 }
 
-memcached_stat_st *memcached_stat(memcached_st *ptr, char *args, memcached_return_t *error)
+memcached_stat_st *memcached_stat(memcached_st *self, char *args, memcached_return_t *error)
 {
   memcached_return_t rc;
   memcached_stat_st *stats;
 
-  if (! ptr)
+  if (! self)
   {
-    WATCHPOINT_ASSERT(memc_ptr);
+    WATCHPOINT_ASSERT(self);
     return NULL;
   }
 
   WATCHPOINT_ASSERT(error);
 
 
-  unlikely (ptr->flags.use_udp)
+  unlikely (self->flags.use_udp)
   {
     if (error)
       *error= MEMCACHED_NOT_SUPPORTED;
@@ -422,7 +422,7 @@ memcached_stat_st *memcached_stat(memcached_st *ptr, char *args, memcached_retur
     return NULL;
   }
 
-  stats= libmemcached_calloc(ptr, memcached_server_count(ptr), sizeof(memcached_stat_st));
+  stats= libmemcached_calloc(self, memcached_server_count(self), sizeof(memcached_stat_st));
 
   if (! stats)
   {
@@ -433,7 +433,7 @@ memcached_stat_st *memcached_stat(memcached_st *ptr, char *args, memcached_retur
   }
 
   rc= MEMCACHED_SUCCESS;
-  for (uint32_t x= 0; x < memcached_server_count(ptr); x++)
+  for (uint32_t x= 0; x < memcached_server_count(self); x++)
   {
     memcached_return_t temp_return;
     memcached_server_write_instance_st instance;
@@ -441,11 +441,11 @@ memcached_stat_st *memcached_stat(memcached_st *ptr, char *args, memcached_retur
 
     stat_instance= stats + x;
 
-    stat_instance->root= ptr;
+    stat_instance->root= self;
 
-    instance= memcached_server_instance_fetch(ptr, x);
+    instance= memcached_server_instance_fetch(self, x);
 
-    if (ptr->flags.binary_protocol)
+    if (self->flags.binary_protocol)
     {
       temp_return= binary_stats_fetch(stat_instance, args, instance, NULL);
     }
index 191802ab6f48c67d012dbe8846253e422730567b..c31a473a3b50291cc56798c50b2900b129f2fec7 100644 (file)
@@ -108,8 +108,7 @@ static inline memcached_return_t memcached_send(memcached_st *ptr,
       check_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE,
                                     "%s %.*s%.*s %u %llu %lu %llu%s\r\n",
                                     storage_op_string(verb),
-                                    (int)ptr->prefix_key_length,
-                                    ptr->prefix_key,
+                                    memcached_print_array(ptr->prefix_key),
                                     (int)key_length, key, flags,
                                     (unsigned long long)expiration, (unsigned long)value_length,
                                     (unsigned long long)cas,
@@ -132,10 +131,10 @@ static inline memcached_return_t memcached_send(memcached_st *ptr,
       memcpy(buffer_ptr, command, strlen(command));
 
       /* Copy in the key prefix, switch to the buffer_ptr */
-      buffer_ptr= memcpy((buffer_ptr + strlen(command)), ptr->prefix_key, ptr->prefix_key_length);
+      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 + (ptr->prefix_key_length ? ptr->prefix_key_length : 0),
+      buffer_ptr= memcpy(buffer_ptr + memcached_array_size(ptr->prefix_key),
                          key, key_length);
       buffer_ptr+= key_length;
       buffer_ptr[0]=  ' ';
@@ -479,7 +478,7 @@ static memcached_return_t memcached_send_binary(memcached_st *ptr,
 
   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 + ptr->prefix_key_length));
+  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! */
@@ -490,7 +489,7 @@ static memcached_return_t memcached_send_binary(memcached_st *ptr,
     request.message.body.expiration= htonl((uint32_t)expiration);
   }
 
-  request.message.header.request.bodylen= htonl((uint32_t) (key_length + ptr->prefix_key_length + value_length +
+  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)
@@ -515,7 +514,7 @@ static memcached_return_t memcached_send_binary(memcached_st *ptr,
   struct libmemcached_io_vector_st vector[]=
   {
     { .length= send_length, .buffer= request.bytes },
-    { .length= ptr->prefix_key_length, .buffer= ptr->prefix_key },
+    { .length= memcached_array_size(ptr->prefix_key), .buffer= memcached_array_string(ptr->prefix_key) },
     { .length= key_length, .buffer= key },
     { .length= value_length, .buffer= value }
   };
index d1c0f2976b5695860cc4ade9901e32f8301e984a..773ccd032de109e0b078fa0337ec88da3444d162 100644 (file)
@@ -91,6 +91,10 @@ const char *memcached_strerror(memcached_st *ptr, memcached_return_t rc)
     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_MAXIMUM_RETURN:
     return "Gibberish returned!";
   default:
index 34597067b157edcbb818100da12a79061bbbd5d0..10a295814ddb9d8e1fd21d5218854041c0cd3016 100644 (file)
@@ -212,3 +212,12 @@ void memcached_string_set_length(memcached_string_st *self, size_t length)
 {
   self->end= self->string + length;
 }
+
+memcached_string_t memcached_string_make(const char *str, size_t length)
+{
+  memcached_string_t tmp;
+  tmp.c_str= str;
+  tmp.size= length;
+
+  return tmp;
+}
index ca2c1220943a60ae93f9c8b77aaddccd31eb2bf5..5dd7d8dd7b0cdde0ef595f9ac13603d82713d6d9 100644 (file)
@@ -9,6 +9,7 @@
  *
  */
 
+#pragma once
 #ifndef __LIBMEMCACHED_STRING_H__
 #define __LIBMEMCACHED_STRING_H__
 
@@ -33,6 +34,11 @@ struct memcached_string_st {
   } options;
 };
 
+struct memcached_string_t {
+  size_t size;
+  const char *c_str;
+};
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -74,9 +80,18 @@ char *memcached_string_value_mutable(const memcached_string_st *self);
 LIBMEMCACHED_LOCAL
 void memcached_string_set_length(memcached_string_st *self, size_t length);
 
+LIBMEMCACHED_LOCAL
+memcached_string_t memcached_string_make(const char *str, size_t length);
+
 #ifdef __cplusplus
 }
 #endif
 
 
+#ifdef __cplusplus
+#define memcached_string_with_size(X) (X), (static_cast<size_t>((sizeof(X) - 1)))
+#else
+#define memcached_string_with_size(X) (X), ((size_t)((sizeof(X) - 1)))
+#endif
+
 #endif /* __LIBMEMCACHED_STRING_H__ */
index 28a1535c9f92b0ac151fb64dad0838d0ee6cc399..89c1203bcbd6e96cd6acb8dbe179d183c1ea0a91 100644 (file)
@@ -16,6 +16,8 @@ typedef struct memcached_st memcached_st;
 typedef struct memcached_stat_st memcached_stat_st;
 typedef struct memcached_analysis_st memcached_analysis_st;
 typedef struct memcached_result_st memcached_result_st;
+typedef struct memcached_array_st memcached_array_st;
+typedef struct memcached_error_st memcached_error_st;
 
 // All of the flavors of memcache_server_st
 typedef struct memcached_server_st memcached_server_st;
@@ -26,6 +28,7 @@ typedef struct memcached_callback_st memcached_callback_st;
 
 // The following two structures are internal, and never exposed to users.
 typedef struct memcached_string_st memcached_string_st;
+typedef struct memcached_string_t memcached_string_t;
 typedef struct memcached_continuum_item_st memcached_continuum_item_st;
 
 
diff --git a/libtest/failed.cc b/libtest/failed.cc
new file mode 100644 (file)
index 0000000..df02246
--- /dev/null
@@ -0,0 +1,75 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  uTest Framework
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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 <config.h>
+
+#include <libtest/failed.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+struct failed_test_names_st
+{
+  failed_test_names_st(const char *collection_arg, const char *test_arg) :
+    collection(collection_arg),
+    test(test_arg)
+  {
+  }
+
+  std::string collection;
+  std::string test;
+};
+
+typedef std::vector<failed_test_names_st> Failures;
+
+static Failures failures;
+
+void push_failed_test(const char *collection, const char *test)
+{
+  failures.push_back(failed_test_names_st(collection, test));
+}
+
+void print_failed_test(void)
+{
+  for (Failures::iterator iter= failures.begin(); iter != failures.end(); iter++)
+  {
+    std::cerr << "\t" << (*iter).collection << " " << (*iter).test << std::endl;
+  }
+  std::cerr << std::endl;
+}
+
diff --git a/libtest/failed.h b/libtest/failed.h
new file mode 100644 (file)
index 0000000..16d1aec
--- /dev/null
@@ -0,0 +1,53 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  uTest Framework
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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 <libtest/visibility.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LIBTEST_INTERNAL_API
+  void push_failed_test(const char *collection, const char *test);
+
+LIBTEST_INTERNAL_API
+  void print_failed_test(void);
+
+#ifdef __cplusplus
+}
+#endif
index d6d9b18a22963de6b4568ec2a5f44c9e85db7744..f14d9435bb06b0139bf556a82019e9f3da875b73 100644 (file)
@@ -9,12 +9,17 @@
 # All paths should be given relative to the root
 
 noinst_HEADERS+= \
+                libtest/failed.h \
                 libtest/server.h \
-                libtest/test.h
+                libtest/test.h \
+                libtest/visibility.h
 
 noinst_LTLIBRARIES+= libtest/libserver.la
 libtest_libserver_la_SOURCES= libtest/server.c
 
 noinst_LTLIBRARIES+= libtest/libtest.la
-libtest_libtest_la_SOURCES= libtest/test.c
+libtest_libtest_la_SOURCES=\
+                          libtest/test.c
+libtest_libtest_la_CFLAGS= ${AM_CFLAGS} ${NO_CONVERSION}
+libtest_libtest_la_CPPFLAGS= ${AM_CPPFLAGS}
 
index 4349a668fdbf5147d8ac5eeb11f37b99bf224aac..c96ffb5153d546cab133f2cf7e1f230d55cc1719 100644 (file)
@@ -47,7 +47,7 @@ static void global_sleep(void)
 
 static void kill_file(const char *file_buffer)
 {
-  FILE *fp= fopen(file_buffer, "r");
+  FILE *fp;
 
   while ((fp= fopen(file_buffer, "r")))
   {
index 3d11d208464c05f72f885366fa66fd14d45622ce..da989b179638bfd95eed8cacefd012fea200b5f7 100644 (file)
@@ -27,6 +27,7 @@
 #include <libmemcached/memcached.h>
 
 #include <libtest/test.h>
+#include <libtest/failed.h>
 
 static void world_stats_print(world_stats_st *stats)
 {
@@ -298,6 +299,9 @@ skip_pre:
         stats.success++;
         break;
       case TEST_FAILURE:
+#if 0
+        push_failed_test(next->name, run->name);
+#endif
         stats.failed++;
         failed= true;
         break;
@@ -343,6 +347,9 @@ cleanup:
   if (stats.collection_failed || stats.collection_skipped)
   {
     fprintf(stderr, "Some test failures and/or skipped test occurred.\n\n");
+#if 0
+    print_failed_test();
+#endif
   }
   else
   {
index cc0fd78aaaf21c8e5614c0776fdf4631ea355d76..b7a2f5eebf161549a01192e972212618c177cd25 100644 (file)
@@ -1,19 +1,49 @@
-/* uTest
- * Copyright (C) 2006-2009 Brian Aker
- * All rights reserved.
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Gearmand client and server 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.
  *
- * Use and distribution licensed under the BSD license.  See
- * the COPYING file in the parent directory for full text.
  */
 
+#pragma once
+
+#include <libtest/visibility.h>
+
 /*
   Structures for generic tests.
 */
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
@@ -39,9 +69,6 @@ typedef test_return_t (*test_callback_fn)(void *);
 typedef test_return_t (*test_callback_runner_fn)(test_callback_fn, void *);
 typedef test_return_t (*test_callback_error_fn)(test_return_t, void *);
 
-/* Help function for use with gettimeofday() */
-long int timedif(struct timeval a, struct timeval b);
-
 /**
   A structure describing the test case.
 */
@@ -139,14 +166,25 @@ typedef struct {
   uint32_t total;
 } world_stats_st;
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Help function for use with gettimeofday() */
+LIBTEST_API
+long int timedif(struct timeval a, struct timeval b);
+
 /* How we make all of this work :) */
+LIBTEST_API
 void get_world(world_st *world);
 
+LIBTEST_INTERNAL_API
 void create_core(void);
 
 /**
   @note Friendly print function for errors.
 */
+LIBTEST_API
 const char *test_strerror(test_return_t code);
 
 #define test_fail(A) \
@@ -189,6 +227,16 @@ do \
   } \
 } while (0)
 
+#define test_false_with(A,B) \
+do \
+{ \
+  if ((A)) { \
+    fprintf(stderr, "\nAssertion failed at %s:%d: %s with %s\n", __FILE__, __LINE__, #A, (B));\
+    create_core(); \
+    return TEST_FAILURE; \
+  } \
+} while (0)
+
 #define test_strcmp(A,B) \
 do \
 { \
@@ -200,6 +248,23 @@ do \
   } \
 } while (0)
 
+
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+#define AT __FILE__ ":" TOSTRING(__LINE__)
+
+#ifdef __cplusplus
+#define STRING_WITH_LEN(X) (X), (static_cast<size_t>((sizeof(X) - 1)))
+#else
+#define STRING_WITH_LEN(X) (X), ((size_t)((sizeof(X) - 1)))
+#endif
+
+#ifdef __cplusplus
+#define STRING_PARAM_WITH_LEN(X) X, static_cast<size_t>(sizeof(X) - 1)
+#else
+#define STRING_PARAM_WITH_LEN(X) X, (size_t)((sizeof(X) - 1))
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libtest/visibility.h b/libtest/visibility.h
new file mode 100644 (file)
index 0000000..73d01f1
--- /dev/null
@@ -0,0 +1,69 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Gearmand client and server library.
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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
+
+#if defined(BUILDING_LIBTEST)
+# if defined(HAVE_VISIBILITY)
+#  define LIBTEST_API __attribute__ ((visibility("default")))
+#  define LIBTEST_INTERNAL_API __attribute__ ((visibility("hidden")))
+#  define LIBTEST_API_DEPRECATED __attribute__ ((deprecated,visibility("default")))
+#  define LIBTEST_LOCAL  __attribute__ ((visibility("hidden")))
+# elif defined (__SUNPRO_C) && (__SUNPRO_C >= 0x550)
+#  define LIBTEST_API __global
+#  define LIBTEST_INTERNAL_API __hidden
+#  define LIBTEST_API_DEPRECATED __global
+#  define LIBTEST_LOCAL __hidden
+# elif defined(_MSC_VER)
+#  define LIBTEST_API extern __declspec(dllexport)
+#  define LIBTEST_INTERNAL_API extern __declspec(dllexport)
+#  define LIBTEST_DEPRECATED_API extern __declspec(dllexport)
+#  define LIBTEST_LOCAL
+# endif /* defined(HAVE_VISIBILITY) */
+#else  /* defined(BUILDING_LIBTEST) */
+# if defined(_MSC_VER)
+#  define LIBTEST_API extern __declspec(dllimport)
+#  define LIBTEST_INTERNAL_API extern __declspec(dllimport)
+#  define LIBTEST_API_DEPRECATED extern __declspec(dllimport)
+#  define LIBTEST_LOCAL
+# else
+#  define LIBTEST_API
+#  define LIBTEST_INTERNAL_API
+#  define LIBTEST_API_DEPRECATED
+#  define LIBTEST_LOCAL
+# endif /* defined(_MSC_VER) */
+#endif /* defined(BUILDING_LIBTEST) */
index 30ff3434359ae28c601d506d2961db1bc5cdcc71..7ef435ff23b329d257b15407fe776cc51aabebd1 100644 (file)
@@ -129,7 +129,7 @@ AC_DEFUN([PANDORA_WARNINGS],[
       m4_if(PW_LESS_WARNINGS,[no],[
         BASE_WARNINGS_FULL="${W_CONVERSION} -Wstrict-aliasing"
         CC_WARNINGS_FULL="-Wswitch-default -Wswitch-enum -Wwrite-strings"
-        CXX_WARNINGS_FULL="-Weffc++ -Wold-style-cast"
+        CXX_WARNINGS_FULL=""
         NO_OLD_STYLE_CAST="-Wno-old-style-cast"
         NO_EFF_CXX="-Wno-effc++"
       ],[
diff --git a/support/example.cnf b/support/example.cnf
new file mode 100644 (file)
index 0000000..3ccb796
--- /dev/null
@@ -0,0 +1,11 @@
+# http://en.wikipedia.org/wiki/Consistent_hashing
+--distribution=consistent
+
+# Store one additional copy on each node.
+--number-of-replicas=2
+--prefix-key="my_foo"
+--prefix-key="my_prefix"
+--server=localhost:11211
+--server=localhost:11212
+--server=localhost:11213
+--verify-key
index ee9f20f9addf551b83f42502b6f43348afce2d22..d297c01fcfceb2d92f7e01ddfc7a9a03146e6a78 100644 (file)
@@ -82,11 +82,11 @@ you will need to install %{name}-devel.
 %exclude %{_libdir}/libhashkit.la
 %exclude %{_libdir}/libmemcachedutil.la
 %exclude %{_libdir}/libmemcachedprotocol.la
-%{_libdir}/libhashkit.so.0.0.0
+%{_libdir}/libhashkit.so.1.0.0
 %{_libdir}/libmemcached.so.6.0.0
 %{_libdir}/libmemcachedutil.so.1.0.0
 %{_libdir}/libmemcachedprotocol.so.0.0.0
-%{_libdir}/libhashkit.so.0
+%{_libdir}/libhashkit.so.1
 %{_libdir}/libmemcached.so.6
 %{_libdir}/libmemcachedprotocol.so.0
 %{_libdir}/libmemcachedutil.so.1
@@ -104,10 +104,6 @@ you will need to install %{name}-devel.
 %files devel
 %defattr (-,root,root,-) 
 %doc examples
-%{_includedir}/libmemcached/allocators.h
-%{_includedir}/libmemcached/delete.h
-%{_includedir}/libmemcached/fetch.h
-%{_includedir}/libmemcached/flush.h
 %{_includedir}/libhashkit/algorithm.h
 %{_includedir}/libhashkit/behavior.h
 %{_includedir}/libhashkit/configure.h
@@ -117,20 +113,28 @@ you will need to install %{name}-devel.
 %{_includedir}/libhashkit/strerror.h
 %{_includedir}/libhashkit/types.h
 %{_includedir}/libhashkit/visibility.h
+%{_includedir}/libmemcached/allocators.h
 %{_includedir}/libmemcached/analyze.h
+%{_includedir}/libmemcached/array.h
 %{_includedir}/libmemcached/auto.h
 %{_includedir}/libmemcached/behavior.h
 %{_includedir}/libmemcached/callback.h
 %{_includedir}/libmemcached/configure.h
 %{_includedir}/libmemcached/constants.h
+%{_includedir}/libmemcached/delete.h
 %{_includedir}/libmemcached/dump.h
+%{_includedir}/libmemcached/error.h
 %{_includedir}/libmemcached/exception.hpp
+%{_includedir}/libmemcached/fetch.h
+%{_includedir}/libmemcached/flush.h
 %{_includedir}/libmemcached/flush_buffers.h
 %{_includedir}/libmemcached/get.h
 %{_includedir}/libmemcached/hash.h
 %{_includedir}/libmemcached/memcached.h
 %{_includedir}/libmemcached/memcached.hpp
+%{_includedir}/libmemcached/memcached/protocol_binary.h
 %{_includedir}/libmemcached/memcached_util.h
+%{_includedir}/libmemcached/options.h
 %{_includedir}/libmemcached/parse.h
 %{_includedir}/libmemcached/platform.h
 %{_includedir}/libmemcached/protocol/cache.h
@@ -154,7 +158,6 @@ you will need to install %{name}-devel.
 %{_includedir}/libmemcached/version.h
 %{_includedir}/libmemcached/visibility.h
 %{_includedir}/libmemcached/watchpoint.h
-%{_includedir}/libmemcached/memcached/protocol_binary.h
 %{_libdir}/libhashkit.so
 %{_libdir}/libmemcached.so
 %{_libdir}/libmemcachedprotocol.so
diff --git a/tests/basic.cc b/tests/basic.cc
new file mode 100644 (file)
index 0000000..fea343d
--- /dev/null
@@ -0,0 +1,130 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Libmemcached
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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/memcached.h>
+#include <libmemcached/is.h>
+#include <libtest/test.h>
+#include "tests/basic.h"
+
+test_return_t basic_init_test(memcached_st *junk)
+{
+  (void)junk;
+
+  memcached_st memc;
+  memcached_st *memc_ptr;
+
+  memc_ptr= memcached_create(&memc);
+  test_true(memc_ptr);
+  test_false(memcached_is_allocated(&memc));
+  memcached_free(memc_ptr);
+
+  return TEST_SUCCESS;
+}
+
+test_return_t basic_clone_test(memcached_st *memc)
+{
+  memcached_st *memc_ptr;
+
+  memc_ptr= memcached_clone(NULL, memc);
+  test_true(memc_ptr);
+  test_true(memcached_is_allocated(memc_ptr));
+  memcached_free(memc_ptr);
+
+  return TEST_SUCCESS;
+}
+
+test_return_t basic_reset_stack_test(memcached_st *junk)
+{
+  (void)junk;
+  memcached_st memc;
+
+  memcached_create(&memc);
+
+  memcached_reset(&memc);
+  test_false(memcached_is_allocated(&memc));
+
+  memcached_free(&memc);
+  test_false(memcached_is_allocated(&memc));
+
+  return TEST_SUCCESS;
+}
+
+test_return_t basic_reset_heap_test(memcached_st *junk)
+{
+  (void)junk;
+  memcached_st *memc_ptr;
+
+  memc_ptr= memcached_create(NULL);
+  test_true(memcached_is_allocated(memc_ptr));
+
+  memcached_reset(memc_ptr);
+  test_true(memcached_is_allocated(memc_ptr));
+
+  memcached_free(memc_ptr);
+
+  return TEST_SUCCESS;
+}
+
+test_return_t basic_reset_stack_clone_test(memcached_st *memc)
+{
+  memcached_st clone;
+  memcached_st *memc_ptr;
+
+  memset(&clone, 0, sizeof(clone));
+  memc_ptr= memcached_clone(&clone, memc);
+  test_true(memc_ptr);
+
+  memcached_reset(memc_ptr);
+
+  memcached_free(memc_ptr);
+
+  return TEST_SUCCESS;
+}
+
+test_return_t basic_reset_heap_clone_test(memcached_st *memc)
+{
+  memcached_st *memc_ptr;
+
+  memc_ptr= memcached_clone(NULL, memc);
+  test_true(memc_ptr);
+
+  memcached_reset(memc_ptr);
+
+  memcached_free(memc_ptr);
+
+  return TEST_SUCCESS;
+}
diff --git a/tests/basic.h b/tests/basic.h
new file mode 100644 (file)
index 0000000..7c1536e
--- /dev/null
@@ -0,0 +1,66 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Libmemcached
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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 <libtest/visibility.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LIBTEST_INTERNAL_API
+test_return_t basic_init_test(memcached_st *junk);
+
+LIBTEST_INTERNAL_API
+test_return_t basic_clone_test(memcached_st *memc);
+
+LIBTEST_INTERNAL_API
+test_return_t basic_reset_stack_test(memcached_st *junk);
+
+LIBTEST_INTERNAL_API
+test_return_t basic_reset_heap_test(memcached_st *junk);
+
+LIBTEST_INTERNAL_API
+test_return_t basic_reset_stack_clone_test(memcached_st *memc);
+
+LIBTEST_INTERNAL_API
+test_return_t basic_reset_heap_clone_test(memcached_st *memc);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/error_conditions.cc b/tests/error_conditions.cc
new file mode 100644 (file)
index 0000000..b46b3ca
--- /dev/null
@@ -0,0 +1,70 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Libmemcached
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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 <config.h>
+
+#include <libmemcached/memcached.h>
+#include <libmemcached/is.h>
+#include <libtest/test.h>
+#include <tests/error_conditions.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+test_return_t memcached_increment_MEMCACHED_NO_SERVERS(memcached_st *junk)
+{
+  (void)junk;
+  memcached_st *memc_ptr;
+
+  memc_ptr= memcached_create(NULL);
+  test_true(memc_ptr);
+
+  memcached_increment(memc_ptr, memcached_string_with_size("dead key"), 1, NULL);
+  test_true(memcached_last_error(memc_ptr) == MEMCACHED_NO_SERVERS);
+
+  memcached_increment(memc_ptr, memcached_string_with_size("dead key"), 1, NULL);
+  test_true(memcached_last_error(memc_ptr) == MEMCACHED_NO_SERVERS);
+
+  memcached_free(memc_ptr);
+
+  return TEST_SUCCESS;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/error_conditions.h b/tests/error_conditions.h
new file mode 100644 (file)
index 0000000..1bfa9ed
--- /dev/null
@@ -0,0 +1,50 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Libmemcached
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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 <libtest/test.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+test_return_t memcached_increment_MEMCACHED_NO_SERVERS(memcached_st *junk);
+
+#ifdef __cplusplus
+}
+#endif
index 0f2c42a99d050cd3c20954cb6d77cf6058274b03..23feb9e50a6f3e46459684fb0a9196d07081359a 100644 (file)
@@ -345,10 +345,10 @@ static test_return_t hashkit_set_function_test(hashkit_st *hashk)
       continue;
 #endif
 
-    if (rc == HASHKIT_FAILURE && algo == HASHKIT_HASH_CUSTOM)
+    if (rc == HASHKIT_INVALID_ARGUMENT && algo == HASHKIT_HASH_CUSTOM)
       continue;
 
-    test_true(rc == HASHKIT_SUCCESS);
+    test_true_got(rc == HASHKIT_SUCCESS, hashkit_strerror(NULL, rc));
 
     switch (algo)
     {
@@ -448,7 +448,7 @@ static test_return_t hashkit_set_distribution_function_test(hashkit_st *hashk)
     if (rc == HASHKIT_FAILURE && algo == HASHKIT_HASH_HSIEH)
       continue;
 
-    if (rc == HASHKIT_FAILURE && algo == HASHKIT_HASH_CUSTOM)
+    if (rc == HASHKIT_INVALID_ARGUMENT && algo == HASHKIT_HASH_CUSTOM)
       continue;
 
     test_true(rc == HASHKIT_SUCCESS);
index f940caf88b3bb03e0edf57db232291a727d13fc9..232cce936b983abf828241a4735dfa05d295ce27 100644 (file)
@@ -28,10 +28,15 @@ EXTRA_DIST+= \
             tests/output_plus.res
 
 noinst_HEADERS+= \
+                tests/basic.h \
+                tests/error_conditions.h \
                 tests/hash_results.h \
                 tests/ketama_test_cases.h \
                 tests/ketama_test_cases_spy.h \
-                tests/libmemcached_world.h
+                tests/libmemcached_world.h \
+                tests/parser.h \
+                tests/print.h \
+                tests/replication.h
 
 noinst_PROGRAMS+= \
                  tests/atomsmasher \
@@ -44,7 +49,14 @@ noinst_PROGRAMS+= \
                  tests/testudp
 
 tests_testapp_CFLAGS= $(AM_CFLAGS) $(NO_CONVERSION) $(NO_STRICT_ALIASING)
-tests_testapp_SOURCES= tests/mem_functions.c
+tests_testapp_SOURCES= \
+                      tests/basic.cc \
+                      tests/error_conditions.cc \
+                      tests/mem_functions.c \
+                      tests/parser.cc \
+                      tests/print.cc \
+                      tests/replication.cc
+
 tests_testapp_DEPENDENCIES= \
                            $(BUILT_SOURCES) \
                            clients/libgenexec.la \
index c46ef51a1d449c3989eb7150f2658dc78915ba41..a69d35acb6c45745ab8ef177e96ce3a9a2c59395 100644 (file)
 #define SMALL_STRING_LEN 1024
 
 #include <libtest/test.h>
+#include "tests/parser.h"
+#include "tests/replication.h"
+#include "tests/basic.h"
+#include "tests/error_conditions.h"
+#include "tests/print.h"
 
 
 #ifdef HAVE_LIBMEMCACHEDUTIL
@@ -189,21 +194,6 @@ static test_return_t server_sort2_test(memcached_st *ptr)
   return TEST_SUCCESS;
 }
 
-static memcached_return_t server_print_callback(const memcached_st *ptr,
-                                                const memcached_server_st *server,
-                                                void *context)
-{
-  (void)server; // Just in case we aren't printing.
-  (void)ptr;
-  (void)context;
-
-#if 0
-  fprintf(stderr, "%s(%d)", memcached_server_name(server), memcached_server_port(server));
-#endif
-
-  return MEMCACHED_SUCCESS;
-}
-
 static test_return_t memcached_server_remove_test(memcached_st *ptr)
 {
   memcached_return_t rc;
@@ -411,10 +401,10 @@ static test_return_t error_test(memcached_st *memc)
                         2300930706U, 2943759320U, 674306647U, 2400528935U,
                         54481931U, 4186304426U, 1741088401U, 2979625118U,
                         4159057246U, 3425930182U, 2593724503U,  1868899624U,
-                        1769812374U, 2302537950U, 1110330676U };
+                        1769812374U, 2302537950U, 1110330676U, 3365377466U, 
+                        1336171666U, 3365377466U };
 
   // You have updated the memcache_error messages but not updated docs/tests.
-  test_true(MEMCACHED_MAXIMUM_RETURN == 43);
   for (rc= MEMCACHED_SUCCESS; rc < MEMCACHED_MAXIMUM_RETURN; rc++)
   {
     uint32_t hash_val;
@@ -428,6 +418,7 @@ static test_return_t error_test(memcached_st *memc)
     }
     test_true(values[rc] == hash_val);
   }
+  test_true(MEMCACHED_MAXIMUM_RETURN == 45);
 
   return TEST_SUCCESS;
 }
@@ -3966,9 +3957,10 @@ static test_return_t set_prefix(memcached_st *memc)
 
   /* Test a clean set */
   rc= memcached_callback_set(memc, MEMCACHED_CALLBACK_PREFIX_KEY, (void *)key);
-  test_true(rc == MEMCACHED_SUCCESS);
+  test_true_got(rc == MEMCACHED_SUCCESS, memcached_last_error_message(memc));
 
   value= memcached_callback_get(memc, MEMCACHED_CALLBACK_PREFIX_KEY, &rc);
+  test_true(value);
   test_true(memcmp(value, key, 4) == 0);
   test_true(rc == MEMCACHED_SUCCESS);
 
@@ -3977,6 +3969,7 @@ static test_return_t set_prefix(memcached_st *memc)
   test_true(rc == MEMCACHED_SUCCESS);
 
   value= memcached_callback_get(memc, MEMCACHED_CALLBACK_PREFIX_KEY, &rc);
+  test_false(value);
   test_true(rc == MEMCACHED_FAILURE);
 
   /* Now setup for main test */
@@ -3984,6 +3977,7 @@ static test_return_t set_prefix(memcached_st *memc)
   test_true(rc == MEMCACHED_SUCCESS);
 
   value= memcached_callback_get(memc, MEMCACHED_CALLBACK_PREFIX_KEY, &rc);
+  test_true(value);
   test_true(rc == MEMCACHED_SUCCESS);
   test_true(memcmp(value, key, 4) == 0);
 
@@ -3996,6 +3990,7 @@ static test_return_t set_prefix(memcached_st *memc)
     test_true(rc == MEMCACHED_SUCCESS);
 
     value= memcached_callback_get(memc, MEMCACHED_CALLBACK_PREFIX_KEY, &rc);
+    test_false(value);
     test_true(rc == MEMCACHED_FAILURE);
     test_true(value == NULL);
 
@@ -4574,259 +4569,6 @@ static test_return_t ping_test(memcached_st *memc)
 }
 #endif
 
-static test_return_t replication_set_test(memcached_st *memc)
-{
-  memcached_return_t rc;
-  memcached_st *memc_clone= memcached_clone(NULL, memc);
-  memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 0);
-
-  rc= memcached_set(memc, "bubba", 5, "0", 1, 0, 0);
-  test_true(rc == MEMCACHED_SUCCESS);
-
-  /*
-  ** We are using the quiet commands to store the replicas, so we need
-  ** to ensure that all of them are processed before we can continue.
-  ** In the test we go directly from storing the object to trying to
-  ** receive the object from all of the different servers, so we
-  ** could end up in a race condition (the memcached server hasn't yet
-  ** processed the quiet command from the replication set when it process
-  ** the request from the other client (created by the clone)). As a
-  ** workaround for that we call memcached_quit to send the quit command
-  ** to the server and wait for the response ;-) If you use the test code
-  ** as an example for your own code, please note that you shouldn't need
-  ** to do this ;-)
-  */
-  memcached_quit(memc);
-
-  /*
-  ** "bubba" should now be stored on all of our servers. We don't have an
-  ** easy to use API to address each individual server, so I'll just iterate
-  ** through a bunch of "master keys" and I should most likely hit all of the
-  ** servers...
-  */
-  for (int x= 'a'; x <= 'z'; ++x)
-  {
-    char key[2]= { [0]= (char)x };
-    size_t len;
-    uint32_t flags;
-    char *val= memcached_get_by_key(memc_clone, key, 1, "bubba", 5,
-                                    &len, &flags, &rc);
-    test_true(rc == MEMCACHED_SUCCESS);
-    test_true(val != NULL);
-    free(val);
-  }
-
-  memcached_free(memc_clone);
-
-  return TEST_SUCCESS;
-}
-
-static test_return_t replication_get_test(memcached_st *memc)
-{
-  memcached_return_t rc;
-
-  /*
-   * Don't do the following in your code. I am abusing the internal details
-   * within the library, and this is not a supported interface.
-   * This is to verify correct behavior in the library
-   */
-  for (uint32_t host= 0; host < memcached_server_count(memc); ++host)
-  {
-    memcached_st *memc_clone= memcached_clone(NULL, memc);
-    memcached_server_instance_st instance=
-      memcached_server_instance_by_position(memc_clone, host);
-
-    ((memcached_server_write_instance_st)instance)->port= 0;
-
-    for (int x= 'a'; x <= 'z'; ++x)
-    {
-      char key[2]= { [0]= (char)x };
-      size_t len;
-      uint32_t flags;
-      char *val= memcached_get_by_key(memc_clone, key, 1, "bubba", 5,
-                                      &len, &flags, &rc);
-      test_true(rc == MEMCACHED_SUCCESS);
-      test_true(val != NULL);
-      free(val);
-    }
-
-    memcached_free(memc_clone);
-  }
-
-  return TEST_SUCCESS;
-}
-
-static test_return_t replication_mget_test(memcached_st *memc)
-{
-  memcached_return_t rc;
-  memcached_st *memc_clone= memcached_clone(NULL, memc);
-  memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 0);
-
-  const char *keys[]= { "bubba", "key1", "key2", "key3" };
-  size_t len[]= { 5, 4, 4, 4 };
-
-  for (size_t x= 0; x< 4; ++x)
-  {
-    rc= memcached_set(memc, keys[x], len[x], "0", 1, 0, 0);
-    test_true(rc == MEMCACHED_SUCCESS);
-  }
-
-  /*
-  ** We are using the quiet commands to store the replicas, so we need
-  ** to ensure that all of them are processed before we can continue.
-  ** In the test we go directly from storing the object to trying to
-  ** receive the object from all of the different servers, so we
-  ** could end up in a race condition (the memcached server hasn't yet
-  ** processed the quiet command from the replication set when it process
-  ** the request from the other client (created by the clone)). As a
-  ** workaround for that we call memcached_quit to send the quit command
-  ** to the server and wait for the response ;-) If you use the test code
-  ** as an example for your own code, please note that you shouldn't need
-  ** to do this ;-)
-  */
-  memcached_quit(memc);
-
-  /*
-   * Don't do the following in your code. I am abusing the internal details
-   * within the library, and this is not a supported interface.
-   * This is to verify correct behavior in the library
-   */
-  memcached_result_st result_obj;
-  for (uint32_t host= 0; host < memc_clone->number_of_hosts; host++)
-  {
-    memcached_st *new_clone= memcached_clone(NULL, memc);
-    memcached_server_instance_st instance=
-      memcached_server_instance_by_position(new_clone, host);
-    ((memcached_server_write_instance_st)instance)->port= 0;
-
-    for (int x= 'a'; x <= 'z'; ++x)
-    {
-      char key[2]= { [0]= (char)x, [1]= 0 };
-
-      rc= memcached_mget_by_key(new_clone, key, 1, keys, len, 4);
-      test_true(rc == MEMCACHED_SUCCESS);
-
-      memcached_result_st *results= memcached_result_create(new_clone, &result_obj);
-      test_true(results);
-
-      int hits= 0;
-      while ((results= memcached_fetch_result(new_clone, &result_obj, &rc)) != NULL)
-      {
-        hits++;
-      }
-      test_true(hits == 4);
-      memcached_result_free(&result_obj);
-    }
-
-    memcached_free(new_clone);
-  }
-
-  memcached_free(memc_clone);
-
-  return TEST_SUCCESS;
-}
-
-static test_return_t replication_randomize_mget_test(memcached_st *memc)
-{
-  memcached_result_st result_obj;
-  memcached_return_t rc;
-  memcached_st *memc_clone= memcached_clone(NULL, memc);
-  memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 3);
-  memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ, 1);
-
-  const char *keys[]= { "key1", "key2", "key3", "key4", "key5", "key6", "key7" };
-  size_t len[]= { 4, 4, 4, 4, 4, 4, 4 };
-
-  for (size_t x= 0; x< 7; ++x)
-  {
-    rc= memcached_set(memc, keys[x], len[x], "1", 1, 0, 0);
-    test_true(rc == MEMCACHED_SUCCESS);
-  }
-
-  memcached_quit(memc);
-
-  for (size_t x= 0; x< 7; ++x)
-  {
-    const char key[2]= { [0]= (const char)x };
-
-    rc= memcached_mget_by_key(memc_clone, key, 1, keys, len, 7);
-    test_true(rc == MEMCACHED_SUCCESS);
-
-    memcached_result_st *results= memcached_result_create(memc_clone, &result_obj);
-    test_true(results);
-
-    int hits= 0;
-    while ((results= memcached_fetch_result(memc_clone, &result_obj, &rc)) != NULL)
-    {
-      ++hits;
-    }
-    test_true(hits == 7);
-    memcached_result_free(&result_obj);
-  }
-  memcached_free(memc_clone);
-  return TEST_SUCCESS;
-}
-
-static test_return_t replication_delete_test(memcached_st *memc)
-{
-  memcached_return_t rc;
-  memcached_st *memc_clone= memcached_clone(NULL, memc);
-  /* Delete the items from all of the servers except 1 */
-  uint64_t repl= memcached_behavior_get(memc,
-                                        MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS);
-  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, --repl);
-
-  const char *keys[]= { "bubba", "key1", "key2", "key3" };
-  size_t len[]= { 5, 4, 4, 4 };
-
-  for (size_t x= 0; x< 4; ++x)
-  {
-    rc= memcached_delete_by_key(memc, keys[0], len[0], keys[x], len[x], 0);
-    test_true(rc == MEMCACHED_SUCCESS);
-  }
-
-  /*
-   * Don't do the following in your code. I am abusing the internal details
-   * within the library, and this is not a supported interface.
-   * This is to verify correct behavior in the library
-   */
-  uint32_t hash= memcached_generate_hash(memc, keys[0], len[0]);
-  for (uint32_t x= 0; x < (repl + 1); ++x)
-  {
-    memcached_server_instance_st instance=
-      memcached_server_instance_by_position(memc_clone, x);
-
-    ((memcached_server_write_instance_st)instance)->port= 0;
-    if (++hash == memc_clone->number_of_hosts)
-      hash= 0;
-  }
-
-  memcached_result_st result_obj;
-  for (uint32_t host= 0; host < memc_clone->number_of_hosts; ++host)
-  {
-    for (size_t x= 'a'; x <= 'z'; ++x)
-    {
-      const char key[2]= { [0]= (const char)x };
-
-      rc= memcached_mget_by_key(memc_clone, key, 1, keys, len, 4);
-      test_true(rc == MEMCACHED_SUCCESS);
-
-      memcached_result_st *results= memcached_result_create(memc_clone, &result_obj);
-      test_true(results);
-
-      int hits= 0;
-      while ((results= memcached_fetch_result(memc_clone, &result_obj, &rc)) != NULL)
-      {
-        ++hits;
-      }
-      test_true(hits == 4);
-      memcached_result_free(&result_obj);
-    }
-  }
-  memcached_free(memc_clone);
-
-  return TEST_SUCCESS;
-}
 
 #if 0
 static test_return_t hash_sanity_test (memcached_st *memc)
@@ -4853,7 +4595,7 @@ static test_return_t hash_sanity_test (memcached_st *memc)
 
 static test_return_t hsieh_avaibility_test (memcached_st *memc)
 {
-  memcached_return_t expected_rc= MEMCACHED_FAILURE;
+  memcached_return_t expected_rc= MEMCACHED_INVALID_ARGUMENTS;
 #ifdef HAVE_HSIEH_HASH
   expected_rc= MEMCACHED_SUCCESS;
 #endif
@@ -4866,7 +4608,7 @@ static test_return_t hsieh_avaibility_test (memcached_st *memc)
 
 static test_return_t murmur_avaibility_test (memcached_st *memc)
 {
-  memcached_return_t expected_rc= MEMCACHED_FAILURE;
+  memcached_return_t expected_rc= MEMCACHED_INVALID_ARGUMENTS;
 #ifdef HAVE_MURMUR_HASH
   expected_rc= MEMCACHED_SUCCESS;
 #endif
@@ -6368,6 +6110,16 @@ test_st behavior_tests[] ={
   {0, 0, 0}
 };
 
+test_st basic_tests[] ={
+  {"init", 1, (test_callback_fn)basic_init_test},
+  {"clone", 1, (test_callback_fn)basic_clone_test},
+  {"reset", 1, (test_callback_fn)basic_reset_stack_test},
+  {"reset heap", 1, (test_callback_fn)basic_reset_heap_test},
+  {"reset stack clone", 1, (test_callback_fn)basic_reset_stack_clone_test},
+  {"reset heap clone", 1, (test_callback_fn)basic_reset_heap_clone_test},
+  {0, 0, 0}
+};
+
 test_st regression_binary_vs_block[] ={
   {"block add", 1, (test_callback_fn)block_add_regression},
   {"binary add", 1, (test_callback_fn)binary_add_regression},
@@ -6556,10 +6308,30 @@ test_st hash_tests[] ={
 };
 
 test_st error_conditions[] ={
-  {"memcached_get_MEMCACHED_ERRNO", 0, (test_callback_fn)memcached_get_MEMCACHED_ERRNO },
-  {"memcached_get_MEMCACHED_NOTFOUND", 0, (test_callback_fn)memcached_get_MEMCACHED_NOTFOUND },
-  {"memcached_get_by_key_MEMCACHED_ERRNO", 0, (test_callback_fn)memcached_get_by_key_MEMCACHED_ERRNO },
-  {"memcached_get_by_key_MEMCACHED_NOTFOUND", 0, (test_callback_fn)memcached_get_by_key_MEMCACHED_NOTFOUND },
+  {"memcached_get(MEMCACHED_ERRNO)", 0, (test_callback_fn)memcached_get_MEMCACHED_ERRNO },
+  {"memcached_get(MEMCACHED_NOTFOUND)", 0, (test_callback_fn)memcached_get_MEMCACHED_NOTFOUND },
+  {"memcached_get_by_key(MEMCACHED_ERRNO)", 0, (test_callback_fn)memcached_get_by_key_MEMCACHED_ERRNO },
+  {"memcached_get_by_key(MEMCACHED_NOTFOUND)", 0, (test_callback_fn)memcached_get_by_key_MEMCACHED_NOTFOUND },
+  {"memcached_get_by_key(MEMCACHED_NOTFOUND)", 0, (test_callback_fn)memcached_get_by_key_MEMCACHED_NOTFOUND },
+  {"memcached_increment(MEMCACHED_NO_SERVERS)", 0, (test_callback_fn)memcached_increment_MEMCACHED_NO_SERVERS },
+  {0, 0, (test_callback_fn)0}
+};
+
+
+test_st parser_tests[] ={
+  {"behavior", 0, (test_callback_fn)behavior_parser_test },
+  {"boolean_options", 0, (test_callback_fn)parser_boolean_options_test },
+  {"configure_file", 0, (test_callback_fn)memcached_create_with_options_with_filename },
+  {"distribtions", 0, (test_callback_fn)parser_distribution_test },
+  {"hash", 0, (test_callback_fn)parser_hash_test },
+  {"libmemcached_check_configuration", 0, (test_callback_fn)libmemcached_check_configuration_test },
+  {"libmemcached_check_configuration_with_filename", 0, (test_callback_fn)libmemcached_check_configuration_with_filename_test },
+  {"memcached_parse_configure_file", 0, (test_callback_fn)memcached_parse_configure_file_test },
+  {"number_options", 0, (test_callback_fn)parser_number_options_test },
+  {"randomly generated options", 0, (test_callback_fn)random_statement_build_test },
+  {"prefix_key", 0, (test_callback_fn)parser_key_prefix_test },
+  {"server", 0, (test_callback_fn)server_test },
+  {"servers", 0, (test_callback_fn)servers_test },
   {0, 0, (test_callback_fn)0}
 };
 
@@ -6567,6 +6339,7 @@ collection_st collection[] ={
 #if 0
   {"hash_sanity", 0, 0, hash_sanity},
 #endif
+  {"basic", 0, 0, basic_tests},
   {"hsieh_availability", 0, 0, hsieh_availability},
   {"murmur_availability", 0, 0, murmur_availability},
   {"block", 0, 0, tests},
@@ -6625,6 +6398,7 @@ collection_st collection[] ={
   {"behaviors", 0, 0, behavior_tests},
   {"regression_binary_vs_block", (test_callback_fn)key_setup, (test_callback_fn)key_teardown, regression_binary_vs_block},
   {"error_conditions", 0, 0, error_conditions},
+  {"parser", 0, 0, parser_tests},
   {0, 0, 0, 0}
 };
 
diff --git a/tests/parser.cc b/tests/parser.cc
new file mode 100644 (file)
index 0000000..717859b
--- /dev/null
@@ -0,0 +1,522 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Gearmand client and server library.
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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 <config.h>
+
+#include <vector>
+#include <iostream>
+#include <string>
+
+#include <libmemcached/memcached.h>
+
+#include "tests/parser.h"
+#include "tests/print.h"
+
+enum scanner_type_t
+{
+  NIL,
+  UNSIGNED,
+  SIGNED,
+  ARRAY
+};
+
+
+struct scanner_string_st {
+  const char *c_str;
+  size_t size;
+};
+
+static inline scanner_string_st scanner_string(const char *arg, size_t arg_size)
+{
+  scanner_string_st local= { arg, arg_size };
+  return local;
+}
+
+#define make_scanner_string(X) scanner_string((X), static_cast<size_t>(sizeof(X) - 1))
+
+static struct scanner_string_st scanner_string_null= { 0, 0};
+
+struct scanner_variable_t {
+  enum scanner_type_t type;
+  struct scanner_string_st option;
+  struct scanner_string_st result;
+  test_return_t (*check_func)(memcached_st *memc, const scanner_string_st &hostname);
+};
+
+// Check and make sure the first host is what we expect it to be
+static test_return_t __check_host(memcached_st *memc, const scanner_string_st &hostname)
+{
+  memcached_server_instance_st instance=
+    memcached_server_instance_by_position(memc, 0);
+
+  test_true(instance);
+
+  const char *first_hostname = memcached_server_name(instance);
+  test_true(first_hostname);
+  test_strcmp(first_hostname, hostname.c_str);
+
+  return TEST_SUCCESS;
+}
+
+// Check and make sure the prefix_key is what we expect it to be
+static test_return_t __check_prefix_key(memcached_st *memc, const scanner_string_st &hostname)
+{
+  memcached_server_instance_st instance=
+    memcached_server_instance_by_position(memc, 0);
+
+  test_true(instance);
+
+  const char *first_hostname = memcached_server_name(instance);
+  test_true(first_hostname);
+  test_strcmp(first_hostname, hostname.c_str);
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t __check_IO_MSG_WATERMARK(memcached_st *memc, const scanner_string_st &value)
+{
+  uint64_t value_number;
+
+  value_number= atoll(value.c_str);
+
+  test_true(memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK) == value_number);
+  return TEST_SUCCESS;
+}
+
+static test_return_t __check_AUTO_EJECT_HOSTS(memcached_st *memc, const scanner_string_st &value)
+{
+  (void)value;
+  test_true(memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS));
+  return TEST_SUCCESS;
+}
+
+static test_return_t __check_CACHE_LOOKUPS(memcached_st *memc, const scanner_string_st &value)
+{
+  (void)value;
+  test_true(memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_CACHE_LOOKUPS));
+  return TEST_SUCCESS;
+}
+
+static test_return_t __check_NOREPLY(memcached_st *memc, const scanner_string_st &value)
+{
+  (void)value;
+  test_true(memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_NOREPLY));
+  return TEST_SUCCESS;
+}
+
+static test_return_t __check_VERIFY_KEY(memcached_st *memc, const scanner_string_st &value)
+{
+  (void)value;
+  test_true(memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_VERIFY_KEY));
+  return TEST_SUCCESS;
+}
+
+static test_return_t __check_distribution_RANDOM(memcached_st *memc, const scanner_string_st &value)
+{
+  (void)value;
+  test_true(memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_DISTRIBUTION) == MEMCACHED_DISTRIBUTION_RANDOM);
+  return TEST_SUCCESS;
+}
+
+scanner_variable_t test_server_strings[]= {
+  { ARRAY, make_scanner_string("--server=localhost"), make_scanner_string("localhost"), __check_host },
+  { ARRAY, make_scanner_string("--server=10.0.2.1"), make_scanner_string("10.0.2.1"), __check_host },
+  { ARRAY, make_scanner_string("--server=example.com"), make_scanner_string("example.com"), __check_host },
+  { ARRAY, make_scanner_string("--server=localhost:30"), make_scanner_string("localhost"), __check_host },
+  { ARRAY, make_scanner_string("--server=10.0.2.1:20"), make_scanner_string("10.0.2.1"), __check_host },
+  { ARRAY, make_scanner_string("--server=example.com:1024"), make_scanner_string("example.com"), __check_host },
+  { NIL, scanner_string_null, scanner_string_null, NULL }
+};
+
+scanner_variable_t test_servers_strings[]= {
+  { ARRAY, make_scanner_string("--servers=localhost:11221,localhost:11222,localhost:11223,localhost:11224,localhost:11225"), scanner_string_null, NULL },
+  { ARRAY, make_scanner_string("--servers=a.example.com:81,localhost:82,b.example.com"), scanner_string_null, NULL },
+  { ARRAY, make_scanner_string("--servers=localhost,localhost:80"), scanner_string_null, NULL },
+  { NIL, scanner_string_null, scanner_string_null, NULL}
+};
+
+
+scanner_variable_t bad_test_strings[]= {
+  { ARRAY, make_scanner_string("-servers=localhost:11221,localhost:11222,localhost:11223,localhost:11224,localhost:11225"), scanner_string_null, NULL },
+  { ARRAY, make_scanner_string("-- servers=a.example.com:81,localhost:82,b.example.com"), scanner_string_null, NULL },
+  { ARRAY, make_scanner_string("--servers=localhost+80"), scanner_string_null, NULL},
+  { ARRAY, make_scanner_string("--servers=localhost.com."), scanner_string_null, NULL},
+  { ARRAY, make_scanner_string("--server=localhost.com."), scanner_string_null, NULL},
+  { ARRAY, make_scanner_string("--server=localhost.com.:80"), scanner_string_null, NULL},
+  { NIL, scanner_string_null, scanner_string_null, NULL}
+};
+
+scanner_variable_t test_number_options[]= {
+  { ARRAY,  make_scanner_string("--CONNECT_TIMEOUT=456"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--IO_MSG_WATERMARK=456"), make_scanner_string("456"), __check_IO_MSG_WATERMARK },
+  { ARRAY,  make_scanner_string("--IO_BYTES_WATERMARK=456"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--IO_KEY_PREFETCH=456"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--NUMBER_OF_REPLICAS=456"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--POLL_TIMEOUT=456"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--RCV_TIMEOUT=456"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--RETRY_TIMEOUT=456"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--SERVER_FAILURE_LIMIT=456"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--SND_TIMEOUT=456"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--SOCKET_RECV_SIZE=456"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--SOCKET_SEND_SIZE=456"), scanner_string_null, NULL },
+  { NIL, scanner_string_null, scanner_string_null, NULL}
+};
+
+scanner_variable_t test_boolean_options[]= {
+  { ARRAY,  make_scanner_string("--AUTO_EJECT_HOSTS"), scanner_string_null, __check_AUTO_EJECT_HOSTS },
+  { ARRAY,  make_scanner_string("--BINARY_PROTOCOL"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--BUFFER_REQUESTS"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--CACHE_LOOKUPS"), scanner_string_null, __check_CACHE_LOOKUPS },
+#if 0 // Not all platforms support
+  { ARRAY,  make_scanner_string("--CORK"), scanner_string_null, NULL },
+#endif
+  { ARRAY,  make_scanner_string("--HASH_WITH_PREFIX_KEY"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--KETAMA"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--KETAMA_WEIGHTED"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--NOREPLY"), scanner_string_null, __check_NOREPLY },
+  { ARRAY,  make_scanner_string("--RANDOMIZE_REPLICA_READ"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--SORT_HOSTS"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--SUPPORT_CAS"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--TCP_NODELAY"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--TCP_KEEPALIVE"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--TCP_KEEPIDLE"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--USE_UDP"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--VERIFY_KEY"), scanner_string_null, __check_VERIFY_KEY },
+  { NIL, scanner_string_null, scanner_string_null, NULL}
+};
+
+scanner_variable_t prefix_key_strings[]= {
+  { ARRAY, make_scanner_string("--PREFIX_KEY=foo"), make_scanner_string("foo"), __check_prefix_key },
+  { ARRAY, make_scanner_string("--PREFIX-KEY=\"foo\""), make_scanner_string("foo"), __check_prefix_key },
+  { ARRAY, make_scanner_string("--PREFIX-KEY=\"This is a very long key\""), make_scanner_string("This is a very long key"), __check_prefix_key },
+  { NIL, scanner_string_null, scanner_string_null, NULL}
+};
+
+scanner_variable_t distribution_strings[]= {
+  { ARRAY,  make_scanner_string("--DISTRIBUTION=consistent"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--DISTRIBUTION=random"), scanner_string_null, __check_distribution_RANDOM },
+  { ARRAY,  make_scanner_string("--DISTRIBUTION=modula"), scanner_string_null, NULL },
+  { NIL, scanner_string_null, scanner_string_null, NULL}
+};
+
+scanner_variable_t hash_strings[]= {
+  { ARRAY,  make_scanner_string("--HASH=CRC"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--HASH=FNV1A_32"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--HASH=FNV1A_64"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--HASH=FNV1_32"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--HASH=FNV1_64"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--HASH=JENKINS"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--HASH=MD5"), scanner_string_null, NULL },
+  { ARRAY,  make_scanner_string("--HASH=MURMUR"), scanner_string_null, NULL },
+  { NIL, scanner_string_null, scanner_string_null, NULL}
+};
+
+
+static test_return_t _test_option(scanner_variable_t *scanner, bool test_true= true)
+{
+  (void)test_true;
+  memcached_st *memc;
+  memc= memcached_create(NULL);
+
+  for (scanner_variable_t *ptr= scanner; ptr->type != NIL; ptr++)
+  {
+    memcached_return_t rc;
+    rc= memcached_parse_configuration(memc, ptr->option.c_str, ptr->option.size);
+    if (test_true)
+    {
+      if (rc != MEMCACHED_SUCCESS)
+        memcached_error_print(memc);
+
+      test_true(rc == MEMCACHED_SUCCESS);
+
+      if (ptr->check_func)
+      {
+        (*ptr->check_func)(memc, ptr->result);
+      }
+    }
+    else
+    {
+      test_false_with(rc == MEMCACHED_SUCCESS, ptr->option.c_str);
+    }
+    memcached_reset(memc);
+  }
+  memcached_free(memc);
+
+  return TEST_SUCCESS;
+}
+
+test_return_t server_test(memcached_st *junk)
+{
+  (void)junk;
+  return _test_option(test_server_strings);
+}
+
+test_return_t servers_test(memcached_st *junk)
+{
+  (void)junk;
+
+  test_return_t rc;
+  if ((rc= _test_option(test_server_strings)) != TEST_SUCCESS)
+  {
+    return rc;
+  }
+
+#if 0
+    memcached_server_fn callbacks[1];
+    callbacks[0]= server_print_callback;
+    memcached_server_cursor(memc, callbacks, NULL,  1);
+#endif
+
+  if ((rc= _test_option(bad_test_strings, false)) != TEST_SUCCESS)
+  {
+    return rc;
+  }
+
+  return TEST_SUCCESS;
+}
+
+test_return_t parser_number_options_test(memcached_st *junk)
+{
+  (void)junk;
+  return _test_option(test_number_options);
+}
+
+test_return_t parser_boolean_options_test(memcached_st *junk)
+{
+  (void)junk;
+  return _test_option(test_boolean_options);
+}
+
+test_return_t behavior_parser_test(memcached_st *junk)
+{
+  (void)junk;
+  return TEST_SUCCESS;
+}
+
+test_return_t parser_hash_test(memcached_st *junk)
+{
+  (void)junk;
+  return _test_option(hash_strings);
+}
+
+test_return_t parser_distribution_test(memcached_st *junk)
+{
+  (void)junk;
+  return _test_option(distribution_strings);
+}
+
+test_return_t parser_key_prefix_test(memcached_st *junk)
+{
+  (void)junk;
+  return _test_option(distribution_strings);
+}
+
+test_return_t memcached_parse_configure_file_test(memcached_st *junk)
+{
+  (void)junk;
+  memcached_st memc;
+  memcached_st *memc_ptr= memcached_create(&memc);
+
+  test_true(memc_ptr);
+
+  memcached_return_t rc= memcached_parse_configure_file(memc_ptr, memcached_string_with_size("support/example.cnf"));
+  test_true_got(rc == MEMCACHED_SUCCESS, memcached_last_error_message(memc_ptr) ? memcached_last_error_message(memc_ptr) : memcached_strerror(NULL, rc));
+  memcached_free(memc_ptr);
+
+  return TEST_SUCCESS;
+}
+
+test_return_t memcached_create_with_options_with_filename(memcached_st *junk)
+{
+  (void)junk;
+
+  memcached_st *memc_ptr;
+  memc_ptr= memcached_create_with_options(STRING_WITH_LEN("--CONFIGURE-FILE=\"support/example.cnf\""));
+  test_true_got(memc_ptr, memcached_last_error_message(memc_ptr));
+  memcached_free(memc_ptr);
+
+  return TEST_SUCCESS;
+}
+
+test_return_t libmemcached_check_configuration_with_filename_test(memcached_st *junk)
+{
+  (void)junk;
+  memcached_return_t rc;
+  char buffer[BUFSIZ];
+
+  rc= libmemcached_check_configuration(STRING_WITH_LEN("--CONFIGURE-FILE=\"support/example.cnf\""), buffer, sizeof(buffer));
+  test_true_got(rc == MEMCACHED_SUCCESS, buffer);
+
+  rc= libmemcached_check_configuration(STRING_WITH_LEN("--CONFIGURE-FILE=support/example.cnf"), buffer, sizeof(buffer));
+  test_false_with(rc == MEMCACHED_SUCCESS, buffer);
+
+  rc= libmemcached_check_configuration(STRING_WITH_LEN("--CONFIGURE-FILE=\"bad-path/example.cnf\""), buffer, sizeof(buffer));
+  test_true_got(rc == MEMCACHED_ERRNO, buffer);
+
+  return TEST_SUCCESS;
+}
+
+test_return_t libmemcached_check_configuration_test(memcached_st *junk)
+{
+  (void)junk;
+
+  memcached_return_t rc;
+  char buffer[BUFSIZ];
+
+  rc= libmemcached_check_configuration(STRING_WITH_LEN("--server=localhost"), buffer, sizeof(buffer));
+  test_true_got(rc == MEMCACHED_SUCCESS, buffer);
+
+  rc= libmemcached_check_configuration(STRING_WITH_LEN("--dude=localhost"), buffer, sizeof(buffer));
+  test_false_with(rc == MEMCACHED_SUCCESS, buffer);
+  test_true(rc == MEMCACHED_PARSE_ERROR);
+
+  return TEST_SUCCESS;
+}
+
+test_return_t memcached_create_with_options_test(memcached_st *junk)
+{
+  (void)junk;
+
+  memcached_st *memc_ptr;
+  memc_ptr= memcached_create_with_options(STRING_WITH_LEN("--server=localhost"));
+  test_true_got(memc_ptr, memcached_last_error_message(memc_ptr));
+  memcached_free(memc_ptr);
+
+  memc_ptr= memcached_create_with_options(STRING_WITH_LEN("--dude=localhost"));
+  test_false_with(memc_ptr, memcached_last_error_message(memc_ptr));
+
+  return TEST_SUCCESS;
+}
+
+test_return_t test_include_keyword(memcached_st *junk)
+{
+  (void)junk;
+  char buffer[BUFSIZ];
+  memcached_return_t rc;
+  rc= libmemcached_check_configuration(STRING_WITH_LEN("INCLUDE \"support/example.cnf\""), buffer, sizeof(buffer));
+  test_true_got(rc == MEMCACHED_SUCCESS, buffer);
+
+  return TEST_SUCCESS;
+}
+
+test_return_t test_end_keyword(memcached_st *junk)
+{
+  (void)junk;
+  char buffer[BUFSIZ];
+  memcached_return_t rc;
+  rc= libmemcached_check_configuration(STRING_WITH_LEN("--server=localhost END bad keywords"), buffer, sizeof(buffer));
+  test_true_got(rc == MEMCACHED_SUCCESS, buffer);
+
+  return TEST_SUCCESS;
+}
+
+test_return_t test_reset_keyword(memcached_st *junk)
+{
+  (void)junk;
+  char buffer[BUFSIZ];
+  memcached_return_t rc;
+  rc= libmemcached_check_configuration(STRING_WITH_LEN("--server=localhost reset --server=bad.com"), buffer, sizeof(buffer));
+  test_true_got(rc == MEMCACHED_SUCCESS, buffer);
+
+  return TEST_SUCCESS;
+}
+
+test_return_t test_error_keyword(memcached_st *junk)
+{
+  (void)junk;
+  char buffer[BUFSIZ];
+  memcached_return_t rc;
+  rc= libmemcached_check_configuration(STRING_WITH_LEN("--server=localhost ERROR --server=bad.com"), buffer, sizeof(buffer));
+  test_true_got(rc != MEMCACHED_SUCCESS, buffer);
+
+  return TEST_SUCCESS;
+}
+
+#define RANDOM_STRINGS 50
+test_return_t random_statement_build_test(memcached_st *junk)
+{
+  (void)junk;
+  std::vector<scanner_string_st *> option_list;
+
+  for (scanner_variable_t *ptr= test_server_strings; ptr->type != NIL; ptr++)
+    option_list.push_back(&ptr->option);
+
+#if 0
+  for (scanner_variable_t *ptr= test_servers_strings; ptr->type != NIL; ptr++)
+    option_list.push_back(&ptr->option);
+#endif
+
+  for (scanner_variable_t *ptr= test_number_options; ptr->type != NIL; ptr++)
+    option_list.push_back(&ptr->option);
+
+  for (scanner_variable_t *ptr= test_boolean_options; ptr->type != NIL; ptr++)
+    option_list.push_back(&ptr->option);
+
+  for (scanner_variable_t *ptr= prefix_key_strings; ptr->type != NIL; ptr++)
+    option_list.push_back(&ptr->option);
+
+  for (scanner_variable_t *ptr= distribution_strings; ptr->type != NIL; ptr++)
+    option_list.push_back(&ptr->option);
+
+  for (scanner_variable_t *ptr= hash_strings; ptr->type != NIL; ptr++)
+    option_list.push_back(&ptr->option);
+
+  for (uint32_t x= 0; x < RANDOM_STRINGS; x++)
+  {
+    std::string random_options;
+
+    uint32_t number_of= random() % option_list.size();
+    for (uint32_t options= 0; options < number_of; options++)
+    {
+      random_options+= option_list[random() % option_list.size()]->c_str;
+      random_options+= " ";
+    }
+    random_options.resize(random_options.size() -1);
+
+    memcached_return_t rc;
+    memcached_st *memc_ptr= memcached_create(NULL);
+    rc= memcached_parse_configuration(memc_ptr, random_options.c_str(), random_options.size());
+    if (rc == MEMCACHED_PARSE_ERROR)
+    {
+      std::cerr << std::endl << "Failed to parse(" << memcached_strerror(NULL, rc) << "): " << random_options << std::endl;
+      memcached_error_print(memc_ptr);
+    }
+    memcached_free(memc_ptr);
+  }
+
+  return TEST_SUCCESS;
+}
diff --git a/tests/parser.h b/tests/parser.h
new file mode 100644 (file)
index 0000000..b21cdf7
--- /dev/null
@@ -0,0 +1,102 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Gearmand client and server library.
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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 <libtest/test.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LIBTEST_INTERNAL_API
+test_return_t server_test(memcached_st *memc);
+
+LIBTEST_INTERNAL_API
+test_return_t servers_test(memcached_st *memc);
+
+LIBTEST_INTERNAL_API
+test_return_t behavior_parser_test(memcached_st *junk);
+
+LIBTEST_INTERNAL_API
+test_return_t parser_number_options_test(memcached_st *junk);
+
+LIBTEST_INTERNAL_API
+test_return_t parser_distribution_test(memcached_st *junk);
+
+LIBTEST_INTERNAL_API
+test_return_t parser_hash_test(memcached_st *junk);
+
+LIBTEST_INTERNAL_API
+test_return_t parser_boolean_options_test(memcached_st *junk);
+
+LIBTEST_INTERNAL_API
+test_return_t parser_key_prefix_test(memcached_st *junk);
+
+LIBTEST_INTERNAL_API
+test_return_t memcached_parse_configure_file_test(memcached_st *junk);
+
+LIBTEST_INTERNAL_API
+  test_return_t libmemcached_check_configuration_test(memcached_st *junk);
+
+LIBTEST_INTERNAL_API
+  test_return_t memcached_create_with_options_test(memcached_st *junk);
+
+LIBTEST_INTERNAL_API
+  test_return_t memcached_create_with_options_with_filename(memcached_st *junk);
+
+LIBTEST_INTERNAL_API
+  test_return_t libmemcached_check_configuration_with_filename_test(memcached_st *junk);
+
+LIBTEST_INTERNAL_API
+  test_return_t random_statement_build_test(memcached_st *junk);
+
+LIBTEST_INTERNAL_API
+test_return_t test_include_keyword(memcached_st *junk);
+
+LIBTEST_INTERNAL_API
+test_return_t test_end_keyword(memcached_st *junk);
+
+LIBTEST_INTERNAL_API
+test_return_t test_reset_keyword(memcached_st *junk);
+
+LIBTEST_INTERNAL_API
+test_return_t test_error_keyword(memcached_st *junk);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/print.cc b/tests/print.cc
new file mode 100644 (file)
index 0000000..570ab0f
--- /dev/null
@@ -0,0 +1,59 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Gearmand client and server library.
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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 <config.h>
+
+#include <iostream>
+
+#include <libmemcached/memcached.h>
+#include <libtest/test.h>
+
+#include "tests/print.h"
+
+memcached_return_t server_print_callback(const memcached_st *ptr,
+                                         const memcached_server_st *server,
+                                         void *context)
+{
+  (void)ptr;
+
+  if (context)
+  {
+    std::cerr << memcached_server_name(server) << ":" << memcached_server_port(server) << std::endl;
+  }
+
+  return MEMCACHED_SUCCESS;
+}
diff --git a/tests/print.h b/tests/print.h
new file mode 100644 (file)
index 0000000..efd148f
--- /dev/null
@@ -0,0 +1,51 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Gearmand client and server library.
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LIBTEST_INTERNAL_API
+memcached_return_t server_print_callback(const memcached_st *ptr,
+                                         const memcached_server_st *server,
+                                         void *context);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/replication.cc b/tests/replication.cc
new file mode 100644 (file)
index 0000000..3034c6a
--- /dev/null
@@ -0,0 +1,296 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Gearmand client and server library.
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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 <config.h>
+
+#include <libtest/test.h>
+#include "libmemcached/common.h"
+#include <tests/replication.h>
+
+test_return_t replication_set_test(memcached_st *memc)
+{
+  memcached_return_t rc;
+  memcached_st *memc_clone= memcached_clone(NULL, memc);
+  memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 0);
+
+  rc= memcached_set(memc, "bubba", 5, "0", 1, 0, 0);
+  test_true(rc == MEMCACHED_SUCCESS);
+
+  /*
+  ** We are using the quiet commands to store the replicas, so we need
+  ** to ensure that all of them are processed before we can continue.
+  ** In the test we go directly from storing the object to trying to
+  ** receive the object from all of the different servers, so we
+  ** could end up in a race condition (the memcached server hasn't yet
+  ** processed the quiet command from the replication set when it process
+  ** the request from the other client (created by the clone)). As a
+  ** workaround for that we call memcached_quit to send the quit command
+  ** to the server and wait for the response ;-) If you use the test code
+  ** as an example for your own code, please note that you shouldn't need
+  ** to do this ;-)
+  */
+  memcached_quit(memc);
+
+  /*
+  ** "bubba" should now be stored on all of our servers. We don't have an
+  ** easy to use API to address each individual server, so I'll just iterate
+  ** through a bunch of "master keys" and I should most likely hit all of the
+  ** servers...
+  */
+  for (int x= 'a'; x <= 'z'; ++x)
+  {
+    const char key[2]= { (char)x, 0 };
+    size_t len;
+    uint32_t flags;
+    char *val= memcached_get_by_key(memc_clone, key, 1, "bubba", 5,
+                                    &len, &flags, &rc);
+    test_true(rc == MEMCACHED_SUCCESS);
+    test_true(val != NULL);
+    free(val);
+  }
+
+  memcached_free(memc_clone);
+
+  return TEST_SUCCESS;
+}
+
+test_return_t replication_get_test(memcached_st *memc)
+{
+  memcached_return_t rc;
+
+  /*
+   * Don't do the following in your code. I am abusing the internal details
+   * within the library, and this is not a supported interface.
+   * This is to verify correct behavior in the library
+   */
+  for (uint32_t host= 0; host < memcached_server_count(memc); ++host)
+  {
+    memcached_st *memc_clone= memcached_clone(NULL, memc);
+    memcached_server_instance_st instance=
+      memcached_server_instance_by_position(memc_clone, host);
+
+    ((memcached_server_write_instance_st)instance)->port= 0;
+
+    for (int x= 'a'; x <= 'z'; ++x)
+    {
+      const char key[2]= { (char)x, 0 };
+      size_t len;
+      uint32_t flags;
+      char *val= memcached_get_by_key(memc_clone, key, 1, "bubba", 5,
+                                      &len, &flags, &rc);
+      test_true(rc == MEMCACHED_SUCCESS);
+      test_true(val != NULL);
+      free(val);
+    }
+
+    memcached_free(memc_clone);
+  }
+
+  return TEST_SUCCESS;
+}
+
+test_return_t replication_mget_test(memcached_st *memc)
+{
+  memcached_return_t rc;
+  memcached_st *memc_clone= memcached_clone(NULL, memc);
+  memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 0);
+
+  const char *keys[]= { "bubba", "key1", "key2", "key3" };
+  size_t len[]= { 5, 4, 4, 4 };
+
+  for (size_t x= 0; x< 4; ++x)
+  {
+    rc= memcached_set(memc, keys[x], len[x], "0", 1, 0, 0);
+    test_true(rc == MEMCACHED_SUCCESS);
+  }
+
+  /*
+  ** We are using the quiet commands to store the replicas, so we need
+  ** to ensure that all of them are processed before we can continue.
+  ** In the test we go directly from storing the object to trying to
+  ** receive the object from all of the different servers, so we
+  ** could end up in a race condition (the memcached server hasn't yet
+  ** processed the quiet command from the replication set when it process
+  ** the request from the other client (created by the clone)). As a
+  ** workaround for that we call memcached_quit to send the quit command
+  ** to the server and wait for the response ;-) If you use the test code
+  ** as an example for your own code, please note that you shouldn't need
+  ** to do this ;-)
+  */
+  memcached_quit(memc);
+
+  /*
+   * Don't do the following in your code. I am abusing the internal details
+   * within the library, and this is not a supported interface.
+   * This is to verify correct behavior in the library
+   */
+  memcached_result_st result_obj;
+  for (uint32_t host= 0; host < memc_clone->number_of_hosts; host++)
+  {
+    memcached_st *new_clone= memcached_clone(NULL, memc);
+    memcached_server_instance_st instance=
+      memcached_server_instance_by_position(new_clone, host);
+    ((memcached_server_write_instance_st)instance)->port= 0;
+
+    for (int x= 'a'; x <= 'z'; ++x)
+    {
+      char key[2]= { (char)x, 0 };
+
+      rc= memcached_mget_by_key(new_clone, key, 1, keys, len, 4);
+      test_true(rc == MEMCACHED_SUCCESS);
+
+      memcached_result_st *results= memcached_result_create(new_clone, &result_obj);
+      test_true(results);
+
+      int hits= 0;
+      while ((results= memcached_fetch_result(new_clone, &result_obj, &rc)) != NULL)
+      {
+        hits++;
+      }
+      test_true(hits == 4);
+      memcached_result_free(&result_obj);
+    }
+
+    memcached_free(new_clone);
+  }
+
+  memcached_free(memc_clone);
+
+  return TEST_SUCCESS;
+}
+
+test_return_t replication_randomize_mget_test(memcached_st *memc)
+{
+  memcached_result_st result_obj;
+  memcached_return_t rc;
+  memcached_st *memc_clone= memcached_clone(NULL, memc);
+  memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 3);
+  memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ, 1);
+
+  const char *keys[]= { "key1", "key2", "key3", "key4", "key5", "key6", "key7" };
+  size_t len[]= { 4, 4, 4, 4, 4, 4, 4 };
+
+  for (size_t x= 0; x< 7; ++x)
+  {
+    rc= memcached_set(memc, keys[x], len[x], "1", 1, 0, 0);
+    test_true(rc == MEMCACHED_SUCCESS);
+  }
+
+  memcached_quit(memc);
+
+  for (size_t x= 0; x< 7; ++x)
+  {
+    const char key[2]= { (char)x, 0 };
+
+    rc= memcached_mget_by_key(memc_clone, key, 1, keys, len, 7);
+    test_true(rc == MEMCACHED_SUCCESS);
+
+    memcached_result_st *results= memcached_result_create(memc_clone, &result_obj);
+    test_true(results);
+
+    int hits= 0;
+    while ((results= memcached_fetch_result(memc_clone, &result_obj, &rc)) != NULL)
+    {
+      ++hits;
+    }
+    test_true(hits == 7);
+    memcached_result_free(&result_obj);
+  }
+  memcached_free(memc_clone);
+  return TEST_SUCCESS;
+}
+
+test_return_t replication_delete_test(memcached_st *memc)
+{
+  memcached_return_t rc;
+  memcached_st *memc_clone= memcached_clone(NULL, memc);
+  /* Delete the items from all of the servers except 1 */
+  uint64_t repl= memcached_behavior_get(memc,
+                                        MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS);
+  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, --repl);
+
+  const char *keys[]= { "bubba", "key1", "key2", "key3" };
+  size_t len[]= { 5, 4, 4, 4 };
+
+  for (size_t x= 0; x< 4; ++x)
+  {
+    rc= memcached_delete_by_key(memc, keys[0], len[0], keys[x], len[x], 0);
+    test_true(rc == MEMCACHED_SUCCESS);
+  }
+
+  /*
+   * Don't do the following in your code. I am abusing the internal details
+   * within the library, and this is not a supported interface.
+   * This is to verify correct behavior in the library
+   */
+  uint32_t hash= memcached_generate_hash(memc, keys[0], len[0]);
+  for (uint32_t x= 0; x < (repl + 1); ++x)
+  {
+    memcached_server_instance_st instance=
+      memcached_server_instance_by_position(memc_clone, x);
+
+    ((memcached_server_write_instance_st)instance)->port= 0;
+    if (++hash == memc_clone->number_of_hosts)
+      hash= 0;
+  }
+
+  memcached_result_st result_obj;
+  for (uint32_t host= 0; host < memc_clone->number_of_hosts; ++host)
+  {
+    for (size_t x= 'a'; x <= 'z'; ++x)
+    {
+      const char key[2]= { (char)x, 0 };
+
+      rc= memcached_mget_by_key(memc_clone, key, 1, keys, len, 4);
+      test_true(rc == MEMCACHED_SUCCESS);
+
+      memcached_result_st *results= memcached_result_create(memc_clone, &result_obj);
+      test_true(results);
+
+      int hits= 0;
+      while ((results= memcached_fetch_result(memc_clone, &result_obj, &rc)) != NULL)
+      {
+        ++hits;
+      }
+      test_true(hits == 4);
+      memcached_result_free(&result_obj);
+    }
+  }
+  memcached_free(memc_clone);
+
+  return TEST_SUCCESS;
+}
diff --git a/tests/replication.h b/tests/replication.h
new file mode 100644 (file)
index 0000000..d7eba14
--- /dev/null
@@ -0,0 +1,61 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Gearmand client and server library.
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  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
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LIBTEST_INTERNAL_API
+test_return_t replication_set_test(memcached_st *memc);
+
+LIBTEST_INTERNAL_API
+test_return_t replication_get_test(memcached_st *memc);
+
+LIBTEST_INTERNAL_API
+test_return_t replication_mget_test(memcached_st *memc);
+
+LIBTEST_INTERNAL_API
+test_return_t replication_delete_test(memcached_st *memc);
+
+LIBTEST_INTERNAL_API
+test_return_t replication_randomize_mget_test(memcached_st *memc);
+
+#ifdef __cplusplus
+}
+#endif