hiredis 0.5.2 → 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/sds.c CHANGED
@@ -1,6 +1,8 @@
1
- /* SDSLib, A C dynamic strings library
1
+ /* SDSLib 2.0 -- A C dynamic strings library
2
2
  *
3
- * Copyright (c) 2006-2010, 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
@@ -32,164 +34,523 @@
32
34
  #include <stdlib.h>
33
35
  #include <string.h>
34
36
  #include <ctype.h>
37
+ #include <assert.h>
35
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
+ }
36
56
 
37
- #ifdef SDS_ABORT_ON_OOM
38
- static void sdsOomAbort(void) {
39
- fprintf(stderr,"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n");
40
- abort();
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;
41
67
  }
42
- #endif
43
68
 
69
+ /* Create a new sds string with the content specified by the 'init' pointer
70
+ * and 'initlen'.
71
+ * If NULL is used for 'init' the string is initialized with zero bytes.
72
+ *
73
+ * The string is always null-termined (all the sds strings are, always) so
74
+ * even if you create an sds string with:
75
+ *
76
+ * mystring = sdsnewlen("abc",3);
77
+ *
78
+ * You can print the string with printf() as there is an implicit \0 at the
79
+ * end of the string. However the string is binary safe and can contain
80
+ * \0 characters in the middle, as the length is stored in the sds header. */
44
81
  sds sdsnewlen(const void *init, size_t initlen) {
45
- struct sdshdr *sh;
46
-
47
- sh = malloc(sizeof(struct sdshdr)+initlen+1);
48
- #ifdef SDS_ABORT_ON_OOM
49
- if (sh == NULL) sdsOomAbort();
50
- #else
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);
51
92
  if (sh == NULL) return NULL;
52
- #endif
53
- sh->len = initlen;
54
- sh->free = 0;
55
- if (initlen) {
56
- if (init) memcpy(sh->buf, init, initlen);
57
- else memset(sh->buf,0,initlen);
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
+ }
58
130
  }
59
- sh->buf[initlen] = '\0';
60
- return (char*)sh->buf;
131
+ if (initlen && init)
132
+ memcpy(s, init, initlen);
133
+ s[initlen] = '\0';
134
+ return s;
61
135
  }
62
136
 
137
+ /* Create an empty (zero length) sds string. Even in this case the string
138
+ * always has an implicit null term. */
63
139
  sds sdsempty(void) {
64
140
  return sdsnewlen("",0);
65
141
  }
66
142
 
143
+ /* Create a new sds string starting from a null terminated C string. */
67
144
  sds sdsnew(const char *init) {
68
145
  size_t initlen = (init == NULL) ? 0 : strlen(init);
69
146
  return sdsnewlen(init, initlen);
70
147
  }
71
148
 
149
+ /* Duplicate an sds string. */
72
150
  sds sdsdup(const sds s) {
73
151
  return sdsnewlen(s, sdslen(s));
74
152
  }
75
153
 
154
+ /* Free an sds string. No operation is performed if 's' is NULL. */
76
155
  void sdsfree(sds s) {
77
156
  if (s == NULL) return;
78
- free(s-sizeof(struct sdshdr));
157
+ s_free((char*)s-sdsHdrSize(s[-1]));
79
158
  }
80
159
 
160
+ /* Set the sds string length to the length as obtained with strlen(), so
161
+ * considering as content only up to the first null term character.
162
+ *
163
+ * This function is useful when the sds string is hacked manually in some
164
+ * way, like in the following example:
165
+ *
166
+ * s = sdsnew("foobar");
167
+ * s[2] = '\0';
168
+ * sdsupdatelen(s);
169
+ * printf("%d\n", sdslen(s));
170
+ *
171
+ * The output will be "2", but if we comment out the call to sdsupdatelen()
172
+ * the output will be "6" as the string was modified but the logical length
173
+ * remains 6 bytes. */
81
174
  void sdsupdatelen(sds s) {
82
- struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
83
175
  int reallen = strlen(s);
84
- sh->free += (sh->len-reallen);
85
- sh->len = reallen;
176
+ sdssetlen(s, reallen);
86
177
  }
87
178
 
