hiredis 0.6.1 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
data/vendor/hiredis/net.h CHANGED
@@ -1,7 +1,9 @@
1
1
  /* Extracted from anet.c to work properly with Hiredis error reporting.
2
2
  *
3
- * Copyright (c) 2006-2011, Salvatore Sanfilippo <antirez at gmail dot com>
4
- * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
3
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
4
+ * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
5
+ * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
6
+ * Jan-Erik Rediger <janerik at fnordig dot com>
5
7
  *
6
8
  * All rights reserved.
7
9
  *
@@ -35,10 +37,6 @@
35
37
 
36
38
  #include "hiredis.h"
37
39
 
38
- #if defined(__sun)
39
- #define AF_LOCAL AF_UNIX
40
- #endif
41
-
42
40
  int redisCheckSocketError(redisContext *c);
43
41
  int redisContextSetTimeout(redisContext *c, const struct timeval tv);
44
42
  int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout);
@@ -33,10 +33,13 @@
33
33
  #include "fmacros.h"
34
34
  #include <string.h>
35
35
  #include <stdlib.h>
36
+ #ifndef _MSC_VER
36
37
  #include <unistd.h>
38
+ #endif
37
39
  #include <assert.h>
38
40
  #include <errno.h>
39
41
  #include <ctype.h>
42
+ #include <limits.h>
40
43
 
41
44
  #include "read.h"
42
45
  #include "sds.h"
@@ -50,11 +53,9 @@ static void __redisReaderSetError(redisReader *r, int type, const char *str) {
50
53
  }
51
54
 
52
55
  /* Clear input buffer on errors. */
53
- if (r->buf != NULL) {
54
- sdsfree(r->buf);
55
- r->buf = NULL;
56
- r->pos = r->len = 0;
57
- }
56
+ sdsfree(r->buf);
57
+ r->buf = NULL;
58
+ r->pos = r->len = 0;
58
59
 
59
60
  /* Reset task stack. */
60
61
  r->ridx = -1;
