Update sds library to commit 14b8e8a1.
[ttfautohint.git] / lib / sds.c
blobb9607136fa85a4ed9218ac87353067f9a765bffe
1 /* SDS (Simple Dynamic Strings), A C dynamic strings library.
3 * Copyright (c) 2006-2014, Salvatore Sanfilippo <antirez at gmail dot com>
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
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.
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.
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <assert.h>
37 #include "sds.h"
39 typedef struct sdshdr_ {
40 int len;
41 int free;
42 char buf[];
43 } sdshdr;
45 static inline sdshdr *sds_start(const sds s)
47 return (sdshdr*) (s-(int)offsetof(sdshdr, buf));
50 /* Create a new sds string with the content specified by the 'init' pointer
51 * and 'initlen'.
52 * If NULL is used for 'init' the string is initialized with zero bytes.
54 * The string is always null-termined (all the sds strings are, always) so
55 * even if you create an sds string with:
57 * mystring = sdsnewlen("abc",3");
59 * You can print the string with printf() as there is an implicit \0 at the
60 * end of the string. However the string is binary safe and can contain
61 * \0 characters in the middle, as the length is stored in the sds header. */
62 sds sdsnewlen(const void *init, size_t initlen) {
63 sdshdr *sh;
65 if (init) {
66 sh = (sdshdr*) malloc(sizeof *sh+initlen+1);
67 } else {
68 sh = (sdshdr*) calloc(sizeof *sh+initlen+1,1);
70 if (sh == NULL) return NULL;
71 sh->len = initlen;
72 sh->free = 0;
73 if (initlen && init)
74 memcpy(sh->buf, init, initlen);
75 sh->buf[initlen] = '\0';
76 return (char*)sh->buf;
79 /* Create an empty (zero length) sds string. Even in this case the string
80 * always has an implicit null term. */
81 sds sdsempty(void) {
82 return sdsnewlen("",0);
85 /* Create a new sds string starting from a null termined C string. */
86 sds sdsnew(const char *init) {
87 size_t initlen = (init == NULL) ? 0 : strlen(init);
88 return sdsnewlen(init, initlen);
91 /* Duplicate an sds string. */
92 sds sdsdup(const sds s) {
93 return sdsnewlen(s, sdslen(s));
96 /* Free an sds string. No operation is performed if 's' is NULL. */
97 void sdsfree(sds s) {
98 if (s == NULL) return;
99 free(sds_start(s));
102 /* Set the sds string length to the length as obtained with strlen(), so
103 * considering as content only up to the first null term character.
105 * This function is useful when the sds string is hacked manually in some
106 * way, like in the following example:
108 * s = sdsnew("foobar");
109 * s[2] = '\0';
110 * sdsupdatelen(s);
111 * printf("%d\n", sdslen(s));
113 * The output will be "2", but if we comment out the call to sdsupdatelen()
114 * the output will be "6" as the string was modified but the logical length
115 * remains 6 bytes. */
116 void sdsupdatelen(sds s) {
117 sdshdr *sh = sds_start(s);
118 int reallen = strlen(s);
119 sh->free += (sh->len-reallen);
120 sh->len = reallen;
123 /* Modify an sds string on-place to make it empty (zero length).
124 * However all the existing buffer is not discarded but set as free space
125 * so that next append operations will not require allocations up to the
126 * number of bytes previously available. */
127 void sdsclear(sds s) {
128 sdshdr *sh = sds_start(s);
129 sh->free += sh->len;
130 sh->len = 0;
131 sh->buf[0] = '\0';
134 /* Enlarge the free space at the end of the sds string so that the caller
135 * is sure that after calling this function can overwrite up to addlen
136 * bytes after the end of the string, plus one more byte for nul term.
138 * Note: this does not change the *length* of the sds string as returned
139 * by sdslen(), but only the free buffer space we have. */
140 sds sdsMakeRoomFor(sds s, size_t addlen) {
141 sdshdr *sh, *newsh;
142 size_t free = sdsavail(s);
143 size_t len, newlen;
145 if (free >= addlen) return s;
146 len = sdslen(s);
147 sh = sds_start(s);
148 newlen = (len+addlen);
149 if (newlen < SDS_MAX_PREALLOC)
150 newlen *= 2;
151 else
152 newlen += SDS_MAX_PREALLOC;
153 newsh = (sdshdr*) realloc(sh, sizeof *newsh+newlen+1);
154 if (newsh == NULL) return NULL;
156 newsh->free = newlen - len;
157 return newsh->buf;
160 /* Reallocate the sds string so that it has no free space at the end. The
161 * contained string remains not altered, but next concatenation operations
162 * will require a reallocation.
164 * After the call, the passed sds string is no longer valid and all the
165 * references must be substituted with the new pointer returned by the call. */
166 sds sdsRemoveFreeSpace(sds s) {
167 sdshdr *sh, *newsh;
169 sh = sds_start(s);
170 newsh = (sdshdr*) realloc(sh, sizeof *sh+sh->len+1);
171 if (newsh == NULL) return NULL;
173 newsh->free = 0;
174 return newsh->buf;
177 /* Return the total size of the allocation of the specifed sds string,
178 * including:
179 * 1) The sds header before the pointer.
180 * 2) The string.
181 * 3) The free buffer at the end if any.
182 * 4) The implicit null term.
184 size_t sdsAllocSize(sds s) {
185 sdshdr *sh = sds_start(s);
187 return sizeof(*sh)+sh->len+sh->free+1;
190 /* Increment the sds length and decrements the left free space at the
191 * end of the string according to 'incr'. Also set the null term
192 * in the new end of the string.
194 * This function is used in order to fix the string length after the
195 * user calls sdsMakeRoomFor(), writes something after the end of
196 * the current string, and finally needs to set the new length.
198 * Note: it is possible to use a negative increment in order to
199 * right-trim the string.
201 * Usage example:
203 * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the
204 * following schema, to cat bytes coming from the kernel to the end of an
205 * sds string without copying into an intermediate buffer:
207 * oldlen = sdslen(s);
208 * s = sdsMakeRoomFor(s, BUFFER_SIZE);
209 * nread = read(fd, s+oldlen, BUFFER_SIZE);
210 * ... check for nread <= 0 and handle it ...
211 * sdsIncrLen(s, nread);
213 void sdsIncrLen(sds s, int incr) {
214 sdshdr *sh = sds_start(s);
216 assert(sh->free >= incr);
217 sh->len += incr;
218 sh->free -= incr;
219 assert(sh->free >= 0);
220 s[sh->len] = '\0';
223 /* Grow the sds to have the specified length. Bytes that were not part of
224 * the original length of the sds will be set to zero.
226 * if the specified length is smaller than the current length, no operation
227 * is performed. */
228 sds sdsgrowzero(sds s, size_t len) {
229 sdshdr *sh = sds_start(s);
230 size_t totlen, curlen = sh->len;
232 if (len <= curlen) return s;
233 s = sdsMakeRoomFor(s,len-curlen);
234 if (s == NULL) return NULL;
236 /* Make sure added region doesn't contain garbage */
237 sh = sds_start(s);
238 memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
239 totlen = sh->len+sh->free;
240 sh->len = len;
241 sh->free = totlen-sh->len;
242 return s;
245 /* Append the specified binary-safe string pointed by 't' of 'len' bytes to the
246 * end of the specified sds string 's'.
248 * After the call, the passed sds string is no longer valid and all the
249 * references must be substituted with the new pointer returned by the call. */
250 sds sdscatlen(sds s, const void *t, size_t len) {
251 sdshdr *sh;
252 size_t curlen = sdslen(s);
254 s = sdsMakeRoomFor(s,len);
255 if (s == NULL) return NULL;
256 sh = sds_start(s);
257 memcpy(s+curlen, t, len);
258 sh->len = curlen+len;
259 sh->free = sh->free-len;
260 s[curlen+len] = '\0';
261 return s;
264 /* Append the specified null termianted C string to the sds string 's'.
266 * After the call, the passed sds string is no longer valid and all the
267 * references must be substituted with the new pointer returned by the call. */
268 sds sdscat(sds s, const char *t) {
269 return sdscatlen(s, t, strlen(t));
272 /* Append the specified sds 't' to the existing sds 's'.
274 * After the call, the modified sds string is no longer valid and all the
275 * references must be substituted with the new pointer returned by the call. */
276 sds sdscatsds(sds s, const sds t) {
277 return sdscatlen(s, t, sdslen(t));
280 /* Destructively modify the sds string 's' to hold the specified binary
281 * safe string pointed by 't' of length 'len' bytes. */
282 sds sdscpylen(sds s, const char *t, size_t len) {
283 sdshdr *sh = sds_start(s);
284 size_t totlen = sh->free+sh->len;
286 if (totlen < len) {
287 s = sdsMakeRoomFor(s,len-sh->len);
288 if (s == NULL) return NULL;
289 sh = sds_start(s);
290 totlen = sh->free+sh->len;
292 memcpy(s, t, len);
293 s[len] = '\0';
294 sh->len = len;
295 sh->free = totlen-len;
296 return s;
299 /* Like sdscpylen() but 't' must be a null-termined string so that the length
300 * of the string is obtained with strlen(). */
301 sds sdscpy(sds s, const char *t) {
302 return sdscpylen(s, t, strlen(t));
305 /* Like sdscatpritf() but gets va_list instead of being variadic. */
306 sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
307 va_list cpy;
308 char *buf, *t;
309 size_t buflen = 16;
311 while(1) {
312 buf = (char*) malloc(buflen);
313 if (buf == NULL) return NULL;
314 buf[buflen-2] = '\0';
315 va_copy(cpy,ap);
316 vsnprintf(buf, buflen, fmt, cpy);
317 va_end(cpy);
318 if (buf[buflen-2] != '\0') {
319 free(buf);
320 buflen *= 2;
321 continue;
323 break;
325 t = sdscat(s, buf);
326 free(buf);
327 return t;
330 /* Append to the sds string 's' a string obtained using printf-alike format
331 * specifier.
333 * After the call, the modified sds string is no longer valid and all the
334 * references must be substituted with the new pointer returned by the call.
336 * Example:
338 * s = sdsempty("Sum is: ");
339 * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b).
341 * Often you need to create a string from scratch with the printf-alike
342 * format. When this is the need, just use sdsempty() as the target string:
344 * s = sdscatprintf(sdsempty(), "... your format ...", args);
346 sds sdscatprintf(sds s, const char *fmt, ...) {
347 va_list ap;
348 char *t;
349 va_start(ap, fmt);
350 t = sdscatvprintf(s,fmt,ap);
351 va_end(ap);
352 return t;
355 /* Remove the part of the string from left and from right composed just of
356 * contiguous characters found in 'cset', that is a null terminted C string.
358 * After the call, the modified sds string is no longer valid and all the
359 * references must be substituted with the new pointer returned by the call.
361 * Example:
363 * s = sdsnew("AA...AA.a.aa.aHello World :::");
364 * sdstrim(s,"aA. :");
365 * printf("%s\n", s);
367 * Output will be just "Hello World".
369 void sdstrim(sds s, const char *cset) {
370 sdshdr *sh = sds_start(s);
371 char *start, *end, *sp, *ep;
372 size_t len;
374 sp = start = s;
375 ep = end = s+sdslen(s)-1;
376 while(sp <= end && strchr(cset, *sp)) sp++;
377 while(ep > start && strchr(cset, *ep)) ep--;
378 len = (sp > ep) ? 0 : ((ep-sp)+1);
379 if (sh->buf != sp) memmove(sh->buf, sp, len);
380 sh->buf[len] = '\0';
381 sh->free = sh->free+(sh->len-len);
382 sh->len = len;
385 /* Turn the string into a smaller (or equal) string containing only the
386 * substring specified by the 'start' and 'end' indexes.
388 * start and end can be negative, where -1 means the last character of the
389 * string, -2 the penultimate character, and so forth.
391 * The interval is inclusive, so the start and end characters will be part
392 * of the resulting string.
394 * The string is modified in-place.
396 * Example:
398 * s = sdsnew("Hello World");
399 * sdsrange(s,1,-1); => "ello World"
401 void sdsrange(sds s, int start, int end) {
402 sdshdr *sh = sds_start(s);
403 size_t newlen, len = sdslen(s);
405 if (len == 0) return;
406 if (start < 0) {
407 start = len+start;
408 if (start < 0) start = 0;
410 if (end < 0) {
411 end = len+end;
412 if (end < -1) end = -1;
414 newlen = (start > end) ? 0 : (end-start)+1;
415 if (newlen != 0) {
416 if (start >= (signed)len) {
417 newlen = 0;
418 } else if (end >= (signed)len) {
419 end = len-1;
420 newlen = (start > end) ? 0 : (end-start)+1;
422 } else {
423 start = 0;
425 if (start && newlen) memmove(sh->buf, sh->buf+start, newlen);
426 sh->buf[newlen] = 0;
427 sh->free = sh->free+(sh->len-newlen);
428 sh->len = newlen;
431 /* Apply tolower() to every character of the sds string 's'. */
432 void sdstolower(sds s) {
433 int len = sdslen(s), j;
435 for (j = 0; j < len; j++) s[j] = tolower((unsigned char)s[j]);
438 /* Apply toupper() to every character of the sds string 's'. */
439 void sdstoupper(sds s) {
440 int len = sdslen(s), j;
442 for (j = 0; j < len; j++) s[j] = toupper((unsigned char)s[j]);
445 /* Compare two sds strings s1 and s2 with memcmp().
447 * Return value:
449 * 1 if s1 > s2.
450 * -1 if s1 < s2.
451 * 0 if s1 and s2 are exactly the same binary string.
453 * If two strings share exactly the same prefix, but one of the two has
454 * additional characters, the longer string is considered to be greater than
455 * the smaller one. */
456 int sdscmp(const sds s1, const sds s2) {
457 size_t l1, l2, minlen;
458 int cmp;
460 l1 = sdslen(s1);
461 l2 = sdslen(s2);
462 minlen = (l1 < l2) ? l1 : l2;
463 cmp = memcmp(s1,s2,minlen);
464 if (cmp == 0) return l1-l2;
465 return cmp;
468 /* Split 's' with separator in 'sep'. An array
469 * of sds strings is returned. *count will be set
470 * by reference to the number of tokens returned.
472 * On out of memory, zero length string, zero length
473 * separator, NULL is returned.
475 * Note that 'sep' is able to split a string using
476 * a multi-character separator. For example
477 * sdssplit("foo_-_bar","_-_"); will return two
478 * elements "foo" and "bar".
480 * This version of the function is binary-safe but
481 * requires length arguments. sdssplit() is just the
482 * same function but for zero-terminated strings.
484 sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) {
485 int elements = 0, slots = 5, start = 0, j;
486 sds *tokens;
488 if (seplen < 1 || len < 0) return NULL;
490 tokens = (sds*) malloc(sizeof(sds)*slots);
491 if (tokens == NULL) return NULL;
493 if (len == 0) {
494 *count = 0;
495 return tokens;
497 for (j = 0; j < (len-(seplen-1)); j++) {
498 /* make sure there is room for the next element and the final one */
499 if (slots < elements+2) {
500 sds *newtokens;
502 slots *= 2;
503 newtokens = (sds*) realloc(tokens,sizeof(sds)*slots);
504 if (newtokens == NULL) goto cleanup;
505 tokens = newtokens;
507 /* search the separator */
508 if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
509 tokens[elements] = sdsnewlen(s+start,j-start);
510 if (tokens[elements] == NULL) goto cleanup;
511 elements++;
512 start = j+seplen;
513 j = j+seplen-1; /* skip the separator */
516 /* Add the final element. We are sure there is room in the tokens array. */
517 tokens[elements] = sdsnewlen(s+start,len-start);
518 if (tokens[elements] == NULL) goto cleanup;
519 elements++;
520 *count = elements;
521 return tokens;
523 cleanup:
525 int i;
526 for (i = 0; i < elements; i++) sdsfree(tokens[i]);
527 free(tokens);
528 *count = 0;
529 return NULL;
533 /* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */
534 void sdsfreesplitres(sds *tokens, int count) {
535 if (!tokens) return;
536 while(count--)
537 sdsfree(tokens[count]);
538 free(tokens);
541 /* Create an sds string from a long long value. It is much faster than:
543 * sdscatprintf(sdsempty(),"%lld\n", value);
545 sds sdsfromlonglong(long long value) {
546 char buf[32], *p;
547 unsigned long long v;
549 v = (value < 0) ? -value : value;
550 p = buf+31; /* point to the last character */
551 do {
552 *p-- = '0'+(v%10);
553 v /= 10;
554 } while(v);
555 if (value < 0) *p-- = '-';
556 p++;
557 return sdsnewlen(p,32-(p-buf));
560 /* Append to the sds string "s" an escaped string representation where
561 * all the non-printable characters (tested with isprint()) are turned into
562 * escapes in the form "\n\r\a...." or "\x<hex-number>".
564 * After the call, the modified sds string is no longer valid and all the
565 * references must be substituted with the new pointer returned by the call. */
566 sds sdscatrepr(sds s, const char *p, size_t len) {
567 s = sdscatlen(s,"\"",1);
568 while(len--) {
569 switch(*p) {
570 case '\\':
571 case '"':
572 s = sdscatprintf(s,"\\%c",*p);
573 break;
574 case '\n': s = sdscatlen(s,"\\n",2); break;
575 case '\r': s = sdscatlen(s,"\\r",2); break;
576 case '\t': s = sdscatlen(s,"\\t",2); break;
577 case '\a': s = sdscatlen(s,"\\a",2); break;
578 case '\b': s = sdscatlen(s,"\\b",2); break;
579 default:
580 if (isprint((unsigned char)*p))
581 s = sdscatprintf(s,"%c",*p);
582 else
583 s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
584 break;
586 p++;
588 return sdscatlen(s,"\"",1);
591 /* Helper function for sdssplitargs() that returns non zero if 'c'
592 * is a valid hex digit. */
593 int is_hex_digit(char c) {
594 return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
595 (c >= 'A' && c <= 'F');
598 /* Helper function for sdssplitargs() that converts a hex digit into an
599 * integer from 0 to 15 */
600 int hex_digit_to_int(char c) {
601 switch(c) {
602 case '0': return 0;
603 case '1': return 1;
604 case '2': return 2;
605 case '3': return 3;
606 case '4': return 4;
607 case '5': return 5;
608 case '6': return 6;
609 case '7': return 7;
610 case '8': return 8;
611 case '9': return 9;
612 case 'a': case 'A': return 10;
613 case 'b': case 'B': return 11;
614 case 'c': case 'C': return 12;
615 case 'd': case 'D': return 13;
616 case 'e': case 'E': return 14;
617 case 'f': case 'F': return 15;
618 default: return 0;
622 /* Split a line into arguments, where every argument can be in the
623 * following programming-language REPL-alike form:
625 * foo bar "newline are supported\n" and "\xff\x00otherstuff"
627 * The number of arguments is stored into *argc, and an array
628 * of sds is returned.
630 * The caller should free the resulting array of sds strings with
631 * sdsfreesplitres().
633 * Note that sdscatrepr() is able to convert back a string into
634 * a quoted string in the same format sdssplitargs() is able to parse.
636 * The function returns the allocated tokens on success, even when the
637 * input string is empty, or NULL if the input contains unbalanced
638 * quotes or closed quotes followed by non space characters
639 * as in: "foo"bar or "foo'
641 sds *sdssplitargs(const char *line, int *argc) {
642 const char *p = line;
643 char *current = NULL;
644 char **vector = NULL;
646 *argc = 0;
647 while(1) {
648 /* skip blanks */
649 while(*p && isspace((unsigned char)*p)) p++;
650 if (*p) {
651 /* get a token */
652 int inq=0; /* set to 1 if we are in "quotes" */
653 int insq=0; /* set to 1 if we are in 'single quotes' */
654 int done=0;
656 if (current == NULL) current = sdsempty();
657 while(!done) {
658 if (inq) {
659 if (*p == '\\' && *(p+1) == 'x' &&
660 is_hex_digit(*(p+2)) &&
661 is_hex_digit(*(p+3)))
663 unsigned char byte;
665 byte = (hex_digit_to_int(*(p+2))*16)+
666 hex_digit_to_int(*(p+3));
667 current = sdscatlen(current,(char*)&byte,1);
668 p += 3;
669 } else if (*p == '\\' && *(p+1)) {
670 char c;
672 p++;
673 switch(*p) {
674 case 'n': c = '\n'; break;
675 case 'r': c = '\r'; break;
676 case 't': c = '\t'; break;
677 case 'b': c = '\b'; break;
678 case 'a': c = '\a'; break;
679 default: c = *p; break;
681 current = sdscatlen(current,&c,1);
682 } else if (*p == '"') {
683 /* closing quote must be followed by a space or
684 * nothing at all. */
685 if (*(p+1) && !isspace((unsigned char)*(p+1))) goto err;
686 done=1;
687 } else if (!*p) {
688 /* unterminated quotes */
689 goto err;
690 } else {
691 current = sdscatlen(current,p,1);
693 } else if (insq) {
694 if (*p == '\\' && *(p+1) == '\'') {
695 p++;
696 current = sdscatlen(current,"'",1);
697 } else if (*p == '\'') {
698 /* closing quote must be followed by a space or
699 * nothing at all. */
700 if (*(p+1) && !isspace((unsigned char)*(p+1))) goto err;
701 done=1;
702 } else if (!*p) {
703 /* unterminated quotes */
704 goto err;
705 } else {
706 current = sdscatlen(current,p,1);
708 } else {
709 switch(*p) {
710 case ' ':
711 case '\n':
712 case '\r':
713 case '\t':
714 case '\0':
715 done=1;
716 break;
717 case '"':
718 inq=1;
719 break;
720 case '\'':
721 insq=1;
722 break;
723 default:
724 current = sdscatlen(current,p,1);
725 break;
728 if (*p) p++;
730 /* add the token to the vector */
731 vector = (char**) realloc(vector,((*argc)+1)*sizeof(char*));
732 vector[*argc] = current;
733 (*argc)++;
734 current = NULL;
735 } else {
736 /* Even on empty input string return something not NULL. */
737 if (vector == NULL) vector = (char**) malloc(sizeof(void*));
738 return vector;
742 err:
743 while((*argc)--)
744 sdsfree(vector[*argc]);
745 free(vector);
746 if (current) sdsfree(current);
747 *argc = 0;
748 return NULL;
751 /* Modify the string substituting all the occurrences of the set of
752 * characters specified in the 'from' string to the corresponding character
753 * in the 'to' array.
755 * For instance: sdsmapchars(mystring, "ho", "01", 2)
756 * will have the effect of turning the string "hello" into "0ell1".
758 * The function returns the sds string pointer, that is always the same
759 * as the input pointer since no resize is needed. */
760 sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {
761 size_t j, i, l = sdslen(s);
763 for (j = 0; j < l; j++) {
764 for (i = 0; i < setlen; i++) {
765 if (s[j] == from[i]) {
766 s[j] = to[i];
767 break;
771 return s;
774 /* Join an array of C strings using the specified separator (also a C string).
775 * Returns the result as an sds string. */
776 sds sdsjoin(char **argv, int argc, char *sep, size_t seplen) {
777 sds join = sdsempty();
778 int j;
780 for (j = 0; j < argc; j++) {
781 join = sdscat(join, argv[j]);
782 if (j != argc-1) join = sdscatlen(join,sep,seplen);
784 return join;
787 /* Like sdsjoin, but joins an array of SDS strings. */
788 sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {
789 sds join = sdsempty();
790 int j;
792 for (j = 0; j < argc; j++) {
793 join = sdscatsds(join, argv[j]);
794 if (j != argc-1) join = sdscatlen(join,sep,seplen);
796 return join;
799 #ifdef SDS_TEST_MAIN
800 #include <stdio.h>
801 #include "testhelp.h"
803 int main(void) {
805 sdshdr *sh;
806 sds x = sdsnew("foo"), y;
808 test_cond("Create a string and obtain the length",
809 sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0)
811 sdsfree(x);
812 x = sdsnewlen("foo",2);
813 test_cond("Create a string with specified length",
814 sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0)
816 x = sdscat(x,"bar");
817 test_cond("Strings concatenation",
818 sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0);
820 x = sdscpy(x,"a");
821 test_cond("sdscpy() against an originally longer string",
822 sdslen(x) == 1 && memcmp(x,"a\0",2) == 0)
824 x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk");
825 test_cond("sdscpy() against an originally shorter string",
826 sdslen(x) == 33 &&
827 memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0)
829 sdsfree(x);
830 x = sdscatprintf(sdsempty(),"%d",123);
831 test_cond("sdscatprintf() seems working in the base case",
832 sdslen(x) == 3 && memcmp(x,"123\0",4) ==0)
834 sdsfree(x);
835 x = sdsnew("xxciaoyyy");
836 sdstrim(x,"xy");
837 test_cond("sdstrim() correctly trims characters",
838 sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)
840 y = sdsdup(x);
841 sdsrange(y,1,1);
842 test_cond("sdsrange(...,1,1)",
843 sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)
845 sdsfree(y);
846 y = sdsdup(x);
847 sdsrange(y,1,-1);
848 test_cond("sdsrange(...,1,-1)",
849 sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
851 sdsfree(y);
852 y = sdsdup(x);
853 sdsrange(y,-2,-1);
854 test_cond("sdsrange(...,-2,-1)",
855 sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)
857 sdsfree(y);
858 y = sdsdup(x);
859 sdsrange(y,2,1);
860 test_cond("sdsrange(...,2,1)",
861 sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
863 sdsfree(y);
864 y = sdsdup(x);
865 sdsrange(y,1,100);
866 test_cond("sdsrange(...,1,100)",
867 sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
869 sdsfree(y);
870 y = sdsdup(x);
871 sdsrange(y,100,100);
872 test_cond("sdsrange(...,100,100)",
873 sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
875 sdsfree(y);
876 sdsfree(x);
877 x = sdsnew("foo");
878 y = sdsnew("foa");
879 test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0)
881 sdsfree(y);
882 sdsfree(x);
883 x = sdsnew("bar");
884 y = sdsnew("bar");
885 test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0)
887 sdsfree(y);
888 sdsfree(x);
889 x = sdsnew("aar");
890 y = sdsnew("bar");
891 test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
893 sdsfree(y);
894 sdsfree(x);
895 x = sdsnewlen("\a\n\0foo\r",7);
896 y = sdscatrepr(sdsempty(),x,sdslen(x));
897 test_cond("sdscatrepr(...data...)",
898 memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0)
901 int oldfree;
903 sdsfree(x);
904 x = sdsnew("0");
905 sh = sds_start(x);
906 test_cond("sdsnew() free/len buffers", sh->len == 1 && sh->free == 0);
907 x = sdsMakeRoomFor(x,1);
908 sh = sds_start(x);
909 test_cond("sdsMakeRoomFor()", sh->len == 1 && sh->free > 0);
910 oldfree = sh->free;
911 x[1] = '1';
912 sdsIncrLen(x,1);
913 test_cond("sdsIncrLen() -- content", x[0] == '0' && x[1] == '1');
914 test_cond("sdsIncrLen() -- len", sh->len == 2);
915 test_cond("sdsIncrLen() -- free", sh->free == oldfree-1);
918 x = sdsnew("0FoO1bar\n");
919 sdstolower(x);
920 test_cond("sdstolower(...)",
921 memcmp(x,"0foo1bar\n\0",10) == 0)
923 sdsfree(x);
925 x = sdsnew("0FoO1bar\n");
926 sdstoupper(x);
927 test_cond("sdstoupper(...)",
928 memcmp(x,"0FOO1BAR\n\0",10) == 0)
930 sdsfree(x);
932 test_report()
933 return 0;
935 #endif