OSDN Git Service

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