1 /*
2  * Copyright © 2006 Joonas Pihlaja
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting documentation, and
8  * that the name of the copyright holders not be used in advertising or
9  * publicity pertaining to distribution of the software without specific,
10  * written prior permission.  The copyright holders make no representations
11  * about the suitability of this software for any purpose.  It is provided "as
12  * is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20  * OF THIS SOFTWARE.
21  */
22 
23 #include "cairoint.h"
24 
25 #include "cairo-error-private.h"
26 #include "cairo-freelist-private.h"
27 
28 void
_cairo_freelist_init(cairo_freelist_t * freelist,unsigned nodesize)29 _cairo_freelist_init (cairo_freelist_t *freelist, unsigned nodesize)
30 {
31     memset (freelist, 0, sizeof (cairo_freelist_t));
32     freelist->nodesize = nodesize;
33 }
34 
35 void
_cairo_freelist_fini(cairo_freelist_t * freelist)36 _cairo_freelist_fini (cairo_freelist_t *freelist)
37 {
38     cairo_freelist_node_t *node = freelist->first_free_node;
39     while (node) {
40 	cairo_freelist_node_t *next;
41 
42 	VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next)));
43 	next = node->next;
44 
45 	free (node);
46 	node = next;
47     }
48 }
49 
50 void *
_cairo_freelist_alloc(cairo_freelist_t * freelist)51 _cairo_freelist_alloc (cairo_freelist_t *freelist)
52 {
53     if (freelist->first_free_node) {
54 	cairo_freelist_node_t *node;
55 
56 	node = freelist->first_free_node;
57 	VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next)));
58 	freelist->first_free_node = node->next;
59 	VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freelist->nodesize));
60 
61 	return node;
62     }
63 
64     return malloc (freelist->nodesize);
65 }
66 
67 void *
_cairo_freelist_calloc(cairo_freelist_t * freelist)68 _cairo_freelist_calloc (cairo_freelist_t *freelist)
69 {
70     void *node = _cairo_freelist_alloc (freelist);
71     if (node)
72 	memset (node, 0, freelist->nodesize);
73     return node;
74 }
75 
76 void
_cairo_freelist_free(cairo_freelist_t * freelist,void * voidnode)77 _cairo_freelist_free (cairo_freelist_t *freelist, void *voidnode)
78 {
79     cairo_freelist_node_t *node = voidnode;
80     if (node) {
81 	node->next = freelist->first_free_node;
82 	freelist->first_free_node = node;
83 	VG (VALGRIND_MAKE_MEM_NOACCESS (node, freelist->nodesize));
84     }
85 }
86 
87 void
_cairo_freepool_init(cairo_freepool_t * freepool,unsigned nodesize)88 _cairo_freepool_init (cairo_freepool_t *freepool, unsigned nodesize)
89 {
90     freepool->first_free_node = NULL;
91     freepool->pools = &freepool->embedded_pool;
92     freepool->freepools = NULL;
93     freepool->nodesize = nodesize;
94 
95     freepool->embedded_pool.next = NULL;
96     freepool->embedded_pool.size = sizeof (freepool->embedded_data);
97     freepool->embedded_pool.rem = sizeof (freepool->embedded_data);
98     freepool->embedded_pool.data = freepool->embedded_data;
99 
100     VG (VALGRIND_MAKE_MEM_NOACCESS (freepool->embedded_data, sizeof (freepool->embedded_data)));
101 }
102 
103 void
_cairo_freepool_fini(cairo_freepool_t * freepool)104 _cairo_freepool_fini (cairo_freepool_t *freepool)
105 {
106     cairo_freelist_pool_t *pool;
107 
108     pool = freepool->pools;
109     while (pool != &freepool->embedded_pool) {
110 	cairo_freelist_pool_t *next = pool->next;
111 	free (pool);
112 	pool = next;
113     }
114 
115     pool = freepool->freepools;
116     while (pool != NULL) {
117 	cairo_freelist_pool_t *next = pool->next;
118 	free (pool);
119 	pool = next;
120     }
121 
122     VG (VALGRIND_MAKE_MEM_NOACCESS (freepool, sizeof (freepool)));
123 }
124 
125 void *
_cairo_freepool_alloc_from_new_pool(cairo_freepool_t * freepool)126 _cairo_freepool_alloc_from_new_pool (cairo_freepool_t *freepool)
127 {
128     cairo_freelist_pool_t *pool;
129     int poolsize;
130 
131     if (freepool->freepools != NULL) {
132 	pool = freepool->freepools;
133 	freepool->freepools = pool->next;
134 
135 	poolsize = pool->size;
136     } else {
137 	if (freepool->pools != &freepool->embedded_pool)
138 	    poolsize = 2 * freepool->pools->size;
139 	else
140 	    poolsize = (128 * freepool->nodesize + 8191) & -8192;
141 
142 	pool = malloc (sizeof (cairo_freelist_pool_t) + poolsize);
143 	if (unlikely (pool == NULL))
144 	    return pool;
145 
146 	pool->size = poolsize;
147     }
148 
149     pool->next = freepool->pools;
150     freepool->pools = pool;
151 
152     pool->rem = poolsize - freepool->nodesize;
153     pool->data = (uint8_t *) (pool + 1) + freepool->nodesize;
154 
155     VG (VALGRIND_MAKE_MEM_NOACCESS (pool->data, pool->rem));
156 
157     return pool + 1;
158 }
159 
160 cairo_status_t
_cairo_freepool_alloc_array(cairo_freepool_t * freepool,int count,void ** array)161 _cairo_freepool_alloc_array (cairo_freepool_t *freepool,
162 			     int count,
163 			     void **array)
164 {
165     int i;
166 
167     for (i = 0; i < count; i++) {
168 	cairo_freelist_node_t *node;
169 
170 	node = freepool->first_free_node;
171 	if (likely (node != NULL)) {
172 	    VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next)));
173 	    freepool->first_free_node = node->next;
174 	    VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freepool->nodesize));
175 	} else {
176 	    node = _cairo_freepool_alloc_from_pool (freepool);
177 	    if (unlikely (node == NULL))
178 		goto CLEANUP;
179 	}
180 
181 	array[i] = node;
182     }
183 
184     return CAIRO_STATUS_SUCCESS;
185 
186   CLEANUP:
187     while (i--)
188 	_cairo_freepool_free (freepool, array[i]);
189 
190     return _cairo_error (CAIRO_STATUS_NO_MEMORY);
191 }
192