@@ -125,7 +126,7 @@ static char *seekNewline(char *s, size_t len) {
125
126
  * might not have a trailing NULL character. */
126
127
  while (pos < _len) {
127
128
  while(pos < _len && s[pos] != '\r') pos++;
128
- if (s[pos] != '\r') {
129
+ if (pos==_len) {
129
130
  /* Not found. */
130
131
  return NULL;
131
132
  } else {
@@ -141,33 +142,79 @@ static char *seekNewline(char *s, size_t len) {
141
142
  return NULL;
142
143
  }
143
144
 
144
- /* Read a long long value starting at *s, under the assumption that it will be
145
- * terminated by \r\n. Ambiguously returns -1 for unexpected input. */
146
- static long long readLongLong(char *s) {
147
- long long v = 0;
148
- int dec, mult = 1;
149
- char c;
150
-
151
- if (*s == '-') {
152
- mult = -1;
153
- s++;
154
- } else if (*s == '+') {
155
- mult = 1;
156
- s++;
145
+ /* Convert a string into a long long. Returns REDIS_OK if the string could be
146
+ * parsed into a (non-overflowing) long long, REDIS_ERR otherwise. The value
147
+ * will be set to the parsed value when appropriate.
148
+ *
149
+ * Note that this function demands that the string strictly represents
150
+ * a long long: no spaces or other characters before or after the string
151
+ * representing the number are accepted, nor zeroes at the start if not
152
+ * for the string "0" representing the zero number.
153
+ *
154
+ * Because of its strictness, it is safe to use this function to check if
155
+ * you can convert a string into a long long, and obtain back the string
156
+ * from the number without any loss in the string representation. */
157
+ static int string2ll(const char *s, size_t slen, long long *value) {
158
+ const char *p = s;
159
+ size_t plen = 0;
160
+ int negative = 0;
161
+ unsigned long long v;
162
+
163
+ if (plen == slen)
164
+ return REDIS_ERR;
165
+
166
+ /* Special case: first and only digit is 0. */
167
+ if (slen == 1 && p[0] == '0') {
168
+ if (value != NULL) *value = 0;
169
+ return REDIS_OK;
157
170
  }
158
171
 
159
- while ((c = *(s++)) != '\r') {
160
- dec = c - '0';
161
- if (dec >= 0 && dec < 10) {
162
- v *= 10;
163
- v += dec;
164
- } else {
165
- /* Should not happen... */
166
- return -1;
167
- }
172
+ if (p[0] == '-') {
173
+ negative = 1;
174
+ p++; plen++;
175
+
176
+ /* Abort on only a negative sign. */
177
+ if (plen == slen)
178
+ return REDIS_ERR;
179
+ }
180
+
181
+ /* First digit should be 1-9, otherwise the string should just be 0. */
182
+ if (p[0] >= '1' && p[0] <= '9') {
183
+ v = p[0]-'0';
184
+ p++; plen++;
185
+ } else if (p[0] == '0' && slen == 1) {
186
+ *value = 0;
187
+ return REDIS_OK;
188
+ } else {
189
+ return REDIS_ERR;
168
190
  }
169
191
 
170
- return mult*v;
192
+ while (plen < slen && p[0] >= '0' && p[0] <= '9') {
193
+ if (v > (ULLONG_MAX / 10)) /* Overflow. */
194
+ return REDIS_ERR;
195
+ v *= 10;
196
+
197
+ if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */
198
+ return REDIS_ERR;
199
+ v += p[0]-'0';
200
+
201
+ p++; plen++;
202
+ }
203
+
204
+ /* Return if not all bytes were used. */
205
+ if (plen < slen)
206
+ return REDIS_ERR;
207
+
208
+ if (negative) {
209
+ if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */
210
+ return REDIS_ERR;
211
+ if (value != NULL) *value = -v;
212
+ } else {
213
+ if (v > LLONG_MAX) /* Overflow. */
214
+ return REDIS_ERR;
215
+ if (value != NULL) *value = v;
216
+ }
217
+ return REDIS_OK;
171
218
  }
172
219
 
173
220
  static char *readLine(redisReader *r, int *_len) {
@@ -218,10 +265,17 @@ static int processLineItem(redisReader *r) {
218
265
 
219
266
  if ((p = readLine(r,&len)) != NULL) {
220
267
  if (cur->type == REDIS_REPLY_INTEGER) {
221
- if (r->fn && r->fn->createInteger)
222
- obj = r->fn->createInteger(cur,readLongLong(p));
223
- else
268
+ if (r->fn && r->fn->createInteger) {
269
+ long long v;
270
+ if (string2ll(p, len, &v) == REDIS_ERR) {
271
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
272
+ "Bad integer value");
273
+ return REDIS_ERR;
274
+ }
275
+ obj = r->fn->createInteger(cur,v);
276
+ } else {
224
277
  obj = (void*)REDIS_REPLY_INTEGER;
278
+ }
225
279
  } else {
226
280
  /* Type will be error or status. */
227
281
  if (r->fn && r->fn->createString)
@@ -248,7 +302,7 @@ static int processBulkItem(redisReader *r) {
248
302
  redisReadTask *cur = &(r->rstack[r->ridx]);
249
303
  void *obj = NULL;
250
304
  char *p, *s;
251
- long len;
305
+ long long len;
252
306
  unsigned long bytelen;
253
307
  int success = 0;
254
308
 
@@ -257,9 +311,20 @@ static int processBulkItem(redisReader *r) {
257
311
  if (s != NULL) {
258
312
  p = r->buf+r->pos;
259
313
  bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
260
- len = readLongLong(p);
261
314
 
262
- if (len < 0) {
315
+ if (string2ll(p, bytelen - 2, &len) == REDIS_ERR) {
316
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
317
+ "Bad bulk string length");
318
+ return REDIS_ERR;
319
+ }
320
+
321
+ if (len < -1 || (LLONG_MAX > SIZE_MAX && len > (long long)SIZE_MAX)) {
322
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
323
+ "Bulk string length out of range");
324
+ return REDIS_ERR;
325
+ }
326
+
327
+ if (len == -1) {
263
328
  /* The nil object can always be created. */
264
329
  if (r->fn && r->fn->createNil)
265
330
  obj = r->fn->createNil(cur);
@@ -301,8 +366,8 @@ static int processMultiBulkItem(redisReader *r) {
301
366
  redisReadTask *cur = &(r->rstack[r->ridx]);
302
367
  void *obj;
303
368
  char *p;
304
- long elements;
305
- int root = 0;
369
+ long long elements;
370
+ int root = 0, len;
306
371
 
307
372
  /* Set error for nested multi bulks with depth > 7 */
308
373
  if (r->ridx == 8) {
@@ -311,10 +376,21 @@ static int processMultiBulkItem(redisReader *r) {
311
376
  return REDIS_ERR;
312
377
  }
313
378
 
314
- if ((p = readLine(r,NULL)) != NULL) {
315
- elements = readLongLong(p);
379
+ if ((p = readLine(r,&len)) != NULL) {
380
+ if (string2ll(p, len, &elements) == REDIS_ERR) {
381
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
382
+ "Bad multi-bulk length");
383
+ return REDIS_ERR;
384
+ }
385
+
316
386
  root = (r->ridx == 0);
317
387
 
388
+ if (elements < -1 || elements > INT_MAX) {
389
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
390
+ "Multi-bulk length out of range");
391
+ return REDIS_ERR;
392
+ }
393
+
318
394
  if (elements == -1) {
319
395
  if (r->fn && r->fn->createNil)
320
396
  obj = r->fn->createNil(cur);
@@ -414,12 +490,10 @@ static int processItem(redisReader *r) {
414
490
  redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {
415
491
  redisReader *r;
416
492
 
417
- r = calloc(sizeof(redisReader),1);
493
+ r = calloc(1,sizeof(redisReader));
418
494
  if (r == NULL)
419
495
  return NULL;
420
496
 
421
- r->err = 0;
422
- r->errstr[0] = '\0';
423
497
  r->fn = fn;
424
498
  r->buf = sdsempty();
425
499
  r->maxbuf = REDIS_READER_MAX_BUF;
@@ -433,10 +507,11 @@ redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {
433
507
  }
434
508
 
435
509
  void redisReaderFree(redisReader *r) {
510
+ if (r == NULL)
511
+ return;
436
512
  if (r->reply != NULL && r->fn && r->fn->freeObject)
437
513
  r->fn->freeObject(r->reply);
438
- if (r->buf != NULL)
439
- sdsfree(r->buf);
514
+ sdsfree(r->buf);
440
515
  free(r);
441
516
  }
442
517
 
@@ -38,7 +38,7 @@
38
38
  #define REDIS_OK 0
39
39
 
40
40
  /* When an error occurs, the err flag in a context is set to hold the type of
41
- * error that occured. REDIS_ERR_IO means there was an I/O error and you
41
+ * error that occurred. REDIS_ERR_IO means there was an I/O error and you
42
42
  * should use the "errno" variable to find out what is wrong.
43
43
  * For other values, the "errstr" field will hold a description. */
44
44
  #define REDIS_ERR_IO 1 /* Error in read or write */
@@ -100,14 +100,9 @@ void redisReaderFree(redisReader *r);
100
100
  int redisReaderFeed(redisReader *r, const char *buf, size_t len);
101
101
  int redisReaderGetReply(redisReader *r, void **reply);
102
102
 
103
- /* Backwards compatibility, can be removed on big version bump. */
104
- #define redisReplyReaderCreate redisReaderCreate
105
- #define redisReplyReaderFree redisReaderFree
106
- #define redisReplyReaderFeed redisReaderFeed
107
- #define redisReplyReaderGetReply redisReaderGetReply
108
- #define redisReplyReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))
109
- #define redisReplyReaderGetObject(_r) (((redisReader*)(_r))->reply)
110
- #define redisReplyReaderGetError(_r) (((redisReader*)(_r))->errstr)
103
+ #define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))
104
+ #define redisReaderGetObject(_r) (((redisReader*)(_r))->reply)
105
+ #define redisReaderGetError(_r) (((redisReader*)(_r))->errstr)
111
106
 
112
107
  #ifdef __cplusplus
113
108
  }
data/vendor/hiredis/sds.c CHANGED
@@ -1,6 +1,8 @@
1
- /* SDS (Simple Dynamic Strings), A C dynamic strings library.
1
+ /* SDSLib 2.0 -- A C dynamic strings library
2
2
  *
3
- * Copyright (c) 2006-2014, Salvatore Sanfilippo <antirez at gmail dot com>
3
+ * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
4
+ * Copyright (c) 2015, Oran Agra
5
+ * Copyright (c) 2015, Redis Labs, Inc
4
6
  * All rights reserved.
5
7
  *
6
8
  * Redistribution and use in source and binary forms, with or without
@@ -33,8 +35,36 @@
33
35
  #include <string.h>
34
36
  #include <ctype.h>
35
37
  #include <assert.h>
36
-
37
38
  #include "sds.h"
39
+ #include "sdsalloc.h"
40
+
41
+ static inline int sdsHdrSize(char type) {
42
+ switch(type&SDS_TYPE_MASK) {
43
+ case SDS_TYPE_5:
44
+ return sizeof(struct sdshdr5);
45
+ case SDS_TYPE_8:
46
+ return sizeof(struct sdshdr8);
47
+ case SDS_TYPE_16:
48
+ return sizeof(struct sdshdr16);
49
+ case SDS_TYPE_32:
50
+ return sizeof(struct sdshdr32);
51
+ case SDS_TYPE_64:
52
+ return sizeof(struct sdshdr64);
53
+ }
54
+ return 0;
55
+ }
56
+
57
+ static inline char sdsReqType(size_t string_size) {
58
+ if (string_size < 32)
59
+ return SDS_TYPE_5;
60
+ if (string_size < 0xff)
61
+ return SDS_TYPE_8;
62
+ if (string_size < 0xffff)
63
+ return SDS_TYPE_16;
64
+ if (string_size < 0xffffffff)
65
+ return SDS_TYPE_32;
66
+ return SDS_TYPE_64;
67
+ }
38
68
 
39
69
  /* Create a new sds string with the content specified by the 'init' pointer
40
70
  * and 'initlen'.
@@ -43,26 +73,65 @@
43
73
  * The string is always null-termined (all the sds strings are, always) so
44
74
  * even if you create an sds string with:
45
75
  *
46
- * mystring = sdsnewlen("abc",3");
76
+ * mystring = sdsnewlen("abc",3);
47
77
  *
48
78
  * You can print the string with printf() as there is an implicit \0 at the
49
79
  * end of the string. However the string is binary safe and can contain
50
80
  * \0 characters in the middle, as the length is stored in the sds header. */
51
81
  sds sdsnewlen(const void *init, size_t initlen) {
52
- struct sdshdr *sh;
53
-
54
- if (init) {
55
- sh = malloc(sizeof *sh+initlen+1);
56
- } else {
57
- sh = calloc(sizeof *sh+initlen+1,1);
58
- }
82
+ void *sh;
83
+ sds s;
84
+ char type = sdsReqType(initlen);
85
+ /* Empty strings are usually created in order to append. Use type 8
86
+ * since type 5 is not good at this. */
87
+ if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;
88
+ int hdrlen = sdsHdrSize(type);
89
+ unsigned char *fp; /* flags pointer. */
90
+
91
+ sh = s_malloc(hdrlen+initlen+1);
59
92
  if (sh == NULL) return NULL;
60
- sh->len = initlen;
61
- sh->free = 0;
93
+ if (!init)
94
+ memset(sh, 0, hdrlen+initlen+1);
95
+ s = (char*)sh+hdrlen;
96
+ fp = ((unsigned char*)s)-1;
97
+ switch(type) {
98
+ case SDS_TYPE_5: {
99
+ *fp = type | (initlen << SDS_TYPE_BITS);
100
+ break;
101
+ }
102
+ case SDS_TYPE_8: {
103
+ SDS_HDR_VAR(8,s);
104
+ sh->len = initlen;
105
+ sh->alloc = initlen;
106
+ *fp = type;
107
+ break;
108
+ }
109
+ case SDS_TYPE_16: {
110
+ SDS_HDR_VAR(16,s);
111
+ sh->len = initlen;
112
+ sh->alloc = initlen;
113
+ *fp = type;
114
+ break;
115
+ }
116
+ case SDS_TYPE_32: {
117
+ SDS_HDR_VAR(32,s);
118
+ sh->len = initlen;
119
+ sh->alloc = initlen;
120
+ *fp = type;
121
+ break;
122
+ }
123
+ case SDS_TYPE_64: {
124
+ SDS_HDR_VAR(64,s);
125
+ sh->len = initlen;
126
+ sh->alloc = initlen;
127
+ *fp = type;
128
+ break;
129
+ }
130
+ }
62
131
  if (initlen && init)
63
- memcpy(sh->buf, init, initlen);
64
- sh->buf[initlen] = '\0';
65
- return (char*)sh->buf;
132
+ memcpy(s, init, initlen);
133
+ s[initlen] = '\0';
134
+ return s;
66
135
  }
67
136
 
68
137
  /* Create an empty (zero length) sds string. Even in this case the string
@@ -71,7 +140,7 @@ sds sdsempty(void) {
71
140
  return sdsnewlen("",0);
72
141
  }
73
142
 
74
- /* Create a new sds string starting from a null termined C string. */
143
+ /* Create a new sds string starting from a null terminated C string. */
75
144
  sds sdsnew(const char *init) {
76
145
  size_t initlen = (init == NULL) ? 0 : strlen(init);
77
146
  return sdsnewlen(init, initlen);
@@ -85,7 +154,7 @@ sds sdsdup(const sds s) {
85
154
  /* Free an sds string. No operation is performed if 's' is NULL. */
86
155
  void sdsfree(sds s) {
87
156
  if (s == NULL) return;
88
- free(s-sizeof(struct sdshdr));
157
+ s_free((char*)s-sdsHdrSize(s[-1]));
89
158
  }
90
159
 
91
160
  /* Set the sds string length to the length as obtained with strlen(), so
@@ -103,21 +172,17 @@ void sdsfree(sds s) {
103
172
  * the output will be "6" as the string was modified but the logical length
104
173
  * remains 6 bytes. */
105
174
  void sdsupdatelen(sds s) {
106
- struct sdshdr *sh = (void*) (s-sizeof *sh);;
107
175
  int reallen = strlen(s);
108
- sh->free += (sh->len-reallen);
109
- sh->len = reallen;
176
+ sdssetlen(s, reallen);
110
177
  }
111
178
 
112
- /* Modify an sds string on-place to make it empty (zero length).
179
+ /* Modify an sds string in-place to make it empty (zero length).
113
180
  * However all the existing buffer is not discarded but set as free space
114
181
  * so that next append operations will not require allocations up to the
115
182
  * number of bytes previously available. */
116
183
  void sdsclear(sds s) {
117
- struct sdshdr *sh = (void*) (s-sizeof *sh);;
118
- sh->free += sh->len;
119
- sh->len = 0;
120
- sh->buf[0] = '\0';
184
+ sdssetlen(s, 0);
185
+ s[0] = '\0';
121
186
  }
122
187
 
123
188
  /* Enlarge the free space at the end of the sds string so that the caller
@@ -127,23 +192,48 @@ void sdsclear(sds s) {
127
192
  * Note: this does not change the *length* of the sds string as returned
128
193
  * by sdslen(), but only the free buffer space we have. */
129
194
  sds sdsMakeRoomFor(sds s, size_t addlen) {
130
- struct sdshdr *sh, *newsh;
131
- size_t free = sdsavail(s);
195
+ void *sh, *newsh;
196
+ size_t avail = sdsavail(s);
132
197
  size_t len, newlen;
198
+ char type, oldtype = s[-1] & SDS_TYPE_MASK;
199
+ int hdrlen;
200
+
201
+ /* Return ASAP if there is enough space left. */
202
+ if (avail >= addlen) return s;
133
203
 
134
- if (free >= addlen) return s;
135
204
  len = sdslen(s);
136
- sh = (void*) (s-sizeof *sh);;
205
+ sh = (char*)s-sdsHdrSize(oldtype);
137
206
  newlen = (len+addlen);
138
207
  if (newlen < SDS_MAX_PREALLOC)
139
208
  newlen *= 2;
140
209
  else
141
210
  newlen += SDS_MAX_PREALLOC;
142
- newsh = realloc(sh, sizeof *newsh+newlen+1);
143
- if (newsh == NULL) return NULL;
144
211
 
145
- newsh->free = newlen - len;
146
- return newsh->buf;
212
+ type = sdsReqType(newlen);
213
+
214
+ /* Don't use type 5: the user is appending to the string and type 5 is
215
+ * not able to remember empty space, so sdsMakeRoomFor() must be called
216
+ * at every appending operation. */
217
+ if (type == SDS_TYPE_5) type = SDS_TYPE_8;
218
+
219
+ hdrlen = sdsHdrSize(type);
220
+ if (oldtype==type) {
221
+ newsh = s_realloc(sh, hdrlen+newlen+1);
222
+ if (newsh == NULL) return NULL;
223
+ s = (char*)newsh+hdrlen;
224
+ } else {
225
+ /* Since the header size changes, need to move the string forward,
226
+ * and can't use realloc */
227
+ newsh = s_malloc(hdrlen+newlen+1);
228
+ if (newsh == NULL) return NULL;
229
+ memcpy((char*)newsh+hdrlen, s, len+1);
230
+ s_free(sh);
231
+ s = (char*)newsh+hdrlen;
232
+ s[-1] = type;
233
+ sdssetlen(s, len);
234
+ }
235
+ sdssetalloc(s, newlen);
236
+ return s;
147
237
  }
148
238
 
149
239
  /* Reallocate the sds string so that it has no free space at the end. The
@@ -153,12 +243,29 @@ sds sdsMakeRoomFor(sds s, size_t addlen) {
153
243
  * After the call, the passed sds string is no longer valid and all the
154
244
  * references must be substituted with the new pointer returned by the call. */
155
245
  sds sdsRemoveFreeSpace(sds s) {
156
- struct sdshdr *sh;
157
-
158
- sh = (void*) (s-sizeof *sh);;
159
- sh = realloc(sh, sizeof *sh+sh->len+1);
160
- sh->free = 0;
161
- return sh->buf;
246
+ void *sh, *newsh;
247
+ char type, oldtype = s[-1] & SDS_TYPE_MASK;
248
+ int hdrlen;
249
+ size_t len = sdslen(s);
250
+ sh = (char*)s-sdsHdrSize(oldtype);
251
+
252
+ type = sdsReqType(len);
253
+ hdrlen = sdsHdrSize(type);
254
+ if (oldtype==type) {
255
+ newsh = s_realloc(sh, hdrlen+len+1);
256
+ if (newsh == NULL) return NULL;
257
+ s = (char*)newsh+hdrlen;
258
+ } else {
259
+ newsh = s_malloc(hdrlen+len+1);
260
+ if (newsh == NULL) return NULL;
261
+ memcpy((char*)newsh+hdrlen, s, len+1);
262
+ s_free(sh);
263
+ s = (char*)newsh+hdrlen;
264
+ s[-1] = type;
265
+ sdssetlen(s, len);
266
+ }
267
+ sdssetalloc(s, len);
268
+ return s;
162
269
  }
163
270
 
164
271
  /* Return the total size of the allocation of the specifed sds string,
@@ -169,9 +276,14 @@ sds sdsRemoveFreeSpace(sds s) {
169
276
  * 4) The implicit null term.
170
277
  */
171
278
  size_t sdsAllocSize(sds s) {
172
- struct sdshdr *sh = (void*) (s-sizeof *sh);;
279
+ size_t alloc = sdsalloc(s);
280
+ return sdsHdrSize(s[-1])+alloc+1;
281
+ }
173
282
 
174
- return sizeof(*sh)+sh->len+sh->free+1;
283
+ /* Return the pointer of the actual SDS allocation (normally SDS strings
284
+ * are referenced by the start of the string buffer). */
285
+ void *sdsAllocPtr(sds s) {
286
+ return (void*) (s-sdsHdrSize(s[-1]));
175
287
  }
176
288
 
177
289
  /* Increment the sds length and decrements the left free space at the
@@ -198,13 +310,44 @@ size_t sdsAllocSize(sds s) {
198
310
  * sdsIncrLen(s, nread);
199
311
  */
200
312
  void sdsIncrLen(sds s, int incr) {
201
- struct sdshdr *sh = (void*) (s-sizeof *sh);;
202
-
203
- assert(sh->free >= incr);
204
- sh->len += incr;
205
- sh->free -= incr;
206
- assert(sh->free >= 0);
207
- s[sh->len] = '\0';
313
+ unsigned char flags = s[-1];
314
+ size_t len;
315
+ switch(flags&SDS_TYPE_MASK) {
316
+ case SDS_TYPE_5: {
317
+ unsigned char *fp = ((unsigned char*)s)-1;
318
+ unsigned char oldlen = SDS_TYPE_5_LEN(flags);
319
+ assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr)));
320
+ *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS);
321
+ len = oldlen+incr;
322
+ break;
323
+ }
324
+ case SDS_TYPE_8: {
325
+ SDS_HDR_VAR(8,s);
326
+ assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
327
+ len = (sh->len += incr);
328
+ break;
329
+ }
330
+ case SDS_TYPE_16: {
331
+ SDS_HDR_VAR(16,s);
332
+ assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
333
+ len = (sh->len += incr);
334
+ break;
335
+ }
336
+ case SDS_TYPE_32: {
337
+ SDS_HDR_VAR(32,s);
338
+ assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
339
+ len = (sh->len += incr);
340
+ break;
341
+ }
342
+ case SDS_TYPE_64: {
343
+ SDS_HDR_VAR(64,s);
344
+ assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr)));
345
+ len = (sh->len += incr);
346
+ break;
347
+ }
348
+ default: len = 0; /* Just to avoid compilation warnings. */
349
+ }
350
+ s[len] = '\0';
208
351
  }
209
352
 
210
353
  /* Grow the sds to have the specified length. Bytes that were not part of
@@ -213,19 +356,15 @@ void sdsIncrLen(sds s, int incr) {
213
356
  * if the specified length is smaller than the current length, no operation
214
357
  * is performed. */
215
358
  sds sdsgrowzero(sds s, size_t len) {
216
- struct sdshdr *sh = (void*) (s-sizeof *sh);
217
- size_t totlen, curlen = sh->len;
359
+ size_t curlen = sdslen(s);
218
360
 
219
361
  if (len <= curlen) return s;
220
362
  s = sdsMakeRoomFor(s,len-curlen);
221
363
  if (s == NULL) return NULL;
222
364
 
223
365
  /* Make sure added region doesn't contain garbage */
224
- sh = (void*)(s-sizeof *sh);
225
366
  memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
226
- totlen = sh->len+sh->free;
227
- sh->len = len;
228
- sh->free = totlen-sh->len;
367
+ sdssetlen(s, len);
229
368
  return s;
230
369
  }
231
370
 
@@ -235,15 +374,12 @@ sds sdsgrowzero(sds s, size_t len) {
235
374
  * After the call, the passed sds string is no longer valid and all the
236
375
  * references must be substituted with the new pointer returned by the call. */
237
376
  sds sdscatlen(sds s, const void *t, size_t len) {
238
- struct sdshdr *sh;
239
377
  size_t curlen = sdslen(s);
240
378
 
241
379
  s = sdsMakeRoomFor(s,len);
242
380
  if (s == NULL) return NULL;
243
- sh = (void*) (s-sizeof *sh);;
244
381
  memcpy(s+curlen, t, len);
245
- sh->len = curlen+len;
246
- sh->free = sh->free-len;
382
+ sdssetlen(s, curlen+len);
247
383
  s[curlen+len] = '\0';
248
384
  return s;
249
385
  }
@@ -267,19 +403,13 @@ sds sdscatsds(sds s, const sds t) {
267
403
  /* Destructively modify the sds string 's' to hold the specified binary
268
404
  * safe string pointed by 't' of length 'len' bytes. */
269
405
  sds sdscpylen(sds s, const char *t, size_t len) {
270
- struct sdshdr *sh = (void*) (s-sizeof *sh);;
271
- size_t totlen = sh->free+sh->len;
272
-
273
- if (totlen < len) {
274
- s = sdsMakeRoomFor(s,len-sh->len);
406
+ if (sdsalloc(s) < len) {
407
+ s = sdsMakeRoomFor(s,len-sdslen(s));
275
408
  if (s == NULL) return NULL;
276
- sh = (void*) (s-sizeof *sh);;
277
- totlen = sh->free+sh->len;
278
409
  }
279
410
  memcpy(s, t, len);
280
411
  s[len] = '\0';
281
- sh->len = len;
282
- sh->free = totlen-len;
412
+ sdssetlen(s, len);
283
413
  return s;
284
414
  }
285
415
 
@@ -293,7 +423,7 @@ sds sdscpy(sds s, const char *t) {
293
423
  * conversion. 's' must point to a string with room for at least
294
424
  * SDS_LLSTR_SIZE bytes.
295
425
  *
296
- * The function returns the lenght of the null-terminated string
426
+ * The function returns the length of the null-terminated string
297
427
  * representation stored at 's'. */
298
428
  #define SDS_LLSTR_SIZE 21
299
429
  int sdsll2str(char *s, long long value) {
@@ -356,27 +486,52 @@ int sdsull2str(char *s, unsigned long long v) {
356
486
  return l;
357
487
  }
358
488
 
359
- /* Like sdscatpritf() but gets va_list instead of being variadic. */
489
+ /* Create an sds string from a long long value. It is much faster than:
490
+ *
491
+ * sdscatprintf(sdsempty(),"%lld\n", value);
492
+ */
493
+ sds sdsfromlonglong(long long value) {
494
+ char buf[SDS_LLSTR_SIZE];
495
+ int len = sdsll2str(buf,value);
496
+
497
+ return sdsnewlen(buf,len);
498
+ }
499
+
500
+ /* Like sdscatprintf() but gets va_list instead of being variadic. */
360
501
  sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
361
502
  va_list cpy;
362
- char *buf, *t;
363
- size_t buflen = 16;
503
+ char staticbuf[1024], *buf = staticbuf, *t;
504
+ size_t buflen = strlen(fmt)*2;
364
505
 
365
- while(1) {
366
- buf = malloc(buflen);
506
+ /* We try to start using a static buffer for speed.
507
+ * If not possible we revert to heap allocation. */
508
+ if (buflen > sizeof(staticbuf)) {
509
+ buf = s_malloc(buflen);
367
510
  if (buf == NULL) return NULL;
511
+ } else {
512
+ buflen = sizeof(staticbuf);
513
+ }
514
+
515
+ /* Try with buffers two times bigger every time we fail to
516
+ * fit the string in the current buffer size. */
517
+ while(1) {
368
518
  buf[buflen-2] = '\0';
369
519
  va_copy(cpy,ap);
370
520
  vsnprintf(buf, buflen, fmt, cpy);
521
+ va_end(cpy);
371
522
  if (buf[buflen-2] != '\0') {
372
- free(buf);
523
+ if (buf != staticbuf) s_free(buf);
373
524
  buflen *= 2;
525
+ buf = s_malloc(buflen);
526
+ if (buf == NULL) return NULL;
374
527
  continue;
375
528
  }
376
529
  break;
377
530
  }
531
+
532
+ /* Finally concat the obtained string to the SDS string and return it. */
378
533
  t = sdscat(s, buf);
379
- free(buf);
534
+ if (buf != staticbuf) s_free(buf);
380
535
  return t;
381
536
  }
382
537
 
@@ -389,7 +544,7 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
389
544
  * Example:
390
545
  *
391
546
  * s = sdsnew("Sum is: ");
392
- * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b);
547
+ * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b).
393
548
  *
394
549
  * Often you need to create a string from scratch with the printf-alike
395
550
  * format. When this is the need, just use sdsempty() as the target string:
@@ -419,29 +574,24 @@ sds sdscatprintf(sds s, const char *fmt, ...) {
419
574
  * %I - 64 bit signed integer (long long, int64_t)
420
575
  * %u - unsigned int
421
576
  * %U - 64 bit unsigned integer (unsigned long long, uint64_t)
422
- * %T - A size_t variable.
423
577
  * %% - Verbatim "%" character.
424
578
  */
425
579
  sds sdscatfmt(sds s, char const *fmt, ...) {
426
- struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
427
- size_t initlen = sdslen(s);
428
580
  const char *f = fmt;
429
581
  int i;
430
582
  va_list ap;
431
583
 
432
584
  va_start(ap,fmt);
433
- f = fmt; /* Next format specifier byte to process. */
434
- i = initlen; /* Position of the next byte to write to dest str. */
585
+ i = sdslen(s); /* Position of the next byte to write to dest str. */
435
586
  while(*f) {
436
587
  char next, *str;
437
- int l;
588
+ size_t l;
438
589
  long long num;
439
590
  unsigned long long unum;
440
591
 
441
592
  /* Make sure there is always space for at least 1 char. */
442
- if (sh->free == 0) {
593
+ if (sdsavail(s)==0) {
443
594
  s = sdsMakeRoomFor(s,1);
444
- sh = (void*) (s-(sizeof(struct sdshdr)));
445
595
  }
446
596
 
447
597
  switch(*f) {
@@ -453,13 +603,11 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
453
603
  case 'S':
454
604
  str = va_arg(ap,char*);
455
605
  l = (next == 's') ? strlen(str) : sdslen(str);
456
- if (sh->free < l) {
606
+ if (sdsavail(s) < l) {
457
607
  s = sdsMakeRoomFor(s,l);
458
- sh = (void*) (s-(sizeof(struct sdshdr)));
459
608
  }
460
609
  memcpy(s+i,str,l);
461
- sh->len += l;
462
- sh->free -= l;
610
+ sdsinclen(s,l);
463
611
  i += l;
464
612
  break;
465
613
  case 'i':
@@ -471,49 +619,40 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
471
619
  {
472
620
  char buf[SDS_LLSTR_SIZE];
473
621
  l = sdsll2str(buf,num);
474
- if (sh->free < l) {
622
+ if (sdsavail(s) < l) {
475
623
  s = sdsMakeRoomFor(s,l);
476
- sh = (void*) (s-(sizeof(struct sdshdr)));
477
624
  }
478
625
  memcpy(s+i,buf,l);
479
- sh->len += l;
480
- sh->free -= l;
626
+ sdsinclen(s,l);
481
627
  i += l;
482
628
  }
483
629
  break;
484
630
  case 'u':
485
631
  case 'U':
486
- case 'T':
487
632
  if (next == 'u')
488
633
  unum = va_arg(ap,unsigned int);
489
- else if(next == 'U')
490
- unum = va_arg(ap,unsigned long long);
491
634
  else
492
- unum = (unsigned long long)va_arg(ap,size_t);
635
+ unum = va_arg(ap,unsigned long long);
493
636
  {
494
637
  char buf[SDS_LLSTR_SIZE];
495
638
  l = sdsull2str(buf,unum);
496
- if (sh->free < l) {
639
+ if (sdsavail(s) < l) {
497
640
  s = sdsMakeRoomFor(s,l);
498
- sh = (void*) (s-(sizeof(struct sdshdr)));
499
641
  }
500
642
  memcpy(s+i,buf,l);
501
- sh->len += l;
502
- sh->free -= l;
643
+ sdsinclen(s,l);
503
644
  i += l;
504
645
  }
505
646
  break;
506
647
  default: /* Handle %% and generally %<unknown>. */
507
648
  s[i++] = next;
508
- sh->len += 1;
509
- sh->free -= 1;
649
+ sdsinclen(s,1);
510
650
  break;
511
651
  }
512
652
  break;
513
653
  default:
514
654
  s[i++] = *f;
515
- sh->len += 1;
516
- sh->free -= 1;
655
+ sdsinclen(s,1);
517
656
  break;
518
657
  }
519
658
  f++;
@@ -525,7 +664,6 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
525
664
  return s;
526
665
  }
527
666
 
528
-
529
667
  /* Remove the part of the string from left and from right composed just of
530
668
  * contiguous characters found in 'cset', that is a null terminted C string.
531
669
  *
@@ -535,25 +673,24 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
535
673
  * Example:
536
674
  *
537
675
  * s = sdsnew("AA...AA.a.aa.aHelloWorld :::");
538
- * s = sdstrim(s,"A. :");
676
+ * s = sdstrim(s,"Aa. :");
539
677
  * printf("%s\n", s);
540
678
  *
541
679
  * Output will be just "Hello World".
542
680
  */
543
- void sdstrim(sds s, const char *cset) {
544
- struct sdshdr *sh = (void*) (s-sizeof *sh);;
681
+ sds sdstrim(sds s, const char *cset) {
545
682
  char *start, *end, *sp, *ep;
546
683
  size_t len;
547
684
 
548
685
  sp = start = s;
549
686
  ep = end = s+sdslen(s)-1;
550
687
  while(sp <= end && strchr(cset, *sp)) sp++;
551
- while(ep > start && strchr(cset, *ep)) ep--;
688
+ while(ep > sp && strchr(cset, *ep)) ep--;
552
689
  len = (sp > ep) ? 0 : ((ep-sp)+1);
553
- if (sh->buf != sp) memmove(sh->buf, sp, len);
554
- sh->buf[len] = '\0';
555
- sh->free = sh->free+(sh->len-len);
556
- sh->len = len;
690
+ if (s != sp) memmove(s, sp, len);
691
+ s[len] = '\0';
692
+ sdssetlen(s,len);
693
+ return s;
557
694
  }
558
695
 
559
696
  /* Turn the string into a smaller (or equal) string containing only the
@@ -573,7 +710,6 @@ void sdstrim(sds s, const char *cset) {
573
710
  * sdsrange(s,1,-1); => "ello World"
574
711
  */
575
712
  void sdsrange(sds s, int start, int end) {
576
- struct sdshdr *sh = (void*) (s-sizeof *sh);;
577
713
  size_t newlen, len = sdslen(s);
578
714
 
579
715
  if (len == 0) return;
@@ -596,10 +732,9 @@ void sdsrange(sds s, int start, int end) {
596
732
  } else {
597
733
  start = 0;
598
734
  }
599
- if (start && newlen) memmove(sh->buf, sh->buf+start, newlen);
600
- sh->buf[newlen] = 0;
601
- sh->free = sh->free+(sh->len-newlen);
602
- sh->len = newlen;
735
+ if (start && newlen) memmove(s, s+start, newlen);
736
+ s[newlen] = 0;
737
+ sdssetlen(s,newlen);
603
738
  }
604
739
 
605
740
  /* Apply tolower() to every character of the sds string 's'. */
@@ -620,8 +755,8 @@ void sdstoupper(sds s) {
620
755
  *
621
756
  * Return value:
622
757
  *
623
- * 1 if s1 > s2.
624
- * -1 if s1 < s2.
758
+ * positive if s1 > s2.
759
+ * negative if s1 < s2.
625
760
  * 0 if s1 and s2 are exactly the same binary string.
626
761
  *
627
762
  * If two strings share exactly the same prefix, but one of the two has
@@ -661,7 +796,7 @@ sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count
661
796
 
662
797
  if (seplen < 1 || len < 0) return NULL;
663
798
 
664
- tokens = malloc(sizeof(sds)*slots);
799
+ tokens = s_malloc(sizeof(sds)*slots);
665
800
  if (tokens == NULL) return NULL;
666
801
 
667
802
  if (len == 0) {
@@ -674,7 +809,7 @@ sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count
674
809
  sds *newtokens;
675
810
 
676
811
  slots *= 2;
677
- newtokens = realloc(tokens,sizeof(sds)*slots);
812
+ newtokens = s_realloc(tokens,sizeof(sds)*slots);
678
813
  if (newtokens == NULL) goto cleanup;
679
814
  tokens = newtokens;
680
815
  }
@@ -698,7 +833,7 @@ cleanup:
698
833
  {
699
834
  int i;
700
835
  for (i = 0; i < elements; i++) sdsfree(tokens[i]);
701
- free(tokens);
836
+ s_free(tokens);
702
837
  *count = 0;
703
838
  return NULL;
704
839
  }
@@ -709,26 +844,7 @@ void sdsfreesplitres(sds *tokens, int count) {
709
844
  if (!tokens) return;
710
845
  while(count--)
711
846
  sdsfree(tokens[count]);
712
- free(tokens);
713
- }
714
-
715
- /* Create an sds string from a long long value. It is much faster than:
716
- *
717
- * sdscatprintf(sdsempty(),"%lld\n", value);
718
- */
719
- sds sdsfromlonglong(long long value) {
720
- char buf[32], *p;
721
- unsigned long long v;
722
-
723
- v = (value < 0) ? -value : value;
724
- p = buf+31; /* point to the last character */
725
- do {
726
- *p-- = '0'+(v%10);
727
- v /= 10;
728
- } while(v);
729
- if (value < 0) *p-- = '-';
730
- p++;
731
- return sdsnewlen(p,32-(p-buf));
847
+ s_free(tokens);
732
848
  }
733
849
 
734
850
  /* Append to the sds string "s" an escaped string representation where
@@ -902,13 +1018,13 @@ sds *sdssplitargs(const char *line, int *argc) {
902
1018
  if (*p) p++;
903
1019
  }
904
1020
  /* add the token to the vector */
905
- vector = realloc(vector,((*argc)+1)*sizeof(char*));
1021
+ vector = s_realloc(vector,((*argc)+1)*sizeof(char*));
906
1022
  vector[*argc] = current;
907
1023
  (*argc)++;
908
1024
  current = NULL;
909
1025
  } else {
910
1026
  /* Even on empty input string return something not NULL. */
911
- if (vector == NULL) vector = malloc(sizeof(void*));
1027
+ if (vector == NULL) vector = s_malloc(sizeof(void*));
912
1028
  return vector;
913
1029
  }
914
1030
  }
@@ -916,7 +1032,7 @@ sds *sdssplitargs(const char *line, int *argc) {
916
1032
  err:
917
1033
  while((*argc)--)
918
1034
  sdsfree(vector[*argc]);
919
- free(vector);
1035
+ s_free(vector);
920
1036
  if (current) sdsfree(current);
921
1037
  *argc = 0;
922
1038
  return NULL;
@@ -947,13 +1063,13 @@ sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {
947
1063
 
948
1064
  /* Join an array of C strings using the specified separator (also a C string).
949
1065
  * Returns the result as an sds string. */
950
- sds sdsjoin(char **argv, int argc, char *sep, size_t seplen) {
1066
+ sds sdsjoin(char **argv, int argc, char *sep) {
951
1067
  sds join = sdsempty();
952
1068
  int j;
953
1069
 
954
1070
  for (j = 0; j < argc; j++) {
955
1071
  join = sdscat(join, argv[j]);
956
- if (j != argc-1) join = sdscatlen(join,sep,seplen);
1072
+ if (j != argc-1) join = sdscat(join,sep);
957
1073
  }
958
1074
  return join;
959
1075
  }
@@ -970,13 +1086,23 @@ sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {
970
1086
  return join;
971
1087
  }
972
1088
 
973
- #ifdef SDS_TEST_MAIN
1089
+ /* Wrappers to the allocators used by SDS. Note that SDS will actually
1090
+ * just use the macros defined into sdsalloc.h in order to avoid to pay
1091
+ * the overhead of function calls. Here we define these wrappers only for
1092
+ * the programs SDS is linked to, if they want to touch the SDS internals
1093
+ * even if they use a different allocator. */
1094
+ void *sds_malloc(size_t size) { return s_malloc(size); }
1095
+ void *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); }
1096
+ void sds_free(void *ptr) { s_free(ptr); }
1097
+
1098
+ #if defined(SDS_TEST_MAIN)
974
1099
  #include <stdio.h>
975
1100
  #include "testhelp.h"
1101
+ #include "limits.h"
976
1102
 
977
- int main(void) {
1103
+ #define UNUSED(x) (void)(x)
1104
+ int sdsTest(void) {
978
1105
  {
979
- struct sdshdr *sh;
980
1106
  sds x = sdsnew("foo"), y;
981
1107
 
982
1108
  test_cond("Create a string and obtain the length",
@@ -1003,7 +1129,35 @@ int main(void) {
1003
1129
  sdsfree(x);
1004
1130
  x = sdscatprintf(sdsempty(),"%d",123);
1005
1131
  test_cond("sdscatprintf() seems working in the base case",
1006
- sdslen(x) == 3 && memcmp(x,"123\0",4) ==0)
1132
+ sdslen(x) == 3 && memcmp(x,"123\0",4) == 0)
1133
+
1134
+ sdsfree(x);
1135
+ x = sdsnew("--");
1136
+ x = sdscatfmt(x, "Hello %s World %I,%I--", "Hi!", LLONG_MIN,LLONG_MAX);
1137
+ test_cond("sdscatfmt() seems working in the base case",
1138
+ sdslen(x) == 60 &&
1139
+ memcmp(x,"--Hello Hi! World -9223372036854775808,"
1140
+ "9223372036854775807--",60) == 0)
1141
+ printf("[%s]\n",x);
1142
+
1143
+ sdsfree(x);
1144
+ x = sdsnew("--");
1145
+ x = sdscatfmt(x, "%u,%U--", UINT_MAX, ULLONG_MAX);
1146
+ test_cond("sdscatfmt() seems working with unsigned numbers",
1147
+ sdslen(x) == 35 &&
1148
+ memcmp(x,"--4294967295,18446744073709551615--",35) == 0)
1149
+
1150
+ sdsfree(x);
1151
+ x = sdsnew(" x ");
1152
+ sdstrim(x," x");
1153
+ test_cond("sdstrim() works when all chars match",
1154
+ sdslen(x) == 0)
1155
+
1156
+ sdsfree(x);
1157
+ x = sdsnew(" x ");
1158
+ sdstrim(x," ");
1159
+ test_cond("sdstrim() works when a single char remains",
1160
+ sdslen(x) == 1 && x[0] == 'x')
1007
1161
 
1008
1162
  sdsfree(x);
1009
1163
  x = sdsnew("xxciaoyyy");
@@ -1072,24 +1226,47 @@ int main(void) {
1072
1226
  memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0)
1073
1227
 
1074
1228
  {
1075
- int oldfree;
1229
+ unsigned int oldfree;
1230
+ char *p;
1231
+ int step = 10, j, i;
1076
1232
 
1077
1233
  sdsfree(x);
1234
+ sdsfree(y);
1078
1235
  x = sdsnew("0");
1079
- sh = (void*) (x-(sizeof(struct sdshdr)));
1080
- test_cond("sdsnew() free/len buffers", sh->len == 1 && sh->free == 0);
1081
- x = sdsMakeRoomFor(x,1);
1082
- sh = (void*) (x-(sizeof(struct sdshdr)));
1083
- test_cond("sdsMakeRoomFor()", sh->len == 1 && sh->free > 0);
1084
- oldfree = sh->free;
1085
- x[1] = '1';
1086
- sdsIncrLen(x,1);
1087
- test_cond("sdsIncrLen() -- content", x[0] == '0' && x[1] == '1');
1088
- test_cond("sdsIncrLen() -- len", sh->len == 2);
1089
- test_cond("sdsIncrLen() -- free", sh->free == oldfree-1);
1236
+ test_cond("sdsnew() free/len buffers", sdslen(x) == 1 && sdsavail(x) == 0);
1237
+
1238
+ /* Run the test a few times in order to hit the first two
1239
+ * SDS header types. */
1240
+ for (i = 0; i < 10; i++) {
1241
+ int oldlen = sdslen(x);
1242
+ x = sdsMakeRoomFor(x,step);
1243
+ int type = x[-1]&SDS_TYPE_MASK;
1244
+
1245
+ test_cond("sdsMakeRoomFor() len", sdslen(x) == oldlen);
1246
+ if (type != SDS_TYPE_5) {
1247
+ test_cond("sdsMakeRoomFor() free", sdsavail(x) >= step);
1248
+ oldfree = sdsavail(x);
1249
+ }
1250
+ p = x+oldlen;
1251
+ for (j = 0; j < step; j++) {
1252
+ p[j] = 'A'+j;
1253
+ }
1254
+ sdsIncrLen(x,step);
1255
+ }
1256
+ test_cond("sdsMakeRoomFor() content",
1257
+ memcmp("0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ",x,101) == 0);
1258
+ test_cond("sdsMakeRoomFor() final length",sdslen(x)==101);
1259
+
1260
+ sdsfree(x);
1090
1261
  }
1091
1262
  }
1092
1263
  test_report()
1093
1264
  return 0;
1094
1265
  }
1095
1266
  #endif
1267
+
1268
+ #ifdef SDS_TEST_MAIN
1269
+ int main(void) {
1270
+ return sdsTest();
1271
+ }
1272
+ #endif