]>
git.saurik.com Git - redis.git/blob - src/sds.c
77052966ceb79e72355df042f400648d35131829
   1 /* SDSLib, A C dynamic strings library 
   3  * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com> 
   6  * Redistribution and use in source and binary forms, with or without 
   7  * modification, are permitted provided that the following conditions are met: 
   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. 
  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. 
  32  * - 22 March 2011: History section created on top of sds.c 
  33  * - 22 March 2011: Fixed a problem with "\xab" escapes convertion in 
  34  *                  function sdssplitargs(). 
  37 #define SDS_ABORT_ON_OOM 
  46 static void sdsOomAbort(void) { 
  47     fprintf(stderr
,"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n"); 
  51 sds 
sdsnewlen(const void *init
, size_t initlen
) { 
  55         sh 
= zmalloc(sizeof(struct sdshdr
)+initlen
+1); 
  57         sh 
= zcalloc(sizeof(struct sdshdr
)+initlen
+1); 
  59 #ifdef SDS_ABORT_ON_OOM 
  60     if (sh 
== NULL
) sdsOomAbort(); 
  62     if (sh 
== NULL
) return NULL
; 
  67         memcpy(sh
->buf
, init
, initlen
); 
  68     sh
->buf
[initlen
] = '\0'; 
  69     return (char*)sh
->buf
; 
  73     return sdsnewlen("",0); 
  76 sds 
sdsnew(const char *init
) { 
  77     size_t initlen 
= (init 
== NULL
) ? 0 : strlen(init
); 
  78     return sdsnewlen(init
, initlen
); 
  81 sds 
sdsdup(const sds s
) { 
  82     return sdsnewlen(s
, sdslen(s
)); 
  86     if (s 
== NULL
) return; 
  87     zfree(s
-sizeof(struct sdshdr
)); 
  90 void sdsupdatelen(sds s
) { 
  91     struct sdshdr 
*sh 
= (void*) (s
-(sizeof(struct sdshdr
))); 
  92     int reallen 
= strlen(s
); 
  93     sh
->free 
+= (sh
->len
-reallen
); 
  97 void sdsclear(sds s
) { 
  98     struct sdshdr 
*sh 
= (void*) (s
-(sizeof(struct sdshdr
))); 
 104 static sds 
sdsMakeRoomFor(sds s
, size_t addlen
) { 
 105     struct sdshdr 
*sh
, *newsh
; 
 106     size_t free 
= sdsavail(s
); 
 109     if (free 
>= addlen
) return s
; 
 111     sh 
= (void*) (s
-(sizeof(struct sdshdr
))); 
 112     newlen 
= (len
+addlen
)*2; 
 113     newsh 
= zrealloc(sh
, sizeof(struct sdshdr
)+newlen
+1); 
 114 #ifdef SDS_ABORT_ON_OOM 
 115     if (newsh 
== NULL
) sdsOomAbort(); 
 117     if (newsh 
== NULL
) return NULL
; 
 120     newsh
->free 
= newlen 
- len
; 
 124 /* Grow the sds to have the specified length. Bytes that were not part of 
 125  * the original length of the sds will be set to zero. */ 
 126 sds 
