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