1 : /* SDSLib, A C dynamic strings library
2 : *
3 : * Copyright (c) 2006-2010, 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 13040426 : sds sdsnewlen(const void *init, size_t initlen) {
40 : struct sdshdr *sh;
41 :
42 13040426 : if (init) {
43 11006610 : sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
44 : } else {
45 2033816 : sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
46 : }
47 13040426 : if (sh == NULL) return NULL;
48 13040426 : sh->len = initlen;
49 13040426 : sh->free = 0;
50 13040426 : if (initlen && init)
51 10729502 : memcpy(sh->buf, init, initlen);
52 13040426 : sh->buf[initlen] = '\0';
53 13040426 : return (char*)sh->buf;
54 : }
55 :
56 275782 : sds sdsempty(void) {
57 275782 : return sdsnewlen("",0);
58 : }
59 :
60 8807 : sds sdsnew(const char *init) {
61 8807 : size_t initlen = (init == NULL) ? 0 : strlen(init);
62 8807 : return sdsnewlen(init, initlen);
63 : }
64 :
65 1911754 : sds sdsdup(const sds s) {
66 1911754 : return sdsnewlen(s, sdslen(s));
67 : }
68 :
69 9614542 : void sdsfree(sds s) {
70 9614542 : if (s == NULL) return;
71 9614533 : zfree(s-sizeof(struct sdshdr));
72 : }
73 :
74 0 : void sdsupdatelen(sds s) {
75 0 : struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
76 0 : int reallen = strlen(s);
77 0 : sh->free += (sh->len-reallen);
78 0 : sh->len = reallen;
79 0 : }
80 :
81 4 : void sdsclear(sds s) {
82 4 : struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
83 4 : sh->free += sh->len;
84 4 : sh->len = 0;
85 4 : sh->buf[0] = '\0';
86 4 : }
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 5059968 : sds sdsMakeRoomFor(sds s, size_t addlen) {
95 : struct sdshdr *sh, *newsh;
96 5059968 : size_t free = sdsavail(s);
97 : size_t len, newlen;
98 :
99 5059968 : if (free >= addlen) return s;
100 394692 : len = sdslen(s);
101 394692 : sh = (void*) (s-(sizeof(struct sdshdr)));
102 394692 : newlen = (len+addlen);
103 394692 : if (newlen < SDS_MAX_PREALLOC)
104 394690 : newlen *= 2;
105 : else
106 2 : newlen += SDS_MAX_PREALLOC;
107 394692 : newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);
108 394692 : if (newsh == NULL) return NULL;
109 :
110 394692 : newsh->free = newlen - len;
111 394692 : 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 1283 : sds sdsRemoveFreeSpace(sds s) {
118 : struct sdshdr *sh;
119 :
120 1283 : sh = (void*) (s-(sizeof(struct sdshdr)));
121 1283 : sh = zrealloc(sh, sizeof(struct sdshdr)+sh->len+1);
122 1283 : sh->free = 0;
123 1283 : return sh->buf;
124 : }
125 :
126 7038 : size_t sdsAllocSize(sds s) {
127 7038 : struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
128 :
129 7038 : 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 kerenl 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 1032445 : void sdsIncrLen(sds s, int incr) {
154 1032445 : struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
155 :
156 1032445 : assert(sh->free >= incr);
157 1032445 : sh->len += incr;
158 1032445 : sh->free -= incr;
159 1032445 : assert(sh->free >= 0);
160 1032445 : s[sh->len] = '\0';
161 1032445 : }
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 2013 : sds sdsgrowzero(sds s, size_t len) {
166 2013 : struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
167 2013 : size_t totlen, curlen = sh->len;
168 :
169 2013 : if (len <= curlen) return s;
170 14 : s = sdsMakeRoomFor(s,len-curlen);
171 14 : if (s == NULL) return NULL;
172 :
173 : /* Make sure added region doesn't contain garbage */
174 14 : sh = (void*)(s-(sizeof(struct sdshdr)));
175 14 : memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
176 14 : totlen = sh->len+sh->free;
177 14 : sh->len = len;
178 14 : sh->free = totlen-sh->len;
179 14 : return s;
180 : }
181 :
182 4026968 : sds sdscatlen(sds s, void *t, size_t len) {
183 : struct sdshdr *sh;
184 4026968 : size_t curlen = sdslen(s);
185 :
186 4026968 : s = sdsMakeRoomFor(s,len);
187 4026968 : if (s == NULL) return NULL;
188 4026968 : sh = (void*) (s-(sizeof(struct sdshdr)));
189 4026968 : memcpy(s+curlen, t, len);
190 4026968 : sh->len = curlen+len;
191 4026968 : sh->free = sh->free-len;
192 4026968 : s[curlen+len] = '\0';
193 4026968 : return s;
194 : }
195 :
196 364634 : sds sdscat(sds s, char *t) {
197 364634 : return sdscatlen(s, t, strlen(t));
198 : }
199 :
200 150711 : sds sdscatsds(sds s, sds t) {
201 150711 : return sdscatlen(s, t, sdslen(t));
202 : }
203 :
204 0 : sds sdscpylen(sds s, char *t, size_t len) {
205 0 : struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
206 0 : size_t totlen = sh->free+sh->len;
207 :
208 0 : if (totlen < len) {
209 0 : s = sdsMakeRoomFor(s,len-sh->len);
210 0 : if (s == NULL) return NULL;
211 0 : sh = (void*) (s-(sizeof(struct sdshdr)));
212 0 : totlen = sh->free+sh->len;
213 : }
214 : memcpy(s, t, len);
215 0 : s[len] = '\0';
216 0 : sh->len = len;
217 0 : sh->free = totlen-len;
218 0 : return s;
219 : }
220 :
221 0 : sds sdscpy(sds s, char *t) {
222 0 : return sdscpylen(s, t, strlen(t));
223 : }
224 :
225 290030 : sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
226 : va_list cpy;
227 : char *buf, *t;
228 290030 : size_t buflen = 16;
229 :
230 : while(1) {
231 1080063 : buf = zmalloc(buflen);
232 1080063 : if (buf == NULL) return NULL;
233 1080063 : buf[buflen-2] = '\0';
234 1080063 : va_copy(cpy,ap);
235 : vsnprintf(buf, buflen, fmt, cpy);
236 1080063 : if (buf[buflen-2] != '\0') {
237 790033 : zfree(buf);
238 790033 : buflen *= 2;
239 790033 : continue;
240 : }
241 : break;
242 790033 : }
243 290030 : t = sdscat(s, buf);
244 290030 : zfree(buf);
245 290030 : return t;
246 : }
247 :
248 286621 : sds sdscatprintf(sds s, const char *fmt, ...) {
249 : va_list ap;
250 : char *t;
251 286621 : va_start(ap, fmt);
252 286621 : t = sdscatvprintf(s,fmt,ap);
253 286621 : va_end(ap);
254 286621 : return t;
255 : }
256 :
257 1227 : sds sdstrim(sds s, const char *cset) {
258 1227 : struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
259 : char *start, *end, *sp, *ep;
260 : size_t len;
261 :
262 1227 : sp = start = s;
263 1227 : ep = end = s+sdslen(s)-1;
264 1227 : while(sp <= end && strchr(cset, *sp)) sp++;
265 0 : while(ep > start && strchr(cset, *ep)) ep--;
266 1227 : len = (sp > ep) ? 0 : ((ep-sp)+1);
267 1227 : if (sh->buf != sp) memmove(sh->buf, sp, len);
268 1227 : sh->buf[len] = '\0';
269 1227 : sh->free = sh->free+(sh->len-len);
270 1227 : sh->len = len;
271 1227 : return s;
272 : }
273 :
274 1751796 : sds sdsrange(sds s, int start, int end) {
275 1751796 : struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
276 1751796 : size_t newlen, len = sdslen(s);
277 :
278 1751796 : if (len == 0) return s;
279 1751796 : if (start < 0) {
280 0 : start = len+start;
281 0 : if (start < 0) start = 0;
282 : }
283 1751796 : if (end < 0) {
284 1751796 : end = len+end;
285 1751796 : if (end < 0) end = 0;
286 : }
287 1751796 : newlen = (start > end) ? 0 : (end-start)+1;
288 1751796 : if (newlen != 0) {
289 753303 : if (start >= (signed)len) {
290 0 : newlen = 0;
291 753303 : } else if (end >= (signed)len) {
292 0 : end = len-1;
293 0 : newlen = (start > end) ? 0 : (end-start)+1;
294 : }
295 : } else {
296 998493 : start = 0;
297 : }
298 1751796 : if (start && newlen) memmove(sh->buf, sh->buf+start, newlen);
299 1751796 : sh->buf[newlen] = 0;
300 1751796 : sh->free = sh->free+(sh->len-newlen);
301 1751796 : sh->len = newlen;
302 1751796 : return s;
303 : }
304 :
305 1121 : void sdstolower(sds s) {
306 1121 : int len = sdslen(s), j;
307 :
308 1121 : for (j = 0; j < len; j++) s[j] = tolower(s[j]);
309 1121 : }
310 :
311 0 : void sdstoupper(sds s) {
312 0 : int len = sdslen(s), j;
313 :
314 0 : for (j = 0; j < len; j++) s[j] = toupper(s[j]);
315 0 : }
316 :
317 2946 : int sdscmp(sds s1, sds s2) {
318 : size_t l1, l2, minlen;
319 : int cmp;
320 :
321 2946 : l1 = sdslen(s1);
322 2946 : l2 = sdslen(s2);
323 2946 : minlen = (l1 < l2) ? l1 : l2;
324 2946 : cmp = memcmp(s1,s2,minlen);
325 2946 : if (cmp == 0) return l1-l2;
326 236 : 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 228801 : sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
346 228801 : int elements = 0, slots = 5, start = 0, j;
347 : sds *tokens;
348 :
349 228801 : if (seplen < 1 || len < 0) return NULL;
350 :
351 228801 : tokens = zmalloc(sizeof(sds)*slots);
352 228801 : if (tokens == NULL) return NULL;
353 :
354 228801 : if (len == 0) {
355 1 : *count = 0;
356 1 : return tokens;
357 : }
358 4468195 : for (j = 0; j < (len-(seplen-1)); j++) {
359 : /* make sure there is room for the next element and the final one */
360 4239395 : if (slots < elements+2) {
361 : sds *newtokens;
362 :
363 159 : slots *= 2;
364 159 : newtokens = zrealloc(tokens,sizeof(sds)*slots);
365 159 : if (newtokens == NULL) goto cleanup;
366 159 : tokens = newtokens;
367 : }
368 : /* search the separator */
369 4239395 : if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
370 329879 : tokens[elements] = sdsnewlen(s+start,j-start);
371 329879 : if (tokens[elements] == NULL) goto cleanup;
372 329879 : elements++;
373 329879 : start = j+seplen;
374 329879 : 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 228800 : tokens[elements] = sdsnewlen(s+start,len-start);
379 228800 : if (tokens[elements] == NULL) goto cleanup;
380 228800 : elements++;
381 228800 : *count = elements;
382 228800 : return tokens;
383 :
384 : cleanup:
385 : {
386 : int i;
387 0 : for (i = 0; i < elements; i++) sdsfree(tokens[i]);
388 0 : zfree(tokens);
389 0 : *count = 0;
390 0 : return NULL;
391 : }
392 : }
393 :
394 1177 : void sdsfreesplitres(sds *tokens, int count) {
395 1177 : if (!tokens) return;
396 4711 : while(count--)
397 3534 : sdsfree(tokens[count]);
398 1177 : zfree(tokens);
399 : }
400 :
401 1366 : sds sdsfromlonglong(long long value) {
402 : char buf[32], *p;
403 : unsigned long long v;
404 :
405 1366 : v = (value < 0) ? -value : value;
406 1366 : p = buf+31; /* point to the last character */
407 : do {
408 8098 : *p-- = '0'+(v%10);
409 8098 : v /= 10;
410 8098 : } while(v);
411 1366 : if (value < 0) *p-- = '-';
412 1366 : p++;
413 1366 : return sdsnewlen(p,32-(p-buf));
414 : }
415 :
416 0 : sds sdscatrepr(sds s, char *p, size_t len) {
417 0 : s = sdscatlen(s,"\"",1);
418 0 : while(len--) {
419 0 : switch(*p) {
420 : case '\\':
421 : case '"':
422 0 : s = sdscatprintf(s,"\\%c",*p);
423 0 : break;
424 0 : case '\n': s = sdscatlen(s,"\\n",2); break;
425 0 : case '\r': s = sdscatlen(s,"\\r",2); break;
426 0 : case '\t': s = sdscatlen(s,"\\t",2); break;
427 0 : case '\a': s = sdscatlen(s,"\\a",2); break;
428 0 : case '\b': s = sdscatlen(s,"\\b",2); break;
429 : default:
430 0 : if (isprint(*p))
431 0 : s = sdscatprintf(s,"%c",*p);
432 : else
433 0 : s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
434 : break;
435 : }
436 0 : p++;
437 : }
438 0 : return sdscatlen(s,"\"",1);
439 : }
440 :
441 : /* Helper function for sdssplitargs() that returns non zero if 'c'
442 : * is a valid hex digit. */
443 0 : int is_hex_digit(char c) {
444 0 : return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
445 0 : (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 0 : int hex_digit_to_int(char c) {
451 0 : switch(c) {
452 0 : case '0': return 0;
453 0 : case '1': return 1;
454 0 : case '2': return 2;
455 0 : case '3': return 3;
456 0 : case '4': return 4;
457 0 : case '5': return 5;
458 0 : case '6': return 6;
459 0 : case '7': return 7;
460 0 : case '8': return 8;
461 0 : case '9': return 9;
462 0 : case 'a': case 'A': return 10;
463 0 : case 'b': case 'B': return 11;
464 0 : case 'c': case 'C': return 12;
465 0 : case 'd': case 'D': return 13;
466 0 : case 'e': case 'E': return 14;
467 0 : case 'f': case 'F': return 15;
468 0 : 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. The caller should sdsfree() all the returned
479 : * strings and finally zfree() the array itself.
480 : *
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.
483 : */
484 1121 : sds *sdssplitargs(char *line, int *argc) {
485 1121 : char *p = line;
486 1121 : char *current = NULL;
487 1121 : char **vector = NULL;
488 :
489 1121 : *argc = 0;
490 : while(1) {
491 : /* skip blanks */
492 0 : while(*p && isspace(*p)) p++;
493 3416 : if (*p) {
494 : /* get a token */
495 2295 : int inq=0; /* set to 1 if we are in "quotes" */
496 2295 : int insq=0; /* set to 1 if we are in 'single quotes' */
497 2295 : int done=0;
498 :
499 2295 : if (current == NULL) current = sdsempty();
500 26052 : while(!done) {
501 23757 : if (inq) {
502 0 : if (*p == '\\' && *(p+1) == 'x' &&
503 0 : is_hex_digit(*(p+2)) &&
504 0 : is_hex_digit(*(p+3)))
505 : {
506 : unsigned char byte;
507 :
508 0 : byte = (hex_digit_to_int(*(p+2))*16)+
509 0 : hex_digit_to_int(*(p+3));
510 0 : current = sdscatlen(current,(char*)&byte,1);
511 0 : p += 3;
512 0 : } else if (*p == '\\' && *(p+1)) {
513 : char c;
514 :
515 0 : p++;
516 0 : switch(*p) {
517 0 : case 'n': c = '\n'; break;
518 0 : case 'r': c = '\r'; break;
519 0 : case 't': c = '\t'; break;
520 0 : case 'b': c = '\b'; break;
521 0 : case 'a': c = '\a'; break;
522 0 : default: c = *p; break;
523 : }
524 0 : current = sdscatlen(current,&c,1);
525 0 : } else if (*p == '"') {
526 : /* closing quote must be followed by a space or
527 : * nothing at all. */
528 0 : if (*(p+1) && !isspace(*(p+1))) goto err;
529 0 : done=1;
530 0 : } else if (!*p) {
531 : /* unterminated quotes */
532 : goto err;
533 : } else {
534 0 : current = sdscatlen(current,p,1);
535 : }
536 23757 : } else if (insq) {
537 0 : if (*p == '\\' && *(p+1) == '\'') {
538 0 : p++;
539 0 : current = sdscatlen(current,"'",1);
540 0 : } else if (*p == '\'') {
541 : /* closing quote must be followed by a space or
542 : * nothing at all. */
543 0 : if (*(p+1) && !isspace(*(p+1))) goto err;
544 0 : done=1;
545 0 : } else if (!*p) {
546 : /* unterminated quotes */
547 : goto err;
548 : } else {
549 0 : current = sdscatlen(current,p,1);
550 : }
551 : } else {
552 23757 : switch(*p) {
553 : case ' ':
554 : case '\n':
555 : case '\r':
556 : case '\t':
557 : case '\0':
558 2295 : done=1;
559 2295 : break;
560 : case '"':
561 0 : inq=1;
562 0 : break;
563 : case '\'':
564 0 : insq=1;
565 0 : break;
566 : default:
567 21462 : current = sdscatlen(current,p,1);
568 : break;
569 : }
570 : }
571 23757 : if (*p) p++;
572 : }
573 : /* add the token to the vector */
574 2295 : vector = zrealloc(vector,((*argc)+1)*sizeof(char*));
575 2295 : vector[*argc] = current;
576 2295 : (*argc)++;
577 2295 : current = NULL;
578 : } else {
579 1121 : return vector;
580 : }
581 2295 : }
582 :
583 : err:
584 0 : while((*argc)--)
585 0 : sdsfree(vector[*argc]);
586 0 : zfree(vector);
587 0 : if (current) sdsfree(current);
588 0 : return NULL;
589 : }
590 :
591 0 : void sdssplitargs_free(sds *argv, int argc) {
592 : int j;
593 :
594 0 : for (j = 0 ;j < argc; j++) sdsfree(argv[j]);
595 0 : zfree(argv);
596 0 : }
597 :
598 : /* Modify the string substituting all the occurrences of the set of
599 : * characters specifed in the 'from' string to the corresponding character
600 : * in the 'to' array.
601 : *
602 : * For instance: sdsmapchars(mystring, "ho", "01", 2)
603 : * will have the effect of turning the string "hello" into "0ell1".
604 : *
605 : * The function returns the sds string pointer, that is always the same
606 : * as the input pointer since no resize is needed. */
607 5 : sds sdsmapchars(sds s, char *from, char *to, size_t setlen) {
608 5 : size_t j, i, l = sdslen(s);
609 :
610 132 : for (j = 0; j < l; j++) {
611 381 : for (i = 0; i < setlen; i++) {
612 254 : if (s[j] == from[i]) {
613 0 : s[j] = to[i];
614 0 : break;
615 : }
616 : }
617 : }
618 5 : return s;
619 : }
620 :
621 : #ifdef SDS_TEST_MAIN
622 : #include <stdio.h>
623 : #include "testhelp.h"
624 :
625 : int main(void) {
626 : {
627 : struct sdshdr *sh;
628 : sds x = sdsnew("foo"), y;
629 :
630 : test_cond("Create a string and obtain the length",
631 : sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0)
632 :
633 : sdsfree(x);
634 : x = sdsnewlen("foo",2);
635 : test_cond("Create a string with specified length",
636 : sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0)
637 :
638 : x = sdscat(x,"bar");
639 : test_cond("Strings concatenation",
640 : sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0);
641 :
642 : x = sdscpy(x,"a");
643 : test_cond("sdscpy() against an originally longer string",
644 : sdslen(x) == 1 && memcmp(x,"a\0",2) == 0)
645 :
646 : x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk");
647 : test_cond("sdscpy() against an originally shorter string",
648 : sdslen(x) == 33 &&
649 : memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0)
650 :
651 : sdsfree(x);
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)
655 :
656 : sdsfree(x);
657 : x = sdstrim(sdsnew("xxciaoyyy"),"xy");
658 : test_cond("sdstrim() correctly trims characters",
659 : sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)
660 :
661 : y = sdsrange(sdsdup(x),1,1);
662 : test_cond("sdsrange(...,1,1)",
663 : sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)
664 :
665 : sdsfree(y);
666 : y = sdsrange(sdsdup(x),1,-1);
667 : test_cond("sdsrange(...,1,-1)",
668 : sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
669 :
670 : sdsfree(y);
671 : y = sdsrange(sdsdup(x),-2,-1);
672 : test_cond("sdsrange(...,-2,-1)",
673 : sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)
674 :
675 : sdsfree(y);
676 : y = sdsrange(sdsdup(x),2,1);
677 : test_cond("sdsrange(...,2,1)",
678 : sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
679 :
680 : sdsfree(y);
681 : y = sdsrange(sdsdup(x),1,100);
682 : test_cond("sdsrange(...,1,100)",
683 : sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
684 :
685 : sdsfree(y);
686 : y = sdsrange(sdsdup(x),100,100);
687 : test_cond("sdsrange(...,100,100)",
688 : sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
689 :
690 : sdsfree(y);
691 : sdsfree(x);
692 : x = sdsnew("foo");
693 : y = sdsnew("foa");
694 : test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0)
695 :
696 : sdsfree(y);
697 : sdsfree(x);
698 : x = sdsnew("bar");
699 : y = sdsnew("bar");
700 : test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0)
701 :
702 : sdsfree(y);
703 : sdsfree(x);
704 : x = sdsnew("aar");
705 : y = sdsnew("bar");
706 : test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
707 :
708 : {
709 : int oldfree;
710 :
711 : sdsfree(x);
712 : x = sdsnew("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);
718 : oldfree = sh->free;
719 : x[1] = '1';
720 : sdsIncrLen(x,1);
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);
724 : }
725 : }
726 : test_report()
727 : return 0;
728 : }
729 : #endif
|