Libical API Documentation 4.0 UNRELEASED Go to the stable 3.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 pthread_setspecific(ring_key, br);
123 }
124 return (br);
125}
126
127#else
133static buffer_ring *get_buffer_ring_global(void)
134{
135 if (global_buffer_ring == 0) {
136 global_buffer_ring = buffer_ring_new();
137 }
138 return (global_buffer_ring);
139}
140
141#endif
142
146static buffer_ring *get_buffer_ring(void)
147{
148#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
149 return (get_buffer_ring_pthread());
150#else
151 return get_buffer_ring_global();
152#endif
153}
154
155/* Add an existing buffer to the buffer ring */
157{
158 buffer_ring *br = get_buffer_ring();
159 if (!br) {
160 return;
161 }
162
163 /* Wrap around the ring */
164 if (++(br->pos) == BUFFER_RING_SIZE) {
165 br->pos = 0;
166 }
167
168 /* Free buffers as their slots are overwritten */
169 icalmemory_free_buffer(br->ring[br->pos]);
170
171 /* Assign the buffer to a slot */
172 br->ring[br->pos] = buf;
173}
174
175/*
176 * Create a new temporary buffer on the ring. Libical owns these and
177 * will deallocate them.
178 */
179
180void *icalmemory_tmp_buffer(size_t size)
181{
182 char *buf;
183
184 if (size < MIN_BUFFER_SIZE) {
185 size = MIN_BUFFER_SIZE;
186 }
187
188 buf = (void *)icalmemory_new_buffer(size);
189
190 if (buf == 0) {
192 return 0;
193 }
194
195 memset(buf, 0, size);
196
198
199 return buf;
200}
201
203{
204 buffer_ring *br;
205
206 br = get_buffer_ring();
207 if (!br) {
208 return;
209 }
210
211 icalmemory_free_ring_byval(br);
212#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
213 pthread_setspecific(ring_key, 0);
214#else
215 global_buffer_ring = 0;
216#endif
217}
218
219/* Like strdup, but the buffer is on the ring. */
220char *icalmemory_tmp_copy(const char *str)
221{
222 char *b;
223
224 if (!str || str[0] == '\0') {
225 return NULL;
226 }
227
228 const size_t len_b = strlen(str) + 1;
229 b = icalmemory_tmp_buffer(len_b);
230
231 if (!b) {
232 return NULL;
233 }
234
235 strncpy(b, str, len_b);
236
237 return b;
238}
239
240char *icalmemory_strdup(const char *s)
241{
242 size_t l;
243 char *res;
244
245 if (!s) {
246 return NULL;
247 }
248
249 l = (strlen(s) + 1) * sizeof(char);
250 res = (char *)icalmemory_new_buffer(l);
251 if (res == NULL) {
252 return NULL;
253 }
254
255 memcpy(res, s, l);
256
257 return res;
258}
259
260#if defined(MEMORY_CONSISTENCY)
261static ICAL_GLOBAL_VAR icalmemory_malloc_f global_icalmem_malloc = &test_malloc;
262#elif defined(ICALMEMORY_DEFAULT_MALLOC) && !defined(S_SPLINT_S)
263static ICAL_GLOBAL_VAR icalmemory_malloc_f global_icalmem_malloc = &ICALMEMORY_DEFAULT_MALLOC;
264#else
265static ICAL_GLOBAL_VAR icalmemory_malloc_f global_icalmem_malloc = NULL;
266#endif
267
268#if defined(MEMORY_CONSISTENCY)
269static ICAL_GLOBAL_VAR icalmemory_realloc_f global_icalmem_realloc = &test_realloc;
270#elif defined(ICALMEMORY_DEFAULT_REALLOC) && !defined(S_SPLINT_S)
271static ICAL_GLOBAL_VAR icalmemory_realloc_f global_icalmem_realloc = &ICALMEMORY_DEFAULT_REALLOC;
272#else
273static ICAL_GLOBAL_VAR icalmemory_realloc_f global_icalmem_realloc = NULL;
274#endif
275
276#if defined(MEMORY_CONSISTENCY)
277static ICAL_GLOBAL_VAR icalmemory_free_f global_icalmem_free = &test_free;
278#elif defined(ICALMEMORY_DEFAULT_FREE) && !defined(S_SPLINT_S)
279static ICAL_GLOBAL_VAR icalmemory_free_f global_icalmem_free = &ICALMEMORY_DEFAULT_FREE;
280#else
281static ICAL_GLOBAL_VAR icalmemory_free_f global_icalmem_free = NULL;
282#endif
283
284void icalmemory_set_mem_alloc_funcs(icalmemory_malloc_f f_malloc,
285 icalmemory_realloc_f f_realloc,
286 icalmemory_free_f f_free)
287{
288 global_icalmem_malloc = f_malloc;
289 global_icalmem_realloc = f_realloc;
290 global_icalmem_free = f_free;
291}
292
293void icalmemory_get_mem_alloc_funcs(icalmemory_malloc_f *f_malloc,
294 icalmemory_realloc_f *f_realloc,
295 icalmemory_free_f *f_free)
296{
297 if (f_malloc) {
298 *f_malloc = global_icalmem_malloc;
299 }
300 if (f_realloc) {
301 *f_realloc = global_icalmem_realloc;
302 }
303 if (f_free) {
304 *f_free = global_icalmem_free;
305 }
306}
307
308/*
309 * These buffer routines create memory the old fashioned way -- so the
310 * caller will have to deallocate the new memory
311 */
312
313void *icalmemory_new_buffer(size_t size)
314{
315 void *b;
316
317 if (global_icalmem_malloc == NULL) {
319 return 0;
320 }
321
322 b = global_icalmem_malloc(size);
323
324 if (b == 0) {
326 return 0;
327 }
328
329 memset(b, 0, size);
330
331 return b;
332}
333
334void *icalmemory_resize_buffer(void *buf, size_t size)
335{
336 void *b;
337
338 if (global_icalmem_realloc == NULL) {
340 return 0;
341 }
342
343 b = global_icalmem_realloc(buf, size);
344
345 if (b == 0) {
347 return 0;
348 }
349
350 return b;
351}
352
354{
355 if (global_icalmem_free == NULL) {
357 return;
358 }
359
360 global_icalmem_free(buf);
361}
362
363void icalmemory_append_string(char **buf, char **pos, size_t *buf_size, const char *string)
364{
365 char *new_buf;
366 char *new_pos;
367
368 size_t data_length, final_length, string_length;
369
370#if !defined(ICAL_NO_INTERNAL_DEBUG)
371 icalerror_check_arg_rv((buf != 0), "buf");
372 icalerror_check_arg_rv((*buf != 0), "*buf");
373 icalerror_check_arg_rv((pos != 0), "pos");
374 icalerror_check_arg_rv((*pos != 0), "*pos");
375 icalerror_check_arg_rv((buf_size != 0), "buf_size");
376 icalerror_check_arg_rv((*buf_size != 0), "*buf_size");
377 icalerror_check_arg_rv((string != 0), "string");
378#endif
379
380 string_length = strlen(string);
381 data_length = (size_t)*pos - (size_t)*buf;
382 final_length = data_length + string_length;
383
384 if (final_length >= (size_t)*buf_size) {
385 *buf_size = (*buf_size) * 2 + final_length;
386
387 new_buf = icalmemory_resize_buffer(*buf, *buf_size);
388 if (!new_buf) {
389 // an error was set in the resize function, so we just return here.
390 return;
391 }
392
393 new_pos = (void *)((size_t)new_buf + data_length);
394
395 *pos = new_pos;
396 *buf = new_buf;
397 }
398
399 strcpy(*pos, string); //NOLINT(clang-analyzer-security.insecureAPI.strcpy)
400
401 *pos += string_length;
402}
403
404void icalmemory_append_char(char **buf, char **pos, size_t *buf_size, char ch)
405{
406 char *new_buf;
407 char *new_pos;
408
409 size_t data_length, final_length;
410
411#if !defined(ICAL_NO_INTERNAL_DEBUG)
412 icalerror_check_arg_rv((buf != 0), "buf");
413 icalerror_check_arg_rv((*buf != 0), "*buf");
414 icalerror_check_arg_rv((pos != 0), "pos");
415 icalerror_check_arg_rv((*pos != 0), "*pos");
416 icalerror_check_arg_rv((buf_size != 0), "buf_size");
417 icalerror_check_arg_rv((*buf_size != 0), "*buf_size");
418#endif
419
420 data_length = (size_t)*pos - (size_t)*buf;
421
422 final_length = data_length + 2;
423
424 if (final_length > (size_t)*buf_size) {
425 *buf_size = (*buf_size) * 2 + final_length + 1;
426
427 new_buf = icalmemory_resize_buffer(*buf, *buf_size);
428 if (!new_buf) {
429 // an error was set in the resize function, so we just return here.
430 return;
431 }
432
433 new_pos = (void *)((size_t)new_buf + data_length);
434
435 *pos = new_pos;
436 *buf = new_buf;
437 }
438
439 **pos = ch;
440 *pos += 1;
441 **pos = 0;
442}
443
459#define UNSAFE_CHARS ";:,"
461
462static bool icalmemory_is_safe_char(unsigned char character, bool quoted)
463{
464 if (character == ' ' || character == '\t' || character == '!') {
465 return true;
466 }
467
468 if (character < 0x23 || character == 0x7f || character > 0xf8 ||
469 (!quoted && strchr(UNSAFE_CHARS, character))) {
470 return false;
471 }
472
473 return true;
474}
475
476void icalmemory_append_encoded_string(char **buf, char **pos,
477 size_t *buf_size, const char *string)
478{
479 bool quoted = false;
480 const char *p;
481
482 /* Encapsulate the string in quotes if necessary */
483 if (!*string || strpbrk(string, UNSAFE_CHARS) != 0) {
484 icalmemory_append_char(buf, pos, buf_size, '"');
485 quoted = true;
486 }
487
488 /* Copy the string */
489 for (p = string; *p; p++) {
490 /* Encode unsafe characters per RFC6868, otherwise replace with SP */
491 switch (*p) {
492 case '\n':
493 icalmemory_append_string(buf, pos, buf_size, "^n");
494 break;
495
496 case '^':
497 icalmemory_append_string(buf, pos, buf_size, "^^");
498 break;
499
500 case '"':
501 icalmemory_append_string(buf, pos, buf_size, "^'");
502 break;
503
504 default:
505 if (icalmemory_is_safe_char((unsigned char)*p, quoted)) {
506 icalmemory_append_char(buf, pos, buf_size, *p);
507 } else {
508 icalmemory_append_char(buf, pos, buf_size, ' ');
509 }
510 break;
511 }
512 }
513
514 if (quoted == true) {
515 icalmemory_append_char(buf, pos, buf_size, '"');
516 }
517}
518
519void icalmemory_append_decoded_string(char **buf, char **pos,
520 size_t *buf_size, const char *string)
521{
522 const char *p;
523
524 /* Copy the string */
525 for (p = string; *p; p++) {
526 switch (*p) {
527 case '"':
528 /* Remove encapsulating quotes if necessary */
529 break;
530
531 case '^':
532 /* Decode unsafe characters per RFC6868 */
533 if (p[1] == 'n') {
534 icalmemory_append_char(buf, pos, buf_size, '\n');
535 p++;
536 } else if (p[1] == '^') {
537 icalmemory_append_char(buf, pos, buf_size, '^');
538 p++;
539 } else if (p[1] == '\'') {
540 icalmemory_append_char(buf, pos, buf_size, '"');
541 p++;
542 } else {
543 // Unknown escape sequence, copy verbatim.
544 icalmemory_append_char(buf, pos, buf_size, *p);
545 }
546 break;
547
548 default:
549 icalmemory_append_char(buf, pos, buf_size, *p);
550 }
551 }
552}
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:202
void icalmemory_free_buffer(void *buf)
Releases a buffer.
Definition icalmemory.c:353
char * icalmemory_strdup(const char *s)
Creates a duplicate of a string.
Definition icalmemory.c:240
void icalmemory_append_decoded_string(char **buf, char **pos, size_t *buf_size, const char *string)
Definition icalmemory.c:519
void icalmemory_append_string(char **buf, char **pos, size_t *buf_size, const char *string)
Appends a string to a buffer.
Definition icalmemory.c:363
void * icalmemory_resize_buffer(void *buf, size_t size)
Resizes a buffer created with icalmemory_new_buffer().
Definition icalmemory.c:334
void * icalmemory_new_buffer(size_t size)
Creates new buffer with the specified size.
Definition icalmemory.c:313
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:220
void icalmemory_append_encoded_string(char **buf, char **pos, size_t *buf_size, const char *string)
Definition icalmemory.c:476
void icalmemory_append_char(char **buf, char **pos, size_t *buf_size, char ch)
Appends a character to a buffer.
Definition icalmemory.c:404
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:293
void icalmemory_add_tmp_buffer(void *buf)
Adds an externally allocated buffer to the ring.
Definition icalmemory.c:156
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:284
void * icalmemory_tmp_buffer(size_t size)
Creates a new temporary buffer on the ring and returns it.
Definition icalmemory.c:180
#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.