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/runtime.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 */
39 const char *__objc_sparse2_id
= "2 level sparse indices";
43 const char *__objc_sparse3_id
= "3 level sparse indices";
46 /* This function removes any structures left over from free operations
47 that were not safe in a multi-threaded environment. */
49 sarray_remove_garbage (void)
54 objc_mutex_lock (__objc_runtime_mutex
);
57 first_free_data
= NULL
;
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
73 sarray_free_garbage (void *vp
)
75 objc_mutex_lock (__objc_runtime_mutex
);
77 if (__objc_runtime_threads_alive
== 1) {
80 sarray_remove_garbage ();
83 *(void **)vp
= first_free_data
;
87 objc_mutex_unlock (__objc_runtime_mutex
);
90 /* sarray_at_put : copies data in such a way as to be thread reader safe. */
92 sarray_at_put (struct sarray
*array
, sidx index
, void *element
)
95 struct sindex
**the_index
;
96 struct sindex
*new_index
;
98 struct sbucket
**the_bucket
;
99 struct sbucket
*new_bucket
;
105 #ifdef PRECOMPUTE_SELECTORS
109 ioffset
= xx
.off
.ioffset
;
111 boffset
= xx
.off
.boffset
;
112 eoffset
= xx
.off
.eoffset
;
113 #else /* not PRECOMPUTE_SELECTORS */
115 ioffset
= index
/INDEX_CAPACITY
;
116 boffset
= (index
/BUCKET_SIZE
)%INDEX_SIZE
;
117 eoffset
= index
%BUCKET_SIZE
;
119 boffset
= index
/BUCKET_SIZE
;
120 eoffset
= index
%BUCKET_SIZE
;
122 #endif /* not PRECOMPUTE_SELECTORS */
124 assert (soffset_decode (index
) < array
->capacity
); /* Range check */
127 the_index
= &(array
->indices
[ioffset
]);
128 the_bucket
= &((*the_index
)->buckets
[boffset
]);
130 the_bucket
= &(array
->buckets
[boffset
]);
133 if ((*the_bucket
)->elems
[eoffset
] == element
)
134 return; /* great! we just avoided a lazy copy */
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
]);
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
]);
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. */
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. */
191 (*the_bucket
)->elems
[eoffset
] = element
;
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
);
203 sarray_new (int size
, void *default_element
)
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
;
217 /* Allocate core array */
218 arr
= (struct sarray
*) objc_malloc (sizeof (struct sarray
));
219 arr
->version
.version
= 0;
221 /* Initialize members */
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;
231 idxsize
+= num_indices
;
234 #else /* OBJC_SPARSE2 */
235 arr
->capacity
= num_indices
*BUCKET_SIZE
;
236 new_buckets
= (struct sbucket
**)
237 objc_malloc (sizeof (struct sbucket
*) * num_indices
);
240 idxsize
+= num_indices
;
244 arr
->empty_bucket
= (struct sbucket
*) objc_malloc (sizeof (struct sbucket
));
245 arr
->empty_bucket
->version
.version
= 0;
250 arr
->is_copy_of
= (struct sarray
*) 0;
252 for (counter
= 0; counter
< BUCKET_SIZE
; counter
++)
253 arr
->empty_bucket
->elems
[counter
] = default_element
;
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
;
270 arr
->indices
= new_indices
;
271 #else /* OBJC_SPARSE2 */
272 arr
->buckets
= new_buckets
;
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. */
284 sarray_realloc (struct sarray
*array
, int newsize
)
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
;
306 assert (newsize
> 0);
308 /* The size is the same, just ignore the request */
309 if (rounded_size
<= array
->capacity
)
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
)
321 rounded_size
= (new_max_index
+ 1) * INDEX_CAPACITY
;
323 #else /* OBJC_SPARSE2 */
325 rounded_size
= (new_max_index
+ 1) * BUCKET_SIZE
;
328 /* update capacity */
329 array
->capacity
= rounded_size
;
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
*));
342 /* copy buckets below old_max_index (they are still valid) */
343 for (counter
= 0; counter
<= old_max_index
; counter
++ ) {
345 new_indices
[counter
] = old_indices
[counter
];
346 #else /* OBJC_SPARSE2 */
347 new_buckets
[counter
] = old_buckets
[counter
];
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
;
362 /* install the new indices */
363 array
->indices
= new_indices
;
364 #else /* OBJC_SPARSE2 */
365 array
->buckets
= new_buckets
;
369 /* free the old indices */
370 sarray_free_garbage (old_indices
);
371 #else /* OBJC_SPARSE2 */
372 sarray_free_garbage (old_buckets
);
375 idxsize
+= (new_max_index
-old_max_index
);
381 /* Free a sparse array allocated with sarray_new */
384 sarray_free (struct sarray
*array
) {
386 size_t old_max_index
= (array
->capacity
- 1)/INDEX_CAPACITY
;
387 struct sindex
**old_indices
;
389 size_t old_max_index
= (array
->capacity
- 1)/BUCKET_SIZE
;
390 struct sbucket
**old_buckets
;
394 assert (array
->ref_count
!= 0); /* Freed multiple times!!! */
396 if (--(array
->ref_count
) != 0) /* There exists copies of me */
400 old_indices
= array
->indices
;
402 old_buckets
= array
->buckets
;
405 /* Free all entries that do not point to empty_bucket */
406 for (counter
= 0; counter
<= old_max_index
; counter
++ ) {
408 struct sindex
*idx
= old_indices
[counter
];
409 if ((idx
!= array
->empty_index
) &&
410 (idx
->version
.version
== array
->version
.version
)) {
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
);
421 sarray_free_garbage (idx
);
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
);
436 /* free empty_index */
437 if (array
->empty_index
->version
.version
== array
->version
.version
) {
438 sarray_free_garbage (array
->empty_index
);
443 /* free empty_bucket */
444 if (array
->empty_bucket
->version
.version
== array
->version
.version
) {
445 sarray_free_garbage (array
->empty_bucket
);
448 idxsize
-= (old_max_index
+ 1);
452 /* free bucket table */
453 sarray_free_garbage (array
->indices
);
456 /* free bucket table */
457 sarray_free_garbage (array
->buckets
);
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
);
468 sarray_free_garbage (array
);
471 /* This is a lazy copy. Only the core of the structure is actually */
475 sarray_lazy_copy (struct sarray
*oarr
)
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
;
487 /* Allocate core array */
488 arr
= (struct sarray
*) objc_malloc (sizeof (struct sarray
)); /* !!! */
489 arr
->version
.version
= oarr
->version
.version
+ 1;
491 arr
->empty_index
= oarr
->empty_index
;
493 arr
->empty_bucket
= oarr
->empty_bucket
;
495 oarr
->ref_count
+= 1;
496 arr
->is_copy_of
= oarr
;
497 arr
->capacity
= oarr
->capacity
;
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
;
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
;
513 idxsize
+= num_indices
;