+static inline memcached_return_t ascii_read_inc(memcached_instance_st *instance, const char *buffer,
+ memcached_result_st *result) {
+ {
+ errno = 0;
+ unsigned long long int auto_return_value = strtoull(buffer, (char **) NULL, 10);
+
+ if (errno) {
+ 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"));
+ }
+
+ result->numeric_value = uint64_t(auto_return_value);
+ return MEMCACHED_SUCCESS;
+ }
+}
+
+static inline memcached_return_t ascii_read_ull(size_t &value, char **string_ptr, char *end_ptr) {
+ char *next_ptr = *string_ptr;
+
+ errno = 0;
+ value = strtoull(next_ptr, string_ptr, 10);
+
+ if (errno) {
+ return MEMCACHED_ERRNO;
+ }
+ if (*string_ptr >= end_ptr) {
+ return MEMCACHED_PARTIAL_READ;
+ }
+ return MEMCACHED_SUCCESS;
+}
+
+static inline memcached_return_t ascii_read_key(char *buf, size_t len, char **str_ptr, char *end_ptr) {
+ char *tmp_ptr = buf;
+ while (**str_ptr != ' ' && **str_ptr != '\r') {
+ *(tmp_ptr++) = *((*str_ptr)++);
+ if (*str_ptr == end_ptr || size_t(tmp_ptr - buf) >= len) {
+ return MEMCACHED_PARTIAL_READ;
+ }
+ }
+ *tmp_ptr = 0;
+ return MEMCACHED_SUCCESS;
+}
+
+static inline memcached_return_t ascii_read_val(memcached_instance_st *instance, memcached_result_st *result,
+ ssize_t &to_read) {
+ /* We add two bytes so that we can walk the \r\n */
+ if (memcached_failed(memcached_string_check(&result->value, to_read))) {
+ return memcached_set_error(*instance, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
+ }
+
+ {
+ char *value_ptr = memcached_string_value_mutable(&result->value);
+ /*
+ We read the \r\n into the string since not doing so is more
+ cycles then the waster of memory to do so.
+
+ We are null terminating through, which will most likely make
+ some people lazy about using the return length.
+ */
+ memcached_return_t read_rc = memcached_io_read(instance, value_ptr, to_read, to_read);
+ if (memcached_failed(read_rc)) {
+ if (read_rc == MEMCACHED_IN_PROGRESS) {
+ memcached_quit_server(instance, true);
+ return memcached_set_error(*instance, MEMCACHED_IN_PROGRESS, MEMCACHED_AT);
+ }
+ return read_rc;
+ }
+
+ /* This next bit blows the API, but this is internal....*/
+ {
+ char *char_ptr = memcached_string_value_mutable(&result->value);
+ char_ptr[--to_read] = 0;
+ char_ptr[--to_read] = 0;
+ memcached_string_set_length(&result->value, to_read);
+ }
+
+ return MEMCACHED_SUCCESS;
+ }
+}
+
+static memcached_return_t result_decrypt(memcached_instance_st *instance, memcached_result_st *result) {
+ memcached_return_t rc = MEMCACHED_SUCCESS;
+
+ if (memcached_result_length(result)) {
+ hashkit_string_st *destination = hashkit_decrypt(&instance->root->hashkit, memcached_result_value(result),
+ memcached_result_length(result));
+ if(!destination) {
+ return memcached_set_error(*instance->root, MEMCACHED_FAILURE, MEMCACHED_AT,
+ memcached_literal_param("hashkit_decrypt() failed"));
+ }
+
+ memcached_result_reset_value(result);
+
+ rc = memcached_result_set_value(result, hashkit_string_c_str(destination), hashkit_string_length(destination));
+ if (memcached_failed(rc)) {
+ rc = memcached_set_error(*instance->root, MEMCACHED_FAILURE, MEMCACHED_AT,
+ memcached_literal_param("hashkit_decrypt() failed"));
+ }
+
+ hashkit_string_free(destination);
+ }
+
+ return rc;
+}
+
+static memcached_return_t meta_fetch_flags(memcached_instance_st *instance, char *str_ptr, char *end_ptr,
+ memcached_result_st *result, char opaque[MEMCACHED_MAX_KEY]) {
+ size_t ull_val;
+ char *tmp_ptr;
+
+ while (str_ptr < end_ptr && *str_ptr != '\r') {
+ switch (*str_ptr++) {
+ case ' ':
+ break;
+
+ case 'c': // CAS
+ if (MEMCACHED_SUCCESS != ascii_read_ull(ull_val, &str_ptr, end_ptr)) {
+ goto read_error;
+ }
+ result->item_cas = ull_val;
+ break;
+
+ case 'f':
+ if (MEMCACHED_SUCCESS != ascii_read_ull(ull_val, &str_ptr, end_ptr)) {
+ goto read_error;
+ }
+ result->item_flags = ull_val;
+ break;
+
+ case 'k':
+ str_ptr += memcached_array_size(instance->root->_namespace);
+ if (str_ptr >= end_ptr) {
+ goto read_error;
+ }
+ tmp_ptr = str_ptr;
+ if (MEMCACHED_SUCCESS != ascii_read_key(result->item_key, sizeof(result->item_key), &str_ptr, end_ptr)) {
+ goto read_error;
+ }
+ result->key_length = str_ptr - tmp_ptr;
+ break;
+
+ case 'l':
+ if (ascii_read_ull(ull_val, &str_ptr, end_ptr)) {
+ goto read_error;
+ }
+ /* legacy result does not support last_accessed */
+ break;
+
+ case 'O':
+ tmp_ptr = str_ptr;
+ if (MEMCACHED_SUCCESS != ascii_read_key(opaque, MEMCACHED_MAX_KEY, &str_ptr, end_ptr)) {
+ goto read_error;
+ }
+ /* legacy result does not support opaque */
+ break;
+
+ case 's': // size
+ if (MEMCACHED_SUCCESS != ascii_read_ull(ull_val, &str_ptr, end_ptr)) {
+ goto read_error;
+ }
+ /* legacy result does not support size */
+ break;
+
+ case 't':
+ if (MEMCACHED_SUCCESS != ascii_read_ull(ull_val, &str_ptr, end_ptr)) {
+ goto read_error;
+ }
+ result->item_expiration = time_t(ull_val);
+ break;
+
+ case 'W':
+ case 'X':
+ case 'Z':
+ /* legacy result does not support re-cache semantics */
+ break;
+ }
+ }
+
+ return MEMCACHED_SUCCESS;
+read_error:
+ memcached_io_reset(instance);
+ return MEMCACHED_PARTIAL_READ;
+}
+
+static memcached_return_t meta_va_fetch(memcached_instance_st *instance, char *buffer,
+ memcached_result_st *result) {
+ char opaque[MEMCACHED_MAX_KEY] = "";
+ char *str_ptr = buffer + sizeof("VA");
+ char *end_ptr = buffer + MEMCACHED_DEFAULT_COMMAND_SIZE;
+ size_t ull_val, val_len;
+ memcached_return_t rc;
+
+ while (isspace(*str_ptr) && str_ptr < end_ptr) {
+ ++str_ptr;
+ }
+ if (str_ptr == end_ptr) {
+ goto read_error;
+ }
+
+ if (MEMCACHED_SUCCESS != ascii_read_ull(ull_val, &str_ptr, end_ptr)) {
+ goto read_error;
+ }
+ val_len = ull_val;
+
+ rc = meta_fetch_flags(instance, str_ptr, end_ptr, result, opaque);
+ if (memcached_success(rc)) {
+ auto read_len = ssize_t(val_len + 2);
+ rc = ascii_read_val(instance, result, read_len);
+ if (memcached_success(rc)) {
+ if (read_len != ssize_t(val_len)) {
+ goto read_error;
+ }
+
+ /* meta INC/DEC response */
+ if ('+' == *opaque) {
+ rc = ascii_read_inc(instance, result->value.string, result);
+ } else if (memcached_is_encrypted(instance->root)) {
+ rc = result_decrypt(instance, result);
+ if (memcached_failed(rc)) {
+ memcached_result_reset(result);
+ }
+ }
+ }
+ }
+
+ return rc;
+
+read_error:
+ memcached_io_reset(instance);
+ return MEMCACHED_PARTIAL_READ;
+}
+