summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPieter Noordhuis <pcnoordhuis@gmail.com>2010-10-31 10:32:31 +0100
committerPieter Noordhuis <pcnoordhuis@gmail.com>2010-10-31 10:34:29 +0100
commita3a405bcbab4947fea0b155e93cc752953428e52 (patch)
tree9ed439092e99106b1151c66a32fd5cadc72a98c7
parente95c9d4c5b434e6829c8e3a2cc36cebdee26edf7 (diff)
Format a command using an argument vector
-rw-r--r--hiredis.c36
-rw-r--r--hiredis.h1
-rw-r--r--test.c43
3 files changed, 80 insertions, 0 deletions
diff --git a/hiredis.c b/hiredis.c
index 771673f..8fa04dc 100644
--- a/hiredis.c
+++ b/hiredis.c
@@ -578,6 +578,42 @@ int redisFormatCommand(char **target, const char *format, ...) {
return len;
}
+/* Format a command according to the Redis protocol. This function takes the
+ * number of arguments, an array with arguments and an array with their
+ * lengths. If the latter is set to NULL, strlen will be used to compute the
+ * argument lengths.
+ */
+int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) {
+ char *cmd = NULL; /* final command */
+ int pos; /* position in final command */
+ size_t len;
+ int totlen, j;
+
+ /* Calculate number of bytes needed for the command */
+ totlen = 1+intlen(argc)+2;
+ for (j = 0; j < argc; j++) {
+ len = argvlen ? argvlen[j] : strlen(argv[j]);
+ totlen += 1+intlen(len)+2+len+2;
+ }
+
+ /* Build the command at protocol level */
+ cmd = malloc(totlen+1);
+ if (!cmd) redisOOM();
+ pos = sprintf(cmd,"*%d\r\n",argc);
+ for (j = 0; j < argc; j++) {
+ len = argvlen ? argvlen[j] : strlen(argv[j]);
+ pos += sprintf(cmd+pos,"$%zu\r\n",len);
+ memcpy(cmd+pos,argv[j],len);
+ pos += len;
+ cmd[pos++] = '\r';
+ cmd[pos++] = '\n';
+ }
+ assert(pos == totlen);
+ cmd[totlen] = '\0';
+ *target = cmd;
+ return totlen;
+}
+
static int redisContextConnect(redisContext *c, const char *ip, int port) {
char err[ANET_ERR_LEN];
if (c->flags & REDIS_BLOCK) {
diff --git a/hiredis.h b/hiredis.h
index 2bce8d6..d7f7ecc 100644
--- a/hiredis.h
+++ b/hiredis.h
@@ -125,6 +125,7 @@ int redisReplyReaderGetReply(void *reader, void **reply);
/* Functions to format a command according to the protocol. */
int redisvFormatCommand(char **target, const char *format, va_list ap);
int redisFormatCommand(char **target, const char *format, ...);
+int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
redisContext *redisConnect(const char *ip, int port, redisReplyObjectFunctions *fn);
redisContext *redisConnectNonBlock(const char *ip, int port, redisReplyObjectFunctions *fn);
diff --git a/test.c b/test.c
index cc15ba5..b4a5447 100644
--- a/test.c
+++ b/test.c
@@ -28,6 +28,48 @@ static void __connect(redisContext **target) {
}
}
+static void test_format_commands() {
+ char *cmd;
+ int len;
+
+ test("Format command without interpolation: ");
+ len = redisFormatCommand(&cmd,"SET foo bar");
+ test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
+ len == 4+4+(3+2)+4+(3+2)+4+(3+2));
+ free(cmd);
+
+ test("Format command with %%s string interpolation: ");
+ len = redisFormatCommand(&cmd,"SET %s %s","foo","bar");
+ test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
+ len == 4+4+(3+2)+4+(3+2)+4+(3+2));
+ free(cmd);
+
+ test("Format command with %%b string interpolation: ");
+ len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"b\0r",3);
+ test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len) == 0 &&
+ len == 4+4+(3+2)+4+(3+2)+4+(3+2));
+ free(cmd);
+
+ const char *argv[3];
+ argv[0] = "SET";
+ argv[1] = "foo";
+ argv[2] = "bar";
+ size_t lens[3] = { 3, 3, 3 };
+ int argc = 3;
+
+ test("Format command by passing argc/argv without lengths: ");
+ len = redisFormatCommandArgv(&cmd,argc,argv,NULL);
+ test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
+ len == 4+4+(3+2)+4+(3+2)+4+(3+2));
+ free(cmd);
+
+ test("Format command by passing argc/argv with lengths: ");
+ len = redisFormatCommandArgv(&cmd,argc,argv,lens);
+ test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
+ len == 4+4+(3+2)+4+(3+2)+4+(3+2));
+ free(cmd);
+}
+
static void test_blocking_connection() {
redisContext *c;
redisReply *reply;
@@ -294,6 +336,7 @@ static void test_nonblocking_connection() {
}
int main(void) {
+ test_format_commands();
test_blocking_connection();
test_reply_reader();
test_nonblocking_connection();