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