1 /* SDS (Simple Dynamic Strings), A C dynamic strings library.
3 * Copyright (c) 2006-2014, Salvatore Sanfilippo <antirez at gmail dot com>
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.
39 /* Create a new sds string with the content specified by the 'init' pointer
41 * If NULL is used for 'init' the string is initialized with zero bytes.
43 * The string is always null-termined (all the sds strings are, always) so
44 * even if you create an sds string with:
46 * mystring = sdsnewlen("abc",3");
48 * You can print the string with printf() as there is an implicit \0 at the
49 * end of the string. However the string is binary safe and can contain
50 * \0 characters in the middle, as the length is stored in the sds header. */
51 sds sdsnewlen(const void *init, size_t initlen) {
55 sh = malloc(sizeof *sh+initlen+1);
57 sh = calloc(sizeof *sh+initlen+1,1);
59 if (sh == NULL) return NULL;
63 memcpy(sh->buf, init, initlen);
64 sh->buf[initlen] = '\0';
65 return (char*)sh->buf;
68 /* Create an empty (zero length) sds string. Even in this case the string
69 * always has an implicit null term. */
71 return sdsnewlen("",0);
74 /* Create a new sds string starting from a null termined C string. */
75 sds sdsnew(const char *init) {
76 size_t initlen = (init == NULL) ? 0 : strlen(init);
77 return sdsnewlen(init, initlen);
80 /* Duplicate an sds string. */
81 sds sdsdup(const sds s) {
82 return sdsnewlen(s, sdslen(s));
85 /* Free an sds string. No operation is performed if 's' is NULL. */
87 if (s == NULL) return;
88 free(s-sizeof(struct sdshdr));
91 /* Set the sds string length to the length as obtained with strlen(), so
92 * considering as content only up to the first null term character.
94 * This function is useful when the sds string is hacked manually in some
95 * way, like in the following example:
97 * s = sdsnew("foobar");
100 * printf("%d\n", sdslen(s));
102 * The output will be "2", but if we comment out the call to sdsupdatelen()
103 * the output will be "6" as the string was modified but the logical length
104 * remains 6 bytes. */
105 void sdsupdatelen(sds s) {
106 struct sdshdr *sh = (void*) (s-sizeof *sh);
107 int reallen = strlen(s);
108 sh->free += (sh->len-reallen);
112 /* Modify an sds string on-place to make it empty (zero length).
113 * However all the existing buffer is not discarded but set as free space
114 * so that next append operations will not require allocations up to the
115 * number of bytes previously available. */
116 void sdsclear(sds s) {
117 struct sdshdr *sh = (void*) (s-sizeof *sh);
123 /* Enlarge the free space at the end of the sds string so that the caller
124 * is sure that after calling this function can overwrite up to addlen
125 * bytes after the end of the string, plus one more byte for nul term.
127 * Note: this does not change the *length* of the sds string as returned
128 * by sdslen(), but only the free buffer space we have. */
129 sds sdsMakeRoomFor(sds s, size_t addlen) {
130 struct sdshdr *sh, *newsh;
131 size_t free = sdsavail(s);
134 if (free >= addlen) return s;
136 sh = (void*) (s-sizeof *sh);
137 newlen = (len+addlen);
138 if (newlen < SDS_MAX_PREALLOC)
141 newlen += SDS_MAX_PREALLOC;
142 newsh = realloc(sh, sizeof *newsh+newlen+1);
143 if (newsh == NULL) return NULL;
145 newsh->free = newlen - len;
149 /* Reallocate the sds string so that it has no free space at the end. The
150 * contained string remains not altered, but next concatenation operations
151 * will require a reallocation.
153 * After the call, the passed sds string is no longer valid and all the
154 * references must be substituted with the new pointer returned by the call. */
155 sds sdsRemoveFreeSpace(sds s) {
158 sh = (void*) (s-sizeof *sh);
159 sh = realloc(sh, sizeof *sh+sh->len+1);
164 /* Return the total size of the allocation of the specifed sds string,
166 * 1) The sds header before the pointer.
168 * 3) The free buffer at the end if any.
169 * 4) The implicit null term.
171 size_t sdsAllocSize(sds s) {
172 struct sdshdr *sh = (void*) (s-sizeof *sh);
174 return sizeof(*sh)+sh->len+sh->free+1;
177 /* Increment the sds length and decrements the left free space at the
178 * end of the string according to 'incr'. Also set the null term
179 * in the new end of the string.
181 * This function is used in order to fix the string length after the
182 * user calls sdsMakeRoomFor(), writes something after the end of
183 * the current string, and finally needs to set the new length.
185 * Note: it is possible to use a negative increment in order to
186 * right-trim the string.
190 * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the
191 * following schema, to cat bytes coming from the kernel to the end of an
192 * sds string without copying into an intermediate buffer:
194 * oldlen = sdslen(s);
195 * s = sdsMakeRoomFor(s, BUFFER_SIZE);
196 * nread = read(fd, s+oldlen, BUFFER_SIZE);
197 * ... check for nread <= 0 and handle it ...
198 * sdsIncrLen(s, nread);
200 void sdsIncrLen(sds s, int incr) {
201 struct sdshdr *sh = (void*) (s-sizeof *sh);
203 assert(sh->free >= incr);
206 assert(sh->free >= 0);
210 /* Grow the sds to have the specified length. Bytes that were not part of
211 * the original length of the sds will be set to zero.
213 * if the specified length is smaller than the current length, no operation
215 sds sdsgrowzero(sds s, size_t len) {
216 struct sdshdr *sh = (void*) (s-sizeof *sh);
217 size_t totlen, curlen = sh->len;
219 if (len <= curlen) return s;
220 s = sdsMakeRoomFor(s,len-curlen);
221 if (s == NULL) return NULL;
223 /* Make sure added region doesn't contain garbage */
224 sh = (void*)(s-sizeof *sh);
225 memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
226 totlen = sh->len+sh->free;
228 sh->free = totlen-sh->len;
232 /* Append the specified binary-safe string pointed by 't' of 'len' bytes to the
233 * end of the specified sds string 's'.
235 * After the call, the passed sds string is no longer valid and all the
236 * references must be substituted with the new pointer returned by the call. */
237 sds sdscatlen(sds s, const void *t, size_t len) {
239 size_t curlen = sdslen(s);
241 s = sdsMakeRoomFor(s,len);
242 if (s == NULL) return NULL;
243 sh = (void*) (s-sizeof *sh);
244 memcpy(s+curlen, t, len);
245 sh->len = curlen+len;
246 sh->free = sh->free-len;
247 s[curlen+len] = '\0';
251 /* Append the specified null termianted C string to the sds string 's'.
253 * After the call, the passed sds string is no longer valid and all the
254 * references must be substituted with the new pointer returned by the call. */
255 sds sdscat(sds s, const char *t) {
256 return sdscatlen(s, t, strlen(t));
259 /* Append the specified sds 't' to the existing sds 's'.
261 * After the call, the modified sds string is no longer valid and all the
262 * references must be substituted with the new pointer returned by the call. */
263 sds sdscatsds(sds s, const sds t) {
264 return sdscatlen(s, t, sdslen(t));
267 /* Destructively modify the sds string 's' to hold the specified binary
268 * safe string pointed by 't' of length 'len' bytes. */
269 sds sdscpylen(sds s, const char *t, size_t len) {
270 struct sdshdr *sh = (void*) (s-sizeof *sh);
271 size_t totlen = sh->free+sh->len;
274 s = sdsMakeRoomFor(s,len-sh->len);
275 if (s == NULL) return NULL;
276 sh = (void*) (s-sizeof *sh);
277 totlen = sh->free+sh->len;
282 sh->free = totlen-len;
286 /* Like sdscpylen() but 't' must be a null-termined string so that the length
287 * of the string is obtained with strlen(). */
288 sds sdscpy(sds s, const char *t) {
289 return sdscpylen(s, t, strlen(t));
292 /* Helper for sdscatlonglong() doing the actual number -> string
293 * conversion. 's' must point to a string with room for at least
294 * SDS_LLSTR_SIZE bytes.
296 * The function returns the lenght of the null-terminated string
297 * representation stored at 's'. */
298 #define SDS_LLSTR_SIZE 21
299 int sdsll2str(char *s, long long value) {
301 unsigned long long v;
304 /* Generate the string representation, this method produces
305 * an reversed string. */
306 v = (value < 0) ? -value : value;
312 if (value < 0) *p++ = '-';
314 /* Compute length and add null term. */
318 /* Reverse the string. */
330 /* Identical sdsll2str(), but for unsigned long long type. */
331 int sdsull2str(char *s, unsigned long long v) {
335 /* Generate the string representation, this method produces
336 * an reversed string. */
343 /* Compute length and add null term. */
347 /* Reverse the string. */
359 /* Like sdscatpritf() but gets va_list instead of being variadic. */
360 sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
366 buf = malloc(buflen);
367 if (buf == NULL) return NULL;
368 buf[buflen-2] = '\0';
370 vsnprintf(buf, buflen, fmt, cpy);
371 if (buf[buflen-2] != '\0') {
383 /* Append to the sds string 's' a string obtained using printf-alike format
386 * After the call, the modified sds string is no longer valid and all the
387 * references must be substituted with the new pointer returned by the call.
391 * s = sdsnew("Sum is: ");
392 * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b);
394 * Often you need to create a string from scratch with the printf-alike
395 * format. When this is the need, just use sdsempty() as the target string:
397 * s = sdscatprintf(sdsempty(), "... your format ...", args);
399 sds sdscatprintf(sds s, const char *fmt, ...) {
403 t = sdscatvprintf(s,fmt,ap);
408 /* This function is similar to sdscatprintf, but much faster as it does
409 * not rely on sprintf() family functions implemented by the libc that
410 * are often very slow. Moreover directly handling the sds string as
411 * new data is concatenated provides a performance improvement.
413 * However this function only handles an incompatible subset of printf-alike
419 * %I - 64 bit signed integer (long long, int64_t)
421 * %U - 64 bit unsigned integer (unsigned long long, uint64_t)
422 * %T - A size_t variable.
423 * %% - Verbatim "%" character.
425 sds sdscatfmt(sds s, char const *fmt, ...) {
426 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
427 size_t initlen = sdslen(s);
433 f = fmt; /* Next format specifier byte to process. */
434 i = initlen; /* Position of the next byte to write to dest str. */
439 unsigned long long unum;
441 /* Make sure there is always space for at least 1 char. */
443 s = sdsMakeRoomFor(s,1);
444 sh = (void*) (s-(sizeof(struct sdshdr)));
454 str = va_arg(ap,char*);
455 l = (next == 's') ? strlen(str) : sdslen(str);
457 s = sdsMakeRoomFor(s,l);
458 sh = (void*) (s-(sizeof(struct sdshdr)));
468 num = va_arg(ap,int);
470 num = va_arg(ap,long long);
472 char buf[SDS_LLSTR_SIZE];
473 l = sdsll2str(buf,num);
475 s = sdsMakeRoomFor(s,l);
476 sh = (void*) (s-(sizeof(struct sdshdr)));
488 unum = va_arg(ap,unsigned int);
490 unum = va_arg(ap,unsigned long long);
492 unum = (unsigned long long)va_arg(ap,size_t);
494 char buf[SDS_LLSTR_SIZE];
495 l = sdsull2str(buf,unum);
497 s = sdsMakeRoomFor(s,l);
498 sh = (void*) (s-(sizeof(struct sdshdr)));
506 default: /* Handle %% and generally %<unknown>. */
529 /* Remove the part of the string from left and from right composed just of
530 * contiguous characters found in 'cset', that is a null terminted C string.
532 * After the call, the modified sds string is no longer valid and all the
533 * references must be substituted with the new pointer returned by the call.
537 * s = sdsnew("AA...AA.a.aa.aHelloWorld :::");
538 * s = sdstrim(s,"A. :");
541 * Output will be just "Hello World".
543 void sdstrim(sds s, const char *cset) {
544 struct sdshdr *sh = (void*) (s-sizeof *sh);
545 char *start, *end, *sp, *ep;
549 ep = end = s+sdslen(s)-1;
550 while(sp <= end && strchr(cset, *sp)) sp++;
551 while(ep > start && strchr(cset, *ep)) ep--;
552 len = (sp > ep) ? 0 : ((ep-sp)+1);
553 if (sh->buf != sp) memmove(sh->buf, sp, len);
555 sh->free = sh->free+(sh->len-len);
559 /* Turn the string into a smaller (or equal) string containing only the
560 * substring specified by the 'start' and 'end' indexes.
562 * start and end can be negative, where -1 means the last character of the
563 * string, -2 the penultimate character, and so forth.
565 * The interval is inclusive, so the start and end characters will be part
566 * of the resulting string.
568 * The string is modified in-place.
572 * s = sdsnew("Hello World");
573 * sdsrange(s,1,-1); => "ello World"
575 void sdsrange(sds s, int start, int end) {
576 struct sdshdr *sh = (void*) (s-sizeof *sh);
577 size_t newlen, len = sdslen(s);
579 if (len == 0) return;
582 if (start < 0) start = 0;
586 if (end < 0) end = 0;
588 newlen = (start > end) ? 0 : (end-start)+1;
590 if (start >= (signed)len) {
592 } else if (end >= (signed)len) {
594 newlen = (start > end) ? 0 : (end-start)+1;
599 if (start && newlen) memmove(sh->buf, sh->buf+start, newlen);
601 sh->free = sh->free+(sh->len-newlen);
605 /* Apply tolower() to every character of the sds string 's'. */
606 void sdstolower(sds s) {
607 int len = sdslen(s), j;
609 for (j = 0; j < len; j++) s[j] = tolower(s[j]);
612 /* Apply toupper() to every character of the sds string 's'. */
613 void sdstoupper(sds s) {
614 int len = sdslen(s), j;
616 for (j = 0; j < len; j++) s[j] = toupper(s[j]);
619 /* Compare two sds strings s1 and s2 with memcmp().
625 * 0 if s1 and s2 are exactly the same binary string.
627 * If two strings share exactly the same prefix, but one of the two has
628 * additional characters, the longer string is considered to be greater than
629 * the smaller one. */
630 int sdscmp(const sds s1, const sds s2) {
631 size_t l1, l2, minlen;
636 minlen = (l1 < l2) ? l1 : l2;
637 cmp = memcmp(s1,s2,minlen);
638 if (cmp == 0) return l1-l2;
642 /* Split 's' with separator in 'sep'. An array
643 * of sds strings is returned. *count will be set
644 * by reference to the number of tokens returned.
646 * On out of memory, zero length string, zero length
647 * separator, NULL is returned.
649 * Note that 'sep' is able to split a string using
650 * a multi-character separator. For example
651 * sdssplit("foo_-_bar","_-_"); will return two
652 * elements "foo" and "bar".
654 * This version of the function is binary-safe but
655 * requires length arguments. sdssplit() is just the
656 * same function but for zero-terminated strings.
658 sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) {
659 int elements = 0, slots = 5, start = 0, j;
662 if (seplen < 1 || len < 0) return NULL;
664 tokens = malloc(sizeof(sds)*slots);
665 if (tokens == NULL) return NULL;
671 for (j = 0; j < (len-(seplen-1)); j++) {
672 /* make sure there is room for the next element and the final one */
673 if (slots < elements+2) {
677 newtokens = realloc(tokens,sizeof(sds)*slots);
678 if (newtokens == NULL) goto cleanup;
681 /* search the separator */
682 if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
683 tokens[elements] = sdsnewlen(s+start,j-start);
684 if (tokens[elements] == NULL) goto cleanup;
687 j = j+seplen-1; /* skip the separator */
690 /* Add the final element. We are sure there is room in the tokens array. */
691 tokens[elements] = sdsnewlen(s+start,len-start);
692 if (tokens[elements] == NULL) goto cleanup;
700 for (i = 0; i < elements; i++) sdsfree(tokens[i]);
707 /* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */
708 void sdsfreesplitres(sds *tokens, int count) {
711 sdsfree(tokens[count]);
715 /* Create an sds string from a long long value. It is much faster than:
717 * sdscatprintf(sdsempty(),"%lld\n", value);
719 sds sdsfromlonglong(long long value) {
721 unsigned long long v;
723 v = (value < 0) ? -value : value;
724 p = buf+31; /* point to the last character */
729 if (value < 0) *p-- = '-';
731 return sdsnewlen(p,32-(p-buf));
734 /* Append to the sds string "s" an escaped string representation where
735 * all the non-printable characters (tested with isprint()) are turned into
736 * escapes in the form "\n\r\a...." or "\x<hex-number>".
738 * After the call, the modified sds string is no longer valid and all the
739 * references must be substituted with the new pointer returned by the call. */
740 sds sdscatrepr(sds s, const char *p, size_t len) {
741 s = sdscatlen(s,"\"",1);
746 s = sdscatprintf(s,"\\%c",*p);
748 case '\n': s = sdscatlen(s,"\\n",2); break;
749 case '\r': s = sdscatlen(s,"\\r",2); break;
750 case '\t': s = sdscatlen(s,"\\t",2); break;
751 case '\a': s = sdscatlen(s,"\\a",2); break;
752 case '\b': s = sdscatlen(s,"\\b",2); break;
755 s = sdscatprintf(s,"%c",*p);
757 s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
762 return sdscatlen(s,"\"",1);
765 /* Helper function for sdssplitargs() that returns non zero if 'c'
766 * is a valid hex digit. */
767 int is_hex_digit(char c) {
768 return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
769 (c >= 'A' && c <= 'F');
772 /* Helper function for sdssplitargs() that converts a hex digit into an
773 * integer from 0 to 15 */
774 int hex_digit_to_int(char c) {
786 case 'a': case 'A': return 10;
787 case 'b': case 'B': return 11;
788 case 'c': case 'C': return 12;
789 case 'd': case 'D': return 13;
790 case 'e': case 'E': return 14;
791 case 'f': case 'F': return 15;
796 /* Split a line into arguments, where every argument can be in the
797 * following programming-language REPL-alike form:
799 * foo bar "newline are supported\n" and "\xff\x00otherstuff"
801 * The number of arguments is stored into *argc, and an array
802 * of sds is returned.
804 * The caller should free the resulting array of sds strings with
807 * Note that sdscatrepr() is able to convert back a string into
808 * a quoted string in the same format sdssplitargs() is able to parse.
810 * The function returns the allocated tokens on success, even when the
811 * input string is empty, or NULL if the input contains unbalanced
812 * quotes or closed quotes followed by non space characters
813 * as in: "foo"bar or "foo'
815 sds *sdssplitargs(const char *line, int *argc) {
816 const char *p = line;
817 char *current = NULL;
818 char **vector = NULL;
823 while(*p && isspace(*p)) p++;
826 int inq=0; /* set to 1 if we are in "quotes" */
827 int insq=0; /* set to 1 if we are in 'single quotes' */
830 if (current == NULL) current = sdsempty();
833 if (*p == '\\' && *(p+1) == 'x' &&
834 is_hex_digit(*(p+2)) &&
835 is_hex_digit(*(p+3)))
839 byte = (hex_digit_to_int(*(p+2))*16)+
840 hex_digit_to_int(*(p+3));
841 current = sdscatlen(current,(char*)&byte,1);
843 } else if (*p == '\\' && *(p+1)) {
848 case 'n': c = '\n'; break;
849 case 'r': c = '\r'; break;
850 case 't': c = '\t'; break;
851 case 'b': c = '\b'; break;
852 case 'a': c = '\a'; break;
853 default: c = *p; break;
855 current = sdscatlen(current,&c,1);
856 } else if (*p == '"') {
857 /* closing quote must be followed by a space or
859 if (*(p+1) && !isspace(*(p+1))) goto err;
862 /* unterminated quotes */
865 current = sdscatlen(current,p,1);
868 if (*p == '\\' && *(p+1) == '\'') {
870 current = sdscatlen(current,"'",1);
871 } else if (*p == '\'') {
872 /* closing quote must be followed by a space or
874 if (*(p+1) && !isspace(*(p+1))) goto err;
877 /* unterminated quotes */
880 current = sdscatlen(current,p,1);
898 current = sdscatlen(current,p,1);
904 /* add the token to the vector */
905 vector = realloc(vector,((*argc)+1)*sizeof(char*));
906 vector[*argc] = current;
910 /* Even on empty input string return something not NULL. */
911 if (vector == NULL) vector = malloc(sizeof(void*));
918 sdsfree(vector[*argc]);
920 if (current) sdsfree(current);
925 /* Modify the string substituting all the occurrences of the set of
926 * characters specified in the 'from' string to the corresponding character
929 * For instance: sdsmapchars(mystring, "ho", "01", 2)
930 * will have the effect of turning the string "hello" into "0ell1".
932 * The function returns the sds string pointer, that is always the same
933 * as the input pointer since no resize is needed. */
934 sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {
935 size_t j, i, l = sdslen(s);
937 for (j = 0; j < l; j++) {
938 for (i = 0; i < setlen; i++) {
939 if (s[j] == from[i]) {
948 /* Join an array of C strings using the specified separator (also a C string).
949 * Returns the result as an sds string. */
950 sds sdsjoin(char **argv, int argc, char *sep, size_t seplen) {
951 sds join = sdsempty();
954 for (j = 0; j < argc; j++) {
955 join = sdscat(join, argv[j]);
956 if (j != argc-1) join = sdscatlen(join,sep,seplen);
961 /* Like sdsjoin, but joins an array of SDS strings. */
962 sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {
963 sds join = sdsempty();
966 for (j = 0; j < argc; j++) {
967 join = sdscatsds(join, argv[j]);
968 if (j != argc-1) join = sdscatlen(join,sep,seplen);
975 #include "testhelp.h"
980 sds x = sdsnew("foo"), y;
982 test_cond("Create a string and obtain the length",
983 sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0)
986 x = sdsnewlen("foo",2);
987 test_cond("Create a string with specified length",
988 sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0)
991 test_cond("Strings concatenation",
992 sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0);
995 test_cond("sdscpy() against an originally longer string",
996 sdslen(x) == 1 && memcmp(x,"a\0",2) == 0)
998 x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk");
999 test_cond("sdscpy() against an originally shorter string",
1001 memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0)
1004 x = sdscatprintf(sdsempty(),"%d",123);
1005 test_cond("sdscatprintf() seems working in the base case",
1006 sdslen(x) == 3 && memcmp(x,"123\0",4) ==0)
1009 x = sdsnew("xxciaoyyy");
1011 test_cond("sdstrim() correctly trims characters",
1012 sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)
1016 test_cond("sdsrange(...,1,1)",
1017 sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)
1022 test_cond("sdsrange(...,1,-1)",
1023 sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
1028 test_cond("sdsrange(...,-2,-1)",
1029 sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)
1034 test_cond("sdsrange(...,2,1)",
1035 sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
1040 test_cond("sdsrange(...,1,100)",
1041 sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
1045 sdsrange(y,100,100);
1046 test_cond("sdsrange(...,100,100)",
1047 sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
1053 test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0)
1059 test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0)
1065 test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
1069 x = sdsnewlen("\a\n\0foo\r",7);
1070 y = sdscatrepr(sdsempty(),x,sdslen(x));
1071 test_cond("sdscatrepr(...data...)",
1072 memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0)
1079 sh = (void*) (x-(sizeof(struct sdshdr)));
1080 test_cond("sdsnew() free/len buffers", sh->len == 1 && sh->free == 0);
1081 x = sdsMakeRoomFor(x,1);
1082 sh = (void*) (x-(sizeof(struct sdshdr)));
1083 test_cond("sdsMakeRoomFor()", sh->len == 1 && sh->free > 0);
1087 test_cond("sdsIncrLen() -- content", x[0] == '0' && x[1] == '1');
1088 test_cond("sdsIncrLen() -- len", sh->len == 2);
1089 test_cond("sdsIncrLen() -- free", sh->free == oldfree-1);