xref: /redis-3.2.3/src/sds.c (revision 60323407)
1 /* SDSLib 2.0 -- A C dynamic strings library
2  *
3  * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
4  * Copyright (c) 2015, Oran Agra
5  * Copyright (c) 2015, Redis Labs, Inc
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  *   * Redistributions of source code must retain the above copyright notice,
12  *     this list of conditions and the following disclaimer.
13  *   * Redistributions in binary form must reproduce the above copyright
14  *     notice, this list of conditions and the following disclaimer in the
15  *     documentation and/or other materials provided with the distribution.
16  *   * Neither the name of Redis nor the names of its contributors may be used
17  *     to endorse or promote products derived from this software without
18  *     specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <assert.h>
38 #include "sds.h"
39 #include "sdsalloc.h"
40 
sdsHdrSize(char type)41 static inline int sdsHdrSize(char type) {
42     switch(type&SDS_TYPE_MASK) {
43         case SDS_TYPE_5:
44             return sizeof(struct sdshdr5);
45         case SDS_TYPE_8:
46             return sizeof(struct sdshdr8);
47         case SDS_TYPE_16:
48             return sizeof(struct sdshdr16);
49         case SDS_TYPE_32:
50             return sizeof(struct sdshdr32);
51         case SDS_TYPE_64:
52             return sizeof(struct sdshdr64);
53     }
54     return 0;
55 }
56 
sdsReqType(size_t string_size)57 static inline char sdsReqType(size_t string_size) {
58     if (string_size < 1<<5)
59         return SDS_TYPE_5;
60     if (string_size < 1<<8)
61         return SDS_TYPE_8;
62     if (string_size < 1<<16)
63         return SDS_TYPE_16;
64     if (string_size < 1ll<<32)
65         return SDS_TYPE_32;
66     return SDS_TYPE_64;
67 }
68 
69 /* Create a new sds string with the content specified by the 'init' pointer
70  * and 'initlen'.
71  * If NULL is used for 'init' the string is initialized with zero bytes.
72  *
73  * The string is always null-termined (all the sds strings are, always) so
74  * even if you create an sds string with:
75  *
76  * mystring = sdsnewlen("abc",3);
77  *
78  * You can print the string with printf() as there is an implicit \0 at the
79  * end of the string. However the string is binary safe and can contain
80  * \0 characters in the middle, as the length is stored in the sds header. */
sdsnewlen(const void * init,size_t initlen)81 sds sdsnewlen(const void *init, size_t initlen) {
82     void *sh;
83     sds s;
84     char type = sdsReqType(initlen);
85     /* Empty strings are usually created in order to append. Use type 8
86      * since type 5 is not good at this. */
87     if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;
88     int hdrlen = sdsHdrSize(type);
89     unsigned char *fp; /* flags pointer. */
90 
91     sh = s_malloc(hdrlen+initlen+1);
92     if (!init)
93         memset(sh, 0, hdrlen+initlen+1);
94     if (sh == NULL) return NULL;
95     s = (char*)sh+hdrlen;
96     fp = ((unsigned char*)s)-1;
97     switch(type) {
98         case SDS_TYPE_5: {
99             *fp = type | (initlen << SDS_TYPE_BITS);
100             break;
101         }
102         case SDS_TYPE_8: {
103             SDS_HDR_VAR(8,s);
104             sh->len = initlen;
105             sh->alloc = initlen;
106             *fp = type;
107             break;
108         }
109         case SDS_TYPE_16: {
110             SDS_HDR_VAR(16,s);
111             sh->len = initlen;
112             sh->alloc = initlen;
113             *fp = type;
114             break;
115         }
116         case SDS_TYPE_32: {
117             SDS_HDR_VAR(32,s);
118             sh->len = initlen;
119             sh->alloc = initlen;
120             *fp = type;
121             break;
122         }
123         case SDS_TYPE_64: {
124             SDS_HDR_VAR(64,s);
125             sh->len = initlen;
126             sh->alloc = initlen;
127             *fp = type;
128             break;
129         }
130     }
131     if (initlen && init)
132         memcpy(s, init, initlen);
133     s[initlen] = '\0';
134     return s;
135 }
136 
137 /* Create an empty (zero length) sds string. Even in this case the string
138  * always has an implicit null term. */
sdsempty(void)139 sds sdsempty(void) {
140     return sdsnewlen("",0);
141 }
142 
143 /* Create a new sds string starting from a null terminated C string. */
sdsnew(const char * init)144 sds sdsnew(const char *init) {
145     size_t initlen = (init == NULL) ? 0 : strlen(init);
146     return sdsnewlen(init, initlen);
147 }
148 
149 /* Duplicate an sds string. */
sdsdup(const sds s)150 sds sdsdup(const sds s) {
151     return sdsnewlen(s, sdslen(s));
152 }
153 
154 /* Free an sds string. No operation is performed if 's' is NULL. */
sdsfree(sds s)155 void sdsfree(sds s) {
156     if (s == NULL) return;
157     s_free((char*)s-sdsHdrSize(s[-1]));
158 }
159 
160 /* Set the sds string length to the length as obtained with strlen(), so
161  * considering as content only up to the first null term character.
162  *
163  * This function is useful when the sds string is hacked manually in some
164  * way, like in the following example:
165  *
166  * s = sdsnew("foobar");
167  * s[2] = '\0';
168  * sdsupdatelen(s);
169  * printf("%d\n", sdslen(s));
170  *
171  * The output will be "2", but if we comment out the call to sdsupdatelen()
172  * the output will be "6" as the string was modified but the logical length
173  * remains 6 bytes. */
sdsupdatelen(sds s)174 void sdsupdatelen(sds s) {
175     int reallen = strlen(s);
176     sdssetlen(s, reallen);
177 }
178 
179 /* Modify an sds string in-place to make it empty (zero length).
180  * However all the existing buffer is not discarded but set as free space
181  * so that next append operations will not require allocations up to the
182  * number of bytes previously available. */
sdsclear(sds s)183 void sdsclear(sds s) {
184     sdssetlen(s, 0);
185     s[0] = '\0';
186 }
187 
188 /* Enlarge the free space at the end of the sds string so that the caller
189  * is sure that after calling this function can overwrite up to addlen
190  * bytes after the end of the string, plus one more byte for nul term.
191  *
192  * Note: this does not change the *length* of the sds string as returned
193  * by sdslen(), but only the free buffer space we have. */
sdsMakeRoomFor(sds s,size_t addlen)194 sds sdsMakeRoomFor(sds s, size_t addlen) {
195     void *sh, *newsh;
196     size_t avail = sdsavail(s);
197     size_t len, newlen;
198     char type, oldtype = s[-1] & SDS_TYPE_MASK;
199     int hdrlen;
200 
201     /* Return ASAP if there is enough space left. */
202     if (avail >= addlen) return s;
203 
204     len = sdslen(s);
205     sh = (char*)s-sdsHdrSize(oldtype);
206     newlen = (len+addlen);
207     if (newlen < SDS_MAX_PREALLOC)
208         newlen *= 2;
209     else
210         newlen += SDS_MAX_PREALLOC;
211 
212     type = sdsReqType(newlen);
213 
214     /* Don't use type 5: the user is appending to the string and type 5 is
215      * not able to remember empty space, so sdsMakeRoomFor() must be called
216      * at every appending operation. */
217     if (type == SDS_TYPE_5) type = SDS_TYPE_8;
218 
219     hdrlen = sdsHdrSize(type);
220     if (oldtype==type) {
221         newsh = s_realloc(sh, hdrlen+newlen+1);
222         if (newsh == NULL) return NULL;
223         s = (char*)newsh+hdrlen;
224     } else {
225         /* Since the header size changes, need to move the string forward,
226          * and can't use realloc */
227         newsh = s_malloc(hdrlen+newlen+1);
228         if (newsh == NULL) return NULL;
229         memcpy((char*)newsh+hdrlen, s, len+1);
230         s_free(sh);
231         s = (char*)newsh+hdrlen;
232         s[-1] = type;
233         sdssetlen(s, len);
234     }
235     sdssetalloc(s, newlen);
236     return s;
237 }
238 
239 /* Reallocate the sds string so that it has no free space at the end. The
240  * contained string remains not altered, but next concatenation operations
241  * will require a reallocation.
242  *
243  * After the call, the passed sds string is no longer valid and all the
244  * references must be substituted with the new pointer returned by the call. */
sdsRemoveFreeSpace(sds s)245 sds sdsRemoveFreeSpace(sds s) {
246     void *sh, *newsh;
247     char type, oldtype = s[-1] & SDS_TYPE_MASK;
248     int hdrlen;
249     size_t len = sdslen(s);
250     sh = (char*)s-sdsHdrSize(oldtype);
251 
252     type = sdsReqType(len);
253     hdrlen = sdsHdrSize(type);
254     if (oldtype==type) {
255         newsh = s_realloc(sh, hdrlen+len+1);
256         if (newsh == NULL) return NULL;
257         s = (char*)newsh+hdrlen;
258     } else {
259         newsh = s_malloc(hdrlen+len+1);
260         if (newsh == NULL) return NULL;
261         memcpy((char*)newsh+hdrlen, s, len+1);
262         s_free(sh);
263         s = (char*)newsh+hdrlen;
264         s[-1] = type;
265         sdssetlen(s, len);
266     }
267     sdssetalloc(s, len);
268     return s;
269 }
270 
271 /* Return the total size of the allocation of the specifed sds string,
272  * including:
273  * 1) The sds header before the pointer.
274  * 2) The string.
275  * 3) The free buffer at the end if any.
276  * 4) The implicit null term.
277  */
sdsAllocSize(sds s)278 size_t sdsAllocSize(sds s) {
279     size_t alloc = sdsalloc(s);
280     return sdsHdrSize(s[-1])+alloc+1;
281 }
282 
283 /* Return the pointer of the actual SDS allocation (normally SDS strings
284  * are referenced by the start of the string buffer). */
sdsAllocPtr(sds s)285 void *sdsAllocPtr(sds s) {
286     return (void*) (s-sdsHdrSize(s[-1]));
287 }
288 
289 /* Increment the sds length and decrements the left free space at the
290  * end of the string according to 'incr'. Also set the null term
291  * in the new end of the string.
292  *
293  * This function is used in order to fix the string length after the
294  * user calls sdsMakeRoomFor(), writes something after the end of
295  * the current string, and finally needs to set the new length.
296  *
297  * Note: it is possible to use a negative increment in order to
298  * right-trim the string.
299  *
300  * Usage example:
301  *
302  * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the
303  * following schema, to cat bytes coming from the kernel to the end of an
304  * sds string without copying into an intermediate buffer:
305  *
306  * oldlen = sdslen(s);
307  * s = sdsMakeRoomFor(s, BUFFER_SIZE);
308  * nread = read(fd, s+oldlen, BUFFER_SIZE);
309  * ... check for nread <= 0 and handle it ...
310  * sdsIncrLen(s, nread);
311  */
sdsIncrLen(sds s,int incr)312 void sdsIncrLen(sds s, int incr) {
313     unsigned char flags = s[-1];
314     size_t len;
315     switch(flags&SDS_TYPE_MASK) {
316         case SDS_TYPE_5: {
317             unsigned char *fp = ((unsigned char*)s)-1;
318             unsigned char oldlen = SDS_TYPE_5_LEN(flags);
319             assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr)));
320             *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS);
321             len = oldlen+incr;
322             break;
323         }
324         case SDS_TYPE_8: {
325             SDS_HDR_VAR(8,s);
326             assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
327             len = (sh->len += incr);
328             break;
329         }
330         case SDS_TYPE_16: {
331             SDS_HDR_VAR(16,s);
332             assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
333             len = (sh->len += incr);
334             break;
335         }
336         case SDS_TYPE_32: {
337             SDS_HDR_VAR(32,s);
338             assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
339             len = (sh->len += incr);
340             break;
341         }
342         case SDS_TYPE_64: {
343             SDS_HDR_VAR(64,s);
344             assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr)));
345             len = (sh->len += incr);
346             break;
347         }
348         default: len = 0; /* Just to avoid compilation warnings. */
349     }
350     s[len] = '\0';
351 }
352 
353 /* Grow the sds to have the specified length. Bytes that were not part of
354  * the original length of the sds will be set to zero.
355  *
356  * if the specified length is smaller than the current length, no operation
357  * is performed. */
sdsgrowzero(sds s,size_t len)358 sds sdsgrowzero(sds s, size_t len) {
359     size_t curlen = sdslen(s);
360 
361     if (len <= curlen) return s;
362     s = sdsMakeRoomFor(s,len-curlen);
363     if (s == NULL) return NULL;
364 
365     /* Make sure added region doesn't contain garbage */
366     memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
367     sdssetlen(s, len);
368     return s;
369 }
370 
371 /* Append the specified binary-safe string pointed by 't' of 'len' bytes to the
372  * end of the specified sds string 's'.
373  *
374  * After the call, the passed sds string is no longer valid and all the
375  * references must be substituted with the new pointer returned by the call. */
sdscatlen(sds s,const void * t,size_t len)376 sds sdscatlen(sds s, const void *t, size_t len) {
377     size_t curlen = sdslen(s);
378 
379     s = sdsMakeRoomFor(s,len);
380     if (s == NULL) return NULL;
381     memcpy(s+curlen, t, len);
382     sdssetlen(s, curlen+len);
383     s[curlen+len] = '\0';
384     return s;
385 }
386 
387 /* Append the specified null termianted C string to the sds string 's'.
388  *
389  * After the call, the passed sds string is no longer valid and all the
390  * references must be substituted with the new pointer returned by the call. */
sdscat(sds s,const char * t)391 sds sdscat(sds s, const char *t) {
392     return sdscatlen(s, t, strlen(t));
393 }
394 
395 /* Append the specified sds 't' to the existing sds 's'.
396  *
397  * After the call, the modified sds string is no longer valid and all the
398  * references must be substituted with the new pointer returned by the call. */
sdscatsds(sds s,const sds t)399 sds sdscatsds(sds s, const sds t) {
400     return sdscatlen(s, t, sdslen(t));
401 }
402 
403 /* Destructively modify the sds string 's' to hold the specified binary
404  * safe string pointed by 't' of length 'len' bytes. */
sdscpylen(sds s,const char * t,size_t len)405 sds sdscpylen(sds s, const char *t, size_t len) {
406     if (sdsalloc(s) < len) {
407         s = sdsMakeRoomFor(s,len-sdslen(s));
408         if (s == NULL) return NULL;
409     }
410     memcpy(s, t, len);
411     s[len] = '\0';
412     sdssetlen(s, len);
413     return s;
414 }
415 
416 /* Like sdscpylen() but 't' must be a null-termined string so that the length
417  * of the string is obtained with strlen(). */
sdscpy(sds s,const char * t)418 sds sdscpy(sds s, const char *t) {
419     return sdscpylen(s, t, strlen(t));
420 }
421 
422 /* Helper for sdscatlonglong() doing the actual number -> string
423  * conversion. 's' must point to a string with room for at least
424  * SDS_LLSTR_SIZE bytes.
425  *
426  * The function returns the length of the null-terminated string
427  * representation stored at 's'. */
428 #define SDS_LLSTR_SIZE 21
sdsll2str(char * s,long long value)429 int sdsll2str(char *s, long long value) {
430     char *p, aux;
431     unsigned long long v;
432     size_t l;
433 
434     /* Generate the string representation, this method produces
435      * an reversed string. */
436     v = (value < 0) ? -value : value;
437     p = s;
438     do {
439         *p++ = '0'+(v%10);
440         v /= 10;
441     } while(v);
442     if (value < 0) *p++ = '-';
443 
444     /* Compute length and add null term. */
445     l = p-s;
446     *p = '\0';
447 
448     /* Reverse the string. */
449     p--;
450     while(s < p) {
451         aux = *s;
452         *s = *p;
453         *p = aux;
454         s++;
455         p--;
456     }
457     return l;
458 }
459 
460 /* Identical sdsll2str(), but for unsigned long long type. */
sdsull2str(char * s,unsigned long long v)461 int sdsull2str(char *s, unsigned long long v) {
462     char *p, aux;
463     size_t l;
464 
465     /* Generate the string representation, this method produces
466      * an reversed string. */
467     p = s;
468     do {
469         *p++ = '0'+(v%10);
470         v /= 10;
471     } while(v);
472 
473     /* Compute length and add null term. */
474     l = p-s;
475     *p = '\0';
476 
477     /* Reverse the string. */
478     p--;
479     while(s < p) {
480         aux = *s;
481         *s = *p;
482         *p = aux;
483         s++;
484         p--;
485     }
486     return l;
487 }
488 
489 /* Create an sds string from a long long value. It is much faster than:
490  *
491  * sdscatprintf(sdsempty(),"%lld\n", value);
492  */
sdsfromlonglong(long long value)493 sds sdsfromlonglong(long long value) {
494     char buf[SDS_LLSTR_SIZE];
495     int len = sdsll2str(buf,value);
496 
497     return sdsnewlen(buf,len);
498 }
499 
500 /* Like sdscatprintf() but gets va_list instead of being variadic. */
sdscatvprintf(sds s,const char * fmt,va_list ap)501 sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
502     va_list cpy;
503     char staticbuf[1024], *buf = staticbuf, *t;
504     size_t buflen = strlen(fmt)*2;
505 
506     /* We try to start using a static buffer for speed.
507      * If not possible we revert to heap allocation. */
508     if (buflen > sizeof(staticbuf)) {
509         buf = s_malloc(buflen);
510         if (buf == NULL) return NULL;
511     } else {
512         buflen = sizeof(staticbuf);
513     }
514 
515     /* Try with buffers two times bigger every time we fail to
516      * fit the string in the current buffer size. */
517     while(1) {
518         buf[buflen-2] = '\0';
519         va_copy(cpy,ap);
520         vsnprintf(buf, buflen, fmt, cpy);
521         va_end(cpy);
522         if (buf[buflen-2] != '\0') {
523             if (buf != staticbuf) s_free(buf);
524             buflen *= 2;
525             buf = s_malloc(buflen);
526             if (buf == NULL) return NULL;
527             continue;
528         }
529         break;
530     }
531 
532     /* Finally concat the obtained string to the SDS string and return it. */
533     t = sdscat(s, buf);
534     if (buf != staticbuf) s_free(buf);
535     return t;
536 }
537 
538 /* Append to the sds string 's' a string obtained using printf-alike format
539  * specifier.
540  *
541  * After the call, the modified sds string is no longer valid and all the
542  * references must be substituted with the new pointer returned by the call.
543  *
544  * Example:
545  *
546  * s = sdsnew("Sum is: ");
547  * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b).
548  *
549  * Often you need to create a string from scratch with the printf-alike
550  * format. When this is the need, just use sdsempty() as the target string:
551  *
552  * s = sdscatprintf(sdsempty(), "... your format ...", args);
553  */
sdscatprintf(sds s,const char * fmt,...)554 sds sdscatprintf(sds s, const char *fmt, ...) {
555     va_list ap;
556     char *t;
557     va_start(ap, fmt);
558     t = sdscatvprintf(s,fmt,ap);
559     va_end(ap);
560     return t;
561 }
562 
563 /* This function is similar to sdscatprintf, but much faster as it does
564  * not rely on sprintf() family functions implemented by the libc that
565  * are often very slow. Moreover directly handling the sds string as
566  * new data is concatenated provides a performance improvement.
567  *
568  * However this function only handles an incompatible subset of printf-alike
569  * format specifiers:
570  *
571  * %s - C String
572  * %S - SDS string
573  * %i - signed int
574  * %I - 64 bit signed integer (long long, int64_t)
575  * %u - unsigned int
576  * %U - 64 bit unsigned integer (unsigned long long, uint64_t)
577  * %% - Verbatim "%" character.
578  */
sdscatfmt(sds s,char const * fmt,...)579 sds sdscatfmt(sds s, char const *fmt, ...) {
580     size_t initlen = sdslen(s);
581     const char *f = fmt;
582     int i;
583     va_list ap;
584 
585     va_start(ap,fmt);
586     f = fmt;    /* Next format specifier byte to process. */
587     i = initlen; /* Position of the next byte to write to dest str. */
588     while(*f) {
589         char next, *str;
590         size_t l;
591         long long num;
592         unsigned long long unum;
593 
594         /* Make sure there is always space for at least 1 char. */
595         if (sdsavail(s)==0) {
596             s = sdsMakeRoomFor(s,1);
597         }
598 
599         switch(*f) {
600         case '%':
601             next = *(f+1);
602             f++;
603             switch(next) {
604             case 's':
605             case 'S':
606                 str = va_arg(ap,char*);
607                 l = (next == 's') ? strlen(str) : sdslen(str);
608                 if (sdsavail(s) < l) {
609                     s = sdsMakeRoomFor(s,l);
610                 }
611                 memcpy(s+i,str,l);
612                 sdsinclen(s,l);
613                 i += l;
614                 break;
615             case 'i':
616             case 'I':
617                 if (next == 'i')
618                     num = va_arg(ap,int);
619                 else
620                     num = va_arg(ap,long long);
621                 {
622                     char buf[SDS_LLSTR_SIZE];
623                     l = sdsll2str(buf,num);
624                     if (sdsavail(s) < l) {
625                         s = sdsMakeRoomFor(s,l);
626                     }
627                     memcpy(s+i,buf,l);
628                     sdsinclen(s,l);
629                     i += l;
630                 }
631                 break;
632             case 'u':
633             case 'U':
634                 if (next == 'u')
635                     unum = va_arg(ap,unsigned int);
636                 else
637                     unum = va_arg(ap,unsigned long long);
638                 {
639                     char buf[SDS_LLSTR_SIZE];
640                     l = sdsull2str(buf,unum);
641                     if (sdsavail(s) < l) {
642                         s = sdsMakeRoomFor(s,l);
643                     }
644                     memcpy(s+i,buf,l);
645                     sdsinclen(s,l);
646                     i += l;
647                 }
648                 break;
649             default: /* Handle %% and generally %<unknown>. */
650                 s[i++] = next;
651                 sdsinclen(s,1);
652                 break;
653             }
654             break;
655         default:
656             s[i++] = *f;
657             sdsinclen(s,1);
658             break;
659         }
660         f++;
661     }
662     va_end(ap);
663 
664     /* Add null-term */
665     s[i] = '\0';
666     return s;
667 }
668 
669 /* Remove the part of the string from left and from right composed just of
670  * contiguous characters found in 'cset', that is a null terminted C string.
671  *
672  * After the call, the modified sds string is no longer valid and all the
673  * references must be substituted with the new pointer returned by the call.
674  *
675  * Example:
676  *
677  * s = sdsnew("AA...AA.a.aa.aHelloWorld     :::");
678  * s = sdstrim(s,"Aa. :");
679  * printf("%s\n", s);
680  *
681  * Output will be just "Hello World".
682  */
sdstrim(sds s,const char * cset)683 sds sdstrim(sds s, const char *cset) {
684     char *start, *end, *sp, *ep;
685     size_t len;
686 
687     sp = start = s;
688     ep = end = s+sdslen(s)-1;
689     while(sp <= end && strchr(cset, *sp)) sp++;
690     while(ep > sp && strchr(cset, *ep)) ep--;
691     len = (sp > ep) ? 0 : ((ep-sp)+1);
692     if (s != sp) memmove(s, sp, len);
693     s[len] = '\0';
694     sdssetlen(s,len);
695     return s;
696 }
697 
698 /* Turn the string into a smaller (or equal) string containing only the
699  * substring specified by the 'start' and 'end' indexes.
700  *
701  * start and end can be negative, where -1 means the last character of the
702  * string, -2 the penultimate character, and so forth.
703  *
704  * The interval is inclusive, so the start and end characters will be part
705  * of the resulting string.
706  *
707  * The string is modified in-place.
708  *
709  * Example:
710  *
711  * s = sdsnew("Hello World");
712  * sdsrange(s,1,-1); => "ello World"
713  */
sdsrange(sds s,int start,int end)714 void sdsrange(sds s, int start, int end) {
715     size_t newlen, len = sdslen(s);
716 
717     if (len == 0) return;
718     if (start < 0) {
719         start = len+start;
720         if (start < 0) start = 0;
721     }
722     if (end < 0) {
723         end = len+end;
724         if (end < 0) end = 0;
725     }
726     newlen = (start > end) ? 0 : (end-start)+1;
727     if (newlen != 0) {
728         if (start >= (signed)len) {
729             newlen = 0;
730         } else if (end >= (signed)len) {
731             end = len-1;
732             newlen = (start > end) ? 0 : (end-start)+1;
733         }
734     } else {
735         start = 0;
736     }
737     if (start && newlen) memmove(s, s+start, newlen);
738     s[newlen] = 0;
739     sdssetlen(s,newlen);
740 }
741 
742 /* Apply tolower() to every character of the sds string 's'. */
sdstolower(sds s)743 void sdstolower(sds s) {
744     int len = sdslen(s), j;
745 
746     for (j = 0; j < len; j++) s[j] = tolower(s[j]);
747 }
748 
749 /* Apply toupper() to every character of the sds string 's'. */
sdstoupper(sds s)750 void sdstoupper(sds s) {
751     int len = sdslen(s), j;
752 
753     for (j = 0; j < len; j++) s[j] = toupper(s[j]);
754 }
755 
756 /* Compare two sds strings s1 and s2 with memcmp().
757  *
758  * Return value:
759  *
760  *     positive if s1 > s2.
761  *     negative if s1 < s2.
762  *     0 if s1 and s2 are exactly the same binary string.
763  *
764  * If two strings share exactly the same prefix, but one of the two has
765  * additional characters, the longer string is considered to be greater than
766  * the smaller one. */
sdscmp(const sds s1,const sds s2)767 int sdscmp(const sds s1, const sds s2) {
768     size_t l1, l2, minlen;
769     int cmp;
770 
771     l1 = sdslen(s1);
772     l2 = sdslen(s2);
773     minlen = (l1 < l2) ? l1 : l2;
774     cmp = memcmp(s1,s2,minlen);
775     if (cmp == 0) return l1-l2;
776     return cmp;
777 }
778 
779 /* Split 's' with separator in 'sep'. An array
780  * of sds strings is returned. *count will be set
781  * by reference to the number of tokens returned.
782  *
783  * On out of memory, zero length string, zero length
784  * separator, NULL is returned.
785  *
786  * Note that 'sep' is able to split a string using
787  * a multi-character separator. For example
788  * sdssplit("foo_-_bar","_-_"); will return two
789  * elements "foo" and "bar".
790  *
791  * This version of the function is binary-safe but
792  * requires length arguments. sdssplit() is just the
793  * same function but for zero-terminated strings.
794  */
sdssplitlen(const char * s,int len,const char * sep,int seplen,int * count)795 sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) {
796     int elements = 0, slots = 5, start = 0, j;
797     sds *tokens;
798 
799     if (seplen < 1 || len < 0) return NULL;
800 
801     tokens = s_malloc(sizeof(sds)*slots);
802     if (tokens == NULL) return NULL;
803 
804     if (len == 0) {
805         *count = 0;
806         return tokens;
807     }
808     for (j = 0; j < (len-(seplen-1)); j++) {
809         /* make sure there is room for the next element and the final one */
810         if (slots < elements+2) {
811             sds *newtokens;
812 
813             slots *= 2;
814             newtokens = s_realloc(tokens,sizeof(sds)*slots);
815             if (newtokens == NULL) goto cleanup;
816             tokens = newtokens;
817         }
818         /* search the separator */
819         if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
820             tokens[elements] = sdsnewlen(s+start,j-start);
821             if (tokens[elements] == NULL) goto cleanup;
822             elements++;
823             start = j+seplen;
824             j = j+seplen-1; /* skip the separator */
825         }
826     }
827     /* Add the final element. We are sure there is room in the tokens array. */
828     tokens[elements] = sdsnewlen(s+start,len-start);
829     if (tokens[elements] == NULL) goto cleanup;
830     elements++;
831     *count = elements;
832     return tokens;
833 
834 cleanup:
835     {
836         int i;
837         for (i = 0; i < elements; i++) sdsfree(tokens[i]);
838         s_free(tokens);
839         *count = 0;
840         return NULL;
841     }
842 }
843 
844 /* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */
sdsfreesplitres(sds * tokens,int count)845 void sdsfreesplitres(sds *tokens, int count) {
846     if (!tokens) return;
847     while(count--)
848         sdsfree(tokens[count]);
849     s_free(tokens);
850 }
851 
852 /* Append to the sds string "s" an escaped string representation where
853  * all the non-printable characters (tested with isprint()) are turned into
854  * escapes in the form "\n\r\a...." or "\x<hex-number>".
855  *
856  * After the call, the modified sds string is no longer valid and all the
857  * references must be substituted with the new pointer returned by the call. */
sdscatrepr(sds s,const char * p,size_t len)858 sds sdscatrepr(sds s, const char *p, size_t len) {
859     s = sdscatlen(s,"\"",1);
860     while(len--) {
861         switch(*p) {
862         case '\\':
863         case '"':
864             s = sdscatprintf(s,"\\%c",*p);
865             break;
866         case '\n': s = sdscatlen(s,"\\n",2); break;
867         case '\r': s = sdscatlen(s,"\\r",2); break;
868         case '\t': s = sdscatlen(s,"\\t",2); break;
869         case '\a': s = sdscatlen(s,"\\a",2); break;
870         case '\b': s = sdscatlen(s,"\\b",2); break;
871         default:
872             if (isprint(*p))
873                 s = sdscatprintf(s,"%c",*p);
874             else
875                 s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
876             break;
877         }
878         p++;
879     }
880     return sdscatlen(s,"\"",1);
881 }
882 
883 /* Helper function for sdssplitargs() that returns non zero if 'c'
884  * is a valid hex digit. */
is_hex_digit(char c)885 int is_hex_digit(char c) {
886     return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
887            (c >= 'A' && c <= 'F');
888 }
889 
890 /* Helper function for sdssplitargs() that converts a hex digit into an
891  * integer from 0 to 15 */
hex_digit_to_int(char c)892 int hex_digit_to_int(char c) {
893     switch(c) {
894     case '0': return 0;
895     case '1': return 1;
896     case '2': return 2;
897     case '3': return 3;
898     case '4': return 4;
899     case '5': return 5;
900     case '6': return 6;
901     case '7': return 7;
902     case '8': return 8;
903     case '9': return 9;
904     case 'a': case 'A': return 10;
905     case 'b': case 'B': return 11;
906     case 'c': case 'C': return 12;
907     case 'd': case 'D': return 13;
908     case 'e': case 'E': return 14;
909     case 'f': case 'F': return 15;
910     default: return 0;
911     }
912 }
913 
914 /* Split a line into arguments, where every argument can be in the
915  * following programming-language REPL-alike form:
916  *
917  * foo bar "newline are supported\n" and "\xff\x00otherstuff"
918  *
919  * The number of arguments is stored into *argc, and an array
920  * of sds is returned.
921  *
922  * The caller should free the resulting array of sds strings with
923  * sdsfreesplitres().
924  *
925  * Note that sdscatrepr() is able to convert back a string into
926  * a quoted string in the same format sdssplitargs() is able to parse.
927  *
928  * The function returns the allocated tokens on success, even when the
929  * input string is empty, or NULL if the input contains unbalanced
930  * quotes or closed quotes followed by non space characters
931  * as in: "foo"bar or "foo'
932  */
sdssplitargs(const char * line,int * argc)933 sds *sdssplitargs(const char *line, int *argc) {
934     const char *p = line;
935     char *current = NULL;
936     char **vector = NULL;
937 
938     *argc = 0;
939     while(1) {
940         /* skip blanks */
941         while(*p && isspace(*p)) p++;
942         if (*p) {
943             /* get a token */
944             int inq=0;  /* set to 1 if we are in "quotes" */
945             int insq=0; /* set to 1 if we are in 'single quotes' */
946             int done=0;
947 
948             if (current == NULL) current = sdsempty();
949             while(!done) {
950                 if (inq) {
951                     if (*p == '\\' && *(p+1) == 'x' &&
952                                              is_hex_digit(*(p+2)) &&
953                                              is_hex_digit(*(p+3)))
954                     {
955                         unsigned char byte;
956 
957                         byte = (hex_digit_to_int(*(p+2))*16)+
958                                 hex_digit_to_int(*(p+3));
959                         current = sdscatlen(current,(char*)&byte,1);
960                         p += 3;
961                     } else if (*p == '\\' && *(p+1)) {
962                         char c;
963 
964                         p++;
965                         switch(*p) {
966                         case 'n': c = '\n'; break;
967                         case 'r': c = '\r'; break;
968                         case 't': c = '\t'; break;
969                         case 'b': c = '\b'; break;
970                         case 'a': c = '\a'; break;
971                         default: c = *p; break;
972                         }
973                         current = sdscatlen(current,&c,1);
974                     } else if (*p == '"') {
975                         /* closing quote must be followed by a space or
976                          * nothing at all. */
977                         if (*(p+1) && !isspace(*(p+1))) goto err;
978                         done=1;
979                     } else if (!*p) {
980                         /* unterminated quotes */
981                         goto err;
982                     } else {
983                         current = sdscatlen(current,p,1);
984                     }
985                 } else if (insq) {
986                     if (*p == '\\' && *(p+1) == '\'') {
987                         p++;
988                         current = sdscatlen(current,"'",1);
989                     } else if (*p == '\'') {
990                         /* closing quote must be followed by a space or
991                          * nothing at all. */
992                         if (*(p+1) && !isspace(*(p+1))) goto err;
993                         done=1;
994                     } else if (!*p) {
995                         /* unterminated quotes */
996                         goto err;
997                     } else {
998                         current = sdscatlen(current,p,1);
999                     }
1000                 } else {
1001                     switch(*p) {
1002                     case ' ':
1003                     case '\n':
1004                     case '\r':
1005                     case '\t':
1006                     case '\0':
1007                         done=1;
1008                         break;
1009                     case '"':
1010                         inq=1;
1011                         break;
1012                     case '\'':
1013                         insq=1;
1014                         break;
1015                     default:
1016                         current = sdscatlen(current,p,1);
1017                         break;
1018                     }
1019                 }
1020                 if (*p) p++;
1021             }
1022             /* add the token to the vector */
1023             vector = s_realloc(vector,((*argc)+1)*sizeof(char*));
1024             vector[*argc] = current;
1025             (*argc)++;
1026             current = NULL;
1027         } else {
1028             /* Even on empty input string return something not NULL. */
1029             if (vector == NULL) vector = s_malloc(sizeof(void*));
1030             return vector;
1031         }
1032     }
1033 
1034 err:
1035     while((*argc)--)
1036         sdsfree(vector[*argc]);
1037     s_free(vector);
1038     if (current) sdsfree(current);
1039     *argc = 0;
1040     return NULL;
1041 }
1042 
1043 /* Modify the string substituting all the occurrences of the set of
1044  * characters specified in the 'from' string to the corresponding character
1045  * in the 'to' array.
1046  *
1047  * For instance: sdsmapchars(mystring, "ho", "01", 2)
1048  * will have the effect of turning the string "hello" into "0ell1".
1049  *
1050  * The function returns the sds string pointer, that is always the same
1051  * as the input pointer since no resize is needed. */
sdsmapchars(sds s,const char * from,const char * to,size_t setlen)1052 sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {
1053     size_t j, i, l = sdslen(s);
1054 
1055     for (j = 0; j < l; j++) {
1056         for (i = 0; i < setlen; i++) {
1057             if (s[j] == from[i]) {
1058                 s[j] = to[i];
1059                 break;
1060             }
1061         }
1062     }
1063     return s;
1064 }
1065 
1066 /* Join an array of C strings using the specified separator (also a C string).
1067  * Returns the result as an sds string. */
sdsjoin(char ** argv,int argc,char * sep)1068 sds sdsjoin(char **argv, int argc, char *sep) {
1069     sds join = sdsempty();
1070     int j;
1071 
1072     for (j = 0; j < argc; j++) {
1073         join = sdscat(join, argv[j]);
1074         if (j != argc-1) join = sdscat(join,sep);
1075     }
1076     return join;
1077 }
1078 
1079 /* Like sdsjoin, but joins an array of SDS strings. */
sdsjoinsds(sds * argv,int argc,const char * sep,size_t seplen)1080 sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {
1081     sds join = sdsempty();
1082     int j;
1083 
1084     for (j = 0; j < argc; j++) {
1085         join = sdscatsds(join, argv[j]);
1086         if (j != argc-1) join = sdscatlen(join,sep,seplen);
1087     }
1088     return join;
1089 }
1090 
1091 /* Wrappers to the allocators used by SDS. Note that SDS will actually
1092  * just use the macros defined into sdsalloc.h in order to avoid to pay
1093  * the overhead of function calls. Here we define these wrappers only for
1094  * the programs SDS is linked to, if they want to touch the SDS internals
1095  * even if they use a different allocator. */
sds_malloc(size_t size)1096 void *sds_malloc(size_t size) { return s_malloc(size); }
sds_realloc(void * ptr,size_t size)1097 void *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); }
sds_free(void * ptr)1098 void sds_free(void *ptr) { s_free(ptr); }
1099 
1100 #if defined(SDS_TEST_MAIN)
1101 #include <stdio.h>
1102 #include "testhelp.h"
1103 #include "limits.h"
1104 
1105 #define UNUSED(x) (void)(x)
sdsTest(void)1106 int sdsTest(void) {
1107     {
1108         sds x = sdsnew("foo"), y;
1109 
1110         test_cond("Create a string and obtain the length",
1111             sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0)
1112 
1113         sdsfree(x);
1114         x = sdsnewlen("foo",2);
1115         test_cond("Create a string with specified length",
1116             sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0)
1117 
1118         x = sdscat(x,"bar");
1119         test_cond("Strings concatenation",
1120             sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0);
1121 
1122         x = sdscpy(x,"a");
1123         test_cond("sdscpy() against an originally longer string",
1124             sdslen(x) == 1 && memcmp(x,"a\0",2) == 0)
1125 
1126         x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk");
1127         test_cond("sdscpy() against an originally shorter string",
1128             sdslen(x) == 33 &&
1129             memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0)
1130 
1131         sdsfree(x);
1132         x = sdscatprintf(sdsempty(),"%d",123);
1133         test_cond("sdscatprintf() seems working in the base case",
1134             sdslen(x) == 3 && memcmp(x,"123\0",4) == 0)
1135 
1136         sdsfree(x);
1137         x = sdsnew("--");
1138         x = sdscatfmt(x, "Hello %s World %I,%I--", "Hi!", LLONG_MIN,LLONG_MAX);
1139         test_cond("sdscatfmt() seems working in the base case",
1140             sdslen(x) == 60 &&
1141             memcmp(x,"--Hello Hi! World -9223372036854775808,"
1142                      "9223372036854775807--",60) == 0)
1143         printf("[%s]\n",x);
1144 
1145         sdsfree(x);
1146         x = sdsnew("--");
1147         x = sdscatfmt(x, "%u,%U--", UINT_MAX, ULLONG_MAX);
1148         test_cond("sdscatfmt() seems working with unsigned numbers",
1149             sdslen(x) == 35 &&
1150             memcmp(x,"--4294967295,18446744073709551615--",35) == 0)
1151 
1152         sdsfree(x);
1153         x = sdsnew(" x ");
1154         sdstrim(x," x");
1155         test_cond("sdstrim() works when all chars match",
1156             sdslen(x) == 0)
1157 
1158         sdsfree(x);
1159         x = sdsnew(" x ");
1160         sdstrim(x," ");
1161         test_cond("sdstrim() works when a single char remains",
1162             sdslen(x) == 1 && x[0] == 'x')
1163 
1164         sdsfree(x);
1165         x = sdsnew("xxciaoyyy");
1166         sdstrim(x,"xy");
1167         test_cond("sdstrim() correctly trims characters",
1168             sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)
1169 
1170         y = sdsdup(x);
1171         sdsrange(y,1,1);
1172         test_cond("sdsrange(...,1,1)",
1173             sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)
1174 
1175         sdsfree(y);
1176         y = sdsdup(x);
1177         sdsrange(y,1,-1);
1178         test_cond("sdsrange(...,1,-1)",
1179             sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
1180 
1181         sdsfree(y);
1182         y = sdsdup(x);
1183         sdsrange(y,-2,-1);
1184         test_cond("sdsrange(...,-2,-1)",
1185             sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)
1186 
1187         sdsfree(y);
1188         y = sdsdup(x);
1189         sdsrange(y,2,1);
1190         test_cond("sdsrange(...,2,1)",
1191             sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
1192 
1193         sdsfree(y);
1194         y = sdsdup(x);
1195         sdsrange(y,1,100);
1196         test_cond("sdsrange(...,1,100)",
1197             sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
1198 
1199         sdsfree(y);
1200         y = sdsdup(x);
1201         sdsrange(y,100,100);
1202         test_cond("sdsrange(...,100,100)",
1203             sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
1204 
1205         sdsfree(y);
1206         sdsfree(x);
1207         x = sdsnew("foo");
1208         y = sdsnew("foa");
1209         test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0)
1210 
1211         sdsfree(y);
1212         sdsfree(x);
1213         x = sdsnew("bar");
1214         y = sdsnew("bar");
1215         test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0)
1216 
1217         sdsfree(y);
1218         sdsfree(x);
1219         x = sdsnew("aar");
1220         y = sdsnew("bar");
1221         test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
1222 
1223         sdsfree(y);
1224         sdsfree(x);
1225         x = sdsnewlen("\a\n\0foo\r",7);
1226         y = sdscatrepr(sdsempty(),x,sdslen(x));
1227         test_cond("sdscatrepr(...data...)",
1228             memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0)
1229 
1230         {
1231             unsigned int oldfree;
1232             char *p;
1233             int step = 10, j, i;
1234 
1235             sdsfree(x);
1236             sdsfree(y);
1237             x = sdsnew("0");
1238             test_cond("sdsnew() free/len buffers", sdslen(x) == 1 && sdsavail(x) == 0);
1239 
1240             /* Run the test a few times in order to hit the first two
1241              * SDS header types. */
1242             for (i = 0; i < 10; i++) {
1243                 int oldlen = sdslen(x);
1244                 x = sdsMakeRoomFor(x,step);
1245                 int type = x[-1]&SDS_TYPE_MASK;
1246 
1247                 test_cond("sdsMakeRoomFor() len", sdslen(x) == oldlen);
1248                 if (type != SDS_TYPE_5) {
1249                     test_cond("sdsMakeRoomFor() free", sdsavail(x) >= step);
1250                     oldfree = sdsavail(x);
1251                 }
1252                 p = x+oldlen;
1253                 for (j = 0; j < step; j++) {
1254                     p[j] = 'A'+j;
1255                 }
1256                 sdsIncrLen(x,step);
1257             }
1258             test_cond("sdsMakeRoomFor() content",
1259                 memcmp("0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ",x,101) == 0);
1260             test_cond("sdsMakeRoomFor() final length",sdslen(x)==101);
1261 
1262             sdsfree(x);
1263         }
1264     }
1265     test_report()
1266     return 0;
1267 }
1268 #endif
1269 
1270 #ifdef SDS_TEST_MAIN
main(void)1271 int main(void) {
1272     return sdsTest();
1273 }
1274 #endif
1275