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)
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/objc.h"
28 #include "objc/objc-api.h"
30 #include "objc/hash.h"
31 #include "objc/objc-list.h"
32 #include "objc-private/runtime.h"
36 int nbuckets = 0; /* !T:MUTEX */
37 int nindices = 0; /* !T:MUTEX */
38 int narrays = 0; /* !T:MUTEX */
39 int idxsize = 0; /* !T:MUTEX */
41 static void *first_free_data = NULL; /* !T:MUTEX */
44 const char *__objc_sparse2_id = "2 level sparse indices";
48 const char *__objc_sparse3_id = "3 level sparse indices";
51 /* This function removes any structures left over from free operations
52 that were not safe in a multi-threaded environment. */
54 sarray_remove_garbage (void)
59 objc_mutex_lock (__objc_runtime_mutex);
62 first_free_data = NULL;
70 objc_mutex_unlock (__objc_runtime_mutex);
73 /* Free a block of dynamically allocated memory. If we are in multi-threaded
74 mode, it is ok to free it. If not, we add it to the garbage heap to be
78 sarray_free_garbage (void *vp)
80 objc_mutex_lock (__objc_runtime_mutex);
82 if (__objc_runtime_threads_alive == 1) {
85 sarray_remove_garbage ();
88 *(void **)vp = first_free_data;
92 objc_mutex_unlock (__objc_runtime_mutex);
95 /* sarray_at_put : copies data in such a way as to be thread reader safe. */
97 sarray_at_put (struct sarray *array, sidx index, void *element)
100 struct sindex **the_index;
101 struct sindex *new_index;
103 struct sbucket **the_bucket;
104 struct sbucket *new_bucket;
110 #ifdef PRECOMPUTE_SELECTORS
114 ioffset = xx.off.ioffset;
116 boffset = xx.off.boffset;
117 eoffset = xx.off.eoffset;
118 #else /* not PRECOMPUTE_SELECTORS */
120 ioffset = index/INDEX_CAPACITY;
121 boffset = (index/BUCKET_SIZE)%INDEX_SIZE;
122 eoffset = index%BUCKET_SIZE;
124 boffset = index/BUCKET_SIZE;
125 eoffset = index%BUCKET_SIZE;
127 #endif /* not PRECOMPUTE_SELECTORS */
129 assert (soffset_decode (index) < array->capacity); /* Range check */
132 the_index = &(array->indices[ioffset]);
133 the_bucket = &((*the_index)->buckets[boffset]);
135 the_bucket = &(array->buckets[boffset]);
138 if ((*the_bucket)->elems[eoffset] == element)
139 return; /* great! we just avoided a lazy copy */
143 /* First, perform lazy copy/allocation of index if needed */
145 if ((*the_index) == array->empty_index) {
147 /* The index was previously empty, allocate a new */
148 new_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
149 memcpy (new_index, array->empty_index, sizeof (struct sindex));
150 new_index->version.version = array->version.version;
151 *the_index = new_index; /* Prepared for install. */
152 the_bucket = &((*the_index)->buckets[boffset]);
155 } else if ((*the_index)->version.version != array->version.version) {
157 /* This index must be lazy copied */
158 struct sindex *old_index = *the_index;
159 new_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
160 memcpy (new_index, old_index, sizeof (struct sindex));
161 new_index->version.version = array->version.version;
162 *the_index = new_index; /* Prepared for install. */
163 the_bucket = &((*the_index)->buckets[boffset]);
168 #endif /* OBJC_SPARSE3 */
170 /* next, perform lazy allocation/copy of the bucket if needed */
172 if ((*the_bucket) == array->empty_bucket) {
174 /* The bucket was previously empty (or something like that), */
175 /* allocate a new. This is the effect of `lazy' allocation */
176 new_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
177 memcpy ((void *) new_bucket, (const void *) array->empty_bucket,
178 sizeof (struct sbucket));
179 new_bucket->version.version = array->version.version;
180 *the_bucket = new_bucket; /* Prepared for install. */
184 } else if ((*the_bucket)->version.version != array->version.version) {
186 /* Perform lazy copy. */
187 struct sbucket *old_bucket = *the_bucket;
188 new_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
189 memcpy (new_bucket, old_bucket, sizeof (struct sbucket));
190 new_bucket->version.version = array->version.version;
191 *the_bucket = new_bucket; /* Prepared for install. */
196 (*the_bucket)->elems[eoffset] = element;
200 sarray_at_put_safe (struct sarray *array, sidx index, void *element)
202 if (soffset_decode (index) >= array->capacity)
203 sarray_realloc (array, soffset_decode (index) + 1);
204 sarray_at_put (array, index, element);
208 sarray_new (int size, void *default_element)
212 size_t num_indices = ((size - 1)/(INDEX_CAPACITY)) + 1;
213 struct sindex **new_indices;
214 #else /* OBJC_SPARSE2 */
215 size_t num_indices = ((size - 1)/BUCKET_SIZE) + 1;
216 struct sbucket **new_buckets;
222 /* Allocate core array */
223 arr = (struct sarray *) objc_malloc (sizeof (struct sarray));
224 arr->version.version = 0;
226 /* Initialize members */
228 arr->capacity = num_indices*INDEX_CAPACITY;
229 new_indices = (struct sindex **)
230 objc_malloc (sizeof (struct sindex *) * num_indices);
232 arr->empty_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
233 arr->empty_index->version.version = 0;
236 idxsize += num_indices;
239 #else /* OBJC_SPARSE2 */
240 arr->capacity = num_indices*BUCKET_SIZE;
241 new_buckets = (struct sbucket **)
242 objc_malloc (sizeof (struct sbucket *) * num_indices);
245 idxsize += num_indices;
249 arr->empty_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
250 arr->empty_bucket->version.version = 0;
255 arr->is_copy_of = (struct sarray *) 0;
257 for (counter = 0; counter < BUCKET_SIZE; counter++)
258 arr->empty_bucket->elems[counter] = default_element;
261 for (counter = 0; counter < INDEX_SIZE; counter++)
262 arr->empty_index->buckets[counter] = arr->empty_bucket;
264 for (counter = 0; counter < num_indices; counter++)
265 new_indices[counter] = arr->empty_index;
267 #else /* OBJC_SPARSE2 */
269 for (counter = 0; counter < num_indices; counter++)
270 new_buckets[counter] = arr->empty_bucket;
275 arr->indices = new_indices;
276 #else /* OBJC_SPARSE2 */
277 arr->buckets = new_buckets;
284 /* Reallocate the sparse array to hold `newsize' entries
285 Note: We really allocate and then free. We have to do this to ensure that
286 any concurrent readers notice the update. */
289 sarray_realloc (struct sarray *array, int newsize)
292 size_t old_max_index = (array->capacity - 1)/INDEX_CAPACITY;
293 size_t new_max_index = ((newsize - 1)/INDEX_CAPACITY);
294 size_t rounded_size = (new_max_index + 1) * INDEX_CAPACITY;
296 struct sindex **new_indices;
297 struct sindex **old_indices;
299 #else /* OBJC_SPARSE2 */
300 size_t old_max_index = (array->capacity - 1)/BUCKET_SIZE;
301 size_t new_max_index = ((newsize - 1)/BUCKET_SIZE);
302 size_t rounded_size = (new_max_index + 1) * BUCKET_SIZE;
304 struct sbucket **new_buckets;
305 struct sbucket **old_buckets;
311 assert (newsize > 0);
313 /* The size is the same, just ignore the request */
314 if (rounded_size <= array->capacity)
317 assert (array->ref_count == 1); /* stop if lazy copied... */
319 /* We are asked to extend the array -- allocate new bucket table, */
320 /* and insert empty_bucket in newly allocated places. */
321 if (rounded_size > array->capacity)
326 rounded_size = (new_max_index + 1) * INDEX_CAPACITY;
328 #else /* OBJC_SPARSE2 */
330 rounded_size = (new_max_index + 1) * BUCKET_SIZE;
333 /* update capacity */
334 array->capacity = rounded_size;
337 /* alloc to force re-read by any concurrent readers. */
338 old_indices = array->indices;
339 new_indices = (struct sindex **)
340 objc_malloc ((new_max_index + 1) * sizeof (struct sindex *));
341 #else /* OBJC_SPARSE2 */
342 old_buckets = array->buckets;
343 new_buckets = (struct sbucket **)
344 objc_malloc ((new_max_index + 1) * sizeof (struct sbucket *));
347 /* copy buckets below old_max_index (they are still valid) */
348 for (counter = 0; counter <= old_max_index; counter++ ) {
350 new_indices[counter] = old_indices[counter];
351 #else /* OBJC_SPARSE2 */
352 new_buckets[counter] = old_buckets[counter];
357 /* reset entries above old_max_index to empty_bucket */
358 for (counter = old_max_index + 1; counter <= new_max_index; counter++)
359 new_indices[counter] = array->empty_index;
360 #else /* OBJC_SPARSE2 */
361 /* reset entries above old_max_index to empty_bucket */
362 for (counter = old_max_index + 1; counter <= new_max_index; counter++)
363 new_buckets[counter] = array->empty_bucket;
367 /* install the new indices */
368 array->indices = new_indices;
369 #else /* OBJC_SPARSE2 */
370 array->buckets = new_buckets;
374 /* free the old indices */
375 sarray_free_garbage (old_indices);
376 #else /* OBJC_SPARSE2 */
377 sarray_free_garbage (old_buckets);
380 idxsize += (new_max_index-old_max_index);
386 /* Free a sparse array allocated with sarray_new */
389 sarray_free (struct sarray *array) {
391 size_t old_max_index = (array->capacity - 1)/INDEX_CAPACITY;
392 struct sindex **old_indices;
394 size_t old_max_index = (array->capacity - 1)/BUCKET_SIZE;
395 struct sbucket **old_buckets;
399 assert (array->ref_count != 0); /* Freed multiple times!!! */
401 if (--(array->ref_count) != 0) /* There exists copies of me */
405 old_indices = array->indices;
407 old_buckets = array->buckets;
410 /* Free all entries that do not point to empty_bucket */
411 for (counter = 0; counter <= old_max_index; counter++ ) {
413 struct sindex *idx = old_indices[counter];
414 if ((idx != array->empty_index) &&
415 (idx->version.version == array->version.version)) {
417 for (c2 = 0; c2 < INDEX_SIZE; c2++) {
418 struct sbucket *bkt = idx->buckets[c2];
419 if ((bkt != array->empty_bucket) &&
420 (bkt->version.version == array->version.version))
422 sarray_free_garbage (bkt);
426 sarray_free_garbage (idx);
429 #else /* OBJC_SPARSE2 */
430 struct sbucket *bkt = old_buckets[counter];
431 if ((bkt != array->empty_bucket) &&
432 (bkt->version.version == array->version.version))
434 sarray_free_garbage (bkt);
441 /* free empty_index */
442 if (array->empty_index->version.version == array->version.version) {
443 sarray_free_garbage (array->empty_index);
448 /* free empty_bucket */
449 if (array->empty_bucket->version.version == array->version.version) {
450 sarray_free_garbage (array->empty_bucket);
453 idxsize -= (old_max_index + 1);
457 /* free bucket table */
458 sarray_free_garbage (array->indices);
461 /* free bucket table */
462 sarray_free_garbage (array->buckets);
466 /* If this is a copy of another array, we free it (which might just
467 * decrement its reference count so it will be freed when no longer in use).
469 if (array->is_copy_of)
470 sarray_free (array->is_copy_of);
473 sarray_free_garbage (array);
476 /* This is a lazy copy. Only the core of the structure is actually */
480 sarray_lazy_copy (struct sarray *oarr)
485 size_t num_indices = ((oarr->capacity - 1)/INDEX_CAPACITY) + 1;
486 struct sindex **new_indices;
487 #else /* OBJC_SPARSE2 */
488 size_t num_indices = ((oarr->capacity - 1)/BUCKET_SIZE) + 1;
489 struct sbucket **new_buckets;
492 /* Allocate core array */
493 arr = (struct sarray *) objc_malloc (sizeof (struct sarray)); /* !!! */
494 arr->version.version = oarr->version.version + 1;
496 arr->empty_index = oarr->empty_index;
498 arr->empty_bucket = oarr->empty_bucket;
500 oarr->ref_count += 1;
501 arr->is_copy_of = oarr;
502 arr->capacity = oarr->capacity;
505 /* Copy bucket table */
506 new_indices = (struct sindex **)
507 objc_malloc (sizeof (struct sindex *) * num_indices);
508 memcpy (new_indices, oarr->indices, sizeof (struct sindex *) * num_indices);
509 arr->indices = new_indices;
511 /* Copy bucket table */
512 new_buckets = (struct sbucket **)
513 objc_malloc (sizeof (struct sbucket *) * num_indices);
514 memcpy (new_buckets, oarr->buckets, sizeof (struct sbucket *) * num_indices);
515 arr->buckets = new_buckets;
518 idxsize += num_indices;