hiredis 0.6.1 → 0.6.3

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.
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