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