hiredis 0.2.0 → 0.3.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.
@@ -1,4 +1,4 @@
1
- /* Hash Tables Implementation.
1
+ /* Hash table implementation.
2
2
  *
3
3
  * This file implements in memory hash tables with insert/del/replace/find/
4
4
  * get-random-element operations. Hash tables will auto resize if needed
@@ -111,20 +111,16 @@ typedef struct dictIterator {
111
111
  #define dictSize(ht) ((ht)->used)
112
112
 
113
113
  /* API */
114
- dict *dictCreate(dictType *type, void *privDataPtr);
115
- int dictExpand(dict *ht, unsigned long size);
116
- int dictAdd(dict *ht, void *key, void *val);
117
- int dictReplace(dict *ht, void *key, void *val);
118
- int dictDelete(dict *ht, const void *key);
119
- int dictDeleteNoFree(dict *ht, const void *key);
120
- void dictRelease(dict *ht);
121
- dictEntry * dictFind(dict *ht, const void *key);
122
- int dictResize(dict *ht);
123
- dictIterator *dictGetIterator(dict *ht);
124
- dictEntry *dictNext(dictIterator *iter);
125
- void dictReleaseIterator(dictIterator *iter);
126
- dictEntry *dictGetRandomKey(dict *ht);
127
- unsigned int dictGenHashFunction(const unsigned char *buf, int len);
128
- void dictEmpty(dict *ht);
114
+ static unsigned int dictGenHashFunction(const unsigned char *buf, int len);
115
+ static dict *dictCreate(dictType *type, void *privDataPtr);
116
+ static int dictExpand(dict *ht, unsigned long size);
117
+ static int dictAdd(dict *ht, void *key, void *val);
118
+ static int dictReplace(dict *ht, void *key, void *val);
119
+ static int dictDelete(dict *ht, const void *key);
120
+ static void dictRelease(dict *ht);
121
+ static dictEntry * dictFind(dict *ht, const void *key);
122
+ static dictIterator *dictGetIterator(dict *ht);
123
+ static dictEntry *dictNext(dictIterator *iter);
124
+ static void dictReleaseIterator(dictIterator *iter);
129
125
 
130
126
  #endif /* __DICT_H */
@@ -434,7 +434,7 @@ static int processItem(redisReader *r) {
434
434
  }
435
435
  }
436
436
 
