/* 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; }
/* Create a reply object */ static redisReply *createReplyObject(int type) { redisReply *r = malloc(sizeof(*r)); if (!r) redisOOM(); r->type = type; return r; }
static redisReply *redisReadIntegerReply(int fd) { sds buf = redisReadLine(fd); redisReply *r = malloc(sizeof(*r)); if (r == NULL) redisOOM(); if (buf == NULL) return redisIOError(); r->type = REDIS_REPLY_INTEGER; r->integer = strtoll(buf,NULL,10); return r; }
static redisReply *redisReadMultiBulkReply(int fd) { sds replylen = redisReadLine(fd); long elements, j; redisReply *r; if (replylen == NULL) return redisIOError(); elements = strtol(replylen,NULL,10); sdsfree(replylen); if (elements == -1) return createReplyObject(REDIS_REPLY_NIL,sdsempty()); if ((r = malloc(sizeof(*r))) == NULL) redisOOM(); r->type = REDIS_REPLY_ARRAY; r->elements = elements; if ((r->element = malloc(sizeof(*r)*elements)) == NULL) redisOOM(); for (j = 0; j < elements; j++) r->element[j] = redisReadReply(fd); return r; }
static void *createArrayObject(const redisReadTask *task, int elements) { redisReply *r = createReplyObject(REDIS_REPLY_ARRAY); r->elements = elements; if ((r->element = calloc(sizeof(redisReply*),elements)) == NULL) redisOOM(); if (task->parent) { redisReply *parent = task->parent->obj; assert(parent->type == REDIS_REPLY_ARRAY); parent->element[task->idx] = r; } return r; }
/* Helper functions to push/shift callbacks */ static int __redisPushCallback(redisCallbackList *list, redisCallback *source) { redisCallback *cb; /* Copy callback from stack to heap */ cb = malloc(sizeof(*cb)); if (!cb) redisOOM(); if (source != NULL) { memcpy(cb, source, sizeof(*cb)); cb->next = NULL; } /* Store callback in list */ if (list->head == NULL) list->head = cb; if (list->tail != NULL) list->tail->next = cb; list->tail = cb; return REDIS_OK; }
static void *createStringObject(const redisReadTask *task, char *str, size_t len) { redisReply *r = createReplyObject(task->type); char *value = malloc(len+1); if (!value) redisOOM(); assert(task->type == REDIS_REPLY_ERROR || task->type == REDIS_REPLY_STATUS || task->type == REDIS_REPLY_STRING); /* Copy string value */ memcpy(value,str,len); value[len] = '\0'; r->str = value; r->len = len; if (task->parent) { redisReply *parent = task->parent->obj; assert(parent->type == REDIS_REPLY_ARRAY); parent->element[task->idx] = r; } return r; }
int redisvFormatCommand(char **target, const char *format, va_list ap) { size_t size; const char *arg, *c = format; char *cmd = NULL; /* final command */ int pos; /* position in final command */ sds current; /* current argument */ int touched = 0; /* was the current argument touched? */ char **argv = NULL; int argc = 0, j; int totlen = 0; /* Abort if there is not target to set */ if (target == NULL) return -1; /* Build the command string accordingly to protocol */ current = sdsempty(); while(*c != '\0') { if (*c != '%' || c[1] == '\0') { if (*c == ' ') { if (touched) { addArgument(current, &argv, &argc, &totlen); current = sdsempty(); touched = 0; } } else { current = sdscatlen(current,c,1); touched = 1; } } else { switch(c[1]) { case 's': arg = va_arg(ap,char*); size = strlen(arg); if (size > 0) current = sdscatlen(current,arg,size); break; case 'b': arg = va_arg(ap,char*); size = va_arg(ap,size_t); if (size > 0) current = sdscatlen(current,arg,size); break; case '%': current = sdscat(current,"%"); break; default: /* Try to detect printf format */ { char _format[16]; const char *_p = c+1; size_t _l = 0; va_list _cpy; /* Flags */ if (*_p != '\0' && *_p == '#') _p++; if (*_p != '\0' && *_p == '0') _p++; if (*_p != '\0' && *_p == '-') _p++; if (*_p != '\0' && *_p == ' ') _p++; if (*_p != '\0' && *_p == '+') _p++; /* Field width */ while (*_p != '\0' && isdigit(*_p)) _p++; /* Precision */ if (*_p == '.') { _p++; while (*_p != '\0' && isdigit(*_p)) _p++; } /* Modifiers */ if (*_p != '\0') { if (*_p == 'h' || *_p == 'l') { /* Allow a single repetition for these modifiers */ if (_p[0] == _p[1]) _p++; _p++; } } /* Conversion specifier */ if (*_p != '\0' && strchr("diouxXeEfFgGaA",*_p) != NULL) { _l = (_p+1)-c; if (_l < sizeof(_format)-2) { memcpy(_format,c,_l); _format[_l] = '\0'; va_copy(_cpy,ap); current = sdscatvprintf(current,_format,_cpy); va_end(_cpy); /* Update current position (note: outer blocks * increment c twice so compensate here) */ c = _p-1; } } /* Consume and discard vararg */ va_arg(ap,void); } } touched = 1; c++; } c++; } /* Add the last argument if needed */ if (touched) { addArgument(current, &argv, &argc, &totlen); } else { sdsfree(current); } /* Add bytes needed to hold multi bulk count */ totlen += 1+intlen(argc)+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++) { pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(argv[j])); memcpy(cmd+pos,argv[j],sdslen(argv[j])); pos += sdslen(argv[j]); sdsfree(argv[j]); cmd[pos++] = '\r'; cmd[pos++] = '\n'; } assert(pos == totlen); free(argv); cmd[totlen] = '\0'; *target = cmd; return totlen; }
/* Helper function for redisvFormatCommand(). */ static void addArgument(sds a, char ***argv, int *argc, int *totlen) { (*argc)++; if ((*argv = realloc(*argv, sizeof(char*)*(*argc))) == NULL) redisOOM(); if (totlen) *totlen = *totlen+1+intlen(sdslen(a))+2+sdslen(a)+2; (*argv)[(*argc)-1] = a; }
/* Helper function for redisCommand(). It's used to append the next argument * to the argument vector. */ static void addArgument(sds a, char ***argv, int *argc) { (*argc)++; if ((*argv = realloc(*argv, sizeof(char*)*(*argc))) == NULL) redisOOM(); (*argv)[(*argc)-1] = a; }