Libical API Documentation 4.0 STABLE VERSION Visit the v3.0 documentation
Loading...
Searching...
No Matches
icalmemory.c
Go to the documentation of this file.
1/*======================================================================
2 FILE: icalmemory.c
3 CREATOR: eric 30 June 1999
4
5 SPDX-FileCopyrightText: 2000 Eric Busboom <eric@civicknowledge.com>
6 SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0
7======================================================================*/
8
13
14#ifdef HAVE_CONFIG_H
15#include <config.h>
16#endif
17
18#include "icalmemory.h"
19#include "icalerror_p.h"
20#if defined(MEMORY_CONSISTENCY)
21#include "test-malloc.h"
22#endif
23
24#include <stdlib.h>
25#include <stdbool.h>
26
31#define BUFFER_RING_SIZE 2500
32
37#define MIN_BUFFER_SIZE 200
38
39/* HACK. Not threadsafe */
40
41typedef struct
42{
43 int pos;
44 void *ring[BUFFER_RING_SIZE];
45} buffer_ring;
46
47#if ICAL_SYNC_MODE != ICAL_SYNC_MODE_PTHREAD
51static ICAL_GLOBAL_VAR buffer_ring *global_buffer_ring = 0;
52#endif
53
59static void icalmemory_free_ring_byval(buffer_ring *br)
60{
61 int i;
62
63 for (i = 0; i < BUFFER_RING_SIZE; i++) {
64 icalmemory_free_buffer(br->ring[i]);
65 }
67}
68
69#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
70#include <pthread.h>
71
72static pthread_key_t ring_key;
73static pthread_once_t ring_key_once = PTHREAD_ONCE_INIT;
74
75static void ring_destroy(void *buf)
76{
77 icalmemory_free_ring_byval((buffer_ring *)buf);
78 pthread_setspecific(ring_key, NULL);
79}
80
81static void ring_key_alloc(void)
82{
83 pthread_key_create(&ring_key, ring_destroy);
84}
85
86#endif
87
91static buffer_ring *buffer_ring_new(void)
92{
93 buffer_ring *br;
94 int i;
95
96 br = (buffer_ring *)icalmemory_new_buffer(sizeof(buffer_ring));
97 if (!br) {
98 return NULL;
99 }
100
101 for (i = 0; i < BUFFER_RING_SIZE; i++) {
102 br->ring[i] = 0;
103 }
104 br->pos = 0;
105 return (br);
106}
107
108#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
112static buffer_ring *get_buffer_ring_pthread(void)
113{
114 buffer_ring *br;
115
116 pthread_once(&ring_key_once, ring_key_alloc);
117
118 br = pthread_getspecific(ring_key);
119
120 if (!br) {
121 br = buffer_ring_new();
122 if (br) {
123 pthread_setspecific(ring_key, br);
124 }
125 }
126 return (br);
127}
128
129#else
135static buffer_ring *get_buffer_ring_global(void)
136{
137 if (global_buffer_ring == 0) {
138 global_buffer_ring = buffer_ring_new();
139 }
140 return (global_buffer_ring);
141}
142
143#endif
144
148static buffer_ring *get_buffer_ring(void)
149{
150#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
151 return (get_buffer_ring_pthread());
152#else
153 return get_buffer_ring_global();
154#endif
155}
156
157/* Add an existing buffer to the buffer ring */
159{
160 buffer_ring *br = get_buffer_ring();
161 if (!br) {
162 return;
163 }
164
165 /* Wrap around the ring */
166 if (++(br->pos) == BUFFER_RING_SIZE) {
167 br->pos = 0;
168 }
169
170 /* Free buffers as their slots are overwritten */
171 icalmemory_free_buffer(br->ring[br->pos]);
172
173 /* Assign the buffer to a slot */
174 br->ring[br->pos] = buf;
175}
176
177/*
178 * Create a new temporary buffer on the ring. Libical owns these and
179 * will deallocate them.
180 */
181
182void *icalmemory_tmp_buffer(size_t size)
183{
184 char *buf;
185
186 if (size < MIN_BUFFER_SIZE) {
187 size = MIN_BUFFER_SIZE;
188 }
189
190 buf = (void *)icalmemory_new_buffer(size);
191
192 if (buf == 0) {
194 return 0;
195 }
196
197 memset(buf, 0, size);
198
200
201 return buf;
202}
203
205{
206 buffer_ring *br;
207
208 br = get_buffer_ring();
209 if (!br) {
210 return;
211 }
212
213 icalmemory_free_ring_byval(br);
214#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
215 pthread_setspecific(ring_key, 0);
216#else
217 global_buffer_ring = 0;
218#endif
219}
220
221/* Like strdup, but the buffer is on the ring. */
222char *icalmemory_tmp_copy(const char *str)
223{
224 char *b;
225
226 if (!str || str[0] == '\0') {
227 return NULL;
228 }
229
230 const size_t len_b = strlen(str) + 1;
231 b = icalmemory_tmp_buffer(len_b);
232
233 if (!b) {
234 return NULL;
235 }
236
237 strncpy(b, str, len_b);
238
239 return b;
240}
241
242char *icalmemory_strdup(const char *s)
243{
244 size_t l;
245 char *res;
246
247 if (!s) {
248 return NULL;
249 }
250
251 l = (strlen(s) + 1) * sizeof(char);
252 res = (char *)icalmemory_new_buffer(l);
253 if (res == NULL) {
254 return NULL;
255 }
256
257 memcpy(res, s, l);
258
259 return res;
260}
261
262#if defined(MEMORY_CONSISTENCY)
263static ICAL_GLOBAL_VAR icalmemory_malloc_f global_icalmem_malloc = &test_malloc;
264#elif defined(ICALMEMORY_DEFAULT_MALLOC) && !defined(S_SPLINT_S)
265static ICAL_GLOBAL_VAR icalmemory_malloc_f global_icalmem_malloc = &ICALMEMORY_DEFAULT_MALLOC;
266#else
267static ICAL_GLOBAL_VAR icalmemory_malloc_f global_icalmem_malloc = NULL;
268#endif
269
270#if defined(MEMORY_CONSISTENCY)
271static ICAL_GLOBAL_VAR icalmemory_realloc_f global_icalmem_realloc = &test_realloc;
272#elif defined(ICALMEMORY_DEFAULT_REALLOC) && !defined(S_SPLINT_S)
273static ICAL_GLOBAL_VAR icalmemory_realloc_f global_icalmem_realloc = &ICALMEMORY_DEFAULT_REALLOC;
274#else
275static ICAL_GLOBAL_VAR icalmemory_realloc_f global_icalmem_realloc = NULL;
276#endif
277
278#if defined(MEMORY_CONSISTENCY)
279static ICAL_GLOBAL_VAR icalmemory_free_f global_icalmem_free = &test_free;
280#elif defined(ICALMEMORY_DEFAULT_FREE) && !defined(S_SPLINT_S)
281static ICAL_GLOBAL_VAR icalmemory_free_f global_icalmem_free = &ICALMEMORY_DEFAULT_FREE;
282#else
283static ICAL_GLOBAL_VAR icalmemory_free_f global_icalmem_free = NULL;
284#endif
285
286void icalmemory_set_mem_alloc_funcs(icalmemory_malloc_f f_malloc,
287 icalmemory_realloc_f f_realloc,
288 icalmemory_free_f f_free)
289{
290 global_icalmem_malloc = f_malloc;
291 global_icalmem_realloc = f_realloc;
292 global_icalmem_free = f_free;
293}
294
295void icalmemory_get_mem_alloc_funcs(icalmemory_malloc_f *f_malloc,
296 icalmemory_realloc_f *f_realloc,
297 icalmemory_free_f *f_free)
298{
299 if (f_malloc) {
300 *f_malloc = global_icalmem_malloc;
301 }
302 if (f_realloc) {
303 *f_realloc = global_icalmem_realloc;
304 }
305 if (f_free) {
306 *f_free = global_icalmem_free;
307 }
308}
309
310/*
311 * These buffer routines create memory the old fashioned way -- so the
312 * caller will have to deallocate the new memory
313 */
314
315void *icalmemory_new_buffer(size_t size)
316{
317 void *b;
318
319 if (global_icalmem_malloc == NULL) {
321 return 0;
322 }
323
324 b = global_icalmem_malloc(size);
325
326 if (b == 0) {
328 return 0;
329 }
330
331 memset(b, 0, size);
332
333 return b;
334}
335
336void *icalmemory_resize_buffer(void *buf, size_t size)
337{
338 void *b;
339
340 if (global_icalmem_realloc == NULL) {
342 return 0;
343 }
344
345 b = global_icalmem_realloc(buf, size);
346
347 if (b == 0) {
349 return 0;
350 }
351
352 return b;
353}
354
356{
357 if (global_icalmem_free == NULL) {
359 return;
360 }
361
362 global_icalmem_free(buf);
363}
364
365void icalmemory_append_string(char **buf, char **pos, size_t *buf_size, const char *string)
366{
367 char *new_buf;
368 char *new_pos;
369
370 size_t data_length, final_length, string_length;
371
372#if !defined(ICAL_NO_INTERNAL_DEBUG)
373 icalerror_check_arg_rv((buf != 0), "buf");
374 icalerror_check_arg_rv((*buf != 0), "*buf");
375 icalerror_check_arg_rv((pos != 0), "pos");
376 icalerror_check_arg_rv((*pos != 0), "*pos");
377 icalerror_check_arg_rv((buf_size != 0), "buf_size");
378 icalerror_check_arg_rv((*buf_size != 0), "*buf_size");
379 icalerror_check_arg_rv((string != 0), "string");
380#endif
381
382 string_length = strlen(string);
383 data_length = (size_t)*pos - (size_t)*buf;
384 final_length = data_length + string_length;
385
386 if (final_length >= (size_t)*buf_size) {
387 *buf_size = (*buf_size) * 2 + final_length;
388
389 new_buf = icalmemory_resize_buffer(*buf, *buf_size);
390 if (!new_buf) {
391 // an error was set in the resize function, so we just return here.
392 return;
393 }
394
395 new_pos = (void *)((size_t)new_buf + data_length);
396
397 *pos = new_pos;
398 *buf = new_buf;
399 }
400
401 strcpy(*pos, string); //NOLINT(clang-analyzer-security.insecureAPI.strcpy)
402
403 *pos += string_length;
404}
405
406void icalmemory_append_char(char **buf, char **pos, size_t *buf_size, char ch)
407{
408 char *new_buf;
409 char *new_pos;
410
411 size_t data_length, final_length;
412
413#if !defined(ICAL_NO_INTERNAL_DEBUG)
414 icalerror_check_arg_rv((buf != 0), "buf");
415 icalerror_check_arg_rv((*buf != 0), "*buf");
416 icalerror_check_arg_rv((pos != 0), "pos");
417 icalerror_check_arg_rv((*pos != 0), "*pos");
418 icalerror_check_arg_rv((buf_size != 0), "buf_size");
419 icalerror_check_arg_rv((*buf_size != 0), "*buf_size");
420#endif
421
422 data_length = (size_t)*pos - (size_t)*buf;
423
424 final_length = data_length + 2;
425
426 if (final_length > (size_t)*buf_size) {
427 *buf_size = (*buf_size) * 2 + final_length + 1;
428
429 new_buf = icalmemory_resize_buffer(*buf, *buf_size);
430 if (!new_buf) {
431 // an error was set in the resize function, so we just return here.
432 return;
433 }
434
435 new_pos = (void *)((size_t)new_buf + data_length);
436
437 *pos = new_pos;
438 *buf = new_buf;
439 }
440
441 **pos = ch;
442 *pos += 1;
443 **pos = 0;
444}
445
461#define UNSAFE_CHARS ";:,"
463
464static bool icalmemory_is_safe_char(unsigned char character, bool quoted)
465{
466 if (character == ' ' || character == '\t' || character == '!') {
467 return true;
468 }
469
470 if (character < 0x23 || character == 0x7f || character > 0xf8 ||
471 (!quoted && strchr(UNSAFE_CHARS, character))) {
472 return false;
473 }
474
475 return true;
476}
477
478void icalmemory_append_encoded_string(char **buf, char **pos,
479 size_t *buf_size, const char *string)
480{
481 bool quoted = false;
482 const char *p;
483
484 /* Encapsulate the string in quotes if necessary */
485 if (!*string || strpbrk(string, UNSAFE_CHARS) != 0) {
486 icalmemory_append_char(buf, pos, buf_size, '"');
487 quoted = true;
488 }
489
490 /* Copy the string */
491 for (p = string; *p; p++) {
492 /* Encode unsafe characters per RFC6868, otherwise replace with SP */
493 switch (*p) {
494 case '\n':
495 icalmemory_append_string(buf, pos, buf_size, "^n");
496 break;
497
498 case '^':
499 icalmemory_append_string(buf, pos, buf_size, "^^");
500 break;
501
502 case '"':
503 icalmemory_append_string(buf, pos, buf_size, "^'");
504 break;
505
506 default:
507 if (icalmemory_is_safe_char((unsigned char)*p, quoted)) {
508 icalmemory_append_char(buf, pos, buf_size, *p);
509 } else {
510 icalmemory_append_char(buf, pos, buf_size, ' ');
511 }
512 break;
513 }
514 }
515
516 if (quoted == true) {
517 icalmemory_append_char(buf, pos, buf_size, '"');
518 }
519}
520
521void icalmemory_append_decoded_string(char **buf, char **pos,
522 size_t *buf_size, const char *string)
523{
524 const char *p;
525
526 /* Copy the string */
527 for (p = string; *p; p++) {
528 switch (*p) {
529 case '"':
530 /* Remove encapsulating quotes if necessary */
531 break;
532
533 case '^':
534 /* Decode unsafe characters per RFC6868 */
535 if (p[1] == 'n') {
536 icalmemory_append_char(buf, pos, buf_size, '\n');
537 p++;
538 } else if (p[1] == '^') {
539 icalmemory_append_char(buf, pos, buf_size, '^');
540 p++;
541 } else if (p[1] == '\'') {
542 icalmemory_append_char(buf, pos, buf_size, '"');
543 p++;
544 } else {
545 // Unknown escape sequence, copy verbatim.
546 icalmemory_append_char(buf, pos, buf_size, *p);
547 }
548 break;
549
550 default:
551 icalmemory_append_char(buf, pos, buf_size, *p);
552 }
553 }
554}
void icalerror_set_errno(icalerrorenum x)
Sets the icalerrno to a given error.
Definition icalerror.c:90
@ ICAL_NEWFAILED_ERROR
Definition icalerror.h:50
#define MIN_BUFFER_SIZE
Determines the minimal size of buffers in the ring that are created with icalmemory_tmp_buffer().
Definition icalmemory.c:37
void icalmemory_free_ring(void)
Frees all memory used in the ring.
Definition icalmemory.c:204
void icalmemory_free_buffer(void *buf)
Releases a buffer.
Definition icalmemory.c:355
char * icalmemory_strdup(const char *s)
Creates a duplicate of a string.
Definition icalmemory.c:242
void icalmemory_append_decoded_string(char **buf, char **pos, size_t *buf_size, const char *string)
Definition icalmemory.c:521
void icalmemory_append_string(char **buf, char **pos, size_t *buf_size, const char *string)
Appends a string to a buffer.
Definition icalmemory.c:365
void * icalmemory_resize_buffer(void *buf, size_t size)
Resizes a buffer created with icalmemory_new_buffer().
Definition icalmemory.c:336
void * icalmemory_new_buffer(size_t size)
Creates new buffer with the specified size.
Definition icalmemory.c:315
char * icalmemory_tmp_copy(const char *str)
Creates a copy of the given string, stored on the ring buffer, and returns it.
Definition icalmemory.c:222
void icalmemory_append_encoded_string(char **buf, char **pos, size_t *buf_size, const char *string)
Definition icalmemory.c:478
void icalmemory_append_char(char **buf, char **pos, size_t *buf_size, char ch)
Appends a character to a buffer.
Definition icalmemory.c:406
void icalmemory_get_mem_alloc_funcs(icalmemory_malloc_f *f_malloc, icalmemory_realloc_f *f_realloc, icalmemory_free_f *f_free)
Returns the functions used for memory management.
Definition icalmemory.c:295
void icalmemory_add_tmp_buffer(void *buf)
Adds an externally allocated buffer to the ring.
Definition icalmemory.c:158
void icalmemory_set_mem_alloc_funcs(icalmemory_malloc_f f_malloc, icalmemory_realloc_f f_realloc, icalmemory_free_f f_free)
Configures the functions to use for memory management.
Definition icalmemory.c:286
void * icalmemory_tmp_buffer(size_t size)
Creates a new temporary buffer on the ring and returns it.
Definition icalmemory.c:182
#define BUFFER_RING_SIZE
Determines the size of the ring buffer used for keeping track of temporary buffers.
Definition icalmemory.c:31
Common memory management routines.