1 /* Sparse Arrays for Objective C dispatch tables
2 Copyright (C) 1993-2022 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/>. */
25 #include "objc-private/common.h"
26 #include "objc-private/sarray.h"
27 #include "objc/runtime.h" /* For objc_malloc */
28 #include "objc/thr.h" /* For objc_mutex_lock */
29 #include "objc-private/module-abi-8.h"
30 #include "objc-private/runtime.h"
32 #include <string.h> /* For memset */
33 #include <assert.h> /* For assert */
35 int nbuckets
= 0; /* !T:MUTEX */
36 int nindices
= 0; /* !T:MUTEX */
37 int narrays
= 0; /* !T:MUTEX */
38 int idxsize
= 0; /* !T:MUTEX */
40 static void *first_free_data
= NULL
; /* !T:MUTEX */
43 const char *__objc_sparse2_id
= "2 level sparse indices";
47 const char *__objc_sparse3_id
= "3 level sparse indices";
50 /* This function removes any structures left over from free operations
51 that were not safe in a multi-threaded environment. */
53 sarray_remove_garbage (void)
58 objc_mutex_lock (__objc_runtime_mutex
);
61 first_free_data
= NULL
;
70 objc_mutex_unlock (__objc_runtime_mutex
);
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. */
77 sarray_free_garbage (void *vp
)
79 objc_mutex_lock (__objc_runtime_mutex
);
81 if (__objc_runtime_threads_alive
== 1)
85 sarray_remove_garbage ();
89 *(void **)vp
= first_free_data
;
93 objc_mutex_unlock (__objc_runtime_mutex
);
96 /* sarray_at_put copies data in such a way as to be thread reader
99 sarray_at_put (struct sarray
*array
, sidx index
, void *element
)
102 struct sindex
**the_index
;
103 struct sindex
*new_index
;
105 struct sbucket
**the_bucket
;
106 struct sbucket
*new_bucket
;
112 #ifdef PRECOMPUTE_SELECTORS
116 ioffset
= xx
.off
.ioffset
;
118 boffset
= xx
.off
.boffset
;
119 eoffset
= xx
.off
.eoffset
;
120 #else /* not PRECOMPUTE_SELECTORS */
122 ioffset
= index
/INDEX_CAPACITY
;
123 boffset
= (index
/BUCKET_SIZE
)%INDEX_SIZE
;
124 eoffset
= index
%BUCKET_SIZE
;
126 boffset
= index
/BUCKET_SIZE
;
127 eoffset
= index
%BUCKET_SIZE
;
129 #endif /* not PRECOMPUTE_SELECTORS */
131 assert (soffset_decode (index
) < array
->capacity
); /* Range check */
134 the_index
= &(array
->indices
[ioffset
]);
135 the_bucket
= &((*the_index
)->buckets
[boffset
]);
137 the_bucket
= &(array
->buckets
[boffset
]);
140 if ((*the_bucket
)->elems
[eoffset
] == element
)
141 return; /* Great! we just avoided a lazy copy. */
145 /* First, perform lazy copy/allocation of index if needed. */
147 if ((*the_index
) == array
->empty_index
)
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
]);
158 else if ((*the_index
)->version
.version
!= array
->version
.version
)
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
]);
171 #endif /* OBJC_SPARSE3 */
173 /* Next, perform lazy allocation/copy of the bucket if needed. */
174 if ((*the_bucket
) == array
->empty_bucket
)
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. */
187 else if ((*the_bucket
)->version
.version
!= array
->version
.version
)
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. */
198 (*the_bucket
)->elems
[eoffset
] = element
;
202 sarray_at_put_safe (struct sarray
*array
, sidx index
, void *element
)
204 if (soffset_decode (index
) >= array
->capacity
)
205 sarray_realloc (array
, soffset_decode (index
) + 1);
206 sarray_at_put (array
, index
, element
);
210 sarray_new (int size
, void *default_element
)
214 size_t num_indices
= ((size
- 1)/(INDEX_CAPACITY
)) + 1;
215 struct sindex
**new_indices
;
216 #else /* OBJC_SPARSE2 */
217 size_t num_indices
= ((size
- 1)/BUCKET_SIZE
) + 1;
218 struct sbucket
**new_buckets
;
224 /* Allocate core array. */
225 arr
= (struct sarray
*) objc_malloc (sizeof (struct sarray
));
226 arr
->version
.version
= 0;
228 /* Initialize members. */
230 arr
->capacity
= num_indices
*INDEX_CAPACITY
;
231 new_indices
= (struct sindex
**)
232 objc_malloc (sizeof (struct sindex
*) * num_indices
);
234 arr
->empty_index
= (struct sindex
*) objc_malloc (sizeof (struct sindex
));
235 arr
->empty_index
->version
.version
= 0;
238 idxsize
+= num_indices
;
241 #else /* OBJC_SPARSE2 */
242 arr
->capacity
= num_indices
*BUCKET_SIZE
;
243 new_buckets
= (struct sbucket
**)
244 objc_malloc (sizeof (struct sbucket
*) * num_indices
);
247 idxsize
+= num_indices
;
251 arr
->empty_bucket
= (struct sbucket
*) objc_malloc (sizeof (struct sbucket
));
252 arr
->empty_bucket
->version
.version
= 0;
257 arr
->is_copy_of
= (struct sarray
*) 0;
259 for (counter
= 0; counter
< BUCKET_SIZE
; counter
++)
260 arr
->empty_bucket
->elems
[counter
] = default_element
;
263 for (counter
= 0; counter
< INDEX_SIZE
; counter
++)
264 arr
->empty_index
->buckets
[counter
] = arr
->empty_bucket
;
266 for (counter
= 0; counter
< num_indices
; counter
++)
267 new_indices
[counter
] = arr
->empty_index
;
269 #else /* OBJC_SPARSE2 */
271 for (counter
= 0; counter
< num_indices
; counter
++)
272 new_buckets
[counter
] = arr
->empty_bucket
;
277 arr
->indices
= new_indices
;
278 #else /* OBJC_SPARSE2 */
279 arr
->buckets
= new_buckets
;
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. */
290 sarray_realloc (struct sarray
*array
, int newsize
)
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
;
297 struct sindex
**new_indices
;
298 struct sindex
**old_indices
;
300 #else /* OBJC_SPARSE2 */
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
;
305 struct sbucket
**new_buckets
;
306 struct sbucket
**old_buckets
;
312 assert (newsize
> 0);
314 /* The size is the same, just ignore the request. */
315 if (rounded_size
<= array
->capacity
)
318 assert (array
->ref_count
== 1); /* stop if lazy copied... */
320 /* We are asked to extend the array -- allocate new bucket table,
321 and insert empty_bucket in newly allocated places. */
322 if (rounded_size
> array
->capacity
)
326 rounded_size
= (new_max_index
+ 1) * INDEX_CAPACITY
;
327 #else /* OBJC_SPARSE2 */
329 rounded_size
= (new_max_index
+ 1) * BUCKET_SIZE
;
332 /* Update capacity. */
333 array
->capacity
= rounded_size
;
336 /* Alloc to force re-read by any concurrent readers. */
337 old_indices
= array
->indices
;
338 new_indices
= (struct sindex
**)
339 objc_malloc ((new_max_index
+ 1) * sizeof (struct sindex
*));
340 #else /* OBJC_SPARSE2 */
341 old_buckets
= array
->buckets
;
342 new_buckets
= (struct sbucket
**)
343 objc_malloc ((new_max_index
+ 1) * sizeof (struct sbucket
*));
346 /* Copy buckets below old_max_index (they are still valid). */
347 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 */
388 sarray_free (struct sarray
*array
) {
390 size_t old_max_index
= (array
->capacity
- 1)/INDEX_CAPACITY
;
391 struct sindex
**old_indices
;
393 size_t old_max_index
= (array
->capacity
- 1)/BUCKET_SIZE
;
394 struct sbucket
**old_buckets
;
398 assert (array
->ref_count
!= 0); /* Freed multiple times!!! */
400 if (--(array
->ref_count
) != 0) /* There exists copies of me */
404 old_indices
= array
->indices
;
406 old_buckets
= array
->buckets
;
409 /* Free all entries that do not point to empty_bucket. */
410 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
))
418 for (c2
= 0; c2
< INDEX_SIZE
; c2
++)
420 struct sbucket
*bkt
= idx
->buckets
[c2
];
421 if ((bkt
!= array
->empty_bucket
)
422 && (bkt
->version
.version
== array
->version
.version
))
424 sarray_free_garbage (bkt
);
428 sarray_free_garbage (idx
);
431 #else /* OBJC_SPARSE2 */
432 struct sbucket
*bkt
= old_buckets
[counter
];
433 if ((bkt
!= array
->empty_bucket
)
434 && (bkt
->version
.version
== array
->version
.version
))
436 sarray_free_garbage (bkt
);
443 /* Free empty_index. */
444 if (array
->empty_index
->version
.version
== array
->version
.version
)
446 sarray_free_garbage (array
->empty_index
);
451 /* Free empty_bucket. */
452 if (array
->empty_bucket
->version
.version
== array
->version
.version
)
454 sarray_free_garbage (array
->empty_bucket
);
457 idxsize
-= (old_max_index
+ 1);
461 /* Free bucket table. */
462 sarray_free_garbage (array
->indices
);
464 /* Free bucket table. */
465 sarray_free_garbage (array
->buckets
);
468 /* If this is a copy of another array, we free it (which might just
469 decrement its reference count so it will be freed when no longer
471 if (array
->is_copy_of
)
472 sarray_free (array
->is_copy_of
);
475 sarray_free_garbage (array
);
478 /* This is a lazy copy. Only the core of the structure is actually
481 sarray_lazy_copy (struct sarray
*oarr
)
486 size_t num_indices
= ((oarr
->capacity
- 1)/INDEX_CAPACITY
) + 1;
487 struct sindex
**new_indices
;
488 #else /* OBJC_SPARSE2 */
489 size_t num_indices
= ((oarr
->capacity
- 1)/BUCKET_SIZE
) + 1;
490 struct sbucket
**new_buckets
;
493 /* Allocate core array. */
494 arr
= (struct sarray
*) objc_malloc (sizeof (struct sarray
)); /* !!! */
495 arr
->version
.version
= oarr
->version
.version
+ 1;
497 arr
->empty_index
= oarr
->empty_index
;
499 arr
->empty_bucket
= oarr
->empty_bucket
;
501 oarr
->ref_count
+= 1;
502 arr
->is_copy_of
= oarr
;
503 arr
->capacity
= oarr
->capacity
;
506 /* Copy bucket table. */
507 new_indices
= (struct sindex
**)
508 objc_malloc (sizeof (struct sindex
*) * num_indices
);
509 memcpy (new_indices
, oarr
->indices
, sizeof (struct sindex
*) * num_indices
);
510 arr
->indices
= new_indices
;
512 /* Copy bucket table. */
513 new_buckets
= (struct sbucket
**)
514 objc_malloc (sizeof (struct sbucket
*) * num_indices
);
515 memcpy (new_buckets
, oarr
->buckets
, sizeof (struct sbucket
*) * num_indices
);
516 arr
->buckets
= new_buckets
;
519 idxsize
+= num_indices
;