1 /*======================================================================
2 FILE: icalmemory.c
3 CREATOR: eric 30 June 1999
4
5 Copyright (C) 2000 Eric Busboom <eric@civicknowledge.com>
6
7 This library is free software; you can redistribute it and/or modify
8 it under the terms of either:
9
10 The LGPL as published by the Free Software Foundation, version
11 2.1, available at: https://www.gnu.org/licenses/lgpl-2.1.html
12
13 Or:
14
15 The Mozilla Public License Version 2.0. You may obtain a copy of
16 the License at https://www.mozilla.org/MPL/
17
18 This library is free software; you can redistribute it and/or modify
19 it under the terms of either:
20
21 The LGPL as published by the Free Software Foundation, version
22 2.1, available at: https://www.gnu.org/licenses/lgpl-2.1.html
23
24 Or:
25
26 The Mozilla Public License Version 2.0. You may obtain a copy of
27 the License at https://www.mozilla.org/MPL/
28 ======================================================================*/
29
30 /**
31 * @file icalmemory.c
32 */
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37
38 #include "icalmemory.h"
39 #include "icalerror.h"
40
41 #include <stdlib.h>
42
43 /**
44 * @brief Determines the size of the ring buffer used for keeping track of
45 * temporary buffers.
46 */
47 #define BUFFER_RING_SIZE 2500
48
49 /**
50 * @brief Determines the minimal size of buffers in the ring that are created
51 * with icalmemory_tmp_buffer().
52 */
53 #define MIN_BUFFER_SIZE 200
54
55 /* HACK. Not threadsafe */
56
57 typedef struct
58 {
59 int pos;
60 void *ring[BUFFER_RING_SIZE];
61 } buffer_ring;
62
63 #if !defined(HAVE_PTHREAD)
64 /**
65 * @private
66 */
67 static buffer_ring *global_buffer_ring = 0;
68 #endif
69
70 /**
71 * @private
72 *
73 * Get rid of this buffer ring.
74 */
icalmemory_free_ring_byval(buffer_ring * br)75 static void icalmemory_free_ring_byval(buffer_ring * br)
76 {
77 int i;
78
79 for (i = 0; i < BUFFER_RING_SIZE; i++) {
80 if (br->ring[i] != 0) {
81 free(br->ring[i]);
82 }
83 }
84 free(br);
85 }
86
87 #if defined(HAVE_PTHREAD)
88 #include <pthread.h>
89
90 static pthread_key_t ring_key;
91 static pthread_once_t ring_key_once = PTHREAD_ONCE_INIT;
92
ring_destroy(void * buf)93 static void ring_destroy(void *buf)
94 {
95 if (buf)
96 icalmemory_free_ring_byval((buffer_ring *) buf);
97
98 pthread_setspecific(ring_key, NULL);
99 }
100
ring_key_alloc(void)101 static void ring_key_alloc(void)
102 {
103 pthread_key_create(&ring_key, ring_destroy);
104 }
105
106 #endif
107
108 #if 0
109 /*keep for historical sake*/
110 static void icalmemory_free_tmp_buffer(void *buf)
111 {
112 if (buf == 0) {
113 return;
114 }
115
116 free(buf);
117 }
118
119 #endif
120
121 /**
122 * @private
123 */
buffer_ring_new(void)124 static buffer_ring *buffer_ring_new(void)
125 {
126 buffer_ring *br;
127 int i;
128
129 br = (buffer_ring *) malloc(sizeof(buffer_ring));
130
131 for (i = 0; i < BUFFER_RING_SIZE; i++) {
132 br->ring[i] = 0;
133 }
134 br->pos = 0;
135 return (br);
136 }
137
138 #if defined(HAVE_PTHREAD)
139 /**
140 * @private
141 */
get_buffer_ring_pthread(void)142 static buffer_ring *get_buffer_ring_pthread(void)
143 {
144 buffer_ring *br;
145
146 pthread_once(&ring_key_once, ring_key_alloc);
147
148 br = pthread_getspecific(ring_key);
149
150 if (!br) {
151 br = buffer_ring_new();
152 pthread_setspecific(ring_key, br);
153 }
154 return (br);
155 }
156
157 #else
158 /**
159 * @private
160 *
161 * Get buffer ring via a single global for a non-threaded program
162 */
get_buffer_ring_global(void)163 static buffer_ring *get_buffer_ring_global(void)
164 {
165 if (global_buffer_ring == 0) {
166 global_buffer_ring = buffer_ring_new();
167 }
168 return (global_buffer_ring);
169 }
170
171 #endif
172
173 /**
174 * @private
175 */
get_buffer_ring(void)176 static buffer_ring *get_buffer_ring(void)
177 {
178 #if defined(HAVE_PTHREAD)
179 return (get_buffer_ring_pthread());
180 #else
181 return get_buffer_ring_global();
182 #endif
183 }
184
185 /* Add an existing buffer to the buffer ring */
icalmemory_add_tmp_buffer(void * buf)186 void icalmemory_add_tmp_buffer(void *buf)
187 {
188 buffer_ring *br = get_buffer_ring();
189
190 /* Wrap around the ring */
191 if (++(br->pos) == BUFFER_RING_SIZE) {
192 br->pos = 0;
193 }
194
195 /* Free buffers as their slots are overwritten */
196 if (br->ring[br->pos] != 0) {
197 free(br->ring[br->pos]);
198 }
199
200 /* Assign the buffer to a slot */
201 br->ring[br->pos] = buf;
202 }
203
204 /*
205 * Create a new temporary buffer on the ring. Libical owns these and
206 * will deallocate them.
207 */
208
icalmemory_tmp_buffer(size_t size)209 void *icalmemory_tmp_buffer(size_t size)
210 {
211 char *buf;
212
213 if (size < MIN_BUFFER_SIZE) {
214 size = MIN_BUFFER_SIZE;
215 }
216
217 buf = (void *)malloc(size);
218
219 if (buf == 0) {
220 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
221 return 0;
222 }
223
224 memset(buf, 0, size);
225
226 icalmemory_add_tmp_buffer(buf);
227
228 return buf;
229 }
230
icalmemory_free_ring()231 void icalmemory_free_ring()
232 {
233 buffer_ring *br;
234
235 br = get_buffer_ring();
236
237 icalmemory_free_ring_byval(br);
238 #if defined(HAVE_PTHREAD)
239 pthread_setspecific(ring_key, 0);
240 #else
241 global_buffer_ring = 0;
242 #endif
243 }
244
245 /* Like strdup, but the buffer is on the ring. */
icalmemory_tmp_copy(const char * str)246 char *icalmemory_tmp_copy(const char *str)
247 {
248 char *b = icalmemory_tmp_buffer(strlen(str) + 1);
249
250 strcpy(b, str);
251
252 return b;
253 }
254
icalmemory_strdup(const char * s)255 char *icalmemory_strdup(const char *s)
256 {
257 return strdup(s);
258 }
259
260 /*
261 * These buffer routines create memory the old fashioned way -- so the
262 * caller will have to deallocate the new memory
263 */
264
icalmemory_new_buffer(size_t size)265 void *icalmemory_new_buffer(size_t size)
266 {
267 void *b = malloc(size);
268
269 if (b == 0) {
270 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
271 return 0;
272 }
273
274 memset(b, 0, size);
275
276 return b;
277 }
278
icalmemory_resize_buffer(void * buf,size_t size)279 void *icalmemory_resize_buffer(void *buf, size_t size)
280 {
281 void *b = realloc(buf, size);
282
283 if (b == 0) {
284 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
285 return 0;
286 }
287
288 return b;
289 }
290
icalmemory_free_buffer(void * buf)291 void icalmemory_free_buffer(void *buf)
292 {
293 free(buf);
294 }
295
icalmemory_append_string(char ** buf,char ** pos,size_t * buf_size,const char * string)296 void icalmemory_append_string(char **buf, char **pos, size_t *buf_size, const char *string)
297 {
298 char *new_buf;
299 char *new_pos;
300
301 size_t data_length, final_length, string_length;
302
303 #if !defined(ICAL_NO_INTERNAL_DEBUG)
304 icalerror_check_arg_rv((buf != 0), "buf");
305 icalerror_check_arg_rv((*buf != 0), "*buf");
306 icalerror_check_arg_rv((pos != 0), "pos");
307 icalerror_check_arg_rv((*pos != 0), "*pos");
308 icalerror_check_arg_rv((buf_size != 0), "buf_size");
309 icalerror_check_arg_rv((*buf_size != 0), "*buf_size");
310 icalerror_check_arg_rv((string != 0), "string");
311 #endif
312
313 string_length = strlen(string);
314 data_length = (size_t) * pos - (size_t) * buf;
315 final_length = data_length + string_length;
316
317 if (final_length >= (size_t) * buf_size) {
318
319 *buf_size = (*buf_size) * 2 + final_length;
320
321 new_buf = realloc(*buf, *buf_size);
322
323 new_pos = (void *)((size_t) new_buf + data_length);
324
325 *pos = new_pos;
326 *buf = new_buf;
327 }
328
329 strcpy(*pos, string);
330
331 *pos += string_length;
332 }
333
icalmemory_append_char(char ** buf,char ** pos,size_t * buf_size,char ch)334 void icalmemory_append_char(char **buf, char **pos, size_t *buf_size, char ch)
335 {
336 char *new_buf;
337 char *new_pos;
338
339 size_t data_length, final_length;
340
341 #if !defined(ICAL_NO_INTERNAL_DEBUG)
342 icalerror_check_arg_rv((buf != 0), "buf");
343 icalerror_check_arg_rv((*buf != 0), "*buf");
344 icalerror_check_arg_rv((pos != 0), "pos");
345 icalerror_check_arg_rv((*pos != 0), "*pos");
346 icalerror_check_arg_rv((buf_size != 0), "buf_size");
347 icalerror_check_arg_rv((*buf_size != 0), "*buf_size");
348 #endif
349
350 data_length = (size_t) * pos - (size_t) * buf;
351
352 final_length = data_length + 2;
353
354 if (final_length > (size_t) * buf_size) {
355
356 *buf_size = (*buf_size) * 2 + final_length + 1;
357
358 new_buf = realloc(*buf, *buf_size);
359
360 new_pos = (void *)((size_t) new_buf + data_length);
361
362 *pos = new_pos;
363 *buf = new_buf;
364 }
365
366 **pos = ch;
367 *pos += 1;
368 **pos = 0;
369 }
370