]>
git.saurik.com Git - redis.git/blob - deps/hiredis/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.
31 #define SDS_ABORT_ON_OOM
39 static void sdsOomAbort(void) {
40 fprintf(stderr
,"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n");
44 sds
sdsnewlen(const void *init
, size_t initlen
) {
47 sh
= malloc(sizeof(struct sdshdr
)+initlen
+1);
48 #ifdef SDS_ABORT_ON_OOM
49 if (sh
== NULL
) sdsOomAbort();
51 if (sh
== NULL
) return NULL
;
56 if (init
) memcpy(sh
->buf
, init
, initlen
);
57 else memset(sh
->buf
,0,initlen
);
59 sh
->buf
[initlen
] = '\0';
60 return (char*)sh
->buf
;
64 return sdsnewlen("",0);
67 sds
sdsnew(const char *init
) {
68 size_t initlen
= (init
== NULL
) ? 0 : strlen(init
);
69 return sdsnewlen(init
, initlen
);
72 sds
sdsdup(const sds s
) {
73 return sdsnewlen(s
, sdslen(s
));
77 if (s
== NULL
) return;
78 free(s
-sizeof(struct sdshdr
));
81 void sdsupdatelen(sds s
) {
82 struct sdshdr
*sh
= (void*) (s
-(sizeof(struct sdshdr
)));
83 int reallen
= strlen(s
);
84 sh
->free
+= (sh
->len
-reallen
);
88 static sds
sdsMakeRoomFor(sds s
, size_t addlen
) {
89 struct sdshdr
*sh
, *newsh
;
90 size_t free
= sdsavail(s
);
93 if (free
>= addlen
) return s
;
95 sh
= (void*) (s
-(sizeof(struct sdshdr
)));
96 newlen
= (len
+addlen
)*2;
97 newsh
= realloc(sh
, sizeof(struct sdshdr
)+newlen
+1);
98 #ifdef SDS_ABORT_ON_OOM
99 if (newsh
== NULL
) sdsOomAbort();
101 if (newsh
== NULL
) return NULL
;
104 newsh
->free
= newlen
- len
;
108 /* Grow the sds to have the specified length. Bytes that were not part of
109 * the original length of the sds will be set to zero. */
110 sds
sdsgrowzero(sds s
, size_t len
) {
111 struct sdshdr
*sh
= (void*)(s
-(sizeof(struct sdshdr
)));
112 size_t totlen
, curlen
= sh
->len
;
114 if (len
<= curlen
) return s
;
115 s
= sdsMakeRoomFor(s
,len
-curlen
);
116 if (s
== NULL
) return NULL
;
118 /* Make sure added region doesn't contain garbage */
119 sh
= (void*)(s
-(sizeof(struct sdshdr
)));
120 memset(s
+curlen
,0,(len
-curlen
+1)); /* also set trailing \0 byte */
121 totlen
= sh
->len
+sh
->free
;
123 sh
->free
= totlen
-sh
->len
;
127 sds
sdscatlen(sds s
, const void *t
, size_t len
) {
129 size_t curlen
= sdslen(s
);
131 s
= sdsMakeRoomFor(s
,len
);
132 if (s
== NULL
) return NULL
;
133 sh
= (void*) (s
-(sizeof(struct sdshdr
)));
134 memcpy(s
+curlen
, t
, len
);
135 sh
->len
= curlen
+len
;
136 sh
->free
= sh
->free
-len
;
137 s
[curlen
+len
] = '\0';
141 sds
sdscat(sds s
, const char *t
) {
142 return sdscatlen(s
, t
, strlen(t
));
145 sds
sdscpylen(sds s
, char *t
, size_t len
) {
146 struct sdshdr
*sh
= (void*) (s
-(sizeof(struct sdshdr
)));
147 size_t totlen
= sh
->free
+sh
->len
;
150 s
= sdsMakeRoomFor(s
,len
-sh
->len
);
151 if (s
== NULL
) return NULL
;
152 sh
= (void*) (s
-(sizeof(struct sdshdr
)));
153 totlen
= sh
->free
+sh
->len
;
158 sh
->free
= totlen
-len
;
162 sds
sdscpy(sds s
, char *t
) {
163 return sdscpylen(s
, t
, strlen(t
));
166 sds
sdscatvprintf(sds s
, const char *fmt
, va_list ap
) {
172 buf
= malloc(buflen
);
173 #ifdef SDS_ABORT_ON_OOM
174 if (buf
== NULL
) sdsOomAbort();
176 if (buf
== NULL
) return NULL
;
178 buf
[buflen
-2] = '\0';
180 vsnprintf(buf
, buflen
, fmt
, cpy
);
181 if (buf
[buflen
-2] != '\0') {
193 sds
sdscatprintf(sds s
, const char *fmt
, ...) {
197 t
= sdscatvprintf(s
,fmt
,ap
);
202 sds
sdstrim(sds s
, const char *cset
) {
203 struct sdshdr
*sh
= (void*) (s
-(sizeof(struct sdshdr
)));
204 char *start
, *end
, *sp
, *ep
;
208 ep
= end
= s
+sdslen(s
)-1;
209 while(sp
<= end
&& strchr(cset
, *sp
)) sp
++;
210 while(ep
> start
&& strchr(cset
, *ep
)) ep
--;
211 len
= (sp
> ep
) ? 0 : ((ep
-sp
)+1);
212 if (sh
->buf
!= sp
) memmove(sh
->buf
, sp
, len
);
214 sh
->free
= sh
->free
+(sh
->len
-len
);
219 sds
sdsrange(sds s
, int start
, int end
) {
220 struct sdshdr
*sh
= (void*) (s
-(sizeof(struct sdshdr
)));
221 size_t newlen
, len
= sdslen(s
);
223 if (len
== 0) return s
;
226 if (start
< 0) start
= 0;
230 if (end
< 0) end
= 0;
232 newlen
= (start
> end
) ? 0 : (end
-start
)+1;
234 if (start
>= (signed)len
) {
236 } else if (end
>= (signed)len
) {
238 newlen
= (start
> end
) ? 0 : (end
-start
)+1;
243 if (start
&& newlen
) memmove(sh
->buf
, sh
->buf
+start
, newlen
);
245 sh
->free
= sh
->free
+(sh
->len
-newlen
);
250 void sdstolower(sds s
) {
251 int len
= sdslen(s
), j
;
253 for (j
= 0; j
< len
; j
++) s
[j
] = tolower(s
[j
]);
256 void sdstoupper(sds s
) {
257 int len
= sdslen(s
), j
;
259 for (j
= 0; j
< len
; j
++) s
[j
] = toupper(s
[j
]);
262 int sdscmp(sds s1
, sds s2
) {
263 size_t l1
, l2
, minlen
;
268 minlen
= (l1
< l2
) ? l1
: l2
;
269 cmp
= memcmp(s1
,s2
,minlen
);
270 if (cmp
== 0) return l1
-l2
;
274 /* Split 's' with separator in 'sep'. An array
275 * of sds strings is returned. *count will be set
276 * by reference to the number of tokens returned.
278 * On out of memory, zero length string, zero length
279 * separator, NULL is returned.
281 * Note that 'sep' is able to split a string using
282 * a multi-character separator. For example
283 * sdssplit("foo_-_bar","_-_"); will return two
284 * elements "foo" and "bar".
286 * This version of the function is binary-safe but
287 * requires length arguments. sdssplit() is just the
288 * same function but for zero-terminated strings.
290 sds
*sdssplitlen(char *s
, int len
, char *sep
, int seplen
, int *count
) {
291 int elements
= 0, slots
= 5, start
= 0, j
;
293 sds
*tokens
= malloc(sizeof(sds
)*slots
);
294 #ifdef SDS_ABORT_ON_OOM
295 if (tokens
== NULL
) sdsOomAbort();
297 if (seplen
< 1 || len
< 0 || tokens
== NULL
) return NULL
;
302 for (j
= 0; j
< (len
-(seplen
-1)); j
++) {
303 /* make sure there is room for the next element and the final one */
304 if (slots
< elements
+2) {
308 newtokens
= realloc(tokens
,sizeof(sds
)*slots
);
309 if (newtokens
== NULL
) {
310 #ifdef SDS_ABORT_ON_OOM
318 /* search the separator */
319 if ((seplen
== 1 && *(s
+j
) == sep
[0]) || (memcmp(s
+j
,sep
,seplen
) == 0)) {
320 tokens
[elements
] = sdsnewlen(s
+start
,j
-start
);
321 if (tokens
[elements
] == NULL
) {
322 #ifdef SDS_ABORT_ON_OOM
330 j
= j
+seplen
-1; /* skip the separator */
333 /* Add the final element. We are sure there is room in the tokens array. */
334 tokens
[elements
] = sdsnewlen(s
+start
,len
-start
);
335 if (tokens
[elements
] == NULL
) {
336 #ifdef SDS_ABORT_ON_OOM
346 #ifndef SDS_ABORT_ON_OOM
350 for (i
= 0; i
< elements
; i
++) sdsfree(tokens
[i
]);
357 void sdsfreesplitres(sds
*tokens
, int count
) {
360 sdsfree(tokens
[count
]);
364 sds
sdsfromlonglong(long long value
) {
366 unsigned long long v
;
368 v
= (value
< 0) ? -value
: value
;
369 p
= buf
+31; /* point to the last character */
374 if (value
< 0) *p
-- = '-';
376 return sdsnewlen(p
,32-(p
-buf
));
379 sds
sdscatrepr(sds s
, char *p
, size_t len
) {
380 s
= sdscatlen(s
,"\"",1);
385 s
= sdscatprintf(s
,"\\%c",*p
);
387 case '\n': s
= sdscatlen(s
,"\\n",1); break;
388 case '\r': s
= sdscatlen(s
,"\\r",1); break;
389 case '\t': s
= sdscatlen(s
,"\\t",1); break;
390 case '\a': s
= sdscatlen(s
,"\\a",1); break;
391 case '\b': s
= sdscatlen(s
,"\\b",1); break;
394 s
= sdscatprintf(s
,"%c",*p
);
396 s
= sdscatprintf(s
,"\\x%02x",(unsigned char)*p
);
401 return sdscatlen(s
,"\"",1);
404 /* Split a line into arguments, where every argument can be in the
405 * following programming-language REPL-alike form:
407 * foo bar "newline are supported\n" and "\xff\x00otherstuff"
409 * The number of arguments is stored into *argc, and an array
410 * of sds is returned. The caller should sdsfree() all the returned
411 * strings and finally free() the array itself.
413 * Note that sdscatrepr() is able to convert back a string into
414 * a quoted string in the same format sdssplitargs() is able to parse.
416 sds
*sdssplitargs(char *line
, int *argc
) {
418 char *current
= NULL
;
419 char **vector
= NULL
;
424 while(*p
&& isspace(*p
)) p
++;
427 int inq
=0; /* set to 1 if we are in "quotes" */
430 if (current
== NULL
) current
= sdsempty();
433 if (*p
== '\\' && *(p
+1)) {
438 case 'n': c
= '\n'; break;
439 case 'r': c
= '\r'; break;
440 case 't': c
= '\t'; break;
441 case 'b': c
= '\b'; break;
442 case 'a': c
= '\a'; break;
443 default: c
= *p
; break;
445 current
= sdscatlen(current
,&c
,1);
446 } else if (*p
== '"') {
447 /* closing quote must be followed by a space */
448 if (*(p
+1) && !isspace(*(p
+1))) goto err
;
451 /* unterminated quotes */
454 current
= sdscatlen(current
,p
,1);
469 current
= sdscatlen(current
,p
,1);
475 /* add the token to the vector */
476 vector
= realloc(vector
,((*argc
)+1)*sizeof(char*));
477 vector
[*argc
] = current
;
487 sdsfree(vector
[*argc
]);
489 if (current
) sdsfree(current
);
496 int __failed_tests
= 0;
498 #define test_cond(descr,_c) do { \
499 __test_num++; printf("%d - %s: ", __test_num, descr); \
500 if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \
502 #define test_report() do { \
503 printf("%d tests, %d passed, %d failed\n", __test_num, \
504 __test_num-__failed_tests, __failed_tests); \
505 if (__failed_tests) { \
506 printf("=== WARNING === We have failed tests here...\n"); \
512 sds x
= sdsnew("foo"), y
;
514 test_cond("Create a string and obtain the length",
515 sdslen(x
) == 3 && memcmp(x
,"foo\0",4) == 0)
518 x
= sdsnewlen("foo",2);
519 test_cond("Create a string with specified length",
520 sdslen(x
) == 2 && memcmp(x
,"fo\0",3) == 0)
523 test_cond("Strings concatenation",
524 sdslen(x
) == 5 && memcmp(x
,"fobar\0",6) == 0);
527 test_cond("sdscpy() against an originally longer string",
528 sdslen(x
) == 1 && memcmp(x
,"a\0",2) == 0)
530 x
= sdscpy(x
,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk");
531 test_cond("sdscpy() against an originally shorter string",
533 memcmp(x
,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0)
536 x
= sdscatprintf(sdsempty(),"%d",123);
537 test_cond("sdscatprintf() seems working in the base case",
538 sdslen(x
) == 3 && memcmp(x
,"123\0",4) ==0)
541 x
= sdstrim(sdsnew("xxciaoyyy"),"xy");
542 test_cond("sdstrim() correctly trims characters",
543 sdslen(x
) == 4 && memcmp(x
,"ciao\0",5) == 0)
545 y
= sdsrange(sdsdup(x
),1,1);
546 test_cond("sdsrange(...,1,1)",
547 sdslen(y
) == 1 && memcmp(y
,"i\0",2) == 0)
550 y
= sdsrange(sdsdup(x
),1,-1);
551 test_cond("sdsrange(...,1,-1)",
552 sdslen(y
) == 3 && memcmp(y
,"iao\0",4) == 0)
555 y
= sdsrange(sdsdup(x
),-2,-1);
556 test_cond("sdsrange(...,-2,-1)",
557 sdslen(y
) == 2 && memcmp(y
,"ao\0",3) == 0)
560 y
= sdsrange(sdsdup(x
),2,1);
561 test_cond("sdsrange(...,2,1)",
562 sdslen(y
) == 0 && memcmp(y
,"\0",1) == 0)
565 y
= sdsrange(sdsdup(x
),1,100);
566 test_cond("sdsrange(...,1,100)",
567 sdslen(y
) == 3 && memcmp(y
,"iao\0",4) == 0)
570 y
= sdsrange(sdsdup(x
),100,100);
571 test_cond("sdsrange(...,100,100)",
572 sdslen(y
) == 0 && memcmp(y
,"\0",1) == 0)
578 test_cond("sdscmp(foo,foa)", sdscmp(x
,y
) > 0)
584 test_cond("sdscmp(bar,bar)", sdscmp(x
,y
) == 0)
590 test_cond("sdscmp(bar,bar)", sdscmp(x
,y
) < 0)