]> git.saurik.com Git - redis.git/blob - src/sds.c
sds.c no longe pre-allocate more than 1MB of free space ahead. This fixes issue ...
[redis.git] / src / sds.c
1 /* SDSLib, A C dynamic strings library
2 *
3 * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of Redis nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without
16 * specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 *
30 * History:
31 *
32 * - 22 March 2011: History section created on top of sds.c
33 * - 22 March 2011: Fixed a problem with "\xab" escapes convertion in
34 * function sdssplitargs().
35 */
36
37 #define SDS_ABORT_ON_OOM
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <assert.h>
44 #include "sds.h"
45 #include "zmalloc.h"
46
47 static void sdsOomAbort(void) {
48 fprintf(stderr,"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n");
49 abort();
50 }
51
52 sds sdsnewlen(const void *init, size_t initlen) {
53 struct sdshdr *sh;
54
55 if (init) {
56 sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
57 } else {
58 sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
59 }
60 #ifdef SDS_ABORT_ON_OOM
61 if (sh == NULL) sdsOomAbort();
62 #else
63 if (sh == NULL) return NULL;
64 #endif
65 sh->len = initlen;
66 sh->free = 0;
67 if (initlen && init)
68 memcpy(sh->buf, init, initlen);
69 sh->buf[initlen] = '\0';
70 return (char*)sh->buf;
71 }
72
73 sds sdsempty(void) {
74 return sdsnewlen("",0);
75 }
76
77 sds sdsnew(const char *init) {
78 size_t initlen = (init == NULL) ? 0 : strlen(init);
79 return sdsnewlen(init, initlen);
80 }
81
82 sds sdsdup(const sds s) {
83 return sdsnewlen(s, sdslen(s));
84 }
85
86 void sdsfree(sds s) {
87 if (s == NULL) return;
88 zfree(s-sizeof(struct sdshdr));
89 }
90
91 void sdsupdatelen(sds s) {
92 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
93 int reallen = strlen(s);
94 sh->free += (sh->len-reallen);
95 sh->len = reallen;
96 }
97
98 void sdsclear(sds s) {
99 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
100 sh->free += sh->len;
101 sh->len = 0;
102 sh->buf[0] = '\0';
103 }
104
105 /* Enlarge the free space at the end of the sds string so that the caller
106 * is sure that after calling this function can overwrite up to addlen
107 * bytes after the end of the string, plus one more byte for nul term.
108 *
109 * Note: this does not change the *size* of the sds string as returned
110 * by sdslen(), but only the free buffer space we have. */
111 sds sdsMakeRoomFor(sds s, size_t addlen) {
112 struct sdshdr *sh, *newsh;
113 size_t free = sdsavail(s);
114 size_t len, newlen;
115
116 if (free >= addlen) return s;
117 len = sdslen(s);
118 sh = (void*) (s-(sizeof(struct sdshdr)));
119 newlen = (len+addlen);
120 if (newlen < SDS_MAX_PREALLOC)
121 newlen *= 2;
122 else
123 newlen += SDS_MAX_PREALLOC;
124 newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);
125 #ifdef SDS_ABORT_ON_OOM
126 if (newsh == NULL) sdsOomAbort();
127 #else
128 if (newsh == NULL) return NULL;
129 #endif
130
131 newsh->free = newlen - len;
132 return newsh->buf;
133 }
134
135 /* Increment the sds length and decrements the left free space at the
136 * end of the string accordingly to 'incr'. Also set the null term
137 * in the new end of the string.
138 *
139 * This function is used in order to fix the string length after the
140 * user calls sdsMakeRoomFor(), writes something after the end of
141 * the current string, and finally needs to set the new length.
142 *
143 * Note: it is possible to use a negative increment in order to
144 * right-trim the string.
145 *
146 * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the
147 * following schema to cat bytes coming from the kerenl to the end of an
148 * sds string new things without copying into an intermediate buffer:
149 *
150 * oldlen = sdslen(s);
151 * s = sdsMakeRoomFor(s, BUFFER_SIZE);
152 * nread = read(fd, s+oldlen, BUFFER_SIZE);
153 * ... check for nread <= 0 and handle it ...
154 * sdsIncrLen(s, nhread);
155 */
156 void sdsIncrLen(sds s, int incr) {
157 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
158
159 assert(sh->free >= incr);
160 sh->len += incr;
161 sh->free -= incr;
162 assert(sh->free >= 0);
163 s[sh->len] = '\0';
164 }
165
166 /* Grow the sds to have the specified length. Bytes that were not part of
167 * the original length of the sds will be set to zero. */
168 sds sdsgrowzero(sds s, size_t len) {
169 struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
170 size_t totlen, curlen = sh->len;
171
172 if (len <= curlen) return s;
173 s = sdsMakeRoomFor(s,len-curlen);
174 if (s == NULL) return NULL;
175
176 /* Make sure added region doesn't contain garbage */
177 sh = (void*)(s-(sizeof(struct sdshdr)));
178 memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
179 totlen = sh->len+sh->free;
180 sh->len = len;
181 sh->free = totlen-sh->len;
182 return s;
183 }
184
185 sds sdscatlen(sds s, void *t, size_t len) {
186 struct sdshdr *sh;
187 size_t curlen = sdslen(s);
188
189 s = sdsMakeRoomFor(s,len);
190 if (s == NULL) return NULL;
191 sh = (void*) (s-(sizeof(struct sdshdr)));
192 memcpy(s+curlen, t, len);
193 sh->len = curlen+len;
194 sh->free = sh->free-len;
195 s[curlen+len] = '\0';
196 return s;
197 }
198
199 sds sdscat(sds s, char *t) {
200 return sdscatlen(s, t, strlen(t));
201 }
202
203 sds sdscatsds(sds s, sds t) {
204 return sdscatlen(s, t, sdslen(t));
205 }
206
207 sds sdscpylen(sds s, char *t, size_t len) {
208 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
209 size_t totlen = sh->free+sh->len;
210
211 if (totlen < len) {
212 s = sdsMakeRoomFor(s,len-sh->len);
213 if (s == NULL) return NULL;
214 sh = (void*) (s-(sizeof(struct sdshdr)));
215 totlen = sh->free+sh->len;
216 }
217 memcpy(s, t, len);
218 s[len] = '\0';
219 sh->len = len;
220 sh->free = totlen-len;
221 return s;
222 }
223
224 sds sdscpy(sds s, char *t) {
225 return sdscpylen(s, t, strlen(t));
226 }
227
228 sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
229 va_list cpy;
230 char *buf, *t;
231 size_t buflen = 16;
232
233 while(1) {
234 buf = zmalloc(buflen);
235 #ifdef SDS_ABORT_ON_OOM
236 if (buf == NULL) sdsOomAbort();
237 #else
238 if (buf == NULL) return NULL;
239 #endif
240 buf[buflen-2] = '\0';
241 va_copy(cpy,ap);
242 vsnprintf(buf, buflen, fmt, cpy);
243 if (buf[buflen-2] != '\0') {
244 zfree(buf);
245 buflen *= 2;
246 continue;
247 }
248 break;
249 }
250 t = sdscat(s, buf);
251 zfree(buf);
252 return t;
253 }
254
255 sds sdscatprintf(sds s, const char *fmt, ...) {
256 va_list ap;
257 char *t;
258 va_start(ap, fmt);
259 t = sdscatvprintf(s,fmt,ap);
260 va_end(ap);
261 return t;
262 }
263
264 sds sdstrim(sds s, const char *cset) {
265 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
266 char *start, *end, *sp, *ep;
267 size_t len;
268
269 sp = start = s;
270 ep = end = s+sdslen(s)-1;
271 while(sp <= end && strchr(cset, *sp)) sp++;
272 while(ep > start && strchr(cset, *ep)) ep--;
273 len = (sp > ep) ? 0 : ((ep-sp)+1);
274 if (sh->buf != sp) memmove(sh->buf, sp, len);
275 sh->buf[len] = '\0';
276 sh->free = sh->free+(sh->len-len);
277 sh->len = len;
278 return s;
279 }
280
281 sds sdsrange(sds s, int start, int end) {
282 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
283 size_t newlen, len = sdslen(s);
284
285 if (len == 0) return s;
286 if (start < 0) {
287 start = len+start;
288 if (start < 0) start = 0;
289 }
290 if (end < 0) {
291 end = len+end;
292 if (end < 0) end = 0;
293 }
294 newlen = (start > end) ? 0 : (end-start)+1;
295 if (newlen != 0) {
296 if (start >= (signed)len) {
297 newlen = 0;
298 } else if (end >= (signed)len) {
299 end = len-1;
300 newlen = (start > end) ? 0 : (end-start)+1;
301 }
302 } else {
303 start = 0;
304 }
305 if (start && newlen) memmove(sh->buf, sh->buf+start, newlen);
306 sh->buf[newlen] = 0;
307 sh->free = sh->free+(sh->len-newlen);
308 sh->len = newlen;
309 return s;
310 }
311
312 void sdstolower(sds s) {
313 int len = sdslen(s), j;
314
315 for (j = 0; j < len; j++) s[j] = tolower(s[j]);
316 }
317
318 void sdstoupper(sds s) {
319 int len = sdslen(s), j;
320
321 for (j = 0; j < len; j++) s[j] = toupper(s[j]);
322 }
323
324 int sdscmp(sds s1, sds s2) {
325 size_t l1, l2, minlen;
326 int cmp;
327
328 l1 = sdslen(s1);
329 l2 = sdslen(s2);
330 minlen = (l1 < l2) ? l1 : l2;
331 cmp = memcmp(s1,s2,minlen);
332 if (cmp == 0) return l1-l2;
333 return cmp;
334 }
335
336 /* Split 's' with separator in 'sep'. An array
337 * of sds strings is returned. *count will be set
338 * by reference to the number of tokens returned.
339 *
340 * On out of memory, zero length string, zero length
341 * separator, NULL is returned.
342 *
343 * Note that 'sep' is able to split a string using
344 * a multi-character separator. For example
345 * sdssplit("foo_-_bar","_-_"); will return two
346 * elements "foo" and "bar".
347 *
348 * This version of the function is binary-safe but
349 * requires length arguments. sdssplit() is just the
350 * same function but for zero-terminated strings.
351 */
352 sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
353 int elements = 0, slots = 5, start = 0, j;
354 sds *tokens;
355
356 if (seplen < 1 || len < 0) return NULL;
357
358 tokens = zmalloc(sizeof(sds)*slots);
359 #ifdef SDS_ABORT_ON_OOM
360 if (tokens == NULL) sdsOomAbort();
361 #else
362 if (tokens == NULL) return NULL;
363 #endif
364
365 if (len == 0) {
366 *count = 0;
367 return tokens;
368 }
369 for (j = 0; j < (len-(seplen-1)); j++) {
370 /* make sure there is room for the next element and the final one */
371 if (slots < elements+2) {
372 sds *newtokens;
373
374 slots *= 2;
375 newtokens = zrealloc(tokens,sizeof(sds)*slots);
376 if (newtokens == NULL) {
377 #ifdef SDS_ABORT_ON_OOM
378 sdsOomAbort();
379 #else
380 goto cleanup;
381 #endif
382 }
383 tokens = newtokens;
384 }
385 /* search the separator */
386 if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
387 tokens[elements] = sdsnewlen(s+start,j-start);
388 if (tokens[elements] == NULL) {
389 #ifdef SDS_ABORT_ON_OOM
390 sdsOomAbort();
391 #else
392 goto cleanup;
393 #endif
394 }
395 elements++;
396 start = j+seplen;
397 j = j+seplen-1; /* skip the separator */
398 }
399 }
400 /* Add the final element. We are sure there is room in the tokens array. */
401 tokens[elements] = sdsnewlen(s+start,len-start);
402 if (tokens[elements] == NULL) {
403 #ifdef SDS_ABORT_ON_OOM
404 sdsOomAbort();
405 #else
406 goto cleanup;
407 #endif
408 }
409 elements++;
410 *count = elements;
411 return tokens;
412
413 #ifndef SDS_ABORT_ON_OOM
414 cleanup:
415 {
416 int i;
417 for (i = 0; i < elements; i++) sdsfree(tokens[i]);
418 zfree(tokens);
419 *count = 0;
420 return NULL;
421 }
422 #endif
423 }
424
425 void sdsfreesplitres(sds *tokens, int count) {
426 if (!tokens) return;
427 while(count--)
428 sdsfree(tokens[count]);
429 zfree(tokens);
430 }
431
432 sds sdsfromlonglong(long long value) {
433 char buf[32], *p;
434 unsigned long long v;
435
436 v = (value < 0) ? -value : value;
437 p = buf+31; /* point to the last character */
438 do {
439 *p-- = '0'+(v%10);
440 v /= 10;
441 } while(v);
442 if (value < 0) *p-- = '-';
443 p++;
444 return sdsnewlen(p,32-(p-buf));
445 }
446
447 sds sdscatrepr(sds s, char *p, size_t len) {
448 s = sdscatlen(s,"\"",1);
449 while(len--) {
450 switch(*p) {
451 case '\\':
452 case '"':
453 s = sdscatprintf(s,"\\%c",*p);
454 break;
455 case '\n': s = sdscatlen(s,"\\n",2); break;
456 case '\r': s = sdscatlen(s,"\\r",2); break;
457 case '\t': s = sdscatlen(s,"\\t",2); break;
458 case '\a': s = sdscatlen(s,"\\a",2); break;
459 case '\b': s = sdscatlen(s,"\\b",2); break;
460 default:
461 if (isprint(*p))
462 s = sdscatprintf(s,"%c",*p);
463 else
464 s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
465 break;
466 }
467 p++;
468 }
469 return sdscatlen(s,"\"",1);
470 }
471
472 /* Helper function for sdssplitargs() that returns non zero if 'c'
473 * is a valid hex digit. */
474 int is_hex_digit(char c) {
475 return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
476 (c >= 'A' && c <= 'F');
477 }
478
479 /* Helper function for sdssplitargs() that converts an hex digit into an
480 * integer from 0 to 15 */
481 int hex_digit_to_int(char c) {
482 switch(c) {
483 case '0': return 0;
484 case '1': return 1;
485 case '2': return 2;
486 case '3': return 3;
487 case '4': return 4;
488 case '5': return 5;
489 case '6': return 6;
490 case '7': return 7;
491 case '8': return 8;
492 case '9': return 9;
493 case 'a': case 'A': return 10;
494 case 'b': case 'B': return 11;
495 case 'c': case 'C': return 12;
496 case 'd': case 'D': return 13;
497 case 'e': case 'E': return 14;
498 case 'f': case 'F': return 15;
499 default: return 0;
500 }
501 }
502
503 /* Split a line into arguments, where every argument can be in the
504 * following programming-language REPL-alike form:
505 *
506 * foo bar "newline are supported\n" and "\xff\x00otherstuff"
507 *
508 * The number of arguments is stored into *argc, and an array
509 * of sds is returned. The caller should sdsfree() all the returned
510 * strings and finally zfree() the array itself.
511 *
512 * Note that sdscatrepr() is able to convert back a string into
513 * a quoted string in the same format sdssplitargs() is able to parse.
514 */
515 sds *sdssplitargs(char *line, int *argc) {
516 char *p = line;
517 char *current = NULL;
518 char **vector = NULL;
519
520 *argc = 0;
521 while(1) {
522 /* skip blanks */
523 while(*p && isspace(*p)) p++;
524 if (*p) {
525 /* get a token */
526 int inq=0; /* set to 1 if we are in "quotes" */
527 int insq=0; /* set to 1 if we are in 'single quotes' */
528 int done=0;
529
530 if (current == NULL) current = sdsempty();
531 while(!done) {
532 if (inq) {
533 if (*p == '\\' && *(p+1) == 'x' &&
534 is_hex_digit(*(p+2)) &&
535 is_hex_digit(*(p+3)))
536 {
537 unsigned char byte;
538
539 byte = (hex_digit_to_int(*(p+2))*16)+
540 hex_digit_to_int(*(p+3));
541 current = sdscatlen(current,(char*)&byte,1);
542 p += 3;
543 } else if (*p == '\\' && *(p+1)) {
544 char c;
545
546 p++;
547 switch(*p) {
548 case 'n': c = '\n'; break;
549 case 'r': c = '\r'; break;
550 case 't': c = '\t'; break;
551 case 'b': c = '\b'; break;
552 case 'a': c = '\a'; break;
553 default: c = *p; break;
554 }
555 current = sdscatlen(current,&c,1);
556 } else if (*p == '"') {
557 /* closing quote must be followed by a space or
558 * nothing at all. */
559 if (*(p+1) && !isspace(*(p+1))) goto err;
560 done=1;
561 } else if (!*p) {
562 /* unterminated quotes */
563 goto err;
564 } else {
565 current = sdscatlen(current,p,1);
566 }
567 } else if (insq) {
568 if (*p == '\\' && *(p+1) == '\'') {
569 p++;
570 current = sdscatlen(current,"'",1);
571 } else if (*p == '\'') {
572 /* closing quote must be followed by a space or
573 * nothing at all. */
574 if (*(p+1) && !isspace(*(p+1))) goto err;
575 done=1;
576 } else if (!*p) {
577 /* unterminated quotes */
578 goto err;
579 } else {
580 current = sdscatlen(current,p,1);
581 }
582 } else {
583 switch(*p) {
584 case ' ':
585 case '\n':
586 case '\r':
587 case '\t':
588 case '\0':
589 done=1;
590 break;
591 case '"':
592 inq=1;
593 break;
594 case '\'':
595 insq=1;
596 break;
597 default:
598 current = sdscatlen(current,p,1);
599 break;
600 }
601 }
602 if (*p) p++;
603 }
604 /* add the token to the vector */
605 vector = zrealloc(vector,((*argc)+1)*sizeof(char*));
606 vector[*argc] = current;
607 (*argc)++;
608 current = NULL;
609 } else {
610 return vector;
611 }
612 }
613
614 err:
615 while((*argc)--)
616 sdsfree(vector[*argc]);
617 zfree(vector);
618 if (current) sdsfree(current);
619 return NULL;
620 }
621
622 void sdssplitargs_free(sds *argv, int argc) {
623 int j;
624
625 for (j = 0 ;j < argc; j++) sdsfree(argv[j]);
626 zfree(argv);
627 }
628
629 /* Modify the string substituting all the occurrences of the set of
630 * characters specifed in the 'from' string to the corresponding character
631 * in the 'to' array.
632 *
633 * For instance: sdsmapchars(mystring, "ho", "01", 2)
634 * will have the effect of turning the string "hello" into "0ell1".
635 *
636 * The function returns the sds string pointer, that is always the same
637 * as the input pointer since no resize is needed. */
638 sds sdsmapchars(sds s, char *from, char *to, size_t setlen) {
639 size_t j, i, l = sdslen(s);
640
641 for (j = 0; j < l; j++) {
642 for (i = 0; i < setlen; i++) {
643 if (s[j] == from[i]) {
644 s[j] = to[i];
645 break;
646 }
647 }
648 }
649 return s;
650 }
651
652 #ifdef SDS_TEST_MAIN
653 #include <stdio.h>
654 #include "testhelp.h"
655
656 int main(void) {
657 {
658 struct sdshdr *sh;
659 sds x = sdsnew("foo"), y;
660
661 test_cond("Create a string and obtain the length",
662 sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0)
663
664 sdsfree(x);
665 x = sdsnewlen("foo",2);
666 test_cond("Create a string with specified length",
667 sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0)
668
669 x = sdscat(x,"bar");
670 test_cond("Strings concatenation",
671 sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0);
672
673 x = sdscpy(x,"a");
674 test_cond("sdscpy() against an originally longer string",
675 sdslen(x) == 1 && memcmp(x,"a\0",2) == 0)
676
677 x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk");
678 test_cond("sdscpy() against an originally shorter string",
679 sdslen(x) == 33 &&
680 memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0)
681
682 sdsfree(x);
683 x = sdscatprintf(sdsempty(),"%d",123);
684 test_cond("sdscatprintf() seems working in the base case",
685 sdslen(x) == 3 && memcmp(x,"123\0",4) ==0)
686
687 sdsfree(x);
688 x = sdstrim(sdsnew("xxciaoyyy"),"xy");
689 test_cond("sdstrim() correctly trims characters",
690 sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)
691
692 y = sdsrange(sdsdup(x),1,1);
693 test_cond("sdsrange(...,1,1)",
694 sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)
695
696 sdsfree(y);
697 y = sdsrange(sdsdup(x),1,-1);
698 test_cond("sdsrange(...,1,-1)",
699 sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
700
701 sdsfree(y);
702 y = sdsrange(sdsdup(x),-2,-1);
703 test_cond("sdsrange(...,-2,-1)",
704 sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)
705
706 sdsfree(y);
707 y = sdsrange(sdsdup(x),2,1);
708 test_cond("sdsrange(...,2,1)",
709 sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
710
711 sdsfree(y);
712 y = sdsrange(sdsdup(x),1,100);
713 test_cond("sdsrange(...,1,100)",
714 sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
715
716 sdsfree(y);
717 y = sdsrange(sdsdup(x),100,100);
718 test_cond("sdsrange(...,100,100)",
719 sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
720
721 sdsfree(y);
722 sdsfree(x);
723 x = sdsnew("foo");
724 y = sdsnew("foa");
725 test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0)
726
727 sdsfree(y);
728 sdsfree(x);
729 x = sdsnew("bar");
730 y = sdsnew("bar");
731 test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0)
732
733 sdsfree(y);
734 sdsfree(x);
735 x = sdsnew("aar");
736 y = sdsnew("bar");
737 test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
738
739 {
740 int oldfree;
741
742 sdsfree(x);
743 x = sdsnew("0");
744 sh = (void*) (x-(sizeof(struct sdshdr)));
745 test_cond("sdsnew() free/len buffers", sh->len == 1 && sh->free == 0);
746 x = sdsMakeRoomFor(x,1);
747 sh = (void*) (x-(sizeof(struct sdshdr)));
748 test_cond("sdsMakeRoomFor()", sh->len == 1 && sh->free > 0);
749 oldfree = sh->free;
750 x[1] = '1';
751 sdsIncrLen(x,1);
752 test_cond("sdsIncrLen() -- content", x[0] == '0' && x[1] == '1');
753 test_cond("sdsIncrLen() -- len", sh->len == 2);
754 test_cond("sdsIncrLen() -- free", sh->free == oldfree-1);
755 }
756 }
757 test_report()
758 return 0;
759 }
760 #endif