]>
git.saurik.com Git - redis.git/blob - src/sds.c
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.
39 sds
sdsnewlen(const void *init
, size_t initlen
) {
43 sh
= zmalloc(sizeof(struct sdshdr
)+initlen
+1);
45 sh
= zcalloc(sizeof(struct sdshdr
)+initlen
+1);
47 if (sh
== NULL
) return NULL
;
51 memcpy(sh
->buf
, init
, initlen
);
52 sh
->buf
[initlen
] = '\0';
53 return (char*)sh
->buf
;
57 return sdsnewlen("",0);
60 sds
sdsnew(const char *init
) {
61 size_t initlen
= (init
== NULL
) ? 0 : strlen(init
);
62 return sdsnewlen(init
, initlen
);
65 sds
sdsdup(const sds s
) {
66 return sdsnewlen(s
, sdslen(s
));
70 if (s
== NULL
) return;
71 zfree(s
-sizeof(struct sdshdr
));
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
);
81 void sdsclear(sds s
) {
82 struct sdshdr
*sh
= (void*) (s
-(sizeof(struct sdshdr
)));
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.
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
);
99 if (free
>= addlen
) return s
;
101 sh
= (void*) (s
-(sizeof(struct sdshdr
)));
102 newlen
= (len
+addlen
);
103 if (newlen
< SDS_MAX_PREALLOC
)
106 newlen
+= SDS_MAX_PREALLOC
;
107 newsh
= zrealloc(sh
, sizeof(struct sdshdr
)+newlen
+1);
108 if (newsh
== NULL
) return NULL
;
110 newsh
->free
= newlen
- len
;
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
) {
120 sh
= (void*) (s
-(sizeof(struct sdshdr
)));
121 sh
= zrealloc(sh
, sizeof(struct sdshdr
)+sh
->len
+1);
126 size_t sdsAllocSize(sds s
) {
127 struct sdshdr
*sh
= (void*) (s
-(sizeof(struct sdshdr
)));
129 return sizeof(*sh
)+sh
->len
+sh
->free
+1;
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.
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.
140 * Note: it is possible to use a negative increment in order to
141 * right-trim the string.
143 * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the
144 * following schema to cat bytes coming from the kerenl to the end of an
145 * sds string new things without copying into an intermediate buffer:
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);
153 void sdsIncrLen(sds s
, int incr
) {
154 struct sdshdr
*sh
= (void*) (s
-(sizeof(struct sdshdr
)));
156 assert(sh
->free
>= incr
);
159 assert(sh
->free
>= 0);
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
;
169 if (len
<= curlen
) return s
;
170 s
= sdsMakeRoomFor(s
,len
-curlen
);
171 if (s
== NULL
) return NULL
;
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
;
178 sh
->free
= totlen
-sh
->len
;
182 sds
sdscatlen(sds s
, const void *t
, size_t len
) {
184 size_t curlen
= sdslen(s
);
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';
196 sds
sdscat(sds s
, const char *t
) {
197 return sdscatlen(s
, t
, strlen(t
));
200 sds
sdscatsds(sds s
, const sds t
) {
201 return sdscatlen(s
, t
, sdslen(t
));
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
;
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
;
217 sh
->free
= totlen
-len
;
221 sds
sdscpy(sds s
, const char *t
) {
222 return sdscpylen(s
, t
, strlen(t
));
225 sds
sdscatvprintf(sds s
, const char *fmt
, va_list ap
) {
231 buf
= zmalloc(buflen
);
232 if (buf
== NULL
) return NULL
;
233 buf
[buflen
-2] = '\0';
235 vsnprintf(buf
, buflen
, fmt
, cpy
);
236 if (buf
[buflen
-2] != '\0') {
248 sds
sdscatprintf(sds s
, const char *fmt
, ...) {
252 t
= sdscatvprintf(s
,fmt
,ap
);
257 sds
sdstrim(sds s
, const char *cset
) {
258 struct sdshdr
*sh
= (void*) (s
-(sizeof(struct sdshdr
)));
259 char *start
, *end
, *sp
, *ep
;
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
);
269 sh
->free
= sh
->free
+(sh
->len
-len
);
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
);
278 if (len
== 0) return s
;
281 if (start
< 0) start
= 0;
285 if (end
< 0) end
= 0;
287 newlen
= (start
> end
) ? 0 : (end
-start
)+1;
289 if (start
>= (signed)len
) {
291 } else if (end
>= (signed)len
) {
293 newlen
= (start
> end
) ? 0 : (end
-start
)+1;
298 if (start
&& newlen
) memmove(sh
->buf
, sh
->buf
+start
, newlen
);
300 sh
->free
= sh
->free
+(sh
->len
-newlen
);
305 void sdstolower(sds s
) {
306 int len
= sdslen(s
), j
;
308 for (j
= 0; j
< len
; j
++) s
[j
] = tolower(s
[j
]);
311 void sdstoupper(sds s
) {
312 int len
= sdslen(s
), j
;
314 for (j
= 0; j
< len
; j
++) s
[j
] = toupper(s
[j
]);
317 int sdscmp(const sds s1
, const sds s2
) {
318 size_t l1
, l2
, minlen
;
323 minlen
= (l1
< l2
) ? l1
: l2
;
324 cmp
= memcmp(s1
,s2
,minlen
);
325 if (cmp
== 0) return l1
-l2
;
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.
333 * On out of memory, zero length string, zero length
334 * separator, NULL is returned.
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".
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.
345 sds
*sdssplitlen(const char *s
, int len
, const char *sep
, int seplen
, int *count
) {
346 int elements
= 0, slots
= 5, start
= 0, j
;
349 if (seplen
< 1 || len
< 0) return NULL
;
351 tokens
= zmalloc(sizeof(sds
)*slots
);
352 if (tokens
== NULL
) return NULL
;
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) {
364 newtokens
= zrealloc(tokens
,sizeof(sds
)*slots
);
365 if (newtokens
== NULL
) goto cleanup
;
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
;
374 j
= j
+seplen
-1; /* skip the separator */
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
;
387 for (i
= 0; i
< elements
; i
++) sdsfree(tokens
[i
]);
394 void sdsfreesplitres(sds
*tokens
, int count
) {
397 sdsfree(tokens
[count
]);
401 sds
sdsfromlonglong(long long value
) {
403 unsigned long long v
;
405 v
= (value
< 0) ? -value
: value
;
406 p
= buf
+31; /* point to the last character */
411 if (value
< 0) *p
-- = '-';
413 return sdsnewlen(p
,32-(p
-buf
));
416 sds
sdscatrepr(sds s
, const char *p
, size_t len
) {
417 s
= sdscatlen(s
,"\"",1);
422 s
= sdscatprintf(s
,"\\%c",*p
);
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;
431 s
= sdscatprintf(s
,"%c",*p
);
433 s
= sdscatprintf(s
,"\\x%02x",(unsigned char)*p
);
438 return sdscatlen(s
,"\"",1);
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');
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
) {
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;
472 /* Split a line into arguments, where every argument can be in the
473 * following programming-language REPL-alike form:
475 * foo bar "newline are supported\n" and "\xff\x00otherstuff"
477 * The number of arguments is stored into *argc, and an array
478 * of sds is returned. The caller should sdsfree() all the returned
479 * strings and finally zfree() the array itself.
481 * Note that sdscatrepr() is able to convert back a string into
482 * a quoted string in the same format sdssplitargs() is able to parse.
484 sds
*sdssplitargs(const char *line
, int *argc
) {
485 const char *p
= line
;
486 char *current
= NULL
;
487 char **vector
= NULL
;
492 while(*p
&& isspace(*p
)) p
++;
495 int inq
=0; /* set to 1 if we are in "quotes" */
496 int insq
=0; /* set to 1 if we are in 'single quotes' */
499 if (current
== NULL
) current
= sdsempty();
502 if (*p
== '\\' && *(p
+1) == 'x' &&
503 is_hex_digit(*(p
+2)) &&
504 is_hex_digit(*(p
+3)))
508 byte
= (hex_digit_to_int(*(p
+2))*16)+
509 hex_digit_to_int(*(p
+3));
510 current
= sdscatlen(current
,(char*)&byte
,1);
512 } else if (*p
== '\\' && *(p
+1)) {
517 case 'n': c
= '\n'; break;
518 case 'r': c
= '\r'; break;
519 case 't': c
= '\t'; break;
520 case 'b': c
= '\b'; break;
521 case 'a': c
= '\a'; break;
522 default: c
= *p
; break;
524 current
= sdscatlen(current
,&c
,1);
525 } else if (*p
== '"') {
526 /* closing quote must be followed by a space or
528 if (*(p
+1) && !isspace(*(p
+1))) goto err
;
531 /* unterminated quotes */
534 current
= sdscatlen(current
,p
,1);
537 if (*p
== '\\' && *(p
+1) == '\'') {
539 current
= sdscatlen(current
,"'",1);
540 } else if (*p
== '\'') {
541 /* closing quote must be followed by a space or
543 if (*(p
+1) && !isspace(*(p
+1))) goto err
;
546 /* unterminated quotes */
549 current
= sdscatlen(current
,p
,1);
567 current
= sdscatlen(current
,p
,1);
573 /* add the token to the vector */
574 vector
= zrealloc(vector
,((*argc
)+1)*sizeof(char*));
575 vector
[*argc
] = current
;
585 sdsfree(vector
[*argc
]);
587 if (current
) sdsfree(current
);
591 void sdssplitargs_free(sds
*argv
, int argc
) {
594 for (j
= 0 ;j
< argc
; j
++) sdsfree(argv
[j
]);
598 /* Modify the string substituting all the occurrences of the set of
599 * characters specifed in the 'from' string to the corresponding character
602 * For instance: sdsmapchars(mystring, "ho", "01", 2)
603 * will have the effect of turning the string "hello" into "0ell1".
605 * The function returns the sds string pointer, that is always the same
606 * as the input pointer since no resize is needed. */
607 sds
sdsmapchars(sds s
, const char *from
, const char *to
, size_t setlen
) {
608 size_t j
, i
, l
= sdslen(s
);
610 for (j
= 0; j
< l
; j
++) {
611 for (i
= 0; i
< setlen
; i
++) {
612 if (s
[j
] == from
[i
]) {
623 #include "testhelp.h"
628 sds x
= sdsnew("foo"), y
;
630 test_cond("Create a string and obtain the length",
631 sdslen(x
) == 3 && memcmp(x
,"foo\0",4) == 0)
634 x
= sdsnewlen("foo",2);
635 test_cond("Create a string with specified length",
636 sdslen(x
) == 2 && memcmp(x
,"fo\0",3) == 0)
639 test_cond("Strings concatenation",
640 sdslen(x
) == 5 && memcmp(x
,"fobar\0",6) == 0);
643 test_cond("sdscpy() against an originally longer string",
644 sdslen(x
) == 1 && memcmp(x
,"a\0",2) == 0)
646 x
= sdscpy(x
,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk");
647 test_cond("sdscpy() against an originally shorter string",
649 memcmp(x
,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0)
652 x
= sdscatprintf(sdsempty(),"%d",123);
653 test_cond("sdscatprintf() seems working in the base case",
654 sdslen(x
) == 3 && memcmp(x
,"123\0",4) ==0)
657 x
= sdstrim(sdsnew("xxciaoyyy"),"xy");
658 test_cond("sdstrim() correctly trims characters",
659 sdslen(x
) == 4 && memcmp(x
,"ciao\0",5) == 0)
661 y
= sdsrange(sdsdup(x
),1,1);
662 test_cond("sdsrange(...,1,1)",
663 sdslen(y
) == 1 && memcmp(y
,"i\0",2) == 0)
666 y
= sdsrange(sdsdup(x
),1,-1);
667 test_cond("sdsrange(...,1,-1)",
668 sdslen(y
) == 3 && memcmp(y
,"iao\0",4) == 0)
671 y
= sdsrange(sdsdup(x
),-2,-1);
672 test_cond("sdsrange(...,-2,-1)",
673 sdslen(y
) == 2 && memcmp(y
,"ao\0",3) == 0)
676 y
= sdsrange(sdsdup(x
),2,1);
677 test_cond("sdsrange(...,2,1)",
678 sdslen(y
) == 0 && memcmp(y
,"\0",1) == 0)
681 y
= sdsrange(sdsdup(x
),1,100);
682 test_cond("sdsrange(...,1,100)",
683 sdslen(y
) == 3 && memcmp(y
,"iao\0",4) == 0)
686 y
= sdsrange(sdsdup(x
),100,100);
687 test_cond("sdsrange(...,100,100)",
688 sdslen(y
) == 0 && memcmp(y
,"\0",1) == 0)
694 test_cond("sdscmp(foo,foa)", sdscmp(x
,y
) > 0)
700 test_cond("sdscmp(bar,bar)", sdscmp(x
,y
) == 0)
706 test_cond("sdscmp(bar,bar)", sdscmp(x
,y
) < 0)
713 sh
= (void*) (x
-(sizeof(struct sdshdr
)));
714 test_cond("sdsnew() free/len buffers", sh
->len
== 1 && sh
->free
== 0);
715 x
= sdsMakeRoomFor(x
,1);
716 sh
= (void*) (x
-(sizeof(struct sdshdr
)));
717 test_cond("sdsMakeRoomFor()", sh
->len
== 1 && sh
->free
> 0);
721 test_cond("sdsIncrLen() -- content", x
[0] == '0' && x
[1] == '1');
722 test_cond("sdsIncrLen() -- len", sh
->len
== 2);
723 test_cond("sdsIncrLen() -- free", sh
->free
== oldfree
-1);