1 /* Sparse Arrays for Objective C dispatch tables
2 Copyright (C) 1993, 1995, 1996, 2002, 2004, 2009, 2010
3 Free Software Foundation, Inc.
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
12 GCC is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 Under Section 7 of GPL version 3, you are granted additional
18 permissions described in the GCC Runtime Library Exception, version
19 3.1, as published by the Free Software Foundation.
21 You should have received a copy of the GNU General Public License and
22 a copy of the GCC Runtime Library Exception along with this program;
23 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
24 <http://www.gnu.org/licenses/>. */
26 #include "objc-private/common.h"
27 #include "objc-private/sarray.h"
28 #include "objc/runtime.h" /* For objc_malloc */
29 #include "objc/thr.h" /* For objc_mutex_lock */
30 #include "objc-private/module-abi-8.h"
31 #include "objc-private/runtime.h"
33 #include <string.h> /* For memset */
34 #include <assert.h> /* For assert */
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
;
71 objc_mutex_unlock (__objc_runtime_mutex
);
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. */
78 sarray_free_garbage (void *vp
)
80 objc_mutex_lock (__objc_runtime_mutex
);
82 if (__objc_runtime_threads_alive
== 1)
86 sarray_remove_garbage ();
90 *(void **)vp
= first_free_data
;
94 objc_mutex_unlock (__objc_runtime_mutex
);
97 /* sarray_at_put copies data in such a way as to be thread reader
100 sarray_at_put (struct sarray
*array
, sidx index
, void *element
)
103 struct sindex
**the_index
;
104 struct sindex
*new_index
;
106 struct sbucket
**the_bucket
;
107 struct sbucket
*new_bucket
;
113 #ifdef PRECOMPUTE_SELECTORS
117 ioffset
= xx
.off
.ioffset
;
119 boffset
= xx
.off
.boffset
;
120 eoffset
= xx
.off
.eoffset
;
121 #else /* not PRECOMPUTE_SELECTORS */
123 ioffset
= index
/INDEX_CAPACITY
;
124 boffset
= (index
/BUCKET_SIZE
)%INDEX_SIZE
;
125 eoffset
= index
%BUCKET_SIZE
;
127 boffset
= index
/BUCKET_SIZE
;
128 eoffset
= index
%BUCKET_SIZE
;
130 #endif /* not PRECOMPUTE_SELECTORS */
132 assert (soffset_decode (index
) < array
->capacity
); /* Range check */
135 the_index
= &(array
->indices
[ioffset
]);
136 the_bucket
= &((*the_index
)->buckets
[boffset
]);
138 the_bucket
= &(array
->buckets
[boffset
]);
141 if ((*the_bucket
)->elems
[eoffset
] == element
)
142 return; /* Great! we just avoided a lazy copy. */
146 /* First, perform lazy copy/allocation of index if needed. */
148 if ((*the_index
) == array
->empty_index
)
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
]);
159 else if ((*the_index
)->version
.version
!= array
->version
.version
)
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
]);
172 #endif /* OBJC_SPARSE3 */
174 /* Next, perform lazy allocation/copy of the bucket if needed. */
175 if ((*the_bucket
) == array
->empty_bucket
)
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. */
188 else if ((*the_bucket
)->version
.version
!= array
->version
.version
)
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. */
199 (*the_bucket
)->elems
[eoffset
] = element
;
203 sarray_at_put_safe (struct sarray
*array
, sidx index
, void *element
)
205 if (soffset_decode (index
) >= array
->capacity
)
206 sarray_realloc (array
, soffset_decode (index
) + 1);
207 sarray_at_put (array
, index
, element
);
211 sarray_new (int size
, void *default_element
)
215 size_t num_indices
= ((size
- 1)/(INDEX_CAPACITY
)) + 1;
216 struct sindex
**new_indices
;
217 #else /* OBJC_SPARSE2 */
218 size_t num_indices
= ((size
- 1)/BUCKET_SIZE
) + 1;
219 struct sbucket
**new_buckets
;
225 /* Allocate core array. */
226 arr
= (struct sarray
*) objc_malloc (sizeof (struct sarray
));
227 arr
->version
.version
= 0;
229 /* Initialize members. */
231 arr
->capacity
= num_indices
*INDEX_CAPACITY
;
232 new_indices
= (struct sindex
**)
233 objc_malloc (sizeof (struct sindex
*) * num_indices
);
235 arr
->empty_index
= (struct sindex
*) objc_malloc (sizeof (struct sindex
));
236 arr
->empty_index
->version
.version
= 0;
239 idxsize
+= num_indices
;
242 #else /* OBJC_SPARSE2 */
243 arr
->capacity
= num_indices
*BUCKET_SIZE
;
244 new_buckets
= (struct sbucket
**)
245 objc_malloc (sizeof (struct sbucket
*) * num_indices
);
248 idxsize
+= num_indices
;
252 arr
->empty_bucket
= (struct sbucket
*) objc_malloc (sizeof (struct sbucket
));
253 arr
->empty_bucket
->version
.version
= 0;
258 arr
->is_copy_of
= (struct sarray
*) 0;
260 for (counter
= 0; counter
< BUCKET_SIZE
; counter
++)
261 arr
->empty_bucket
->elems
[counter
] = default_element
;
264 for (counter
= 0; counter
< INDEX_SIZE
; counter
++)
265 arr
->empty_index
->buckets
[counter
] = arr
->empty_bucket
;
267 for (counter
= 0; counter
< num_indices
; counter
++)
268 new_indices
[counter
] = arr
->empty_index
;
270 #else /* OBJC_SPARSE2 */
272 for (counter
= 0; counter
< num_indices
; counter
++)
273 new_buckets
[counter
] = arr
->empty_bucket
;
278 arr
->indices
= new_indices
;
279 #else /* OBJC_SPARSE2 */
280 arr
->buckets
= new_buckets
;
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. */
291 sarray_realloc (struct sarray
*array
, int newsize
)
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
;
298 struct sindex
**new_indices
;
299 struct sindex
**old_indices
;
301 #else /* OBJC_SPARSE2 */
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
;
306 struct sbucket
**new_buckets
;
307 struct sbucket
**old_buckets
;
313 assert (newsize
> 0);
315 /* The size is the same, just ignore the request. */
316 if (rounded_size
<= array
->capacity
)
319 assert (array
->ref_count
== 1); /* stop if lazy copied... */
321 /* We are asked to extend the array -- allocate new bucket table,
322 and insert empty_bucket in newly allocated places. */
323 if (rounded_size
> array
->capacity
)
327 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
++ )
351 new_indices
[counter
] = old_indices
[counter
];
352 #else /* OBJC_SPARSE2 */
353 new_buckets
[counter
] = old_buckets
[counter
];
358 /* Reset entries above old_max_index to empty_bucket. */
359 for (counter
= old_max_index
+ 1; counter
<= new_max_index
; counter
++)
360 new_indices
[counter
] = array
->empty_index
;
361 #else /* OBJC_SPARSE2 */
362 /* Reset entries above old_max_index to empty_bucket. */
363 for (counter
= old_max_index
+ 1; counter
<= new_max_index
; counter
++)
364 new_buckets
[counter
] = array
->empty_bucket
;
368 /* Install the new indices. */
369 array
->indices
= new_indices
;
370 #else /* OBJC_SPARSE2 */
371 array
->buckets
= new_buckets
;
375 /* Free the old indices. */
376 sarray_free_garbage (old_indices
);
377 #else /* OBJC_SPARSE2 */
378 sarray_free_garbage (old_buckets
);
381 idxsize
+= (new_max_index
-old_max_index
);
387 /* 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
++ )
414 struct sindex
*idx
= old_indices
[counter
];
415 if ((idx
!= array
->empty_index
)
416 && (idx
->version
.version
== array
->version
.version
))
419 for (c2
= 0; c2
< INDEX_SIZE
; c2
++)
421 struct sbucket
*bkt
= idx
->buckets
[c2
];
422 if ((bkt
!= array
->empty_bucket
)
423 && (bkt
->version
.version
== array
->version
.version
))
425 sarray_free_garbage (bkt
);
429 sarray_free_garbage (idx
);
432 #else /* OBJC_SPARSE2 */
433 struct sbucket
*bkt
= old_buckets
[counter
];
434 if ((bkt
!= array
->empty_bucket
)
435 && (bkt
->version
.version
== array
->version
.version
))
437 sarray_free_garbage (bkt
);
444 /* Free empty_index. */
445 if (array
->empty_index
->version
.version
== array
->version
.version
)
447 sarray_free_garbage (array
->empty_index
);
452 /* Free empty_bucket. */
453 if (array
->empty_bucket
->version
.version
== array
->version
.version
)
455 sarray_free_garbage (array
->empty_bucket
);
458 idxsize
-= (old_max_index
+ 1);
462 /* Free bucket table. */
463 sarray_free_garbage (array
->indices
);
465 /* Free bucket table. */
466 sarray_free_garbage (array
->buckets
);
469 /* If this is a copy of another array, we free it (which might just
470 decrement its reference count so it will be freed when no longer
472 if (array
->is_copy_of
)
473 sarray_free (array
->is_copy_of
);
476 sarray_free_garbage (array
);
479 /* This is a lazy copy. Only the core of the structure is actually
482 sarray_lazy_copy (struct sarray
*oarr
)
487 size_t num_indices
= ((oarr
->capacity
- 1)/INDEX_CAPACITY
) + 1;
488 struct sindex
**new_indices
;
489 #else /* OBJC_SPARSE2 */
490 size_t num_indices
= ((oarr
->capacity
- 1)/BUCKET_SIZE
) + 1;
491 struct sbucket
**new_buckets
;
494 /* Allocate core array. */
495 arr
= (struct sarray
*) objc_malloc (sizeof (struct sarray
)); /* !!! */
496 arr
->version
.version
= oarr
->version
.version
+ 1;
498 arr
->empty_index
= oarr
->empty_index
;
500 arr
->empty_bucket
= oarr
->empty_bucket
;
502 oarr
->ref_count
+= 1;
503 arr
->is_copy_of
= oarr
;
504 arr
->capacity
= oarr
->capacity
;
507 /* Copy bucket table. */
508 new_indices
= (struct sindex
**)
509 objc_malloc (sizeof (struct sindex
*) * num_indices
);
510 memcpy (new_indices
, oarr
->indices
, sizeof (struct sindex
*) * num_indices
);
511 arr
->indices
= new_indices
;
513 /* Copy bucket table. */
514 new_buckets
= (struct sbucket
**)
515 objc_malloc (sizeof (struct sbucket
*) * num_indices
);
516 memcpy (new_buckets
, oarr
->buckets
, sizeof (struct sbucket
*) * num_indices
);
517 arr
->buckets
= new_buckets
;
520 idxsize
+= num_indices
;