Push version parsing into the parser.
[awesomized/libmemcached] / libmemcached / response.cc
index e876123d4290ef9a468ac1ff7946a29875c2b777..b840e9d7037a1a8a5cfe0b81674242f0bbb6f7ea 100644 (file)
@@ -180,14 +180,12 @@ read_error:
   return MEMCACHED_PARTIAL_READ;
 }
 
-static memcached_return_t textual_read_one_response(memcached_server_write_instance_st ptr,
+static memcached_return_t textual_read_one_response(memcached_server_write_instance_st instance,
                                                     char *buffer, const size_t buffer_length,
-                                                    memcached_result_st *result,
-                                                    uint64_t& numeric_value)
+                                                    memcached_result_st *result)
 {
-  numeric_value= UINT64_MAX;
   size_t total_read;
-  memcached_return_t rc= memcached_io_readline(ptr, buffer, buffer_length, total_read);
+  memcached_return_t rc= memcached_io_readline(instance, buffer, buffer_length, total_read);
 
   if (memcached_failed(rc))
   {
@@ -203,12 +201,46 @@ static memcached_return_t textual_read_one_response(memcached_server_write_insta
       if (buffer[1] == 'A' and buffer[2] == 'L' and buffer[3] == 'U' and buffer[4] == 'E') /* VALUE */
       {
         /* We add back in one because we will need to search for END */
-        memcached_server_response_increment(ptr);
-        return textual_value_fetch(ptr, buffer, result);
+        memcached_server_response_increment(instance);
+        return textual_value_fetch(instance, buffer, result);
       }
       // VERSION
       else if (buffer[1] == 'E' and buffer[2] == 'R' and buffer[3] == 'S' and buffer[4] == 'I' and buffer[5] == 'O' and buffer[6] == 'N') /* VERSION */
       {
+        /* Find the space, and then move one past it to copy version */
+        char *response_ptr= index(buffer, ' ');
+        response_ptr++;
+
+        long int version= strtol(response_ptr, (char **)NULL, 10);
+        if (version == LONG_MIN or version == LONG_MAX or errno == EINVAL or version > UINT8_MAX or version == 0)
+        {
+          instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX;
+          return memcached_set_error(*instance, MEMCACHED_PROTOCOL_ERROR, MEMCACHED_AT, memcached_literal_param("strtol() failed to parse major version"));
+        }
+        instance->major_version= uint8_t(version);
+
+        response_ptr= index(response_ptr, '.');
+        response_ptr++;
+
+        version= strtol(response_ptr, (char **)NULL, 10);
+        if (version == LONG_MIN or version == LONG_MAX or errno == EINVAL or version > UINT8_MAX)
+        {
+          instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX;
+          return memcached_set_error(*instance, MEMCACHED_PROTOCOL_ERROR, MEMCACHED_AT, memcached_literal_param("strtol() failed to parse minor version"));
+        }
+        instance->minor_version= uint8_t(version);
+
+        response_ptr= index(response_ptr, '.');
+        response_ptr++;
+
+        version= strtol(response_ptr, (char **)NULL, 10);
+        if (version == LONG_MIN or version == LONG_MAX or errno == EINVAL or version > UINT8_MAX)
+        {
+          instance->major_version= instance->minor_version= instance->micro_version= UINT8_MAX;
+          return memcached_set_error(*instance, MEMCACHED_PROTOCOL_ERROR, MEMCACHED_AT, memcached_literal_param("strtol() failed to parse micro version"));
+        }
+        instance->micro_version= uint8_t(version);
+
         return MEMCACHED_SUCCESS;
       }
     }
@@ -229,7 +261,7 @@ static memcached_return_t textual_read_one_response(memcached_server_write_insta
       // STAT
       if (buffer[1] == 'T' and buffer[2] == 'A' and buffer[3] == 'T') /* STORED STATS */
       {
-        memcached_server_response_increment(ptr);
+        memcached_server_response_increment(instance);
         return MEMCACHED_STAT;
       }
       // SERVER_ERROR
@@ -242,12 +274,18 @@ static memcached_return_t textual_read_one_response(memcached_server_write_insta
           return MEMCACHED_SERVER_ERROR;
         }
 
-        if (total_read > memcached_literal_param_size("SERVER_ERROR object too large for cache") and
+        if (total_read >= memcached_literal_param_size("SERVER_ERROR object too large for cache") and
             (memcmp(buffer, memcached_literal_param("SERVER_ERROR object too large for cache")) == 0))
         {
           return MEMCACHED_E2BIG;
         }
 
+        if (total_read >= memcached_literal_param_size("SERVER_ERROR out of memory storing object") and
+            (memcmp(buffer, memcached_literal_param("SERVER_ERROR out of memory storing object")) == 0))
+        {
+          return MEMCACHED_SERVER_MEMORY_ALLOCATION_FAILURE;
+        }
+
         // Move past the basic error message and whitespace
         char *startptr= buffer + memcached_literal_param_size("SERVER_ERROR");
         if (startptr[0] == ' ')
@@ -258,7 +296,7 @@ static memcached_return_t textual_read_one_response(memcached_server_write_insta
         char *endptr= startptr;
         while (*endptr != '\r' && *endptr != '\n') endptr++;
 
-        return memcached_set_error(*ptr, MEMCACHED_SERVER_ERROR, MEMCACHED_AT, startptr, size_t(endptr - startptr));
+        return memcached_set_error(*instance, MEMCACHED_SERVER_ERROR, MEMCACHED_AT, startptr, size_t(endptr - startptr));
       }
       // STORED
       else if (buffer[1] == 'T' and buffer[2] == 'O' and buffer[3] == 'R') //  and buffer[4] == 'E' and buffer[5] == 'D')
@@ -313,6 +351,11 @@ static memcached_return_t textual_read_one_response(memcached_server_write_insta
         return MEMCACHED_PROTOCOL_ERROR;
       }
 #endif
+      // ERROR
+      else if (buffer[1] == 'R' and buffer[2] == 'R' and buffer[3] == 'O' and buffer[4] == 'R')
+      {
+        return MEMCACHED_ERROR;
+      }
       // EXISTS
       else if (buffer[1] == 'X' and buffer[2] == 'I' and buffer[3] == 'S' and buffer[4] == 'T' and buffer[5] == 'S')
       {
@@ -337,7 +380,7 @@ static memcached_return_t textual_read_one_response(memcached_server_write_insta
       if (buffer[1] == 'T' and buffer[2] == 'E' and buffer[3] == 'M')
       {
         /* We add back in one because we will need to search for END */
-        memcached_server_response_increment(ptr);
+        memcached_server_response_increment(instance);
         return MEMCACHED_ITEM;
       }
     }
@@ -370,16 +413,18 @@ static memcached_return_t textual_read_one_response(memcached_server_write_insta
 
       if (auto_return_value == ULLONG_MAX and errno == ERANGE)
       {
-        return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
+        result->numeric_value= UINT64_MAX;
+        return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
                                    memcached_literal_param("Numeric response was out of range"));
       }
       else if (errno == EINVAL)
       {
-        return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
+        result->numeric_value= UINT64_MAX;
+        return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
                                    memcached_literal_param("Numeric response was out of range"));
       }
 
-      numeric_value= uint64_t(auto_return_value);
+      result->numeric_value= uint64_t(auto_return_value);
 
       WATCHPOINT_STRING(buffer);
       return MEMCACHED_SUCCESS;
@@ -394,11 +439,11 @@ static memcached_return_t textual_read_one_response(memcached_server_write_insta
   if (total_read >= sizeof("STORSTORED") -1)
   {
     fprintf(stderr, "%s:%d '%s', %.*s\n", __FILE__, __LINE__,
-            buffer, MEMCACHED_MAX_BUFFER, ptr->read_buffer);
+            buffer, MEMCACHED_MAX_BUFFER, instance->read_buffer);
     assert(memcmp(buffer,"STORSTORED", sizeof("STORSTORED") -1));
   }
 #endif
-  return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
+  return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
                              buffer, total_read);
 }
 
@@ -498,21 +543,20 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
     case PROTOCOL_BINARY_CMD_INCREMENT:
     case PROTOCOL_BINARY_CMD_DECREMENT:
       {
-        if (bodylen != sizeof(uint64_t) or buffer_length != sizeof(uint64_t))
+        if (bodylen != sizeof(uint64_t))
         {
+          result->numeric_value= UINT64_MAX;
           return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
         }
 
-        WATCHPOINT_ASSERT(bodylen == buffer_length);
         uint64_t val;
         if ((rc= memcached_safe_read(ptr, &val, sizeof(val))) != MEMCACHED_SUCCESS)
         {
-          WATCHPOINT_ERROR(rc);
+          result->numeric_value= UINT64_MAX;
           return MEMCACHED_UNKNOWN_READ_FAILURE;
         }
 
-        val= memcached_ntohll(val);
-        memcpy(buffer, &val, sizeof(val));
+        result->numeric_value= memcached_ntohll(val);
       }
       break;
 
@@ -683,8 +727,7 @@ static memcached_return_t binary_read_one_response(memcached_server_write_instan
 
 static memcached_return_t _read_one_response(memcached_server_write_instance_st ptr,
                                              char *buffer, const size_t buffer_length,
-                                             memcached_result_st *result,
-                                             uint64_t& numeric_value)
+                                             memcached_result_st *result)
 {
   memcached_server_response_decrement(ptr);
 
@@ -701,15 +744,11 @@ static memcached_return_t _read_one_response(memcached_server_write_instance_st
   }
   else
   {
-    rc= textual_read_one_response(ptr, buffer, buffer_length, result, numeric_value);
+    rc= textual_read_one_response(ptr, buffer, buffer_length, result);
     assert(rc != MEMCACHED_PROTOCOL_ERROR);
   }
 
-  if (rc == MEMCACHED_UNKNOWN_READ_FAILURE or
-      rc == MEMCACHED_READ_FAILURE or
-      rc == MEMCACHED_PROTOCOL_ERROR or
-      rc == MEMCACHED_CLIENT_ERROR or
-      rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE)
+  if (memcached_fatal(rc))
   {
     memcached_io_reset(ptr);
   }
@@ -720,7 +759,6 @@ static memcached_return_t _read_one_response(memcached_server_write_instance_st
 memcached_return_t memcached_read_one_response(memcached_server_write_instance_st ptr,
                                                memcached_result_st *result)
 {
-  uint64_t numeric_value;
   char buffer[SMALL_STRING_LEN];
 
   if (memcached_is_udp(ptr->root))
@@ -729,22 +767,20 @@ memcached_return_t memcached_read_one_response(memcached_server_write_instance_s
   }
 
 
-  return _read_one_response(ptr, buffer, sizeof(buffer), result, numeric_value);
+  return _read_one_response(ptr, buffer, sizeof(buffer), result);
 }
 
 memcached_return_t memcached_response(memcached_server_write_instance_st ptr,
-                                      char *buffer, size_t buffer_length,
                                       memcached_result_st *result)
 {
-  uint64_t numeric_value;
+  char buffer[1024];
 
-  return memcached_response(ptr, buffer, buffer_length, result, numeric_value);
+  return memcached_response(ptr, buffer, sizeof(buffer), result);
 }
 
 memcached_return_t memcached_response(memcached_server_write_instance_st ptr,
                                       char *buffer, size_t buffer_length,
-                                      memcached_result_st *result,
-                                      uint64_t& numeric_value)
+                                      memcached_result_st *result)
 {
   if (memcached_is_udp(ptr->root))
   {
@@ -771,17 +807,10 @@ memcached_return_t memcached_response(memcached_server_write_instance_st ptr,
 
     while (memcached_server_response_count(ptr) > 1)
     {
-      memcached_return_t rc= _read_one_response(ptr, buffer, buffer_length, junked_result_ptr, numeric_value);
+      memcached_return_t rc= _read_one_response(ptr, buffer, buffer_length, junked_result_ptr);
 
       // @TODO should we return an error on another but a bad read case?
-      if (rc != MEMCACHED_END and
-          rc != MEMCACHED_STORED and
-          rc != MEMCACHED_SUCCESS and
-          rc != MEMCACHED_STAT and
-          rc != MEMCACHED_DELETED and
-          rc != MEMCACHED_NOTFOUND and
-          rc != MEMCACHED_NOTSTORED and
-          rc != MEMCACHED_DATA_EXISTS)
+      if (memcached_fatal(rc))
       {
         memcached_result_free(junked_result_ptr);
         return rc;
@@ -790,5 +819,5 @@ memcached_return_t memcached_response(memcached_server_write_instance_st ptr,
     memcached_result_free(junked_result_ptr);
   }
 
-  return _read_one_response(ptr, buffer, buffer_length, result, numeric_value);
+  return _read_one_response(ptr, buffer, buffer_length, result);
 }