1 | /* SDSLib 2.0 -- A C dynamic strings library |
2 | * |
3 | * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com> |
4 | * Copyright (c) 2015, Oran Agra |
5 | * Copyright (c) 2015, Redis Labs, Inc |
6 | * All rights reserved. |
7 | * |
8 | * Redistribution and use in source and binary forms, with or without |
9 | * modification, are permitted provided that the following conditions are met: |
10 | * |
11 | * * Redistributions of source code must retain the above copyright notice, |
12 | * this list of conditions and the following disclaimer. |
13 | * * Redistributions in binary form must reproduce the above copyright |
14 | * notice, this list of conditions and the following disclaimer in the |
15 | * documentation and/or other materials provided with the distribution. |
16 | * * Neither the name of Redis nor the names of its contributors may be used |
17 | * to endorse or promote products derived from this software without |
18 | * specific prior written permission. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 | * POSSIBILITY OF SUCH DAMAGE. |
31 | */ |
32 | |
33 | #include <stdio.h> |
34 | #include <stdlib.h> |
35 | #include <string.h> |
36 | #include <ctype.h> |
37 | #include <assert.h> |
38 | #include <limits.h> |
39 | #include "sds.h" |
40 | #include "sdsalloc.h" |
41 | |
42 | const char *SDS_NOINIT = "SDS_NOINIT" ; |
43 | |
44 | static inline int sdsHdrSize(char type) { |
45 | switch(type&SDS_TYPE_MASK) { |
46 | case SDS_TYPE_5: |
47 | return sizeof(struct sdshdr5); |
48 | case SDS_TYPE_8: |
49 | return sizeof(struct sdshdr8); |
50 | case SDS_TYPE_16: |
51 | return sizeof(struct sdshdr16); |
52 | case SDS_TYPE_32: |
53 | return sizeof(struct sdshdr32); |
54 | case SDS_TYPE_64: |
55 | return sizeof(struct sdshdr64); |
56 | } |
57 | return 0; |
58 | } |
59 | |
60 | static inline char sdsReqType(size_t string_size) { |
61 | if (string_size < 1<<5) |
62 | return SDS_TYPE_5; |
63 | if (string_size < 1<<8) |
64 | return SDS_TYPE_8; |
65 | if (string_size < 1<<16) |
66 | return SDS_TYPE_16; |
67 | #if (LONG_MAX == LLONG_MAX) |
68 | if (string_size < 1ll<<32) |
69 | return SDS_TYPE_32; |
70 | return SDS_TYPE_64; |
71 | #else |
72 | return SDS_TYPE_32; |
73 | #endif |
74 | } |
75 | |
76 | static inline size_t sdsTypeMaxSize(char type) { |
77 | if (type == SDS_TYPE_5) |
78 | return (1<<5) - 1; |
79 | if (type == SDS_TYPE_8) |
80 | return (1<<8) - 1; |
81 | if (type == SDS_TYPE_16) |
82 | return (1<<16) - 1; |
83 | #if (LONG_MAX == LLONG_MAX) |
84 | if (type == SDS_TYPE_32) |
85 | return (1ll<<32) - 1; |
86 | #endif |
87 | return -1; /* this is equivalent to the max SDS_TYPE_64 or SDS_TYPE_32 */ |
88 | } |
89 | |
90 | /* Create a new sds string with the content specified by the 'init' pointer |
91 | * and 'initlen'. |
92 | * If NULL is used for 'init' the string is initialized with zero bytes. |
93 | * If SDS_NOINIT is used, the buffer is left uninitialized; |
94 | * |
95 | * The string is always null-terminated (all the sds strings are, always) so |
96 | * even if you create an sds string with: |
97 | * |
98 | * mystring = sdsnewlen("abc",3); |
99 | * |
100 | * You can print the string with printf() as there is an implicit \0 at the |
101 | * end of the string. However the string is binary safe and can contain |
102 | * \0 characters in the middle, as the length is stored in the sds header. */ |
103 | sds _sdsnewlen(const void *init, size_t initlen, int trymalloc) { |
104 | void *sh; |
105 | sds s; |
106 | char type = sdsReqType(initlen); |
107 | /* Empty strings are usually created in order to append. Use type 8 |
108 | * since type 5 is not good at this. */ |
109 | if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8; |
110 | int hdrlen = sdsHdrSize(type); |
111 | unsigned char *fp; /* flags pointer. */ |
112 | size_t usable; |
113 | |
114 | assert(initlen + hdrlen + 1 > initlen); /* Catch size_t overflow */ |
115 | sh = trymalloc? |
116 | s_trymalloc_usable(hdrlen+initlen+1, &usable) : |
117 | s_malloc_usable(hdrlen+initlen+1, &usable); |
118 | if (sh == NULL) return NULL; |
119 | if (init==SDS_NOINIT) |
120 | init = NULL; |
121 | else if (!init) |
122 | memset(sh, 0, hdrlen+initlen+1); |
123 | s = (char*)sh+hdrlen; |
124 | fp = ((unsigned char*)s)-1; |
125 | usable = usable-hdrlen-1; |
126 | if (usable > sdsTypeMaxSize(type)) |
127 | usable = sdsTypeMaxSize(type); |
128 | switch(type) { |
129 | case SDS_TYPE_5: { |
130 | *fp = type | (initlen << SDS_TYPE_BITS); |
131 | break; |
132 | } |
133 | case SDS_TYPE_8: { |
134 | SDS_HDR_VAR(8,s); |
135 | sh->len = initlen; |
136 | sh->alloc = usable; |
137 | *fp = type; |
138 | break; |
139 | } |
140 | case SDS_TYPE_16: { |
141 | SDS_HDR_VAR(16,s); |
142 | sh->len = initlen; |
143 | sh->alloc = usable; |
144 | *fp = type; |
145 | break; |
146 | } |
147 | case SDS_TYPE_32: { |
148 | SDS_HDR_VAR(32,s); |
149 | sh->len = initlen; |
150 | sh->alloc = usable; |
151 | *fp = type; |
152 | break; |
153 | } |
154 | case SDS_TYPE_64: { |
155 | SDS_HDR_VAR(64,s); |
156 | sh->len = initlen; |
157 | sh->alloc = usable; |
158 | *fp = type; |
159 | break; |
160 | } |
161 | } |
162 | if (initlen && init) |
163 | memcpy(s, init, initlen); |
164 | s[initlen] = '\0'; |
165 | return s; |
166 | } |
167 | |
168 | sds sdsnewlen(const void *init, size_t initlen) { |
169 | return _sdsnewlen(init, initlen, 0); |
170 | } |
171 | |
172 | sds sdstrynewlen(const void *init, size_t initlen) { |
173 | return _sdsnewlen(init, initlen, 1); |
174 | } |
175 | |
176 | /* Create an empty (zero length) sds string. Even in this case the string |
177 | * always has an implicit null term. */ |
178 | sds sdsempty(void) { |
179 | return sdsnewlen("" ,0); |
180 | } |
181 | |
182 | /* Create a new sds string starting from a null terminated C string. */ |
183 | sds sdsnew(const char *init) { |
184 | size_t initlen = (init == NULL) ? 0 : strlen(init); |
185 | return sdsnewlen(init, initlen); |
186 | } |
187 | |
188 | /* Duplicate an sds string. */ |
189 | sds sdsdup(const sds s) { |
190 | return sdsnewlen(s, sdslen(s)); |
191 | } |
192 | |
193 | /* Free an sds string. No operation is performed if 's' is NULL. */ |
194 | void sdsfree(sds s) { |
195 | if (s == NULL) return; |
196 | s_free((char*)s-sdsHdrSize(s[-1])); |
197 | } |
198 | |
199 | /* Set the sds string length to the length as obtained with strlen(), so |
200 | * considering as content only up to the first null term character. |
201 | * |
202 | * This function is useful when the sds string is hacked manually in some |
203 | * way, like in the following example: |
204 | * |
205 | * s = sdsnew("foobar"); |
206 | * s[2] = '\0'; |
207 | * sdsupdatelen(s); |
208 | * printf("%d\n", sdslen(s)); |
209 | * |
210 | * The output will be "2", but if we comment out the call to sdsupdatelen() |
211 | * the output will be "6" as the string was modified but the logical length |
212 | * remains 6 bytes. */ |
213 | void sdsupdatelen(sds s) { |
214 | size_t reallen = strlen(s); |
215 | sdssetlen(s, reallen); |
216 | } |
217 | |
218 | /* Modify an sds string in-place to make it empty (zero length). |
219 | * However all the existing buffer is not discarded but set as free space |
220 | * so that next append operations will not require allocations up to the |
221 | * number of bytes previously available. */ |
222 | void sdsclear(sds s) { |
223 | sdssetlen(s, 0); |
224 | s[0] = '\0'; |
225 | } |
226 | |
227 | /* Enlarge the free space at the end of the sds string so that the caller |
228 | * is sure that after calling this function can overwrite up to addlen |
229 | * bytes after the end of the string, plus one more byte for nul term. |
230 | * If there's already sufficient free space, this function returns without any |
231 | * action, if there isn't sufficient free space, it'll allocate what's missing, |
232 | * and possibly more: |
233 | * When greedy is 1, enlarge more than needed, to avoid need for future reallocs |
234 | * on incremental growth. |
235 | * When greedy is 0, enlarge just enough so that there's free space for 'addlen'. |
236 | * |
237 | * Note: this does not change the *length* of the sds string as returned |
238 | * by sdslen(), but only the free buffer space we have. */ |
239 | sds _sdsMakeRoomFor(sds s, size_t addlen, int greedy) { |
240 | void *sh, *newsh; |
241 | size_t avail = sdsavail(s); |
242 | size_t len, newlen, reqlen; |
243 | char type, oldtype = s[-1] & SDS_TYPE_MASK; |
244 | int hdrlen; |
245 | size_t usable; |
246 | |
247 | /* Return ASAP if there is enough space left. */ |
248 | if (avail >= addlen) return s; |
249 | |
250 | len = sdslen(s); |
251 | sh = (char*)s-sdsHdrSize(oldtype); |
252 | reqlen = newlen = (len+addlen); |
253 | assert(newlen > len); /* Catch size_t overflow */ |
254 | if (greedy == 1) { |
255 | if (newlen < SDS_MAX_PREALLOC) |
256 | newlen *= 2; |
257 | else |
258 | newlen += SDS_MAX_PREALLOC; |
259 | } |
260 | |
261 | type = sdsReqType(newlen); |
262 | |
263 | /* Don't use type 5: the user is appending to the string and type 5 is |
264 | * not able to remember empty space, so sdsMakeRoomFor() must be called |
265 | * at every appending operation. */ |
266 | if (type == SDS_TYPE_5) type = SDS_TYPE_8; |
267 | |
268 | hdrlen = sdsHdrSize(type); |
269 | assert(hdrlen + newlen + 1 > reqlen); /* Catch size_t overflow */ |
270 | if (oldtype==type) { |
271 | newsh = s_realloc_usable(sh, hdrlen+newlen+1, &usable); |
272 | if (newsh == NULL) return NULL; |
273 | s = (char*)newsh+hdrlen; |
274 | } else { |
275 | /* Since the header size changes, need to move the string forward, |
276 | * and can't use realloc */ |
277 | newsh = s_malloc_usable(hdrlen+newlen+1, &usable); |
278 | if (newsh == NULL) return NULL; |
279 | memcpy((char*)newsh+hdrlen, s, len+1); |
280 | s_free(sh); |
281 | s = (char*)newsh+hdrlen; |
282 | s[-1] = type; |
283 | sdssetlen(s, len); |
284 | } |
285 | usable = usable-hdrlen-1; |
286 | if (usable > sdsTypeMaxSize(type)) |
287 | usable = sdsTypeMaxSize(type); |
288 | sdssetalloc(s, usable); |
289 | return s; |
290 | } |
291 | |
292 | /* Enlarge the free space at the end of the sds string more than needed, |
293 | * This is useful to avoid repeated re-allocations when repeatedly appending to the sds. */ |
294 | sds sdsMakeRoomFor(sds s, size_t addlen) { |
295 | return _sdsMakeRoomFor(s, addlen, 1); |
296 | } |
297 | |
298 | /* Unlike sdsMakeRoomFor(), this one just grows to the necessary size. */ |
299 | sds sdsMakeRoomForNonGreedy(sds s, size_t addlen) { |
300 | return _sdsMakeRoomFor(s, addlen, 0); |
301 | } |
302 | |
303 | /* Reallocate the sds string so that it has no free space at the end. The |
304 | * contained string remains not altered, but next concatenation operations |
305 | * will require a reallocation. |
306 | * |
307 | * After the call, the passed sds string is no longer valid and all the |
308 | * references must be substituted with the new pointer returned by the call. */ |
309 | sds sdsRemoveFreeSpace(sds s) { |
310 | void *sh, *newsh; |
311 | char type, oldtype = s[-1] & SDS_TYPE_MASK; |
312 | int hdrlen, oldhdrlen = sdsHdrSize(oldtype); |
313 | size_t len = sdslen(s); |
314 | size_t avail = sdsavail(s); |
315 | sh = (char*)s-oldhdrlen; |
316 | |
317 | /* Return ASAP if there is no space left. */ |
318 | if (avail == 0) return s; |
319 | |
320 | /* Check what would be the minimum SDS header that is just good enough to |
321 | * fit this string. */ |
322 | type = sdsReqType(len); |
323 | hdrlen = sdsHdrSize(type); |
324 | |
325 | /* If the type is the same, or at least a large enough type is still |
326 | * required, we just realloc(), letting the allocator to do the copy |
327 | * only if really needed. Otherwise if the change is huge, we manually |
328 | * reallocate the string to use the different header type. */ |
329 | if (oldtype==type || type > SDS_TYPE_8) { |
330 | newsh = s_realloc(sh, oldhdrlen+len+1); |
331 | if (newsh == NULL) return NULL; |
332 | s = (char*)newsh+oldhdrlen; |
333 | } else { |
334 | newsh = s_malloc(hdrlen+len+1); |
335 | if (newsh == NULL) return NULL; |
336 | memcpy((char*)newsh+hdrlen, s, len+1); |
337 | s_free(sh); |
338 | s = (char*)newsh+hdrlen; |
339 | s[-1] = type; |
340 | sdssetlen(s, len); |
341 | } |
342 | sdssetalloc(s, len); |
343 | return s; |
344 | } |
345 | |
346 | /* Resize the allocation, this can make the allocation bigger or smaller, |
347 | * if the size is smaller than currently used len, the data will be truncated */ |
348 | sds sdsResize(sds s, size_t size) { |
349 | void *sh, *newsh; |
350 | char type, oldtype = s[-1] & SDS_TYPE_MASK; |
351 | int hdrlen, oldhdrlen = sdsHdrSize(oldtype); |
352 | size_t len = sdslen(s); |
353 | sh = (char*)s-oldhdrlen; |
354 | |
355 | /* Return ASAP if the size is already good. */ |
356 | if (sdsalloc(s) == size) return s; |
357 | |
358 | /* Truncate len if needed. */ |
359 | if (size < len) len = size; |
360 | |
361 | /* Check what would be the minimum SDS header that is just good enough to |
362 | * fit this string. */ |
363 | type = sdsReqType(size); |
364 | /* Don't use type 5, it is not good for strings that are resized. */ |
365 | if (type == SDS_TYPE_5) type = SDS_TYPE_8; |
366 | hdrlen = sdsHdrSize(type); |
367 | |
368 | /* If the type is the same, or can hold the size in it with low overhead |
369 | * (larger than SDS_TYPE_8), we just realloc(), letting the allocator |
370 | * to do the copy only if really needed. Otherwise if the change is |
371 | * huge, we manually reallocate the string to use the different header |
372 | * type. */ |
373 | if (oldtype==type || (type < oldtype && type > SDS_TYPE_8)) { |
374 | newsh = s_realloc(sh, oldhdrlen+size+1); |
375 | if (newsh == NULL) return NULL; |
376 | s = (char*)newsh+oldhdrlen; |
377 | } else { |
378 | newsh = s_malloc(hdrlen+size+1); |
379 | if (newsh == NULL) return NULL; |
380 | memcpy((char*)newsh+hdrlen, s, len); |
381 | s_free(sh); |
382 | s = (char*)newsh+hdrlen; |
383 | s[-1] = type; |
384 | } |
385 | s[len] = 0; |
386 | sdssetlen(s, len); |
387 | sdssetalloc(s, size); |
388 | return s; |
389 | } |
390 | |
391 | /* Return the total size of the allocation of the specified sds string, |
392 | * including: |
393 | * 1) The sds header before the pointer. |
394 | * 2) The string. |
395 | * 3) The free buffer at the end if any. |
396 | * 4) The implicit null term. |
397 | */ |
398 | size_t sdsAllocSize(sds s) { |
399 | size_t alloc = sdsalloc(s); |
400 | return sdsHdrSize(s[-1])+alloc+1; |
401 | } |
402 | |
403 | /* Return the pointer of the actual SDS allocation (normally SDS strings |
404 | * are referenced by the start of the string buffer). */ |
405 | void *sdsAllocPtr(sds s) { |
406 | return (void*) (s-sdsHdrSize(s[-1])); |
407 | } |
408 | |
409 | /* Increment the sds length and decrements the left free space at the |
410 | * end of the string according to 'incr'. Also set the null term |
411 | * in the new end of the string. |
412 | * |
413 | * This function is used in order to fix the string length after the |
414 | * user calls sdsMakeRoomFor(), writes something after the end of |
415 | * the current string, and finally needs to set the new length. |
416 | * |
417 | * Note: it is possible to use a negative increment in order to |
418 | * right-trim the string. |
419 | * |
420 | * Usage example: |
421 | * |
422 | * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the |
423 | * following schema, to cat bytes coming from the kernel to the end of an |
424 | * sds string without copying into an intermediate buffer: |
425 | * |
426 | * oldlen = sdslen(s); |
427 | * s = sdsMakeRoomFor(s, BUFFER_SIZE); |
428 | * nread = read(fd, s+oldlen, BUFFER_SIZE); |
429 | * ... check for nread <= 0 and handle it ... |
430 | * sdsIncrLen(s, nread); |
431 | */ |
432 | void sdsIncrLen(sds s, ssize_t incr) { |
433 | unsigned char flags = s[-1]; |
434 | size_t len; |
435 | switch(flags&SDS_TYPE_MASK) { |
436 | case SDS_TYPE_5: { |
437 | unsigned char *fp = ((unsigned char*)s)-1; |
438 | unsigned char oldlen = SDS_TYPE_5_LEN(flags); |
439 | assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr))); |
440 | *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS); |
441 | len = oldlen+incr; |
442 | break; |
443 | } |
444 | case SDS_TYPE_8: { |
445 | SDS_HDR_VAR(8,s); |
446 | assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); |
447 | len = (sh->len += incr); |
448 | break; |
449 | } |
450 | case SDS_TYPE_16: { |
451 | SDS_HDR_VAR(16,s); |
452 | assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); |
453 | len = (sh->len += incr); |
454 | break; |
455 | } |
456 | case SDS_TYPE_32: { |
457 | SDS_HDR_VAR(32,s); |
458 | assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); |
459 | len = (sh->len += incr); |
460 | break; |
461 | } |
462 | case SDS_TYPE_64: { |
463 | SDS_HDR_VAR(64,s); |
464 | assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr))); |
465 | len = (sh->len += incr); |
466 | break; |
467 | } |
468 | default: len = 0; /* Just to avoid compilation warnings. */ |
469 | } |
470 | s[len] = '\0'; |
471 | } |
472 | |
473 | /* Grow the sds to have the specified length. Bytes that were not part of |
474 | * the original length of the sds will be set to zero. |
475 | * |
476 | * if the specified length is smaller than the current length, no operation |
477 | * is performed. */ |
478 | sds sdsgrowzero(sds s, size_t len) { |
479 | size_t curlen = sdslen(s); |
480 | |
481 | if (len <= curlen) return s; |
482 | s = sdsMakeRoomFor(s,len-curlen); |
483 | if (s == NULL) return NULL; |
484 | |
485 | /* Make sure added region doesn't contain garbage */ |
486 | memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */ |
487 | sdssetlen(s, len); |
488 | return s; |
489 | } |
490 | |
491 | /* Append the specified binary-safe string pointed by 't' of 'len' bytes to the |
492 | * end of the specified sds string 's'. |
493 | * |
494 | * After the call, the passed sds string is no longer valid and all the |
495 | * references must be substituted with the new pointer returned by the call. */ |
496 | sds sdscatlen(sds s, const void *t, size_t len) { |
497 | size_t curlen = sdslen(s); |
498 | |
499 | s = sdsMakeRoomFor(s,len); |
500 | if (s == NULL) return NULL; |
501 | memcpy(s+curlen, t, len); |
502 | sdssetlen(s, curlen+len); |
503 | s[curlen+len] = '\0'; |
504 | return s; |
505 | } |
506 | |
507 | /* Append the specified null terminated C string to the sds string 's'. |
508 | * |
509 | * After the call, the passed sds string is no longer valid and all the |
510 | * references must be substituted with the new pointer returned by the call. */ |
511 | sds sdscat(sds s, const char *t) { |
512 | return sdscatlen(s, t, strlen(t)); |
513 | } |
514 | |
515 | /* Append the specified sds 't' to the existing sds 's'. |
516 | * |
517 | * After the call, the modified sds string is no longer valid and all the |
518 | * references must be substituted with the new pointer returned by the call. */ |
519 | sds sdscatsds(sds s, const sds t) { |
520 | return sdscatlen(s, t, sdslen(t)); |
521 | } |
522 | |
523 | /* Destructively modify the sds string 's' to hold the specified binary |
524 | * safe string pointed by 't' of length 'len' bytes. */ |
525 | sds sdscpylen(sds s, const char *t, size_t len) { |
526 | if (sdsalloc(s) < len) { |
527 | s = sdsMakeRoomFor(s,len-sdslen(s)); |
528 | if (s == NULL) return NULL; |
529 | } |
530 | memcpy(s, t, len); |
531 | s[len] = '\0'; |
532 | sdssetlen(s, len); |
533 | return s; |
534 | } |
535 | |
536 | /* Like sdscpylen() but 't' must be a null-terminated string so that the length |
537 | * of the string is obtained with strlen(). */ |
538 | sds sdscpy(sds s, const char *t) { |
539 | return sdscpylen(s, t, strlen(t)); |
540 | } |
541 | |
542 | /* Helper for sdscatlonglong() doing the actual number -> string |
543 | * conversion. 's' must point to a string with room for at least |
544 | * SDS_LLSTR_SIZE bytes. |
545 | * |
546 | * The function returns the length of the null-terminated string |
547 | * representation stored at 's'. */ |
548 | #define SDS_LLSTR_SIZE 21 |
549 | int sdsll2str(char *s, long long value) { |
550 | char *p, aux; |
551 | unsigned long long v; |
552 | size_t l; |
553 | |
554 | /* Generate the string representation, this method produces |
555 | * a reversed string. */ |
556 | if (value < 0) { |
557 | /* Since v is unsigned, if value==LLONG_MIN, -LLONG_MIN will overflow. */ |
558 | if (value != LLONG_MIN) { |
559 | v = -value; |
560 | } else { |
561 | v = ((unsigned long long)LLONG_MAX) + 1; |
562 | } |
563 | } else { |
564 | v = value; |
565 | } |
566 | |
567 | p = s; |
568 | do { |
569 | *p++ = '0'+(v%10); |
570 | v /= 10; |
571 | } while(v); |
572 | if (value < 0) *p++ = '-'; |
573 | |
574 | /* Compute length and add null term. */ |
575 | l = p-s; |
576 | *p = '\0'; |
577 | |
578 | /* Reverse the string. */ |
579 | p--; |
580 | while(s < p) { |
581 | aux = *s; |
582 | *s = *p; |
583 | *p = aux; |
584 | s++; |
585 | p--; |
586 | } |
587 | return l; |
588 | } |
589 | |
590 | /* Identical sdsll2str(), but for unsigned long long type. */ |
591 | int sdsull2str(char *s, unsigned long long v) { |
592 | char *p, aux; |
593 | size_t l; |
594 | |
595 | /* Generate the string representation, this method produces |
596 | * a reversed string. */ |
597 | p = s; |
598 | do { |
599 | *p++ = '0'+(v%10); |
600 | v /= 10; |
601 | } while(v); |
602 | |
603 | /* Compute length and add null term. */ |
604 | l = p-s; |
605 | *p = '\0'; |
606 | |
607 | /* Reverse the string. */ |
608 | p--; |
609 | while(s < p) { |
610 | aux = *s; |
611 | *s = *p; |
612 | *p = aux; |
613 | s++; |
614 | p--; |
615 | } |
616 | return l; |
617 | } |
618 | |
619 | /* Create an sds string from a long long value. It is much faster than: |
620 | * |
621 | * sdscatprintf(sdsempty(),"%lld\n", value); |
622 | */ |
623 | sds sdsfromlonglong(long long value) { |
624 | char buf[SDS_LLSTR_SIZE]; |
625 | int len = sdsll2str(buf,value); |
626 | |
627 | return sdsnewlen(buf,len); |
628 | } |
629 | |
630 | /* Like sdscatprintf() but gets va_list instead of being variadic. */ |
631 | sds sdscatvprintf(sds s, const char *fmt, va_list ap) { |
632 | va_list cpy; |
633 | char staticbuf[1024], *buf = staticbuf, *t; |
634 | size_t buflen = strlen(fmt)*2; |
635 | int bufstrlen; |
636 | |
637 | /* We try to start using a static buffer for speed. |
638 | * If not possible we revert to heap allocation. */ |
639 | if (buflen > sizeof(staticbuf)) { |
640 | buf = s_malloc(buflen); |
641 | if (buf == NULL) return NULL; |
642 | } else { |
643 | buflen = sizeof(staticbuf); |
644 | } |
645 | |
646 | /* Alloc enough space for buffer and \0 after failing to |
647 | * fit the string in the current buffer size. */ |
648 | while(1) { |
649 | va_copy(cpy,ap); |
650 | bufstrlen = vsnprintf(buf, buflen, fmt, cpy); |
651 | va_end(cpy); |
652 | if (bufstrlen < 0) { |
653 | if (buf != staticbuf) s_free(buf); |
654 | return NULL; |
655 | } |
656 | if (((size_t)bufstrlen) >= buflen) { |
657 | if (buf != staticbuf) s_free(buf); |
658 | buflen = ((size_t)bufstrlen) + 1; |
659 | buf = s_malloc(buflen); |
660 | if (buf == NULL) return NULL; |
661 | continue; |
662 | } |
663 | break; |
664 | } |
665 | |
666 | /* Finally concat the obtained string to the SDS string and return it. */ |
667 | t = sdscatlen(s, buf, bufstrlen); |
668 | if (buf != staticbuf) s_free(buf); |
669 | return t; |
670 | } |
671 | |
672 | /* Append to the sds string 's' a string obtained using printf-alike format |
673 | * specifier. |
674 | * |
675 | * After the call, the modified sds string is no longer valid and all the |
676 | * references must be substituted with the new pointer returned by the call. |
677 | * |
678 | * Example: |
679 | * |
680 | * s = sdsnew("Sum is: "); |
681 | * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b). |
682 | * |
683 | * Often you need to create a string from scratch with the printf-alike |
684 | * format. When this is the need, just use sdsempty() as the target string: |
685 | * |
686 | * s = sdscatprintf(sdsempty(), "... your format ...", args); |
687 | */ |
688 | sds sdscatprintf(sds s, const char *fmt, ...) { |
689 | va_list ap; |
690 | char *t; |
691 | va_start(ap, fmt); |
692 | t = sdscatvprintf(s,fmt,ap); |
693 | va_end(ap); |
694 | return t; |
695 | } |
696 | |
697 | /* This function is similar to sdscatprintf, but much faster as it does |
698 | * not rely on sprintf() family functions implemented by the libc that |
699 | * are often very slow. Moreover directly handling the sds string as |
700 | * new data is concatenated provides a performance improvement. |
701 | * |
702 | * However this function only handles an incompatible subset of printf-alike |
703 | * format specifiers: |
704 | * |
705 | * %s - C String |
706 | * %S - SDS string |
707 | * %i - signed int |
708 | * %I - 64 bit signed integer (long long, int64_t) |
709 | * %u - unsigned int |
710 | * %U - 64 bit unsigned integer (unsigned long long, uint64_t) |
711 | * %% - Verbatim "%" character. |
712 | */ |
713 | sds sdscatfmt(sds s, char const *fmt, ...) { |
714 | size_t initlen = sdslen(s); |
715 | const char *f = fmt; |
716 | long i; |
717 | va_list ap; |
718 | |
719 | /* To avoid continuous reallocations, let's start with a buffer that |
720 | * can hold at least two times the format string itself. It's not the |
721 | * best heuristic but seems to work in practice. */ |
722 | s = sdsMakeRoomFor(s, strlen(fmt)*2); |
723 | va_start(ap,fmt); |
724 | f = fmt; /* Next format specifier byte to process. */ |
725 | i = initlen; /* Position of the next byte to write to dest str. */ |
726 | while(*f) { |
727 | char next, *str; |
728 | size_t l; |
729 | long long num; |
730 | unsigned long long unum; |
731 | |
732 | /* Make sure there is always space for at least 1 char. */ |
733 | if (sdsavail(s)==0) { |
734 | s = sdsMakeRoomFor(s,1); |
735 | } |
736 | |
737 | switch(*f) { |
738 | case '%': |
739 | next = *(f+1); |
740 | if (next == '\0') break; |
741 | f++; |
742 | switch(next) { |
743 | case 's': |
744 | case 'S': |
745 | str = va_arg(ap,char*); |
746 | l = (next == 's') ? strlen(str) : sdslen(str); |
747 | if (sdsavail(s) < l) { |
748 | s = sdsMakeRoomFor(s,l); |
749 | } |
750 | memcpy(s+i,str,l); |
751 | sdsinclen(s,l); |
752 | i += l; |
753 | break; |
754 | case 'i': |
755 | case 'I': |
756 | if (next == 'i') |
757 | num = va_arg(ap,int); |
758 | else |
759 | num = va_arg(ap,long long); |
760 | { |
761 | char buf[SDS_LLSTR_SIZE]; |
762 | l = sdsll2str(buf,num); |
763 | if (sdsavail(s) < l) { |
764 | s = sdsMakeRoomFor(s,l); |
765 | } |
766 | memcpy(s+i,buf,l); |
767 | sdsinclen(s,l); |
768 | i += l; |
769 | } |
770 | break; |
771 | case 'u': |
772 | case 'U': |
773 | if (next == 'u') |
774 | unum = va_arg(ap,unsigned int); |
775 | else |
776 | unum = va_arg(ap,unsigned long long); |
777 | { |
778 | char buf[SDS_LLSTR_SIZE]; |
779 | l = sdsull2str(buf,unum); |
780 | if (sdsavail(s) < l) { |
781 | s = sdsMakeRoomFor(s,l); |
782 | } |
783 | memcpy(s+i,buf,l); |
784 | sdsinclen(s,l); |
785 | i += l; |
786 | } |
787 | break; |
788 | default: /* Handle %% and generally %<unknown>. */ |
789 | s[i++] = next; |
790 | sdsinclen(s,1); |
791 | break; |
792 | } |
793 | break; |
794 | default: |
795 | s[i++] = *f; |
796 | sdsinclen(s,1); |
797 | break; |
798 | } |
799 | f++; |
800 | } |
801 | va_end(ap); |
802 | |
803 | /* Add null-term */ |
804 | s[i] = '\0'; |
805 | return s; |
806 | } |
807 | |
808 | /* Remove the part of the string from left and from right composed just of |
809 | * contiguous characters found in 'cset', that is a null terminated C string. |
810 | * |
811 | * After the call, the modified sds string is no longer valid and all the |
812 | * references must be substituted with the new pointer returned by the call. |
813 | * |
814 | * Example: |
815 | * |
816 | * s = sdsnew("AA...AA.a.aa.aHelloWorld :::"); |
817 | * s = sdstrim(s,"Aa. :"); |
818 | * printf("%s\n", s); |
819 | * |
820 | * Output will be just "HelloWorld". |
821 | */ |
822 | sds sdstrim(sds s, const char *cset) { |
823 | char *end, *sp, *ep; |
824 | size_t len; |
825 | |
826 | sp = s; |
827 | ep = end = s+sdslen(s)-1; |
828 | while(sp <= end && strchr(cset, *sp)) sp++; |
829 | while(ep > sp && strchr(cset, *ep)) ep--; |
830 | len = (ep-sp)+1; |
831 | if (s != sp) memmove(s, sp, len); |
832 | s[len] = '\0'; |
833 | sdssetlen(s,len); |
834 | return s; |
835 | } |
836 | |
837 | /* Changes the input string to be a subset of the original. |
838 | * It does not release the free space in the string, so a call to |
839 | * sdsRemoveFreeSpace may be wise after. */ |
840 | void sdssubstr(sds s, size_t start, size_t len) { |
841 | /* Clamp out of range input */ |
842 | size_t oldlen = sdslen(s); |
843 | if (start >= oldlen) start = len = 0; |
844 | if (len > oldlen-start) len = oldlen-start; |
845 | |
846 | /* Move the data */ |
847 | if (len) memmove(s, s+start, len); |
848 | s[len] = 0; |
849 | sdssetlen(s,len); |
850 | } |
851 | |
852 | /* Turn the string into a smaller (or equal) string containing only the |
853 | * substring specified by the 'start' and 'end' indexes. |
854 | * |
855 | * start and end can be negative, where -1 means the last character of the |
856 | * string, -2 the penultimate character, and so forth. |
857 | * |
858 | * The interval is inclusive, so the start and end characters will be part |
859 | * of the resulting string. |
860 | * |
861 | * The string is modified in-place. |
862 | * |
863 | * NOTE: this function can be misleading and can have unexpected behaviour, |
864 | * specifically when you want the length of the new string to be 0. |
865 | * Having start==end will result in a string with one character. |
866 | * please consider using sdssubstr instead. |
867 | * |
868 | * Example: |
869 | * |
870 | * s = sdsnew("Hello World"); |
871 | * sdsrange(s,1,-1); => "ello World" |
872 | */ |
873 | void sdsrange(sds s, ssize_t start, ssize_t end) { |
874 | size_t newlen, len = sdslen(s); |
875 | if (len == 0) return; |
876 | if (start < 0) |
877 | start = len + start; |
878 | if (end < 0) |
879 | end = len + end; |
880 | newlen = (start > end) ? 0 : (end-start)+1; |
881 | sdssubstr(s, start, newlen); |
882 | } |
883 | |
884 | /* Apply tolower() to every character of the sds string 's'. */ |
885 | void sdstolower(sds s) { |
886 | size_t len = sdslen(s), j; |
887 | |
888 | for (j = 0; j < len; j++) s[j] = tolower(s[j]); |
889 | } |
890 | |
891 | /* Apply toupper() to every character of the sds string 's'. */ |
892 | void sdstoupper(sds s) { |
893 | size_t len = sdslen(s), j; |
894 | |
895 | for (j = 0; j < len; j++) s[j] = toupper(s[j]); |
896 | } |
897 | |
898 | /* Compare two sds strings s1 and s2 with memcmp(). |
899 | * |
900 | * Return value: |
901 | * |
902 | * positive if s1 > s2. |
903 | * negative if s1 < s2. |
904 | * 0 if s1 and s2 are exactly the same binary string. |
905 | * |
906 | * If two strings share exactly the same prefix, but one of the two has |
907 | * additional characters, the longer string is considered to be greater than |
908 | * the smaller one. */ |
909 | int sdscmp(const sds s1, const sds s2) { |
910 | size_t l1, l2, minlen; |
911 | int cmp; |
912 | |
913 | l1 = sdslen(s1); |
914 | l2 = sdslen(s2); |
915 | minlen = (l1 < l2) ? l1 : l2; |
916 | cmp = memcmp(s1,s2,minlen); |
917 | if (cmp == 0) return l1>l2? 1: (l1<l2? -1: 0); |
918 | return cmp; |
919 | } |
920 | |
921 | /* Split 's' with separator in 'sep'. An array |
922 | * of sds strings is returned. *count will be set |
923 | * by reference to the number of tokens returned. |
924 | * |
925 | * On out of memory, zero length string, zero length |
926 | * separator, NULL is returned. |
927 | * |
928 | * Note that 'sep' is able to split a string using |
929 | * a multi-character separator. For example |
930 | * sdssplit("foo_-_bar","_-_"); will return two |
931 | * elements "foo" and "bar". |
932 | * |
933 | * This version of the function is binary-safe but |
934 | * requires length arguments. sdssplit() is just the |
935 | * same function but for zero-terminated strings. |
936 | */ |
937 | sds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count) { |
938 | int elements = 0, slots = 5; |
939 | long start = 0, j; |
940 | sds *tokens; |
941 | |
942 | if (seplen < 1 || len <= 0) { |
943 | *count = 0; |
944 | return NULL; |
945 | } |
946 | tokens = s_malloc(sizeof(sds)*slots); |
947 | if (tokens == NULL) return NULL; |
948 | |
949 | for (j = 0; j < (len-(seplen-1)); j++) { |
950 | /* make sure there is room for the next element and the final one */ |
951 | if (slots < elements+2) { |
952 | sds *newtokens; |
953 | |
954 | slots *= 2; |
955 | newtokens = s_realloc(tokens,sizeof(sds)*slots); |
956 | if (newtokens == NULL) goto cleanup; |
957 | tokens = newtokens; |
958 | } |
959 | /* search the separator */ |
960 | if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) { |
961 | tokens[elements] = sdsnewlen(s+start,j-start); |
962 | if (tokens[elements] == NULL) goto cleanup; |
963 | elements++; |
964 | start = j+seplen; |
965 | j = j+seplen-1; /* skip the separator */ |
966 | } |
967 | } |
968 | /* Add the final element. We are sure there is room in the tokens array. */ |
969 | tokens[elements] = sdsnewlen(s+start,len-start); |
970 | if (tokens[elements] == NULL) goto cleanup; |
971 | elements++; |
972 | *count = elements; |
973 | return tokens; |
974 | |
975 | cleanup: |
976 | { |
977 | int i; |
978 | for (i = 0; i < elements; i++) sdsfree(tokens[i]); |
979 | s_free(tokens); |
980 | *count = 0; |
981 | return NULL; |
982 | } |
983 | } |
984 | |
985 | /* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */ |
986 | void sdsfreesplitres(sds *tokens, int count) { |
987 | if (!tokens) return; |
988 | while(count--) |
989 | sdsfree(tokens[count]); |
990 | s_free(tokens); |
991 | } |
992 | |
993 | /* Append to the sds string "s" an escaped string representation where |
994 | * all the non-printable characters (tested with isprint()) are turned into |
995 | * escapes in the form "\n\r\a...." or "\x<hex-number>". |
996 | * |
997 | * After the call, the modified sds string is no longer valid and all the |
998 | * references must be substituted with the new pointer returned by the call. */ |
999 | sds sdscatrepr(sds s, const char *p, size_t len) { |
1000 | s = sdscatlen(s,"\"" ,1); |
1001 | while(len--) { |
1002 | switch(*p) { |
1003 | case '\\': |
1004 | case '"': |
1005 | s = sdscatprintf(s,"\\%c" ,*p); |
1006 | break; |
1007 | case '\n': s = sdscatlen(s,"\\n" ,2); break; |
1008 | case '\r': s = sdscatlen(s,"\\r" ,2); break; |
1009 | case '\t': s = sdscatlen(s,"\\t" ,2); break; |
1010 | case '\a': s = sdscatlen(s,"\\a" ,2); break; |
1011 | case '\b': s = sdscatlen(s,"\\b" ,2); break; |
1012 | default: |
1013 | if (isprint(*p)) |
1014 | s = sdscatprintf(s,"%c" ,*p); |
1015 | else |
1016 | s = sdscatprintf(s,"\\x%02x" ,(unsigned char)*p); |
1017 | break; |
1018 | } |
1019 | p++; |
1020 | } |
1021 | return sdscatlen(s,"\"" ,1); |
1022 | } |
1023 | |
1024 | /* Returns one if the string contains characters to be escaped |
1025 | * by sdscatrepr(), zero otherwise. |
1026 | * |
1027 | * Typically, this should be used to help protect aggregated strings in a way |
1028 | * that is compatible with sdssplitargs(). For this reason, also spaces will be |
1029 | * treated as needing an escape. |
1030 | */ |
1031 | int sdsneedsrepr(const sds s) { |
1032 | size_t len = sdslen(s); |
1033 | const char *p = s; |
1034 | |
1035 | while (len--) { |
1036 | if (*p == '\\' || *p == '"' || *p == '\n' || *p == '\r' || |
1037 | *p == '\t' || *p == '\a' || *p == '\b' || !isprint(*p) || isspace(*p)) return 1; |
1038 | p++; |
1039 | } |
1040 | |
1041 | return 0; |
1042 | } |
1043 | |
1044 | /* Helper function for sdssplitargs() that returns non zero if 'c' |
1045 | * is a valid hex digit. */ |
1046 | int is_hex_digit(char c) { |
1047 | return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || |
1048 | (c >= 'A' && c <= 'F'); |
1049 | } |
1050 | |
1051 | /* Helper function for sdssplitargs() that converts a hex digit into an |
1052 | * integer from 0 to 15 */ |
1053 | int hex_digit_to_int(char c) { |
1054 | switch(c) { |
1055 | case '0': return 0; |
1056 | case '1': return 1; |
1057 | case '2': return 2; |
1058 | case '3': return 3; |
1059 | case '4': return 4; |
1060 | case '5': return 5; |
1061 | case '6': return 6; |
1062 | case '7': return 7; |
1063 | case '8': return 8; |
1064 | case '9': return 9; |
1065 | case 'a': case 'A': return 10; |
1066 | case 'b': case 'B': return 11; |
1067 | case 'c': case 'C': return 12; |
1068 | case 'd': case 'D': return 13; |
1069 | case 'e': case 'E': return 14; |
1070 | case 'f': case 'F': return 15; |
1071 | default: return 0; |
1072 | } |
1073 | } |
1074 | |
1075 | /* Split a line into arguments, where every argument can be in the |
1076 | * following programming-language REPL-alike form: |
1077 | * |
1078 | * foo bar "newline are supported\n" and "\xff\x00otherstuff" |
1079 | * |
1080 | * The number of arguments is stored into *argc, and an array |
1081 | * of sds is returned. |
1082 | * |
1083 | * The caller should free the resulting array of sds strings with |
1084 | * sdsfreesplitres(). |
1085 | * |
1086 | * Note that sdscatrepr() is able to convert back a string into |
1087 | * a quoted string in the same format sdssplitargs() is able to parse. |
1088 | * |
1089 | * The function returns the allocated tokens on success, even when the |
1090 | * input string is empty, or NULL if the input contains unbalanced |
1091 | * quotes or closed quotes followed by non space characters |
1092 | * as in: "foo"bar or "foo' |
1093 | */ |
1094 | sds *sdssplitargs(const char *line, int *argc) { |
1095 | const char *p = line; |
1096 | char *current = NULL; |
1097 | char **vector = NULL; |
1098 | |
1099 | *argc = 0; |
1100 | while(1) { |
1101 | /* skip blanks */ |
1102 | while(*p && isspace(*p)) p++; |
1103 | if (*p) { |
1104 | /* get a token */ |
1105 | int inq=0; /* set to 1 if we are in "quotes" */ |
1106 | int insq=0; /* set to 1 if we are in 'single quotes' */ |
1107 | int done=0; |
1108 | |
1109 | if (current == NULL) current = sdsempty(); |
1110 | while(!done) { |
1111 | if (inq) { |
1112 | if (*p == '\\' && *(p+1) == 'x' && |
1113 | is_hex_digit(*(p+2)) && |
1114 | is_hex_digit(*(p+3))) |
1115 | { |
1116 | unsigned char byte; |
1117 | |
1118 | byte = (hex_digit_to_int(*(p+2))*16)+ |
1119 | hex_digit_to_int(*(p+3)); |
1120 | current = sdscatlen(current,(char*)&byte,1); |
1121 | p += 3; |
1122 | } else if (*p == '\\' && *(p+1)) { |
1123 | char c; |
1124 | |
1125 | p++; |
1126 | switch(*p) { |
1127 | case 'n': c = '\n'; break; |
1128 | case 'r': c = '\r'; break; |
1129 | case 't': c = '\t'; break; |
1130 | case 'b': c = '\b'; break; |
1131 | case 'a': c = '\a'; break; |
1132 | default: c = *p; break; |
1133 | } |
1134 | current = sdscatlen(current,&c,1); |
1135 | } else if (*p == '"') { |
1136 | /* closing quote must be followed by a space or |
1137 | * nothing at all. */ |
1138 | if (*(p+1) && !isspace(*(p+1))) goto err; |
1139 | done=1; |
1140 | } else if (!*p) { |
1141 | /* unterminated quotes */ |
1142 | goto err; |
1143 | } else { |
1144 | current = sdscatlen(current,p,1); |
1145 | } |
1146 | } else if (insq) { |
1147 | if (*p == '\\' && *(p+1) == '\'') { |
1148 | p++; |
1149 | current = sdscatlen(current,"'" ,1); |
1150 | } else if (*p == '\'') { |
1151 | /* closing quote must be followed by a space or |
1152 | * nothing at all. */ |
1153 | if (*(p+1) && !isspace(*(p+1))) goto err; |
1154 | done=1; |
1155 | } else if (!*p) { |
1156 | /* unterminated quotes */ |
1157 | goto err; |
1158 | } else { |
1159 | current = sdscatlen(current,p,1); |
1160 | } |
1161 | } else { |
1162 | switch(*p) { |
1163 | case ' ': |
1164 | case '\n': |
1165 | case '\r': |
1166 | case '\t': |
1167 | case '\0': |
1168 | done=1; |
1169 | break; |
1170 | case '"': |
1171 | inq=1; |
1172 | break; |
1173 | case '\'': |
1174 | insq=1; |
1175 | break; |
1176 | default: |
1177 | current = sdscatlen(current,p,1); |
1178 | break; |
1179 | } |
1180 | } |
1181 | if (*p) p++; |
1182 | } |
1183 | /* add the token to the vector */ |
1184 | vector = s_realloc(vector,((*argc)+1)*sizeof(char*)); |
1185 | vector[*argc] = current; |
1186 | (*argc)++; |
1187 | current = NULL; |
1188 | } else { |
1189 | /* Even on empty input string return something not NULL. */ |
1190 | if (vector == NULL) vector = s_malloc(sizeof(void*)); |
1191 | return vector; |
1192 | } |
1193 | } |
1194 | |
1195 | err: |
1196 | while((*argc)--) |
1197 | sdsfree(vector[*argc]); |
1198 | s_free(vector); |
1199 | if (current) sdsfree(current); |
1200 | *argc = 0; |
1201 | return NULL; |
1202 | } |
1203 | |
1204 | /* Modify the string substituting all the occurrences of the set of |
1205 | * characters specified in the 'from' string to the corresponding character |
1206 | * in the 'to' array. |
1207 | * |
1208 | * For instance: sdsmapchars(mystring, "ho", "01", 2) |
1209 | * will have the effect of turning the string "hello" into "0ell1". |
1210 | * |
1211 | * The function returns the sds string pointer, that is always the same |
1212 | * as the input pointer since no resize is needed. */ |
1213 | sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) { |
1214 | size_t j, i, l = sdslen(s); |
1215 | |
1216 | for (j = 0; j < l; j++) { |
1217 | for (i = 0; i < setlen; i++) { |
1218 | if (s[j] == from[i]) { |
1219 | s[j] = to[i]; |
1220 | break; |
1221 | } |
1222 | } |
1223 | } |
1224 | return s; |
1225 | } |
1226 | |
1227 | /* Join an array of C strings using the specified separator (also a C string). |
1228 | * Returns the result as an sds string. */ |
1229 | sds sdsjoin(char **argv, int argc, char *sep) { |
1230 | sds join = sdsempty(); |
1231 | int j; |
1232 | |
1233 | for (j = 0; j < argc; j++) { |
1234 | join = sdscat(join, argv[j]); |
1235 | if (j != argc-1) join = sdscat(join,sep); |
1236 | } |
1237 | return join; |
1238 | } |
1239 | |
1240 | /* Like sdsjoin, but joins an array of SDS strings. */ |
1241 | sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) { |
1242 | sds join = sdsempty(); |
1243 | int j; |
1244 | |
1245 | for (j = 0; j < argc; j++) { |
1246 | join = sdscatsds(join, argv[j]); |
1247 | if (j != argc-1) join = sdscatlen(join,sep,seplen); |
1248 | } |
1249 | return join; |
1250 | } |
1251 | |
1252 | /* Wrappers to the allocators used by SDS. Note that SDS will actually |
1253 | * just use the macros defined into sdsalloc.h in order to avoid to pay |
1254 | * the overhead of function calls. Here we define these wrappers only for |
1255 | * the programs SDS is linked to, if they want to touch the SDS internals |
1256 | * even if they use a different allocator. */ |
1257 | void *sds_malloc(size_t size) { return s_malloc(size); } |
1258 | void *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); } |
1259 | void sds_free(void *ptr) { s_free(ptr); } |
1260 | |
1261 | /* Perform expansion of a template string and return the result as a newly |
1262 | * allocated sds. |
1263 | * |
1264 | * Template variables are specified using curly brackets, e.g. {variable}. |
1265 | * An opening bracket can be quoted by repeating it twice. |
1266 | */ |
1267 | sds sdstemplate(const char *template, sdstemplate_callback_t cb_func, void *cb_arg) |
1268 | { |
1269 | sds res = sdsempty(); |
1270 | const char *p = template; |
1271 | |
1272 | while (*p) { |
1273 | /* Find next variable, copy everything until there */ |
1274 | const char *sv = strchr(p, '{'); |
1275 | if (!sv) { |
1276 | /* Not found: copy till rest of template and stop */ |
1277 | res = sdscat(res, p); |
1278 | break; |
1279 | } else if (sv > p) { |
1280 | /* Found: copy anything up to the beginning of the variable */ |
1281 | res = sdscatlen(res, p, sv - p); |
1282 | } |
1283 | |
1284 | /* Skip into variable name, handle premature end or quoting */ |
1285 | sv++; |
1286 | if (!*sv) goto error; /* Premature end of template */ |
1287 | if (*sv == '{') { |
1288 | /* Quoted '{' */ |
1289 | p = sv + 1; |
1290 | res = sdscat(res, "{" ); |
1291 | continue; |
1292 | } |
1293 | |
1294 | /* Find end of variable name, handle premature end of template */ |
1295 | const char *ev = strchr(sv, '}'); |
1296 | if (!ev) goto error; |
1297 | |
1298 | /* Pass variable name to callback and obtain value. If callback failed, |
1299 | * abort. */ |
1300 | sds varname = sdsnewlen(sv, ev - sv); |
1301 | sds value = cb_func(varname, cb_arg); |
1302 | sdsfree(varname); |
1303 | if (!value) goto error; |
1304 | |
1305 | /* Append value to result and continue */ |
1306 | res = sdscat(res, value); |
1307 | sdsfree(value); |
1308 | p = ev + 1; |
1309 | } |
1310 | |
1311 | return res; |
1312 | |
1313 | error: |
1314 | sdsfree(res); |
1315 | return NULL; |
1316 | } |
1317 | |
1318 | #ifdef REDIS_TEST |
1319 | #include <stdio.h> |
1320 | #include <limits.h> |
1321 | #include "testhelp.h" |
1322 | |
1323 | #define UNUSED(x) (void)(x) |
1324 | |
1325 | static sds sdsTestTemplateCallback(sds varname, void *arg) { |
1326 | UNUSED(arg); |
1327 | static const char *_var1 = "variable1" ; |
1328 | static const char *_var2 = "variable2" ; |
1329 | |
1330 | if (!strcmp(varname, _var1)) return sdsnew("value1" ); |
1331 | else if (!strcmp(varname, _var2)) return sdsnew("value2" ); |
1332 | else return NULL; |
1333 | } |
1334 | |
1335 | int sdsTest(int argc, char **argv, int flags) { |
1336 | UNUSED(argc); |
1337 | UNUSED(argv); |
1338 | UNUSED(flags); |
1339 | |
1340 | { |
1341 | sds x = sdsnew("foo" ), y; |
1342 | |
1343 | test_cond("Create a string and obtain the length" , |
1344 | sdslen(x) == 3 && memcmp(x,"foo\0" ,4) == 0); |
1345 | |
1346 | sdsfree(x); |
1347 | x = sdsnewlen("foo" ,2); |
1348 | test_cond("Create a string with specified length" , |
1349 | sdslen(x) == 2 && memcmp(x,"fo\0" ,3) == 0); |
1350 | |
1351 | x = sdscat(x,"bar" ); |
1352 | test_cond("Strings concatenation" , |
1353 | sdslen(x) == 5 && memcmp(x,"fobar\0" ,6) == 0); |
1354 | |
1355 | x = sdscpy(x,"a" ); |
1356 | test_cond("sdscpy() against an originally longer string" , |
1357 | sdslen(x) == 1 && memcmp(x,"a\0" ,2) == 0); |
1358 | |
1359 | x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk" ); |
1360 | test_cond("sdscpy() against an originally shorter string" , |
1361 | sdslen(x) == 33 && |
1362 | memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0" ,33) == 0); |
1363 | |
1364 | sdsfree(x); |
1365 | x = sdscatprintf(sdsempty(),"%d" ,123); |
1366 | test_cond("sdscatprintf() seems working in the base case" , |
1367 | sdslen(x) == 3 && memcmp(x,"123\0" ,4) == 0); |
1368 | |
1369 | sdsfree(x); |
1370 | x = sdscatprintf(sdsempty(),"a%cb" ,0); |
1371 | test_cond("sdscatprintf() seems working with \\0 inside of result" , |
1372 | sdslen(x) == 3 && memcmp(x,"a\0" "b\0" ,4) == 0); |
1373 | |
1374 | { |
1375 | sdsfree(x); |
1376 | char etalon[1024*1024]; |
1377 | for (size_t i = 0; i < sizeof(etalon); i++) { |
1378 | etalon[i] = '0'; |
1379 | } |
1380 | x = sdscatprintf(sdsempty(),"%0*d" ,(int)sizeof(etalon),0); |
1381 | test_cond("sdscatprintf() can print 1MB" , |
1382 | sdslen(x) == sizeof(etalon) && memcmp(x,etalon,sizeof(etalon)) == 0); |
1383 | } |
1384 | |
1385 | sdsfree(x); |
1386 | x = sdsnew("--" ); |
1387 | x = sdscatfmt(x, "Hello %s World %I,%I--" , "Hi!" , LLONG_MIN,LLONG_MAX); |
1388 | test_cond("sdscatfmt() seems working in the base case" , |
1389 | sdslen(x) == 60 && |
1390 | memcmp(x,"--Hello Hi! World -9223372036854775808," |
1391 | "9223372036854775807--" ,60) == 0); |
1392 | printf("[%s]\n" ,x); |
1393 | |
1394 | sdsfree(x); |
1395 | x = sdsnew("--" ); |
1396 | x = sdscatfmt(x, "%u,%U--" , UINT_MAX, ULLONG_MAX); |
1397 | test_cond("sdscatfmt() seems working with unsigned numbers" , |
1398 | sdslen(x) == 35 && |
1399 | memcmp(x,"--4294967295,18446744073709551615--" ,35) == 0); |
1400 | |
1401 | sdsfree(x); |
1402 | x = sdsnew(" x " ); |
1403 | sdstrim(x," x" ); |
1404 | test_cond("sdstrim() works when all chars match" , |
1405 | sdslen(x) == 0); |
1406 | |
1407 | sdsfree(x); |
1408 | x = sdsnew(" x " ); |
1409 | sdstrim(x," " ); |
1410 | test_cond("sdstrim() works when a single char remains" , |
1411 | sdslen(x) == 1 && x[0] == 'x'); |
1412 | |
1413 | sdsfree(x); |
1414 | x = sdsnew("xxciaoyyy" ); |
1415 | sdstrim(x,"xy" ); |
1416 | test_cond("sdstrim() correctly trims characters" , |
1417 | sdslen(x) == 4 && memcmp(x,"ciao\0" ,5) == 0); |
1418 | |
1419 | y = sdsdup(x); |
1420 | sdsrange(y,1,1); |
1421 | test_cond("sdsrange(...,1,1)" , |
1422 | sdslen(y) == 1 && memcmp(y,"i\0" ,2) == 0); |
1423 | |
1424 | sdsfree(y); |
1425 | y = sdsdup(x); |
1426 | sdsrange(y,1,-1); |
1427 | test_cond("sdsrange(...,1,-1)" , |
1428 | sdslen(y) == 3 && memcmp(y,"iao\0" ,4) == 0); |
1429 | |
1430 | sdsfree(y); |
1431 | y = sdsdup(x); |
1432 | sdsrange(y,-2,-1); |
1433 | test_cond("sdsrange(...,-2,-1)" , |
1434 | sdslen(y) == 2 && memcmp(y,"ao\0" ,3) == 0); |
1435 | |
1436 | sdsfree(y); |
1437 | y = sdsdup(x); |
1438 | sdsrange(y,2,1); |
1439 | test_cond("sdsrange(...,2,1)" , |
1440 | sdslen(y) == 0 && memcmp(y,"\0" ,1) == 0); |
1441 | |
1442 | sdsfree(y); |
1443 | y = sdsdup(x); |
1444 | sdsrange(y,1,100); |
1445 | test_cond("sdsrange(...,1,100)" , |
1446 | sdslen(y) == 3 && memcmp(y,"iao\0" ,4) == 0); |
1447 | |
1448 | sdsfree(y); |
1449 | y = sdsdup(x); |
1450 | sdsrange(y,100,100); |
1451 | test_cond("sdsrange(...,100,100)" , |
1452 | sdslen(y) == 0 && memcmp(y,"\0" ,1) == 0); |
1453 | |
1454 | sdsfree(y); |
1455 | y = sdsdup(x); |
1456 | sdsrange(y,4,6); |
1457 | test_cond("sdsrange(...,4,6)" , |
1458 | sdslen(y) == 0 && memcmp(y,"\0" ,1) == 0); |
1459 | |
1460 | sdsfree(y); |
1461 | y = sdsdup(x); |
1462 | sdsrange(y,3,6); |
1463 | test_cond("sdsrange(...,3,6)" , |
1464 | sdslen(y) == 1 && memcmp(y,"o\0" ,2) == 0); |
1465 | |
1466 | sdsfree(y); |
1467 | sdsfree(x); |
1468 | x = sdsnew("foo" ); |
1469 | y = sdsnew("foa" ); |
1470 | test_cond("sdscmp(foo,foa)" , sdscmp(x,y) > 0); |
1471 | |
1472 | sdsfree(y); |
1473 | sdsfree(x); |
1474 | x = sdsnew("bar" ); |
1475 | y = sdsnew("bar" ); |
1476 | test_cond("sdscmp(bar,bar)" , sdscmp(x,y) == 0); |
1477 | |
1478 | sdsfree(y); |
1479 | sdsfree(x); |
1480 | x = sdsnew("aar" ); |
1481 | y = sdsnew("bar" ); |
1482 | test_cond("sdscmp(bar,bar)" , sdscmp(x,y) < 0); |
1483 | |
1484 | sdsfree(y); |
1485 | sdsfree(x); |
1486 | x = sdsnewlen("\a\n\0foo\r" ,7); |
1487 | y = sdscatrepr(sdsempty(),x,sdslen(x)); |
1488 | test_cond("sdscatrepr(...data...)" , |
1489 | memcmp(y,"\"\\a\\n\\x00foo\\r\"" ,15) == 0); |
1490 | |
1491 | { |
1492 | unsigned int oldfree; |
1493 | char *p; |
1494 | int i; |
1495 | size_t step = 10, j; |
1496 | |
1497 | sdsfree(x); |
1498 | sdsfree(y); |
1499 | x = sdsnew("0" ); |
1500 | test_cond("sdsnew() free/len buffers" , sdslen(x) == 1 && sdsavail(x) == 0); |
1501 | |
1502 | /* Run the test a few times in order to hit the first two |
1503 | * SDS header types. */ |
1504 | for (i = 0; i < 10; i++) { |
1505 | size_t oldlen = sdslen(x); |
1506 | x = sdsMakeRoomFor(x,step); |
1507 | int type = x[-1]&SDS_TYPE_MASK; |
1508 | |
1509 | test_cond("sdsMakeRoomFor() len" , sdslen(x) == oldlen); |
1510 | if (type != SDS_TYPE_5) { |
1511 | test_cond("sdsMakeRoomFor() free" , sdsavail(x) >= step); |
1512 | oldfree = sdsavail(x); |
1513 | UNUSED(oldfree); |
1514 | } |
1515 | p = x+oldlen; |
1516 | for (j = 0; j < step; j++) { |
1517 | p[j] = 'A'+j; |
1518 | } |
1519 | sdsIncrLen(x,step); |
1520 | } |
1521 | test_cond("sdsMakeRoomFor() content" , |
1522 | memcmp("0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ" ,x,101) == 0); |
1523 | test_cond("sdsMakeRoomFor() final length" ,sdslen(x)==101); |
1524 | |
1525 | sdsfree(x); |
1526 | } |
1527 | |
1528 | /* Simple template */ |
1529 | x = sdstemplate("v1={variable1} v2={variable2}" , sdsTestTemplateCallback, NULL); |
1530 | test_cond("sdstemplate() normal flow" , |
1531 | memcmp(x,"v1=value1 v2=value2" ,19) == 0); |
1532 | sdsfree(x); |
1533 | |
1534 | /* Template with callback error */ |
1535 | x = sdstemplate("v1={variable1} v3={doesnotexist}" , sdsTestTemplateCallback, NULL); |
1536 | test_cond("sdstemplate() with callback error" , x == NULL); |
1537 | |
1538 | /* Template with empty var name */ |
1539 | x = sdstemplate("v1={" , sdsTestTemplateCallback, NULL); |
1540 | test_cond("sdstemplate() with empty var name" , x == NULL); |
1541 | |
1542 | /* Template with truncated var name */ |
1543 | x = sdstemplate("v1={start" , sdsTestTemplateCallback, NULL); |
1544 | test_cond("sdstemplate() with truncated var name" , x == NULL); |
1545 | |
1546 | /* Template with quoting */ |
1547 | x = sdstemplate("v1={{{variable1}} {{} v2={variable2}" , sdsTestTemplateCallback, NULL); |
1548 | test_cond("sdstemplate() with quoting" , |
1549 | memcmp(x,"v1={value1} {} v2=value2" ,24) == 0); |
1550 | sdsfree(x); |
1551 | |
1552 | /* Test sdsresize - extend */ |
1553 | x = sdsnew("1234567890123456789012345678901234567890" ); |
1554 | x = sdsResize(x, 200); |
1555 | test_cond("sdsrezie() expand len" , sdslen(x) == 40); |
1556 | test_cond("sdsrezie() expand strlen" , strlen(x) == 40); |
1557 | test_cond("sdsrezie() expand alloc" , sdsalloc(x) == 200); |
1558 | /* Test sdsresize - trim free space */ |
1559 | x = sdsResize(x, 80); |
1560 | test_cond("sdsrezie() shrink len" , sdslen(x) == 40); |
1561 | test_cond("sdsrezie() shrink strlen" , strlen(x) == 40); |
1562 | test_cond("sdsrezie() shrink alloc" , sdsalloc(x) == 80); |
1563 | /* Test sdsresize - crop used space */ |
1564 | x = sdsResize(x, 30); |
1565 | test_cond("sdsrezie() crop len" , sdslen(x) == 30); |
1566 | test_cond("sdsrezie() crop strlen" , strlen(x) == 30); |
1567 | test_cond("sdsrezie() crop alloc" , sdsalloc(x) == 30); |
1568 | /* Test sdsresize - extend to different class */ |
1569 | x = sdsResize(x, 400); |
1570 | test_cond("sdsrezie() expand len" , sdslen(x) == 30); |
1571 | test_cond("sdsrezie() expand strlen" , strlen(x) == 30); |
1572 | test_cond("sdsrezie() expand alloc" , sdsalloc(x) == 400); |
1573 | /* Test sdsresize - shrink to different class */ |
1574 | x = sdsResize(x, 4); |
1575 | test_cond("sdsrezie() crop len" , sdslen(x) == 4); |
1576 | test_cond("sdsrezie() crop strlen" , strlen(x) == 4); |
1577 | test_cond("sdsrezie() crop alloc" , sdsalloc(x) == 4); |
1578 | sdsfree(x); |
1579 | } |
1580 | return 0; |
1581 | } |
1582 | #endif |
1583 | |