Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 1 | /* Cairo - a vector graphics library with display and print output |
2 | * |
||
3 | * Copyright © 2007 Chris Wilson |
||
4 | * Copyright © 2009 Intel Corporation |
||
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 recipoolent 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 Red Hat, Inc. |
||
32 | * |
||
33 | * Contributors(s): |
||
34 | * Chris Wilson |
||
35 | */ |
||
36 | |||
37 | #include "cairoint.h" |
||
38 | |||
39 | #include "cairo-mempool-private.h" |
||
40 | #include "cairo-list-inline.h" |
||
41 | |||
42 | /* a simple buddy allocator for memory pools |
||
43 | * XXX fragmentation? use Doug Lea's malloc? |
||
44 | */ |
||
45 | |||
46 | #define BITTEST(p, n) ((p)->map[(n) >> 3] & (128 >> ((n) & 7))) |
||
47 | #define BITSET(p, n) ((p)->map[(n) >> 3] |= (128 >> ((n) & 7))) |
||
48 | #define BITCLEAR(p, n) ((p)->map[(n) >> 3] &= ~(128 >> ((n) & 7))) |
||
49 | |||
50 | static void |
||
51 | clear_bits (cairo_mempool_t *pool, size_t first, size_t last) |
||
52 | { |
||
53 | size_t i, n = last; |
||
54 | size_t first_full = (first + 7) & ~7; |
||
55 | size_t past_full = last & ~7; |
||
56 | size_t bytes; |
||
57 | |||
58 | if (n > first_full) |
||
59 | n = first_full; |
||
60 | for (i = first; i < n; i++) |
||
61 | BITCLEAR (pool, i); |
||
62 | |||
63 | if (past_full > first_full) { |
||
64 | bytes = past_full - first_full; |
||
65 | bytes = bytes >> 3; |
||
66 | memset (pool->map + (first_full >> 3), 0, bytes); |
||
67 | } |
||
68 | |||
69 | if (past_full < n) |
||
70 | past_full = n; |
||
71 | for (i = past_full; i < last; i++) |
||
72 | BITCLEAR (pool, i); |
||
73 | } |
||
74 | |||
75 | static void |
||
76 | free_bits (cairo_mempool_t *pool, size_t start, int bits, cairo_bool_t clear) |
||
77 | { |
||
78 | struct _cairo_memblock *block; |
||
79 | |||
80 | if (clear) |
||
81 | clear_bits (pool, start, start + (1 << bits)); |
||
82 | |||
83 | block = pool->blocks + start; |
||
84 | block->bits = bits; |
||
85 | |||
86 | cairo_list_add (&block->link, &pool->free[bits]); |
||
87 | |||
88 | pool->free_bytes += 1 << (bits + pool->min_bits); |
||
89 | if (bits > pool->max_free_bits) |
||
90 | pool->max_free_bits = bits; |
||
91 | } |
||
92 | |||
93 | /* Add a chunk to the free list */ |
||
94 | static void |
||
95 | free_blocks (cairo_mempool_t *pool, |
||
96 | size_t first, |
||
97 | size_t last, |
||
98 | cairo_bool_t clear) |
||
99 | { |
||
100 | size_t i, len; |
||
101 | int bits = 0; |
||
102 | |||
103 | for (i = first, len = 1; i < last; i += len) { |
||
104 | /* To avoid cost quadratic in the number of different |
||
105 | * blocks produced from this chunk of store, we have to |
||
106 | * use the size of the previous block produced from this |
||
107 | * chunk as the starting point to work out the size of the |
||
108 | * next block we can produce. If you look at the binary |
||
109 | * representation of the starting points of the blocks |
||
110 | * produced, you can see that you first of all increase the |
||
111 | * size of the blocks produced up to some maximum as the |
||
112 | * address dealt with gets offsets added on which zap out |
||
113 | * low order bits, then decrease as the low order bits of the |
||
114 | * final block produced get added in. E.g. as you go from |
||
115 | * 001 to 0111 you generate blocks |
||
116 | * of size 001 at 001 taking you to 010 |
||
117 | * of size 010 at 010 taking you to 100 |
||
118 | * of size 010 at 100 taking you to 110 |
||
119 | * of size 001 at 110 taking you to 111 |
||
120 | * So the maximum total cost of the loops below this comment |
||
121 | * is one trip from the lowest blocksize to the highest and |
||
122 | * back again. |
||
123 | */ |
||
124 | while (bits < pool->num_sizes - 1) { |
||
125 | size_t next_bits = bits + 1; |
||
126 | size_t next_len = len << 1; |
||
127 | |||
128 | if (i + next_bits > last) { |
||
129 | /* off end of chunk to be freed */ |
||
130 | break; |
||
131 | } |
||
132 | |||
133 | if (i & (next_len - 1)) /* block would not be on boundary */ |
||
134 | break; |
||
135 | |||
136 | bits = next_bits; |
||
137 | len = next_len; |
||
138 | } |
||
139 | |||
140 | do { |
||
141 | if (i + len <= last && /* off end of chunk to be freed */ |
||
142 | (i & (len - 1)) == 0) /* block would not be on boundary */ |
||
143 | break; |
||
144 | |||
145 | bits--; len >>=1; |
||
146 | } while (len); |
||
147 | |||
148 | if (len == 0) |
||
149 | break; |
||
150 | |||
151 | free_bits (pool, i, bits, clear); |
||
152 | } |
||
153 | } |
||
154 | |||
155 | static struct _cairo_memblock * |
||
156 | get_buddy (cairo_mempool_t *pool, size_t offset, int bits) |
||
157 | { |
||
158 | struct _cairo_memblock *block; |
||
159 | |||
160 | if (offset + (1 << bits) >= pool->num_blocks) |
||
161 | return NULL; /* invalid */ |
||
162 | |||
163 | if (BITTEST (pool, offset + (1 << bits) - 1)) |
||
164 | return NULL; /* buddy is allocated */ |
||
165 | |||
166 | block = pool->blocks + offset; |
||
167 | if (block->bits != bits) |
||
168 | return NULL; /* buddy is partially allocated */ |
||
169 | |||
170 | return block; |
||
171 | } |
||
172 | |||
173 | static void |
||
174 | merge_buddies (cairo_mempool_t *pool, |
||
175 | struct _cairo_memblock *block, |
||
176 | int max_bits) |
||
177 | { |
||
178 | size_t block_offset = block - pool->blocks; |
||
179 | int bits = block->bits; |
||
180 | |||
181 | while (bits < max_bits - 1) { |
||
182 | /* while you can, merge two blocks and get a legal block size */ |
||
183 | size_t buddy_offset = block_offset ^ (1 << bits); |
||
184 | |||
185 | block = get_buddy (pool, buddy_offset, bits); |
||
186 | if (block == NULL) |
||
187 | break; |
||
188 | |||
189 | cairo_list_del (&block->link); |
||
190 | |||
191 | /* Merged block starts at buddy */ |
||
192 | if (buddy_offset < block_offset) |
||
193 | block_offset = buddy_offset; |
||
194 | |||
195 | bits++; |
||
196 | } |
||
197 | |||
198 | block = pool->blocks + block_offset; |
||
199 | block->bits = bits; |
||
200 | cairo_list_add (&block->link, &pool->free[bits]); |
||
201 | |||
202 | if (bits > pool->max_free_bits) |
||
203 | pool->max_free_bits = bits; |
||
204 | } |
||
205 | |||
206 | /* attempt to merge all available buddies up to a particular size */ |
||
207 | static int |
||
208 | merge_bits (cairo_mempool_t *pool, int max_bits) |
||
209 | { |
||
210 | struct _cairo_memblock *block, *buddy, *next; |
||
211 | int bits; |
||
212 | |||
213 | for (bits = 0; bits < max_bits - 1; bits++) { |
||
214 | cairo_list_foreach_entry_safe (block, next, |
||
215 | struct _cairo_memblock, |
||
216 | &pool->free[bits], |
||
217 | link) |
||
218 | { |
||
219 | size_t buddy_offset = (block - pool->blocks) ^ (1 << bits); |
||
220 | |||
221 | buddy = get_buddy (pool, buddy_offset, bits); |
||
222 | if (buddy == NULL) |
||
223 | continue; |
||
224 | |||
225 | if (buddy == next) { |
||
226 | next = cairo_container_of (buddy->link.next, |
||
227 | struct _cairo_memblock, |
||
228 | link); |
||
229 | } |
||
230 | |||
231 | cairo_list_del (&block->link); |
||
232 | merge_buddies (pool, block, max_bits); |
||
233 | } |
||
234 | } |
||
235 | |||
236 | return pool->max_free_bits; |
||
237 | } |
||
238 | |||
239 | /* find store for 1 << bits blocks */ |
||
240 | static void * |
||
241 | buddy_malloc (cairo_mempool_t *pool, int bits) |
||
242 | { |
||
243 | size_t past, offset; |
||
244 | struct _cairo_memblock *block; |
||
245 | int b; |
||
246 | |||
247 | if (bits > pool->max_free_bits && bits > merge_bits (pool, bits)) |
||
248 | return NULL; |
||
249 | |||
250 | /* Find a list with blocks big enough on it */ |
||
251 | block = NULL; |
||
252 | for (b = bits; b <= pool->max_free_bits; b++) { |
||
253 | if (! cairo_list_is_empty (&pool->free[b])) { |
||
254 | block = cairo_list_first_entry (&pool->free[b], |
||
255 | struct _cairo_memblock, |
||
256 | link); |
||
257 | break; |
||
258 | } |
||
259 | } |
||
260 | assert (block != NULL); |
||
261 | |||
262 | cairo_list_del (&block->link); |
||
263 | |||
264 | while (cairo_list_is_empty (&pool->free[pool->max_free_bits])) { |
||
265 | if (--pool->max_free_bits == -1) |
||
266 | break; |
||
267 | } |
||
268 | |||
269 | /* Mark end of allocated area */ |
||
270 | offset = block - pool->blocks; |
||
271 | past = offset + (1 << bits); |
||
272 | BITSET (pool, past - 1); |
||
273 | block->bits = bits; |
||
274 | |||
275 | /* If we used a larger free block than we needed, free the rest */ |
||
276 | pool->free_bytes -= 1 << (b + pool->min_bits); |
||
277 | free_blocks (pool, past, offset + (1 << b), 0); |
||
278 | |||
279 | return pool->base + ((block - pool->blocks) << pool->min_bits); |
||
280 | } |
||
281 | |||
282 | cairo_status_t |
||
283 | _cairo_mempool_init (cairo_mempool_t *pool, |
||
284 | void *base, size_t bytes, |
||
285 | int min_bits, int num_sizes) |
||
286 | { |
||
287 | unsigned long tmp; |
||
288 | int num_blocks; |
||
289 | int i; |
||
290 | |||
291 | /* Align the start to an integral chunk */ |
||
292 | tmp = ((unsigned long) base) & ((1 << min_bits) - 1); |
||
293 | if (tmp) { |
||
294 | tmp = (1 << min_bits) - tmp; |
||
295 | base = (char *)base + tmp; |
||
296 | bytes -= tmp; |
||
297 | } |
||
298 | |||
299 | assert ((((unsigned long) base) & ((1 << min_bits) - 1)) == 0); |
||
300 | assert (num_sizes < ARRAY_LENGTH (pool->free)); |
||
301 | |||
302 | pool->base = base; |
||
303 | pool->free_bytes = 0; |
||
304 | pool->max_bytes = bytes; |
||
305 | pool->max_free_bits = -1; |
||
306 | |||
307 | num_blocks = bytes >> min_bits; |
||
308 | pool->blocks = calloc (num_blocks, sizeof (struct _cairo_memblock)); |
||
309 | if (pool->blocks == NULL) |
||
310 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
311 | |||
312 | pool->num_blocks = num_blocks; |
||
313 | pool->min_bits = min_bits; |
||
314 | pool->num_sizes = num_sizes; |
||
315 | |||
316 | for (i = 0; i < ARRAY_LENGTH (pool->free); i++) |
||
317 | cairo_list_init (&pool->free[i]); |
||
318 | |||
319 | pool->map = malloc ((num_blocks + 7) >> 3); |
||
320 | if (pool->map == NULL) { |
||
321 | free (pool->blocks); |
||
322 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
323 | } |
||
324 | |||
325 | memset (pool->map, -1, (num_blocks + 7) >> 3); |
||
326 | clear_bits (pool, 0, num_blocks); |
||
327 | |||
328 | /* Now add all blocks to the free list */ |
||
329 | free_blocks (pool, 0, num_blocks, 1); |
||
330 | |||
331 | return CAIRO_STATUS_SUCCESS; |
||
332 | } |
||
333 | |||
334 | void * |
||
335 | _cairo_mempool_alloc (cairo_mempool_t *pool, size_t bytes) |
||
336 | { |
||
337 | size_t size; |
||
338 | int bits; |
||
339 | |||
340 | size = 1 << pool->min_bits; |
||
341 | for (bits = 0; size < bytes; bits++) |
||
342 | size <<= 1; |
||
343 | if (bits >= pool->num_sizes) |
||
344 | return NULL; |
||
345 | |||
346 | return buddy_malloc (pool, bits); |
||
347 | } |
||
348 | |||
349 | void |
||
350 | _cairo_mempool_free (cairo_mempool_t *pool, void *storage) |
||
351 | { |
||
352 | size_t block_offset; |
||
353 | struct _cairo_memblock *block; |
||
354 | |||
355 | block_offset = ((char *)storage - pool->base) >> pool->min_bits; |
||
356 | block = pool->blocks + block_offset; |
||
357 | |||
358 | BITCLEAR (pool, block_offset + ((1 << block->bits) - 1)); |
||
359 | pool->free_bytes += 1 << (block->bits + pool->min_bits); |
||
360 | |||
361 | merge_buddies (pool, block, pool->num_sizes); |
||
362 | } |
||
363 | |||
364 | void |
||
365 | _cairo_mempool_fini (cairo_mempool_t *pool) |
||
366 | { |
||
367 | free (pool->map); |
||
368 | free (pool->blocks); |
||
369 | }><>><>=><=>>><>>>><>><>><>><>><>><>><>=>><>><>>>><>>><>><>=>><>>>><>><>>>> |