mirror of
https://github.com/cjdelisle/cjdns
synced 2025-10-06 00:32:50 +02:00
233 lines
7.2 KiB
C
233 lines
7.2 KiB
C
/* vim: set expandtab ts=4 sw=4: */
|
|
/*
|
|
* You may redistribute this program and/or modify it under the terms of
|
|
* the GNU General Public License as published by the Free Software Foundation,
|
|
* either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#ifndef Map_H
|
|
#define Map_H
|
|
#endif // This header can be used multiple times as long as the name is different.
|
|
|
|
#include "util/Bits.h"
|
|
|
|
|
|
#if defined(Map_KEY_TYPE)
|
|
Assert_compileTime(!(sizeof(Map_KEY_TYPE) % 4));
|
|
#define Map_ENABLE_KEYS
|
|
#elif !defined(Map_ENABLE_HANDLES)
|
|
#error must define Map_KEY_TYPE or Map_ENABLE_HANDLES or both
|
|
#endif
|
|
#ifndef Map_VALUE_TYPE
|
|
#error must define Map_VALUE_TYPE
|
|
#endif
|
|
#ifndef Map_NAME
|
|
#error must give this map type a name by defining Map_NAME
|
|
#endif
|
|
|
|
#define Map_CONTEXT Map_GLUE(Map_, Map_NAME)
|
|
#define Map_FUNCTION(name) Map_GLUE(Map_GLUE(Map_GLUE(Map_, Map_NAME),_), name)
|
|
#define Map_GLUE(x, y) Map_GLUE2(x, y)
|
|
#define Map_GLUE2(x, y) x ## y
|
|
|
|
#ifdef Map_ENABLE_KEYS
|
|
// Hashcode calculator.
|
|
static inline uint32_t Map_FUNCTION(hash)(Map_KEY_TYPE* key);
|
|
#ifndef Map_USE_HASH
|
|
#include "util/Hash.h"
|
|
static inline uint32_t Map_FUNCTION(hash)(Map_KEY_TYPE* key)
|
|
{
|
|
return Hash_compute((uint8_t*)key, sizeof(Map_KEY_TYPE));
|
|
}
|
|
#endif
|
|
|
|
|
|
static inline int Map_FUNCTION(compare)(Map_KEY_TYPE* keyA, Map_KEY_TYPE* keyB);
|
|
#ifndef Map_USE_COMPARATOR
|
|
static inline int Map_FUNCTION(compare)(Map_KEY_TYPE* keyA, Map_KEY_TYPE* keyB)
|
|
{
|
|
return Bits_memcmp(keyA, keyB, sizeof(Map_KEY_TYPE));
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
struct Map_CONTEXT
|
|
{
|
|
#ifdef Map_ENABLE_KEYS
|
|
uint32_t* hashCodes;
|
|
Map_KEY_TYPE* keys;
|
|
#endif
|
|
|
|
#ifdef Map_ENABLE_HANDLES
|
|
uint32_t* handles;
|
|
uint32_t nextHandle;
|
|
#endif
|
|
|
|
Map_VALUE_TYPE* values;
|
|
|
|
uint32_t count;
|
|
uint32_t capacity;
|
|
|
|
struct Allocator* allocator;
|
|
};
|
|
|
|
static inline struct Map_CONTEXT* Map_FUNCTION(new)(struct Allocator* allocator)
|
|
{
|
|
return Allocator_clone(allocator, (&(struct Map_CONTEXT) {
|
|
.allocator = allocator
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* This is a very hot loop,
|
|
* a large amount of code relies on this being fast so it is a good target for optimization.
|
|
*/
|
|
#ifdef Map_ENABLE_KEYS
|
|
static inline int Map_FUNCTION(indexForKey)(Map_KEY_TYPE* key, struct Map_CONTEXT* map)
|
|
{
|
|
uint32_t hashCode = (Map_FUNCTION(hash)(key));
|
|
for (uint32_t i = 0; i < map->count; i++) {
|
|
if (map->hashCodes[i] == hashCode
|
|
&& Map_FUNCTION(compare)(key, &map->keys[i]) == 0)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
#ifdef Map_ENABLE_HANDLES
|
|
static inline int Map_FUNCTION(indexForHandle)(uint32_t handle, struct Map_CONTEXT* map)
|
|
{
|
|
uint32_t base = 0;
|
|
for (uint32_t bufferLen = map->count; bufferLen != 0; bufferLen /= 2) {
|
|
uint32_t currentHandle = map->handles[base + (bufferLen / 2)];
|
|
if (handle >= currentHandle) {
|
|
if (currentHandle == handle) {
|
|
return base + (bufferLen / 2);
|
|
}
|
|
base += (bufferLen / 2) + 1;
|
|
bufferLen--;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @param key the key of the entry to remove.
|
|
* @param map the map to remove from.
|
|
* @return 0 if the entry is removed, -1 if it could not be found.
|
|
*/
|
|
static inline int Map_FUNCTION(remove)(int index, struct Map_CONTEXT* map)
|
|
{
|
|
if (index >= 0 && index < (int) map->count - 1) {
|
|
#ifdef Map_ENABLE_HANDLES
|
|
// If we use handels then we need to keep the map sorted.
|
|
#ifdef Map_ENABLE_KEYS
|
|
Bits_memmove(&map->hashCodes[index],
|
|
&map->hashCodes[index + 1],
|
|
(map->count - index - 1) * sizeof(uint32_t));
|
|
|
|
Bits_memmove(&map->keys[index],
|
|
&map->keys[index + 1],
|
|
(map->count - index - 1) * sizeof(Map_KEY_TYPE));
|
|
#endif
|
|
Bits_memmove(&map->handles[index],
|
|
&map->handles[index + 1],
|
|
(map->count - index - 1) * sizeof(uint32_t));
|
|
|
|
Bits_memmove(&map->values[index],
|
|
&map->values[index + 1],
|
|
(map->count - index - 1) * sizeof(Map_VALUE_TYPE));
|
|
|
|
map->count--;
|
|
#else
|
|
// No handles, we can just fold the top entry down on one to remove.
|
|
map->count--;
|
|
map->hashCodes[index] = map->hashCodes[map->count];
|
|
Bits_memcpy(&map->keys[index], &map->keys[map->count], sizeof(Map_KEY_TYPE));
|
|
Bits_memcpy(&map->values[index], &map->values[map->count], sizeof(Map_VALUE_TYPE));
|
|
#endif
|
|
return 0;
|
|
} else if (index == (int) map->count - 1) {
|
|
map->count--;
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
#ifdef Map_ENABLE_KEYS
|
|
static inline int Map_FUNCTION(put)(Map_KEY_TYPE* key,
|
|
Map_VALUE_TYPE* value,
|
|
struct Map_CONTEXT* map)
|
|
#else
|
|
static inline int Map_FUNCTION(put)(Map_VALUE_TYPE* value,
|
|
struct Map_CONTEXT* map)
|
|
#endif
|
|
{
|
|
if (map->count == map->capacity) {
|
|
#ifdef Map_ENABLE_KEYS
|
|
map->hashCodes = Allocator_realloc(map->allocator,
|
|
map->hashCodes,
|
|
sizeof(uint32_t) * (map->count + 10));
|
|
map->keys = Allocator_realloc(map->allocator,
|
|
map->keys,
|
|
sizeof(Map_KEY_TYPE) * (map->count + 10));
|
|
#endif
|
|
|
|
#ifdef Map_ENABLE_HANDLES
|
|
map->handles = Allocator_realloc(map->allocator,
|
|
map->handles,
|
|
sizeof(uint32_t) * (map->count + 10));
|
|
#endif
|
|
|
|
map->values = Allocator_realloc(map->allocator,
|
|
map->values,
|
|
sizeof(Map_VALUE_TYPE) * (map->count + 10));
|
|
|
|
map->capacity += 10;
|
|
}
|
|
|
|
int i = -1;
|
|
|
|
#ifdef Map_ENABLE_KEYS
|
|
i = Map_FUNCTION(indexForKey)(key, map);
|
|
#endif
|
|
|
|
if (i < 0) {
|
|
i = map->count;
|
|
map->count++;
|
|
#ifdef Map_ENABLE_HANDLES
|
|
map->handles[i] = map->nextHandle++;
|
|
#endif
|
|
#ifdef Map_ENABLE_KEYS
|
|
map->hashCodes[i] = (Map_FUNCTION(hash)(key));
|
|
Bits_memcpy(&map->keys[i], key, sizeof(Map_KEY_TYPE));
|
|
#endif
|
|
}
|
|
|
|
Bits_memcpy(&map->values[i], value, sizeof(Map_VALUE_TYPE));
|
|
|
|
return i;
|
|
}
|
|
|
|
#undef Map
|
|
#undef Map_Entry
|
|
#undef Map_FUNCTION
|
|
#undef Map_USE_HASH
|
|
#undef Map_NAME
|
|
#undef Map_VALUE_TYPE
|
|
#undef Map_ENABLE_HANDLES
|
|
#undef Map_KEY_TYPE
|
|
#undef Map_ENABLE_KEYS
|
|
#undef Map_USE_COMPARATOR
|