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