Bug Summary

File:src/sds.c
Warning:line 751, column 10
Although the value stored to 'start' is used in the enclosing expression, the value is never actually read from 'start'

Annotated Source Code

Press '?' to see keyboard shortcuts

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