88
- static sds sdsMakeRoomFor(sds s, size_t addlen) {
89
- struct sdshdr *sh, *newsh;
90
- size_t free = sdsavail(s);
179
+ /* Modify an sds string in-place to make it empty (zero length).
180
+ * However all the existing buffer is not discarded but set as free space
181
+ * so that next append operations will not require allocations up to the
182
+ * number of bytes previously available. */
183
+ void sdsclear(sds s) {
184
+ sdssetlen(s, 0);
185
+ s[0] = '\0';
186
+ }
187
+
188
+ /* Enlarge the free space at the end of the sds string so that the caller
189
+ * is sure that after calling this function can overwrite up to addlen
190
+ * bytes after the end of the string, plus one more byte for nul term.
191
+ *
192
+ * Note: this does not change the *length* of the sds string as returned
193
+ * by sdslen(), but only the free buffer space we have. */
194
+ sds sdsMakeRoomFor(sds s, size_t addlen) {
195
+ void *sh, *newsh;
196
+ size_t avail = sdsavail(s);
91
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;
92
203
 
93
- if (free >= addlen) return s;
94
204
  len = sdslen(s);
95
- sh = (void*) (s-(sizeof(struct sdshdr)));
96
- newlen = (len+addlen)*2;
97
- newsh = realloc(sh, sizeof(struct sdshdr)+newlen+1);
98
- #ifdef SDS_ABORT_ON_OOM
99
- if (newsh == NULL) sdsOomAbort();
100
- #else
101
- if (newsh == NULL) return NULL;
102
- #endif
205
+ sh = (char*)s-sdsHdrSize(oldtype);
206
+ newlen = (len+addlen);
207
+ if (newlen < SDS_MAX_PREALLOC)
208
+ newlen *= 2;
209
+ else
210
+ newlen += SDS_MAX_PREALLOC;
211
+
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;
237
+ }
103
238
 
104
- newsh->free = newlen - len;
105
- return newsh->buf;
239
+ /* Reallocate the sds string so that it has no free space at the end. The
240
+ * contained string remains not altered, but next concatenation operations
241
+ * will require a reallocation.
242
+ *
243
+ * After the call, the passed sds string is no longer valid and all the
244
+ * references must be substituted with the new pointer returned by the call. */
245
+ sds sdsRemoveFreeSpace(sds s) {
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;
269
+ }
270
+
271
+ /* Return the total size of the allocation of the specifed sds string,
272
+ * including:
273
+ * 1) The sds header before the pointer.
274
+ * 2) The string.
275
+ * 3) The free buffer at the end if any.
276
+ * 4) The implicit null term.
277
+ */
278
+ size_t sdsAllocSize(sds s) {
279
+ size_t alloc = sdsalloc(s);
280
+ return sdsHdrSize(s[-1])+alloc+1;
281
+ }
282
+
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]));
287
+ }
288
+
289
+ /* Increment the sds length and decrements the left free space at the
290
+ * end of the string according to 'incr'. Also set the null term
291
+ * in the new end of the string.
292
+ *
293
+ * This function is used in order to fix the string length after the
294
+ * user calls sdsMakeRoomFor(), writes something after the end of
295
+ * the current string, and finally needs to set the new length.
296
+ *
297
+ * Note: it is possible to use a negative increment in order to
298
+ * right-trim the string.
299
+ *
300
+ * Usage example:
301
+ *
302
+ * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the
303
+ * following schema, to cat bytes coming from the kernel to the end of an
304
+ * sds string without copying into an intermediate buffer:
305
+ *
306
+ * oldlen = sdslen(s);
307
+ * s = sdsMakeRoomFor(s, BUFFER_SIZE);
308
+ * nread = read(fd, s+oldlen, BUFFER_SIZE);
309
+ * ... check for nread <= 0 and handle it ...
310
+ * sdsIncrLen(s, nread);
311
+ */
312
+ void sdsIncrLen(sds s, int incr) {
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';
106
351
  }
107
352
 
108
353
  /* Grow the sds to have the specified length. Bytes that were not part of
109
- * the original length of the sds will be set to zero. */
354
+ * the original length of the sds will be set to zero.
355
+ *
356
+ * if the specified length is smaller than the current length, no operation
357
+ * is performed. */
110
358
  sds sdsgrowzero(sds s, size_t len) {
111
- struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
112
- size_t totlen, curlen = sh->len;
359
+ size_t curlen = sdslen(s);
113
360
 
114
361
  if (len <= curlen) return s;
115
362
  s = sdsMakeRoomFor(s,len-curlen);
116
363
  if (s == NULL) return NULL;
117
364
 
118
365
  /* Make sure added region doesn't contain garbage */
119
- sh = (void*)(s-(sizeof(struct sdshdr)));
120
366
  memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
121
- totlen = sh->len+sh->free;
122
- sh->len = len;
123
- sh->free = totlen-sh->len;
367
+ sdssetlen(s, len);
124
368
  return s;
125
369
  }
126
370
 
371
+ /* Append the specified binary-safe string pointed by 't' of 'len' bytes to the
372
+ * end of the specified sds string 's'.
373
+ *
374
+ * After the call, the passed sds string is no longer valid and all the
375
+ * references must be substituted with the new pointer returned by the call. */
127
376
  sds sdscatlen(sds s, const void *t, size_t len) {
128
- struct sdshdr *sh;
129
377
  size_t curlen = sdslen(s);
130
378
 
131
379
  s = sdsMakeRoomFor(s,len);
132
380
  if (s == NULL) return NULL;
133
- sh = (void*) (s-(sizeof(struct sdshdr)));
134
381
  memcpy(s+curlen, t, len);
135
- sh->len = curlen+len;
136
- sh->free = sh->free-len;
382
+ sdssetlen(s, curlen+len);
137
383
  s[curlen+len] = '\0';
138
384
  return s;
139
385
  }
140
386
 
387
+ /* Append the specified null termianted C string to the sds string 's'.
388
+ *
389
+ * After the call, the passed sds string is no longer valid and all the
390
+ * references must be substituted with the new pointer returned by the call. */
141
391
  sds sdscat(sds s, const char *t) {
142
392
  return sdscatlen(s, t, strlen(t));
143
393
  }
144
394
 
