Format a command using an argument vector
This commit is contained in:
parent
e95c9d4c5b
commit
a3a405bcba
36
hiredis.c
36
hiredis.c
@ -578,6 +578,42 @@ int redisFormatCommand(char **target, const char *format, ...) {
|
|||||||
return len;
|
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) {
|
static int redisContextConnect(redisContext *c, const char *ip, int port) {
|
||||||
char err[ANET_ERR_LEN];
|
char err[ANET_ERR_LEN];
|
||||||
if (c->flags & REDIS_BLOCK) {
|
if (c->flags & REDIS_BLOCK) {
|
||||||
|
@ -125,6 +125,7 @@ int redisReplyReaderGetReply(void *reader, void **reply);
|
|||||||
/* Functions to format a command according to the protocol. */
|
/* Functions to format a command according to the protocol. */
|
||||||
int redisvFormatCommand(char **target, const char *format, va_list ap);
|
int redisvFormatCommand(char **target, const char *format, va_list ap);
|
||||||
int redisFormatCommand(char **target, const char *format, ...);
|
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 *redisConnect(const char *ip, int port, redisReplyObjectFunctions *fn);
|
||||||
redisContext *redisConnectNonBlock(const char *ip, int port, redisReplyObjectFunctions *fn);
|
redisContext *redisConnectNonBlock(const char *ip, int port, redisReplyObjectFunctions *fn);
|
||||||
|
43
test.c
43
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() {
|
static void test_blocking_connection() {
|
||||||
redisContext *c;
|
redisContext *c;
|
||||||
redisReply *reply;
|
redisReply *reply;
|
||||||
@ -294,6 +336,7 @@ static void test_nonblocking_connection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
test_format_commands();
|
||||||
test_blocking_connection();
|
test_blocking_connection();
|
||||||
test_reply_reader();
|
test_reply_reader();
|
||||||
test_nonblocking_connection();
|
test_nonblocking_connection();
|
||||||
|
Loading…
Reference in New Issue
Block a user