xref: /netbsd-src/external/gpl3/gcc/dist/libobjc/sarray.c (revision b1e838363e3c6fc78a55519254d99869742dd33c)
14fee23f9Smrg /* Sparse Arrays for Objective C dispatch tables
2*b1e83836Smrg    Copyright (C) 1993-2022 Free Software Foundation, Inc.
34fee23f9Smrg 
44fee23f9Smrg This file is part of GCC.
54fee23f9Smrg 
64fee23f9Smrg GCC is free software; you can redistribute it and/or modify
74fee23f9Smrg it under the terms of the GNU General Public License as published by
84fee23f9Smrg the Free Software Foundation; either version 3, or (at your option)
94fee23f9Smrg any later version.
104fee23f9Smrg 
114fee23f9Smrg GCC is distributed in the hope that it will be useful,
124fee23f9Smrg but WITHOUT ANY WARRANTY; without even the implied warranty of
134fee23f9Smrg MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
144fee23f9Smrg GNU General Public License for more details.
154fee23f9Smrg 
164fee23f9Smrg Under Section 7 of GPL version 3, you are granted additional
174fee23f9Smrg permissions described in the GCC Runtime Library Exception, version
184fee23f9Smrg 3.1, as published by the Free Software Foundation.
194fee23f9Smrg 
204fee23f9Smrg You should have received a copy of the GNU General Public License and
214fee23f9Smrg a copy of the GCC Runtime Library Exception along with this program;
224fee23f9Smrg see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
234fee23f9Smrg <http://www.gnu.org/licenses/>.  */
244fee23f9Smrg 
2548fb7bfaSmrg #include "objc-private/common.h"
2648fb7bfaSmrg #include "objc-private/sarray.h"
2748fb7bfaSmrg #include "objc/runtime.h" /* For objc_malloc */
2848fb7bfaSmrg #include "objc/thr.h"     /* For objc_mutex_lock */
2948fb7bfaSmrg #include "objc-private/module-abi-8.h"
3048fb7bfaSmrg #include "objc-private/runtime.h"
314fee23f9Smrg #include <stdio.h>
3248fb7bfaSmrg #include <string.h> /* For memset */
3348fb7bfaSmrg #include <assert.h> /* For assert */
344fee23f9Smrg 
354fee23f9Smrg int nbuckets = 0;					/* !T:MUTEX */
364fee23f9Smrg int nindices = 0;					/* !T:MUTEX */
374fee23f9Smrg int narrays = 0;					/* !T:MUTEX */
384fee23f9Smrg int idxsize = 0;					/* !T:MUTEX */
394fee23f9Smrg 
404fee23f9Smrg static void *first_free_data = NULL;			/* !T:MUTEX */
414fee23f9Smrg 
424fee23f9Smrg #ifdef OBJC_SPARSE2
434fee23f9Smrg const char *__objc_sparse2_id = "2 level sparse indices";
444fee23f9Smrg #endif
454fee23f9Smrg 
464fee23f9Smrg #ifdef OBJC_SPARSE3
474fee23f9Smrg const char *__objc_sparse3_id = "3 level sparse indices";
484fee23f9Smrg #endif
494fee23f9Smrg 
504fee23f9Smrg /* This function removes any structures left over from free operations
514fee23f9Smrg    that were not safe in a multi-threaded environment. */
524fee23f9Smrg void
sarray_remove_garbage(void)534fee23f9Smrg sarray_remove_garbage (void)
544fee23f9Smrg {
554fee23f9Smrg   void **vp;
564fee23f9Smrg   void *np;
574fee23f9Smrg 
584fee23f9Smrg   objc_mutex_lock (__objc_runtime_mutex);
594fee23f9Smrg 
604fee23f9Smrg   vp = first_free_data;
614fee23f9Smrg   first_free_data = NULL;
624fee23f9Smrg 
6348fb7bfaSmrg   while (vp)
6448fb7bfaSmrg     {
654fee23f9Smrg       np = *vp;
664fee23f9Smrg       objc_free (vp);
674fee23f9Smrg       vp = np;
684fee23f9Smrg     }
694fee23f9Smrg 
704fee23f9Smrg   objc_mutex_unlock (__objc_runtime_mutex);
714fee23f9Smrg }
724fee23f9Smrg 
7348fb7bfaSmrg /* Free a block of dynamically allocated memory.  If we are in
7448fb7bfaSmrg    multi-threaded mode, it is ok to free it.  If not, we add it to the
7548fb7bfaSmrg    garbage heap to be freed later. */
764fee23f9Smrg static void
sarray_free_garbage(void * vp)774fee23f9Smrg sarray_free_garbage (void *vp)
784fee23f9Smrg {
794fee23f9Smrg   objc_mutex_lock (__objc_runtime_mutex);
804fee23f9Smrg 
8148fb7bfaSmrg   if (__objc_runtime_threads_alive == 1)
8248fb7bfaSmrg     {
834fee23f9Smrg       objc_free (vp);
844fee23f9Smrg       if (first_free_data)
854fee23f9Smrg 	sarray_remove_garbage ();
864fee23f9Smrg     }
8748fb7bfaSmrg   else
8848fb7bfaSmrg     {
894fee23f9Smrg       *(void **)vp = first_free_data;
904fee23f9Smrg       first_free_data = vp;
914fee23f9Smrg     }
924fee23f9Smrg 
934fee23f9Smrg   objc_mutex_unlock (__objc_runtime_mutex);
944fee23f9Smrg }
954fee23f9Smrg 
9648fb7bfaSmrg /* sarray_at_put copies data in such a way as to be thread reader
9748fb7bfaSmrg    safe.  */
984fee23f9Smrg void
sarray_at_put(struct sarray * array,sidx index,void * element)994fee23f9Smrg sarray_at_put (struct sarray *array, sidx index, void *element)
1004fee23f9Smrg {
1014fee23f9Smrg #ifdef OBJC_SPARSE3
1024fee23f9Smrg   struct sindex **the_index;
1034fee23f9Smrg   struct sindex *new_index;
1044fee23f9Smrg #endif
1054fee23f9Smrg   struct sbucket **the_bucket;
1064fee23f9Smrg   struct sbucket *new_bucket;
1074fee23f9Smrg #ifdef OBJC_SPARSE3
1084fee23f9Smrg   size_t ioffset;
1094fee23f9Smrg #endif
1104fee23f9Smrg   size_t boffset;
1114fee23f9Smrg   size_t eoffset;
1124fee23f9Smrg #ifdef PRECOMPUTE_SELECTORS
1134fee23f9Smrg   union sofftype xx;
1144fee23f9Smrg   xx.idx = index;
1154fee23f9Smrg #ifdef OBJC_SPARSE3
1164fee23f9Smrg   ioffset = xx.off.ioffset;
1174fee23f9Smrg #endif
1184fee23f9Smrg   boffset = xx.off.boffset;
1194fee23f9Smrg   eoffset = xx.off.eoffset;
1204fee23f9Smrg #else /* not PRECOMPUTE_SELECTORS */
1214fee23f9Smrg #ifdef OBJC_SPARSE3
1224fee23f9Smrg   ioffset = index/INDEX_CAPACITY;
1234fee23f9Smrg   boffset = (index/BUCKET_SIZE)%INDEX_SIZE;
1244fee23f9Smrg   eoffset = index%BUCKET_SIZE;
1254fee23f9Smrg #else
1264fee23f9Smrg   boffset = index/BUCKET_SIZE;
1274fee23f9Smrg   eoffset = index%BUCKET_SIZE;
1284fee23f9Smrg #endif
1294fee23f9Smrg #endif /* not PRECOMPUTE_SELECTORS */
1304fee23f9Smrg 
1314fee23f9Smrg   assert (soffset_decode (index) < array->capacity); /* Range check */
1324fee23f9Smrg 
1334fee23f9Smrg #ifdef OBJC_SPARSE3
1344fee23f9Smrg   the_index = &(array->indices[ioffset]);
1354fee23f9Smrg   the_bucket = &((*the_index)->buckets[boffset]);
1364fee23f9Smrg #else
1374fee23f9Smrg   the_bucket = &(array->buckets[boffset]);
1384fee23f9Smrg #endif
1394fee23f9Smrg 
1404fee23f9Smrg   if ((*the_bucket)->elems[eoffset] == element)
14148fb7bfaSmrg     return;		/* Great! we just avoided a lazy copy.  */
1424fee23f9Smrg 
1434fee23f9Smrg #ifdef OBJC_SPARSE3
1444fee23f9Smrg 
14548fb7bfaSmrg   /* First, perform lazy copy/allocation of index if needed.  */
1464fee23f9Smrg 
14748fb7bfaSmrg   if ((*the_index) == array->empty_index)
14848fb7bfaSmrg     {
14948fb7bfaSmrg       /* The index was previously empty, allocate a new.  */
1504fee23f9Smrg       new_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
1514fee23f9Smrg       memcpy (new_index, array->empty_index, sizeof (struct sindex));
1524fee23f9Smrg       new_index->version.version = array->version.version;
1534fee23f9Smrg       *the_index = new_index;                     /* Prepared for install. */
1544fee23f9Smrg       the_bucket = &((*the_index)->buckets[boffset]);
1554fee23f9Smrg 
1564fee23f9Smrg       nindices += 1;
15748fb7bfaSmrg     }
15848fb7bfaSmrg   else if ((*the_index)->version.version != array->version.version)
15948fb7bfaSmrg     {
16048fb7bfaSmrg       /* This index must be lazy copied.  */
1614fee23f9Smrg       struct sindex *old_index = *the_index;
1624fee23f9Smrg       new_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
1634fee23f9Smrg       memcpy (new_index, old_index, sizeof (struct sindex));
1644fee23f9Smrg       new_index->version.version = array->version.version;
1654fee23f9Smrg       *the_index = new_index;                     /* Prepared for install. */
1664fee23f9Smrg       the_bucket = &((*the_index)->buckets[boffset]);
1674fee23f9Smrg 
1684fee23f9Smrg       nindices += 1;
1694fee23f9Smrg     }
1704fee23f9Smrg 
1714fee23f9Smrg #endif /* OBJC_SPARSE3 */
1724fee23f9Smrg 
17348fb7bfaSmrg   /* Next, perform lazy allocation/copy of the bucket if needed.  */
17448fb7bfaSmrg   if ((*the_bucket) == array->empty_bucket)
17548fb7bfaSmrg     {
17648fb7bfaSmrg       /* The bucket was previously empty (or something like that),
17748fb7bfaSmrg 	 allocate a new.  This is the effect of `lazy' allocation.  */
1784fee23f9Smrg       new_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
1794fee23f9Smrg       memcpy ((void *) new_bucket, (const void *) array->empty_bucket,
1804fee23f9Smrg 	      sizeof (struct sbucket));
1814fee23f9Smrg       new_bucket->version.version = array->version.version;
1824fee23f9Smrg       *the_bucket = new_bucket;                   /* Prepared for install. */
1834fee23f9Smrg 
1844fee23f9Smrg       nbuckets += 1;
1854fee23f9Smrg 
18648fb7bfaSmrg     }
18748fb7bfaSmrg   else if ((*the_bucket)->version.version != array->version.version)
18848fb7bfaSmrg     {
1894fee23f9Smrg       /* Perform lazy copy.  */
1904fee23f9Smrg       struct sbucket *old_bucket = *the_bucket;
1914fee23f9Smrg       new_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
1924fee23f9Smrg       memcpy (new_bucket, old_bucket, sizeof (struct sbucket));
1934fee23f9Smrg       new_bucket->version.version = array->version.version;
1944fee23f9Smrg       *the_bucket = new_bucket;                   /* Prepared for install. */
1954fee23f9Smrg 
1964fee23f9Smrg       nbuckets += 1;
1974fee23f9Smrg     }
1984fee23f9Smrg   (*the_bucket)->elems[eoffset] = element;
1994fee23f9Smrg }
2004fee23f9Smrg 
2014fee23f9Smrg void
sarray_at_put_safe(struct sarray * array,sidx index,void * element)2024fee23f9Smrg sarray_at_put_safe (struct sarray *array, sidx index, void *element)
2034fee23f9Smrg {
2044fee23f9Smrg   if (soffset_decode (index) >= array->capacity)
2054fee23f9Smrg     sarray_realloc (array, soffset_decode (index) + 1);
2064fee23f9Smrg   sarray_at_put (array, index, element);
2074fee23f9Smrg }
2084fee23f9Smrg 
2094fee23f9Smrg struct sarray *
sarray_new(int size,void * default_element)2104fee23f9Smrg sarray_new (int size, void *default_element)
2114fee23f9Smrg {
2124fee23f9Smrg   struct sarray *arr;
2134fee23f9Smrg #ifdef OBJC_SPARSE3
2144fee23f9Smrg   size_t num_indices = ((size - 1)/(INDEX_CAPACITY)) + 1;
2154fee23f9Smrg   struct sindex **new_indices;
2164fee23f9Smrg #else /* OBJC_SPARSE2 */
2174fee23f9Smrg   size_t num_indices = ((size - 1)/BUCKET_SIZE) + 1;
2184fee23f9Smrg   struct sbucket **new_buckets;
2194fee23f9Smrg #endif
2204fee23f9Smrg   size_t counter;
2214fee23f9Smrg 
2224fee23f9Smrg   assert (size > 0);
2234fee23f9Smrg 
22448fb7bfaSmrg   /* Allocate core array.  */
2254fee23f9Smrg   arr = (struct sarray *) objc_malloc (sizeof (struct sarray));
2264fee23f9Smrg   arr->version.version = 0;
2274fee23f9Smrg 
22848fb7bfaSmrg   /* Initialize members.  */
2294fee23f9Smrg #ifdef OBJC_SPARSE3
2304fee23f9Smrg   arr->capacity = num_indices*INDEX_CAPACITY;
2314fee23f9Smrg   new_indices = (struct sindex **)
2324fee23f9Smrg     objc_malloc (sizeof (struct sindex *) * num_indices);
2334fee23f9Smrg 
2344fee23f9Smrg   arr->empty_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
2354fee23f9Smrg   arr->empty_index->version.version = 0;
2364fee23f9Smrg 
2374fee23f9Smrg   narrays  += 1;
2384fee23f9Smrg   idxsize  += num_indices;
2394fee23f9Smrg   nindices += 1;
2404fee23f9Smrg 
2414fee23f9Smrg #else /* OBJC_SPARSE2 */
2424fee23f9Smrg   arr->capacity = num_indices*BUCKET_SIZE;
2434fee23f9Smrg   new_buckets = (struct sbucket **)
2444fee23f9Smrg     objc_malloc (sizeof (struct sbucket *) * num_indices);
2454fee23f9Smrg 
2464fee23f9Smrg   narrays  += 1;
2474fee23f9Smrg   idxsize  += num_indices;
2484fee23f9Smrg 
2494fee23f9Smrg #endif
2504fee23f9Smrg 
2514fee23f9Smrg   arr->empty_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
2524fee23f9Smrg   arr->empty_bucket->version.version = 0;
2534fee23f9Smrg 
2544fee23f9Smrg   nbuckets += 1;
2554fee23f9Smrg 
2564fee23f9Smrg   arr->ref_count = 1;
2574fee23f9Smrg   arr->is_copy_of = (struct sarray *) 0;
2584fee23f9Smrg 
2594fee23f9Smrg   for (counter = 0; counter < BUCKET_SIZE; counter++)
2604fee23f9Smrg     arr->empty_bucket->elems[counter] = default_element;
2614fee23f9Smrg 
2624fee23f9Smrg #ifdef OBJC_SPARSE3
2634fee23f9Smrg   for (counter = 0; counter < INDEX_SIZE; counter++)
2644fee23f9Smrg     arr->empty_index->buckets[counter] = arr->empty_bucket;
2654fee23f9Smrg 
2664fee23f9Smrg   for (counter = 0; counter < num_indices; counter++)
2674fee23f9Smrg     new_indices[counter] = arr->empty_index;
2684fee23f9Smrg 
2694fee23f9Smrg #else /* OBJC_SPARSE2 */
2704fee23f9Smrg 
2714fee23f9Smrg   for (counter = 0; counter < num_indices; counter++)
2724fee23f9Smrg     new_buckets[counter] = arr->empty_bucket;
2734fee23f9Smrg 
2744fee23f9Smrg #endif
2754fee23f9Smrg 
2764fee23f9Smrg #ifdef OBJC_SPARSE3
2774fee23f9Smrg   arr->indices = new_indices;
2784fee23f9Smrg #else /* OBJC_SPARSE2 */
2794fee23f9Smrg   arr->buckets = new_buckets;
2804fee23f9Smrg #endif
2814fee23f9Smrg 
2824fee23f9Smrg   return arr;
2834fee23f9Smrg }
2844fee23f9Smrg 
2854fee23f9Smrg 
28648fb7bfaSmrg /* Reallocate the sparse array to hold `newsize' entries Note: We
28748fb7bfaSmrg    really allocate and then free.  We have to do this to ensure that
2884fee23f9Smrg    any concurrent readers notice the update.  */
2894fee23f9Smrg void
sarray_realloc(struct sarray * array,int newsize)2904fee23f9Smrg sarray_realloc (struct sarray *array, int newsize)
2914fee23f9Smrg {
2924fee23f9Smrg #ifdef OBJC_SPARSE3
2934fee23f9Smrg   size_t old_max_index = (array->capacity - 1)/INDEX_CAPACITY;
2944fee23f9Smrg   size_t new_max_index = ((newsize - 1)/INDEX_CAPACITY);
2954fee23f9Smrg   size_t rounded_size = (new_max_index + 1) * INDEX_CAPACITY;
2964fee23f9Smrg 
2974fee23f9Smrg   struct sindex **new_indices;
2984fee23f9Smrg   struct sindex **old_indices;
2994fee23f9Smrg 
3004fee23f9Smrg #else /* OBJC_SPARSE2 */
3014fee23f9Smrg   size_t old_max_index = (array->capacity - 1)/BUCKET_SIZE;
3024fee23f9Smrg   size_t new_max_index = ((newsize - 1)/BUCKET_SIZE);
3034fee23f9Smrg   size_t rounded_size = (new_max_index + 1) * BUCKET_SIZE;
3044fee23f9Smrg 
3054fee23f9Smrg   struct sbucket **new_buckets;
3064fee23f9Smrg   struct sbucket **old_buckets;
3074fee23f9Smrg 
3084fee23f9Smrg #endif
3094fee23f9Smrg 
3104fee23f9Smrg   size_t counter;
3114fee23f9Smrg 
3124fee23f9Smrg   assert (newsize > 0);
3134fee23f9Smrg 
31448fb7bfaSmrg   /* The size is the same, just ignore the request.  */
3154fee23f9Smrg   if (rounded_size <= array->capacity)
3164fee23f9Smrg     return;
3174fee23f9Smrg 
3184fee23f9Smrg   assert (array->ref_count == 1);	/* stop if lazy copied... */
3194fee23f9Smrg 
32048fb7bfaSmrg   /* We are asked to extend the array -- allocate new bucket table,
32148fb7bfaSmrg      and insert empty_bucket in newly allocated places.  */
3224fee23f9Smrg   if (rounded_size > array->capacity)
3234fee23f9Smrg     {
3244fee23f9Smrg #ifdef OBJC_SPARSE3
3254fee23f9Smrg       new_max_index += 4;
3264fee23f9Smrg       rounded_size = (new_max_index + 1) * INDEX_CAPACITY;
3274fee23f9Smrg #else /* OBJC_SPARSE2 */
3284fee23f9Smrg       new_max_index += 4;
3294fee23f9Smrg       rounded_size = (new_max_index + 1) * BUCKET_SIZE;
3304fee23f9Smrg #endif
3314fee23f9Smrg 
33248fb7bfaSmrg       /* Update capacity.  */
3334fee23f9Smrg       array->capacity = rounded_size;
3344fee23f9Smrg 
3354fee23f9Smrg #ifdef OBJC_SPARSE3
33648fb7bfaSmrg       /* Alloc to force re-read by any concurrent readers.  */
3374fee23f9Smrg       old_indices = array->indices;
3384fee23f9Smrg       new_indices = (struct sindex **)
3394fee23f9Smrg 	objc_malloc ((new_max_index + 1) * sizeof (struct sindex *));
3404fee23f9Smrg #else /* OBJC_SPARSE2 */
3414fee23f9Smrg       old_buckets = array->buckets;
3424fee23f9Smrg       new_buckets = (struct sbucket **)
3434fee23f9Smrg 	objc_malloc ((new_max_index + 1) * sizeof (struct sbucket *));
3444fee23f9Smrg #endif
3454fee23f9Smrg 
34648fb7bfaSmrg       /* Copy buckets below old_max_index (they are still valid).  */
34748fb7bfaSmrg       for (counter = 0; counter <= old_max_index; counter++ )
34848fb7bfaSmrg 	{
3494fee23f9Smrg #ifdef OBJC_SPARSE3
3504fee23f9Smrg 	  new_indices[counter] = old_indices[counter];
3514fee23f9Smrg #else /* OBJC_SPARSE2 */
3524fee23f9Smrg 	  new_buckets[counter] = old_buckets[counter];
3534fee23f9Smrg #endif
3544fee23f9Smrg 	}
3554fee23f9Smrg 
3564fee23f9Smrg #ifdef OBJC_SPARSE3
35748fb7bfaSmrg       /* Reset entries above old_max_index to empty_bucket.  */
3584fee23f9Smrg       for (counter = old_max_index + 1; counter <= new_max_index; counter++)
3594fee23f9Smrg 	new_indices[counter] = array->empty_index;
3604fee23f9Smrg #else /* OBJC_SPARSE2 */
36148fb7bfaSmrg       /* Reset entries above old_max_index to empty_bucket.  */
3624fee23f9Smrg       for (counter = old_max_index + 1; counter <= new_max_index; counter++)
3634fee23f9Smrg 	new_buckets[counter] = array->empty_bucket;
3644fee23f9Smrg #endif
3654fee23f9Smrg 
3664fee23f9Smrg #ifdef OBJC_SPARSE3
36748fb7bfaSmrg       /* Install the new indices.  */
3684fee23f9Smrg       array->indices = new_indices;
3694fee23f9Smrg #else /* OBJC_SPARSE2 */
3704fee23f9Smrg       array->buckets = new_buckets;
3714fee23f9Smrg #endif
3724fee23f9Smrg 
3734fee23f9Smrg #ifdef OBJC_SPARSE3
37448fb7bfaSmrg       /* Free the old indices.  */
3754fee23f9Smrg       sarray_free_garbage (old_indices);
3764fee23f9Smrg #else /* OBJC_SPARSE2 */
3774fee23f9Smrg       sarray_free_garbage (old_buckets);
3784fee23f9Smrg #endif
3794fee23f9Smrg 
3804fee23f9Smrg       idxsize += (new_max_index-old_max_index);
3814fee23f9Smrg       return;
3824fee23f9Smrg     }
3834fee23f9Smrg }
3844fee23f9Smrg 
3854fee23f9Smrg 
3864fee23f9Smrg /* Free a sparse array allocated with sarray_new */
3874fee23f9Smrg void
sarray_free(struct sarray * array)3884fee23f9Smrg sarray_free (struct sarray *array) {
3894fee23f9Smrg #ifdef OBJC_SPARSE3
3904fee23f9Smrg   size_t old_max_index = (array->capacity - 1)/INDEX_CAPACITY;
3914fee23f9Smrg   struct sindex **old_indices;
3924fee23f9Smrg #else
3934fee23f9Smrg   size_t old_max_index = (array->capacity - 1)/BUCKET_SIZE;
3944fee23f9Smrg   struct sbucket **old_buckets;
3954fee23f9Smrg #endif
3964fee23f9Smrg   size_t counter = 0;
3974fee23f9Smrg 
3984fee23f9Smrg   assert (array->ref_count != 0);	/* Freed multiple times!!! */
3994fee23f9Smrg 
4004fee23f9Smrg   if (--(array->ref_count) != 0)	/* There exists copies of me */
4014fee23f9Smrg     return;
4024fee23f9Smrg 
4034fee23f9Smrg #ifdef OBJC_SPARSE3
4044fee23f9Smrg   old_indices = array->indices;
4054fee23f9Smrg #else
4064fee23f9Smrg   old_buckets = array->buckets;
4074fee23f9Smrg #endif
4084fee23f9Smrg 
40948fb7bfaSmrg   /* Free all entries that do not point to empty_bucket.  */
41048fb7bfaSmrg   for (counter = 0; counter <= old_max_index; counter++ )
41148fb7bfaSmrg     {
4124fee23f9Smrg #ifdef OBJC_SPARSE3
4134fee23f9Smrg       struct sindex *idx = old_indices[counter];
41448fb7bfaSmrg       if ((idx != array->empty_index)
41548fb7bfaSmrg 	  && (idx->version.version == array->version.version))
41648fb7bfaSmrg 	{
4174fee23f9Smrg 	  int c2;
41848fb7bfaSmrg 	  for (c2 = 0; c2 < INDEX_SIZE; c2++)
41948fb7bfaSmrg 	    {
4204fee23f9Smrg 	      struct sbucket *bkt = idx->buckets[c2];
42148fb7bfaSmrg 	      if ((bkt != array->empty_bucket)
42248fb7bfaSmrg 		  && (bkt->version.version == array->version.version))
4234fee23f9Smrg 		{
4244fee23f9Smrg 		  sarray_free_garbage (bkt);
4254fee23f9Smrg 		  nbuckets -= 1;
4264fee23f9Smrg 		}
4274fee23f9Smrg 	    }
4284fee23f9Smrg 	  sarray_free_garbage (idx);
4294fee23f9Smrg 	  nindices -= 1;
4304fee23f9Smrg 	}
4314fee23f9Smrg #else /* OBJC_SPARSE2 */
4324fee23f9Smrg       struct sbucket *bkt = old_buckets[counter];
43348fb7bfaSmrg       if ((bkt != array->empty_bucket)
43448fb7bfaSmrg 	  && (bkt->version.version == array->version.version))
4354fee23f9Smrg 	{
4364fee23f9Smrg 	  sarray_free_garbage (bkt);
4374fee23f9Smrg 	  nbuckets -= 1;
4384fee23f9Smrg 	}
4394fee23f9Smrg #endif
4404fee23f9Smrg     }
4414fee23f9Smrg 
4424fee23f9Smrg #ifdef OBJC_SPARSE3
44348fb7bfaSmrg   /* Free empty_index.  */
44448fb7bfaSmrg   if (array->empty_index->version.version == array->version.version)
44548fb7bfaSmrg     {
4464fee23f9Smrg       sarray_free_garbage (array->empty_index);
4474fee23f9Smrg       nindices -= 1;
4484fee23f9Smrg     }
4494fee23f9Smrg #endif
4504fee23f9Smrg 
45148fb7bfaSmrg   /* Free empty_bucket.  */
45248fb7bfaSmrg   if (array->empty_bucket->version.version == array->version.version)
45348fb7bfaSmrg     {
4544fee23f9Smrg       sarray_free_garbage (array->empty_bucket);
4554fee23f9Smrg       nbuckets -= 1;
4564fee23f9Smrg     }
4574fee23f9Smrg   idxsize -= (old_max_index + 1);
4584fee23f9Smrg   narrays -= 1;
4594fee23f9Smrg 
4604fee23f9Smrg #ifdef OBJC_SPARSE3
46148fb7bfaSmrg   /* Free bucket table.  */
4624fee23f9Smrg   sarray_free_garbage (array->indices);
4634fee23f9Smrg #else
46448fb7bfaSmrg   /* Free bucket table.  */
4654fee23f9Smrg   sarray_free_garbage (array->buckets);
4664fee23f9Smrg #endif
4674fee23f9Smrg 
4684fee23f9Smrg   /* If this is a copy of another array, we free it (which might just
46948fb7bfaSmrg      decrement its reference count so it will be freed when no longer
47048fb7bfaSmrg      in use).  */
4714fee23f9Smrg   if (array->is_copy_of)
4724fee23f9Smrg     sarray_free (array->is_copy_of);
4734fee23f9Smrg 
47448fb7bfaSmrg   /* Free array.  */
4754fee23f9Smrg   sarray_free_garbage (array);
4764fee23f9Smrg }
4774fee23f9Smrg 
47848fb7bfaSmrg /* This is a lazy copy.  Only the core of the structure is actually
47948fb7bfaSmrg    copied.  */
4804fee23f9Smrg struct sarray *
sarray_lazy_copy(struct sarray * oarr)4814fee23f9Smrg sarray_lazy_copy (struct sarray *oarr)
4824fee23f9Smrg {
4834fee23f9Smrg   struct sarray *arr;
4844fee23f9Smrg 
4854fee23f9Smrg #ifdef OBJC_SPARSE3
4864fee23f9Smrg   size_t num_indices = ((oarr->capacity - 1)/INDEX_CAPACITY) + 1;
4874fee23f9Smrg   struct sindex **new_indices;
4884fee23f9Smrg #else /* OBJC_SPARSE2 */
4894fee23f9Smrg   size_t num_indices = ((oarr->capacity - 1)/BUCKET_SIZE) + 1;
4904fee23f9Smrg   struct sbucket **new_buckets;
4914fee23f9Smrg #endif
4924fee23f9Smrg 
49348fb7bfaSmrg   /* Allocate core array.  */
4944fee23f9Smrg   arr = (struct sarray *) objc_malloc (sizeof (struct sarray)); /* !!! */
4954fee23f9Smrg   arr->version.version = oarr->version.version + 1;
4964fee23f9Smrg #ifdef OBJC_SPARSE3
4974fee23f9Smrg   arr->empty_index = oarr->empty_index;
4984fee23f9Smrg #endif
4994fee23f9Smrg   arr->empty_bucket = oarr->empty_bucket;
5004fee23f9Smrg   arr->ref_count = 1;
5014fee23f9Smrg   oarr->ref_count += 1;
5024fee23f9Smrg   arr->is_copy_of = oarr;
5034fee23f9Smrg   arr->capacity = oarr->capacity;
5044fee23f9Smrg 
5054fee23f9Smrg #ifdef OBJC_SPARSE3
50648fb7bfaSmrg   /* Copy bucket table.  */
5074fee23f9Smrg   new_indices = (struct sindex **)
5084fee23f9Smrg     objc_malloc (sizeof (struct sindex *) * num_indices);
5094fee23f9Smrg   memcpy (new_indices, oarr->indices, sizeof (struct sindex *) * num_indices);
5104fee23f9Smrg   arr->indices = new_indices;
5114fee23f9Smrg #else
51248fb7bfaSmrg   /* Copy bucket table.  */
5134fee23f9Smrg   new_buckets = (struct sbucket **)
5144fee23f9Smrg     objc_malloc (sizeof (struct sbucket *) * num_indices);
5154fee23f9Smrg   memcpy (new_buckets, oarr->buckets, sizeof (struct sbucket *) * num_indices);
5164fee23f9Smrg   arr->buckets = new_buckets;
5174fee23f9Smrg #endif
5184fee23f9Smrg 
5194fee23f9Smrg   idxsize += num_indices;
5204fee23f9Smrg   narrays += 1;
5214fee23f9Smrg 
5224fee23f9Smrg   return arr;
5234fee23f9Smrg }
524