/* Copyright (c) 2012, Broadcom Europe Ltd All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "interface/khronos/common/khrn_client_platform.h" #include "interface/khronos/common/khrn_int_generic_map.h" #include "interface/khronos/common/khrn_int_util.h" #ifndef KHRN_GENERIC_MAP_CMP_VALUE #define KHRN_GENERIC_MAP_CMP_VALUE(x, y) (x==y) #endif static INLINE uint32_t hash(KHRN_GENERIC_MAP_KEY_T key, uint32_t capacity) { return (uint32_t)key & (capacity - 1); } static KHRN_GENERIC_MAP(ENTRY_T) *get_entry(KHRN_GENERIC_MAP(ENTRY_T) *base, uint32_t capacity, KHRN_GENERIC_MAP_KEY_T key) { uint32_t h = hash(key, capacity); while (!KHRN_GENERIC_MAP_CMP_VALUE(base[h].value, KHRN_GENERIC_MAP_VALUE_NONE)) { if (base[h].key == key) { return (KHRN_GENERIC_MAP_CMP_VALUE(base[h].value, KHRN_GENERIC_MAP_VALUE_DELETED)) ? NULL : (base + h); } if (++h == capacity) { h = 0; } } return NULL; } static KHRN_GENERIC_MAP(ENTRY_T) *get_free_entry(KHRN_GENERIC_MAP(ENTRY_T) *base, uint32_t capacity, KHRN_GENERIC_MAP_KEY_T key) { uint32_t h = hash(key, capacity); while ((!KHRN_GENERIC_MAP_CMP_VALUE(base[h].value, KHRN_GENERIC_MAP_VALUE_DELETED)) && (!KHRN_GENERIC_MAP_CMP_VALUE(base[h].value, KHRN_GENERIC_MAP_VALUE_NONE))) { if (++h == capacity) { h = 0; } } return base + h; } static bool realloc_storage(KHRN_GENERIC_MAP(T) *map, uint32_t new_capacity) { #ifdef KHRN_GENERIC_MAP_RELOCATABLE MEM_HANDLE_T handle = map->storage; KHRN_GENERIC_MAP(ENTRY_T) *base; #else KHRN_GENERIC_MAP(ENTRY_T) *base = map->storage; #endif uint32_t capacity = map->capacity; uint32_t i; /* new map */ if (!khrn_generic_map(init)(map, new_capacity)) { /* khrn_generic_map(init) fills in struct only once it is sure to succeed, * so if we get here struct will be unmodified */ return false; } /* copy entries across to new map and destroy old map */ #ifdef KHRN_GENERIC_MAP_RELOCATABLE base = (KHRN_GENERIC_MAP(ENTRY_T) *)mem_lock(handle); #endif for (i = 0; i != capacity; ++i) { if ((!KHRN_GENERIC_MAP_CMP_VALUE(base[i].value, KHRN_GENERIC_MAP_VALUE_DELETED)) && (!KHRN_GENERIC_MAP_CMP_VALUE(base[i].value, KHRN_GENERIC_MAP_VALUE_NONE))) { verify(khrn_generic_map(insert)(map, base[i].key, base[i].value)); /* khrn_generic_map(insert) can only fail if the map is too small */ #ifdef KHRN_GENERIC_MAP_RELEASE_VALUE KHRN_GENERIC_MAP_RELEASE_VALUE(base[i].value); /* new reference added by khrn_generic_map(insert) */ #endif } } #ifdef KHRN_GENERIC_MAP_RELOCATABLE mem_unlock(handle); mem_release(handle); #else KHRN_GENERIC_MAP_FREE(base); #endif return true; } bool khrn_generic_map(init)(KHRN_GENERIC_MAP(T) *map, uint32_t capacity) { #ifdef KHRN_GENERIC_MAP_RELOCATABLE MEM_HANDLE_T handle; #else KHRN_GENERIC_MAP(ENTRY_T) *base; uint32_t i; #endif /* we need (capacity - 1) > (capacity / 2) and (capacity - 1) > ((3 * capacity) / 4) to ensure we always have at least 1 unused slot the smallest number that satisfies these constraints is 8 (7 > 4, 7 > 6) */ vcos_assert(capacity >= 8); vcos_assert(is_power_of_2(capacity)); /* hash stuff assumes this */ /* alloc and clear storage */ #define STRINGIZE2(X) #X #define STRINGIZE(X) STRINGIZE2(X) /* X will be expanded here */ #ifdef KHRN_GENERIC_MAP_RELOCATABLE handle = mem_alloc_ex(capacity * sizeof(KHRN_GENERIC_MAP(ENTRY_T)), alignof(KHRN_GENERIC_MAP(ENTRY_T)), MEM_FLAG_INIT, STRINGIZE(KHRN_GENERIC_MAP(T)) ".storage", MEM_COMPACT_DISCARD); /* no term (struct containing KHRN_GENERIC_MAP(T) must call khrn_generic_map(term)()) */ if (handle == MEM_INVALID_HANDLE) { return false; } /* all values already initialised to KHRN_GENERIC_MAP_VALUE_NONE */ #else base = (KHRN_GENERIC_MAP(ENTRY_T) *)KHRN_GENERIC_MAP_ALLOC(capacity * sizeof(KHRN_GENERIC_MAP(ENTRY_T)), STRINGIZE(KHRN_GENERIC_MAP(T)) ".storage"); if (!base) { return false; } for (i = 0; i != capacity; ++i) { base[i].value = KHRN_GENERIC_MAP_VALUE_NONE; } #endif #undef STRINGIZE #undef STRINGIZE2 /* fill in struct (do this only once we are sure to succeed -- realloc_storage and khrn_generic_map(term) under gl object semantics rely on this behaviour) */ map->entries = 0; map->deletes = 0; #ifdef KHRN_GENERIC_MAP_RELOCATABLE map->storage = handle; #else map->storage = base; #endif map->capacity = capacity; return true; } /* in KHRN_GENERIC_MAP_RELOCATABLE mode, khrn_generic_map(term) may be called: - before init: map->storage will be MEM_INVALID_HANDLE. - after init fails: map is unchanged. - after term: map->storage will have been set back to MEM_INVALID_HANDLE. */ void khrn_generic_map(term)(KHRN_GENERIC_MAP(T) *map) { #ifdef KHRN_GENERIC_MAP_RELOCATABLE if (map->storage != MEM_INVALID_HANDLE) { #endif #ifdef KHRN_GENERIC_MAP_RELEASE_VALUE #ifdef KHRN_GENERIC_MAP_RELOCATABLE KHRN_GENERIC_MAP(ENTRY_T) *base = (KHRN_GENERIC_MAP(ENTRY_T) *)mem_lock(map->storage); #else KHRN_GENERIC_MAP(ENTRY_T) *base = map->storage; #endif uint32_t i; for (i = 0; i != map->capacity; ++i) { if ((!KHRN_GENERIC_MAP_CMP_VALUE(base[i].value, KHRN_GENERIC_MAP_VALUE_DELETED)) && (!KHRN_GENERIC_MAP_CMP_VALUE(base[i].value, KHRN_GENERIC_MAP_VALUE_NONE))) { KHRN_GENERIC_MAP_RELEASE_VALUE(base[i].value); } } #ifdef KHRN_GENERIC_MAP_RELOCATABLE mem_unlock(map->storage); #endif #endif #ifdef KHRN_GENERIC_MAP_RELOCATABLE mem_release(map->storage); map->storage = MEM_INVALID_HANDLE; } #else KHRN_GENERIC_MAP_FREE(map->storage); #endif } bool khrn_generic_map(insert)(KHRN_GENERIC_MAP(T) *map, KHRN_GENERIC_MAP_KEY_T key, KHRN_GENERIC_MAP_VALUE_T value) { uint32_t capacity = map->capacity; KHRN_GENERIC_MAP(ENTRY_T) *entry; vcos_assert(!KHRN_GENERIC_MAP_CMP_VALUE(value, KHRN_GENERIC_MAP_VALUE_DELETED)); vcos_assert(!KHRN_GENERIC_MAP_CMP_VALUE(value, KHRN_GENERIC_MAP_VALUE_NONE)); entry = get_entry( #ifdef KHRN_GENERIC_MAP_RELOCATABLE (KHRN_GENERIC_MAP(ENTRY_T) *)mem_lock(map->storage), #else map->storage, #endif capacity, key); if (entry) { #ifdef KHRN_GENERIC_MAP_ACQUIRE_VALUE KHRN_GENERIC_MAP_ACQUIRE_VALUE(value); #endif #ifdef KHRN_GENERIC_MAP_RELEASE_VALUE KHRN_GENERIC_MAP_RELEASE_VALUE(entry->value); #endif entry->value = value; #ifdef KHRN_GENERIC_MAP_RELOCATABLE mem_unlock(map->storage); #endif } else { #ifdef KHRN_GENERIC_MAP_RELOCATABLE mem_unlock(map->storage); #endif if (map->entries > (capacity / 2)) { capacity *= 2; if (!realloc_storage(map, capacity)) { return false; } } else if ((map->entries + map->deletes) > ((3 * capacity) / 4)) { if (!realloc_storage(map, capacity)) { return false; } } #ifdef KHRN_GENERIC_MAP_ACQUIRE_VALUE KHRN_GENERIC_MAP_ACQUIRE_VALUE(value); #endif entry = get_free_entry( #ifdef KHRN_GENERIC_MAP_RELOCATABLE (KHRN_GENERIC_MAP(ENTRY_T) *)mem_lock(map->storage), #else map->storage, #endif capacity, key); if (KHRN_GENERIC_MAP_CMP_VALUE(entry->value, KHRN_GENERIC_MAP_VALUE_DELETED)) { vcos_assert(map->deletes > 0); --map->deletes; } entry->key = key; entry->value = value; ++map->entries; #ifdef KHRN_GENERIC_MAP_RELOCATABLE mem_unlock(map->storage); #endif } return true; } bool khrn_generic_map(delete)(KHRN_GENERIC_MAP(T) *map, KHRN_GENERIC_MAP_KEY_T key) { KHRN_GENERIC_MAP(ENTRY_T) *entry = get_entry( #ifdef KHRN_GENERIC_MAP_RELOCATABLE (KHRN_GENERIC_MAP(ENTRY_T) *)mem_lock(map->storage), #else map->storage, #endif map->capacity, key); if (entry) { #ifdef KHRN_GENERIC_MAP_RELEASE_VALUE KHRN_GENERIC_MAP_RELEASE_VALUE(entry->value); #endif entry->value = KHRN_GENERIC_MAP_VALUE_DELETED; ++map->deletes; vcos_assert(map->entries > 0); --map->entries; } #ifdef KHRN_GENERIC_MAP_RELOCATABLE mem_unlock(map->storage); #endif return !!entry; } uint32_t khrn_generic_map(get_count)(KHRN_GENERIC_MAP(T) *map) { return map->entries; } KHRN_GENERIC_MAP_VALUE_T khrn_generic_map(lookup)(KHRN_GENERIC_MAP(T) *map, KHRN_GENERIC_MAP_KEY_T key) #ifdef KHRN_GENERIC_MAP_RELOCATABLE { KHRN_GENERIC_MAP_VALUE_T value = khrn_generic_map(lookup_locked)(map, key, mem_lock(map->storage)); mem_unlock(map->storage); return value; } KHRN_GENERIC_MAP_VALUE_T khrn_generic_map(lookup_locked)(KHRN_GENERIC_MAP(T) *map, KHRN_GENERIC_MAP_KEY_T key, void *storage) #endif { KHRN_GENERIC_MAP(ENTRY_T) *entry = get_entry( #ifdef KHRN_GENERIC_MAP_RELOCATABLE (KHRN_GENERIC_MAP(ENTRY_T) *)storage, #else map->storage, #endif map->capacity, key); return entry ? entry->value : KHRN_GENERIC_MAP_VALUE_NONE; } void khrn_generic_map(iterate)(KHRN_GENERIC_MAP(T) *map, KHRN_GENERIC_MAP(CALLBACK_T) func, void *data) { #ifdef KHRN_GENERIC_MAP_RELOCATABLE KHRN_GENERIC_MAP(ENTRY_T) *base = (KHRN_GENERIC_MAP(ENTRY_T) *)mem_lock(map->storage); #else KHRN_GENERIC_MAP(ENTRY_T) *base = map->storage; #endif uint32_t i; for (i = 0; i != map->capacity; ++i) { if ((!KHRN_GENERIC_MAP_CMP_VALUE(base[i].value, KHRN_GENERIC_MAP_VALUE_DELETED)) && (!KHRN_GENERIC_MAP_CMP_VALUE(base[i].value, KHRN_GENERIC_MAP_VALUE_NONE))) { func(map, base[i].key, base[i].value, data); } } #ifdef KHRN_GENERIC_MAP_RELOCATABLE mem_unlock(map->storage); #endif }