diff options
Diffstat (limited to '')
-rw-r--r-- | libraries/eina/src/lib/eina_strbuf_common.c | 874 |
1 files changed, 874 insertions, 0 deletions
diff --git a/libraries/eina/src/lib/eina_strbuf_common.c b/libraries/eina/src/lib/eina_strbuf_common.c new file mode 100644 index 0000000..46067cd --- /dev/null +++ b/libraries/eina/src/lib/eina_strbuf_common.c | |||
@@ -0,0 +1,874 @@ | |||
1 | #ifdef HAVE_CONFIG_H | ||
2 | # include "config.h" | ||
3 | #endif | ||
4 | |||
5 | #include <stdio.h> | ||
6 | #include <stdlib.h> | ||
7 | #include <string.h> | ||
8 | |||
9 | #ifdef _WIN32 | ||
10 | # include <Evil.h> | ||
11 | #endif | ||
12 | |||
13 | #include "eina_private.h" | ||
14 | #include "eina_str.h" | ||
15 | #include "eina_magic.h" | ||
16 | #include "eina_error.h" | ||
17 | #include "eina_safety_checks.h" | ||
18 | #include "eina_strbuf.h" | ||
19 | #include "eina_strbuf_common.h" | ||
20 | |||
21 | /*============================================================================* | ||
22 | * Local * | ||
23 | *============================================================================*/ | ||
24 | |||
25 | /** | ||
26 | * @cond LOCAL | ||
27 | */ | ||
28 | |||
29 | #define EINA_STRBUF_INIT_SIZE 32 | ||
30 | #define EINA_STRBUF_INIT_STEP 32 | ||
31 | #define EINA_STRBUF_MAX_STEP 4096 | ||
32 | |||
33 | /** | ||
34 | * @endcond | ||
35 | */ | ||
36 | |||
37 | /*============================================================================* | ||
38 | * Global * | ||
39 | *============================================================================*/ | ||
40 | |||
41 | /** | ||
42 | * @internal | ||
43 | * @brief Initialize the strbuf module. | ||
44 | * | ||
45 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||
46 | * | ||
47 | * This function sets up the strbuf module of Eina. It is called by | ||
48 | * eina_init(). | ||
49 | * | ||
50 | * @see eina_init() | ||
51 | */ | ||
52 | Eina_Bool | ||
53 | eina_strbuf_common_init(void) | ||
54 | { | ||
55 | return EINA_TRUE; | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * @internal | ||
60 | * @brief Shut down the strbuf module. | ||
61 | * | ||
62 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||
63 | * | ||
64 | * This function shuts down the strbuf module set up by | ||
65 | * eina_strbuf_common_init(). It is called by eina_shutdown(). | ||
66 | * | ||
67 | * @see eina_shutdown() | ||
68 | */ | ||
69 | Eina_Bool | ||
70 | eina_strbuf_common_shutdown(void) | ||
71 | { | ||
72 | return EINA_TRUE; | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * @internal | ||
77 | * | ||
78 | * init the buffer | ||
79 | * @param csize the character size | ||
80 | * @param buf the buffer to init | ||
81 | * | ||
82 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||
83 | */ | ||
84 | static Eina_Bool | ||
85 | _eina_strbuf_common_init(size_t csize, Eina_Strbuf *buf) | ||
86 | { | ||
87 | buf->len = 0; | ||
88 | buf->size = EINA_STRBUF_INIT_SIZE; | ||
89 | buf->step = EINA_STRBUF_INIT_STEP; | ||
90 | |||
91 | eina_error_set(0); | ||
92 | buf->buf = calloc(csize, buf->size); | ||
93 | if (EINA_UNLIKELY(!buf->buf)) | ||
94 | { | ||
95 | eina_error_set(EINA_ERROR_OUT_OF_MEMORY); | ||
96 | return EINA_FALSE; | ||
97 | } | ||
98 | |||
99 | return EINA_TRUE; | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * @internal | ||
104 | * | ||
105 | * init the buffer without allocating the actual string (for managed) | ||
106 | * @param csize the character size | ||
107 | * @param buf the buffer to init | ||
108 | * | ||
109 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||
110 | */ | ||
111 | static Eina_Bool | ||
112 | _eina_strbuf_common_manage_init(size_t csize __UNUSED__, | ||
113 | Eina_Strbuf *buf, | ||
114 | void *str, | ||
115 | size_t len) | ||
116 | { | ||
117 | buf->len = len; | ||
118 | buf->size = len + 1; | ||
119 | buf->step = EINA_STRBUF_INIT_STEP; | ||
120 | buf->buf = str; | ||
121 | |||
122 | return EINA_TRUE; | ||
123 | } | ||
124 | |||
125 | |||
126 | /** | ||
127 | * @internal | ||
128 | * | ||
129 | * resize the buffer | ||
130 | * @param csize the character size | ||
131 | * @param buf the buffer to resize | ||
132 | * @param size the minimum size of the buffer | ||
133 | * | ||
134 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||
135 | */ | ||
136 | static inline Eina_Bool | ||
137 | _eina_strbuf_common_resize(size_t csize, Eina_Strbuf *buf, size_t size) | ||
138 | { | ||
139 | size_t new_size, new_step, delta; | ||
140 | void *buffer; | ||
141 | |||
142 | size += 1; // Add extra space for '\0' | ||
143 | |||
144 | /* nothing to do */ | ||
145 | if (size == buf->size) return EINA_TRUE; | ||
146 | else if (size > buf->size) delta = size - buf->size; | ||
147 | else delta = buf->size - size; | ||
148 | |||
149 | /* check if should keep the same step (just used while growing) */ | ||
150 | if ((delta <= buf->step) && (size > buf->size)) new_step = buf->step; | ||
151 | else | ||
152 | { | ||
153 | new_step = (((delta / EINA_STRBUF_INIT_STEP) + 1) | ||
154 | * EINA_STRBUF_INIT_STEP); | ||
155 | if (new_step > EINA_STRBUF_MAX_STEP) new_step = EINA_STRBUF_MAX_STEP; | ||
156 | } | ||
157 | |||
158 | new_size = (((size / new_step) + 1) * new_step); | ||
159 | |||
160 | /* reallocate the buffer to the new size */ | ||
161 | buffer = realloc(buf->buf, new_size * csize); | ||
162 | if (EINA_UNLIKELY(!buffer)) | ||
163 | { | ||
164 | eina_error_set(EINA_ERROR_OUT_OF_MEMORY); | ||
165 | return EINA_FALSE; | ||
166 | } | ||
167 | |||
168 | buf->buf = buffer; | ||
169 | buf->size = new_size; | ||
170 | buf->step = new_step; | ||
171 | eina_error_set(0); | ||
172 | return EINA_TRUE; | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * @internal | ||
177 | * | ||
178 | * If required, enlarge the buffer to fit the new size. | ||
179 | * | ||
180 | * @param csize the character size | ||
181 | * @param buf the buffer to resize | ||
182 | * @param size the minimum size of the buffer | ||
183 | * | ||
184 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||
185 | */ | ||
186 | Eina_Bool | ||
187 | _eina_strbuf_common_grow(size_t csize, Eina_Strbuf *buf, size_t size) | ||
188 | { | ||
189 | if ((size + 1) < buf->size) return EINA_TRUE; | ||
190 | return _eina_strbuf_common_resize(csize, buf, size); | ||
191 | } | ||
192 | |||
193 | /** | ||
194 | * @internal | ||
195 | * | ||
196 | * insert string of known length at random within existing strbuf limits. | ||
197 | * | ||
198 | * @param csize the character size | ||
199 | * @param buf the buffer to resize, must be valid. | ||
200 | * @param str the string to copy, must be valid (!NULL and smaller than @a len) | ||
201 | * @param len the amount of bytes in @a str to copy, must be valid. | ||
202 | * @param pos the position inside buffer to insert, must be valid (smaller | ||
203 | * than eina_strbuf_common_length_get()) | ||
204 | * | ||
205 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||
206 | */ | ||
207 | static inline Eina_Bool | ||
208 | _eina_strbuf_common_insert_length(size_t csize, | ||
209 | Eina_Strbuf *buf, | ||
210 | const void *str, | ||
211 | size_t len, | ||
212 | size_t pos) | ||
213 | { | ||
214 | if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + len))) | ||
215 | return EINA_FALSE; | ||
216 | |||
217 | /* move the existing text */ | ||
218 | memmove(((unsigned char *)(buf->buf)) + ((len + pos) * csize), | ||
219 | ((unsigned char *)(buf->buf)) + (pos * csize), | ||
220 | (buf->len - pos) * csize); | ||
221 | |||
222 | /* and now insert the given string */ | ||
223 | memcpy((unsigned char *)buf->buf + (pos * csize), str, len * csize); | ||
224 | |||
225 | buf->len += len; | ||
226 | memset(((unsigned char *)(buf->buf)) + (buf->len * csize), 0, csize); | ||
227 | return EINA_TRUE; | ||
228 | } | ||
229 | |||
230 | /*============================================================================* | ||
231 | * API * | ||
232 | *============================================================================*/ | ||
233 | |||
234 | /** | ||
235 | * @internal | ||
236 | * @brief Create a new string buffer. | ||
237 | * | ||
238 | * @param csize the character size | ||
239 | * @return Newly allocated string buffer instance. | ||
240 | * | ||
241 | * This function creates a new string buffer. On error, @c NULL is | ||
242 | * returned and Eina error is set to #EINA_ERROR_OUT_OF_MEMORY. To | ||
243 | * free the resources, use eina_strbuf_common_free(). | ||
244 | * | ||
245 | * @see eina_strbuf_common_free() | ||
246 | * @see eina_strbuf_common_append() | ||
247 | * @see eina_strbuf_common_string_get() | ||
248 | */ | ||
249 | Eina_Strbuf * | ||
250 | eina_strbuf_common_new(size_t csize) | ||
251 | { | ||
252 | Eina_Strbuf *buf; | ||
253 | |||
254 | eina_error_set(0); | ||
255 | buf = malloc(sizeof(Eina_Strbuf)); | ||
256 | if (EINA_UNLIKELY(!buf)) | ||
257 | { | ||
258 | eina_error_set(EINA_ERROR_OUT_OF_MEMORY); | ||
259 | return NULL; | ||
260 | } | ||
261 | if (EINA_UNLIKELY(!_eina_strbuf_common_init(csize, buf))) | ||
262 | { | ||
263 | eina_strbuf_common_free(buf); | ||
264 | return NULL; | ||
265 | } | ||
266 | return buf; | ||
267 | } | ||
268 | |||
269 | /** | ||
270 | * @internal | ||
271 | * @brief Create a new string buffer managing str. | ||
272 | * | ||
273 | * @param csize the character size | ||
274 | * @param str the string to manage | ||
275 | * @param len the length of the string to manage | ||
276 | * @return Newly allocated string buffer instance. | ||
277 | * | ||
278 | * This function creates a new string buffer. On error, @c NULL is | ||
279 | * returned and Eina error is set to #EINA_ERROR_OUT_OF_MEMORY. To | ||
280 | * free the resources, use eina_strbuf_common_free(). | ||
281 | * | ||
282 | * @see eina_strbuf_common_free() | ||
283 | * @see eina_strbuf_common_append() | ||
284 | * @see eina_strbuf_common_string_get() | ||
285 | * @since 1.1.0 | ||
286 | */ | ||
287 | Eina_Strbuf * | ||
288 | eina_strbuf_common_manage_new(size_t csize, | ||
289 | void *str, | ||
290 | size_t len) | ||
291 | { | ||
292 | Eina_Strbuf *buf; | ||
293 | |||
294 | eina_error_set(0); | ||
295 | buf = malloc(sizeof(Eina_Strbuf)); | ||
296 | if (EINA_UNLIKELY(!buf)) | ||
297 | { | ||
298 | eina_error_set(EINA_ERROR_OUT_OF_MEMORY); | ||
299 | return NULL; | ||
300 | } | ||
301 | if (EINA_UNLIKELY(!_eina_strbuf_common_manage_init(csize, buf, str, len))) | ||
302 | { | ||
303 | eina_strbuf_common_free(buf); | ||
304 | return NULL; | ||
305 | } | ||
306 | return buf; | ||
307 | } | ||
308 | |||
309 | /** | ||
310 | * @internal | ||
311 | * @brief Free a string buffer. | ||
312 | * | ||
313 | * @param buf The string buffer to free. | ||
314 | * | ||
315 | * This function frees the memory of @p buf. @p buf must have been | ||
316 | * created by eina_strbuf_common_new(). | ||
317 | */ | ||
318 | void | ||
319 | eina_strbuf_common_free(Eina_Strbuf *buf) | ||
320 | { | ||
321 | free(buf->buf); | ||
322 | free(buf); | ||
323 | } | ||
324 | |||
325 | /** | ||
326 | * @internal | ||
327 | * @brief Reset a string buffer. | ||
328 | * | ||
329 | * @param csize the character size | ||
330 | * @param buf The string buffer to reset. | ||
331 | * | ||
332 | * This function reset @p buf: the buffer len is set to 0, and the | ||
333 | * string is set to '\\0'. No memory is free'd. | ||
334 | */ | ||
335 | void | ||
336 | eina_strbuf_common_reset(size_t csize, Eina_Strbuf *buf) | ||
337 | { | ||
338 | buf->len = 0; | ||
339 | buf->step = EINA_STRBUF_INIT_STEP; | ||
340 | memset(buf->buf, 0, csize); | ||
341 | } | ||
342 | |||
343 | /** | ||
344 | * @internal | ||
345 | * @brief Append a string to a buffer, reallocating as necessary. | ||
346 | * | ||
347 | * @param csize the character size | ||
348 | * @param buf The string buffer to append to. | ||
349 | * @param str The string to append. | ||
350 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||
351 | * | ||
352 | * This function appends @p str to @p buf. It computes the length of | ||
353 | * @p str, so is slightly slower than eina_strbuf_common_append_length(). If | ||
354 | * the length is known beforehand, consider using that variant. If | ||
355 | * @p buf can't append it, #EINA_FALSE is returned, otherwise | ||
356 | * #EINA_TRUE is returned. | ||
357 | * | ||
358 | * @see eina_strbuf_common_append() | ||
359 | * @see eina_strbuf_common_append_length() | ||
360 | */ | ||
361 | Eina_Bool | ||
362 | eina_strbuf_common_append(size_t csize, | ||
363 | Eina_Strbuf *buf, | ||
364 | const void *str, | ||
365 | size_t len) | ||
366 | { | ||
367 | EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE); | ||
368 | |||
369 | if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + len))) | ||
370 | return EINA_FALSE; | ||
371 | memcpy(((unsigned char *)(buf->buf)) + (buf->len * csize), str, | ||
372 | (len + 1) * csize); | ||
373 | buf->len += len; | ||
374 | return EINA_TRUE; | ||
375 | } | ||
376 | |||
377 | /** | ||
378 | * @internal | ||
379 | * @brief Append a string to a buffer, reallocating as necessary, | ||
380 | * limited by the given length. | ||
381 | * | ||
382 | * @param csize the character size | ||
383 | * @param buf The string buffer to append to. | ||
384 | * @param str The string to append. | ||
385 | * @param maxlen The maximum number of characters to append. | ||
386 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||
387 | * | ||
388 | * This function appends at most @p maxlen characters of @p str to | ||
389 | * @p buf. It can't appends more than the length of @p str. It | ||
390 | * computes the length of @p str, so is slightly slower than | ||
391 | * eina_strbuf_common_append_length(). If the length is known beforehand, | ||
392 | * consider using that variant (@p maxlen should then be checked so | ||
393 | * that it is greater than the size of @p str). If @p str can not be | ||
394 | * appended, #EINA_FALSE is returned, otherwise, #EINA_TRUE is | ||
395 | * returned. | ||
396 | * | ||
397 | * @see eina_strbuf_common_append() | ||
398 | * @see eina_strbuf_common_append_length() | ||
399 | */ | ||
400 | Eina_Bool | ||
401 | eina_strbuf_common_append_n(size_t csize, | ||
402 | Eina_Strbuf *buf, | ||
403 | const void *str, | ||
404 | size_t len, | ||
405 | size_t maxlen) | ||
406 | { | ||
407 | EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE); | ||
408 | |||
409 | if (len > maxlen) len = maxlen; | ||
410 | if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + len))) | ||
411 | return EINA_FALSE; | ||
412 | memcpy(((unsigned char *)(buf->buf)) + (buf->len * csize), str, | ||
413 | len * csize); | ||
414 | buf->len += len; | ||
415 | memset(((unsigned char *)(buf->buf)) + (buf->len * csize), 0, csize); | ||
416 | return EINA_TRUE; | ||
417 | } | ||
418 | |||
419 | /** | ||
420 | * @internal | ||
421 | * @brief Append a string of exact length to a buffer, reallocating as necessary. | ||
422 | * | ||
423 | * @param csize the character size | ||
424 | * @param buf The string buffer to append to. | ||
425 | * @param str The string to append. | ||
426 | * @param length The exact length to use. | ||
427 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||
428 | * | ||
429 | * This function appends @p str to @p buf. @p str must be of size at | ||
430 | * most @p length. It is slightly faster than eina_strbuf_common_append() as | ||
431 | * it does not compute the size of @p str. It is useful when dealing | ||
432 | * with strings of known size, such as eina_strngshare. If @p buf | ||
433 | * can't append it, #EINA_FALSE is returned, otherwise #EINA_TRUE is | ||
434 | * returned. | ||
435 | * | ||
436 | * @see eina_stringshare_length() | ||
437 | * @see eina_strbuf_common_append() | ||
438 | * @see eina_strbuf_common_append_n() | ||
439 | */ | ||
440 | Eina_Bool | ||
441 | eina_strbuf_common_append_length(size_t csize, | ||
442 | Eina_Strbuf *buf, | ||
443 | const void *str, | ||
444 | size_t length) | ||
445 | { | ||
446 | EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE); | ||
447 | |||
448 | if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + length))) | ||
449 | return EINA_FALSE; | ||
450 | memcpy(((unsigned char *)(buf->buf)) + (buf->len * csize), str, | ||
451 | length * csize); | ||
452 | buf->len += length; | ||
453 | memset(((unsigned char *)(buf->buf)) + (buf->len * csize), 0, csize); | ||
454 | return EINA_TRUE; | ||
455 | } | ||
456 | |||
457 | /** | ||
458 | * @internal | ||
459 | * @brief Insert a string to a buffer, reallocating as necessary. | ||
460 | * | ||
461 | * @param csize the character size | ||
462 | * @param buf The string buffer to insert. | ||
463 | * @param str The string to insert. | ||
464 | * @param pos The position to insert the string. | ||
465 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||
466 | * | ||
467 | * This function inserts @p str to @p buf at position @p pos. It | ||
468 | * computes the length of @p str, so is slightly slower than | ||
469 | * eina_strbuf_common_insert_length(). If the length is known beforehand, | ||
470 | * consider using that variant. If @p buf can't insert it, #EINA_FALSE | ||
471 | * is returned, otherwise #EINA_TRUE is returned. | ||
472 | */ | ||
473 | Eina_Bool | ||
474 | eina_strbuf_common_insert(size_t csize, | ||
475 | Eina_Strbuf *buf, | ||
476 | const void *str, | ||
477 | size_t len, | ||
478 | size_t pos) | ||
479 | { | ||
480 | EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE); | ||
481 | |||
482 | if (pos >= buf->len) return eina_strbuf_common_append(csize, buf, str, len); | ||
483 | return _eina_strbuf_common_insert_length(csize, buf, str, len, pos); | ||
484 | } | ||
485 | |||
486 | /** | ||
487 | * @internal | ||
488 | * @brief Insert a string to a buffer, reallocating as necessary. Limited by maxlen. | ||
489 | * | ||
490 | * @param csize the character size | ||
491 | * @param buf The string buffer to insert to. | ||
492 | * @param str The string to insert. | ||
493 | * @param maxlen The maximum number of chars to insert. | ||
494 | * @param pos The position to insert the string. | ||
495 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||
496 | * | ||
497 | * This function inserts @p str ot @p buf at position @p pos, with at | ||
498 | * most @p maxlen bytes. The number of inserted characters can not be | ||
499 | * greater than the length of @p str. It computes the length of | ||
500 | * @p str, so is slightly slower than eina_strbuf_common_insert_length(). If the | ||
501 | * length is known beforehand, consider using that variant (@p maxlen | ||
502 | * should then be checked so that it is greater than the size of | ||
503 | * @p str). If @p str can not be inserted, #EINA_FALSE is returned, | ||
504 | * otherwise, #EINA_TRUE is returned. | ||
505 | */ | ||
506 | Eina_Bool | ||
507 | eina_strbuf_common_insert_n(size_t csize, | ||
508 | Eina_Strbuf *buf, | ||
509 | const void *str, | ||
510 | size_t len, | ||
511 | size_t maxlen, | ||
512 | size_t pos) | ||
513 | { | ||
514 | EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE); | ||
515 | |||
516 | if (pos >= buf->len) | ||
517 | return eina_strbuf_common_append_n(csize, buf, str, len, maxlen); | ||
518 | if (len > maxlen) len = maxlen; | ||
519 | return _eina_strbuf_common_insert_length(csize, buf, str, len, pos); | ||
520 | } | ||
521 | |||
522 | /** | ||
523 | * @internal | ||
524 | * @brief Insert a string of exact length to a buffer, reallocating as necessary. | ||
525 | * | ||
526 | * @param csize the character size | ||
527 | * @param buf The string buffer to insert to. | ||
528 | * @param str The string to insert. | ||
529 | * @param length The exact length to use. | ||
530 | * @param pos The position to insert the string. | ||
531 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||
532 | * | ||
533 | * This function inserts @p str to @p buf. @p str must be of size at | ||
534 | * most @p length. It is slightly faster than eina_strbuf_common_insert() as | ||
535 | * it does not compute the size of @p str. It is useful when dealing | ||
536 | * with strings of known size, such as eina_strngshare. If @p buf | ||
537 | * can't insert it, #EINA_FALSE is returned, otherwise #EINA_TRUE is | ||
538 | * returned. | ||
539 | * | ||
540 | * @see eina_stringshare_length() | ||
541 | * @see eina_strbuf_common_insert() | ||
542 | * @see eina_strbuf_common_insert_n() | ||
543 | */ | ||
544 | Eina_Bool | ||
545 | eina_strbuf_common_insert_length(size_t csize, | ||
546 | Eina_Strbuf *buf, | ||
547 | const void *str, | ||
548 | size_t length, | ||
549 | size_t pos) | ||
550 | { | ||
551 | EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE); | ||
552 | |||
553 | if (pos >= buf->len) | ||
554 | return eina_strbuf_common_append_length(csize, buf, str, length); | ||
555 | return _eina_strbuf_common_insert_length(csize, buf, str, length, pos); | ||
556 | } | ||
557 | |||
558 | /** | ||
559 | * @internal | ||
560 | * @brief Append a character to a string buffer, reallocating as | ||
561 | * necessary. | ||
562 | * | ||
563 | * @param csize the character size | ||
564 | * @param buf The string buffer to append to. | ||
565 | * @param c The char to append. | ||
566 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||
567 | * | ||
568 | * This function inserts @p c to @p buf. If it can not insert it, | ||
569 | * #EINA_FALSE is returned, otherwise #EINA_TRUE is returned. | ||
570 | */ | ||
571 | Eina_Bool | ||
572 | eina_strbuf_common_append_char(size_t csize, Eina_Strbuf *buf, const void *c) | ||
573 | { | ||
574 | if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + 1))) | ||
575 | return EINA_FALSE; | ||
576 | |||
577 | memcpy(((unsigned char *)(buf->buf)) + ((buf->len)++ *csize), c, csize); | ||
578 | memset(((unsigned char *)(buf->buf)) + (buf->len * csize), 0, csize); | ||
579 | return EINA_TRUE; | ||
580 | } | ||
581 | |||
582 | /** | ||
583 | * @internal | ||
584 | * @brief Insert a character to a string buffer, reallocating as | ||
585 | * necessary. | ||
586 | * | ||
587 | * @param csize the character size | ||
588 | * @param buf The string buffer to insert to. | ||
589 | * @param c The char to insert. | ||
590 | * @param pos The position to insert the char. | ||
591 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||
592 | * | ||
593 | * This function inserts @p c to @p buf at position @p pos. If @p buf | ||
594 | * can't append it, #EINA_FALSE is returned, otherwise #EINA_TRUE is | ||
595 | * returned. | ||
596 | */ | ||
597 | Eina_Bool | ||
598 | eina_strbuf_common_insert_char(size_t csize, | ||
599 | Eina_Strbuf *buf, | ||
600 | const void *c, | ||
601 | size_t pos) | ||
602 | { | ||
603 | if (pos >= buf->len) | ||
604 | return eina_strbuf_common_append_char(csize, buf, c); | ||
605 | return _eina_strbuf_common_insert_length(csize, buf, c, 1, pos); | ||
606 | } | ||
607 | |||
608 | /** | ||
609 | * @internal | ||
610 | * @brief Remove a slice of the given string buffer. | ||
611 | * | ||
612 | * @param csize the character size | ||
613 | * @param buf The string buffer to remove a slice. | ||
614 | * @param start The initial (inclusive) slice position to start | ||
615 | * removing, in bytes. | ||
616 | * @param end The final (non-inclusive) slice position to finish | ||
617 | * removing, in bytes. | ||
618 | * @return #EINA_TRUE on success, #EINA_FALSE on failure. | ||
619 | * | ||
620 | * This function removes a slice of @p buf, starting at @p start | ||
621 | * (inclusive) and ending at @p end (non-inclusive). Both values are | ||
622 | * in bytes. It returns #EINA_FALSE on failure, #EINA_TRUE otherwise. | ||
623 | */ | ||
624 | Eina_Bool | ||
625 | eina_strbuf_common_remove(size_t csize, | ||
626 | Eina_Strbuf *buf, | ||
627 | size_t start, | ||
628 | size_t end) | ||
629 | { | ||
630 | size_t remove_len, tail_len; | ||
631 | |||
632 | if (end >= buf->len) end = buf->len; | ||
633 | if (end <= start) return EINA_TRUE; | ||
634 | |||
635 | remove_len = end - start; | ||
636 | if (remove_len == buf->len) | ||
637 | { | ||
638 | free(buf->buf); | ||
639 | return _eina_strbuf_common_init(csize, buf); | ||
640 | } | ||
641 | |||
642 | tail_len = buf->len - end + 1; /* includes '\0' */ | ||
643 | memmove(((unsigned char *)(buf->buf)) + (start * csize), | ||
644 | ((unsigned char *)(buf->buf)) + (end * csize), | ||
645 | tail_len * csize); | ||
646 | buf->len -= remove_len; | ||
647 | return _eina_strbuf_common_resize(csize, buf, buf->len); | ||
648 | } | ||
649 | |||
650 | /** | ||
651 | * @internal | ||
652 | * @brief Retrieve a pointer to the contents of a string buffer | ||
653 | * | ||
654 | * @param buf The string buffer. | ||
655 | * @return The current string in the string buffer. | ||
656 | * | ||
657 | * This function returns the string contained in @p buf. The returned | ||
658 | * value must not be modified and will no longer be valid if @p buf is | ||
659 | * modified. In other words, any eina_strbuf_common_append() or similar will | ||
660 | * make that pointer invalid. | ||
661 | * | ||
662 | * @see eina_strbuf_common_string_steal() | ||
663 | */ | ||
664 | const void * | ||
665 | eina_strbuf_common_string_get(const Eina_Strbuf *buf) | ||
666 | { | ||
667 | return buf->buf; | ||
668 | } | ||
669 | |||
670 | /** | ||
671 | * @internal | ||
672 | * @brief Steal the contents of a string buffer. | ||
673 | * | ||
674 | * @param csize the character size | ||
675 | * @param buf The string buffer to steal. | ||
676 | * @return The current string in the string buffer. | ||
677 | * | ||
678 | * This function returns the string contained in @p buf. @p buf is | ||
679 | * then initialized and does not own the returned string anymore. The | ||
680 | * caller must release the memory of the returned string by calling | ||
681 | * free(). | ||
682 | * | ||
683 | * @see eina_strbuf_common_string_get() | ||
684 | */ | ||
685 | void * | ||
686 | eina_strbuf_common_string_steal(size_t csize, Eina_Strbuf *buf) | ||
687 | { | ||
688 | void *ret; | ||
689 | |||
690 | ret = buf->buf; | ||
691 | // TODO: Check return value and do something clever | ||
692 | _eina_strbuf_common_init(csize, buf); | ||
693 | return ret; | ||
694 | } | ||
695 | |||
696 | /** | ||
697 | * @internal | ||
698 | * @brief Free the contents of a string buffer but not the buffer. | ||
699 | * | ||
700 | * @param csize the character size | ||
701 | * @param buf The string buffer to free the string of. | ||
702 | * | ||
703 | * This function frees the string contained in @p buf without freeing | ||
704 | * @p buf. | ||
705 | */ | ||
706 | void | ||
707 | eina_strbuf_common_string_free(size_t csize, Eina_Strbuf *buf) | ||
708 | { | ||
709 | free(buf->buf); | ||
710 | _eina_strbuf_common_init(csize, buf); | ||
711 | } | ||
712 | |||
713 | /** | ||
714 | * @internal | ||
715 | * @brief Retrieve the length of the string buffer content. | ||
716 | * | ||
717 | * @param buf The string buffer. | ||
718 | * @return The current length of the string, in bytes. | ||
719 | * | ||
720 | * This function returns the length of @p buf. | ||
721 | */ | ||
722 | size_t | ||
723 | eina_strbuf_common_length_get(const Eina_Strbuf *buf) | ||
724 | { | ||
725 | return buf->len; | ||
726 | } | ||
727 | |||
728 | /** | ||
729 | * @cond LOCAL | ||
730 | */ | ||
731 | |||
732 | /*FIXME: Implementing them here is a hack! */ | ||
733 | |||
734 | #ifdef _STRBUF_CSIZE | ||
735 | # undef _STRBUF_CSIZE | ||
736 | #endif | ||
737 | |||
738 | #ifdef _STRBUF_MAGIC | ||
739 | # undef _STRBUF_MAGIC | ||
740 | #endif | ||
741 | |||
742 | #ifdef _STRBUF_MAGIC_STR | ||
743 | # undef _STRBUF_MAGIC_STR | ||
744 | #endif | ||
745 | |||
746 | #define _STRBUF_CSIZE 1 | ||
747 | #define _STRBUF_MAGIC EINA_MAGIC_STRBUF | ||
748 | #define _STRBUF_MAGIC_STR __STRBUF_STR_MAGIC_STR | ||
749 | static const char __STRBUF_STR_MAGIC_STR[] = "Eina Strbuf"; | ||
750 | |||
751 | |||
752 | /** | ||
753 | * @endcond | ||
754 | */ | ||
755 | |||
756 | |||
757 | EAPI Eina_Bool | ||
758 | eina_strbuf_replace(Eina_Strbuf *buf, | ||
759 | const char *str, | ||
760 | const char *with, | ||
761 | unsigned int n) | ||
762 | { | ||
763 | size_t len1, len2; | ||
764 | char *spos; | ||
765 | size_t pos; | ||
766 | |||
767 | EINA_SAFETY_ON_NULL_RETURN_VAL( str, EINA_FALSE); | ||
768 | EINA_SAFETY_ON_NULL_RETURN_VAL(with, EINA_FALSE); | ||
769 | EINA_MAGIC_CHECK_STRBUF(buf, 0); | ||
770 | if (n == 0) return EINA_FALSE; | ||
771 | |||
772 | spos = buf->buf; | ||
773 | while (n--) | ||
774 | { | ||
775 | spos = strstr(spos, str); | ||
776 | if (!spos || *spos == '\0') return EINA_FALSE; | ||
777 | if (n) spos++; | ||
778 | } | ||
779 | |||
780 | pos = spos - (const char *)buf->buf; | ||
781 | len1 = strlen(str); | ||
782 | len2 = strlen(with); | ||
783 | if (len1 != len2) | ||
784 | { | ||
785 | /* resize the buffer if necessary */ | ||
786 | if (EINA_UNLIKELY(!_eina_strbuf_common_grow(_STRBUF_CSIZE, buf, | ||
787 | buf->len - len1 + len2))) | ||
788 | return EINA_FALSE; /* move the existing text */ | ||
789 | memmove(((unsigned char *)(buf->buf)) + pos + len2, | ||
790 | ((unsigned char *)(buf->buf)) + pos + len1, | ||
791 | buf->len - pos - len1); | ||
792 | } | ||
793 | /* and now insert the given string */ | ||
794 | memcpy(((unsigned char *)(buf->buf)) + pos, with, len2); | ||
795 | buf->len += len2 - len1; | ||
796 | memset(((unsigned char *)(buf->buf)) + buf->len, 0, 1); | ||
797 | return EINA_TRUE; | ||
798 | } | ||
799 | |||
800 | EAPI int | ||
801 | eina_strbuf_replace_all(Eina_Strbuf *buf, const char *str, const char *with) | ||
802 | { | ||
803 | size_t len1, len2, len; | ||
804 | char *tmp_buf = NULL; | ||
805 | char *spos; | ||
806 | size_t pos, start; | ||
807 | size_t pos_tmp, start_tmp; | ||
808 | int n = 0; | ||
809 | |||
810 | EINA_SAFETY_ON_NULL_RETURN_VAL( str, 0); | ||
811 | EINA_SAFETY_ON_NULL_RETURN_VAL(with, 0); | ||
812 | EINA_MAGIC_CHECK_STRBUF(buf, 0); | ||
813 | |||
814 | spos = strstr(buf->buf, str); | ||
815 | if (!spos || *spos == '\0') return 0; | ||
816 | len1 = strlen(str); | ||
817 | len2 = strlen(with); | ||
818 | /* if the size of the two string is equal, it is fairly easy to replace them | ||
819 | * we don't need to resize the buffer or doing other calculations */ | ||
820 | if (len1 == len2) | ||
821 | { | ||
822 | while (spos) | ||
823 | { | ||
824 | memcpy(spos, with, len2); | ||
825 | spos = strstr(spos + len2, str); | ||
826 | n++; | ||
827 | } | ||
828 | return n; | ||
829 | } | ||
830 | pos = pos_tmp = spos - (const char *)buf->buf; | ||
831 | tmp_buf = buf->buf; | ||
832 | buf->buf = malloc(buf->size); | ||
833 | if (EINA_UNLIKELY(!buf->buf)) | ||
834 | { | ||
835 | buf->buf = tmp_buf; | ||
836 | return 0; | ||
837 | } | ||
838 | start = start_tmp = 0; | ||
839 | len = buf->len; | ||
840 | while (spos) | ||
841 | { | ||
842 | n++; | ||
843 | len = (len + len2) - len1; | ||
844 | /* resize the buffer if necessary */ | ||
845 | if (EINA_UNLIKELY(!_eina_strbuf_common_grow(_STRBUF_CSIZE, buf, len))) | ||
846 | { | ||
847 | /* we have to stop replacing here, because we haven't enough | ||
848 | * memory to go on */ | ||
849 | len = (len + len1) - len2; | ||
850 | break; | ||
851 | } | ||
852 | /* copy the untouched text */ | ||
853 | memcpy(((unsigned char *)(buf->buf)) + start, tmp_buf + start_tmp, | ||
854 | pos - start); | ||
855 | /* copy the new string */ | ||
856 | memcpy(((unsigned char *)(buf->buf)) + pos, with, len2); | ||
857 | /* calculate the next positions */ | ||
858 | start_tmp = pos_tmp + len1; | ||
859 | start = pos + len2; | ||
860 | spos = strstr(tmp_buf + start_tmp, str); | ||
861 | /* this calculations don't make sense if spos == NULL, but the | ||
862 | * calculated values won't be used, because the loop will stop | ||
863 | * then */ | ||
864 | pos_tmp = spos - tmp_buf; | ||
865 | pos = start + pos_tmp - start_tmp; | ||
866 | } | ||
867 | /* and now copy the rest of the text */ | ||
868 | memcpy(((unsigned char *)(buf->buf)) + start, tmp_buf + start_tmp, | ||
869 | len - start); | ||
870 | buf->len = len; | ||
871 | memset(((unsigned char *)(buf->buf)) + buf->len, 0, 1); | ||
872 | free(tmp_buf); | ||
873 | return n; | ||
874 | } | ||