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