sdsgrowzero(sds s
, size_t len
) { 
 127     struct sdshdr 
*sh 
= (void*)(s
-(sizeof(struct sdshdr
))); 
 128     size_t totlen
, curlen 
= sh
->len
; 
 130     if (len 
<= curlen
) return s
; 
 131     s 
= sdsMakeRoomFor(s
,len
-curlen
); 
 132     if (s 
== NULL
) return NULL
; 
 134     /* Make sure added region doesn't contain garbage */ 
 135     sh 
= (void*)(s
-(sizeof(struct sdshdr
))); 
 136     memset(s
+curlen
,0,(len
-curlen
+1)); /* also set trailing \0 byte */ 
 137     totlen 
= sh
->len
+sh
->free
; 
 139     sh
->free 
= totlen
-sh
->len
; 
 143 sds 
sdscatlen(sds s
, void *t
, size_t len
) { 
 145     size_t curlen 
= sdslen(s
); 
 147     s 
= sdsMakeRoomFor(s
,len
); 
 148     if (s 
== NULL
) return NULL
; 
 149     sh 
= (void*) (s
-(sizeof(struct sdshdr
))); 
 150     memcpy(s
+curlen
, t
, len
); 
 151     sh
->len 
= curlen
+len
; 
 152     sh
->free 
= sh
->free
-len
; 
 153     s
[curlen
+len
] = '\0'; 
 157 sds 
sdscat(sds s
, char *t
) { 
 158     return sdscatlen(s
, t
, strlen(t
)); 
 161 sds 
sdscpylen(sds s
, char *t
, size_t len
) { 
 162     struct sdshdr 
*sh 
= (void*) (s
-(sizeof(struct sdshdr
))); 
 163     size_t totlen 
= sh
->free
+sh
->len
; 
 166         s 
= sdsMakeRoomFor(s
,len
-sh
->len
); 
 167         if (s 
== NULL
) return NULL
; 
 168         sh 
= (void*) (s
-(sizeof(struct sdshdr
))); 
 169         totlen 
= sh
->free
+sh
->len
; 
 174     sh
->free 
= totlen
-len
; 
 178 sds 
sdscpy(sds s
, char *t
) { 
 179     return sdscpylen(s
, t
, strlen(t
)); 
 182 sds 
sdscatvprintf(sds s
, const char *fmt
, va_list ap
) { 
 188         buf 
= zmalloc(buflen
); 
 189 #ifdef SDS_ABORT_ON_OOM 
 190         if (buf 
== NULL
) sdsOomAbort(); 
 192         if (buf 
== NULL
) return NULL
; 
 194         buf
[buflen
-2] = '\0'; 
 196         vsnprintf(buf
, buflen
, fmt
, cpy
); 
 197         if (buf
[buflen
-2] != '\0') { 
 209 sds 
sdscatprintf(sds s
, const char *fmt
, ...) { 
 213     t 
= sdscatvprintf(s
,fmt
,ap
); 
 218 sds 
sdstrim(sds s
, const char *cset
) { 
 219     struct sdshdr 
*sh 
= (void*) (s
-(sizeof(struct sdshdr
))); 
 220     char *start
, *end
, *sp
, *ep
; 
 224     ep 
= end 
= s
+sdslen(s
)-1; 
 225     while(sp 
<= end 
&& strchr(cset
, *sp
)) sp
++; 
 226     while(ep 
> start 
&& strchr(cset
, *ep
)) ep
--; 
 227     len 
= (sp 
> ep
) ? 0 : ((ep
-sp
)+1); 
 228     if (sh
->buf 
!= sp
) memmove(sh
->buf
, sp
, len
); 
 230     sh
->free 
= sh
->free
+(sh
->len
-len
); 
 235 sds 
sdsrange(sds s
, int start
, int end
) { 
 236     struct sdshdr 
*sh 
= (void*) (s
-(sizeof(struct sdshdr
))); 
 237     size_t newlen
, len 
= sdslen(s
); 
 239     if (len 
== 0) return s
; 
 242         if (start 
< 0) start 
= 0; 
 246         if (end 
< 0) end 
= 0; 
 248     newlen 
= (start 
> end
) ? 0 : (end
-start
)+1; 
 250         if (start 
>= (signed)len
) { 
 252         } else if (end 
>= (signed)len
) { 
 254             newlen 
= (start 
> end
) ? 0 : (end
-start
)+1; 
 259     if (start 
&& newlen
) memmove(sh
->buf
, sh
->buf
+start
, newlen
); 
 261     sh
->free 
= sh
->free
+(sh
->len
-newlen
); 
 266 void sdstolower(sds s
) { 
 267     int len 
= sdslen(s
), j
; 
 269     for (j 
= 0; j 
< len
; j
++) s
[j
] = tolower(s
[j
]); 
 272 void sdstoupper(sds s
) { 
 273     int len 
= sdslen(s
), j
; 
 275     for (j 
= 0; j 
< len
; j
++) s
[j
] = toupper(s
[j
]); 
 278 int sdscmp(sds s1
, sds s2
) { 
 279     size_t l1
, l2
, minlen
; 
 284     minlen 
= (l1 
< l2
) ? l1 
: l2
; 
 285     cmp 
= memcmp(s1
,s2
,minlen
); 
 286     if (cmp 
== 0) return l1
-l2
; 
 290 /* Split 's' with separator in 'sep'. An array 
 291  * of sds strings is returned. *count will be set 
 292  * by reference to the number of tokens returned. 
 294  * On out of memory, zero length string, zero length 
 295  * separator, NULL is returned. 
 297  * Note that 'sep' is able to split a string using 
 298  * a multi-character separator. For example 
 299  * sdssplit("foo_-_bar","_-_"); will return two 
 300  * elements "foo" and "bar". 
 302  * This version of the function is binary-safe but 
 303  * requires length arguments. sdssplit() is just the 
 304  * same function but for zero-terminated strings. 
 306 sds 
*sdssplitlen(char *s
, int len
, char *sep
, int seplen
, int *count
) { 
 307     int elements 
= 0, slots 
= 5, start 
= 0, j
; 
 310     if (seplen 
< 1 || len 
< 0) return NULL
; 
 312     tokens 
= zmalloc(sizeof(sds
)*slots
); 
 313 #ifdef SDS_ABORT_ON_OOM 
 314     if (tokens 
== NULL
) sdsOomAbort(); 
 316     if (tokens 
== NULL
) return NULL
; 
 323     for (j 
= 0; j 
< (len
-(seplen
-1)); j
++) { 
 324         /* make sure there is room for the next element and the final one */ 
 325         if (slots 
< elements
+2) { 
 329             newtokens 
= zrealloc(tokens
,sizeof(sds
)*slots
); 
 330             if (newtokens 
== NULL
) { 
 331 #ifdef SDS_ABORT_ON_OOM 
 339         /* search the separator */ 
 340         if ((seplen 
== 1 && *(s
+j
) == sep
[0]) || (memcmp(s
+j
,sep
,seplen
) == 0)) { 
 341             tokens
[elements
] = sdsnewlen(s
+start
,j
-start
); 
 342             if (tokens
[elements
] == NULL
) { 
 343 #ifdef SDS_ABORT_ON_OOM 
 351             j 
= j
+seplen
-1; /* skip the separator */ 
 354     /* Add the final element. We are sure there is room in the tokens array. */ 
 355     tokens
[elements
] = sdsnewlen(s
+start
,len
-start
); 
 356     if (tokens
[elements
] == NULL
) { 
 357 #ifdef SDS_ABORT_ON_OOM 
 367 #ifndef SDS_ABORT_ON_OOM 
 371         for (i 
= 0; i 
< elements
; i
++) sdsfree(tokens
[i
]); 
 379 void sdsfreesplitres(sds 
*tokens
, int count
) { 
 382         sdsfree(tokens
[count
]); 
 386 sds 
sdsfromlonglong(long long value
) { 
 388     unsigned long long v
; 
 390     v 
= (value 
< 0) ? -value 
: value
; 
 391     p 
= buf
+31; /* point to the last character */ 
 396     if (value 
< 0) *p
-- = '-'; 
 398     return sdsnewlen(p
,32-(p
-buf
)); 
 401 sds 
sdscatrepr(sds s
, char *p
, size_t len
) { 
 402     s 
= sdscatlen(s
,"\"",1); 
 407             s 
= sdscatprintf(s
,"\\%c",*p
); 
 409         case '\n': s 
= sdscatlen(s
,"\\n",2); break; 
 410         case '\r': s 
= sdscatlen(s
,"\\r",2); break; 
 411         case '\t': s 
= sdscatlen(s
,"\\t",2); break; 
 412         case '\a': s 
= sdscatlen(s
,"\\a",2); break; 
 413         case '\b': s 
= sdscatlen(s
,"\\b",2); break; 
 416                 s 
= sdscatprintf(s
,"%c",*p
); 
 418                 s 
= sdscatprintf(s
,"\\x%02x",(unsigned char)*p
); 
 423     return sdscatlen(s
,"\"",1); 
 426 /* Helper function for sdssplitargs() that returns non zero if 'c' 
 427  * is a valid hex digit. */ 
 428 int is_hex_digit(char c
) { 
 429     return (c 
>= '0' && c 
<= '9') || (c 
>= 'a' && c 
<= 'f') || 
 430            (c 
>= 'A' && c 
<= 'F'); 
 433 /* Helper function for sdssplitargs() that converts an hex digit into an 
 434  * integer from 0 to 15 */ 
 435 int hex_digit_to_int(char c
) { 
 447     case 'a': case 'A': return 10; 
 448     case 'b': case 'B': return 11; 
 449     case 'c': case 'C': return 12; 
 450     case 'd': case 'D': return 13; 
 451     case 'e': case 'E': return 14; 
 452     case 'f': case 'F': return 15; 
 457 /* Split a line into arguments, where every argument can be in the 
 458  * following programming-language REPL-alike form: 
 460  * foo bar "newline are supported\n" and "\xff\x00otherstuff" 
 462  * The number of arguments is stored into *argc, and an array 
 463  * of sds is returned. The caller should sdsfree() all the returned 
 464  * strings and finally zfree() the array itself. 
 466  * Note that sdscatrepr() is able to convert back a string into 
 467  * a quoted string in the same format sdssplitargs() is able to parse. 
 469 sds 
*sdssplitargs(char *line
, int *argc
) { 
 471     char *current 
= NULL
; 
 472     char **vector 
= NULL
; 
 477         while(*p 
&& isspace(*p
)) p
++; 
 480             int inq
=0; /* set to 1 if we are in "quotes" */ 
 483             if (current 
== NULL
) current 
= sdsempty(); 
 486                     if (*p 
== '\\' && *(p
+1) == 'x' && 
 487                                              is_hex_digit(*(p
+2)) && 
 488                                              is_hex_digit(*(p
+3))) 
 492                         byte 
= (hex_digit_to_int(*(p
+2))*16)+ 
 493                                 hex_digit_to_int(*(p
+3)); 
 494                         current 
= sdscatlen(current
,(char*)&byte
,1); 
 496                     } else if (*p 
== '\\' && *(p
+1)) { 
 501                         case 'n': c 
= '\n'; break; 
 502                         case 'r': c 
= '\r'; break; 
 503                         case 't': c 
= '\t'; break; 
 504                         case 'b': c 
= '\b'; break; 
 505                         case 'a': c 
= '\a'; break; 
 506                         default: c 
= *p
; break; 
 508                         current 
= sdscatlen(current
,&c
,1); 
 509                     } else if (*p 
== '"') { 
 510                         /* closing quote must be followed by a space */ 
 511                         if (*(p
+1) && !isspace(*(p
+1))) goto err
; 
 514                         /* unterminated quotes */ 
 517                         current 
= sdscatlen(current
,p
,1); 
 532                         current 
= sdscatlen(current
,p
,1); 
 538             /* add the token to the vector */ 
 539             vector 
= zrealloc(vector
,((*argc
)+1)*sizeof(char*)); 
 540             vector
[*argc
] = current
; 
 550         sdsfree(vector
[*argc
]); 
 552     if (current
) sdsfree(current
); 
 556 void sdssplitargs_free(sds 
*argv
, int argc
) { 
 559     for (j 
= 0 ;j 
< argc
; j
++) sdsfree(argv
[j
]); 
 563 /* Modify the string substituting all the occurrences of the set of 
 564  * characters specifed in the 'from' string to the corresponding character 
 567  * For instance: sdsmapchars(mystring, "ho", "01", 2) 
 568  * will have the effect of turning the string "hello" into "0ell1". 
 570  * The function returns the sds string pointer, that is always the same 
 571  * as the input pointer since no resize is needed. */ 
 572 sds 
sdsmapchars(sds s
, char *from
, char *to
, size_t setlen
) { 
 573     size_t j
, i
, l 
= sdslen(s
); 
 575     for (j 
= 0; j 
< l
; j
++) { 
 576         for (i 
= 0; i 
< setlen
; i
++) { 
 577             if (s
[j
] == from
[i
]) { 
 588 #include "testhelp.h" 
 592         sds x 
= sdsnew("foo"), y
; 
 594         test_cond("Create a string and obtain the length", 
 595             sdslen(x
) == 3 && memcmp(x
,"foo\0",4) == 0) 
 598         x 
= sdsnewlen("foo",2); 
 599         test_cond("Create a string with specified length", 
 600             sdslen(x
) == 2 && memcmp(x
,"fo\0",3) == 0) 
 603         test_cond("Strings concatenation", 
 604             sdslen(x
) == 5 && memcmp(x
,"fobar\0",6) == 0); 
 607         test_cond("sdscpy() against an originally longer string", 
 608             sdslen(x
) == 1 && memcmp(x
,"a\0",2) == 0) 
 610         x 
= sdscpy(x
,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); 
 611         test_cond("sdscpy() against an originally shorter string", 
 613             memcmp(x
,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0) 
 616         x 
= sdscatprintf(sdsempty(),"%d",123); 
 617         test_cond("sdscatprintf() seems working in the base case", 
 618             sdslen(x
) == 3 && memcmp(x
,"123\0",4) ==0) 
 621         x 
= sdstrim(sdsnew("xxciaoyyy"),"xy"); 
 622         test_cond("sdstrim() correctly trims characters", 
 623             sdslen(x
) == 4 && memcmp(x
,"ciao\0",5) == 0) 
 625         y 
= sdsrange(sdsdup(x
),1,1); 
 626         test_cond("sdsrange(...,1,1)", 
 627             sdslen(y
) == 1 && memcmp(y
,"i\0",2) == 0) 
 630         y 
= sdsrange(sdsdup(x
),1,-1); 
 631         test_cond("sdsrange(...,1,-1)", 
 632             sdslen(y
) == 3 && memcmp(y
,"iao\0",4) == 0) 
 635         y 
= sdsrange(sdsdup(x
),-2,-1); 
 636         test_cond("sdsrange(...,-2,-1)", 
 637             sdslen(y
) == 2 && memcmp(y
,"ao\0",3) == 0) 
 640         y 
= sdsrange(sdsdup(x
),2,1); 
 641         test_cond("sdsrange(...,2,1)", 
 642             sdslen(y
) == 0 && memcmp(y
,"\0",1) == 0) 
 645         y 
= sdsrange(sdsdup(x
),1,100); 
 646         test_cond("sdsrange(...,1,100)", 
 647             sdslen(y
) == 3 && memcmp(y
,"iao\0",4) == 0) 
 650         y 
= sdsrange(sdsdup(x
),100,100); 
 651         test_cond("sdsrange(...,100,100)", 
 652             sdslen(y
) == 0 && memcmp(y
,"\0",1) == 0) 
 658         test_cond("sdscmp(foo,foa)", sdscmp(x
,y
) > 0) 
 664         test_cond("sdscmp(bar,bar)", sdscmp(x
,y
) == 0) 
 670         test_cond("sdscmp(bar,bar)", sdscmp(x
,y
) < 0)