1 : /* cairo - a vector graphics library with display and print output
2 : *
3 : * Copyright © 2004 Red Hat, Inc
4 : *
5 : * This library is free software; you can redistribute it and/or
6 : * modify it either under the terms of the GNU Lesser General Public
7 : * License version 2.1 as published by the Free Software Foundation
8 : * (the "LGPL") or, at your option, under the terms of the Mozilla
9 : * Public License Version 1.1 (the "MPL"). If you do not alter this
10 : * notice, a recipient may use your version of this file under either
11 : * the MPL or the LGPL.
12 : *
13 : * You should have received a copy of the LGPL along with this library
14 : * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15 : * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16 : * You should have received a copy of the MPL along with this library
17 : * in the file COPYING-MPL-1.1
18 : *
19 : * The contents of this file are subject to the Mozilla Public License
20 : * Version 1.1 (the "License"); you may not use this file except in
21 : * compliance with the License. You may obtain a copy of the License at
22 : * http://www.mozilla.org/MPL/
23 : *
24 : * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25 : * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26 : * the specific language governing rights and limitations.
27 : *
28 : * The Original Code is the cairo graphics library.
29 : *
30 : * The Initial Developer of the Original Code is University of Southern
31 : * California.
32 : *
33 : * Contributor(s):
34 : * Kristian Høgsberg <krh@redhat.com>
35 : * Carl Worth <cworth@cworth.org>
36 : */
37 :
38 : #include "cairoint.h"
39 : #include "cairo-error-private.h"
40 :
41 : /**
42 : * _cairo_array_init:
43 : *
44 : * Initialize a new #cairo_array_t object to store objects each of size
45 : * @element_size.
46 : *
47 : * The #cairo_array_t object provides grow-by-doubling storage. It
48 : * never interprets the data passed to it, nor does it provide any
49 : * sort of callback mechanism for freeing resources held onto by
50 : * stored objects.
51 : *
52 : * When finished using the array, _cairo_array_fini() should be
53 : * called to free resources allocated during use of the array.
54 : **/
55 : void
56 288 : _cairo_array_init (cairo_array_t *array, int element_size)
57 : {
58 288 : array->size = 0;
59 288 : array->num_elements = 0;
60 288 : array->element_size = element_size;
61 288 : array->elements = NULL;
62 :
63 288 : array->is_snapshot = FALSE;
64 288 : }
65 :
66 : /**
67 : * _cairo_array_init_snapshot:
68 : * @array: A #cairo_array_t to be initialized as a snapshot
69 : * @other: The #cairo_array_t from which to create the snapshot
70 : *
71 : * Initialize @array as an immutable copy of @other. It is an error to
72 : * call an array-modifying function (other than _cairo_array_fini) on
73 : * @array after calling this function.
74 : **/
75 : void
76 0 : _cairo_array_init_snapshot (cairo_array_t *array,
77 : const cairo_array_t *other)
78 : {
79 0 : array->size = other->size;
80 0 : array->num_elements = other->num_elements;
81 0 : array->element_size = other->element_size;
82 0 : array->elements = other->elements;
83 :
84 0 : array->is_snapshot = TRUE;
85 0 : }
86 :
87 : /**
88 : * _cairo_array_fini:
89 : * @array: A #cairo_array_t
90 : *
91 : * Free all resources associated with @array. After this call, @array
92 : * should not be used again without a subsequent call to
93 : * _cairo_array_init() again first.
94 : **/
95 : void
96 241 : _cairo_array_fini (cairo_array_t *array)
97 : {
98 241 : if (array->is_snapshot)
99 0 : return;
100 :
101 241 : if (array->elements) {
102 65 : free (* array->elements);
103 65 : free (array->elements);
104 : }
105 : }
106 :
107 : /**
108 : * _cairo_array_grow_by:
109 : * @array: a #cairo_array_t
110 : *
111 : * Increase the size of @array (if needed) so that there are at least
112 : * @additional free spaces in the array. The actual size of the array
113 : * is always increased by doubling as many times as necessary.
114 : **/
115 : cairo_status_t
116 65 : _cairo_array_grow_by (cairo_array_t *array, unsigned int additional)
117 : {
118 : char *new_elements;
119 65 : unsigned int old_size = array->size;
120 65 : unsigned int required_size = array->num_elements + additional;
121 : unsigned int new_size;
122 :
123 65 : assert (! array->is_snapshot);
124 :
125 : /* check for integer overflow */
126 65 : if (required_size > INT_MAX || required_size < array->num_elements)
127 0 : return _cairo_error (CAIRO_STATUS_NO_MEMORY);
128 :
129 : if (CAIRO_INJECT_FAULT ())
130 : return _cairo_error (CAIRO_STATUS_NO_MEMORY);
131 :
132 65 : if (required_size <= old_size)
133 0 : return CAIRO_STATUS_SUCCESS;
134 :
135 65 : if (old_size == 0)
136 65 : new_size = 1;
137 : else
138 0 : new_size = old_size * 2;
139 :
140 130 : while (new_size < required_size)
141 0 : new_size = new_size * 2;
142 :
143 65 : if (array->elements == NULL) {
144 65 : array->elements = malloc (sizeof (char *));
145 65 : if (unlikely (array->elements == NULL))
146 0 : return _cairo_error (CAIRO_STATUS_NO_MEMORY);
147 :
148 65 : *array->elements = NULL;
149 : }
150 :
151 65 : array->size = new_size;
152 65 : new_elements = _cairo_realloc_ab (*array->elements,
153 : array->size, array->element_size);
154 :
155 65 : if (unlikely (new_elements == NULL)) {
156 0 : array->size = old_size;
157 0 : return _cairo_error (CAIRO_STATUS_NO_MEMORY);
158 : }
159 :
160 65 : *array->elements = new_elements;
161 :
162 65 : return CAIRO_STATUS_SUCCESS;
163 : }
164 :
165 : /**
166 : * _cairo_array_truncate:
167 : * @array: a #cairo_array_t
168 : *
169 : * Truncate size of the array to @num_elements if less than the
170 : * current size. No memory is actually freed. The stored objects
171 : * beyond @num_elements are simply "forgotten".
172 : **/
173 : void
174 0 : _cairo_array_truncate (cairo_array_t *array, unsigned int num_elements)
175 : {
176 0 : assert (! array->is_snapshot);
177 :
178 0 : if (num_elements < array->num_elements)
179 0 : array->num_elements = num_elements;
180 0 : }
181 :
182 : /**
183 : * _cairo_array_index:
184 : * @array: a #cairo_array_t
185 : * Returns: A pointer to the object stored at @index.
186 : *
187 : * If the resulting value is assigned to a pointer to an object of the same
188 : * element_size as initially passed to _cairo_array_init() then that
189 : * pointer may be used for further direct indexing with []. For
190 : * example:
191 : *
192 : * <informalexample><programlisting>
193 : * cairo_array_t array;
194 : * double *values;
195 : *
196 : * _cairo_array_init (&array, sizeof(double));
197 : * ... calls to _cairo_array_append() here ...
198 : *
199 : * values = _cairo_array_index (&array, 0);
200 : * for (i = 0; i < _cairo_array_num_elements (&array); i++)
201 : * ... use values[i] here ...
202 : * </programlisting></informalexample>
203 : **/
204 : void *
205 130 : _cairo_array_index (cairo_array_t *array, unsigned int index)
206 : {
207 : /* We allow an index of 0 for the no-elements case.
208 : * This makes for cleaner calling code which will often look like:
209 : *
210 : * elements = _cairo_array_index (array, num_elements);
211 : * for (i=0; i < num_elements; i++) {
212 : * ... use elements[i] here ...
213 : * }
214 : *
215 : * which in the num_elements==0 case gets the NULL pointer here,
216 : * but never dereferences it.
217 : */
218 130 : if (index == 0 && array->num_elements == 0)
219 65 : return NULL;
220 :
221 65 : assert (index < array->num_elements);
222 :
223 65 : return (void *) &(*array->elements)[index * array->element_size];
224 : }
225 :
226 : /**
227 : * _cairo_array_copy_element:
228 : * @array: a #cairo_array_t
229 : *
230 : * Copy a single element out of the array from index @index into the
231 : * location pointed to by @dst.
232 : **/
233 : void
234 0 : _cairo_array_copy_element (cairo_array_t *array, int index, void *dst)
235 : {
236 0 : memcpy (dst, _cairo_array_index (array, index), array->element_size);
237 0 : }
238 :
239 : /**
240 : * _cairo_array_append:
241 : * @array: a #cairo_array_t
242 : *
243 : * Append a single item onto the array by growing the array by at
244 : * least one element, then copying element_size bytes from @element
245 : * into the array. The address of the resulting object within the
246 : * array can be determined with:
247 : *
248 : * _cairo_array_index (array, _cairo_array_num_elements (array) - 1);
249 : *
250 : * Return value: %CAIRO_STATUS_SUCCESS if successful or
251 : * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the
252 : * operation.
253 : **/
254 : cairo_status_t
255 65 : _cairo_array_append (cairo_array_t *array,
256 : const void *element)
257 : {
258 65 : assert (! array->is_snapshot);
259 :
260 65 : return _cairo_array_append_multiple (array, element, 1);
261 : }
262 :
263 : /**
264 : * _cairo_array_append_multiple:
265 : * @array: a #cairo_array_t
266 : *
267 : * Append one or more items onto the array by growing the array by
268 : * @num_elements, then copying @num_elements * element_size bytes from
269 : * @elements into the array.
270 : *
271 : * Return value: %CAIRO_STATUS_SUCCESS if successful or
272 : * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the
273 : * operation.
274 : **/
275 : cairo_status_t
276 65 : _cairo_array_append_multiple (cairo_array_t *array,
277 : const void *elements,
278 : int num_elements)
279 : {
280 : cairo_status_t status;
281 : void *dest;
282 :
283 65 : assert (! array->is_snapshot);
284 :
285 65 : status = _cairo_array_allocate (array, num_elements, &dest);
286 65 : if (unlikely (status))
287 0 : return status;
288 :
289 65 : memcpy (dest, elements, num_elements * array->element_size);
290 :
291 65 : return CAIRO_STATUS_SUCCESS;
292 : }
293 :
294 : /**
295 : * _cairo_array_allocate:
296 : * @array: a #cairo_array_t
297 : *
298 : * Allocate space at the end of the array for @num_elements additional
299 : * elements, providing the address of the new memory chunk in
300 : * @elements. This memory will be unitialized, but will be accounted
301 : * for in the return value of _cairo_array_num_elements().
302 : *
303 : * Return value: %CAIRO_STATUS_SUCCESS if successful or
304 : * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the
305 : * operation.
306 : **/
307 : cairo_status_t
308 65 : _cairo_array_allocate (cairo_array_t *array,
309 : unsigned int num_elements,
310 : void **elements)
311 : {
312 : cairo_status_t status;
313 :
314 65 : assert (! array->is_snapshot);
315 :
316 65 : status = _cairo_array_grow_by (array, num_elements);
317 65 : if (unlikely (status))
318 0 : return status;
319 :
320 65 : assert (array->num_elements + num_elements <= array->size);
321 :
322 65 : *elements = &(*array->elements)[array->num_elements * array->element_size];
323 :
324 65 : array->num_elements += num_elements;
325 :
326 65 : return CAIRO_STATUS_SUCCESS;
327 : }
328 :
329 : /**
330 : * _cairo_array_num_elements:
331 : * @array: a #cairo_array_t
332 : * Returns: The number of elements stored in @array.
333 : *
334 : * This space was left intentionally blank, but gtk-doc filled it.
335 : **/
336 : int
337 0 : _cairo_array_num_elements (cairo_array_t *array)
338 : {
339 0 : return array->num_elements;
340 : }
341 :
342 : /**
343 : * _cairo_array_size:
344 : * @array: a #cairo_array_t
345 : * Returns: The number of elements for which there is currently space
346 : * allocated in @array.
347 : *
348 : * This space was left intentionally blank, but gtk-doc filled it.
349 : **/
350 : int
351 0 : _cairo_array_size (cairo_array_t *array)
352 : {
353 0 : return array->size;
354 : }
355 :
356 : /**
357 : * _cairo_user_data_array_init:
358 : * @array: a #cairo_user_data_array_t
359 : *
360 : * Initializes a #cairo_user_data_array_t structure for future
361 : * use. After initialization, the array has no keys. Call
362 : * _cairo_user_data_array_fini() to free any allocated memory
363 : * when done using the array.
364 : **/
365 : void
366 288 : _cairo_user_data_array_init (cairo_user_data_array_t *array)
367 : {
368 288 : _cairo_array_init (array, sizeof (cairo_user_data_slot_t));
369 288 : }
370 :
371 : /**
372 : * _cairo_user_data_array_fini:
373 : * @array: a #cairo_user_data_array_t
374 : *
375 : * Destroys all current keys in the user data array and deallocates
376 : * any memory allocated for the array itself.
377 : **/
378 : void
379 241 : _cairo_user_data_array_fini (cairo_user_data_array_t *array)
380 : {
381 : unsigned int num_slots;
382 :
383 241 : num_slots = array->num_elements;
384 241 : if (num_slots) {
385 : cairo_user_data_slot_t *slots;
386 :
387 65 : slots = _cairo_array_index (array, 0);
388 : do {
389 65 : if (slots->user_data != NULL && slots->destroy != NULL)
390 65 : slots->destroy (slots->user_data);
391 65 : slots++;
392 65 : } while (--num_slots);
393 : }
394 :
395 241 : _cairo_array_fini (array);
396 241 : }
397 :
398 : /**
399 : * _cairo_user_data_array_get_data:
400 : * @array: a #cairo_user_data_array_t
401 : * @key: the address of the #cairo_user_data_key_t the user data was
402 : * attached to
403 : *
404 : * Returns user data previously attached using the specified
405 : * key. If no user data has been attached with the given key this
406 : * function returns %NULL.
407 : *
408 : * Return value: the user data previously attached or %NULL.
409 : **/
410 : void *
411 0 : _cairo_user_data_array_get_data (cairo_user_data_array_t *array,
412 : const cairo_user_data_key_t *key)
413 : {
414 : int i, num_slots;
415 : cairo_user_data_slot_t *slots;
416 :
417 : /* We allow this to support degenerate objects such as cairo_surface_nil. */
418 0 : if (array == NULL)
419 0 : return NULL;
420 :
421 0 : num_slots = array->num_elements;
422 0 : slots = _cairo_array_index (array, 0);
423 0 : for (i = 0; i < num_slots; i++) {
424 0 : if (slots[i].key == key)
425 0 : return slots[i].user_data;
426 : }
427 :
428 0 : return NULL;
429 : }
430 :
431 : /**
432 : * _cairo_user_data_array_set_data:
433 : * @array: a #cairo_user_data_array_t
434 : * @key: the address of a #cairo_user_data_key_t to attach the user data to
435 : * @user_data: the user data to attach
436 : * @destroy: a #cairo_destroy_func_t which will be called when the
437 : * user data array is destroyed or when new user data is attached using the
438 : * same key.
439 : *
440 : * Attaches user data to a user data array. To remove user data,
441 : * call this function with the key that was used to set it and %NULL
442 : * for @data.
443 : *
444 : * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
445 : * slot could not be allocated for the user data.
446 : **/
447 : cairo_status_t
448 65 : _cairo_user_data_array_set_data (cairo_user_data_array_t *array,
449 : const cairo_user_data_key_t *key,
450 : void *user_data,
451 : cairo_destroy_func_t destroy)
452 : {
453 : cairo_status_t status;
454 : int i, num_slots;
455 : cairo_user_data_slot_t *slots, *slot, new_slot;
456 :
457 65 : if (user_data) {
458 65 : new_slot.key = key;
459 65 : new_slot.user_data = user_data;
460 65 : new_slot.destroy = destroy;
461 : } else {
462 0 : new_slot.key = NULL;
463 0 : new_slot.user_data = NULL;
464 0 : new_slot.destroy = NULL;
465 : }
466 :
467 65 : slot = NULL;
468 65 : num_slots = array->num_elements;
469 65 : slots = _cairo_array_index (array, 0);
470 65 : for (i = 0; i < num_slots; i++) {
471 0 : if (slots[i].key == key) {
472 0 : slot = &slots[i];
473 0 : if (slot->destroy && slot->user_data)
474 0 : slot->destroy (slot->user_data);
475 0 : break;
476 : }
477 0 : if (user_data && slots[i].user_data == NULL) {
478 0 : slot = &slots[i]; /* Have to keep searching for an exact match */
479 : }
480 : }
481 :
482 65 : if (slot) {
483 0 : *slot = new_slot;
484 0 : return CAIRO_STATUS_SUCCESS;
485 : }
486 :
487 65 : status = _cairo_array_append (array, &new_slot);
488 65 : if (unlikely (status))
489 0 : return status;
490 :
491 65 : return CAIRO_STATUS_SUCCESS;
492 : }
493 :
494 : cairo_status_t
495 0 : _cairo_user_data_array_copy (cairo_user_data_array_t *dst,
496 : cairo_user_data_array_t *src)
497 : {
498 : /* discard any existing user-data */
499 0 : if (dst->num_elements != 0) {
500 0 : _cairo_user_data_array_fini (dst);
501 0 : _cairo_user_data_array_init (dst);
502 : }
503 :
504 0 : if (src->num_elements == 0)
505 0 : return CAIRO_STATUS_SUCCESS;
506 :
507 0 : return _cairo_array_append_multiple (dst,
508 0 : _cairo_array_index (src, 0),
509 0 : src->num_elements);
510 : }
511 :
512 : void
513 0 : _cairo_user_data_array_foreach (cairo_user_data_array_t *array,
514 : void (*func) (const void *key,
515 : void *elt,
516 : void *closure),
517 : void *closure)
518 : {
519 : cairo_user_data_slot_t *slots;
520 : int i, num_slots;
521 :
522 0 : num_slots = array->num_elements;
523 0 : slots = _cairo_array_index (array, 0);
524 0 : for (i = 0; i < num_slots; i++) {
525 0 : if (slots[i].user_data != NULL)
526 0 : func (slots[i].key, slots[i].user_data, closure);
527 : }
528 0 : }
|