diff options
author | michael-grunder <michael-grunder@github.com> | 2012-05-10 04:48:11 +0400 |
---|---|---|
committer | michael-grunder <michael-grunder@github.com> | 2012-05-10 04:48:11 +0400 |
commit | 9c9c63fb46ad6129d09a70c08dcd18d830f039d9 (patch) | |
tree | c08f87f2e4b6fd938b09e7f6e68670a75389a0ab /library.c | |
parent | b61ae6ab45fbb0153ec1601059e8fca97e31455a (diff) |
Initial commit of EVAL and EVALSHA
Diffstat (limited to 'library.c')
-rw-r--r-- | library.c | 276 |
1 files changed, 274 insertions, 2 deletions
@@ -222,6 +222,30 @@ integer_length(int i) { } int +redis_cmd_format_header(char **ret, char *keyword, int arg_count) { + // Our return buffer + smart_str buf = {0}; + + // Keyword length + int l = strlen(keyword); + + smart_str_appendc(&buf, '*'); + smart_str_append_long(&buf, arg_count + 1); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendc(&buf, '$'); + smart_str_append_long(&buf, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendl(&buf, keyword, l); + smart_str_appendl(&buf, _NL, sizeof(_NL) - 1); + + // Set our return pointer + *ret = buf.c; + + // Return the length + return buf.len; +} + +int redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { char *p = format; @@ -302,7 +326,7 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) { /** * This command behave somehow like printf, except that strings need 2 arguments: * Their data and their size (strlen). - * Supported formats are: %d, %i, %s + * Supported formats are: %d, %i, %s, %l */ int redis_cmd_format(char **ret, char *format, ...) { @@ -360,6 +384,46 @@ redis_cmd_format(char **ret, char *format, ...) { return buf.len; } +/* + * Append a command sequence to a Redis command + */ +int redis_cmd_append_str(char **cmd, int cmd_len, char *append, int append_len) { + // Smart string buffer + smart_str buf = {0}; + + // Append the current command to our smart_str + smart_str_appendl(&buf, *cmd, cmd_len); + + // Append our new command sequence + smart_str_appendc(&buf, '$'); + smart_str_append_long(&buf, append_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + smart_str_appendl(&buf, append, append_len); + smart_str_appendl(&buf, _NL, sizeof(_NL) -1); + + // Free our old command + efree(*cmd); + + // Set our return pointer + *cmd = buf.c; + + // Return new command length + return buf.len; +} + +/* + * Append an integer command to a Redis command + */ +int redis_cmd_append_int(char **cmd, int cmd_len, int append) { + char int_buf[32]; + + // Conver to an int, capture length + int int_len = snprintf(int_buf, sizeof(int_buf), "%d", append); + + // Return the new length + return redis_cmd_append_str(cmd, cmd_len, int_buf, int_len); +} + PHPAPI void redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; @@ -595,7 +659,6 @@ PHPAPI int redis_sock_read_multibulk_reply_zipped_with_flag(INTERNAL_FUNCTION_PA if(php_stream_gets(redis_sock->stream, inbuf, 1024) == NULL) { redis_stream_close(redis_sock TSRMLS_CC); redis_sock->stream = NULL; - redis_sock->stream = NULL; redis_sock->status = REDIS_SOCK_STATUS_FAILED; redis_sock->mode = ATOMIC; redis_sock->watching = 0; @@ -1242,5 +1305,214 @@ redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC) { return 1; } +/* + * Processing for variant reply types (think EVAL) + */ + +PHPAPI int +redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, int *line_size) { + // Handle EOF + if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + return -1; + } + + if(php_stream_get_line(redis_sock->stream, buf, buf_size, line_size) == NULL) { + // Close, put our socket state into error + redis_stream_close(redis_sock TSRMLS_CC); + redis_sock->stream = NULL; + redis_sock->status = REDIS_SOCK_STATUS_FAILED; + redis_sock->mode = ATOMIC; + redis_sock->watching = 0; + + // Throw a read error exception + zend_throw_exception(redis_exception_ce, "read error on connection", 0 TSRMLS_CC); + } + + // Success! + return 0; +} + +PHPAPI int +redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, int *reply_info) { + // Make sure we haven't lost the connection, even trying to reconnect + if(-1 == redis_check_eof(redis_sock TSRMLS_CC)) { + // Failure + return -1; + } + + // Attempt to read the reply-type byte + if((*reply_type = php_stream_getc(redis_sock->stream)) == EOF) { + zend_throw_exception(redis_exception_ce, "socket error on read socket", 0 TSRMLS_CC); + } + + // If this is a BULK, MULTI BULK, or simply an INTEGER response, we can extract the value or size info here + if(*reply_type == TYPE_INT || *reply_type == TYPE_BULK || *reply_type == TYPE_MULTIBULK) { + // Buffer to hold size information + char inbuf[255]; + + // Read up to our newline + if(php_stream_gets(redis_sock->stream, inbuf, sizeof(inbuf)) < 0) { + return -1; + } + + // Set our size response + *reply_info = atoi(inbuf); + } + + // Success! + return 0; +} + +/* + * Read a single line response, having already consumed the reply-type byte + */ +PHPAPI int +redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval **z_ret) { + // Buffer to read our single line reply + char inbuf[1024]; + int line_size; + + // Attempt to read our single line reply + if(redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &line_size) < 0) { + return -1; + } + + // If this is an error response, check if it is a SYNC error, and throw in that case + if(reply_type == TYPE_ERR) { + if(memcmp(inbuf, "ERR SYNC", 9) == 0) { + zend_throw_exception(redis_exception_ce, "SYNC with master in progress", 0 TSRMLS_CC); + } + + // Set our response to FALSE + ZVAL_FALSE(*z_ret); + } else { + // Set our response to TRUE + ZVAL_TRUE(*z_ret); + //ZVAL_STRINGL(*z_ret, inbuf, line_size, 1); + } + + return 0; +} + +PHPAPI int +redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret) { + // Attempt to read the bulk reply + char *bulk_resp = redis_sock_read_bulk_reply(redis_sock, size); + + // Set our reply to FALSE on failure, and the string on success + if(bulk_resp == NULL) { + ZVAL_FALSE(*z_ret); + return -1; + } else { + ZVAL_STRINGL(*z_ret, bulk_resp, size, 0); + return 0; + } +} + +PHPAPI int +redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret) { + int reply_info; + REDIS_REPLY_TYPE reply_type; + zval *z_subelem; + + // Iterate while we have elements + while(elements > 0) { + // Attempt to read our reply type + if(redis_read_reply_type(redis_sock, &reply_type, &reply_info) < 0) { + zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, couldn't parse MULTI-BULK response\n", reply_type); + return -1; + } + + // Switch on our reply-type byte + switch(reply_type) { + case TYPE_ERR: + case TYPE_LINE: + ALLOC_INIT_ZVAL(z_subelem); + redis_read_variant_line(redis_sock, reply_type, &z_subelem); + add_next_index_zval(*z_ret, z_subelem); + break; + case TYPE_INT: + // Add our long value + add_next_index_long(*z_ret, reply_info); + break; + case TYPE_BULK: + // Init a zval for our bulk response, read and add it + ALLOC_INIT_ZVAL(z_subelem); + redis_read_variant_bulk(redis_sock, reply_info, &z_subelem); + add_next_index_zval(*z_ret, z_subelem); + break; + case TYPE_MULTIBULK: + // Construct an array for our sub element, and add it, and recurse + ALLOC_INIT_ZVAL(z_subelem); + array_init(z_subelem); + add_next_index_zval(*z_ret, z_subelem); + redis_read_multibulk_recursive(redis_sock, reply_info, &z_subelem); + break; + } + + // Decrement our element counter + elements--; + } + + return 0; +} + +PHPAPI int +redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) { + // Reply type, and reply size vars + REDIS_REPLY_TYPE reply_type; + int reply_info; + //char *bulk_resp; + zval *z_ret; + + // Attempt to read our header + if(redis_read_reply_type(redis_sock, &reply_type, &reply_info) < 0) { + return -1; + } + + // Our return ZVAL + MAKE_STD_ZVAL(z_ret); + + // Switch based on our top level reply type + switch(reply_type) { + case TYPE_ERR: + case TYPE_LINE: + redis_read_variant_line(redis_sock, reply_type, &z_ret); + break; + case TYPE_INT: + ZVAL_LONG(z_ret, reply_info); + break; + case TYPE_BULK: + redis_read_variant_bulk(redis_sock, reply_info, &z_ret); + break; + case TYPE_MULTIBULK: + // Initialize an array for our multi-bulk response + array_init(z_ret); + + // If we've got more than zero elements, parse our multi bulk respoinse recursively + if(reply_info > -1) { + redis_read_multibulk_recursive(redis_sock, reply_info, &z_ret); + } + break; + default: + // Protocol error + zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type); + break; + } + + IF_MULTI_OR_PIPELINE() { + add_next_index_zval(z_tab, z_ret); + } else { + // Set our return value + *return_value = *z_ret; + zval_copy_ctor(return_value); + zval_dtor(z_ret); + efree(z_ret); + } + + // Success + return 0; +} + /* vim: set tabstop=4 softtabstop=4 noexpandtab shiftwidth=4: */ |