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