437
- void *redisReplyReaderCreate() {
437
+ void *redisReplyReaderCreate(void) {
438
438
  redisReader *r = calloc(sizeof(redisReader),1);
439
439
  r->error = NULL;
440
440
  r->fn = &defaultFunctions;
@@ -493,7 +493,7 @@ static void redisSetReplyReaderError(redisReader *r, sds err) {
493
493
  if (r->buf != NULL) {
494
494
  sdsfree(r->buf);
495
495
  r->buf = sdsempty();
496
- r->pos = 0;
496
+ r->pos = r->len = 0;
497
497
  }
498
498
  r->ridx = -1;
499
499
  r->error = err;
@@ -504,7 +504,7 @@ char *redisReplyReaderGetError(void *reader) {
504
504
  return r->error;
505
505
  }
506
506
 
507
- void redisReplyReaderFeed(void *reader, char *buf, size_t len) {
507
+ void redisReplyReaderFeed(void *reader, const char *buf, size_t len) {
508
508
  redisReader *r = reader;
509
509
 
510
510
  /* Copy the provided buffer. */
@@ -538,15 +538,10 @@ int redisReplyReaderGetReply(void *reader, void **reply) {
538
538
  if (processItem(r) < 0)
539
539
  break;
540
540
 
541
- /* Discard the consumed part of the buffer. */
542
- if (r->pos > 0) {
543
- if (r->pos == r->len) {
544
- /* sdsrange has a quirck on this edge case. */
545
- sdsfree(r->buf);
546
- r->buf = sdsempty();
547
- } else {
548
- r->buf = sdsrange(r->buf,r->pos,r->len);
549
- }
541
+ /* Discard part of the buffer when we've consumed at least 1k, to avoid
542
+ * doing unnecessary calls to memmove() in sds.c. */
543
+ if (r->pos >= 1024) {
544
+ r->buf = sdsrange(r->buf,r->pos,-1);
550
545
  r->pos = 0;
551
546
  r->len = sdslen(r->buf);
552
547
  }
@@ -798,7 +793,7 @@ void __redisSetError(redisContext *c, int type, const sds errstr) {
798
793
  }
799
794
  }
800
795
 
801
- static redisContext *redisContextInit() {
796
+ static redisContext *redisContextInit(void) {
802
797
  redisContext *c = calloc(sizeof(redisContext),1);
803
798
  c->err = 0;
804
799
  c->errstr = NULL;
@@ -826,28 +821,42 @@ void redisFree(redisContext *c) {
826
821
  redisContext *redisConnect(const char *ip, int port) {
827
822
  redisContext *c = redisContextInit();
828
823
  c->flags |= REDIS_BLOCK;
829
- redisContextConnectTcp(c,ip,port);
824
+ redisContextConnectTcp(c,ip,port,NULL);
825
+ return c;
826
+ }
827
+
828
+ redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv) {
829
+ redisContext *c = redisContextInit();
830
+ c->flags |= REDIS_BLOCK;
831
+ redisContextConnectTcp(c,ip,port,&tv);
830
832
  return c;
831
833
  }
832
834
 
833
835
  redisContext *redisConnectNonBlock(const char *ip, int port) {
834
836
  redisContext *c = redisContextInit();
835
837
  c->flags &= ~REDIS_BLOCK;
836
- redisContextConnectTcp(c,ip,port);
838
+ redisContextConnectTcp(c,ip,port,NULL);
837
839
  return c;
838
840
  }
839
841
 
840
842
  redisContext *redisConnectUnix(const char *path) {
841
843
  redisContext *c = redisContextInit();
842
844
  c->flags |= REDIS_BLOCK;
843
- redisContextConnectUnix(c,path);
845
+ redisContextConnectUnix(c,path,NULL);
846
+ return c;
847
+ }
848
+
849
+ redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv) {
850
+ redisContext *c = redisContextInit();
851
+ c->flags |= REDIS_BLOCK;
852
+ redisContextConnectUnix(c,path,&tv);
844
853
  return c;
845
854
  }
846
855
 
847
856
  redisContext *redisConnectUnixNonBlock(const char *path) {
848
857
  redisContext *c = redisContextInit();
849
858
  c->flags &= ~REDIS_BLOCK;
850
- redisContextConnectUnix(c,path);
859
+ redisContextConnectUnix(c,path,NULL);
851
860
  return c;
852
861
  }
853
862
 
@@ -129,13 +129,13 @@ typedef struct redisContext {
129
129
  } redisContext;
130
130
 
131
131
  void freeReplyObject(void *reply);
132
- void *redisReplyReaderCreate();
132
+ void *redisReplyReaderCreate(void);
133
133
  int redisReplyReaderSetReplyObjectFunctions(void *reader, redisReplyObjectFunctions *fn);
134
134
  int redisReplyReaderSetPrivdata(void *reader, void *privdata);
135
135
  void *redisReplyReaderGetObject(void *reader);
136
136
  char *redisReplyReaderGetError(void *reader);
137
137
  void redisReplyReaderFree(void *ptr);
138
- void redisReplyReaderFeed(void *reader, char *buf, size_t len);
138
+ void redisReplyReaderFeed(void *reader, const char *buf, size_t len);
139
139
  int redisReplyReaderGetReply(void *reader, void **reply);
140
140
 
141
141
  /* Functions to format a command according to the protocol. */
@@ -144,8 +144,10 @@ int redisFormatCommand(char **target, const char *format, ...);
144
144
  int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
145
145
 
146
146
  redisContext *redisConnect(const char *ip, int port);
147
+ redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv);
147
148
  redisContext *redisConnectNonBlock(const char *ip, int port);
148
149
  redisContext *redisConnectUnix(const char *path);
150
+ redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv);
149
151
  redisContext *redisConnectUnixNonBlock(const char *path);
150
152
  int redisSetTimeout(redisContext *c, struct timeval tv);
151
153
  int redisSetReplyObjectFunctions(redisContext *c, redisReplyObjectFunctions *fn);
@@ -33,6 +33,7 @@
33
33
  #include "fmacros.h"
34
34
  #include <sys/types.h>
35
35
  #include <sys/socket.h>
36
+ #include <sys/select.h>
36
37
  #include <sys/un.h>
37
38
  #include <netinet/in.h>
38
39
  #include <netinet/tcp.h>
@@ -67,7 +68,7 @@ static int redisCreateSocket(redisContext *c, int type) {
67
68
  return s;
68
69
  }
69
70
 
70
- static int redisSetNonBlock(redisContext *c, int fd) {
71
+ static int redisSetBlocking(redisContext *c, int fd, int blocking) {
71
72
  int flags;
72
73
 
73
74
  /* Set the socket nonblocking.
@@ -79,9 +80,15 @@ static int redisSetNonBlock(redisContext *c, int fd) {
79
80
  close(fd);
80
81
  return REDIS_ERR;
81
82
  }
82
- if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
83
+
84
+ if (blocking)
85
+ flags &= ~O_NONBLOCK;
86
+ else
87
+ flags |= O_NONBLOCK;
88
+
89
+ if (fcntl(fd, F_SETFL, flags) == -1) {
83
90
  __redisSetError(c,REDIS_ERR_IO,
84
- sdscatprintf(sdsempty(), "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno)));
91
+ sdscatprintf(sdsempty(), "fcntl(F_SETFL): %s", strerror(errno)));
85
92
  close(fd);
86
93
  return REDIS_ERR;
87
94
  }
@@ -93,11 +100,67 @@ static int redisSetTcpNoDelay(redisContext *c, int fd) {
93
100
  if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
94
101
  __redisSetError(c,REDIS_ERR_IO,
95
102
  sdscatprintf(sdsempty(), "setsockopt(TCP_NODELAY): %s", strerror(errno)));
103
+ close(fd);
96
104
  return REDIS_ERR;
97
105
  }
98
106
  return REDIS_OK;
99
107
  }
100
108
 
109
+ static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *timeout) {
110
+ struct timeval to;
111
+ struct timeval *toptr = NULL;
112
+ fd_set wfd;
113
+ int err;
114
+ socklen_t errlen;
115
+
116
+ /* Only use timeout when not NULL. */
117
+ if (timeout != NULL) {
118
+ to = *timeout;
119
+ toptr = &to;
120
+ }
121
+
122
+ if (errno == EINPROGRESS) {
123
+ FD_ZERO(&wfd);
124
+ FD_SET(fd, &wfd);
125
+
126
+ if (select(FD_SETSIZE, NULL, &wfd, NULL, toptr) == -1) {
127
+ __redisSetError(c,REDIS_ERR_IO,
128
+ sdscatprintf(sdsempty(), "select(2): %s", strerror(errno)));
129
+ close(fd);
130
+ return REDIS_ERR;
131
+ }
132
+
133
+ if (!FD_ISSET(fd, &wfd)) {
134
+ errno = ETIMEDOUT;
135
+ __redisSetError(c,REDIS_ERR_IO,NULL);
136
+ close(fd);
137
+ return REDIS_ERR;
138
+ }
139
+
140
+ err = 0;
141
+ errlen = sizeof(err);
142
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
143
+ __redisSetError(c,REDIS_ERR_IO,
144
+ sdscatprintf(sdsempty(), "getsockopt(SO_ERROR): %s", strerror(errno)));
145
+ close(fd);
146
+ return REDIS_ERR;
147
+ }
148
+
149
+ if (err) {
150
+ errno = err;
151
+ __redisSetError(c,REDIS_ERR_IO,NULL);
152
+ close(fd);
153
+ return REDIS_ERR;
154
+ }
155
+
156
+ return REDIS_OK;
157
+ }
158
+
159
+ __redisSetError(c,REDIS_ERR_IO,NULL);
160
+ close(fd);
161
+ return REDIS_ERR;
162
+ }
163
+
101
164
  int redisContextSetTimeout(redisContext *c, struct timeval tv) {
102
165
  if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) {
103
166
  __redisSetError(c,REDIS_ERR_IO,
@@ -112,14 +175,14 @@ int redisContextSetTimeout(redisContext *c, struct timeval tv) {
112
175
  return REDIS_OK;
113
176
  }
114
177
 
115
- int redisContextConnectTcp(redisContext *c, const char *addr, int port) {
178
+ int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout) {
116
179
  int s;
117
180
  int blocking = (c->flags & REDIS_BLOCK);
118
181
  struct sockaddr_in sa;
119
182
 
120
- if ((s = redisCreateSocket(c,AF_INET)) == REDIS_ERR)
183
+ if ((s = redisCreateSocket(c,AF_INET)) < 0)
121
184
  return REDIS_ERR;
122
- if (!blocking && redisSetNonBlock(c,s) == REDIS_ERR)
185
+ if (redisSetBlocking(c,s,0) != REDIS_OK)
123
186
  return REDIS_ERR;
124
187
 
125
188
  sa.sin_family = AF_INET;
@@ -141,30 +204,31 @@ int redisContextConnectTcp(redisContext *c, const char *addr, int port) {
141
204
  if (errno == EINPROGRESS && !blocking) {
142
205
  /* This is ok. */
143
206
  } else {
144
- __redisSetError(c,REDIS_ERR_IO,NULL);
145
- close(s);
146
- return REDIS_ERR;
207
+ if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
208
+ return REDIS_ERR;
147
209
  }
148
210
  }
149
211
 
150
- if (redisSetTcpNoDelay(c,s) != REDIS_OK) {
151
- close(s);
212
+ /* Reset socket to be blocking after connect(2). */
213
+ if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
214
+ return REDIS_ERR;
215
+
216
+ if (redisSetTcpNoDelay(c,s) != REDIS_OK)
152
217
  return REDIS_ERR;
153
- }
154
218
 
155
219
  c->fd = s;
156
220
  c->flags |= REDIS_CONNECTED;
157
221
  return REDIS_OK;
158
222
  }
159
223
 
160
- int redisContextConnectUnix(redisContext *c, const char *path) {
224
+ int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout) {
161
225
  int s;
162
226
  int blocking = (c->flags & REDIS_BLOCK);
163
227
  struct sockaddr_un sa;
164
228
 
165
- if ((s = redisCreateSocket(c,AF_LOCAL)) == REDIS_ERR)
229
+ if ((s = redisCreateSocket(c,AF_LOCAL)) < 0)
166
230
  return REDIS_ERR;
167
- if (!blocking && redisSetNonBlock(c,s) != REDIS_OK)
231
+ if (redisSetBlocking(c,s,0) != REDIS_OK)
168
232
  return REDIS_ERR;
169
233
 
170
234
  sa.sun_family = AF_LOCAL;
@@ -173,12 +237,15 @@ int redisContextConnectUnix(redisContext *c, const char *path) {
173
237
  if (errno == EINPROGRESS && !blocking) {
174
238
  /* This is ok. */
175
239
  } else {
176
- __redisSetError(c,REDIS_ERR_IO,NULL);
177
- close(s);
178
- return REDIS_ERR;
240
+ if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
241
+ return REDIS_ERR;
179
242
  }
180
243
  }
181
244
 
245
+ /* Reset socket to be blocking after connect(2). */
246
+ if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
247
+ return REDIS_ERR;
248
+
182
249
  c->fd = s;
183
250
  c->flags |= REDIS_CONNECTED;
184
251
  return REDIS_OK;
@@ -40,7 +40,7 @@
40
40
  #endif
41
41
 
42
42
  int redisContextSetTimeout(redisContext *c, struct timeval tv);
43
- int redisContextConnectTcp(redisContext *c, const char *addr, int port);
44
- int redisContextConnectUnix(redisContext *c, const char *path);
43
+ int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout);
44
+ int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout);
45
45
 
46
46
  #endif
@@ -115,6 +115,25 @@ static sds sdsMakeRoomFor(sds s, size_t addlen) {
115
115
  return newsh->buf;
116
116
  }
117
117
 
118
+ /* Grow the sds to have the specified length. Bytes that were not part of
119
+ * the original length of the sds will be set to zero. */
120
+ sds sdsgrowzero(sds s, size_t len) {
121
+ struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
122
+ size_t totlen, curlen = sh->len;
123
+
124
+ if (len <= curlen) return s;
125
+ s = sdsMakeRoomFor(s,len-curlen);
126
+ if (s == NULL) return NULL;
127
+
128
+ /* Make sure added region doesn't contain garbage */
129
+ sh = (void*)(s-(sizeof(struct sdshdr)));
130
+ memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
131
+ totlen = sh->len+sh->free;
132
+ sh->len = len;
133
+ sh->free = totlen-sh->len;
134
+ return s;
135
+ }
136
+
118
137
  sds sdscatlen(sds s, const void *t, size_t len) {
119
138
  struct sdshdr *sh;
120
139
  size_t curlen = sdslen(s);
@@ -222,13 +241,16 @@ sds sdsrange(sds s, int start, int end) {
222
241
  }
223
242
  newlen = (start > end) ? 0 : (end-start)+1;
224
243
  if (newlen != 0) {
225
- if (start >= (signed)len) start = len-1;
226
- if (end >= (signed)len) end = len-1;
227
- newlen = (start > end) ? 0 : (end-start)+1;
244
+ if (start >= (signed)len) {
245
+ newlen = 0;
246
+ } else if (end >= (signed)len) {
247
+ end = len-1;
248
+ newlen = (start > end) ? 0 : (end-start)+1;
249
+ }
228
250
  } else {
229
251
  start = 0;
230
252
  }
231
- if (start != 0) memmove(sh->buf, sh->buf+start, newlen);
253
+ if (start && newlen) memmove(sh->buf, sh->buf+start, newlen);
232
254
  sh->buf[newlen] = 0;
233
255
  sh->free = sh->free+(sh->len-newlen);
234
256
  sh->len = newlen;
@@ -477,3 +499,106 @@ err:
477
499
  if (current) sdsfree(current);
478
500
  return NULL;
479
501
  }
502
+
503
+ #ifdef SDS_TEST_MAIN
504
+ #include <stdio.h>
505
+
506
+ int __failed_tests = 0;
507
+ int __test_num = 0;
508
+ #define test_cond(descr,_c) do { \
509
+ __test_num++; printf("%d - %s: ", __test_num, descr); \
510
+ if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \
511
+ } while(0);
512
+ #define test_report() do { \
513
+ printf("%d tests, %d passed, %d failed\n", __test_num, \
514
+ __test_num-__failed_tests, __failed_tests); \
515
+ if (__failed_tests) { \
516
+ printf("=== WARNING === We have failed tests here...\n"); \
517
+ } \
518
+ } while(0);
519
+
520
+ int main(void) {
521
+ {
522
+ sds x = sdsnew("foo"), y;
523
+
524
+ test_cond("Create a string and obtain the length",
525
+ sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0)
526
+
527
+ sdsfree(x);
528
+ x = sdsnewlen("foo",2);
529
+ test_cond("Create a string with specified length",
530
+ sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0)
531
+
532
+ x = sdscat(x,"bar");
533
+ test_cond("Strings concatenation",
534
+ sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0);
535
+
536
+ x = sdscpy(x,"a");
537
+ test_cond("sdscpy() against an originally longer string",
538
+ sdslen(x) == 1 && memcmp(x,"a\0",2) == 0)
539
+
540
+ x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk");
541
+ test_cond("sdscpy() against an originally shorter string",
542
+ sdslen(x) == 33 &&
543
+ memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0)
544
+
545
+ sdsfree(x);
546
+ x = sdscatprintf(sdsempty(),"%d",123);
547
+ test_cond("sdscatprintf() seems working in the base case",
548
+ sdslen(x) == 3 && memcmp(x,"123\0",4) ==0)
549
+
550
+ sdsfree(x);
551
+ x = sdstrim(sdsnew("xxciaoyyy"),"xy");
552
+ test_cond("sdstrim() correctly trims characters",
553
+ sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)
554
+
555
+ y = sdsrange(sdsdup(x),1,1);
556
+ test_cond("sdsrange(...,1,1)",
557
+ sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)
558
+
559
+ sdsfree(y);
560
+ y = sdsrange(sdsdup(x),1,-1);
561
+ test_cond("sdsrange(...,1,-1)",
562
+ sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
563
+
564
+ sdsfree(y);
565
+ y = sdsrange(sdsdup(x),-2,-1);
566
+ test_cond("sdsrange(...,-2,-1)",
567
+ sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)
568
+
569
+ sdsfree(y);
570
+ y = sdsrange(sdsdup(x),2,1);
571
+ test_cond("sdsrange(...,2,1)",
572
+ sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
573
+
574
+ sdsfree(y);
575
+ y = sdsrange(sdsdup(x),1,100);
576
+ test_cond("sdsrange(...,1,100)",
577
+ sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
578
+
579
+ sdsfree(y);
580
+ y = sdsrange(sdsdup(x),100,100);
581
+ test_cond("sdsrange(...,100,100)",
582
+ sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
583
+
584
+ sdsfree(y);
585
+ sdsfree(x);
586
+ x = sdsnew("foo");
587
+ y = sdsnew("foa");
588
+ test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0)
589
+
590
+ sdsfree(y);
591
+ sdsfree(x);
592
+ x = sdsnew("bar");
593
+ y = sdsnew("bar");
594
+ test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0)
595
+
596
+ sdsfree(y);
597
+ sdsfree(x);
598
+ x = sdsnew("aar");
599
+ y = sdsnew("bar");
600
+ test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
601
+ }
602
+ test_report()
603
+ }
604
+ #endif