| /* Copyright (C) 2009 The Android Open Source Project |
| ** |
| ** This software is licensed under the terms of the GNU General Public |
| ** License version 2, as published by the Free Software Foundation, and |
| ** may be copied, distributed, and modified under those terms. |
| ** |
| ** This program is distributed in the hope that it will be useful, |
| ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| ** GNU General Public License for more details. |
| */ |
| #include <android/utils/refset.h> |
| #include <stddef.h> |
| |
| #define AREFSET_STEP 5 |
| |
| AINLINED uint32_t |
| _arefSet_hashItem( void* item ) |
| { |
| uint32_t hash; |
| |
| hash = (uint32_t)(ptrdiff_t)item >> 2; |
| if (sizeof(item) > 4) |
| hash ^= ((uint64_t)(ptrdiff_t)item >> 32); |
| |
| return hash; |
| } |
| |
| static void** |
| _arefSet_lookup( ARefSet* s, void* item) |
| { |
| uint32_t hash = _arefSet_hashItem(item); |
| unsigned index = hash & (s->max_buckets-1); |
| |
| for (;;) { |
| void** pnode = &s->buckets[index]; |
| |
| if (*pnode == item || *pnode == NULL) |
| return pnode; |
| |
| index += AREFSET_STEP; |
| if (index >= s->max_buckets) |
| index -= s->max_buckets; |
| } |
| } |
| |
| static void** |
| _arefSet_lookupInsert( ARefSet* s, void* item) |
| { |
| uint32_t hash = _arefSet_hashItem(item); |
| unsigned index = hash & (s->max_buckets-1); |
| void** insert = NULL; |
| |
| for (;;) { |
| void** pnode = &s->buckets[index]; |
| |
| if (*pnode == NULL) { |
| return insert ? insert : pnode; |
| } else if (*pnode == AREFSET_DELETED) { |
| if (!insert) |
| insert = pnode; |
| } else if (*pnode == item) |
| return pnode; |
| |
| index = (index + AREFSET_STEP) & (s->max_buckets-1); |
| } |
| } |
| |
| extern ABool |
| arefSet_has( ARefSet* s, void* item ) |
| { |
| void** lookup; |
| |
| if (item == NULL || s->max_buckets == 0) |
| return 0; |
| |
| lookup = _arefSet_lookup(s, item); |
| return (*lookup == item); |
| } |
| |
| /* the code below assumes, in the case of a down-size, |
| * that there aren't too many items in the set. |
| */ |
| static void |
| _arefSet_resize( ARefSet* s, unsigned newSize ) |
| { |
| ARefSet newSet; |
| unsigned nn, count = s->num_buckets; |
| |
| AVECTOR_INIT_ALLOC(&newSet,buckets, newSize); |
| |
| for (nn = 0; nn < s->max_buckets; nn++) { |
| void* item = s->buckets[nn]; |
| if (item != NULL && item != AREFSET_DELETED) { |
| void** lookup = _arefSet_lookup(&newSet, item); |
| *lookup = item; |
| } |
| } |
| |
| AVECTOR_DONE(s,buckets); |
| s->buckets = newSet.buckets; |
| s->num_buckets = count; |
| s->max_buckets = newSet.max_buckets; |
| } |
| |
| extern void |
| arefSet_add( ARefSet* s, void* item ) |
| { |
| void** lookup; |
| |
| if (item == NULL) |
| return; |
| |
| /* You can't add items to a set during iteration! */ |
| AASSERT(s->iteration == 0); |
| |
| if (s->max_buckets == 0) |
| AVECTOR_INIT_ALLOC(s,buckets,4); |
| |
| lookup = _arefSet_lookupInsert(s, item); |
| if (*lookup == item) |
| return; |
| |
| *lookup = item; |
| s->num_buckets += 1; |
| |
| if (s->iteration == 0) { |
| if (s->num_buckets > s->max_buckets*0.85) |
| _arefSet_resize(s, s->max_buckets*2); |
| } |
| } |
| |
| extern void |
| arefSet_del( ARefSet* s, void* item ) |
| { |
| void** lookup; |
| |
| if (item == NULL || s->max_buckets == 0) |
| return; |
| |
| lookup = _arefSet_lookup(s, item); |
| if (*lookup != item) |
| return; |
| |
| if (s->iteration == 0) { |
| /* direct deletion */ |
| *lookup = NULL; |
| s->num_buckets -= 1; |
| if (s->num_buckets < s->max_buckets*0.25) |
| _arefSet_resize(s, s->max_buckets/2); |
| } else { |
| /* deferred deletion */ |
| *lookup = AREFSET_DELETED; |
| s->num_buckets -= 1; |
| s->iteration |= 1; |
| } |
| } |
| |
| void |
| _arefSet_removeDeferred( ARefSet* s ) |
| { |
| unsigned nn, newSize; |
| |
| for (nn = 0; nn < s->max_buckets; nn++) { |
| if (s->buckets[nn] == AREFSET_DELETED) { |
| s->buckets[nn] = NULL; |
| } |
| } |
| s->iteration = 0; |
| |
| newSize = s->max_buckets; |
| while (s->num_buckets < newSize*0.25) |
| newSize /= 2; |
| |
| if (newSize != s->max_buckets) |
| _arefSet_resize(s, newSize); |
| } |
| |