Frames

Untitled

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