/src/h2o/deps/hiredis/sds.c
Line | Count | Source (jump to first uncovered line) |
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 "fmacros.h" |
34 | | #include <stdio.h> |
35 | | #include <stdlib.h> |
36 | | #include <string.h> |
37 | | #include <ctype.h> |
38 | | #include <assert.h> |
39 | | #include <limits.h> |
40 | | #include "sds.h" |
41 | | #include "sdsalloc.h" |
42 | | |
43 | 0 | static inline int sdsHdrSize(char type) { |
44 | 0 | switch(type&SDS_TYPE_MASK) { |
45 | 0 | case SDS_TYPE_5: |
46 | 0 | return sizeof(struct sdshdr5); |
47 | 0 | case SDS_TYPE_8: |
48 | 0 | return sizeof(struct sdshdr8); |
49 | 0 | case SDS_TYPE_16: |
50 | 0 | return sizeof(struct sdshdr16); |
51 | 0 | case SDS_TYPE_32: |
52 | 0 | return sizeof(struct sdshdr32); |
53 | 0 | case SDS_TYPE_64: |
54 | 0 | return sizeof(struct sdshdr64); |
55 | 0 | } |
56 | 0 | return 0; |
57 | 0 | } |
58 | | |
59 | 0 | static inline char sdsReqType(size_t string_size) { |
60 | 0 | if (string_size < 32) |
61 | 0 | return SDS_TYPE_5; |
62 | 0 | if (string_size < 0xff) |
63 | 0 | return SDS_TYPE_8; |
64 | 0 | if (string_size < 0xffff) |
65 | 0 | return SDS_TYPE_16; |
66 | 0 | if (string_size < 0xffffffff) |
67 | 0 | return SDS_TYPE_32; |
68 | 0 | return SDS_TYPE_64; |
69 | 0 | } |
70 | | |
71 | | /* Create a new sds string with the content specified by the 'init' pointer |
72 | | * and 'initlen'. |
73 | | * If NULL is used for 'init' the string is initialized with zero bytes. |
74 | | * |
75 | | * The string is always null-terminated (all the sds strings are, always) so |
76 | | * even if you create an sds string with: |
77 | | * |
78 | | * mystring = sdsnewlen("abc",3); |
79 | | * |
80 | | * You can print the string with printf() as there is an implicit \0 at the |
81 | | * end of the string. However the string is binary safe and can contain |
82 | | * \0 characters in the middle, as the length is stored in the sds header. */ |
83 | 0 | sds sdsnewlen(const void *init, size_t initlen) { |
84 | 0 | void *sh; |
85 | 0 | sds s; |
86 | 0 | char type = sdsReqType(initlen); |
87 | | /* Empty strings are usually created in order to append. Use type 8 |
88 | | * since type 5 is not good at this. */ |
89 | 0 | if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8; |
90 | 0 | int hdrlen = sdsHdrSize(type); |
91 | 0 | unsigned char *fp; /* flags pointer. */ |
92 | |
|
93 | 0 | if (hdrlen+initlen+1 <= initlen) return NULL; /* Catch size_t overflow */ |
94 | 0 | sh = s_malloc(hdrlen+initlen+1); |
95 | 0 | if (sh == NULL) return NULL; |
96 | 0 | if (!init) |
97 | 0 | memset(sh, 0, hdrlen+initlen+1); |
98 | 0 | s = (char*)sh+hdrlen; |
99 | 0 | fp = ((unsigned char*)s)-1; |
100 | 0 | switch(type) { |
101 | 0 | case SDS_TYPE_5: { |
102 | 0 | *fp = type | (initlen << SDS_TYPE_BITS); |
103 | 0 | break; |
104 | 0 | } |
105 | 0 | case SDS_TYPE_8: { |
106 | 0 | SDS_HDR_VAR(8,s); |
107 | 0 | sh->len = initlen; |
108 | 0 | sh->alloc = initlen; |
109 | 0 | *fp = type; |
110 | 0 | break; |
111 | 0 | } |
112 | 0 | case SDS_TYPE_16: { |
113 | 0 | SDS_HDR_VAR(16,s); |
114 | 0 | sh->len = initlen; |
115 | 0 | sh->alloc = initlen; |
116 | 0 | *fp = type; |
117 | 0 | break; |
118 | 0 | } |
119 | 0 | case SDS_TYPE_32: { |
120 | 0 | SDS_HDR_VAR(32,s); |
121 | 0 | sh->len = initlen; |
122 | 0 | sh->alloc = initlen; |
123 | 0 | *fp = type; |
124 | 0 | break; |
125 | 0 | } |
126 | 0 | case SDS_TYPE_64: { |
127 | 0 | SDS_HDR_VAR(64,s); |
128 | 0 | sh->len = initlen; |
129 | 0 | sh->alloc = initlen; |
130 | 0 | *fp = type; |
131 | 0 | break; |
132 | 0 | } |
133 | 0 | } |
134 | 0 | if (initlen && init) |
135 | 0 | memcpy(s, init, initlen); |
136 | 0 | s[initlen] = '\0'; |
137 | 0 | return s; |
138 | 0 | } |
139 | | |
140 | | /* Create an empty (zero length) sds string. Even in this case the string |
141 | | * always has an implicit null term. */ |
142 | 0 | sds sdsempty(void) { |
143 | 0 | return sdsnewlen("",0); |
144 | 0 | } |
145 | | |
146 | | /* Create a new sds string starting from a null terminated C string. */ |
147 | 0 | sds sdsnew(const char *init) { |
148 | 0 | size_t initlen = (init == NULL) ? 0 : strlen(init); |
149 | 0 | return sdsnewlen(init, initlen); |
150 | 0 | } |
151 | | |
152 | | /* Duplicate an sds string. */ |
153 | 0 | sds sdsdup(const sds s) { |
154 | 0 | return sdsnewlen(s, sdslen(s)); |
155 | 0 | } |
156 | | |
157 | | /* Free an sds string. No operation is performed if 's' is NULL. */ |
158 | 0 | void sdsfree(sds s) { |
159 | 0 | if (s == NULL) return; |
160 | 0 | s_free((char*)s-sdsHdrSize(s[-1])); |
161 | 0 | } |
162 | | |
163 | | /* Set the sds string length to the length as obtained with strlen(), so |
164 | | * considering as content only up to the first null term character. |
165 | | * |
166 | | * This function is useful when the sds string is hacked manually in some |
167 | | * way, like in the following example: |
168 | | * |
169 | | * s = sdsnew("foobar"); |
170 | | * s[2] = '\0'; |
171 | | * sdsupdatelen(s); |
172 | | * printf("%d\n", sdslen(s)); |
173 | | * |
174 | | * The output will be "2", but if we comment out the call to sdsupdatelen() |
175 | | * the output will be "6" as the string was modified but the logical length |
176 | | * remains 6 bytes. */ |
177 | 0 | void sdsupdatelen(sds s) { |
178 | 0 | size_t reallen = strlen(s); |
179 | 0 | sdssetlen(s, reallen); |
180 | 0 | } |
181 | | |
182 | | /* Modify an sds string in-place to make it empty (zero length). |
183 | | * However all the existing buffer is not discarded but set as free space |
184 | | * so that next append operations will not require allocations up to the |
185 | | * number of bytes previously available. */ |
186 | 0 | void sdsclear(sds s) { |
187 | 0 | sdssetlen(s, 0); |
188 | 0 | s[0] = '\0'; |
189 | 0 | } |
190 | | |
191 | | /* Enlarge the free space at the end of the sds string so that the caller |
192 | | * is sure that after calling this function can overwrite up to addlen |
193 | | * bytes after the end of the string, plus one more byte for nul term. |
194 | | * |
195 | | * Note: this does not change the *length* of the sds string as returned |
196 | | * by sdslen(), but only the free buffer space we have. */ |
197 | 0 | sds sdsMakeRoomFor(sds s, size_t addlen) { |
198 | 0 | void *sh, *newsh; |
199 | 0 | size_t avail = sdsavail(s); |
200 | 0 | size_t len, newlen, reqlen; |
201 | 0 | char type, oldtype = s[-1] & SDS_TYPE_MASK; |
202 | 0 | int hdrlen; |
203 | | |
204 | | /* Return ASAP if there is enough space left. */ |
205 | 0 | if (avail >= addlen) return s; |
206 | | |
207 | 0 | len = sdslen(s); |
208 | 0 | sh = (char*)s-sdsHdrSize(oldtype); |
209 | 0 | reqlen = newlen = (len+addlen); |
210 | 0 | if (newlen <= len) return NULL; /* Catch size_t overflow */ |
211 | 0 | if (newlen < SDS_MAX_PREALLOC) |
212 | 0 | newlen *= 2; |
213 | 0 | else |
214 | 0 | newlen += SDS_MAX_PREALLOC; |
215 | |
|
216 | 0 | type = sdsReqType(newlen); |
217 | | |
218 | | /* Don't use type 5: the user is appending to the string and type 5 is |
219 | | * not able to remember empty space, so sdsMakeRoomFor() must be called |
220 | | * at every appending operation. */ |
221 | 0 | if (type == SDS_TYPE_5) type = SDS_TYPE_8; |
222 | |
|
223 | 0 | hdrlen = sdsHdrSize(type); |
224 | 0 | if (hdrlen+newlen+1 <= reqlen) return NULL; /* Catch size_t overflow */ |
225 | 0 | if (oldtype==type) { |
226 | 0 | newsh = s_realloc(sh, hdrlen+newlen+1); |
227 | 0 | if (newsh == NULL) return NULL; |
228 | 0 | s = (char*)newsh+hdrlen; |
229 | 0 | } else { |
230 | | /* Since the header size changes, need to move the string forward, |
231 | | * and can't use realloc */ |
232 | 0 | newsh = s_malloc(hdrlen+newlen+1); |
233 | 0 | if (newsh == NULL) return NULL; |
234 | 0 | memcpy((char*)newsh+hdrlen, s, len+1); |
235 | 0 | s_free(sh); |
236 | 0 | s = (char*)newsh+hdrlen; |
237 | 0 | s[-1] = type; |
238 | 0 | sdssetlen(s, len); |
239 | 0 | } |
240 | 0 | sdssetalloc(s, newlen); |
241 | 0 | return s; |
242 | 0 | } |
243 | | |
244 | | /* Reallocate the sds string so that it has no free space at the end. The |
245 | | * contained string remains not altered, but next concatenation operations |
246 | | * will require a reallocation. |
247 | | * |
248 | | * After the call, the passed sds string is no longer valid and all the |
249 | | * references must be substituted with the new pointer returned by the call. */ |
250 | 0 | sds sdsRemoveFreeSpace(sds s) { |
251 | 0 | void *sh, *newsh; |
252 | 0 | char type, oldtype = s[-1] & SDS_TYPE_MASK; |
253 | 0 | int hdrlen; |
254 | 0 | size_t len = sdslen(s); |
255 | 0 | sh = (char*)s-sdsHdrSize(oldtype); |
256 | |
|
257 | 0 | type = sdsReqType(len); |
258 | 0 | hdrlen = sdsHdrSize(type); |
259 | 0 | if (oldtype==type) { |
260 | 0 | newsh = s_realloc(sh, hdrlen+len+1); |
261 | 0 | if (newsh == NULL) return NULL; |
262 | 0 | s = (char*)newsh+hdrlen; |
263 | 0 | } else { |
264 | 0 | newsh = s_malloc(hdrlen+len+1); |
265 | 0 | if (newsh == NULL) return NULL; |
266 | 0 | memcpy((char*)newsh+hdrlen, s, len+1); |
267 | 0 | s_free(sh); |
268 | 0 | s = (char*)newsh+hdrlen; |
269 | 0 | s[-1] = type; |
270 | 0 | sdssetlen(s, len); |
271 | 0 | } |
272 | 0 | sdssetalloc(s, len); |
273 | 0 | return s; |
274 | 0 | } |
275 | | |
276 | | /* Return the total size of the allocation of the specifed sds string, |
277 | | * including: |
278 | | * 1) The sds header before the pointer. |
279 | | * 2) The string. |
280 | | * 3) The free buffer at the end if any. |
281 | | * 4) The implicit null term. |
282 | | */ |
283 | 0 | size_t sdsAllocSize(sds s) { |
284 | 0 | size_t alloc = sdsalloc(s); |
285 | 0 | return sdsHdrSize(s[-1])+alloc+1; |
286 | 0 | } |
287 | | |
288 | | /* Return the pointer of the actual SDS allocation (normally SDS strings |
289 | | * are referenced by the start of the string buffer). */ |
290 | 0 | void *sdsAllocPtr(sds s) { |
291 | 0 | return (void*) (s-sdsHdrSize(s[-1])); |
292 | 0 | } |
293 | | |
294 | | /* Increment the sds length and decrements the left free space at the |
295 | | * end of the string according to 'incr'. Also set the null term |
296 | | * in the new end of the string. |
297 | | * |
298 | | * This function is used in order to fix the string length after the |
299 | | * user calls sdsMakeRoomFor(), writes something after the end of |
300 | | * the current string, and finally needs to set the new length. |
301 | | * |
302 | | * Note: it is possible to use a negative increment in order to |
303 | | * right-trim the string. |
304 | | * |
305 | | * Usage example: |
306 | | * |
307 | | * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the |
308 | | * following schema, to cat bytes coming from the kernel to the end of an |
309 | | * sds string without copying into an intermediate buffer: |
310 | | * |
311 | | * oldlen = sdslen(s); |
312 | | * s = sdsMakeRoomFor(s, BUFFER_SIZE); |
313 | | * nread = read(fd, s+oldlen, BUFFER_SIZE); |
314 | | * ... check for nread <= 0 and handle it ... |
315 | | * sdsIncrLen(s, nread); |
316 | | */ |
317 | 0 | void sdsIncrLen(sds s, int incr) { |
318 | 0 | unsigned char flags = s[-1]; |
319 | 0 | size_t len; |
320 | 0 | switch(flags&SDS_TYPE_MASK) { |
321 | 0 | case SDS_TYPE_5: { |
322 | 0 | unsigned char *fp = ((unsigned char*)s)-1; |
323 | 0 | unsigned char oldlen = SDS_TYPE_5_LEN(flags); |
324 | 0 | assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr))); |
325 | 0 | *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS); |
326 | 0 | len = oldlen+incr; |
327 | 0 | break; |
328 | 0 | } |
329 | 0 | case SDS_TYPE_8: { |
330 | 0 | SDS_HDR_VAR(8,s); |
331 | 0 | assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); |
332 | 0 | len = (sh->len += incr); |
333 | 0 | break; |
334 | 0 | } |
335 | 0 | case SDS_TYPE_16: { |
336 | 0 | SDS_HDR_VAR(16,s); |
337 | 0 | assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); |
338 | 0 | len = (sh->len += incr); |
339 | 0 | break; |
340 | 0 | } |
341 | 0 | case SDS_TYPE_32: { |
342 | 0 | SDS_HDR_VAR(32,s); |
343 | 0 | assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); |
344 | 0 | len = (sh->len += incr); |
345 | 0 | break; |
346 | 0 | } |
347 | 0 | case SDS_TYPE_64: { |
348 | 0 | SDS_HDR_VAR(64,s); |
349 | 0 | assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr))); |
350 | 0 | len = (sh->len += incr); |
351 | 0 | break; |
352 | 0 | } |
353 | 0 | default: len = 0; /* Just to avoid compilation warnings. */ |
354 | 0 | } |
355 | 0 | s[len] = '\0'; |
356 | 0 | } |
357 | | |
358 | | /* Grow the sds to have the specified length. Bytes that were not part of |
359 | | * the original length of the sds will be set to zero. |
360 | | * |
361 | | * if the specified length is smaller than the current length, no operation |
362 | | * is performed. */ |
363 | 0 | sds sdsgrowzero(sds s, size_t len) { |
364 | 0 | size_t curlen = sdslen(s); |
365 | |
|
366 | 0 | if (len <= curlen) return s; |
367 | 0 | s = sdsMakeRoomFor(s,len-curlen); |
368 | 0 | if (s == NULL) return NULL; |
369 | | |
370 | | /* Make sure added region doesn't contain garbage */ |
371 | 0 | memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */ |
372 | 0 | sdssetlen(s, len); |
373 | 0 | return s; |
374 | 0 | } |
375 | | |
376 | | /* Append the specified binary-safe string pointed by 't' of 'len' bytes to the |
377 | | * end of the specified sds string 's'. |
378 | | * |
379 | | * After the call, the passed sds string is no longer valid and all the |
380 | | * references must be substituted with the new pointer returned by the call. */ |
381 | 0 | sds sdscatlen(sds s, const void *t, size_t len) { |
382 | 0 | size_t curlen = sdslen(s); |
383 | |
|
384 | 0 | s = sdsMakeRoomFor(s,len); |
385 | 0 | if (s == NULL) return NULL; |
386 | 0 | memcpy(s+curlen, t, len); |
387 | 0 | sdssetlen(s, curlen+len); |
388 | 0 | s[curlen+len] = '\0'; |
389 | 0 | return s; |
390 | 0 | } |
391 | | |
392 | | /* Append the specified null termianted C string to the sds string 's'. |
393 | | * |
394 | | * After the call, the passed sds string is no longer valid and all the |
395 | | * references must be substituted with the new pointer returned by the call. */ |
396 | 0 | sds sdscat(sds s, const char *t) { |
397 | 0 | return sdscatlen(s, t, strlen(t)); |
398 | 0 | } |
399 | | |
400 | | /* Append the specified sds 't' to the existing sds 's'. |
401 | | * |
402 | | * After the call, the modified sds string is no longer valid and all the |
403 | | * references must be substituted with the new pointer returned by the call. */ |
404 | 0 | sds sdscatsds(sds s, const sds t) { |
405 | 0 | return sdscatlen(s, t, sdslen(t)); |
406 | 0 | } |
407 | | |
408 | | /* Destructively modify the sds string 's' to hold the specified binary |
409 | | * safe string pointed by 't' of length 'len' bytes. */ |
410 | 0 | sds sdscpylen(sds s, const char *t, size_t len) { |
411 | 0 | if (sdsalloc(s) < len) { |
412 | 0 | s = sdsMakeRoomFor(s,len-sdslen(s)); |
413 | 0 | if (s == NULL) return NULL; |
414 | 0 | } |
415 | 0 | memcpy(s, t, len); |
416 | 0 | s[len] = '\0'; |
417 | 0 | sdssetlen(s, len); |
418 | 0 | return s; |
419 | 0 | } |
420 | | |
421 | | /* Like sdscpylen() but 't' must be a null-terminated string so that the length |
422 | | * of the string is obtained with strlen(). */ |
423 | 0 | sds sdscpy(sds s, const char *t) { |
424 | 0 | return sdscpylen(s, t, strlen(t)); |
425 | 0 | } |
426 | | |
427 | | /* Helper for sdscatlonglong() doing the actual number -> string |
428 | | * conversion. 's' must point to a string with room for at least |
429 | | * SDS_LLSTR_SIZE bytes. |
430 | | * |
431 | | * The function returns the length of the null-terminated string |
432 | | * representation stored at 's'. */ |
433 | | #define SDS_LLSTR_SIZE 21 |
434 | 0 | int sdsll2str(char *s, long long value) { |
435 | 0 | char *p, aux; |
436 | 0 | unsigned long long v; |
437 | 0 | size_t l; |
438 | | |
439 | | /* Generate the string representation, this method produces |
440 | | * an reversed string. */ |
441 | 0 | v = (value < 0) ? -value : value; |
442 | 0 | p = s; |
443 | 0 | do { |
444 | 0 | *p++ = '0'+(v%10); |
445 | 0 | v /= 10; |
446 | 0 | } while(v); |
447 | 0 | if (value < 0) *p++ = '-'; |
448 | | |
449 | | /* Compute length and add null term. */ |
450 | 0 | l = p-s; |
451 | 0 | *p = '\0'; |
452 | | |
453 | | /* Reverse the string. */ |
454 | 0 | p--; |
455 | 0 | while(s < p) { |
456 | 0 | aux = *s; |
457 | 0 | *s = *p; |
458 | 0 | *p = aux; |
459 | 0 | s++; |
460 | 0 | p--; |
461 | 0 | } |
462 | 0 | return l; |
463 | 0 | } |
464 | | |
465 | | /* Identical sdsll2str(), but for unsigned long long type. */ |
466 | 0 | int sdsull2str(char *s, unsigned long long v) { |
467 | 0 | char *p, aux; |
468 | 0 | size_t l; |
469 | | |
470 | | /* Generate the string representation, this method produces |
471 | | * an reversed string. */ |
472 | 0 | p = s; |
473 | 0 | do { |
474 | 0 | *p++ = '0'+(v%10); |
475 | 0 | v /= 10; |
476 | 0 | } while(v); |
477 | | |
478 | | /* Compute length and add null term. */ |
479 | 0 | l = p-s; |
480 | 0 | *p = '\0'; |
481 | | |
482 | | /* Reverse the string. */ |
483 | 0 | p--; |
484 | 0 | while(s < p) { |
485 | 0 | aux = *s; |
486 | 0 | *s = *p; |
487 | 0 | *p = aux; |
488 | 0 | s++; |
489 | 0 | p--; |
490 | 0 | } |
491 | 0 | return l; |
492 | 0 | } |
493 | | |
494 | | /* Create an sds string from a long long value. It is much faster than: |
495 | | * |
496 | | * sdscatprintf(sdsempty(),"%lld\n", value); |
497 | | */ |
498 | 0 | sds sdsfromlonglong(long long value) { |
499 | 0 | char buf[SDS_LLSTR_SIZE]; |
500 | 0 | int len = sdsll2str(buf,value); |
501 | |
|
502 | 0 | return sdsnewlen(buf,len); |
503 | 0 | } |
504 | | |
505 | | /* Like sdscatprintf() but gets va_list instead of being variadic. */ |
506 | 0 | sds sdscatvprintf(sds s, const char *fmt, va_list ap) { |
507 | 0 | va_list cpy; |
508 | 0 | char staticbuf[1024], *buf = staticbuf, *t; |
509 | 0 | size_t buflen = strlen(fmt)*2; |
510 | | |
511 | | /* We try to start using a static buffer for speed. |
512 | | * If not possible we revert to heap allocation. */ |
513 | 0 | if (buflen > sizeof(staticbuf)) { |
514 | 0 | buf = s_malloc(buflen); |
515 | 0 | if (buf == NULL) return NULL; |
516 | 0 | } else { |
517 | 0 | buflen = sizeof(staticbuf); |
518 | 0 | } |
519 | | |
520 | | /* Try with buffers two times bigger every time we fail to |
521 | | * fit the string in the current buffer size. */ |
522 | 0 | while(1) { |
523 | 0 | buf[buflen-2] = '\0'; |
524 | 0 | va_copy(cpy,ap); |
525 | 0 | vsnprintf(buf, buflen, fmt, cpy); |
526 | 0 | va_end(cpy); |
527 | 0 | if (buf[buflen-2] != '\0') { |
528 | 0 | if (buf != staticbuf) s_free(buf); |
529 | 0 | buflen *= 2; |
530 | 0 | buf = s_malloc(buflen); |
531 | 0 | if (buf == NULL) return NULL; |
532 | 0 | continue; |
533 | 0 | } |
534 | 0 | break; |
535 | 0 | } |
536 | | |
537 | | /* Finally concat the obtained string to the SDS string and return it. */ |
538 | 0 | t = sdscat(s, buf); |
539 | 0 | if (buf != staticbuf) s_free(buf); |
540 | 0 | return t; |
541 | 0 | } |
542 | | |
543 | | /* Append to the sds string 's' a string obtained using printf-alike format |
544 | | * specifier. |
545 | | * |
546 | | * After the call, the modified sds string is no longer valid and all the |
547 | | * references must be substituted with the new pointer returned by the call. |
548 | | * |
549 | | * Example: |
550 | | * |
551 | | * s = sdsnew("Sum is: "); |
552 | | * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b). |
553 | | * |
554 | | * Often you need to create a string from scratch with the printf-alike |
555 | | * format. When this is the need, just use sdsempty() as the target string: |
556 | | * |
557 | | * s = sdscatprintf(sdsempty(), "... your format ...", args); |
558 | | */ |
559 | 0 | sds sdscatprintf(sds s, const char *fmt, ...) { |
560 | 0 | va_list ap; |
561 | 0 | char *t; |
562 | 0 | va_start(ap, fmt); |
563 | 0 | t = sdscatvprintf(s,fmt,ap); |
564 | 0 | va_end(ap); |
565 | 0 | return t; |
566 | 0 | } |
567 | | |
568 | | /* This function is similar to sdscatprintf, but much faster as it does |
569 | | * not rely on sprintf() family functions implemented by the libc that |
570 | | * are often very slow. Moreover directly handling the sds string as |
571 | | * new data is concatenated provides a performance improvement. |
572 | | * |
573 | | * However this function only handles an incompatible subset of printf-alike |
574 | | * format specifiers: |
575 | | * |
576 | | * %s - C String |
577 | | * %S - SDS string |
578 | | * %i - signed int |
579 | | * %I - 64 bit signed integer (long long, int64_t) |
580 | | * %u - unsigned int |
581 | | * %U - 64 bit unsigned integer (unsigned long long, uint64_t) |
582 | | * %% - Verbatim "%" character. |
583 | | */ |
584 | 0 | sds sdscatfmt(sds s, char const *fmt, ...) { |
585 | 0 | const char *f = fmt; |
586 | 0 | long i; |
587 | 0 | va_list ap; |
588 | |
|
589 | 0 | va_start(ap,fmt); |
590 | 0 | i = sdslen(s); /* Position of the next byte to write to dest str. */ |
591 | 0 | while(*f) { |
592 | 0 | char next, *str; |
593 | 0 | size_t l; |
594 | 0 | long long num; |
595 | 0 | unsigned long long unum; |
596 | | |
597 | | /* Make sure there is always space for at least 1 char. */ |
598 | 0 | if (sdsavail(s)==0) { |
599 | 0 | s = sdsMakeRoomFor(s,1); |
600 | 0 | if (s == NULL) goto fmt_error; |
601 | 0 | } |
602 | | |
603 | 0 | switch(*f) { |
604 | 0 | case '%': |
605 | 0 | next = *(f+1); |
606 | 0 | f++; |
607 | 0 | switch(next) { |
608 | 0 | case 's': |
609 | 0 | case 'S': |
610 | 0 | str = va_arg(ap,char*); |
611 | 0 | l = (next == 's') ? strlen(str) : sdslen(str); |
612 | 0 | if (sdsavail(s) < l) { |
613 | 0 | s = sdsMakeRoomFor(s,l); |
614 | 0 | if (s == NULL) goto fmt_error; |
615 | 0 | } |
616 | 0 | memcpy(s+i,str,l); |
617 | 0 | sdsinclen(s,l); |
618 | 0 | i += l; |
619 | 0 | break; |
620 | 0 | case 'i': |
621 | 0 | case 'I': |
622 | 0 | if (next == 'i') |
623 | 0 | num = va_arg(ap,int); |
624 | 0 | else |
625 | 0 | num = va_arg(ap,long long); |
626 | 0 | { |
627 | 0 | char buf[SDS_LLSTR_SIZE]; |
628 | 0 | l = sdsll2str(buf,num); |
629 | 0 | if (sdsavail(s) < l) { |
630 | 0 | s = sdsMakeRoomFor(s,l); |
631 | 0 | if (s == NULL) goto fmt_error; |
632 | 0 | } |
633 | 0 | memcpy(s+i,buf,l); |
634 | 0 | sdsinclen(s,l); |
635 | 0 | i += l; |
636 | 0 | } |
637 | 0 | break; |
638 | 0 | case 'u': |
639 | 0 | case 'U': |
640 | 0 | if (next == 'u') |
641 | 0 | unum = va_arg(ap,unsigned int); |
642 | 0 | else |
643 | 0 | unum = va_arg(ap,unsigned long long); |
644 | 0 | { |
645 | 0 | char buf[SDS_LLSTR_SIZE]; |
646 | 0 | l = sdsull2str(buf,unum); |
647 | 0 | if (sdsavail(s) < l) { |
648 | 0 | s = sdsMakeRoomFor(s,l); |
649 | 0 | if (s == NULL) goto fmt_error; |
650 | 0 | } |
651 | 0 | memcpy(s+i,buf,l); |
652 | 0 | sdsinclen(s,l); |
653 | 0 | i += l; |
654 | 0 | } |
655 | 0 | break; |
656 | 0 | default: /* Handle %% and generally %<unknown>. */ |
657 | 0 | s[i++] = next; |
658 | 0 | sdsinclen(s,1); |
659 | 0 | break; |
660 | 0 | } |
661 | 0 | break; |
662 | 0 | default: |
663 | 0 | s[i++] = *f; |
664 | 0 | sdsinclen(s,1); |
665 | 0 | break; |
666 | 0 | } |
667 | 0 | f++; |
668 | 0 | } |
669 | 0 | va_end(ap); |
670 | | |
671 | | /* Add null-term */ |
672 | 0 | s[i] = '\0'; |
673 | 0 | return s; |
674 | | |
675 | 0 | fmt_error: |
676 | 0 | va_end(ap); |
677 | 0 | return NULL; |
678 | 0 | } |
679 | | |
680 | | /* Remove the part of the string from left and from right composed just of |
681 | | * contiguous characters found in 'cset', that is a null terminted C string. |
682 | | * |
683 | | * After the call, the modified sds string is no longer valid and all the |
684 | | * references must be substituted with the new pointer returned by the call. |
685 | | * |
686 | | * Example: |
687 | | * |
688 | | * s = sdsnew("AA...AA.a.aa.aHelloWorld :::"); |
689 | | * s = sdstrim(s,"Aa. :"); |
690 | | * printf("%s\n", s); |
691 | | * |
692 | | * Output will be just "Hello World". |
693 | | */ |
694 | 0 | sds sdstrim(sds s, const char *cset) { |
695 | 0 | char *start, *end, *sp, *ep; |
696 | 0 | size_t len; |
697 | |
|
698 | 0 | sp = start = s; |
699 | 0 | ep = end = s+sdslen(s)-1; |
700 | 0 | while(sp <= end && strchr(cset, *sp)) sp++; |
701 | 0 | while(ep > sp && strchr(cset, *ep)) ep--; |
702 | 0 | len = (sp > ep) ? 0 : ((ep-sp)+1); |
703 | 0 | if (s != sp) memmove(s, sp, len); |
704 | 0 | s[len] = '\0'; |
705 | 0 | sdssetlen(s,len); |
706 | 0 | return s; |
707 | 0 | } |
708 | | |
709 | | /* Turn the string into a smaller (or equal) string containing only the |
710 | | * substring specified by the 'start' and 'end' indexes. |
711 | | * |
712 | | * start and end can be negative, where -1 means the last character of the |
713 | | * string, -2 the penultimate character, and so forth. |
714 | | * |
715 | | * The interval is inclusive, so the start and end characters will be part |
716 | | * of the resulting string. |
717 | | * |
718 | | * The string is modified in-place. |
719 | | * |
720 | | * Return value: |
721 | | * -1 (error) if sdslen(s) is larger than maximum positive ssize_t value. |
722 | | * 0 on success. |
723 | | * |
724 | | * Example: |
725 | | * |
726 | | * s = sdsnew("Hello World"); |
727 | | * sdsrange(s,1,-1); => "ello World" |
728 | | */ |
729 | 0 | int sdsrange(sds s, ssize_t start, ssize_t end) { |
730 | 0 | size_t newlen, len = sdslen(s); |
731 | 0 | if (len > SSIZE_MAX) return -1; |
732 | | |
733 | 0 | if (len == 0) return 0; |
734 | 0 | if (start < 0) { |
735 | 0 | start = len+start; |
736 | 0 | if (start < 0) start = 0; |
737 | 0 | } |
738 | 0 | if (end < 0) { |
739 | 0 | end = len+end; |
740 | 0 | if (end < 0) end = 0; |
741 | 0 | } |
742 | 0 | newlen = (start > end) ? 0 : (end-start)+1; |
743 | 0 | if (newlen != 0) { |
744 | 0 | if (start >= (ssize_t)len) { |
745 | 0 | newlen = 0; |
746 | 0 | } else if (end >= (ssize_t)len) { |
747 | 0 | end = len-1; |
748 | 0 | newlen = (start > end) ? 0 : (end-start)+1; |
749 | 0 | } |
750 | 0 | } else { |
751 | 0 | start = 0; |
752 | 0 | } |
753 | 0 | if (start && newlen) memmove(s, s+start, newlen); |
754 | 0 | s[newlen] = 0; |
755 | 0 | sdssetlen(s,newlen); |
756 | 0 | return 0; |
757 | 0 | } |
758 | | |
759 | | /* Apply tolower() to every character of the sds string 's'. */ |
760 | 0 | void sdstolower(sds s) { |
761 | 0 | size_t len = sdslen(s), j; |
762 | |
|
763 | 0 | for (j = 0; j < len; j++) s[j] = tolower(s[j]); |
764 | 0 | } |
765 | | |
766 | | /* Apply toupper() to every character of the sds string 's'. */ |
767 | 0 | void sdstoupper(sds s) { |
768 | 0 | size_t len = sdslen(s), j; |
769 | |
|
770 | 0 | for (j = 0; j < len; j++) s[j] = toupper(s[j]); |
771 | 0 | } |
772 | | |
773 | | /* Compare two sds strings s1 and s2 with memcmp(). |
774 | | * |
775 | | * Return value: |
776 | | * |
777 | | * positive if s1 > s2. |
778 | | * negative if s1 < s2. |
779 | | * 0 if s1 and s2 are exactly the same binary string. |
780 | | * |
781 | | * If two strings share exactly the same prefix, but one of the two has |
782 | | * additional characters, the longer string is considered to be greater than |
783 | | * the smaller one. */ |
784 | 0 | int sdscmp(const sds s1, const sds s2) { |
785 | 0 | size_t l1, l2, minlen; |
786 | 0 | int cmp; |
787 | |
|
788 | 0 | l1 = sdslen(s1); |
789 | 0 | l2 = sdslen(s2); |
790 | 0 | minlen = (l1 < l2) ? l1 : l2; |
791 | 0 | cmp = memcmp(s1,s2,minlen); |
792 | 0 | if (cmp == 0) return l1-l2; |
793 | 0 | return cmp; |
794 | 0 | } |
795 | | |
796 | | /* Split 's' with separator in 'sep'. An array |
797 | | * of sds strings is returned. *count will be set |
798 | | * by reference to the number of tokens returned. |
799 | | * |
800 | | * On out of memory, zero length string, zero length |
801 | | * separator, NULL is returned. |
802 | | * |
803 | | * Note that 'sep' is able to split a string using |
804 | | * a multi-character separator. For example |
805 | | * sdssplit("foo_-_bar","_-_"); will return two |
806 | | * elements "foo" and "bar". |
807 | | * |
808 | | * This version of the function is binary-safe but |
809 | | * requires length arguments. sdssplit() is just the |
810 | | * same function but for zero-terminated strings. |
811 | | */ |
812 | 0 | sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) { |
813 | 0 | int elements = 0, slots = 5, start = 0, j; |
814 | 0 | sds *tokens; |
815 | |
|
816 | 0 | if (seplen < 1 || len < 0) return NULL; |
817 | | |
818 | 0 | tokens = s_malloc(sizeof(sds)*slots); |
819 | 0 | if (tokens == NULL) return NULL; |
820 | | |
821 | 0 | if (len == 0) { |
822 | 0 | *count = 0; |
823 | 0 | return tokens; |
824 | 0 | } |
825 | 0 | for (j = 0; j < (len-(seplen-1)); j++) { |
826 | | /* make sure there is room for the next element and the final one */ |
827 | 0 | if (slots < elements+2) { |
828 | 0 | sds *newtokens; |
829 | |
|
830 | 0 | slots *= 2; |
831 | 0 | newtokens = s_realloc(tokens,sizeof(sds)*slots); |
832 | 0 | if (newtokens == NULL) goto cleanup; |
833 | 0 | tokens = newtokens; |
834 | 0 | } |
835 | | /* search the separator */ |
836 | 0 | if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) { |
837 | 0 | tokens[elements] = sdsnewlen(s+start,j-start); |
838 | 0 | if (tokens[elements] == NULL) goto cleanup; |
839 | 0 | elements++; |
840 | 0 | start = j+seplen; |
841 | 0 | j = j+seplen-1; /* skip the separator */ |
842 | 0 | } |
843 | 0 | } |
844 | | /* Add the final element. We are sure there is room in the tokens array. */ |
845 | 0 | tokens[elements] = sdsnewlen(s+start,len-start); |
846 | 0 | if (tokens[elements] == NULL) goto cleanup; |
847 | 0 | elements++; |
848 | 0 | *count = elements; |
849 | 0 | return tokens; |
850 | | |
851 | 0 | cleanup: |
852 | 0 | { |
853 | 0 | int i; |
854 | 0 | for (i = 0; i < elements; i++) sdsfree(tokens[i]); |
855 | 0 | s_free(tokens); |
856 | 0 | *count = 0; |
857 | 0 | return NULL; |
858 | 0 | } |
859 | 0 | } |
860 | | |
861 | | /* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */ |
862 | 0 | void sdsfreesplitres(sds *tokens, int count) { |
863 | 0 | if (!tokens) return; |
864 | 0 | while(count--) |
865 | 0 | sdsfree(tokens[count]); |
866 | 0 | s_free(tokens); |
867 | 0 | } |
868 | | |
869 | | /* Append to the sds string "s" an escaped string representation where |
870 | | * all the non-printable characters (tested with isprint()) are turned into |
871 | | * escapes in the form "\n\r\a...." or "\x<hex-number>". |
872 | | * |
873 | | * After the call, the modified sds string is no longer valid and all the |
874 | | * references must be substituted with the new pointer returned by the call. */ |
875 | 0 | sds sdscatrepr(sds s, const char *p, size_t len) { |
876 | 0 | s = sdscatlen(s,"\"",1); |
877 | 0 | while(len--) { |
878 | 0 | switch(*p) { |
879 | 0 | case '\\': |
880 | 0 | case '"': |
881 | 0 | s = sdscatprintf(s,"\\%c",*p); |
882 | 0 | break; |
883 | 0 | case '\n': s = sdscatlen(s,"\\n",2); break; |
884 | 0 | case '\r': s = sdscatlen(s,"\\r",2); break; |
885 | 0 | case '\t': s = sdscatlen(s,"\\t",2); break; |
886 | 0 | case '\a': s = sdscatlen(s,"\\a",2); break; |
887 | 0 | case '\b': s = sdscatlen(s,"\\b",2); break; |
888 | 0 | default: |
889 | 0 | if (isprint((int) *p)) |
890 | 0 | s = sdscatprintf(s,"%c",*p); |
891 | 0 | else |
892 | 0 | s = sdscatprintf(s,"\\x%02x",(unsigned char)*p); |
893 | 0 | break; |
894 | 0 | } |
895 | 0 | p++; |
896 | 0 | } |
897 | 0 | return sdscatlen(s,"\"",1); |
898 | 0 | } |
899 | | |
900 | | /* Helper function for sdssplitargs() that converts a hex digit into an |
901 | | * integer from 0 to 15 */ |
902 | 0 | int hex_digit_to_int(char c) { |
903 | 0 | switch(c) { |
904 | 0 | case '0': return 0; |
905 | 0 | case '1': return 1; |
906 | 0 | case '2': return 2; |
907 | 0 | case '3': return 3; |
908 | 0 | case '4': return 4; |
909 | 0 | case '5': return 5; |
910 | 0 | case '6': return 6; |
911 | 0 | case '7': return 7; |
912 | 0 | case '8': return 8; |
913 | 0 | case '9': return 9; |
914 | 0 | case 'a': case 'A': return 10; |
915 | 0 | case 'b': case 'B': return 11; |
916 | 0 | case 'c': case 'C': return 12; |
917 | 0 | case 'd': case 'D': return 13; |
918 | 0 | case 'e': case 'E': return 14; |
919 | 0 | case 'f': case 'F': return 15; |
920 | 0 | default: return 0; |
921 | 0 | } |
922 | 0 | } |
923 | | |
924 | | /* Split a line into arguments, where every argument can be in the |
925 | | * following programming-language REPL-alike form: |
926 | | * |
927 | | * foo bar "newline are supported\n" and "\xff\x00otherstuff" |
928 | | * |
929 | | * The number of arguments is stored into *argc, and an array |
930 | | * of sds is returned. |
931 | | * |
932 | | * The caller should free the resulting array of sds strings with |
933 | | * sdsfreesplitres(). |
934 | | * |
935 | | * Note that sdscatrepr() is able to convert back a string into |
936 | | * a quoted string in the same format sdssplitargs() is able to parse. |
937 | | * |
938 | | * The function returns the allocated tokens on success, even when the |
939 | | * input string is empty, or NULL if the input contains unbalanced |
940 | | * quotes or closed quotes followed by non space characters |
941 | | * as in: "foo"bar or "foo' |
942 | | */ |
943 | 0 | sds *sdssplitargs(const char *line, int *argc) { |
944 | 0 | const char *p = line; |
945 | 0 | char *current = NULL; |
946 | 0 | char **vector = NULL; |
947 | |
|
948 | 0 | *argc = 0; |
949 | 0 | while(1) { |
950 | | /* skip blanks */ |
951 | 0 | while(*p && isspace((int) *p)) p++; |
952 | 0 | if (*p) { |
953 | | /* get a token */ |
954 | 0 | int inq=0; /* set to 1 if we are in "quotes" */ |
955 | 0 | int insq=0; /* set to 1 if we are in 'single quotes' */ |
956 | 0 | int done=0; |
957 | |
|
958 | 0 | if (current == NULL) current = sdsempty(); |
959 | 0 | while(!done) { |
960 | 0 | if (inq) { |
961 | 0 | if (*p == '\\' && *(p+1) == 'x' && |
962 | 0 | isxdigit((int) *(p+2)) && |
963 | 0 | isxdigit((int) *(p+3))) |
964 | 0 | { |
965 | 0 | unsigned char byte; |
966 | |
|
967 | 0 | byte = (hex_digit_to_int(*(p+2))*16)+ |
968 | 0 | hex_digit_to_int(*(p+3)); |
969 | 0 | current = sdscatlen(current,(char*)&byte,1); |
970 | 0 | p += 3; |
971 | 0 | } else if (*p == '\\' && *(p+1)) { |
972 | 0 | char c; |
973 | |
|
974 | 0 | p++; |
975 | 0 | switch(*p) { |
976 | 0 | case 'n': c = '\n'; break; |
977 | 0 | case 'r': c = '\r'; break; |
978 | 0 | case 't': c = '\t'; break; |
979 | 0 | case 'b': c = '\b'; break; |
980 | 0 | case 'a': c = '\a'; break; |
981 | 0 | default: c = *p; break; |
982 | 0 | } |
983 | 0 | current = sdscatlen(current,&c,1); |
984 | 0 | } else if (*p == '"') { |
985 | | /* closing quote must be followed by a space or |
986 | | * nothing at all. */ |
987 | 0 | if (*(p+1) && !isspace((int) *(p+1))) goto err; |
988 | 0 | done=1; |
989 | 0 | } else if (!*p) { |
990 | | /* unterminated quotes */ |
991 | 0 | goto err; |
992 | 0 | } else { |
993 | 0 | current = sdscatlen(current,p,1); |
994 | 0 | } |
995 | 0 | } else if (insq) { |
996 | 0 | if (*p == '\\' && *(p+1) == '\'') { |
997 | 0 | p++; |
998 | 0 | current = sdscatlen(current,"'",1); |
999 | 0 | } else if (*p == '\'') { |
1000 | | /* closing quote must be followed by a space or |
1001 | | * nothing at all. */ |
1002 | 0 | if (*(p+1) && !isspace((int) *(p+1))) goto err; |
1003 | 0 | done=1; |
1004 | 0 | } else if (!*p) { |
1005 | | /* unterminated quotes */ |
1006 | 0 | goto err; |
1007 | 0 | } else { |
1008 | 0 | current = sdscatlen(current,p,1); |
1009 | 0 | } |
1010 | 0 | } else { |
1011 | 0 | switch(*p) { |
1012 | 0 | case ' ': |
1013 | 0 | case '\n': |
1014 | 0 | case '\r': |
1015 | 0 | case '\t': |
1016 | 0 | case '\0': |
1017 | 0 | done=1; |
1018 | 0 | break; |
1019 | 0 | case '"': |
1020 | 0 | inq=1; |
1021 | 0 | break; |
1022 | 0 | case '\'': |
1023 | 0 | insq=1; |
1024 | 0 | break; |
1025 | 0 | default: |
1026 | 0 | current = sdscatlen(current,p,1); |
1027 | 0 | break; |
1028 | 0 | } |
1029 | 0 | } |
1030 | 0 | if (*p) p++; |
1031 | 0 | } |
1032 | | /* add the token to the vector */ |
1033 | 0 | { |
1034 | 0 | char **new_vector = s_realloc(vector,((*argc)+1)*sizeof(char*)); |
1035 | 0 | if (new_vector == NULL) { |
1036 | 0 | s_free(vector); |
1037 | 0 | return NULL; |
1038 | 0 | } |
1039 | | |
1040 | 0 | vector = new_vector; |
1041 | 0 | vector[*argc] = current; |
1042 | 0 | (*argc)++; |
1043 | 0 | current = NULL; |
1044 | 0 | } |
1045 | 0 | } else { |
1046 | | /* Even on empty input string return something not NULL. */ |
1047 | 0 | if (vector == NULL) vector = s_malloc(sizeof(void*)); |
1048 | 0 | return vector; |
1049 | 0 | } |
1050 | 0 | } |
1051 | | |
1052 | 0 | err: |
1053 | 0 | while((*argc)--) |
1054 | 0 | sdsfree(vector[*argc]); |
1055 | 0 | s_free(vector); |
1056 | 0 | if (current) sdsfree(current); |
1057 | 0 | *argc = 0; |
1058 | 0 | return NULL; |
1059 | 0 | } |
1060 | | |
1061 | | /* Modify the string substituting all the occurrences of the set of |
1062 | | * characters specified in the 'from' string to the corresponding character |
1063 | | * in the 'to' array. |
1064 | | * |
1065 | | * For instance: sdsmapchars(mystring, "ho", "01", 2) |
1066 | | * will have the effect of turning the string "hello" into "0ell1". |
1067 | | * |
1068 | | * The function returns the sds string pointer, that is always the same |
1069 | | * as the input pointer since no resize is needed. */ |
1070 | 0 | sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) { |
1071 | 0 | size_t j, i, l = sdslen(s); |
1072 | |
|
1073 | 0 | for (j = 0; j < l; j++) { |
1074 | 0 | for (i = 0; i < setlen; i++) { |
1075 | 0 | if (s[j] == from[i]) { |
1076 | 0 | s[j] = to[i]; |
1077 | 0 | break; |
1078 | 0 | } |
1079 | 0 | } |
1080 | 0 | } |
1081 | 0 | return s; |
1082 | 0 | } |
1083 | | |
1084 | | /* Join an array of C strings using the specified separator (also a C string). |
1085 | | * Returns the result as an sds string. */ |
1086 | 0 | sds sdsjoin(char **argv, int argc, char *sep) { |
1087 | 0 | sds join = sdsempty(); |
1088 | 0 | int j; |
1089 | |
|
1090 | 0 | for (j = 0; j < argc; j++) { |
1091 | 0 | join = sdscat(join, argv[j]); |
1092 | 0 | if (j != argc-1) join = sdscat(join,sep); |
1093 | 0 | } |
1094 | 0 | return join; |
1095 | 0 | } |
1096 | | |
1097 | | /* Like sdsjoin, but joins an array of SDS strings. */ |
1098 | 0 | sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) { |
1099 | 0 | sds join = sdsempty(); |
1100 | 0 | int j; |
1101 | |
|
1102 | 0 | for (j = 0; j < argc; j++) { |
1103 | 0 | join = sdscatsds(join, argv[j]); |
1104 | 0 | if (j != argc-1) join = sdscatlen(join,sep,seplen); |
1105 | 0 | } |
1106 | 0 | return join; |
1107 | 0 | } |
1108 | | |
1109 | | /* Wrappers to the allocators used by SDS. Note that SDS will actually |
1110 | | * just use the macros defined into sdsalloc.h in order to avoid to pay |
1111 | | * the overhead of function calls. Here we define these wrappers only for |
1112 | | * the programs SDS is linked to, if they want to touch the SDS internals |
1113 | | * even if they use a different allocator. */ |
1114 | 0 | void *sds_malloc(size_t size) { return s_malloc(size); } |
1115 | 0 | void *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); } |
1116 | 0 | void sds_free(void *ptr) { s_free(ptr); } |
1117 | | |
1118 | | #if defined(SDS_TEST_MAIN) |
1119 | | #include <stdio.h> |
1120 | | #include "testhelp.h" |
1121 | | #include "limits.h" |
1122 | | |
1123 | | #define UNUSED(x) (void)(x) |
1124 | | int sdsTest(void) { |
1125 | | { |
1126 | | sds x = sdsnew("foo"), y; |
1127 | | |
1128 | | test_cond("Create a string and obtain the length", |
1129 | | sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0) |
1130 | | |
1131 | | sdsfree(x); |
1132 | | x = sdsnewlen("foo",2); |
1133 | | test_cond("Create a string with specified length", |
1134 | | sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0) |
1135 | | |
1136 | | x = sdscat(x,"bar"); |
1137 | | test_cond("Strings concatenation", |
1138 | | sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0); |
1139 | | |
1140 | | x = sdscpy(x,"a"); |
1141 | | test_cond("sdscpy() against an originally longer string", |
1142 | | sdslen(x) == 1 && memcmp(x,"a\0",2) == 0) |
1143 | | |
1144 | | x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); |
1145 | | test_cond("sdscpy() against an originally shorter string", |
1146 | | sdslen(x) == 33 && |
1147 | | memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0) |
1148 | | |
1149 | | sdsfree(x); |
1150 | | x = sdscatprintf(sdsempty(),"%d",123); |
1151 | | test_cond("sdscatprintf() seems working in the base case", |
1152 | | sdslen(x) == 3 && memcmp(x,"123\0",4) == 0) |
1153 | | |
1154 | | sdsfree(x); |
1155 | | x = sdsnew("--"); |
1156 | | x = sdscatfmt(x, "Hello %s World %I,%I--", "Hi!", LLONG_MIN,LLONG_MAX); |
1157 | | test_cond("sdscatfmt() seems working in the base case", |
1158 | | sdslen(x) == 60 && |
1159 | | memcmp(x,"--Hello Hi! World -9223372036854775808," |
1160 | | "9223372036854775807--",60) == 0) |
1161 | | printf("[%s]\n",x); |
1162 | | |
1163 | | sdsfree(x); |
1164 | | x = sdsnew("--"); |
1165 | | x = sdscatfmt(x, "%u,%U--", UINT_MAX, ULLONG_MAX); |
1166 | | test_cond("sdscatfmt() seems working with unsigned numbers", |
1167 | | sdslen(x) == 35 && |
1168 | | memcmp(x,"--4294967295,18446744073709551615--",35) == 0) |
1169 | | |
1170 | | sdsfree(x); |
1171 | | x = sdsnew(" x "); |
1172 | | sdstrim(x," x"); |
1173 | | test_cond("sdstrim() works when all chars match", |
1174 | | sdslen(x) == 0) |
1175 | | |
1176 | | sdsfree(x); |
1177 | | x = sdsnew(" x "); |
1178 | | sdstrim(x," "); |
1179 | | test_cond("sdstrim() works when a single char remains", |
1180 | | sdslen(x) == 1 && x[0] == 'x') |
1181 | | |
1182 | | sdsfree(x); |
1183 | | x = sdsnew("xxciaoyyy"); |
1184 | | sdstrim(x,"xy"); |
1185 | | test_cond("sdstrim() correctly trims characters", |
1186 | | sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0) |
1187 | | |
1188 | | y = sdsdup(x); |
1189 | | sdsrange(y,1,1); |
1190 | | test_cond("sdsrange(...,1,1)", |
1191 | | sdslen(y) == 1 && memcmp(y,"i\0",2) == 0) |
1192 | | |
1193 | | sdsfree(y); |
1194 | | y = sdsdup(x); |
1195 | | sdsrange(y,1,-1); |
1196 | | test_cond("sdsrange(...,1,-1)", |
1197 | | sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) |
1198 | | |
1199 | | sdsfree(y); |
1200 | | y = sdsdup(x); |
1201 | | sdsrange(y,-2,-1); |
1202 | | test_cond("sdsrange(...,-2,-1)", |
1203 | | sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0) |
1204 | | |
1205 | | sdsfree(y); |
1206 | | y = sdsdup(x); |
1207 | | sdsrange(y,2,1); |
1208 | | test_cond("sdsrange(...,2,1)", |
1209 | | sdslen(y) == 0 && memcmp(y,"\0",1) == 0) |
1210 | | |
1211 | | sdsfree(y); |
1212 | | y = sdsdup(x); |
1213 | | sdsrange(y,1,100); |
1214 | | test_cond("sdsrange(...,1,100)", |
1215 | | sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) |
1216 | | |
1217 | | sdsfree(y); |
1218 | | y = sdsdup(x); |
1219 | | sdsrange(y,100,100); |
1220 | | test_cond("sdsrange(...,100,100)", |
1221 | | sdslen(y) == 0 && memcmp(y,"\0",1) == 0) |
1222 | | |
1223 | | sdsfree(y); |
1224 | | sdsfree(x); |
1225 | | x = sdsnew("foo"); |
1226 | | y = sdsnew("foa"); |
1227 | | test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0) |
1228 | | |
1229 | | sdsfree(y); |
1230 | | sdsfree(x); |
1231 | | x = sdsnew("bar"); |
1232 | | y = sdsnew("bar"); |
1233 | | test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0) |
1234 | | |
1235 | | sdsfree(y); |
1236 | | sdsfree(x); |
1237 | | x = sdsnew("aar"); |
1238 | | y = sdsnew("bar"); |
1239 | | test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0) |
1240 | | |
1241 | | sdsfree(y); |
1242 | | sdsfree(x); |
1243 | | x = sdsnewlen("\a\n\0foo\r",7); |
1244 | | y = sdscatrepr(sdsempty(),x,sdslen(x)); |
1245 | | test_cond("sdscatrepr(...data...)", |
1246 | | memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0) |
1247 | | |
1248 | | { |
1249 | | unsigned int oldfree; |
1250 | | char *p; |
1251 | | int step = 10, j, i; |
1252 | | |
1253 | | sdsfree(x); |
1254 | | sdsfree(y); |
1255 | | x = sdsnew("0"); |
1256 | | test_cond("sdsnew() free/len buffers", sdslen(x) == 1 && sdsavail(x) == 0); |
1257 | | |
1258 | | /* Run the test a few times in order to hit the first two |
1259 | | * SDS header types. */ |
1260 | | for (i = 0; i < 10; i++) { |
1261 | | int oldlen = sdslen(x); |
1262 | | x = sdsMakeRoomFor(x,step); |
1263 | | int type = x[-1]&SDS_TYPE_MASK; |
1264 | | |
1265 | | test_cond("sdsMakeRoomFor() len", sdslen(x) == oldlen); |
1266 | | if (type != SDS_TYPE_5) { |
1267 | | test_cond("sdsMakeRoomFor() free", sdsavail(x) >= step); |
1268 | | oldfree = sdsavail(x); |
1269 | | } |
1270 | | p = x+oldlen; |
1271 | | for (j = 0; j < step; j++) { |
1272 | | p[j] = 'A'+j; |
1273 | | } |
1274 | | sdsIncrLen(x,step); |
1275 | | } |
1276 | | test_cond("sdsMakeRoomFor() content", |
1277 | | memcmp("0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ",x,101) == 0); |
1278 | | test_cond("sdsMakeRoomFor() final length",sdslen(x)==101); |
1279 | | |
1280 | | sdsfree(x); |
1281 | | } |
1282 | | } |
1283 | | test_report() |
1284 | | return 0; |
1285 | | } |
1286 | | #endif |
1287 | | |
1288 | | #ifdef SDS_TEST_MAIN |
1289 | | int main(void) { |
1290 | | return sdsTest(); |
1291 | | } |
1292 | | #endif |