mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
341 lines
11 KiB
341 lines
11 KiB
/* |
|
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 |
|
}
|
|
|