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