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