hiredis 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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