Use `Control_Type' to handle different segment directions.
[ttfautohint.git] / lib / sds.c
blobaee7037e70317a8c66f54d910c671ed08c43bb23
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;
100 free(sds_start(s));
103 /* Set the sds string length to the length as obtained with strlen(), so
104 * considering as content only up to the first null term character.
106 * This function is useful when the sds string is hacked manually in some
107 * way, like in the following example:
109 * s = sdsnew("foobar");
110 * s[2] = '\0';
111 * sdsupdatelen(s);
112 * printf("%d\n", sdslen(s));
114 * The output will be "2", but if we comment out the call to sdsupdatelen()
115 * the output will be "6" as the string was modified but the logical length
116 * remains 6 bytes. */
117 void sdsupdatelen(sds s) {
118 if (s == NULL) return;
120 sdshdr *sh = sds_start(s);
121 int reallen = strlen(s);
122 sh->free += (sh->len-reallen);
123 sh->len = reallen;
126 /* Modify an sds string on-place to make it empty (zero length).
127 * However all the existing buffer is not discarded but set as free space
128 * so that next append operations will not require allocations up to the
129 * number of bytes previously available. */
130 void sdsclear(sds s) {
131 if (s == NULL) return;
133 sdshdr *sh = sds_start(s);
134 sh->free += sh->len;
135 sh->len = 0;
136 sh->buf[0] = '\0';
139 /* Enlarge the free space at the end of the sds string so that the caller
140 * is sure that after calling this function can overwrite up to addlen
141 * bytes after the end of the string, plus one more byte for nul term.
143 * Note: this does not change the *length* of the sds string as returned
144 * by sdslen(), but only the free buffer space we have. */
145 sds sdsMakeRoomFor(sds s, size_t addlen) {
146 if (s == NULL) return NULL;
148 sdshdr *sh, *newsh;
149 size_t free = sdsavail(s);
150 size_t len, newlen;
152 if (free >= addlen) return s;
153 len = sdslen(s);
154 sh = sds_start(s);
155 newlen = (len+addlen);
156 if (newlen < SDS_MAX_PREALLOC)
157 newlen *= 2;
158 else
159 newlen += SDS_MAX_PREALLOC;
160 newsh = (sdshdr*) realloc(sh, sizeof *newsh+newlen+1);
161 if (newsh == NULL) return NULL;
163 newsh->free = newlen - len;
164 return newsh->buf;
167 /* Reallocate the sds string so that it has no free space at the end. The
168 * contained string remains not altered, but next concatenation operations
169 * will require a reallocation.
171 * After the call, the passed sds string is no longer valid and all the
172 * references must be substituted with the new pointer returned by the call. */
173 sds sdsRemoveFreeSpace(sds s) {
174 if (s == NULL) return NULL;
176 sdshdr *sh, *newsh;
178 sh = sds_start(s);
179 newsh = (sdshdr*) realloc(sh, sizeof *sh+sh->len+1);
180 if (newsh == NULL) return NULL;
182 newsh->free = 0;
183 return newsh->buf;
186 /* Return the total size of the allocation of the specifed sds string,
187 * including:
188 * 1) The sds header before the pointer.
189 * 2) The string.
190 * 3) The free buffer at the end if any.
191 * 4) The implicit null term.
193 size_t sdsAllocSize(sds s) {
194 if (s == NULL) return 0;
196 sdshdr *sh = sds_start(s);
198 return sizeof(*sh)+sh->len+sh->free+1;
201 /* Increment the sds length and decrements the left free space at the
202 * end of the string according to 'incr'. Also set the null term
203 * in the new end of the string.
205 * This function is used in order to fix the string length after the
206 * user calls sdsMakeRoomFor(), writes something after the end of
207 * the current string, and finally needs to set the new length.
209 * Note: it is possible to use a negative increment in order to
210 * right-trim the string.
212 * Usage example:
214 * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the
215 * following schema, to cat bytes coming from the kernel to the end of an
216 * sds string without copying into an intermediate buffer:
218 * oldlen = sdslen(s);
219 * s = sdsMakeRoomFor(s, BUFFER_SIZE);
220 * nread = read(fd, s+oldlen, BUFFER_SIZE);
221 * ... check for nread <= 0 and handle it ...
222 * sdsIncrLen(s, nread);
224 void sdsIncrLen(sds s, int incr) {
225 if (s == NULL) return;
227 sdshdr *sh = sds_start(s);
229 assert(sh->free >= incr);
230 sh->len += incr;
231 sh->free -= incr;
232 assert(sh->free >= 0);
233 s[sh->len] = '\0';
236 /* Grow the sds to have the specified length. Bytes that were not part of
237 * the original length of the sds will be set to zero.
239 * if the specified length is smaller than the current length, no operation
240 * is performed. */
241 sds sdsgrowzero(sds s, size_t len) {
242 if (s == NULL) return NULL;
244 sdshdr *sh = sds_start(s);
245 size_t totlen, curlen = sh->len;
247 if (len <= curlen) return s;
248 s = sdsMakeRoomFor(s,len-curlen);
249 if (s == NULL) return NULL;
251 /* Make sure added region doesn't contain garbage */
252 sh = sds_start(s);
253 memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
254 totlen = sh->len+sh->free;
255 sh->len = len;
256 sh->free = totlen-sh->len;
257 return s;
260 /* Append the specified binary-safe string pointed by 't' of 'len' bytes to the
261 * end of the specified sds string 's'.
263 * After the call, the passed sds string is no longer valid and all the
264 * references must be substituted with the new pointer returned by the call. */
265 sds sdscatlen(sds s, const void *t, size_t len) {
266 sdshdr *sh;
267 size_t curlen = sdslen(s);
269 s = sdsMakeRoomFor(s,len);
270 if (s == NULL) return NULL;
271 sh = sds_start(s);
272 memcpy(s+curlen, t, len);
273 sh->len = curlen+len;
274 sh->free = sh->free-len;
275 s[curlen+len] = '\0';
276 return s;
279 /* Append the specified null termianted C string to the sds string 's'.
281 * After the call, the passed sds string is no longer valid and all the
282 * references must be substituted with the new pointer returned by the call. */
283 sds sdscat(sds s, const char *t) {
284 return sdscatlen(s, t, strlen(t));
287 /* Append the specified sds 't' to the existing sds 's'.
289 * After the call, the modified sds string is no longer valid and all the
290 * references must be substituted with the new pointer returned by the call. */
291 sds sdscatsds(sds s, const sds t) {
292 return sdscatlen(s, t, sdslen(t));
295 /* Destructively modify the sds string 's' to hold the specified binary
296 * safe string pointed by 't' of length 'len' bytes. */
297 sds sdscpylen(sds s, const char *t, size_t len) {
298 if (s == NULL) return NULL;
300 sdshdr *sh = sds_start(s);
301 size_t totlen = sh->free+sh->len;
303 if (totlen < len) {
304 s = sdsMakeRoomFor(s,len-sh->len);
305 if (s == NULL) return NULL;
306 sh = sds_start(s);
307 totlen = sh->free+sh->len;
309 memcpy(s, t, len);
310 s[len] = '\0';
311 sh->len = len;
312 sh->free = totlen-len;
313 return s;
316 /* Like sdscpylen() but 't' must be a null-termined string so that the length
317 * of the string is obtained with strlen(). */
318 sds sdscpy(sds s, const char *t) {
319 return sdscpylen(s, t, strlen(t));
322 /* Like sdscatpritf() but gets va_list instead of being variadic. */
323 sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
324 va_list cpy;
325 char *buf, *t;
326 size_t buflen = 16;
328 while(1) {
329 buf = (char*) malloc(buflen);
330 if (buf == NULL) return NULL;
331 buf[buflen-2] = '\0';
332 va_copy(cpy,ap);
333 vsnprintf(buf, buflen, fmt, cpy);
334 va_end(cpy);
335 if (buf[buflen-2] != '\0') {
336 free(buf);
337 buflen *= 2;
338 continue;
340 break;
342 t = sdscat(s, buf);
343 free(buf);
344 return t;
347 /* Append to the sds string 's' a string obtained using printf-alike format
348 * specifier.
350 * After the call, the modified sds string is no longer valid and all the
351 * references must be substituted with the new pointer returned by the call.
353 * Example:
355 * s = sdsempty("Sum is: ");
356 * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b).
358 * Often you need to create a string from scratch with the printf-alike
359 * format. When this is the need, just use sdsempty() as the target string:
361 * s = sdscatprintf(sdsempty(), "... your format ...", args);
363 sds sdscatprintf(sds s, const char *fmt, ...) {
364 va_list ap;
365 char *t;
366 va_start(ap, fmt);
367 t = sdscatvprintf(s,fmt,ap);
368 va_end(ap);
369 return t;
372 /* Remove the part of the string from left and from right composed just of
373 * contiguous characters found in 'cset', that is a null terminted C string.
375 * After the call, the modified sds string is no longer valid and all the
376 * references must be substituted with the new pointer returned by the call.
378 * Example:
380 * s = sdsnew("AA...AA.a.aa.aHello World :::");
381 * sdstrim(s,"aA. :");
382 * printf("%s\n", s);
384 * Output will be just "Hello World".
386 void sdstrim(sds s, const char *cset) {
387 if (s == NULL) return;
389 sdshdr *sh = sds_start(s);
390 char *start, *end, *sp, *ep;
391 size_t len;
393 sp = start = s;
394 ep = end = s+sdslen(s)-1;
395 while(sp <= end && strchr(cset, *sp)) sp++;
396 while(ep > start && strchr(cset, *ep)) ep--;
397 len = (sp > ep) ? 0 : ((ep-sp)+1);
398 if (sh->buf != sp) memmove(sh->buf, sp, len);
399 sh->buf[len] = '\0';
400 sh->free = sh->free+(sh->len-len);
401 sh->len = len;
404 /* Turn the string into a smaller (or equal) string containing only the
405 * substring specified by the 'start' and 'end' indexes.
407 * start and end can be negative, where -1 means the last character of the
408 * string, -2 the penultimate character, and so forth.
410 * The interval is inclusive, so the start and end characters will be part
411 * of the resulting string.
413 * The string is modified in-place.
415 * Example:
417 * s = sdsnew("Hello World");
418 * sdsrange(s,1,-1); => "ello World"
420 void sdsrange(sds s, int start, int end) {
421 sdshdr *sh = sds_start(s);
422 size_t newlen, len = sdslen(s);
424 if (len == 0) return;
425 if (start < 0) {
426 start = len+start;
427 if (start < 0) start = 0;
429 if (end < 0) {
430 end = len+end;
431 if (end < -1) end = -1;
433 newlen = (start > end) ? 0 : (end-start)+1;
434 if (newlen != 0) {
435 if (start >= (signed)len) {
436 newlen = 0;
437 } else if (end >= (signed)len) {
438 end = len-1;
439 newlen = (start > end) ? 0 : (end-start)+1;
441 } else {
442 start = 0;
444 if (start && newlen) memmove(sh->buf, sh->buf+start, newlen);
445 sh->buf[newlen] = 0;
446 sh->free = sh->free+(sh->len-newlen);
447 sh->len = newlen;
450 /* Apply tolower() to every character of the sds string 's'. */
451 void sdstolower(sds s) {
452 int len = sdslen(s), j;
454 for (j = 0; j < len; j++) s[j] = tolower((unsigned char)s[j]);
457 /* Apply toupper() to every character of the sds string 's'. */
458 void sdstoupper(sds s) {
459 int len = sdslen(s), j;
461 for (j = 0; j < len; j++) s[j] = toupper((unsigned char)s[j]);
464 /* Compare two sds strings s1 and s2 with memcmp().
466 * Return value:
468 * 1 if s1 > s2.
469 * -1 if s1 < s2.
470 * 0 if s1 and s2 are exactly the same binary string.
472 * If two strings share exactly the same prefix, but one of the two has
473 * additional characters, the longer string is considered to be greater than
474 * the smaller one. */
475 int sdscmp(const sds s1, const sds s2) {
476 size_t l1, l2, minlen;
477 int cmp;
479 l1 = sdslen(s1);
480 l2 = sdslen(s2);
481 minlen = (l1 < l2) ? l1 : l2;
482 cmp = memcmp(s1,s2,minlen);
483 if (cmp == 0) return l1-l2;
484 return cmp;
487 /* Split 's' with separator in 'sep'. An array
488 * of sds strings is returned. *count will be set
489 * by reference to the number of tokens returned.
491 * On out of memory, zero length string, zero length
492 * separator, NULL is returned.
494 * Note that 'sep' is able to split a string using
495 * a multi-character separator. For example
496 * sdssplit("foo_-_bar","_-_"); will return two
497 * elements "foo" and "bar".
499 * This version of the function is binary-safe but
500 * requires length arguments. sdssplit() is just the
501 * same function but for zero-terminated strings.
503 sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) {
504 int elements = 0, slots = 5, start = 0, j;
505 sds *tokens;
507 if (seplen < 1 || len < 0) return NULL;
509 tokens = (sds*) malloc(sizeof(sds)*slots);
510 if (tokens == NULL) return NULL;
512 if (len == 0) {
513 *count = 0;
514 return tokens;
516 for (j = 0; j < (len-(seplen-1)); j++) {
517 /* make sure there is room for the next element and the final one */
518 if (slots < elements+2) {
519 sds *newtokens;
521 slots *= 2;
522 newtokens = (sds*) realloc(tokens,sizeof(sds)*slots);
523 if (newtokens == NULL) goto cleanup;
524 tokens = newtokens;
526 /* search the separator */
527 if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
528 tokens[elements] = sdsnewlen(s+start,j-start);
529 if (tokens[elements] == NULL) goto cleanup;
530 elements++;
531 start = j+seplen;
532 j = j+seplen-1; /* skip the separator */
535 /* Add the final element. We are sure there is room in the tokens array. */
536 tokens[elements] = sdsnewlen(s+start,len-start);
537 if (tokens[elements] == NULL) goto cleanup;
538 elements++;
539 *count = elements;
540 return tokens;
542 cleanup:
544 int i;
545 for (i = 0; i < elements; i++) sdsfree(tokens[i]);
546 free(tokens);
547 *count = 0;
548 return NULL;
552 /* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */
553 void sdsfreesplitres(sds *tokens, int count) {
554 if (!tokens) return;
555 while(count--)
556 sdsfree(tokens[count]);
557 free(tokens);
560 /* Create an sds string from a long long value. It is much faster than:
562 * sdscatprintf(sdsempty(),"%lld\n", value);
564 sds sdsfromlonglong(long long value) {
565 char buf[32], *p;
566 unsigned long long v;
568 v = (value < 0) ? -value : value;
569 p = buf+31; /* point to the last character */
570 do {
571 *p-- = '0'+(v%10);
572 v /= 10;
573 } while(v);
574 if (value < 0) *p-- = '-';
575 p++;
576 return sdsnewlen(p,32-(p-buf));
579 /* Append to the sds string "s" an escaped string representation where
580 * all the non-printable characters (tested with isprint()) are turned into
581 * escapes in the form "\n\r\a...." or "\x<hex-number>".
583 * After the call, the modified sds string is no longer valid and all the
584 * references must be substituted with the new pointer returned by the call. */
585 sds sdscatrepr(sds s, const char *p, size_t len) {
586 s = sdscatlen(s,"\"",1);
587 while(len--) {
588 switch(*p) {
589 case '\\':
590 case '"':
591 s = sdscatprintf(s,"\\%c",*p);
592 break;
593 case '\n': s = sdscatlen(s,"\\n",2); break;
594 case '\r': s = sdscatlen(s,"\\r",2); break;
595 case '\t': s = sdscatlen(s,"\\t",2); break;
596 case '\a': s = sdscatlen(s,"\\a",2); break;
597 case '\b': s = sdscatlen(s,"\\b",2); break;
598 default:
599 if (isprint((unsigned char)*p))
600 s = sdscatprintf(s,"%c",*p);
601 else
602 s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
603 break;
605 p++;
607 return sdscatlen(s,"\"",1);
610 /* Helper function for sdssplitargs() that returns non zero if 'c'
611 * is a valid hex digit. */
612 int is_hex_digit(char c) {
613 return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
614 (c >= 'A' && c <= 'F');
617 /* Helper function for sdssplitargs() that converts a hex digit into an
618 * integer from 0 to 15 */
619 int hex_digit_to_int(char c) {
620 switch(c) {
621 case '0': return 0;
622 case '1': return 1;
623 case '2': return 2;
624 case '3': return 3;
625 case '4': return 4;
626 case '5': return 5;
627 case '6': return 6;
628 case '7': return 7;
629 case '8': return 8;
630 case '9': return 9;
631 case 'a': case 'A': return 10;
632 case 'b': case 'B': return 11;
633 case 'c': case 'C': return 12;
634 case 'd': case 'D': return 13;
635 case 'e': case 'E': return 14;
636 case 'f': case 'F': return 15;
637 default: return 0;
641 /* Split a line into arguments, where every argument can be in the
642 * following programming-language REPL-alike form:
644 * foo bar "newline are supported\n" and "\xff\x00otherstuff"
646 * The number of arguments is stored into *argc, and an array
647 * of sds is returned.
649 * The caller should free the resulting array of sds strings with
650 * sdsfreesplitres().
652 * Note that sdscatrepr() is able to convert back a string into
653 * a quoted string in the same format sdssplitargs() is able to parse.
655 * The function returns the allocated tokens on success, even when the
656 * input string is empty, or NULL if the input contains unbalanced
657 * quotes or closed quotes followed by non space characters
658 * as in: "foo"bar or "foo'
660 sds *sdssplitargs(const char *line, int *argc) {
661 const char *p = line;
662 char *current = NULL;
663 char **vector = NULL;
665 *argc = 0;
666 while(1) {
667 /* skip blanks */
668 while(*p && isspace((unsigned char)*p)) p++;
669 if (*p) {
670 /* get a token */
671 int inq=0; /* set to 1 if we are in "quotes" */
672 int insq=0; /* set to 1 if we are in 'single quotes' */
673 int done=0;
675 if (current == NULL) current = sdsempty();
676 while(!done) {
677 if (inq) {
678 if (*p == '\\' && *(p+1) == 'x' &&
679 is_hex_digit(*(p+2)) &&
680 is_hex_digit(*(p+3)))
682 unsigned char byte;
684 byte = (hex_digit_to_int(*(p+2))*16)+
685 hex_digit_to_int(*(p+3));
686 current = sdscatlen(current,(char*)&byte,1);
687 p += 3;
688 } else if (*p == '\\' && *(p+1)) {
689 char c;
691 p++;
692 switch(*p) {
693 case 'n': c = '\n'; break;
694 case 'r': c = '\r'; break;
695 case 't': c = '\t'; break;
696 case 'b': c = '\b'; break;
697 case 'a': c = '\a'; break;
698 default: c = *p; break;
700 current = sdscatlen(current,&c,1);
701 } else if (*p == '"') {
702 /* closing quote must be followed by a space or
703 * nothing at all. */
704 if (*(p+1) && !isspace((unsigned char)*(p+1))) goto err;
705 done=1;
706 } else if (!*p) {
707 /* unterminated quotes */
708 goto err;
709 } else {
710 current = sdscatlen(current,p,1);
712 } else if (insq) {
713 if (*p == '\\' && *(p+1) == '\'') {
714 p++;
715 current = sdscatlen(current,"'",1);
716 } else if (*p == '\'') {
717 /* closing quote must be followed by a space or
718 * nothing at all. */
719 if (*(p+1) && !isspace((unsigned char)*(p+1))) goto err;
720 done=1;
721 } else if (!*p) {
722 /* unterminated quotes */
723 goto err;
724 } else {
725 current = sdscatlen(current,p,1);
727 } else {
728 switch(*p) {
729 case ' ':
730 case '\n':
731 case '\r':
732 case '\t':
733 case '\0':
734 done=1;
735 break;
736 case '"':
737 inq=1;
738 break;
739 case '\'':
740 insq=1;
741 break;
742 default:
743 current = sdscatlen(current,p,1);
744 break;
747 if (*p) p++;
749 /* add the token to the vector */
750 vector = (char**) realloc(vector,((*argc)+1)*sizeof(char*));
751 vector[*argc] = current;
752 (*argc)++;
753 current = NULL;
754 } else {
755 /* Even on empty input string return something not NULL. */
756 if (vector == NULL) vector = (char**) malloc(sizeof(void*));
757 return vector;
761 err:
762 while((*argc)--)
763 sdsfree(vector[*argc]);
764 free(vector);
765 if (current) sdsfree(current);
766 *argc = 0;
767 return NULL;
770 /* Modify the string substituting all the occurrences of the set of
771 * characters specified in the 'from' string to the corresponding character
772 * in the 'to' array.
774 * For instance: sdsmapchars(mystring, "ho", "01", 2)
775 * will have the effect of turning the string "hello" into "0ell1".
777 * The function returns the sds string pointer, that is always the same
778 * as the input pointer since no resize is needed. */
779 sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {
780 size_t j, i, l = sdslen(s);
782 for (j = 0; j < l; j++) {
783 for (i = 0; i < setlen; i++) {
784 if (s[j] == from[i]) {
785 s[j] = to[i];
786 break;
790 return s;
793 /* Join an array of C strings using the specified separator (also a C string).
794 * Returns the result as an sds string. */
795 sds sdsjoin(char **argv, int argc, char *sep, size_t seplen) {
796 sds join = sdsempty();
797 int j;
799 for (j = 0; j < argc; j++) {
800 join = sdscat(join, argv[j]);
801 if (j != argc-1) join = sdscatlen(join,sep,seplen);
803 return join;
806 /* Like sdsjoin, but joins an array of SDS strings. */
807 sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {
808 sds join = sdsempty();
809 int j;
811 for (j = 0; j < argc; j++) {
812 join = sdscatsds(join, argv[j]);
813 if (j != argc-1) join = sdscatlen(join,sep,seplen);
815 return join;
818 #ifdef SDS_TEST_MAIN
819 #include <stdio.h>
820 #include "testhelp.h"
822 int main(void) {
824 sdshdr *sh;
825 sds x = sdsnew("foo"), y;
827 test_cond("Create a string and obtain the length",
828 sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0)
830 sdsfree(x);
831 x = sdsnewlen("foo",2);
832 test_cond("Create a string with specified length",
833 sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0)
835 x = sdscat(x,"bar");
836 test_cond("Strings concatenation",
837 sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0);
839 x = sdscpy(x,"a");
840 test_cond("sdscpy() against an originally longer string",
841 sdslen(x) == 1 && memcmp(x,"a\0",2) == 0)
843 x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk");
844 test_cond("sdscpy() against an originally shorter string",
845 sdslen(x) == 33 &&
846 memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0)
848 sdsfree(x);
849 x = sdscatprintf(sdsempty(),"%d",123);
850 test_cond("sdscatprintf() seems working in the base case",
851 sdslen(x) == 3 && memcmp(x,"123\0",4) ==0)
853 sdsfree(x);
854 x = sdsnew("xxciaoyyy");
855 sdstrim(x,"xy");
856 test_cond("sdstrim() correctly trims characters",
857 sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)
859 y = sdsdup(x);
860 sdsrange(y,1,1);
861 test_cond("sdsrange(...,1,1)",
862 sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)
864 sdsfree(y);
865 y = sdsdup(x);
866 sdsrange(y,1,-1);
867 test_cond("sdsrange(...,1,-1)",
868 sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
870 sdsfree(y);
871 y = sdsdup(x);
872 sdsrange(y,-2,-1);
873 test_cond("sdsrange(...,-2,-1)",
874 sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)
876 sdsfree(y);
877 y = sdsdup(x);
878 sdsrange(y,2,1);
879 test_cond("sdsrange(...,2,1)",
880 sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
882 sdsfree(y);
883 y = sdsdup(x);
884 sdsrange(y,1,100);
885 test_cond("sdsrange(...,1,100)",
886 sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
888 sdsfree(y);
889 y = sdsdup(x);
890 sdsrange(y,100,100);
891 test_cond("sdsrange(...,100,100)",
892 sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
894 sdsfree(y);
895 sdsfree(x);
896 x = sdsnew("foo");
897 y = sdsnew("foa");
898 test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0)
900 sdsfree(y);
901 sdsfree(x);
902 x = sdsnew("bar");
903 y = sdsnew("bar");
904 test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0)
906 sdsfree(y);
907 sdsfree(x);
908 x = sdsnew("aar");
909 y = sdsnew("bar");
910 test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
912 sdsfree(y);
913 sdsfree(x);
914 x = sdsnewlen("\a\n\0foo\r",7);
915 y = sdscatrepr(sdsempty(),x,sdslen(x));
916 test_cond("sdscatrepr(...data...)",
917 memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0)
920 int oldfree;
922 sdsfree(x);
923 x = sdsnew("0");
924 sh = sds_start(x);
925 test_cond("sdsnew() free/len buffers", sh->len == 1 && sh->free == 0);
926 x = sdsMakeRoomFor(x,1);
927 sh = sds_start(x);
928 test_cond("sdsMakeRoomFor()", sh->len == 1 && sh->free > 0);
929 oldfree = sh->free;
930 x[1] = '1';
931 sdsIncrLen(x,1);
932 test_cond("sdsIncrLen() -- content", x[0] == '0' && x[1] == '1');
933 test_cond("sdsIncrLen() -- len", sh->len == 2);
934 test_cond("sdsIncrLen() -- free", sh->free == oldfree-1);
937 x = sdsnew("0FoO1bar\n");
938 sdstolower(x);
939 test_cond("sdstolower(...)",
940 memcmp(x,"0foo1bar\n\0",10) == 0)
942 sdsfree(x);
944 x = sdsnew("0FoO1bar\n");
945 sdstoupper(x);
946 test_cond("sdstoupper(...)",
947 memcmp(x,"0FOO1BAR\n\0",10) == 0)
949 sdsfree(x);
951 test_report()
952 return 0;
954 #endif