1 /* SDSLib, A C dynamic strings library 2 * 3 * Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * * Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * * Neither the name of Redis nor the names of its contributors may be used 15 * to endorse or promote products derived from this software without 16 * specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <ctype.h> 35 #include <assert.h> 36 #include "sds.h" 37 #include "zmalloc.h" 38 39 sds sdsnewlen(const void *init, size_t initlen) { 40 struct sdshdr *sh; 41 42 if (init) { 43 sh = zmalloc(sizeof(struct sdshdr)+initlen+1); 44 } else { 45 sh = zcalloc(sizeof(struct sdshdr)+initlen+1); 46 } 47 if (sh == NULL) return NULL; 48 sh->len = initlen; 49 sh->free = 0; 50 if (initlen && init) 51 memcpy(sh->buf, init, initlen); 52 sh->buf[initlen] = '\0'; 53 return (char*)sh->buf; 54 } 55 56 sds sdsempty(void) { 57 return sdsnewlen("",0); 58 } 59 60 sds sdsnew(const char *init) { 61 size_t initlen = (init == NULL) ? 0 : strlen(init); 62 return sdsnewlen(init, initlen); 63 } 64 65 sds sdsdup(const sds s) { 66 return sdsnewlen(s, sdslen(s)); 67 } 68 69 void sdsfree(sds s) { 70 if (s == NULL) return; 71 zfree(s-sizeof(struct sdshdr)); 72 } 73 74 void sdsupdatelen(sds s) { 75 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 76 int reallen = strlen(s); 77 sh->free += (sh->len-reallen); 78 sh->len = reallen; 79 } 80 81 void sdsclear(sds s) { 82 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 83 sh->free += sh->len; 84 sh->len = 0; 85 sh->buf[0] = '\0'; 86 } 87 88 /* Enlarge the free space at the end of the sds string so that the caller 89 * is sure that after calling this function can overwrite up to addlen 90 * bytes after the end of the string, plus one more byte for nul term. 91 * 92 * Note: this does not change the *size* of the sds string as returned 93 * by sdslen(), but only the free buffer space we have. */ 94 sds sdsMakeRoomFor(sds s, size_t addlen) { 95 struct sdshdr *sh, *newsh; 96 size_t free = sdsavail(s); 97 size_t len, newlen; 98 99 if (free >= addlen) return s; 100 len = sdslen(s); 101 sh = (void*) (s-(sizeof(struct sdshdr))); 102 newlen = (len+addlen); 103 if (newlen < SDS_MAX_PREALLOC) 104 newlen *= 2; 105 else 106 newlen += SDS_MAX_PREALLOC; 107 newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1); 108 if (newsh == NULL) return NULL; 109 110 newsh->free = newlen - len; 111 return newsh->buf; 112 } 113 114 /* Reallocate the sds string so that it has no free space at the end. The 115 * contained string remains not altered, but next concatenation operations 116 * will require a reallocation. */ 117 sds sdsRemoveFreeSpace(sds s) { 118 struct sdshdr *sh; 119 120 sh = (void*) (s-(sizeof(struct sdshdr))); 121 sh = zrealloc(sh, sizeof(struct sdshdr)+sh->len+1); 122 sh->free = 0; 123 return sh->buf; 124 } 125 126 size_t sdsAllocSize(sds s) { 127 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 128 129 return sizeof(*sh)+sh->len+sh->free+1; 130 } 131 132 /* Increment the sds length and decrements the left free space at the 133 * end of the string accordingly to 'incr'. Also set the null term 134 * in the new end of the string. 135 * 136 * This function is used in order to fix the string length after the 137 * user calls sdsMakeRoomFor(), writes something after the end of 138 * the current string, and finally needs to set the new length. 139 * 140 * Note: it is possible to use a negative increment in order to 141 * right-trim the string. 142 * 143 * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the 144 * following schema to cat bytes coming from the kernel to the end of an 145 * sds string new things without copying into an intermediate buffer: 146 * 147 * oldlen = sdslen(s); 148 * s = sdsMakeRoomFor(s, BUFFER_SIZE); 149 * nread = read(fd, s+oldlen, BUFFER_SIZE); 150 * ... check for nread <= 0 and handle it ... 151 * sdsIncrLen(s, nhread); 152 */ 153 void sdsIncrLen(sds s, int incr) { 154 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 155 156 assert(sh->free >= incr); 157 sh->len += incr; 158 sh->free -= incr; 159 assert(sh->free >= 0); 160 s[sh->len] = '\0'; 161 } 162 163 /* Grow the sds to have the specified length. Bytes that were not part of 164 * the original length of the sds will be set to zero. */ 165 sds sdsgrowzero(sds s, size_t len) { 166 struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); 167 size_t totlen, curlen = sh->len; 168 169 if (len <= curlen) return s; 170 s = sdsMakeRoomFor(s,len-curlen); 171 if (s == NULL) return NULL; 172 173 /* Make sure added region doesn't contain garbage */ 174 sh = (void*)(s-(sizeof(struct sdshdr))); 175 memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */ 176 totlen = sh->len+sh->free; 177 sh->len = len; 178 sh->free = totlen-sh->len; 179 return s; 180 } 181 182 sds sdscatlen(sds s, const void *t, size_t len) { 183 struct sdshdr *sh; 184 size_t curlen = sdslen(s); 185 186 s = sdsMakeRoomFor(s,len); 187 if (s == NULL) return NULL; 188 sh = (void*) (s-(sizeof(struct sdshdr))); 189 memcpy(s+curlen, t, len); 190 sh->len = curlen+len; 191 sh->free = sh->free-len; 192 s[curlen+len] = '\0'; 193 return s; 194 } 195 196 sds sdscat(sds s, const char *t) { 197 return sdscatlen(s, t, strlen(t)); 198 } 199 200 sds sdscatsds(sds s, const sds t) { 201 return sdscatlen(s, t, sdslen(t)); 202 } 203 204 sds sdscpylen(sds s, const char *t, size_t len) { 205 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 206 size_t totlen = sh->free+sh->len; 207 208 if (totlen < len) { 209 s = sdsMakeRoomFor(s,len-sh->len); 210 if (s == NULL) return NULL; 211 sh = (void*) (s-(sizeof(struct sdshdr))); 212 totlen = sh->free+sh->len; 213 } 214 memcpy(s, t, len); 215 s[len] = '\0'; 216 sh->len = len; 217 sh->free = totlen-len; 218 return s; 219 } 220 221 sds sdscpy(sds s, const char *t) { 222 return sdscpylen(s, t, strlen(t)); 223 } 224 225 sds sdscatvprintf(sds s, const char *fmt, va_list ap) { 226 va_list cpy; 227 char *buf, *t; 228 size_t buflen = 16; 229 230 while(1) { 231 buf = zmalloc(buflen); 232 if (buf == NULL) return NULL; 233 buf[buflen-2] = '\0'; 234 va_copy(cpy,ap); 235 vsnprintf(buf, buflen, fmt, cpy); 236 if (buf[buflen-2] != '\0') { 237 zfree(buf); 238 buflen *= 2; 239 continue; 240 } 241 break; 242 } 243 t = sdscat(s, buf); 244 zfree(buf); 245 return t; 246 } 247 248 sds sdscatprintf(sds s, const char *fmt, ...) { 249 va_list ap; 250 char *t; 251 va_start(ap, fmt); 252 t = sdscatvprintf(s,fmt,ap); 253 va_end(ap); 254 return t; 255 } 256 257 sds sdstrim(sds s, const char *cset) { 258 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 259 char *start, *end, *sp, *ep; 260 size_t len; 261 262 sp = start = s; 263 ep = end = s+sdslen(s)-1; 264 while(sp <= end && strchr(cset, *sp)) sp++; 265 while(ep > start && strchr(cset, *ep)) ep--; 266 len = (sp > ep) ? 0 : ((ep-sp)+1); 267 if (sh->buf != sp) memmove(sh->buf, sp, len); 268 sh->buf[len] = '\0'; 269 sh->free = sh->free+(sh->len-len); 270 sh->len = len; 271 return s; 272 } 273 274 sds sdsrange(sds s, int start, int end) { 275 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 276 size_t newlen, len = sdslen(s); 277 278 if (len == 0) return s; 279 if (start < 0) { 280 start = len+start; 281 if (start < 0) start = 0; 282 } 283 if (end < 0) { 284 end = len+end; 285 if (end < 0) end = 0; 286 } 287 newlen = (start > end) ? 0 : (end-start)+1; 288 if (newlen != 0) { 289 if (start >= (signed)len) { 290 newlen = 0; 291 } else if (end >= (signed)len) { 292 end = len-1; 293 newlen = (start > end) ? 0 : (end-start)+1; 294 } 295 } else { 296 start = 0; 297 } 298 if (start && newlen) memmove(sh->buf, sh->buf+start, newlen); 299 sh->buf[newlen] = 0; 300 sh->free = sh->free+(sh->len-newlen); 301 sh->len = newlen; 302 return s; 303 } 304 305 void sdstolower(sds s) { 306 int len = sdslen(s), j; 307 308 for (j = 0; j < len; j++) s[j] = tolower(s[j]); 309 } 310 311 void sdstoupper(sds s) { 312 int len = sdslen(s), j; 313 314 for (j = 0; j < len; j++) s[j] = toupper(s[j]); 315 } 316 317 int sdscmp(const sds s1, const sds s2) { 318 size_t l1, l2, minlen; 319 int cmp; 320 321 l1 = sdslen(s1); 322 l2 = sdslen(s2); 323 minlen = (l1 < l2) ? l1 : l2; 324 cmp = memcmp(s1,s2,minlen); 325 if (cmp == 0) return l1-l2; 326 return cmp; 327 } 328 329 /* Split 's' with separator in 'sep'. An array 330 * of sds strings is returned. *count will be set 331 * by reference to the number of tokens returned. 332 * 333 * On out of memory, zero length string, zero length 334 * separator, NULL is returned. 335 * 336 * Note that 'sep' is able to split a string using 337 * a multi-character separator. For example 338 * sdssplit("foo_-_bar","_-_"); will return two 339 * elements "foo" and "bar". 340 * 341 * This version of the function is binary-safe but 342 * requires length arguments. sdssplit() is just the 343 * same function but for zero-terminated strings. 344 */ 345 sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) { 346 int elements = 0, slots = 5, start = 0, j; 347 sds *tokens; 348 349 if (seplen < 1 || len < 0) return NULL; 350 351 tokens = zmalloc(sizeof(sds)*slots); 352 if (tokens == NULL) return NULL; 353 354 if (len == 0) { 355 *count = 0; 356 return tokens; 357 } 358 for (j = 0; j < (len-(seplen-1)); j++) { 359 /* make sure there is room for the next element and the final one */ 360 if (slots < elements+2) { 361 sds *newtokens; 362 363 slots *= 2; 364 newtokens = zrealloc(tokens,sizeof(sds)*slots); 365 if (newtokens == NULL) goto cleanup; 366 tokens = newtokens; 367 } 368 /* search the separator */ 369 if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) { 370 tokens[elements] = sdsnewlen(s+start,j-start); 371 if (tokens[elements] == NULL) goto cleanup; 372 elements++; 373 start = j+seplen; 374 j = j+seplen-1; /* skip the separator */ 375 } 376 } 377 /* Add the final element. We are sure there is room in the tokens array. */ 378 tokens[elements] = sdsnewlen(s+start,len-start); 379 if (tokens[elements] == NULL) goto cleanup; 380 elements++; 381 *count = elements; 382 return tokens; 383 384 cleanup: 385 { 386 int i; 387 for (i = 0; i < elements; i++) sdsfree(tokens[i]); 388 zfree(tokens); 389 *count = 0; 390 return NULL; 391 } 392 } 393 394 void sdsfreesplitres(sds *tokens, int count) { 395 if (!tokens) return; 396 while(count--) 397 sdsfree(tokens[count]); 398 zfree(tokens); 399 } 400 401 sds sdsfromlonglong(long long value) { 402 char buf[32], *p; 403 unsigned long long v; 404 405 v = (value < 0) ? -value : value; 406 p = buf+31; /* point to the last character */ 407 do { 408 *p-- = '0'+(v%10); 409 v /= 10; 410 } while(v); 411 if (value < 0) *p-- = '-'; 412 p++; 413 return sdsnewlen(p,32-(p-buf)); 414 } 415 416 sds sdscatrepr(sds s, const char *p, size_t len) { 417 s = sdscatlen(s,"\"",1); 418 while(len--) { 419 switch(*p) { 420 case '\\': 421 case '"': 422 s = sdscatprintf(s,"\\%c",*p); 423 break; 424 case '\n': s = sdscatlen(s,"\\n",2); break; 425 case '\r': s = sdscatlen(s,"\\r",2); break; 426 case '\t': s = sdscatlen(s,"\\t",2); break; 427 case '\a': s = sdscatlen(s,"\\a",2); break; 428 case '\b': s = sdscatlen(s,"\\b",2); break; 429 default: 430 if (isprint(*p)) 431 s = sdscatprintf(s,"%c",*p); 432 else 433 s = sdscatprintf(s,"\\x%02x",(unsigned char)*p); 434 break; 435 } 436 p++; 437 } 438 return sdscatlen(s,"\"",1); 439 } 440 441 /* Helper function for sdssplitargs() that returns non zero if 'c' 442 * is a valid hex digit. */ 443 int is_hex_digit(char c) { 444 return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || 445 (c >= 'A' && c <= 'F'); 446 } 447 448 /* Helper function for sdssplitargs() that converts an hex digit into an 449 * integer from 0 to 15 */ 450 int hex_digit_to_int(char c) { 451 switch(c) { 452 case '0': return 0; 453 case '1': return 1; 454 case '2': return 2; 455 case '3': return 3; 456 case '4': return 4; 457 case '5': return 5; 458 case '6': return 6; 459 case '7': return 7; 460 case '8': return 8; 461 case '9': return 9; 462 case 'a': case 'A': return 10; 463 case 'b': case 'B': return 11; 464 case 'c': case 'C': return 12; 465 case 'd': case 'D': return 13; 466 case 'e': case 'E': return 14; 467 case 'f': case 'F': return 15; 468 default: return 0; 469 } 470 } 471 472 /* Split a line into arguments, where every argument can be in the 473 * following programming-language REPL-alike form: 474 * 475 * foo bar "newline are supported\n" and "\xff\x00otherstuff" 476 * 477 * The number of arguments is stored into *argc, and an array 478 * of sds is returned. 479 * 480 * The caller should free the resulting array of sds strings with 481 * sdsfreesplitres(). 482 * 483 * Note that sdscatrepr() is able to convert back a string into 484 * a quoted string in the same format sdssplitargs() is able to parse. 485 * 486 * The function returns the allocated tokens on success, even when the 487 * input string is empty, or NULL if the input contains unbalanced 488 * quotes or closed quotes followed by non space characters 489 * as in: "foo"bar or "foo' 490 */ 491 sds *sdssplitargs(const char *line, int *argc) { 492 const char *p = line; 493 char *current = NULL; 494 char **vector = NULL; 495 496 *argc = 0; 497 while(1) { 498 /* skip blanks */ 499 while(*p && isspace(*p)) p++; 500 if (*p) { 501 /* get a token */ 502 int inq=0; /* set to 1 if we are in "quotes" */ 503 int insq=0; /* set to 1 if we are in 'single quotes' */ 504 int done=0; 505 506 if (current == NULL) current = sdsempty(); 507 while(!done) { 508 if (inq) { 509 if (*p == '\\' && *(p+1) == 'x' && 510 is_hex_digit(*(p+2)) && 511 is_hex_digit(*(p+3))) 512 { 513 unsigned char byte; 514 515 byte = (hex_digit_to_int(*(p+2))*16)+ 516 hex_digit_to_int(*(p+3)); 517 current = sdscatlen(current,(char*)&byte,1); 518 p += 3; 519 } else if (*p == '\\' && *(p+1)) { 520 char c; 521 522 p++; 523 switch(*p) { 524 case 'n': c = '\n'; break; 525 case 'r': c = '\r'; break; 526 case 't': c = '\t'; break; 527 case 'b': c = '\b'; break; 528 case 'a': c = '\a'; break; 529 default: c = *p; break; 530 } 531 current = sdscatlen(current,&c,1); 532 } else if (*p == '"') { 533 /* closing quote must be followed by a space or 534 * nothing at all. */ 535 if (*(p+1) && !isspace(*(p+1))) goto err; 536 done=1; 537 } else if (!*p) { 538 /* unterminated quotes */ 539 goto err; 540 } else { 541 current = sdscatlen(current,p,1); 542 } 543 } else if (insq) { 544 if (*p == '\\' && *(p+1) == '\'') { 545 p++; 546 current = sdscatlen(current,"'",1); 547 } else if (*p == '\'') { 548 /* closing quote must be followed by a space or 549 * nothing at all. */ 550 if (*(p+1) && !isspace(*(p+1))) goto err; 551 done=1; 552 } else if (!*p) { 553 /* unterminated quotes */ 554 goto err; 555 } else { 556 current = sdscatlen(current,p,1); 557 } 558 } else { 559 switch(*p) { 560 case ' ': 561 case '\n': 562 case '\r': 563 case '\t': 564 case '\0': 565 done=1; 566 break; 567 case '"': 568 inq=1; 569 break; 570 case '\'': 571 insq=1; 572 break; 573 default: 574 current = sdscatlen(current,p,1); 575 break; 576 } 577 } 578 if (*p) p++; 579 } 580 /* add the token to the vector */ 581 vector = zrealloc(vector,((*argc)+1)*sizeof(char*)); 582 vector[*argc] = current; 583 (*argc)++; 584 current = NULL; 585 } else { 586 /* Even on empty input string return something not NULL. */ 587 if (vector == NULL) vector = zmalloc(sizeof(void*)); 588 return vector; 589 } 590 } 591 592 err: 593 while((*argc)--) 594 sdsfree(vector[*argc]); 595 zfree(vector); 596 if (current) sdsfree(current); 597 *argc = 0; 598 return NULL; 599 } 600 601 /* Modify the string substituting all the occurrences of the set of 602 * characters specified in the 'from' string to the corresponding character 603 * in the 'to' array. 604 * 605 * For instance: sdsmapchars(mystring, "ho", "01", 2) 606 * will have the effect of turning the string "hello" into "0ell1". 607 * 608 * The function returns the sds string pointer, that is always the same 609 * as the input pointer since no resize is needed. */ 610 sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) { 611 size_t j, i, l = sdslen(s); 612 613 for (j = 0; j < l; j++) { 614 for (i = 0; i < setlen; i++) { 615 if (s[j] == from[i]) { 616 s[j] = to[i]; 617 break; 618 } 619 } 620 } 621 return s; 622 } 623 624 #ifdef SDS_TEST_MAIN 625 #include <stdio.h> 626 #include "testhelp.h" 627 628 int main(void) { 629 { 630 struct sdshdr *sh; 631 sds x = sdsnew("foo"), y; 632 633 test_cond("Create a string and obtain the length", 634 sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0) 635 636 sdsfree(x); 637 x = sdsnewlen("foo",2); 638 test_cond("Create a string with specified length", 639 sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0) 640 641 x = sdscat(x,"bar"); 642 test_cond("Strings concatenation", 643 sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0); 644 645 x = sdscpy(x,"a"); 646 test_cond("sdscpy() against an originally longer string", 647 sdslen(x) == 1 && memcmp(x,"a\0",2) == 0) 648 649 x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); 650 test_cond("sdscpy() against an originally shorter string", 651 sdslen(x) == 33 && 652 memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0) 653 654 sdsfree(x); 655 x = sdscatprintf(sdsempty(),"%d",123); 656 test_cond("sdscatprintf() seems working in the base case", 657 sdslen(x) == 3 && memcmp(x,"123\0",4) ==0) 658 659 sdsfree(x); 660 x = sdstrim(sdsnew("xxciaoyyy"),"xy"); 661 test_cond("sdstrim() correctly trims characters", 662 sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0) 663 664 y = sdsrange(sdsdup(x),1,1); 665 test_cond("sdsrange(...,1,1)", 666 sdslen(y) == 1 && memcmp(y,"i\0",2) == 0) 667 668 sdsfree(y); 669 y = sdsrange(sdsdup(x),1,-1); 670 test_cond("sdsrange(...,1,-1)", 671 sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) 672 673 sdsfree(y); 674 y = sdsrange(sdsdup(x),-2,-1); 675 test_cond("sdsrange(...,-2,-1)", 676 sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0) 677 678 sdsfree(y); 679 y = sdsrange(sdsdup(x),2,1); 680 test_cond("sdsrange(...,2,1)", 681 sdslen(y) == 0 && memcmp(y,"\0",1) == 0) 682 683 sdsfree(y); 684 y = sdsrange(sdsdup(x),1,100); 685 test_cond("sdsrange(...,1,100)", 686 sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) 687 688 sdsfree(y); 689 y = sdsrange(sdsdup(x),100,100); 690 test_cond("sdsrange(...,100,100)", 691 sdslen(y) == 0 && memcmp(y,"\0",1) == 0) 692 693 sdsfree(y); 694 sdsfree(x); 695 x = sdsnew("foo"); 696 y = sdsnew("foa"); 697 test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0) 698 699 sdsfree(y); 700 sdsfree(x); 701 x = sdsnew("bar"); 702 y = sdsnew("bar"); 703 test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0) 704 705 sdsfree(y); 706 sdsfree(x); 707 x = sdsnew("aar"); 708 y = sdsnew("bar"); 709 test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0) 710 711 { 712 int oldfree; 713 714 sdsfree(x); 715 x = sdsnew("0"); 716 sh = (void*) (x-(sizeof(struct sdshdr))); 717 test_cond("sdsnew() free/len buffers", sh->len == 1 && sh->free == 0); 718 x = sdsMakeRoomFor(x,1); 719 sh = (void*) (x-(sizeof(struct sdshdr))); 720 test_cond("sdsMakeRoomFor()", sh->len == 1 && sh->free > 0); 721 oldfree = sh->free; 722 x[1] = '1'; 723 sdsIncrLen(x,1); 724 test_cond("sdsIncrLen() -- content", x[0] == '0' && x[1] == '1'); 725 test_cond("sdsIncrLen() -- len", sh->len == 2); 726 test_cond("sdsIncrLen() -- free", sh->free == oldfree-1); 727 } 728 } 729 test_report() 730 return 0; 731 } 732 #endif 733