]> git.ipfire.org Git - thirdparty/gcc.git/blame - libobjc/sarray.c
Makefile.def (language=c++): Add check-c++0x and check-target-libmudflap-c++.
[thirdparty/gcc.git] / libobjc / sarray.c
CommitLineData
88e17b57 1/* Sparse Arrays for Objective C dispatch tables
d652f226
JJ
2 Copyright (C) 1993, 1995, 1996, 2002, 2004, 2009, 2010
3 Free Software Foundation, Inc.
88e17b57 4
38709cad 5This file is part of GCC.
88e17b57 6
38709cad 7GCC is free software; you can redistribute it and/or modify
88e17b57 8it under the terms of the GNU General Public License as published by
748086b7 9the Free Software Foundation; either version 3, or (at your option)
88e17b57
BE
10any later version.
11
38709cad 12GCC is distributed in the hope that it will be useful,
88e17b57
BE
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
748086b7
JJ
17Under Section 7 of GPL version 3, you are granted additional
18permissions described in the GCC Runtime Library Exception, version
193.1, as published by the Free Software Foundation.
20
21You should have received a copy of the GNU General Public License and
22a copy of the GCC Runtime Library Exception along with this program;
23see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
24<http://www.gnu.org/licenses/>. */
88e17b57 25
6dead247 26#include "objc-private/common.h"
5d3b14bd 27#include "objc-private/sarray.h"
718a8e53
NP
28#include "objc/runtime.h" /* For objc_malloc */
29#include "objc/thr.h" /* For objc_mutex_lock */
80e4b9e5 30#include "objc-private/module-abi-8.h"
a19fac96 31#include "objc-private/runtime.h"
88e17b57 32#include <stdio.h>
5be9cdc1 33#include <string.h> /* For memset */
5d3b14bd 34#include <assert.h> /* For assert */
88e17b57
BE
35
36int nbuckets = 0; /* !T:MUTEX */
37int nindices = 0; /* !T:MUTEX */
38int narrays = 0; /* !T:MUTEX */
39int idxsize = 0; /* !T:MUTEX */
40
40165636 41static void *first_free_data = NULL; /* !T:MUTEX */
88e17b57
BE
42
43#ifdef OBJC_SPARSE2
40165636 44const char *__objc_sparse2_id = "2 level sparse indices";
88e17b57
BE
45#endif
46
47#ifdef OBJC_SPARSE3
40165636 48const char *__objc_sparse3_id = "3 level sparse indices";
88e17b57
BE
49#endif
50
88e17b57
BE
51/* This function removes any structures left over from free operations
52 that were not safe in a multi-threaded environment. */
53void
40165636 54sarray_remove_garbage (void)
88e17b57
BE
55{
56 void **vp;
57 void *np;
58
40165636 59 objc_mutex_lock (__objc_runtime_mutex);
88e17b57
BE
60
61 vp = first_free_data;
62 first_free_data = NULL;
63
575584a9
NP
64 while (vp)
65 {
66 np = *vp;
67 objc_free (vp);
68 vp = np;
69 }
88e17b57 70
40165636 71 objc_mutex_unlock (__objc_runtime_mutex);
88e17b57
BE
72}
73
575584a9
NP
74/* Free a block of dynamically allocated memory. If we are in
75 multi-threaded mode, it is ok to free it. If not, we add it to the
76 garbage heap to be freed later. */
88e17b57 77static void
40165636 78sarray_free_garbage (void *vp)
88e17b57 79{
40165636 80 objc_mutex_lock (__objc_runtime_mutex);
88e17b57 81
575584a9
NP
82 if (__objc_runtime_threads_alive == 1)
83 {
84 objc_free (vp);
85 if (first_free_data)
86 sarray_remove_garbage ();
87 }
88 else
89 {
90 *(void **)vp = first_free_data;
91 first_free_data = vp;
92 }
93
40165636 94 objc_mutex_unlock (__objc_runtime_mutex);
88e17b57
BE
95}
96
575584a9
NP
97/* sarray_at_put copies data in such a way as to be thread reader
98 safe. */
88e17b57 99void
40165636 100sarray_at_put (struct sarray *array, sidx index, void *element)
88e17b57
BE
101{
102#ifdef OBJC_SPARSE3
40165636
RB
103 struct sindex **the_index;
104 struct sindex *new_index;
88e17b57 105#endif
40165636
RB
106 struct sbucket **the_bucket;
107 struct sbucket *new_bucket;
88e17b57
BE
108#ifdef OBJC_SPARSE3
109 size_t ioffset;
110#endif
111 size_t boffset;
112 size_t eoffset;
113#ifdef PRECOMPUTE_SELECTORS
114 union sofftype xx;
115 xx.idx = index;
116#ifdef OBJC_SPARSE3
117 ioffset = xx.off.ioffset;
118#endif
119 boffset = xx.off.boffset;
120 eoffset = xx.off.eoffset;
121#else /* not PRECOMPUTE_SELECTORS */
122#ifdef OBJC_SPARSE3
123 ioffset = index/INDEX_CAPACITY;
124 boffset = (index/BUCKET_SIZE)%INDEX_SIZE;
125 eoffset = index%BUCKET_SIZE;
126#else
127 boffset = index/BUCKET_SIZE;
128 eoffset = index%BUCKET_SIZE;
129#endif
130#endif /* not PRECOMPUTE_SELECTORS */
131
40165636 132 assert (soffset_decode (index) < array->capacity); /* Range check */
88e17b57
BE
133
134#ifdef OBJC_SPARSE3
135 the_index = &(array->indices[ioffset]);
136 the_bucket = &((*the_index)->buckets[boffset]);
137#else
138 the_bucket = &(array->buckets[boffset]);
139#endif
140
141 if ((*the_bucket)->elems[eoffset] == element)
575584a9 142 return; /* Great! we just avoided a lazy copy. */
88e17b57
BE
143
144#ifdef OBJC_SPARSE3
145
575584a9 146 /* First, perform lazy copy/allocation of index if needed. */
88e17b57 147
575584a9
NP
148 if ((*the_index) == array->empty_index)
149 {
150 /* The index was previously empty, allocate a new. */
151 new_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
152 memcpy (new_index, array->empty_index, sizeof (struct sindex));
153 new_index->version.version = array->version.version;
154 *the_index = new_index; /* Prepared for install. */
155 the_bucket = &((*the_index)->buckets[boffset]);
156
157 nindices += 1;
158 }
159 else if ((*the_index)->version.version != array->version.version)
160 {
161 /* This index must be lazy copied. */
162 struct sindex *old_index = *the_index;
163 new_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
164 memcpy (new_index, old_index, sizeof (struct sindex));
165 new_index->version.version = array->version.version;
166 *the_index = new_index; /* Prepared for install. */
167 the_bucket = &((*the_index)->buckets[boffset]);
168
169 nindices += 1;
170 }
171
88e17b57 172#endif /* OBJC_SPARSE3 */
575584a9
NP
173
174 /* Next, perform lazy allocation/copy of the bucket if needed. */
175 if ((*the_bucket) == array->empty_bucket)
176 {
177 /* The bucket was previously empty (or something like that),
178 allocate a new. This is the effect of `lazy' allocation. */
179 new_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
180 memcpy ((void *) new_bucket, (const void *) array->empty_bucket,
181 sizeof (struct sbucket));
182 new_bucket->version.version = array->version.version;
183 *the_bucket = new_bucket; /* Prepared for install. */
184
185 nbuckets += 1;
186
187 }
188 else if ((*the_bucket)->version.version != array->version.version)
189 {
190 /* Perform lazy copy. */
191 struct sbucket *old_bucket = *the_bucket;
192 new_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
193 memcpy (new_bucket, old_bucket, sizeof (struct sbucket));
194 new_bucket->version.version = array->version.version;
195 *the_bucket = new_bucket; /* Prepared for install. */
196
197 nbuckets += 1;
198 }
88e17b57
BE
199 (*the_bucket)->elems[eoffset] = element;
200}
201
202void
40165636 203sarray_at_put_safe (struct sarray *array, sidx index, void *element)
88e17b57 204{
40165636
RB
205 if (soffset_decode (index) >= array->capacity)
206 sarray_realloc (array, soffset_decode (index) + 1);
207 sarray_at_put (array, index, element);
88e17b57
BE
208}
209
40165636
RB
210struct sarray *
211sarray_new (int size, void *default_element)
88e17b57 212{
40165636 213 struct sarray *arr;
88e17b57 214#ifdef OBJC_SPARSE3
40165636
RB
215 size_t num_indices = ((size - 1)/(INDEX_CAPACITY)) + 1;
216 struct sindex **new_indices;
88e17b57 217#else /* OBJC_SPARSE2 */
40165636
RB
218 size_t num_indices = ((size - 1)/BUCKET_SIZE) + 1;
219 struct sbucket **new_buckets;
88e17b57 220#endif
8f8c44cb 221 size_t counter;
88e17b57 222
40165636 223 assert (size > 0);
88e17b57 224
575584a9 225 /* Allocate core array. */
40165636 226 arr = (struct sarray *) objc_malloc (sizeof (struct sarray));
88e17b57
BE
227 arr->version.version = 0;
228
575584a9 229 /* Initialize members. */
88e17b57
BE
230#ifdef OBJC_SPARSE3
231 arr->capacity = num_indices*INDEX_CAPACITY;
40165636
RB
232 new_indices = (struct sindex **)
233 objc_malloc (sizeof (struct sindex *) * num_indices);
575584a9 234
40165636 235 arr->empty_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
88e17b57
BE
236 arr->empty_index->version.version = 0;
237
238 narrays += 1;
239 idxsize += num_indices;
240 nindices += 1;
241
242#else /* OBJC_SPARSE2 */
243 arr->capacity = num_indices*BUCKET_SIZE;
40165636
RB
244 new_buckets = (struct sbucket **)
245 objc_malloc (sizeof (struct sbucket *) * num_indices);
88e17b57
BE
246
247 narrays += 1;
248 idxsize += num_indices;
249
250#endif
251
40165636 252 arr->empty_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
88e17b57
BE
253 arr->empty_bucket->version.version = 0;
254
255 nbuckets += 1;
256
257 arr->ref_count = 1;
40165636 258 arr->is_copy_of = (struct sarray *) 0;
88e17b57 259
40165636 260 for (counter = 0; counter < BUCKET_SIZE; counter++)
88e17b57
BE
261 arr->empty_bucket->elems[counter] = default_element;
262
263#ifdef OBJC_SPARSE3
40165636 264 for (counter = 0; counter < INDEX_SIZE; counter++)
88e17b57
BE
265 arr->empty_index->buckets[counter] = arr->empty_bucket;
266
40165636 267 for (counter = 0; counter < num_indices; counter++)
88e17b57
BE
268 new_indices[counter] = arr->empty_index;
269
270#else /* OBJC_SPARSE2 */
271
40165636 272 for (counter = 0; counter < num_indices; counter++)
88e17b57
BE
273 new_buckets[counter] = arr->empty_bucket;
274
275#endif
276
277#ifdef OBJC_SPARSE3
278 arr->indices = new_indices;
279#else /* OBJC_SPARSE2 */
280 arr->buckets = new_buckets;
281#endif
282
283 return arr;
284}
285\f
286
575584a9
NP
287/* Reallocate the sparse array to hold `newsize' entries Note: We
288 really allocate and then free. We have to do this to ensure that
289 any concurrent readers notice the update. */
88e17b57 290void
40165636 291sarray_realloc (struct sarray *array, int newsize)
88e17b57
BE
292{
293#ifdef OBJC_SPARSE3
40165636
RB
294 size_t old_max_index = (array->capacity - 1)/INDEX_CAPACITY;
295 size_t new_max_index = ((newsize - 1)/INDEX_CAPACITY);
296 size_t rounded_size = (new_max_index + 1) * INDEX_CAPACITY;
88e17b57 297
40165636
RB
298 struct sindex **new_indices;
299 struct sindex **old_indices;
88e17b57
BE
300
301#else /* OBJC_SPARSE2 */
40165636
RB
302 size_t old_max_index = (array->capacity - 1)/BUCKET_SIZE;
303 size_t new_max_index = ((newsize - 1)/BUCKET_SIZE);
304 size_t rounded_size = (new_max_index + 1) * BUCKET_SIZE;
88e17b57 305
40165636
RB
306 struct sbucket **new_buckets;
307 struct sbucket **old_buckets;
88e17b57
BE
308
309#endif
310
8f8c44cb 311 size_t counter;
88e17b57 312
40165636 313 assert (newsize > 0);
88e17b57 314
575584a9 315 /* The size is the same, just ignore the request. */
40165636 316 if (rounded_size <= array->capacity)
88e17b57
BE
317 return;
318
40165636 319 assert (array->ref_count == 1); /* stop if lazy copied... */
88e17b57 320
575584a9
NP
321 /* We are asked to extend the array -- allocate new bucket table,
322 and insert empty_bucket in newly allocated places. */
40165636 323 if (rounded_size > array->capacity)
88e17b57 324 {
88e17b57
BE
325#ifdef OBJC_SPARSE3
326 new_max_index += 4;
40165636 327 rounded_size = (new_max_index + 1) * INDEX_CAPACITY;
88e17b57
BE
328#else /* OBJC_SPARSE2 */
329 new_max_index += 4;
40165636 330 rounded_size = (new_max_index + 1) * BUCKET_SIZE;
88e17b57
BE
331#endif
332
575584a9 333 /* Update capacity. */
88e17b57
BE
334 array->capacity = rounded_size;
335
336#ifdef OBJC_SPARSE3
575584a9 337 /* Alloc to force re-read by any concurrent readers. */
88e17b57 338 old_indices = array->indices;
40165636
RB
339 new_indices = (struct sindex **)
340 objc_malloc ((new_max_index + 1) * sizeof (struct sindex *));
88e17b57
BE
341#else /* OBJC_SPARSE2 */
342 old_buckets = array->buckets;
40165636
RB
343 new_buckets = (struct sbucket **)
344 objc_malloc ((new_max_index + 1) * sizeof (struct sbucket *));
88e17b57
BE
345#endif
346
575584a9
NP
347 /* Copy buckets below old_max_index (they are still valid). */
348 for (counter = 0; counter <= old_max_index; counter++ )
349 {
88e17b57 350#ifdef OBJC_SPARSE3
575584a9 351 new_indices[counter] = old_indices[counter];
88e17b57 352#else /* OBJC_SPARSE2 */
575584a9 353 new_buckets[counter] = old_buckets[counter];
88e17b57 354#endif
575584a9 355 }
88e17b57
BE
356
357#ifdef OBJC_SPARSE3
575584a9 358 /* Reset entries above old_max_index to empty_bucket. */
40165636 359 for (counter = old_max_index + 1; counter <= new_max_index; counter++)
88e17b57
BE
360 new_indices[counter] = array->empty_index;
361#else /* OBJC_SPARSE2 */
575584a9 362 /* Reset entries above old_max_index to empty_bucket. */
40165636 363 for (counter = old_max_index + 1; counter <= new_max_index; counter++)
88e17b57
BE
364 new_buckets[counter] = array->empty_bucket;
365#endif
366
367#ifdef OBJC_SPARSE3
575584a9 368 /* Install the new indices. */
88e17b57
BE
369 array->indices = new_indices;
370#else /* OBJC_SPARSE2 */
371 array->buckets = new_buckets;
372#endif
373
374#ifdef OBJC_SPARSE3
575584a9 375 /* Free the old indices. */
40165636 376 sarray_free_garbage (old_indices);
88e17b57 377#else /* OBJC_SPARSE2 */
40165636 378 sarray_free_garbage (old_buckets);
88e17b57
BE
379#endif
380
381 idxsize += (new_max_index-old_max_index);
382 return;
383 }
384}
385\f
386
387/* Free a sparse array allocated with sarray_new */
88e17b57 388void
40165636 389sarray_free (struct sarray *array) {
88e17b57 390#ifdef OBJC_SPARSE3
40165636
RB
391 size_t old_max_index = (array->capacity - 1)/INDEX_CAPACITY;
392 struct sindex **old_indices;
88e17b57 393#else
40165636
RB
394 size_t old_max_index = (array->capacity - 1)/BUCKET_SIZE;
395 struct sbucket **old_buckets;
88e17b57 396#endif
8f8c44cb 397 size_t counter = 0;
88e17b57 398
40165636 399 assert (array->ref_count != 0); /* Freed multiple times!!! */
88e17b57 400
40165636 401 if (--(array->ref_count) != 0) /* There exists copies of me */
88e17b57
BE
402 return;
403
404#ifdef OBJC_SPARSE3
405 old_indices = array->indices;
406#else
407 old_buckets = array->buckets;
408#endif
88e17b57 409
575584a9
NP
410 /* Free all entries that do not point to empty_bucket. */
411 for (counter = 0; counter <= old_max_index; counter++ )
412 {
88e17b57 413#ifdef OBJC_SPARSE3
575584a9
NP
414 struct sindex *idx = old_indices[counter];
415 if ((idx != array->empty_index)
416 && (idx->version.version == array->version.version))
417 {
418 int c2;
419 for (c2 = 0; c2 < INDEX_SIZE; c2++)
420 {
421 struct sbucket *bkt = idx->buckets[c2];
422 if ((bkt != array->empty_bucket)
423 && (bkt->version.version == array->version.version))
424 {
425 sarray_free_garbage (bkt);
426 nbuckets -= 1;
427 }
428 }
429 sarray_free_garbage (idx);
430 nindices -= 1;
431 }
88e17b57 432#else /* OBJC_SPARSE2 */
575584a9
NP
433 struct sbucket *bkt = old_buckets[counter];
434 if ((bkt != array->empty_bucket)
435 && (bkt->version.version == array->version.version))
436 {
437 sarray_free_garbage (bkt);
438 nbuckets -= 1;
439 }
88e17b57 440#endif
575584a9
NP
441 }
442
88e17b57 443#ifdef OBJC_SPARSE3
575584a9
NP
444 /* Free empty_index. */
445 if (array->empty_index->version.version == array->version.version)
446 {
447 sarray_free_garbage (array->empty_index);
448 nindices -= 1;
449 }
88e17b57
BE
450#endif
451
575584a9
NP
452 /* Free empty_bucket. */
453 if (array->empty_bucket->version.version == array->version.version)
454 {
455 sarray_free_garbage (array->empty_bucket);
456 nbuckets -= 1;
457 }
40165636 458 idxsize -= (old_max_index + 1);
88e17b57 459 narrays -= 1;
575584a9 460
88e17b57 461#ifdef OBJC_SPARSE3
575584a9 462 /* Free bucket table. */
40165636 463 sarray_free_garbage (array->indices);
88e17b57 464#else
575584a9 465 /* Free bucket table. */
40165636 466 sarray_free_garbage (array->buckets);
88e17b57
BE
467#endif
468
435317e2 469 /* If this is a copy of another array, we free it (which might just
575584a9
NP
470 decrement its reference count so it will be freed when no longer
471 in use). */
b39f1868
AR
472 if (array->is_copy_of)
473 sarray_free (array->is_copy_of);
474
575584a9 475 /* Free array. */
40165636 476 sarray_free_garbage (array);
88e17b57
BE
477}
478
575584a9
NP
479/* This is a lazy copy. Only the core of the structure is actually
480 copied. */
40165636
RB
481struct sarray *
482sarray_lazy_copy (struct sarray *oarr)
88e17b57 483{
40165636 484 struct sarray *arr;
88e17b57
BE
485
486#ifdef OBJC_SPARSE3
40165636
RB
487 size_t num_indices = ((oarr->capacity - 1)/INDEX_CAPACITY) + 1;
488 struct sindex **new_indices;
88e17b57 489#else /* OBJC_SPARSE2 */
40165636
RB
490 size_t num_indices = ((oarr->capacity - 1)/BUCKET_SIZE) + 1;
491 struct sbucket **new_buckets;
88e17b57
BE
492#endif
493
575584a9 494 /* Allocate core array. */
40165636 495 arr = (struct sarray *) objc_malloc (sizeof (struct sarray)); /* !!! */
88e17b57
BE
496 arr->version.version = oarr->version.version + 1;
497#ifdef OBJC_SPARSE3
498 arr->empty_index = oarr->empty_index;
499#endif
500 arr->empty_bucket = oarr->empty_bucket;
501 arr->ref_count = 1;
502 oarr->ref_count += 1;
503 arr->is_copy_of = oarr;
504 arr->capacity = oarr->capacity;
505
506#ifdef OBJC_SPARSE3
575584a9 507 /* Copy bucket table. */
40165636
RB
508 new_indices = (struct sindex **)
509 objc_malloc (sizeof (struct sindex *) * num_indices);
510 memcpy (new_indices, oarr->indices, sizeof (struct sindex *) * num_indices);
88e17b57
BE
511 arr->indices = new_indices;
512#else
575584a9 513 /* Copy bucket table. */
40165636
RB
514 new_buckets = (struct sbucket **)
515 objc_malloc (sizeof (struct sbucket *) * num_indices);
516 memcpy (new_buckets, oarr->buckets, sizeof (struct sbucket *) * num_indices);
88e17b57
BE
517 arr->buckets = new_buckets;
518#endif
519
520 idxsize += num_indices;
521 narrays += 1;
522
523 return arr;
524}