1 /* Sparse Arrays for Objective C dispatch tables
2 Copyright (C) 1993, 1995, 1996, 2002, 2004 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 2, 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 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING. If not, write to
18 the Free Software Foundation, 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
21 /* As a special exception, if you link this library with files
22 compiled with GCC to produce an executable, this does not cause
23 the resulting executable to be covered by the GNU General Public License.
24 This exception does not however invalidate any other reasons why
25 the executable file might be covered by the GNU General Public License. */
27 #include "objc/sarray.h"
28 #include "objc/runtime.h"
32 int nbuckets
= 0; /* !T:MUTEX */
33 int nindices
= 0; /* !T:MUTEX */
34 int narrays
= 0; /* !T:MUTEX */
35 int idxsize
= 0; /* !T:MUTEX */
37 static void *first_free_data
= NULL
; /* !T:MUTEX */
40 const char *__objc_sparse2_id
= "2 level sparse indices";
44 const char *__objc_sparse3_id
= "3 level sparse indices";
47 /* This function removes any structures left over from free operations
48 that were not safe in a multi-threaded environment. */
50 sarray_remove_garbage (void)
55 objc_mutex_lock (__objc_runtime_mutex
);
58 first_free_data
= NULL
;
66 objc_mutex_unlock (__objc_runtime_mutex
);
69 /* Free a block of dynamically allocated memory. If we are in multi-threaded
70 mode, it is ok to free it. If not, we add it to the garbage heap to be
74 sarray_free_garbage (void *vp
)
76 objc_mutex_lock (__objc_runtime_mutex
);
78 if (__objc_runtime_threads_alive
== 1) {
81 sarray_remove_garbage ();
84 *(void **)vp
= first_free_data
;
88 objc_mutex_unlock (__objc_runtime_mutex
);
91 /* sarray_at_put : copies data in such a way as to be thread reader safe. */
93 sarray_at_put (struct sarray
*array
, sidx index
, void *element
)
96 struct sindex
**the_index
;
97 struct sindex
*new_index
;
99 struct sbucket
**the_bucket
;
100 struct sbucket
*new_bucket
;
106 #ifdef PRECOMPUTE_SELECTORS
110 ioffset
= xx
.off
.ioffset
;
112 boffset
= xx
.off
.boffset
;
113 eoffset
= xx
.off
.eoffset
;
114 #else /* not PRECOMPUTE_SELECTORS */
116 ioffset
= index
/INDEX_CAPACITY
;
117 boffset
= (index
/BUCKET_SIZE
)%INDEX_SIZE
;
118 eoffset
= index
%BUCKET_SIZE
;
120 boffset
= index
/BUCKET_SIZE
;
121 eoffset
= index
%BUCKET_SIZE
;
123 #endif /* not PRECOMPUTE_SELECTORS */
125 assert (soffset_decode (index
) < array
->capacity
); /* Range check */
128 the_index
= &(array
->indices
[ioffset
]);
129 the_bucket
= &((*the_index
)->buckets
[boffset
]);
131 the_bucket
= &(array
->buckets
[boffset
]);
134 if ((*the_bucket
)->elems
[eoffset
] == element
)
135 return; /* great! we just avoided a lazy copy */
139 /* First, perform lazy copy/allocation of index if needed */
141 if ((*the_index
) == array
->empty_index
) {
143 /* The index was previously empty, allocate a new */
144 new_index
= (struct sindex
*) objc_malloc (sizeof (struct sindex
));
145 memcpy (new_index
, array
->empty_index
, sizeof (struct sindex
));
146 new_index
->version
.version
= array
->version
.version
;
147 *the_index
= new_index
; /* Prepared for install. */
148 the_bucket
= &((*the_index
)->buckets
[boffset
]);
151 } else if ((*the_index
)->version
.version
!= array
->version
.version
) {
153 /* This index must be lazy copied */
154 struct sindex
*old_index
= *the_index
;
155 new_index
= (struct sindex
*) objc_malloc (sizeof (struct sindex
));
156 memcpy (new_index
, old_index
, sizeof (struct sindex
));
157 new_index
->version
.version
= array
->version
.version
;
158 *the_index
= new_index
; /* Prepared for install. */
159 the_bucket
= &((*the_index
)->buckets
[boffset
]);
164 #endif /* OBJC_SPARSE3 */
166 /* next, perform lazy allocation/copy of the bucket if needed */
168 if ((*the_bucket
) == array
->empty_bucket
) {
170 /* The bucket was previously empty (or something like that), */
171 /* allocate a new. This is the effect of `lazy' allocation */
172 new_bucket
= (struct sbucket
*) objc_malloc (sizeof (struct sbucket
));
173 memcpy ((void *) new_bucket
, (const void *) array
->empty_bucket
,
174 sizeof (struct sbucket
));
175 new_bucket
->version
.version
= array
->version
.version
;
176 *the_bucket
= new_bucket
; /* Prepared for install. */
180 } else if ((*the_bucket
)->version
.version
!= array
->version
.version
) {
182 /* Perform lazy copy. */
183 struct sbucket
*old_bucket
= *the_bucket
;
184 new_bucket
= (struct sbucket
*) objc_malloc (sizeof (struct sbucket
));
185 memcpy (new_bucket
, old_bucket
, sizeof (struct sbucket
));
186 new_bucket
->version
.version
= array
->version
.version
;
187 *the_bucket
= new_bucket
; /* Prepared for install. */
192 (*the_bucket
)->elems
[eoffset
] = element
;
196 sarray_at_put_safe (struct sarray
*array
, sidx index
, void *element
)
198 if (soffset_decode (index
) >= array
->capacity
)
199 sarray_realloc (array
, soffset_decode (index
) + 1);
200 sarray_at_put (array
, index
, element
);
204 sarray_new (int size
, void *default_element
)
208 size_t num_indices
= ((size
- 1)/(INDEX_CAPACITY
)) + 1;
209 struct sindex
**new_indices
;
210 #else /* OBJC_SPARSE2 */
211 size_t num_indices
= ((size
- 1)/BUCKET_SIZE
) + 1;
212 struct sbucket
**new_buckets
;
218 /* Allocate core array */
219 arr
= (struct sarray
*) objc_malloc (sizeof (struct sarray
));
220 arr
->version
.version
= 0;
222 /* Initialize members */
224 arr
->capacity
= num_indices
*INDEX_CAPACITY
;
225 new_indices
= (struct sindex
**)
226 objc_malloc (sizeof (struct sindex
*) * num_indices
);
228 arr
->empty_index
= (struct sindex
*) objc_malloc (sizeof (struct sindex
));
229 arr
->empty_index
->version
.version
= 0;
232 idxsize
+= num_indices
;
235 #else /* OBJC_SPARSE2 */
236 arr
->capacity
= num_indices
*BUCKET_SIZE
;
237 new_buckets
= (struct sbucket
**)
238 objc_malloc (sizeof (struct sbucket
*) * num_indices
);
241 idxsize
+= num_indices
;
245 arr
->empty_bucket
= (struct sbucket
*) objc_malloc (sizeof (struct sbucket
));
246 arr
->empty_bucket
->version
.version
= 0;
251 arr
->is_copy_of
= (struct sarray
*) 0;
253 for (counter
= 0; counter
< BUCKET_SIZE
; counter
++)
254 arr
->empty_bucket
->elems
[counter
] = default_element
;
257 for (counter
= 0; counter
< INDEX_SIZE
; counter
++)
258 arr
->empty_index
->buckets
[counter
] = arr
->empty_bucket
;
260 for (counter
= 0; counter
< num_indices
; counter
++)
261 new_indices
[counter
] = arr
->empty_index
;
263 #else /* OBJC_SPARSE2 */
265 for (counter
= 0; counter
< num_indices
; counter
++)
266 new_buckets
[counter
] = arr
->empty_bucket
;
271 arr
->indices
= new_indices
;
272 #else /* OBJC_SPARSE2 */
273 arr
->buckets
= new_buckets
;
280 /* Reallocate the sparse array to hold `newsize' entries
281 Note: We really allocate and then free. We have to do this to ensure that
282 any concurrent readers notice the update. */
285 sarray_realloc (struct sarray
*array
, int newsize
)
288 size_t old_max_index
= (array
->capacity
- 1)/INDEX_CAPACITY
;
289 size_t new_max_index
= ((newsize
- 1)/INDEX_CAPACITY
);
290 size_t rounded_size
= (new_max_index
+ 1) * INDEX_CAPACITY
;
292 struct sindex
**new_indices
;
293 struct sindex
**old_indices
;
295 #else /* OBJC_SPARSE2 */
296 size_t old_max_index
= (array
->capacity
- 1)/BUCKET_SIZE
;
297 size_t new_max_index
= ((newsize
- 1)/BUCKET_SIZE
);
298 size_t rounded_size
= (new_max_index
+ 1) * BUCKET_SIZE
;
300 struct sbucket
**new_buckets
;
301 struct sbucket
**old_buckets
;
307 assert (newsize
> 0);
309 /* The size is the same, just ignore the request */
310 if (rounded_size
<= array
->capacity
)
313 assert (array
->ref_count
== 1); /* stop if lazy copied... */
315 /* We are asked to extend the array -- allocate new bucket table, */
316 /* and insert empty_bucket in newly allocated places. */
317 if (rounded_size
> array
->capacity
)
322 rounded_size
= (new_max_index
+ 1) * INDEX_CAPACITY
;
324 #else /* OBJC_SPARSE2 */
326 rounded_size
= (new_max_index
+ 1) * BUCKET_SIZE
;
329 /* update capacity */
330 array
->capacity
= rounded_size
;
333 /* alloc to force re-read by any concurrent readers. */
334 old_indices
= array
->indices
;
335 new_indices
= (struct sindex
**)
336 objc_malloc ((new_max_index
+ 1) * sizeof (struct sindex
*));
337 #else /* OBJC_SPARSE2 */
338 old_buckets
= array
->buckets
;
339 new_buckets
= (struct sbucket
**)
340 objc_malloc ((new_max_index
+ 1) * sizeof (struct sbucket
*));
343 /* copy buckets below old_max_index (they are still valid) */
344 for (counter
= 0; counter
<= old_max_index
; counter
++ ) {
346 new_indices
[counter
] = old_indices
[counter
];
347 #else /* OBJC_SPARSE2 */
348 new_buckets
[counter
] = old_buckets
[counter
];
353 /* reset entries above old_max_index to empty_bucket */
354 for (counter
= old_max_index
+ 1; counter
<= new_max_index
; counter
++)
355 new_indices
[counter
] = array
->empty_index
;
356 #else /* OBJC_SPARSE2 */
357 /* reset entries above old_max_index to empty_bucket */
358 for (counter
= old_max_index
+ 1; counter
<= new_max_index
; counter
++)
359 new_buckets
[counter
] = array
->empty_bucket
;
363 /* install the new indices */
364 array
->indices
= new_indices
;
365 #else /* OBJC_SPARSE2 */
366 array
->buckets
= new_buckets
;
370 /* free the old indices */
371 sarray_free_garbage (old_indices
);
372 #else /* OBJC_SPARSE2 */
373 sarray_free_garbage (old_buckets
);
376 idxsize
+= (new_max_index
-old_max_index
);
382 /* Free a sparse array allocated with sarray_new */
385 sarray_free (struct sarray
*array
) {
387 size_t old_max_index
= (array
->capacity
- 1)/INDEX_CAPACITY
;
388 struct sindex
**old_indices
;
390 size_t old_max_index
= (array
->capacity
- 1)/BUCKET_SIZE
;
391 struct sbucket
**old_buckets
;
395 assert (array
->ref_count
!= 0); /* Freed multiple times!!! */
397 if (--(array
->ref_count
) != 0) /* There exists copies of me */
401 old_indices
= array
->indices
;
403 old_buckets
= array
->buckets
;
406 /* Free all entries that do not point to empty_bucket */
407 for (counter
= 0; counter
<= old_max_index
; counter
++ ) {
409 struct sindex
*idx
= old_indices
[counter
];
410 if ((idx
!= array
->empty_index
) &&
411 (idx
->version
.version
== array
->version
.version
)) {
413 for (c2
= 0; c2
< INDEX_SIZE
; c2
++) {
414 struct sbucket
*bkt
= idx
->buckets
[c2
];
415 if ((bkt
!= array
->empty_bucket
) &&
416 (bkt
->version
.version
== array
->version
.version
))
418 sarray_free_garbage (bkt
);
422 sarray_free_garbage (idx
);
425 #else /* OBJC_SPARSE2 */
426 struct sbucket
*bkt
= array
->buckets
[counter
];
427 if ((bkt
!= array
->empty_bucket
) &&
428 (bkt
->version
.version
== array
->version
.version
))
430 sarray_free_garbage (bkt
);
437 /* free empty_index */
438 if (array
->empty_index
->version
.version
== array
->version
.version
) {
439 sarray_free_garbage (array
->empty_index
);
444 /* free empty_bucket */
445 if (array
->empty_bucket
->version
.version
== array
->version
.version
) {
446 sarray_free_garbage (array
->empty_bucket
);
449 idxsize
-= (old_max_index
+ 1);
453 /* free bucket table */
454 sarray_free_garbage (array
->indices
);
457 /* free bucket table */
458 sarray_free_garbage (array
->buckets
);
462 /* If this is a copy of another array, we free it (which might just
463 * decrement its reference count so it will be freed when no longer in use).
465 if (array
->is_copy_of
)
466 sarray_free (array
->is_copy_of
);
469 sarray_free_garbage (array
);
472 /* This is a lazy copy. Only the core of the structure is actually */
476 sarray_lazy_copy (struct sarray
*oarr
)
481 size_t num_indices
= ((oarr
->capacity
- 1)/INDEX_CAPACITY
) + 1;
482 struct sindex
**new_indices
;
483 #else /* OBJC_SPARSE2 */
484 size_t num_indices
= ((oarr
->capacity
- 1)/BUCKET_SIZE
) + 1;
485 struct sbucket
**new_buckets
;
488 /* Allocate core array */
489 arr
= (struct sarray
*) objc_malloc (sizeof (struct sarray
)); /* !!! */
490 arr
->version
.version
= oarr
->version
.version
+ 1;
492 arr
->empty_index
= oarr
->empty_index
;
494 arr
->empty_bucket
= oarr
->empty_bucket
;
496 oarr
->ref_count
+= 1;
497 arr
->is_copy_of
= oarr
;
498 arr
->capacity
= oarr
->capacity
;
501 /* Copy bucket table */
502 new_indices
= (struct sindex
**)
503 objc_malloc (sizeof (struct sindex
*) * num_indices
);
504 memcpy (new_indices
, oarr
->indices
, sizeof (struct sindex
*) * num_indices
);
505 arr
->indices
= new_indices
;
507 /* Copy bucket table */
508 new_buckets
= (struct sbucket
**)
509 objc_malloc (sizeof (struct sbucket
*) * num_indices
);
510 memcpy (new_buckets
, oarr
->buckets
, sizeof (struct sbucket
*) * num_indices
);
511 arr
->buckets
= new_buckets
;
514 idxsize
+= num_indices
;