Add PR reference to ChangeLog entry.
[official-gcc.git] / libobjc / sarray.c
blob147fd43335b79ecb0c5583c044a1c9cbcb34e454
1 /* Sparse Arrays for Objective C dispatch tables
2 Copyright (C) 1993, 1995, 1996, 2002, 2004, 2009 Free Software Foundation, Inc.
4 This file is part of GCC.
6 GCC is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
9 any later version.
11 GCC is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 Under Section 7 of GPL version 3, you are granted additional
17 permissions described in the GCC Runtime Library Exception, version
18 3.1, as published by the Free Software Foundation.
20 You should have received a copy of the GNU General Public License and
21 a copy of the GCC Runtime Library Exception along with this program;
22 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 <http://www.gnu.org/licenses/>. */
26 #include "objc/sarray.h"
27 #include "objc/runtime.h"
28 #include <stdio.h>
29 #include "assert.h"
31 int nbuckets = 0; /* !T:MUTEX */
32 int nindices = 0; /* !T:MUTEX */
33 int narrays = 0; /* !T:MUTEX */
34 int idxsize = 0; /* !T:MUTEX */
36 static void *first_free_data = NULL; /* !T:MUTEX */
38 #ifdef OBJC_SPARSE2
39 const char *__objc_sparse2_id = "2 level sparse indices";
40 #endif
42 #ifdef OBJC_SPARSE3
43 const char *__objc_sparse3_id = "3 level sparse indices";
44 #endif
46 /* This function removes any structures left over from free operations
47 that were not safe in a multi-threaded environment. */
48 void
49 sarray_remove_garbage (void)
51 void **vp;
52 void *np;
54 objc_mutex_lock (__objc_runtime_mutex);
56 vp = first_free_data;
57 first_free_data = NULL;
59 while (vp) {
60 np = *vp;
61 objc_free (vp);
62 vp = np;
65 objc_mutex_unlock (__objc_runtime_mutex);
68 /* Free a block of dynamically allocated memory. If we are in multi-threaded
69 mode, it is ok to free it. If not, we add it to the garbage heap to be
70 freed later. */
72 static void
73 sarray_free_garbage (void *vp)
75 objc_mutex_lock (__objc_runtime_mutex);
77 if (__objc_runtime_threads_alive == 1) {
78 objc_free (vp);
79 if (first_free_data)
80 sarray_remove_garbage ();
82 else {
83 *(void **)vp = first_free_data;
84 first_free_data = vp;
87 objc_mutex_unlock (__objc_runtime_mutex);
90 /* sarray_at_put : copies data in such a way as to be thread reader safe. */
91 void
92 sarray_at_put (struct sarray *array, sidx index, void *element)
94 #ifdef OBJC_SPARSE3
95 struct sindex **the_index;
96 struct sindex *new_index;
97 #endif
98 struct sbucket **the_bucket;
99 struct sbucket *new_bucket;
100 #ifdef OBJC_SPARSE3
101 size_t ioffset;
102 #endif
103 size_t boffset;
104 size_t eoffset;
105 #ifdef PRECOMPUTE_SELECTORS
106 union sofftype xx;
107 xx.idx = index;
108 #ifdef OBJC_SPARSE3
109 ioffset = xx.off.ioffset;
110 #endif
111 boffset = xx.off.boffset;
112 eoffset = xx.off.eoffset;
113 #else /* not PRECOMPUTE_SELECTORS */
114 #ifdef OBJC_SPARSE3
115 ioffset = index/INDEX_CAPACITY;
116 boffset = (index/BUCKET_SIZE)%INDEX_SIZE;
117 eoffset = index%BUCKET_SIZE;
118 #else
119 boffset = index/BUCKET_SIZE;
120 eoffset = index%BUCKET_SIZE;
121 #endif
122 #endif /* not PRECOMPUTE_SELECTORS */
124 assert (soffset_decode (index) < array->capacity); /* Range check */
126 #ifdef OBJC_SPARSE3
127 the_index = &(array->indices[ioffset]);
128 the_bucket = &((*the_index)->buckets[boffset]);
129 #else
130 the_bucket = &(array->buckets[boffset]);
131 #endif
133 if ((*the_bucket)->elems[eoffset] == element)
134 return; /* great! we just avoided a lazy copy */
136 #ifdef OBJC_SPARSE3
138 /* First, perform lazy copy/allocation of index if needed */
140 if ((*the_index) == array->empty_index) {
142 /* The index was previously empty, allocate a new */
143 new_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
144 memcpy (new_index, array->empty_index, sizeof (struct sindex));
145 new_index->version.version = array->version.version;
146 *the_index = new_index; /* Prepared for install. */
147 the_bucket = &((*the_index)->buckets[boffset]);
149 nindices += 1;
150 } else if ((*the_index)->version.version != array->version.version) {
152 /* This index must be lazy copied */
153 struct sindex *old_index = *the_index;
154 new_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
155 memcpy (new_index, old_index, sizeof (struct sindex));
156 new_index->version.version = array->version.version;
157 *the_index = new_index; /* Prepared for install. */
158 the_bucket = &((*the_index)->buckets[boffset]);
160 nindices += 1;
163 #endif /* OBJC_SPARSE3 */
165 /* next, perform lazy allocation/copy of the bucket if needed */
167 if ((*the_bucket) == array->empty_bucket) {
169 /* The bucket was previously empty (or something like that), */
170 /* allocate a new. This is the effect of `lazy' allocation */
171 new_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
172 memcpy ((void *) new_bucket, (const void *) array->empty_bucket,
173 sizeof (struct sbucket));
174 new_bucket->version.version = array->version.version;
175 *the_bucket = new_bucket; /* Prepared for install. */
177 nbuckets += 1;
179 } else if ((*the_bucket)->version.version != array->version.version) {
181 /* Perform lazy copy. */
182 struct sbucket *old_bucket = *the_bucket;
183 new_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
184 memcpy (new_bucket, old_bucket, sizeof (struct sbucket));
185 new_bucket->version.version = array->version.version;
186 *the_bucket = new_bucket; /* Prepared for install. */
188 nbuckets += 1;
191 (*the_bucket)->elems[eoffset] = element;
194 void
195 sarray_at_put_safe (struct sarray *array, sidx index, void *element)
197 if (soffset_decode (index) >= array->capacity)
198 sarray_realloc (array, soffset_decode (index) + 1);
199 sarray_at_put (array, index, element);
202 struct sarray *
203 sarray_new (int size, void *default_element)
205 struct sarray *arr;
206 #ifdef OBJC_SPARSE3
207 size_t num_indices = ((size - 1)/(INDEX_CAPACITY)) + 1;
208 struct sindex **new_indices;
209 #else /* OBJC_SPARSE2 */
210 size_t num_indices = ((size - 1)/BUCKET_SIZE) + 1;
211 struct sbucket **new_buckets;
212 #endif
213 size_t counter;
215 assert (size > 0);
217 /* Allocate core array */
218 arr = (struct sarray *) objc_malloc (sizeof (struct sarray));
219 arr->version.version = 0;
221 /* Initialize members */
222 #ifdef OBJC_SPARSE3
223 arr->capacity = num_indices*INDEX_CAPACITY;
224 new_indices = (struct sindex **)
225 objc_malloc (sizeof (struct sindex *) * num_indices);
227 arr->empty_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
228 arr->empty_index->version.version = 0;
230 narrays += 1;
231 idxsize += num_indices;
232 nindices += 1;
234 #else /* OBJC_SPARSE2 */
235 arr->capacity = num_indices*BUCKET_SIZE;
236 new_buckets = (struct sbucket **)
237 objc_malloc (sizeof (struct sbucket *) * num_indices);
239 narrays += 1;
240 idxsize += num_indices;
242 #endif
244 arr->empty_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
245 arr->empty_bucket->version.version = 0;
247 nbuckets += 1;
249 arr->ref_count = 1;
250 arr->is_copy_of = (struct sarray *) 0;
252 for (counter = 0; counter < BUCKET_SIZE; counter++)
253 arr->empty_bucket->elems[counter] = default_element;
255 #ifdef OBJC_SPARSE3
256 for (counter = 0; counter < INDEX_SIZE; counter++)
257 arr->empty_index->buckets[counter] = arr->empty_bucket;
259 for (counter = 0; counter < num_indices; counter++)
260 new_indices[counter] = arr->empty_index;
262 #else /* OBJC_SPARSE2 */
264 for (counter = 0; counter < num_indices; counter++)
265 new_buckets[counter] = arr->empty_bucket;
267 #endif
269 #ifdef OBJC_SPARSE3
270 arr->indices = new_indices;
271 #else /* OBJC_SPARSE2 */
272 arr->buckets = new_buckets;
273 #endif
275 return arr;
279 /* Reallocate the sparse array to hold `newsize' entries
280 Note: We really allocate and then free. We have to do this to ensure that
281 any concurrent readers notice the update. */
283 void
284 sarray_realloc (struct sarray *array, int newsize)
286 #ifdef OBJC_SPARSE3
287 size_t old_max_index = (array->capacity - 1)/INDEX_CAPACITY;
288 size_t new_max_index = ((newsize - 1)/INDEX_CAPACITY);
289 size_t rounded_size = (new_max_index + 1) * INDEX_CAPACITY;
291 struct sindex **new_indices;
292 struct sindex **old_indices;
294 #else /* OBJC_SPARSE2 */
295 size_t old_max_index = (array->capacity - 1)/BUCKET_SIZE;
296 size_t new_max_index = ((newsize - 1)/BUCKET_SIZE);
297 size_t rounded_size = (new_max_index + 1) * BUCKET_SIZE;
299 struct sbucket **new_buckets;
300 struct sbucket **old_buckets;
302 #endif
304 size_t counter;
306 assert (newsize > 0);
308 /* The size is the same, just ignore the request */
309 if (rounded_size <= array->capacity)
310 return;
312 assert (array->ref_count == 1); /* stop if lazy copied... */
314 /* We are asked to extend the array -- allocate new bucket table, */
315 /* and insert empty_bucket in newly allocated places. */
316 if (rounded_size > array->capacity)
319 #ifdef OBJC_SPARSE3
320 new_max_index += 4;
321 rounded_size = (new_max_index + 1) * INDEX_CAPACITY;
323 #else /* OBJC_SPARSE2 */
324 new_max_index += 4;
325 rounded_size = (new_max_index + 1) * BUCKET_SIZE;
326 #endif
328 /* update capacity */
329 array->capacity = rounded_size;
331 #ifdef OBJC_SPARSE3
332 /* alloc to force re-read by any concurrent readers. */
333 old_indices = array->indices;
334 new_indices = (struct sindex **)
335 objc_malloc ((new_max_index + 1) * sizeof (struct sindex *));
336 #else /* OBJC_SPARSE2 */
337 old_buckets = array->buckets;
338 new_buckets = (struct sbucket **)
339 objc_malloc ((new_max_index + 1) * sizeof (struct sbucket *));
340 #endif
342 /* copy buckets below old_max_index (they are still valid) */
343 for (counter = 0; counter <= old_max_index; counter++ ) {
344 #ifdef OBJC_SPARSE3
345 new_indices[counter] = old_indices[counter];
346 #else /* OBJC_SPARSE2 */
347 new_buckets[counter] = old_buckets[counter];
348 #endif
351 #ifdef OBJC_SPARSE3
352 /* reset entries above old_max_index to empty_bucket */
353 for (counter = old_max_index + 1; counter <= new_max_index; counter++)
354 new_indices[counter] = array->empty_index;
355 #else /* OBJC_SPARSE2 */
356 /* reset entries above old_max_index to empty_bucket */
357 for (counter = old_max_index + 1; counter <= new_max_index; counter++)
358 new_buckets[counter] = array->empty_bucket;
359 #endif
361 #ifdef OBJC_SPARSE3
362 /* install the new indices */
363 array->indices = new_indices;
364 #else /* OBJC_SPARSE2 */
365 array->buckets = new_buckets;
366 #endif
368 #ifdef OBJC_SPARSE3
369 /* free the old indices */
370 sarray_free_garbage (old_indices);
371 #else /* OBJC_SPARSE2 */
372 sarray_free_garbage (old_buckets);
373 #endif
375 idxsize += (new_max_index-old_max_index);
376 return;
381 /* Free a sparse array allocated with sarray_new */
383 void
384 sarray_free (struct sarray *array) {
385 #ifdef OBJC_SPARSE3
386 size_t old_max_index = (array->capacity - 1)/INDEX_CAPACITY;
387 struct sindex **old_indices;
388 #else
389 size_t old_max_index = (array->capacity - 1)/BUCKET_SIZE;
390 struct sbucket **old_buckets;
391 #endif
392 size_t counter = 0;
394 assert (array->ref_count != 0); /* Freed multiple times!!! */
396 if (--(array->ref_count) != 0) /* There exists copies of me */
397 return;
399 #ifdef OBJC_SPARSE3
400 old_indices = array->indices;
401 #else
402 old_buckets = array->buckets;
403 #endif
405 /* Free all entries that do not point to empty_bucket */
406 for (counter = 0; counter <= old_max_index; counter++ ) {
407 #ifdef OBJC_SPARSE3
408 struct sindex *idx = old_indices[counter];
409 if ((idx != array->empty_index) &&
410 (idx->version.version == array->version.version)) {
411 int c2;
412 for (c2 = 0; c2 < INDEX_SIZE; c2++) {
413 struct sbucket *bkt = idx->buckets[c2];
414 if ((bkt != array->empty_bucket) &&
415 (bkt->version.version == array->version.version))
417 sarray_free_garbage (bkt);
418 nbuckets -= 1;
421 sarray_free_garbage (idx);
422 nindices -= 1;
424 #else /* OBJC_SPARSE2 */
425 struct sbucket *bkt = array->buckets[counter];
426 if ((bkt != array->empty_bucket) &&
427 (bkt->version.version == array->version.version))
429 sarray_free_garbage (bkt);
430 nbuckets -= 1;
432 #endif
435 #ifdef OBJC_SPARSE3
436 /* free empty_index */
437 if (array->empty_index->version.version == array->version.version) {
438 sarray_free_garbage (array->empty_index);
439 nindices -= 1;
441 #endif
443 /* free empty_bucket */
444 if (array->empty_bucket->version.version == array->version.version) {
445 sarray_free_garbage (array->empty_bucket);
446 nbuckets -= 1;
448 idxsize -= (old_max_index + 1);
449 narrays -= 1;
451 #ifdef OBJC_SPARSE3
452 /* free bucket table */
453 sarray_free_garbage (array->indices);
455 #else
456 /* free bucket table */
457 sarray_free_garbage (array->buckets);
459 #endif
461 /* If this is a copy of another array, we free it (which might just
462 * decrement its reference count so it will be freed when no longer in use).
464 if (array->is_copy_of)
465 sarray_free (array->is_copy_of);
467 /* free array */
468 sarray_free_garbage (array);
471 /* This is a lazy copy. Only the core of the structure is actually */
472 /* copied. */
474 struct sarray *
475 sarray_lazy_copy (struct sarray *oarr)
477 struct sarray *arr;
479 #ifdef OBJC_SPARSE3
480 size_t num_indices = ((oarr->capacity - 1)/INDEX_CAPACITY) + 1;
481 struct sindex **new_indices;
482 #else /* OBJC_SPARSE2 */
483 size_t num_indices = ((oarr->capacity - 1)/BUCKET_SIZE) + 1;
484 struct sbucket **new_buckets;
485 #endif
487 /* Allocate core array */
488 arr = (struct sarray *) objc_malloc (sizeof (struct sarray)); /* !!! */
489 arr->version.version = oarr->version.version + 1;
490 #ifdef OBJC_SPARSE3
491 arr->empty_index = oarr->empty_index;
492 #endif
493 arr->empty_bucket = oarr->empty_bucket;
494 arr->ref_count = 1;
495 oarr->ref_count += 1;
496 arr->is_copy_of = oarr;
497 arr->capacity = oarr->capacity;
499 #ifdef OBJC_SPARSE3
500 /* Copy bucket table */
501 new_indices = (struct sindex **)
502 objc_malloc (sizeof (struct sindex *) * num_indices);
503 memcpy (new_indices, oarr->indices, sizeof (struct sindex *) * num_indices);
504 arr->indices = new_indices;
505 #else
506 /* Copy bucket table */
507 new_buckets = (struct sbucket **)
508 objc_malloc (sizeof (struct sbucket *) * num_indices);
509 memcpy (new_buckets, oarr->buckets, sizeof (struct sbucket *) * num_indices);
510 arr->buckets = new_buckets;
511 #endif
513 idxsize += num_indices;
514 narrays += 1;
516 return arr;