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