145
- sds sdscpylen(sds s, char *t, size_t len) {
146
- struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
147
- size_t totlen = sh->free+sh->len;
395
+ /* Append the specified sds 't' to the existing sds 's'.
396
+ *
397
+ * After the call, the modified sds string is no longer valid and all the
398
+ * references must be substituted with the new pointer returned by the call. */
399
+ sds sdscatsds(sds s, const sds t) {
400
+ return sdscatlen(s, t, sdslen(t));
401
+ }
148
402
 
149
- if (totlen < len) {
150
- s = sdsMakeRoomFor(s,len-sh->len);
403
+ /* Destructively modify the sds string 's' to hold the specified binary
404
+ * safe string pointed by 't' of length 'len' bytes. */
405
+ sds sdscpylen(sds s, const char *t, size_t len) {
406
+ if (sdsalloc(s) < len) {
407
+ s = sdsMakeRoomFor(s,len-sdslen(s));
151
408
  if (s == NULL) return NULL;
152
- sh = (void*) (s-(sizeof(struct sdshdr)));
153
- totlen = sh->free+sh->len;
154
409
  }
155
410
  memcpy(s, t, len);
156
411
  s[len] = '\0';
157
- sh->len = len;
158
- sh->free = totlen-len;
412
+ sdssetlen(s, len);
159
413
  return s;
160
414
  }
161
415
 
162
- sds sdscpy(sds s, char *t) {
416
+ /* Like sdscpylen() but 't' must be a null-termined string so that the length
417
+ * of the string is obtained with strlen(). */
418
+ sds sdscpy(sds s, const char *t) {
163
419
  return sdscpylen(s, t, strlen(t));
164
420
  }
165
421
 
422
+ /* Helper for sdscatlonglong() doing the actual number -> string
423
+ * conversion. 's' must point to a string with room for at least
424
+ * SDS_LLSTR_SIZE bytes.
425
+ *
426
+ * The function returns the length of the null-terminated string
427
+ * representation stored at 's'. */
428
+ #define SDS_LLSTR_SIZE 21
429
+ int sdsll2str(char *s, long long value) {
430
+ char *p, aux;
431
+ unsigned long long v;
432
+ size_t l;
433
+
434
+ /* Generate the string representation, this method produces
435
+ * an reversed string. */
436
+ v = (value < 0) ? -value : value;
437
+ p = s;
438
+ do {
439
+ *p++ = '0'+(v%10);
440
+ v /= 10;
441
+ } while(v);
442
+ if (value < 0) *p++ = '-';
443
+
444
+ /* Compute length and add null term. */
445
+ l = p-s;
446
+ *p = '\0';
447
+
448
+ /* Reverse the string. */
449
+ p--;
450
+ while(s < p) {
451
+ aux = *s;
452
+ *s = *p;
453
+ *p = aux;
454
+ s++;
455
+ p--;
456
+ }
457
+ return l;
458
+ }
459
+
460
+ /* Identical sdsll2str(), but for unsigned long long type. */
461
+ int sdsull2str(char *s, unsigned long long v) {
462
+ char *p, aux;
463
+ size_t l;
464
+
465
+ /* Generate the string representation, this method produces
466
+ * an reversed string. */
467
+ p = s;
468
+ do {
469
+ *p++ = '0'+(v%10);
470
+ v /= 10;
471
+ } while(v);
472
+
473
+ /* Compute length and add null term. */
474
+ l = p-s;
475
+ *p = '\0';
476
+
477
+ /* Reverse the string. */
478
+ p--;
479
+ while(s < p) {
480
+ aux = *s;
481
+ *s = *p;
482
+ *p = aux;
483
+ s++;
484
+ p--;
485
+ }
486
+ return l;
487
+ }
488
+
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. */
166
501
  sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
167
502
  va_list cpy;
168
- char *buf, *t;
169
- size_t buflen = 16;
503
+ char staticbuf[1024], *buf = staticbuf, *t;
504
+ size_t buflen = strlen(fmt)*2;
170
505
 
171
- while(1) {
172
- buf = malloc(buflen);
173
- #ifdef SDS_ABORT_ON_OOM
174
- if (buf == NULL) sdsOomAbort();
175
- #else
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);
176
510
  if (buf == NULL) return NULL;
177
- #endif
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) {
178
518
  buf[buflen-2] = '\0';
179
519
  va_copy(cpy,ap);
180
520
  vsnprintf(buf, buflen, fmt, cpy);
521
+ va_end(cpy);
181
522
  if (buf[buflen-2] != '\0') {
182
- free(buf);
523
+ if (buf != staticbuf) s_free(buf);
183
524
  buflen *= 2;
525
+ buf = s_malloc(buflen);
526
+ if (buf == NULL) return NULL;
184
527
  continue;
185
528
  }
186
529
  break;
187
530
  }
531
+
532
+ /* Finally concat the obtained string to the SDS string and return it. */
188
533
  t = sdscat(s, buf);
189
- free(buf);
534
+ if (buf != staticbuf) s_free(buf);
190
535
  return t;
191
536
  }
192
537
 
538
+ /* Append to the sds string 's' a string obtained using printf-alike format
539
+ * specifier.
540
+ *
541
+ * After the call, the modified sds string is no longer valid and all the
542
+ * references must be substituted with the new pointer returned by the call.
543
+ *
544
+ * Example:
545
+ *
546
+ * s = sdsnew("Sum is: ");
547
+ * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b).
548
+ *
549
+ * Often you need to create a string from scratch with the printf-alike
550
+ * format. When this is the need, just use sdsempty() as the target string:
551
+ *
552
+ * s = sdscatprintf(sdsempty(), "... your format ...", args);
553
+ */
193
554
  sds sdscatprintf(sds s, const char *fmt, ...) {
194
555
  va_list ap;
195
556
  char *t;
@@ -199,28 +560,159 @@ sds sdscatprintf(sds s, const char *fmt, ...) {
199
560
  return t;
200
561
  }
201
562
 
563
+ /* This function is similar to sdscatprintf, but much faster as it does
564
+ * not rely on sprintf() family functions implemented by the libc that
565
+ * are often very slow. Moreover directly handling the sds string as
566
+ * new data is concatenated provides a performance improvement.
567
+ *
568
+ * However this function only handles an incompatible subset of printf-alike
569
+ * format specifiers:
570
+ *
571
+ * %s - C String
572
+ * %S - SDS string
573
+ * %i - signed int
574
+ * %I - 64 bit signed integer (long long, int64_t)
575
+ * %u - unsigned int
576
+ * %U - 64 bit unsigned integer (unsigned long long, uint64_t)
577
+ * %% - Verbatim "%" character.
578
+ */
579
+ sds sdscatfmt(sds s, char const *fmt, ...) {
580
+ const char *f = fmt;
581
+ int i;
582
+ va_list ap;
583
+
584
+ va_start(ap,fmt);
585
+ i = sdslen(s); /* Position of the next byte to write to dest str. */
586
+ while(*f) {
587
+ char next, *str;
588
+ size_t l;
589
+ long long num;
590
+ unsigned long long unum;
591
+
592
+ /* Make sure there is always space for at least 1 char. */
593
+ if (sdsavail(s)==0) {
594
+ s = sdsMakeRoomFor(s,1);
595
+ }
596
+
597
+ switch(*f) {
598
+ case '%':
599
+ next = *(f+1);
600
+ f++;
601
+ switch(next) {
602
+ case 's':
603
+ case 'S':
604
+ str = va_arg(ap,char*);
605
+ l = (next == 's') ? strlen(str) : sdslen(str);
606
+ if (sdsavail(s) < l) {
607
+ s = sdsMakeRoomFor(s,l);
608
+ }
609
+ memcpy(s+i,str,l);
610
+ sdsinclen(s,l);
611
+ i += l;
612
+ break;
613
+ case 'i':
614
+ case 'I':
615
+ if (next == 'i')
616
+ num = va_arg(ap,int);
617
+ else
618
+ num = va_arg(ap,long long);
619
+ {
620
+ char buf[SDS_LLSTR_SIZE];
621
+ l = sdsll2str(buf,num);
622
+ if (sdsavail(s) < l) {
623
+ s = sdsMakeRoomFor(s,l);
624
+ }
625
+ memcpy(s+i,buf,l);
626
+ sdsinclen(s,l);
627
+ i += l;
628
+ }
629
+ break;
630
+ case 'u':
631
+ case 'U':
632
+ if (next == 'u')
633
+ unum = va_arg(ap,unsigned int);
634
+ else
635
+ unum = va_arg(ap,unsigned long long);
636
+ {
637
+ char buf[SDS_LLSTR_SIZE];
638
+ l = sdsull2str(buf,unum);
639
+ if (sdsavail(s) < l) {
640
+ s = sdsMakeRoomFor(s,l);
641
+ }
642
+ memcpy(s+i,buf,l);
643
+ sdsinclen(s,l);
644
+ i += l;
645
+ }
646
+ break;
647
+ default: /* Handle %% and generally %<unknown>. */
648
+ s[i++] = next;
649
+ sdsinclen(s,1);
650
+ break;
651
+ }
652
+ break;
653
+ default:
654
+ s[i++] = *f;
655
+ sdsinclen(s,1);
656
+ break;
657
+ }
658
+ f++;
659
+ }
660
+ va_end(ap);
661
+
662
+ /* Add null-term */
663
+ s[i] = '\0';
664
+ return s;
665
+ }
666
+
667
+ /* Remove the part of the string from left and from right composed just of
668
+ * contiguous characters found in 'cset', that is a null terminted C string.
669
+ *
670
+ * After the call, the modified sds string is no longer valid and all the
671
+ * references must be substituted with the new pointer returned by the call.
672
+ *
673
+ * Example:
674
+ *
675
+ * s = sdsnew("AA...AA.a.aa.aHelloWorld :::");
676
+ * s = sdstrim(s,"Aa. :");
677
+ * printf("%s\n", s);
678
+ *
679
+ * Output will be just "Hello World".
680
+ */
202
681
  sds sdstrim(sds s, const char *cset) {
203
- struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
204
682
  char *start, *end, *sp, *ep;
205
683
  size_t len;
206
684
 
207
685
  sp = start = s;
208
686
  ep = end = s+sdslen(s)-1;
209
687
  while(sp <= end && strchr(cset, *sp)) sp++;
210
- while(ep > start && strchr(cset, *ep)) ep--;
688
+ while(ep > sp && strchr(cset, *ep)) ep--;
211
689
  len = (sp > ep) ? 0 : ((ep-sp)+1);
212
- if (sh->buf != sp) memmove(sh->buf, sp, len);
213
- sh->buf[len] = '\0';
214
- sh->free = sh->free+(sh->len-len);
215
- sh->len = len;
690
+ if (s != sp) memmove(s, sp, len);
691
+ s[len] = '\0';
692
+ sdssetlen(s,len);
216
693
  return s;
217
694
  }
218
695
 
219
- sds sdsrange(sds s, int start, int end) {
220
- struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
696
+ /* Turn the string into a smaller (or equal) string containing only the
697
+ * substring specified by the 'start' and 'end' indexes.
698
+ *
699
+ * start and end can be negative, where -1 means the last character of the
700
+ * string, -2 the penultimate character, and so forth.
701
+ *
702
+ * The interval is inclusive, so the start and end characters will be part
703
+ * of the resulting string.
704
+ *
705
+ * The string is modified in-place.
706
+ *
707
+ * Example:
708
+ *
709
+ * s = sdsnew("Hello World");
710
+ * sdsrange(s,1,-1); => "ello World"
711
+ */
712
+ void sdsrange(sds s, int start, int end) {
221
713
  size_t newlen, len = sdslen(s);
222
714
 
223
- if (len == 0) return s;
715
+ if (len == 0) return;
224
716
  if (start < 0) {
225
717
  start = len+start;
226
718
  if (start < 0) start = 0;
@@ -240,26 +732,37 @@ sds sdsrange(sds s, int start, int end) {
240
732
  } else {
241
733
  start = 0;
242
734
  }
243
- if (start && newlen) memmove(sh->buf, sh->buf+start, newlen);
244
- sh->buf[newlen] = 0;
245
- sh->free = sh->free+(sh->len-newlen);
246
- sh->len = newlen;
247
- return s;
735
+ if (start && newlen) memmove(s, s+start, newlen);
736
+ s[newlen] = 0;
737
+ sdssetlen(s,newlen);
248
738
  }
249
739
 
740
+ /* Apply tolower() to every character of the sds string 's'. */
250
741
  void sdstolower(sds s) {
251
742
  int len = sdslen(s), j;
252
743
 
253
744
  for (j = 0; j < len; j++) s[j] = tolower(s[j]);
254
745
  }
255
746
 
747
+ /* Apply toupper() to every character of the sds string 's'. */
256
748
  void sdstoupper(sds s) {
257
749
  int len = sdslen(s), j;
258
750
 
259
751
  for (j = 0; j < len; j++) s[j] = toupper(s[j]);
260
752
  }
261
753
 
262
- int sdscmp(sds s1, sds s2) {
754
+ /* Compare two sds strings s1 and s2 with memcmp().
755
+ *
756
+ * Return value:
757
+ *
758
+ * positive if s1 > s2.
759
+ * negative if s1 < s2.
760
+ * 0 if s1 and s2 are exactly the same binary string.
761
+ *
762
+ * If two strings share exactly the same prefix, but one of the two has
763
+ * additional characters, the longer string is considered to be greater than
764
+ * the smaller one. */
765
+ int sdscmp(const sds s1, const sds s2) {
263
766
  size_t l1, l2, minlen;
264
767
  int cmp;
265
768
 
@@ -287,14 +790,15 @@ int sdscmp(sds s1, sds s2) {
287
790
  * requires length arguments. sdssplit() is just the
288
791
  * same function but for zero-terminated strings.
289
792
  */
290
- sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
793
+ sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) {
291
794
  int elements = 0, slots = 5, start = 0, j;
795
+ sds *tokens;
796
+
797
+ if (seplen < 1 || len < 0) return NULL;
798
+
799
+ tokens = s_malloc(sizeof(sds)*slots);
800
+ if (tokens == NULL) return NULL;
292
801
 
293
- sds *tokens = malloc(sizeof(sds)*slots);
294
- #ifdef SDS_ABORT_ON_OOM
295
- if (tokens == NULL) sdsOomAbort();
296
- #endif
297
- if (seplen < 1 || len < 0 || tokens == NULL) return NULL;
298
802
  if (len == 0) {
299
803
  *count = 0;
300
804
  return tokens;
@@ -305,26 +809,14 @@ sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
305
809
  sds *newtokens;
306
810
 
307
811
  slots *= 2;
308
- newtokens = realloc(tokens,sizeof(sds)*slots);
309
- if (newtokens == NULL) {
310
- #ifdef SDS_ABORT_ON_OOM
311
- sdsOomAbort();
312
- #else
313
- goto cleanup;
314
- #endif
315
- }
812
+ newtokens = s_realloc(tokens,sizeof(sds)*slots);
813
+ if (newtokens == NULL) goto cleanup;
316
814
  tokens = newtokens;
317
815
  }
318
816
  /* search the separator */
319
817
  if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
320
818
  tokens[elements] = sdsnewlen(s+start,j-start);
321
- if (tokens[elements] == NULL) {
322
- #ifdef SDS_ABORT_ON_OOM
323
- sdsOomAbort();
324
- #else
325
- goto cleanup;
326
- #endif
327
- }
819
+ if (tokens[elements] == NULL) goto cleanup;
328
820
  elements++;
329
821
  start = j+seplen;
330
822
  j = j+seplen-1; /* skip the separator */
@@ -332,54 +824,37 @@ sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
332
824
  }
333
825
  /* Add the final element. We are sure there is room in the tokens array. */
334
826
  tokens[elements] = sdsnewlen(s+start,len-start);
335
- if (tokens[elements] == NULL) {
336
- #ifdef SDS_ABORT_ON_OOM
337
- sdsOomAbort();
338
- #else
339
- goto cleanup;
340
- #endif
341
- }
827
+ if (tokens[elements] == NULL) goto cleanup;
342
828
  elements++;
343
829
  *count = elements;
344
830
  return tokens;
345
831
 
346
- #ifndef SDS_ABORT_ON_OOM
347
832
  cleanup:
348
833
  {
349
834
  int i;
350
835
  for (i = 0; i < elements; i++) sdsfree(tokens[i]);
351
- free(tokens);
836
+ s_free(tokens);
837
+ *count = 0;
352
838
  return NULL;
353
839
  }
354
- #endif
355
840
  }
356
841
 
842
+ /* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */
357
843
  void sdsfreesplitres(sds *tokens, int count) {
358
844
  if (!tokens) return;
359
845
  while(count--)
360
846
  sdsfree(tokens[count]);
361
- free(tokens);
362
- }
363
-
364
- sds sdsfromlonglong(long long value) {
365
- char buf[32], *p;
366
- unsigned long long v;
367
-
368
- v = (value < 0) ? -value : value;
369
- p = buf+31; /* point to the last character */
370
- do {
371
- *p-- = '0'+(v%10);
372
- v /= 10;
373
- } while(v);
374
- if (value < 0) *p-- = '-';
375
- p++;
376
- return sdsnewlen(p,32-(p-buf));
847
+ s_free(tokens);
377
848
  }
378
849
 
379
- sds sdscatrepr(sds s, char *p, size_t len) {
850
+ /* Append to the sds string "s" an escaped string representation where
851
+ * all the non-printable characters (tested with isprint()) are turned into
852
+ * escapes in the form "\n\r\a...." or "\x<hex-number>".
853
+ *
854
+ * After the call, the modified sds string is no longer valid and all the
855
+ * references must be substituted with the new pointer returned by the call. */
856
+ sds sdscatrepr(sds s, const char *p, size_t len) {
380
857
  s = sdscatlen(s,"\"",1);
381
- if (s == NULL) return NULL;
382
-
383
858
  while(len--) {
384
859
  switch(*p) {
385
860
  case '\\':
@@ -399,27 +874,64 @@ sds sdscatrepr(sds s, char *p, size_t len) {
399
874
  break;
400
875
  }
401
876
  p++;
402
- if (s == NULL) return NULL;
403
877
  }
404
878
  return sdscatlen(s,"\"",1);
405
879
  }
406
880
 
881
+ /* Helper function for sdssplitargs() that returns non zero if 'c'
882
+ * is a valid hex digit. */
883
+ int is_hex_digit(char c) {
884
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
885
+ (c >= 'A' && c <= 'F');
886
+ }
887
+
888
+ /* Helper function for sdssplitargs() that converts a hex digit into an
889
+ * integer from 0 to 15 */
890
+ int hex_digit_to_int(char c) {
891
+ switch(c) {
892
+ case '0': return 0;
893
+ case '1': return 1;
894
+ case '2': return 2;
895
+ case '3': return 3;
896
+ case '4': return 4;
897
+ case '5': return 5;
898
+ case '6': return 6;
899
+ case '7': return 7;
900
+ case '8': return 8;
901
+ case '9': return 9;
902
+ case 'a': case 'A': return 10;
903
+ case 'b': case 'B': return 11;
904
+ case 'c': case 'C': return 12;
905
+ case 'd': case 'D': return 13;
906
+ case 'e': case 'E': return 14;
907
+ case 'f': case 'F': return 15;
908
+ default: return 0;
909
+ }
910
+ }
911
+
407
912
  /* Split a line into arguments, where every argument can be in the
408
913
  * following programming-language REPL-alike form:
409
914
  *
410
915
  * foo bar "newline are supported\n" and "\xff\x00otherstuff"
411
916
  *
412
917
  * The number of arguments is stored into *argc, and an array
413
- * of sds is returned. The caller should sdsfree() all the returned
414
- * strings and finally free() the array itself.
918
+ * of sds is returned.
919
+ *
920
+ * The caller should free the resulting array of sds strings with
921
+ * sdsfreesplitres().
415
922
  *
416
923
  * Note that sdscatrepr() is able to convert back a string into
417
924
  * a quoted string in the same format sdssplitargs() is able to parse.
925
+ *
926
+ * The function returns the allocated tokens on success, even when the
927
+ * input string is empty, or NULL if the input contains unbalanced
928
+ * quotes or closed quotes followed by non space characters
929
+ * as in: "foo"bar or "foo'
418
930
  */
419
- sds *sdssplitargs(char *line, int *argc) {
420
- char *p = line;
931
+ sds *sdssplitargs(const char *line, int *argc) {
932
+ const char *p = line;
421
933
  char *current = NULL;
422
- char **vector = NULL, **_vector = NULL;
934
+ char **vector = NULL;
423
935
 
424
936
  *argc = 0;
425
937
  while(1) {
@@ -427,17 +939,24 @@ sds *sdssplitargs(char *line, int *argc) {
427
939
  while(*p && isspace(*p)) p++;
428
940
  if (*p) {
429
941
  /* get a token */
430
- int inq=0; /* set to 1 if we are in "quotes" */
942
+ int inq=0; /* set to 1 if we are in "quotes" */
943
+ int insq=0; /* set to 1 if we are in 'single quotes' */
431
944
  int done=0;
432
945
 
433
- if (current == NULL) {
434
- current = sdsempty();
435
- if (current == NULL) goto err;
436
- }
437
-
946
+ if (current == NULL) current = sdsempty();
438
947
  while(!done) {
439
948
  if (inq) {
440
- if (*p == '\\' && *(p+1)) {
949
+ if (*p == '\\' && *(p+1) == 'x' &&
950
+ is_hex_digit(*(p+2)) &&
951
+ is_hex_digit(*(p+3)))
952
+ {
953
+ unsigned char byte;
954
+
955
+ byte = (hex_digit_to_int(*(p+2))*16)+
956
+ hex_digit_to_int(*(p+3));
957
+ current = sdscatlen(current,(char*)&byte,1);
958
+ p += 3;
959
+ } else if (*p == '\\' && *(p+1)) {
441
960
  char c;
442
961
 
443
962
  p++;
@@ -451,7 +970,23 @@ sds *sdssplitargs(char *line, int *argc) {
451
970
  }
452
971
  current = sdscatlen(current,&c,1);
453
972
  } else if (*p == '"') {
454
- /* closing quote must be followed by a space */
973
+ /* closing quote must be followed by a space or
974
+ * nothing at all. */
975
+ if (*(p+1) && !isspace(*(p+1))) goto err;
976
+ done=1;
977
+ } else if (!*p) {
978
+ /* unterminated quotes */
979
+ goto err;
980
+ } else {
981
+ current = sdscatlen(current,p,1);
982
+ }
983
+ } else if (insq) {
984
+ if (*p == '\\' && *(p+1) == '\'') {
985
+ p++;
986
+ current = sdscatlen(current,"'",1);
987
+ } else if (*p == '\'') {
988
+ /* closing quote must be followed by a space or
989
+ * nothing at all. */
455
990
  if (*(p+1) && !isspace(*(p+1))) goto err;
456
991
  done=1;
457
992
  } else if (!*p) {
@@ -472,23 +1007,24 @@ sds *sdssplitargs(char *line, int *argc) {
472
1007
  case '"':
473
1008
  inq=1;
474
1009
  break;
1010
+ case '\'':
1011
+ insq=1;
1012
+ break;
475
1013
  default:
476
1014
  current = sdscatlen(current,p,1);
477
1015
  break;
478
1016
  }
479
1017
  }
480
1018
  if (*p) p++;
481
- if (current == NULL) goto err;
482
1019
  }
483
1020
  /* add the token to the vector */
484
- _vector = realloc(vector,((*argc)+1)*sizeof(char*));
485
- if (_vector == NULL) goto err;
486
-
487
- vector = _vector;
1021
+ vector = s_realloc(vector,((*argc)+1)*sizeof(char*));
488
1022
  vector[*argc] = current;
489
1023
  (*argc)++;
490
1024
  current = NULL;
491
1025
  } else {
1026
+ /* Even on empty input string return something not NULL. */
1027
+ if (vector == NULL) vector = s_malloc(sizeof(void*));
492
1028
  return vector;
493
1029
  }
494
1030
  }
@@ -496,29 +1032,76 @@ sds *sdssplitargs(char *line, int *argc) {
496
1032
  err:
497
1033
  while((*argc)--)
498
1034
  sdsfree(vector[*argc]);
499
- if (vector != NULL) free(vector);
500
- if (current != NULL) sdsfree(current);
1035
+ s_free(vector);
1036
+ if (current) sdsfree(current);
1037
+ *argc = 0;
501
1038
  return NULL;
502
1039
  }
503
1040
 
504
- #ifdef SDS_TEST_MAIN
505
- #include <stdio.h>
1041
+ /* Modify the string substituting all the occurrences of the set of
1042
+ * characters specified in the 'from' string to the corresponding character
1043
+ * in the 'to' array.
1044
+ *
1045
+ * For instance: sdsmapchars(mystring, "ho", "01", 2)
1046
+ * will have the effect of turning the string "hello" into "0ell1".
1047
+ *
1048
+ * The function returns the sds string pointer, that is always the same
1049
+ * as the input pointer since no resize is needed. */
1050
+ sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {
1051
+ size_t j, i, l = sdslen(s);
1052
+
1053
+ for (j = 0; j < l; j++) {
1054
+ for (i = 0; i < setlen; i++) {
1055
+ if (s[j] == from[i]) {
1056
+ s[j] = to[i];
1057
+ break;
1058
+ }
1059
+ }
1060
+ }
1061
+ return s;
1062
+ }
506
1063
 
507
- int __failed_tests = 0;
508
- int __test_num = 0;
509
- #define test_cond(descr,_c) do { \
510
- __test_num++; printf("%d - %s: ", __test_num, descr); \
511
- if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \
512
- } while(0);
513
- #define test_report() do { \
514
- printf("%d tests, %d passed, %d failed\n", __test_num, \
515
- __test_num-__failed_tests, __failed_tests); \
516
- if (__failed_tests) { \
517
- printf("=== WARNING === We have failed tests here...\n"); \
518
- } \
519
- } while(0);
1064
+ /* Join an array of C strings using the specified separator (also a C string).
1065
+ * Returns the result as an sds string. */
1066
+ sds sdsjoin(char **argv, int argc, char *sep) {
1067
+ sds join = sdsempty();
1068
+ int j;
520
1069
 
521
- int main(void) {
1070
+ for (j = 0; j < argc; j++) {
1071
+ join = sdscat(join, argv[j]);
1072
+ if (j != argc-1) join = sdscat(join,sep);
1073
+ }
1074
+ return join;
1075
+ }
1076
+
1077
+ /* Like sdsjoin, but joins an array of SDS strings. */
1078
+ sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {
1079
+ sds join = sdsempty();
1080
+ int j;
1081
+
1082
+ for (j = 0; j < argc; j++) {
1083
+ join = sdscatsds(join, argv[j]);
1084
+ if (j != argc-1) join = sdscatlen(join,sep,seplen);
1085
+ }
1086
+ return join;
1087
+ }
1088
+
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)
1099
+ #include <stdio.h>
1100
+ #include "testhelp.h"
1101
+ #include "limits.h"
1102
+
1103
+ #define UNUSED(x) (void)(x)
1104
+ int sdsTest(void) {
522
1105
  {
523
1106
  sds x = sdsnew("foo"), y;
524
1107
 
@@ -546,39 +1129,74 @@ int main(void) {
546
1129
  sdsfree(x);
547
1130
  x = sdscatprintf(sdsempty(),"%d",123);
548
1131
  test_cond("sdscatprintf() seems working in the base case",
549
- 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')
550
1161
 
551
1162
  sdsfree(x);
552
- x = sdstrim(sdsnew("xxciaoyyy"),"xy");
1163
+ x = sdsnew("xxciaoyyy");
1164
+ sdstrim(x,"xy");
553
1165
  test_cond("sdstrim() correctly trims characters",
554
1166
  sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)
555
1167
 
556
- y = sdsrange(sdsdup(x),1,1);
1168
+ y = sdsdup(x);
1169
+ sdsrange(y,1,1);
557
1170
  test_cond("sdsrange(...,1,1)",
558
1171
  sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)
559
1172
 
560
1173
  sdsfree(y);
561
- y = sdsrange(sdsdup(x),1,-1);
1174
+ y = sdsdup(x);
1175
+ sdsrange(y,1,-1);
562
1176
  test_cond("sdsrange(...,1,-1)",
563
1177
  sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
564
1178
 
565
1179
  sdsfree(y);
566
- y = sdsrange(sdsdup(x),-2,-1);
1180
+ y = sdsdup(x);
1181
+ sdsrange(y,-2,-1);
567
1182
  test_cond("sdsrange(...,-2,-1)",
568
1183
  sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)
569
1184
 
570
1185
  sdsfree(y);
571
- y = sdsrange(sdsdup(x),2,1);
1186
+ y = sdsdup(x);
1187
+ sdsrange(y,2,1);
572
1188
  test_cond("sdsrange(...,2,1)",
573
1189
  sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
574
1190
 
575
1191
  sdsfree(y);
576
- y = sdsrange(sdsdup(x),1,100);
1192
+ y = sdsdup(x);
1193
+ sdsrange(y,1,100);
577
1194
  test_cond("sdsrange(...,1,100)",
578
1195
  sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
579
1196
 
580
1197
  sdsfree(y);
581
- y = sdsrange(sdsdup(x),100,100);
1198
+ y = sdsdup(x);
1199
+ sdsrange(y,100,100);
582
1200
  test_cond("sdsrange(...,100,100)",
583
1201
  sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
584
1202
 
@@ -599,7 +1217,56 @@ int main(void) {
599
1217
  x = sdsnew("aar");
600
1218
  y = sdsnew("bar");
601
1219
  test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
1220
+
1221
+ sdsfree(y);
1222
+ sdsfree(x);
1223
+ x = sdsnewlen("\a\n\0foo\r",7);
1224
+ y = sdscatrepr(sdsempty(),x,sdslen(x));
1225
+ test_cond("sdscatrepr(...data...)",
1226
+ memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0)
1227
+
1228
+ {
1229
+ unsigned int oldfree;
1230
+ char *p;
1231
+ int step = 10, j, i;
1232
+
1233
+ sdsfree(x);
1234
+ sdsfree(y);
1235
+ x = sdsnew("0");
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);
1261
+ }
602
1262
  }
603
1263
  test_report()
1264
+ return 0;
1265
+ }
1266
+ #endif
1267
+
1268
+ #ifdef SDS_TEST_MAIN
1269
+ int main(void) {
1270
+ return sdsTest();
604
1271
  }
605
1272
  #endif