hiredis 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/hiredis/version.rb +1 -1
- data/vendor/hiredis/Makefile +78 -32
- data/vendor/hiredis/async.c +84 -21
- data/vendor/hiredis/async.h +4 -0
- data/vendor/hiredis/fmacros.h +7 -2
- data/vendor/hiredis/hiredis.c +228 -534
- data/vendor/hiredis/hiredis.h +53 -74
- data/vendor/hiredis/net.c +155 -46
- data/vendor/hiredis/net.h +8 -4
- data/vendor/hiredis/read.c +523 -0
- data/vendor/hiredis/read.h +116 -0
- data/vendor/hiredis/sds.c +610 -120
- data/vendor/hiredis/sds.h +27 -13
- data/vendor/hiredis/test.c +106 -12
- metadata +44 -28
- data/lib/hiredis/errors.rb +0 -5
data/vendor/hiredis/hiredis.c
CHANGED
@@ -73,6 +73,9 @@ void freeReplyObject(void *reply) {
|
|
73
73
|
redisReply *r = reply;
|
74
74
|
size_t j;
|
75
75
|
|
76
|
+
if (r == NULL)
|
77
|
+
return;
|
78
|
+
|
76
79
|
switch(r->type) {
|
77
80
|
case REDIS_REPLY_INTEGER:
|
78
81
|
break; /* Nothing to free */
|
@@ -183,504 +186,23 @@ static void *createNilObject(const redisReadTask *task) {
|
|
183
186
|
return r;
|
184
187
|
}
|
185
188
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
r->pos = r->len = 0;
|
199
|
-
}
|
200
|
-
|
201
|
-
/* Reset task stack. */
|
202
|
-
r->ridx = -1;
|
203
|
-
|
204
|
-
/* Set error. */
|
205
|
-
r->err = type;
|
206
|
-
len = strlen(str);
|
207
|
-
len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1);
|
208
|
-
memcpy(r->errstr,str,len);
|
209
|
-
r->errstr[len] = '\0';
|
210
|
-
}
|
211
|
-
|
212
|
-
static size_t chrtos(char *buf, size_t size, char byte) {
|
213
|
-
size_t len = 0;
|
214
|
-
|
215
|
-
switch(byte) {
|
216
|
-
case '\\':
|
217
|
-
case '"':
|
218
|
-
len = snprintf(buf,size,"\"\\%c\"",byte);
|
219
|
-
break;
|
220
|
-
case '\n': len = snprintf(buf,size,"\"\\n\""); break;
|
221
|
-
case '\r': len = snprintf(buf,size,"\"\\r\""); break;
|
222
|
-
case '\t': len = snprintf(buf,size,"\"\\t\""); break;
|
223
|
-
case '\a': len = snprintf(buf,size,"\"\\a\""); break;
|
224
|
-
case '\b': len = snprintf(buf,size,"\"\\b\""); break;
|
225
|
-
default:
|
226
|
-
if (isprint(byte))
|
227
|
-
len = snprintf(buf,size,"\"%c\"",byte);
|
228
|
-
else
|
229
|
-
len = snprintf(buf,size,"\"\\x%02x\"",(unsigned char)byte);
|
230
|
-
break;
|
231
|
-
}
|
232
|
-
|
233
|
-
return len;
|
234
|
-
}
|
235
|
-
|
236
|
-
static void __redisReaderSetErrorProtocolByte(redisReader *r, char byte) {
|
237
|
-
char cbuf[8], sbuf[128];
|
238
|
-
|
239
|
-
chrtos(cbuf,sizeof(cbuf),byte);
|
240
|
-
snprintf(sbuf,sizeof(sbuf),
|
241
|
-
"Protocol error, got %s as reply type byte", cbuf);
|
242
|
-
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf);
|
243
|
-
}
|
244
|
-
|
245
|
-
static void __redisReaderSetErrorOOM(redisReader *r) {
|
246
|
-
__redisReaderSetError(r,REDIS_ERR_OOM,"Out of memory");
|
247
|
-
}
|
248
|
-
|
249
|
-
static char *readBytes(redisReader *r, unsigned int bytes) {
|
250
|
-
char *p;
|
251
|
-
if (r->len-r->pos >= bytes) {
|
252
|
-
p = r->buf+r->pos;
|
253
|
-
r->pos += bytes;
|
254
|
-
return p;
|
255
|
-
}
|
256
|
-
return NULL;
|
257
|
-
}
|
258
|
-
|
259
|
-
/* Find pointer to \r\n. */
|
260
|
-
static char *seekNewline(char *s, size_t len) {
|
261
|
-
int pos = 0;
|
262
|
-
int _len = len-1;
|
263
|
-
|
264
|
-
/* Position should be < len-1 because the character at "pos" should be
|
265
|
-
* followed by a \n. Note that strchr cannot be used because it doesn't
|
266
|
-
* allow to search a limited length and the buffer that is being searched
|
267
|
-
* might not have a trailing NULL character. */
|
268
|
-
while (pos < _len) {
|
269
|
-
while(pos < _len && s[pos] != '\r') pos++;
|
270
|
-
if (s[pos] != '\r') {
|
271
|
-
/* Not found. */
|
272
|
-
return NULL;
|
273
|
-
} else {
|
274
|
-
if (s[pos+1] == '\n') {
|
275
|
-
/* Found. */
|
276
|
-
return s+pos;
|
277
|
-
} else {
|
278
|
-
/* Continue searching. */
|
279
|
-
pos++;
|
280
|
-
}
|
281
|
-
}
|
282
|
-
}
|
283
|
-
return NULL;
|
284
|
-
}
|
285
|
-
|
286
|
-
/* Read a long long value starting at *s, under the assumption that it will be
|
287
|
-
* terminated by \r\n. Ambiguously returns -1 for unexpected input. */
|
288
|
-
static long long readLongLong(char *s) {
|
289
|
-
long long v = 0;
|
290
|
-
int dec, mult = 1;
|
291
|
-
char c;
|
292
|
-
|
293
|
-
if (*s == '-') {
|
294
|
-
mult = -1;
|
295
|
-
s++;
|
296
|
-
} else if (*s == '+') {
|
297
|
-
mult = 1;
|
298
|
-
s++;
|
299
|
-
}
|
300
|
-
|
301
|
-
while ((c = *(s++)) != '\r') {
|
302
|
-
dec = c - '0';
|
303
|
-
if (dec >= 0 && dec < 10) {
|
304
|
-
v *= 10;
|
305
|
-
v += dec;
|
306
|
-
} else {
|
307
|
-
/* Should not happen... */
|
308
|
-
return -1;
|
309
|
-
}
|
310
|
-
}
|
311
|
-
|
312
|
-
return mult*v;
|
313
|
-
}
|
314
|
-
|
315
|
-
static char *readLine(redisReader *r, int *_len) {
|
316
|
-
char *p, *s;
|
317
|
-
int len;
|
318
|
-
|
319
|
-
p = r->buf+r->pos;
|
320
|
-
s = seekNewline(p,(r->len-r->pos));
|
321
|
-
if (s != NULL) {
|
322
|
-
len = s-(r->buf+r->pos);
|
323
|
-
r->pos += len+2; /* skip \r\n */
|
324
|
-
if (_len) *_len = len;
|
325
|
-
return p;
|
326
|
-
}
|
327
|
-
return NULL;
|
328
|
-
}
|
329
|
-
|
330
|
-
static void moveToNextTask(redisReader *r) {
|
331
|
-
redisReadTask *cur, *prv;
|
332
|
-
while (r->ridx >= 0) {
|
333
|
-
/* Return a.s.a.p. when the stack is now empty. */
|
334
|
-
if (r->ridx == 0) {
|
335
|
-
r->ridx--;
|
336
|
-
return;
|
337
|
-
}
|
338
|
-
|
339
|
-
cur = &(r->rstack[r->ridx]);
|
340
|
-
prv = &(r->rstack[r->ridx-1]);
|
341
|
-
assert(prv->type == REDIS_REPLY_ARRAY);
|
342
|
-
if (cur->idx == prv->elements-1) {
|
343
|
-
r->ridx--;
|
344
|
-
} else {
|
345
|
-
/* Reset the type because the next item can be anything */
|
346
|
-
assert(cur->idx < prv->elements);
|
347
|
-
cur->type = -1;
|
348
|
-
cur->elements = -1;
|
349
|
-
cur->idx++;
|
350
|
-
return;
|
351
|
-
}
|
352
|
-
}
|
353
|
-
}
|
354
|
-
|
355
|
-
static int processLineItem(redisReader *r) {
|
356
|
-
redisReadTask *cur = &(r->rstack[r->ridx]);
|
357
|
-
void *obj;
|
358
|
-
char *p;
|
359
|
-
int len;
|
360
|
-
|
361
|
-
if ((p = readLine(r,&len)) != NULL) {
|
362
|
-
if (cur->type == REDIS_REPLY_INTEGER) {
|
363
|
-
if (r->fn && r->fn->createInteger)
|
364
|
-
obj = r->fn->createInteger(cur,readLongLong(p));
|
365
|
-
else
|
366
|
-
obj = (void*)REDIS_REPLY_INTEGER;
|
367
|
-
} else {
|
368
|
-
/* Type will be error or status. */
|
369
|
-
if (r->fn && r->fn->createString)
|
370
|
-
obj = r->fn->createString(cur,p,len);
|
371
|
-
else
|
372
|
-
obj = (void*)(size_t)(cur->type);
|
373
|
-
}
|
374
|
-
|
375
|
-
if (obj == NULL) {
|
376
|
-
__redisReaderSetErrorOOM(r);
|
377
|
-
return REDIS_ERR;
|
378
|
-
}
|
379
|
-
|
380
|
-
/* Set reply if this is the root object. */
|
381
|
-
if (r->ridx == 0) r->reply = obj;
|
382
|
-
moveToNextTask(r);
|
383
|
-
return REDIS_OK;
|
384
|
-
}
|
385
|
-
|
386
|
-
return REDIS_ERR;
|
387
|
-
}
|
388
|
-
|
389
|
-
static int processBulkItem(redisReader *r) {
|
390
|
-
redisReadTask *cur = &(r->rstack[r->ridx]);
|
391
|
-
void *obj = NULL;
|
392
|
-
char *p, *s;
|
393
|
-
long len;
|
394
|
-
unsigned long bytelen;
|
395
|
-
int success = 0;
|
396
|
-
|
397
|
-
p = r->buf+r->pos;
|
398
|
-
s = seekNewline(p,r->len-r->pos);
|
399
|
-
if (s != NULL) {
|
400
|
-
p = r->buf+r->pos;
|
401
|
-
bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
|
402
|
-
len = readLongLong(p);
|
403
|
-
|
404
|
-
if (len < 0) {
|
405
|
-
/* The nil object can always be created. */
|
406
|
-
if (r->fn && r->fn->createNil)
|
407
|
-
obj = r->fn->createNil(cur);
|
408
|
-
else
|
409
|
-
obj = (void*)REDIS_REPLY_NIL;
|
410
|
-
success = 1;
|
411
|
-
} else {
|
412
|
-
/* Only continue when the buffer contains the entire bulk item. */
|
413
|
-
bytelen += len+2; /* include \r\n */
|
414
|
-
if (r->pos+bytelen <= r->len) {
|
415
|
-
if (r->fn && r->fn->createString)
|
416
|
-
obj = r->fn->createString(cur,s+2,len);
|
417
|
-
else
|
418
|
-
obj = (void*)REDIS_REPLY_STRING;
|
419
|
-
success = 1;
|
420
|
-
}
|
421
|
-
}
|
422
|
-
|
423
|
-
/* Proceed when obj was created. */
|
424
|
-
if (success) {
|
425
|
-
if (obj == NULL) {
|
426
|
-
__redisReaderSetErrorOOM(r);
|
427
|
-
return REDIS_ERR;
|
428
|
-
}
|
429
|
-
|
430
|
-
r->pos += bytelen;
|
431
|
-
|
432
|
-
/* Set reply if this is the root object. */
|
433
|
-
if (r->ridx == 0) r->reply = obj;
|
434
|
-
moveToNextTask(r);
|
435
|
-
return REDIS_OK;
|
436
|
-
}
|
437
|
-
}
|
438
|
-
|
439
|
-
return REDIS_ERR;
|
440
|
-
}
|
441
|
-
|
442
|
-
static int processMultiBulkItem(redisReader *r) {
|
443
|
-
redisReadTask *cur = &(r->rstack[r->ridx]);
|
444
|
-
void *obj;
|
445
|
-
char *p;
|
446
|
-
long elements;
|
447
|
-
int root = 0;
|
448
|
-
|
449
|
-
/* Set error for nested multi bulks with depth > 7 */
|
450
|
-
if (r->ridx == 8) {
|
451
|
-
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
452
|
-
"No support for nested multi bulk replies with depth > 7");
|
453
|
-
return REDIS_ERR;
|
454
|
-
}
|
455
|
-
|
456
|
-
if ((p = readLine(r,NULL)) != NULL) {
|
457
|
-
elements = readLongLong(p);
|
458
|
-
root = (r->ridx == 0);
|
459
|
-
|
460
|
-
if (elements == -1) {
|
461
|
-
if (r->fn && r->fn->createNil)
|
462
|
-
obj = r->fn->createNil(cur);
|
463
|
-
else
|
464
|
-
obj = (void*)REDIS_REPLY_NIL;
|
465
|
-
|
466
|
-
if (obj == NULL) {
|
467
|
-
__redisReaderSetErrorOOM(r);
|
468
|
-
return REDIS_ERR;
|
469
|
-
}
|
470
|
-
|
471
|
-
moveToNextTask(r);
|
472
|
-
} else {
|
473
|
-
if (r->fn && r->fn->createArray)
|
474
|
-
obj = r->fn->createArray(cur,elements);
|
475
|
-
else
|
476
|
-
obj = (void*)REDIS_REPLY_ARRAY;
|
477
|
-
|
478
|
-
if (obj == NULL) {
|
479
|
-
__redisReaderSetErrorOOM(r);
|
480
|
-
return REDIS_ERR;
|
481
|
-
}
|
482
|
-
|
483
|
-
/* Modify task stack when there are more than 0 elements. */
|
484
|
-
if (elements > 0) {
|
485
|
-
cur->elements = elements;
|
486
|
-
cur->obj = obj;
|
487
|
-
r->ridx++;
|
488
|
-
r->rstack[r->ridx].type = -1;
|
489
|
-
r->rstack[r->ridx].elements = -1;
|
490
|
-
r->rstack[r->ridx].idx = 0;
|
491
|
-
r->rstack[r->ridx].obj = NULL;
|
492
|
-
r->rstack[r->ridx].parent = cur;
|
493
|
-
r->rstack[r->ridx].privdata = r->privdata;
|
494
|
-
} else {
|
495
|
-
moveToNextTask(r);
|
496
|
-
}
|
497
|
-
}
|
498
|
-
|
499
|
-
/* Set reply if this is the root object. */
|
500
|
-
if (root) r->reply = obj;
|
501
|
-
return REDIS_OK;
|
502
|
-
}
|
503
|
-
|
504
|
-
return REDIS_ERR;
|
505
|
-
}
|
506
|
-
|
507
|
-
static int processItem(redisReader *r) {
|
508
|
-
redisReadTask *cur = &(r->rstack[r->ridx]);
|
509
|
-
char *p;
|
510
|
-
|
511
|
-
/* check if we need to read type */
|
512
|
-
if (cur->type < 0) {
|
513
|
-
if ((p = readBytes(r,1)) != NULL) {
|
514
|
-
switch (p[0]) {
|
515
|
-
case '-':
|
516
|
-
cur->type = REDIS_REPLY_ERROR;
|
517
|
-
break;
|
518
|
-
case '+':
|
519
|
-
cur->type = REDIS_REPLY_STATUS;
|
520
|
-
break;
|
521
|
-
case ':':
|
522
|
-
cur->type = REDIS_REPLY_INTEGER;
|
523
|
-
break;
|
524
|
-
case '$':
|
525
|
-
cur->type = REDIS_REPLY_STRING;
|
526
|
-
break;
|
527
|
-
case '*':
|
528
|
-
cur->type = REDIS_REPLY_ARRAY;
|
529
|
-
break;
|
530
|
-
default:
|
531
|
-
__redisReaderSetErrorProtocolByte(r,*p);
|
532
|
-
return REDIS_ERR;
|
533
|
-
}
|
534
|
-
} else {
|
535
|
-
/* could not consume 1 byte */
|
536
|
-
return REDIS_ERR;
|
537
|
-
}
|
538
|
-
}
|
539
|
-
|
540
|
-
/* process typed item */
|
541
|
-
switch(cur->type) {
|
542
|
-
case REDIS_REPLY_ERROR:
|
543
|
-
case REDIS_REPLY_STATUS:
|
544
|
-
case REDIS_REPLY_INTEGER:
|
545
|
-
return processLineItem(r);
|
546
|
-
case REDIS_REPLY_STRING:
|
547
|
-
return processBulkItem(r);
|
548
|
-
case REDIS_REPLY_ARRAY:
|
549
|
-
return processMultiBulkItem(r);
|
550
|
-
default:
|
551
|
-
assert(NULL);
|
552
|
-
return REDIS_ERR; /* Avoid warning. */
|
553
|
-
}
|
554
|
-
}
|
555
|
-
|
556
|
-
redisReader *redisReaderCreate(void) {
|
557
|
-
redisReader *r;
|
558
|
-
|
559
|
-
r = calloc(sizeof(redisReader),1);
|
560
|
-
if (r == NULL)
|
561
|
-
return NULL;
|
562
|
-
|
563
|
-
r->err = 0;
|
564
|
-
r->errstr[0] = '\0';
|
565
|
-
r->fn = &defaultFunctions;
|
566
|
-
r->buf = sdsempty();
|
567
|
-
r->maxbuf = REDIS_READER_MAX_BUF;
|
568
|
-
if (r->buf == NULL) {
|
569
|
-
free(r);
|
570
|
-
return NULL;
|
571
|
-
}
|
572
|
-
|
573
|
-
r->ridx = -1;
|
574
|
-
return r;
|
575
|
-
}
|
576
|
-
|
577
|
-
void redisReaderFree(redisReader *r) {
|
578
|
-
if (r->reply != NULL && r->fn && r->fn->freeObject)
|
579
|
-
r->fn->freeObject(r->reply);
|
580
|
-
if (r->buf != NULL)
|
581
|
-
sdsfree(r->buf);
|
582
|
-
free(r);
|
583
|
-
}
|
584
|
-
|
585
|
-
int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
|
586
|
-
sds newbuf;
|
587
|
-
|
588
|
-
/* Return early when this reader is in an erroneous state. */
|
589
|
-
if (r->err)
|
590
|
-
return REDIS_ERR;
|
591
|
-
|
592
|
-
/* Copy the provided buffer. */
|
593
|
-
if (buf != NULL && len >= 1) {
|
594
|
-
/* Destroy internal buffer when it is empty and is quite large. */
|
595
|
-
if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) {
|
596
|
-
sdsfree(r->buf);
|
597
|
-
r->buf = sdsempty();
|
598
|
-
r->pos = 0;
|
599
|
-
|
600
|
-
/* r->buf should not be NULL since we just free'd a larger one. */
|
601
|
-
assert(r->buf != NULL);
|
602
|
-
}
|
603
|
-
|
604
|
-
newbuf = sdscatlen(r->buf,buf,len);
|
605
|
-
if (newbuf == NULL) {
|
606
|
-
__redisReaderSetErrorOOM(r);
|
607
|
-
return REDIS_ERR;
|
608
|
-
}
|
609
|
-
|
610
|
-
r->buf = newbuf;
|
611
|
-
r->len = sdslen(r->buf);
|
612
|
-
}
|
613
|
-
|
614
|
-
return REDIS_OK;
|
615
|
-
}
|
616
|
-
|
617
|
-
int redisReaderGetReply(redisReader *r, void **reply) {
|
618
|
-
/* Default target pointer to NULL. */
|
619
|
-
if (reply != NULL)
|
620
|
-
*reply = NULL;
|
621
|
-
|
622
|
-
/* Return early when this reader is in an erroneous state. */
|
623
|
-
if (r->err)
|
624
|
-
return REDIS_ERR;
|
625
|
-
|
626
|
-
/* When the buffer is empty, there will never be a reply. */
|
627
|
-
if (r->len == 0)
|
628
|
-
return REDIS_OK;
|
629
|
-
|
630
|
-
/* Set first item to process when the stack is empty. */
|
631
|
-
if (r->ridx == -1) {
|
632
|
-
r->rstack[0].type = -1;
|
633
|
-
r->rstack[0].elements = -1;
|
634
|
-
r->rstack[0].idx = -1;
|
635
|
-
r->rstack[0].obj = NULL;
|
636
|
-
r->rstack[0].parent = NULL;
|
637
|
-
r->rstack[0].privdata = r->privdata;
|
638
|
-
r->ridx = 0;
|
639
|
-
}
|
640
|
-
|
641
|
-
/* Process items in reply. */
|
642
|
-
while (r->ridx >= 0)
|
643
|
-
if (processItem(r) != REDIS_OK)
|
644
|
-
break;
|
645
|
-
|
646
|
-
/* Return ASAP when an error occurred. */
|
647
|
-
if (r->err)
|
648
|
-
return REDIS_ERR;
|
649
|
-
|
650
|
-
/* Discard part of the buffer when we've consumed at least 1k, to avoid
|
651
|
-
* doing unnecessary calls to memmove() in sds.c. */
|
652
|
-
if (r->pos >= 1024) {
|
653
|
-
r->buf = sdsrange(r->buf,r->pos,-1);
|
654
|
-
r->pos = 0;
|
655
|
-
r->len = sdslen(r->buf);
|
656
|
-
}
|
657
|
-
|
658
|
-
/* Emit a reply when there is one. */
|
659
|
-
if (r->ridx == -1) {
|
660
|
-
if (reply != NULL)
|
661
|
-
*reply = r->reply;
|
662
|
-
r->reply = NULL;
|
663
|
-
}
|
664
|
-
return REDIS_OK;
|
665
|
-
}
|
666
|
-
|
667
|
-
/* Calculate the number of bytes needed to represent an integer as string. */
|
668
|
-
static int intlen(int i) {
|
669
|
-
int len = 0;
|
670
|
-
if (i < 0) {
|
671
|
-
len++;
|
672
|
-
i = -i;
|
673
|
-
}
|
674
|
-
do {
|
675
|
-
len++;
|
676
|
-
i /= 10;
|
677
|
-
} while(i);
|
678
|
-
return len;
|
189
|
+
/* Return the number of digits of 'v' when converted to string in radix 10.
|
190
|
+
* Implementation borrowed from link in redis/src/util.c:string2ll(). */
|
191
|
+
static uint32_t countDigits(uint64_t v) {
|
192
|
+
uint32_t result = 1;
|
193
|
+
for (;;) {
|
194
|
+
if (v < 10) return result;
|
195
|
+
if (v < 100) return result + 1;
|
196
|
+
if (v < 1000) return result + 2;
|
197
|
+
if (v < 10000) return result + 3;
|
198
|
+
v /= 10000U;
|
199
|
+
result += 4;
|
200
|
+
}
|
679
201
|
}
|
680
202
|
|
681
203
|
/* Helper that calculates the bulk length given a certain string length. */
|
682
204
|
static size_t bulklen(size_t len) {
|
683
|
-
return 1+
|
205
|
+
return 1+countDigits(len)+2+len+2;
|
684
206
|
}
|
685
207
|
|
686
208
|
int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
@@ -692,6 +214,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
|
692
214
|
char **curargv = NULL, **newargv = NULL;
|
693
215
|
int argc = 0;
|
694
216
|
int totlen = 0;
|
217
|
+
int error_type = 0; /* 0 = no error; -1 = memory error; -2 = format error */
|
695
218
|
int j;
|
696
219
|
|
697
220
|
/* Abort if there is not target to set */
|
@@ -708,19 +231,19 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
|
708
231
|
if (*c == ' ') {
|
709
232
|
if (touched) {
|
710
233
|
newargv = realloc(curargv,sizeof(char*)*(argc+1));
|
711
|
-
if (newargv == NULL) goto
|
234
|
+
if (newargv == NULL) goto memory_err;
|
712
235
|
curargv = newargv;
|
713
236
|
curargv[argc++] = curarg;
|
714
237
|
totlen += bulklen(sdslen(curarg));
|
715
238
|
|
716
239
|
/* curarg is put in argv so it can be overwritten. */
|
717
240
|
curarg = sdsempty();
|
718
|
-
if (curarg == NULL) goto
|
241
|
+
if (curarg == NULL) goto memory_err;
|
719
242
|
touched = 0;
|
720
243
|
}
|
721
244
|
} else {
|
722
245
|
newarg = sdscatlen(curarg,c,1);
|
723
|
-
if (newarg == NULL) goto
|
246
|
+
if (newarg == NULL) goto memory_err;
|
724
247
|
curarg = newarg;
|
725
248
|
touched = 1;
|
726
249
|
}
|
@@ -751,17 +274,14 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
|
751
274
|
/* Try to detect printf format */
|
752
275
|
{
|
753
276
|
static const char intfmts[] = "diouxX";
|
277
|
+
static const char flags[] = "#0-+ ";
|
754
278
|
char _format[16];
|
755
279
|
const char *_p = c+1;
|
756
280
|
size_t _l = 0;
|
757
281
|
va_list _cpy;
|
758
282
|
|
759
283
|
/* Flags */
|
760
|
-
|
761
|
-
if (*_p != '\0' && *_p == '0') _p++;
|
762
|
-
if (*_p != '\0' && *_p == '-') _p++;
|
763
|
-
if (*_p != '\0' && *_p == ' ') _p++;
|
764
|
-
if (*_p != '\0' && *_p == '+') _p++;
|
284
|
+
while (*_p != '\0' && strchr(flags,*_p) != NULL) _p++;
|
765
285
|
|
766
286
|
/* Field width */
|
767
287
|
while (*_p != '\0' && isdigit(*_p)) _p++;
|
@@ -829,7 +349,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
|
829
349
|
|
830
350
|
fmt_invalid:
|
831
351
|
va_end(_cpy);
|
832
|
-
goto
|
352
|
+
goto format_err;
|
833
353
|
|
834
354
|
fmt_valid:
|
835
355
|
_l = (_p+1)-c;
|
@@ -848,7 +368,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
|
848
368
|
}
|
849
369
|
}
|
850
370
|
|
851
|
-
if (newarg == NULL) goto
|
371
|
+
if (newarg == NULL) goto memory_err;
|
852
372
|
curarg = newarg;
|
853
373
|
|
854
374
|
touched = 1;
|
@@ -860,7 +380,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
|
860
380
|
/* Add the last argument if needed */
|
861
381
|
if (touched) {
|
862
382
|
newargv = realloc(curargv,sizeof(char*)*(argc+1));
|
863
|
-
if (newargv == NULL) goto
|
383
|
+
if (newargv == NULL) goto memory_err;
|
864
384
|
curargv = newargv;
|
865
385
|
curargv[argc++] = curarg;
|
866
386
|
totlen += bulklen(sdslen(curarg));
|
@@ -872,11 +392,11 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
|
872
392
|
curarg = NULL;
|
873
393
|
|
874
394
|
/* Add bytes needed to hold multi bulk count */
|
875
|
-
totlen += 1+
|
395
|
+
totlen += 1+countDigits(argc)+2;
|
876
396
|
|
877
397
|
/* Build the command at protocol level */
|
878
398
|
cmd = malloc(totlen+1);
|
879
|
-
if (cmd == NULL) goto
|
399
|
+
if (cmd == NULL) goto memory_err;
|
880
400
|
|
881
401
|
pos = sprintf(cmd,"*%d\r\n",argc);
|
882
402
|
for (j = 0; j < argc; j++) {
|
@@ -894,20 +414,29 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
|
894
414
|
*target = cmd;
|
895
415
|
return totlen;
|
896
416
|
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
free(curargv);
|
417
|
+
format_err:
|
418
|
+
error_type = -2;
|
419
|
+
goto cleanup;
|
901
420
|
|
902
|
-
|
903
|
-
|
421
|
+
memory_err:
|
422
|
+
error_type = -1;
|
423
|
+
goto cleanup;
|
424
|
+
|
425
|
+
cleanup:
|
426
|
+
if (curargv) {
|
427
|
+
while(argc--)
|
428
|
+
sdsfree(curargv[argc]);
|
429
|
+
free(curargv);
|
430
|
+
}
|
431
|
+
|
432
|
+
sdsfree(curarg);
|
904
433
|
|
905
434
|
/* No need to check cmd since it is the last statement that can fail,
|
906
435
|
* but do it anyway to be as defensive as possible. */
|
907
436
|
if (cmd != NULL)
|
908
437
|
free(cmd);
|
909
438
|
|
910
|
-
return
|
439
|
+
return error_type;
|
911
440
|
}
|
912
441
|
|
913
442
|
/* Format a command according to the Redis protocol. This function
|
@@ -917,7 +446,7 @@ err:
|
|
917
446
|
* %b represents a binary safe string
|
918
447
|
*
|
919
448
|
* When using %b you need to provide both the pointer to the string
|
920
|
-
* and the length in bytes. Examples:
|
449
|
+
* and the length in bytes as a size_t. Examples:
|
921
450
|
*
|
922
451
|
* len = redisFormatCommand(target, "GET %s", mykey);
|
923
452
|
* len = redisFormatCommand(target, "SET %s %b", mykey, myval, myvallen);
|
@@ -928,9 +457,69 @@ int redisFormatCommand(char **target, const char *format, ...) {
|
|
928
457
|
va_start(ap,format);
|
929
458
|
len = redisvFormatCommand(target,format,ap);
|
930
459
|
va_end(ap);
|
460
|
+
|
461
|
+
/* The API says "-1" means bad result, but we now also return "-2" in some
|
462
|
+
* cases. Force the return value to always be -1. */
|
463
|
+
if (len < 0)
|
464
|
+
len = -1;
|
465
|
+
|
931
466
|
return len;
|
932
467
|
}
|
933
468
|
|
469
|
+
/* Format a command according to the Redis protocol using an sds string and
|
470
|
+
* sdscatfmt for the processing of arguments. This function takes the
|
471
|
+
* number of arguments, an array with arguments and an array with their
|
472
|
+
* lengths. If the latter is set to NULL, strlen will be used to compute the
|
473
|
+
* argument lengths.
|
474
|
+
*/
|
475
|
+
int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv,
|
476
|
+
const size_t *argvlen)
|
477
|
+
{
|
478
|
+
sds cmd;
|
479
|
+
unsigned long long totlen;
|
480
|
+
int j;
|
481
|
+
size_t len;
|
482
|
+
|
483
|
+
/* Abort on a NULL target */
|
484
|
+
if (target == NULL)
|
485
|
+
return -1;
|
486
|
+
|
487
|
+
/* Calculate our total size */
|
488
|
+
totlen = 1+countDigits(argc)+2;
|
489
|
+
for (j = 0; j < argc; j++) {
|
490
|
+
len = argvlen ? argvlen[j] : strlen(argv[j]);
|
491
|
+
totlen += bulklen(len);
|
492
|
+
}
|
493
|
+
|
494
|
+
/* Use an SDS string for command construction */
|
495
|
+
cmd = sdsempty();
|
496
|
+
if (cmd == NULL)
|
497
|
+
return -1;
|
498
|
+
|
499
|
+
/* We already know how much storage we need */
|
500
|
+
cmd = sdsMakeRoomFor(cmd, totlen);
|
501
|
+
if (cmd == NULL)
|
502
|
+
return -1;
|
503
|
+
|
504
|
+
/* Construct command */
|
505
|
+
cmd = sdscatfmt(cmd, "*%i\r\n", argc);
|
506
|
+
for (j=0; j < argc; j++) {
|
507
|
+
len = argvlen ? argvlen[j] : strlen(argv[j]);
|
508
|
+
cmd = sdscatfmt(cmd, "$%T\r\n", len);
|
509
|
+
cmd = sdscatlen(cmd, argv[j], len);
|
510
|
+
cmd = sdscatlen(cmd, "\r\n", sizeof("\r\n")-1);
|
511
|
+
}
|
512
|
+
|
513
|
+
assert(sdslen(cmd)==totlen);
|
514
|
+
|
515
|
+
*target = cmd;
|
516
|
+
return totlen;
|
517
|
+
}
|
518
|
+
|
519
|
+
void redisFreeSdsCommand(sds cmd) {
|
520
|
+
sdsfree(cmd);
|
521
|
+
}
|
522
|
+
|
934
523
|
/* Format a command according to the Redis protocol. This function takes the
|
935
524
|
* number of arguments, an array with arguments and an array with their
|
936
525
|
* lengths. If the latter is set to NULL, strlen will be used to compute the
|
@@ -942,8 +531,12 @@ int redisFormatCommandArgv(char **target, int argc, const char **argv, const siz
|
|
942
531
|
size_t len;
|
943
532
|
int totlen, j;
|
944
533
|
|
534
|
+
/* Abort on a NULL target */
|
535
|
+
if (target == NULL)
|
536
|
+
return -1;
|
537
|
+
|
945
538
|
/* Calculate number of bytes needed for the command */
|
946
|
-
totlen = 1+
|
539
|
+
totlen = 1+countDigits(argc)+2;
|
947
540
|
for (j = 0; j < argc; j++) {
|
948
541
|
len = argvlen ? argvlen[j] : strlen(argv[j]);
|
949
542
|
totlen += bulklen(len);
|
@@ -970,6 +563,10 @@ int redisFormatCommandArgv(char **target, int argc, const char **argv, const siz
|
|
970
563
|
return totlen;
|
971
564
|
}
|
972
565
|
|
566
|
+
void redisFreeCommand(char *cmd) {
|
567
|
+
free(cmd);
|
568
|
+
}
|
569
|
+
|
973
570
|
void __redisSetError(redisContext *c, int type, const char *str) {
|
974
571
|
size_t len;
|
975
572
|
|
@@ -982,10 +579,14 @@ void __redisSetError(redisContext *c, int type, const char *str) {
|
|
982
579
|
} else {
|
983
580
|
/* Only REDIS_ERR_IO may lack a description! */
|
984
581
|
assert(type == REDIS_ERR_IO);
|
985
|
-
|
582
|
+
__redis_strerror_r(errno, c->errstr, sizeof(c->errstr));
|
986
583
|
}
|
987
584
|
}
|
988
585
|
|
586
|
+
redisReader *redisReaderCreate(void) {
|
587
|
+
return redisReaderCreateWithFunctions(&defaultFunctions);
|
588
|
+
}
|
589
|
+
|
989
590
|
static redisContext *redisContextInit(void) {
|
990
591
|
redisContext *c;
|
991
592
|
|
@@ -997,10 +598,18 @@ static redisContext *redisContextInit(void) {
|
|
997
598
|
c->errstr[0] = '\0';
|
998
599
|
c->obuf = sdsempty();
|
999
600
|
c->reader = redisReaderCreate();
|
601
|
+
|
602
|
+
if (c->obuf == NULL || c->reader == NULL) {
|
603
|
+
redisFree(c);
|
604
|
+
return NULL;
|
605
|
+
}
|
606
|
+
|
1000
607
|
return c;
|
1001
608
|
}
|
1002
609
|
|
1003
610
|
void redisFree(redisContext *c) {
|
611
|
+
if (c == NULL)
|
612
|
+
return;
|
1004
613
|
if (c->fd > 0)
|
1005
614
|
close(c->fd);
|
1006
615
|
if (c->obuf != NULL)
|
@@ -1010,58 +619,131 @@ void redisFree(redisContext *c) {
|
|
1010
619
|
free(c);
|
1011
620
|
}
|
1012
621
|
|
622
|
+
int redisFreeKeepFd(redisContext *c) {
|
623
|
+
int fd = c->fd;
|
624
|
+
c->fd = -1;
|
625
|
+
redisFree(c);
|
626
|
+
return fd;
|
627
|
+
}
|
628
|
+
|
1013
629
|
/* Connect to a Redis instance. On error the field error in the returned
|
1014
630
|
* context will be set to the return value of the error function.
|
1015
631
|
* When no set of reply functions is given, the default set will be used. */
|
1016
632
|
redisContext *redisConnect(const char *ip, int port) {
|
1017
|
-
redisContext *c
|
633
|
+
redisContext *c;
|
634
|
+
|
635
|
+
c = redisContextInit();
|
636
|
+
if (c == NULL)
|
637
|
+
return NULL;
|
638
|
+
|
1018
639
|
c->flags |= REDIS_BLOCK;
|
1019
640
|
redisContextConnectTcp(c,ip,port,NULL);
|
1020
641
|
return c;
|
1021
642
|
}
|
1022
643
|
|
1023
|
-
redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv) {
|
1024
|
-
redisContext *c
|
644
|
+
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) {
|
645
|
+
redisContext *c;
|
646
|
+
|
647
|
+
c = redisContextInit();
|
648
|
+
if (c == NULL)
|
649
|
+
return NULL;
|
650
|
+
|
1025
651
|
c->flags |= REDIS_BLOCK;
|
1026
652
|
redisContextConnectTcp(c,ip,port,&tv);
|
1027
653
|
return c;
|
1028
654
|
}
|
1029
655
|
|
1030
656
|
redisContext *redisConnectNonBlock(const char *ip, int port) {
|
1031
|
-
redisContext *c
|
657
|
+
redisContext *c;
|
658
|
+
|
659
|
+
c = redisContextInit();
|
660
|
+
if (c == NULL)
|
661
|
+
return NULL;
|
662
|
+
|
1032
663
|
c->flags &= ~REDIS_BLOCK;
|
1033
664
|
redisContextConnectTcp(c,ip,port,NULL);
|
1034
665
|
return c;
|
1035
666
|
}
|
1036
667
|
|
1037
|
-
redisContext *
|
668
|
+
redisContext *redisConnectBindNonBlock(const char *ip, int port,
|
669
|
+
const char *source_addr) {
|
670
|
+
redisContext *c = redisContextInit();
|
671
|
+
c->flags &= ~REDIS_BLOCK;
|
672
|
+
redisContextConnectBindTcp(c,ip,port,NULL,source_addr);
|
673
|
+
return c;
|
674
|
+
}
|
675
|
+
|
676
|
+
redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
|
677
|
+
const char *source_addr) {
|
1038
678
|
redisContext *c = redisContextInit();
|
679
|
+
c->flags &= ~REDIS_BLOCK;
|
680
|
+
c->flags |= REDIS_REUSEADDR;
|
681
|
+
redisContextConnectBindTcp(c,ip,port,NULL,source_addr);
|
682
|
+
return c;
|
683
|
+
}
|
684
|
+
|
685
|
+
redisContext *redisConnectUnix(const char *path) {
|
686
|
+
redisContext *c;
|
687
|
+
|
688
|
+
c = redisContextInit();
|
689
|
+
if (c == NULL)
|
690
|
+
return NULL;
|
691
|
+
|
1039
692
|
c->flags |= REDIS_BLOCK;
|
1040
693
|
redisContextConnectUnix(c,path,NULL);
|
1041
694
|
return c;
|
1042
695
|
}
|
1043
696
|
|
1044
|
-
redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv) {
|
1045
|
-
redisContext *c
|
697
|
+
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) {
|
698
|
+
redisContext *c;
|
699
|
+
|
700
|
+
c = redisContextInit();
|
701
|
+
if (c == NULL)
|
702
|
+
return NULL;
|
703
|
+
|
1046
704
|
c->flags |= REDIS_BLOCK;
|
1047
705
|
redisContextConnectUnix(c,path,&tv);
|
1048
706
|
return c;
|
1049
707
|
}
|
1050
708
|
|
1051
709
|
redisContext *redisConnectUnixNonBlock(const char *path) {
|
1052
|
-
redisContext *c
|
710
|
+
redisContext *c;
|
711
|
+
|
712
|
+
c = redisContextInit();
|
713
|
+
if (c == NULL)
|
714
|
+
return NULL;
|
715
|
+
|
1053
716
|
c->flags &= ~REDIS_BLOCK;
|
1054
717
|
redisContextConnectUnix(c,path,NULL);
|
1055
718
|
return c;
|
1056
719
|
}
|
1057
720
|
|
721
|
+
redisContext *redisConnectFd(int fd) {
|
722
|
+
redisContext *c;
|
723
|
+
|
724
|
+
c = redisContextInit();
|
725
|
+
if (c == NULL)
|
726
|
+
return NULL;
|
727
|
+
|
728
|
+
c->fd = fd;
|
729
|
+
c->flags |= REDIS_BLOCK | REDIS_CONNECTED;
|
730
|
+
return c;
|
731
|
+
}
|
732
|
+
|
1058
733
|
/* Set read/write timeout on a blocking socket. */
|
1059
|
-
int redisSetTimeout(redisContext *c, struct timeval tv) {
|
734
|
+
int redisSetTimeout(redisContext *c, const struct timeval tv) {
|
1060
735
|
if (c->flags & REDIS_BLOCK)
|
1061
736
|
return redisContextSetTimeout(c,tv);
|
1062
737
|
return REDIS_ERR;
|
1063
738
|
}
|
1064
739
|
|
740
|
+
/* Enable connection KeepAlive. */
|
741
|
+
int redisEnableKeepAlive(redisContext *c) {
|
742
|
+
if (redisKeepAlive(c, REDIS_KEEPALIVE_INTERVAL) != REDIS_OK)
|
743
|
+
return REDIS_ERR;
|
744
|
+
return REDIS_OK;
|
745
|
+
}
|
746
|
+
|
1065
747
|
/* Use this function to handle a read event on the descriptor. It will try
|
1066
748
|
* and read some bytes from the socket and feed them to the reply parser.
|
1067
749
|
*
|
@@ -1077,7 +759,7 @@ int redisBufferRead(redisContext *c) {
|
|
1077
759
|
|
1078
760
|
nread = read(c->fd,buf,sizeof(buf));
|
1079
761
|
if (nread == -1) {
|
1080
|
-
if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) {
|
762
|
+
if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
|
1081
763
|
/* Try again later */
|
1082
764
|
} else {
|
1083
765
|
__redisSetError(c,REDIS_ERR_IO,NULL);
|
@@ -1114,7 +796,7 @@ int redisBufferWrite(redisContext *c, int *done) {
|
|
1114
796
|
if (sdslen(c->obuf) > 0) {
|
1115
797
|
nwritten = write(c->fd,c->obuf,sdslen(c->obuf));
|
1116
798
|
if (nwritten == -1) {
|
1117
|
-
if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) {
|
799
|
+
if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
|
1118
800
|
/* Try again later */
|
1119
801
|
} else {
|
1120
802
|
__redisSetError(c,REDIS_ERR_IO,NULL);
|
@@ -1125,7 +807,7 @@ int redisBufferWrite(redisContext *c, int *done) {
|
|
1125
807
|
sdsfree(c->obuf);
|
1126
808
|
c->obuf = sdsempty();
|
1127
809
|
} else {
|
1128
|
-
|
810
|
+
sdsrange(c->obuf,nwritten,-1);
|
1129
811
|
}
|
1130
812
|
}
|
1131
813
|
}
|
@@ -1180,7 +862,7 @@ int redisGetReply(redisContext *c, void **reply) {
|
|
1180
862
|
* is used, you need to call redisGetReply yourself to retrieve
|
1181
863
|
* the reply (or replies in pub/sub).
|
1182
864
|
*/
|
1183
|
-
int __redisAppendCommand(redisContext *c, char *cmd, size_t len) {
|
865
|
+
int __redisAppendCommand(redisContext *c, const char *cmd, size_t len) {
|
1184
866
|
sds newbuf;
|
1185
867
|
|
1186
868
|
newbuf = sdscatlen(c->obuf,cmd,len);
|
@@ -1193,6 +875,15 @@ int __redisAppendCommand(redisContext *c, char *cmd, size_t len) {
|
|
1193
875
|
return REDIS_OK;
|
1194
876
|
}
|
1195
877
|
|
878
|
+
int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len) {
|
879
|
+
|
880
|
+
if (__redisAppendCommand(c, cmd, len) != REDIS_OK) {
|
881
|
+
return REDIS_ERR;
|
882
|
+
}
|
883
|
+
|
884
|
+
return REDIS_OK;
|
885
|
+
}
|
886
|
+
|
1196
887
|
int redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
|
1197
888
|
char *cmd;
|
1198
889
|
int len;
|
@@ -1201,6 +892,9 @@ int redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
|
|
1201
892
|
if (len == -1) {
|
1202
893
|
__redisSetError(c,REDIS_ERR_OOM,"Out of memory");
|
1203
894
|
return REDIS_ERR;
|
895
|
+
} else if (len == -2) {
|
896
|
+
__redisSetError(c,REDIS_ERR_OTHER,"Invalid format string");
|
897
|
+
return REDIS_ERR;
|
1204
898
|
}
|
1205
899
|
|
1206
900
|
if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
|
@@ -1223,21 +917,21 @@ int redisAppendCommand(redisContext *c, const char *format, ...) {
|
|
1223
917
|
}
|
1224
918
|
|
1225
919
|
int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
|
1226
|
-
|
920
|
+
sds cmd;
|
1227
921
|
int len;
|
1228
922
|
|
1229
|
-
len =
|
923
|
+
len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
|
1230
924
|
if (len == -1) {
|
1231
925
|
__redisSetError(c,REDIS_ERR_OOM,"Out of memory");
|
1232
926
|
return REDIS_ERR;
|
1233
927
|
}
|
1234
928
|
|
1235
929
|
if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
|
1236
|
-
|
930
|
+
sdsfree(cmd);
|
1237
931
|
return REDIS_ERR;
|
1238
932
|
}
|
1239
933
|
|
1240
|
-
|
934
|
+
sdsfree(cmd);
|
1241
935
|
return REDIS_OK;
|
1242
936
|
}
|
1243
937
|
|