This repository was archived by the owner on May 27, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 119
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit afb9818
Showing
4 changed files
with
383 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
Copyright (c) 2014 rxi | ||
|
||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
this software and associated documentation files (the "Software"), to deal in | ||
the Software without restriction, including without limitation the rights to | ||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
of the Software, and to permit persons to whom the Software is furnished to do | ||
so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
# map | ||
A type-safe generic hashmap implementation for C. | ||
|
||
## Installation | ||
The [map.c](src/map.c?raw=1) and [map.h](src/map.h?raw=1) files can be dropped | ||
into an existing C project and compiled along with it. | ||
|
||
|
||
## Usage | ||
Before using a map it should first be initialised using the `map_init()` | ||
function. | ||
```c | ||
map_int_t m; | ||
map_init(&m); | ||
``` | ||
Values can added to a map using the `map_set()` function. | ||
```c | ||
map_set(&m, "testkey", 123); | ||
``` | ||
|
||
To retrieve a value from a map, the `map_get()` function can be used. | ||
`map_get()` will return a pointer to the key's value, or `NULL` if no mapping | ||
for that key exists. | ||
```c | ||
int *val = map_get(&m, "testkey"); | ||
if (val) { | ||
printf("value: %d\n", *val); | ||
} else { | ||
printf("value not found\n"); | ||
} | ||
``` | ||
|
||
When you are done with a map the `map_deinit()` function should be called on | ||
it. This will free any memory the map allocated during use. | ||
```c | ||
map_deinit(&m); | ||
``` | ||
## Types | ||
map.h provides the following predefined map types: | ||
Contained Type | Type name | ||
----------------|---------------------------------- | ||
void* | map_void_t | ||
char* | map_str_t | ||
int | map_int_t | ||
char | map_char_t | ||
float | map_float_t | ||
double | map_double_t | ||
To define a new map type the `map_t()` macro should be used: | ||
```c | ||
/* Creates the type uint_map_t for storing unsigned ints */ | ||
typedef map_t(unsigned int) uint_map_t; | ||
``` | ||
|
||
## Functions | ||
All map functions are macro functions. The parameter `m` in each function | ||
should be a pointer to the map struct which the operation is to be performed | ||
on. The `key` parameter should always be a string value. | ||
|
||
### map\_t(T) | ||
Creates a map struct for containing values of type `T`. | ||
```c | ||
/* Typedefs the struct `fp_map_t` as a container for type FILE* */ | ||
typedef map_t(FILE*) fp_map_t; | ||
``` | ||
### map\_init(m) | ||
Initialises the map, this must be called before the map can be used. | ||
### map\_deinit(m) | ||
Deinitialises the map, freeing the memory the map allocated during use; | ||
this should be called when we're finished with a map. | ||
### map\_get(m, key) | ||
Returns a pointer to the value of the given `key`. If no mapping for the `key` | ||
exists then `NULL` will be returned. | ||
### map\_set(m, key, value) | ||
Sets the given `key` to the given `value`. Returns `0` on success, otherwise | ||
`-1` is returned and the map remains unchanged. | ||
### map\_remove(m, key) | ||
Removes the mapping of the given `key` from the map. If the `key` does not | ||
exist in the map then the function has no effect. | ||
### map\_iter(m) | ||
Returns a `map_iter_t` which can be used with `map_next()` to iterate all the | ||
keys in the map. | ||
### map\_next(m, iter) | ||
Uses the `map_iter_t` returned by `map_iter()` to iterate all the keys in the | ||
map. `map_next()` returns a key with each call and returns `NULL` when there | ||
are no more keys. | ||
```c | ||
const char *key; | ||
map_iter_t iter = map_iter(&m); | ||
while ((key = map_next(&m, &iter))) { | ||
printf("%s -> %d", key, *map_get(&m, key)); | ||
} | ||
``` | ||
|
||
## License | ||
This library is free software; you can redistribute it and/or modify it under | ||
the terms of the MIT license. See [LICENSE](LICENSE) for details. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include "map.h" | ||
|
||
|
||
static unsigned map_hash(const char *str) { | ||
unsigned hash = 5381; | ||
while (*str) { | ||
hash = ((hash << 5) + hash) ^ *str++; | ||
} | ||
return hash; | ||
} | ||
|
||
|
||
static map_node_t *map_newnode(const char *key, void *value, int vsize) { | ||
map_node_t *node; | ||
int klen = strlen(key); | ||
int voffset = klen + (sizeof(void*) - klen % sizeof(void*)); | ||
node = malloc(sizeof(*node) + voffset + vsize); | ||
memcpy(node->key, key, klen + 1); | ||
node->hash = map_hash(key); | ||
node->value = node->key + voffset; | ||
memcpy(node->value, value, vsize); | ||
return node; | ||
} | ||
|
||
|
||
static int map_bucketidx(map_base_t *m, unsigned hash) { | ||
/* If the implementation is changed to allow a non-power-of-2 bucket count, | ||
* the line below should be changed to use mod instead of AND */ | ||
return hash & (m->nbuckets - 1); | ||
} | ||
|
||
|
||
static void map_addnode(map_base_t *m, map_node_t *node) { | ||
int n = map_bucketidx(m, node->hash); | ||
node->next = m->buckets[n]; | ||
m->buckets[n] = node; | ||
} | ||
|
||
|
||
static int map_resize(map_base_t *m, int nbuckets) { | ||
map_node_t *nodes, *node, *next; | ||
map_node_t **buckets; | ||
int i; | ||
/* Chain all nodes together */ | ||
nodes = NULL; | ||
i = m->nbuckets; | ||
while (i--) { | ||
node = (m->buckets)[i]; | ||
while (node) { | ||
next = node->next; | ||
node->next = nodes; | ||
nodes = node; | ||
node = next; | ||
} | ||
} | ||
/* Reset buckets */ | ||
buckets = realloc(m->buckets, sizeof(*m->buckets) * nbuckets); | ||
if (buckets != NULL) { | ||
m->buckets = buckets; | ||
m->nbuckets = nbuckets; | ||
} | ||
if (m->buckets) { | ||
memset(m->buckets, 0, sizeof(*m->buckets) * m->nbuckets); | ||
/* Re-add nodes to buckets */ | ||
node = nodes; | ||
while (node) { | ||
next = node->next; | ||
map_addnode(m, node); | ||
node = next; | ||
} | ||
} | ||
/* Return error code if realloc() failed */ | ||
return (buckets == NULL) ? -1 : 0; | ||
} | ||
|
||
|
||
static map_node_t **map_getref(map_base_t *m, const char *key) { | ||
unsigned hash = map_hash(key); | ||
map_node_t **next; | ||
if (m->nbuckets > 0) { | ||
next = &m->buckets[map_bucketidx(m, hash)]; | ||
while (*next) { | ||
if ((*next)->hash == hash && !strcmp((*next)->key, key)) { | ||
return next; | ||
} | ||
next = &(*next)->next; | ||
} | ||
} | ||
return NULL; | ||
} | ||
|
||
|
||
void map_deinit_(map_base_t *m) { | ||
map_node_t *next, *node; | ||
int i; | ||
i = m->nbuckets; | ||
while (i--) { | ||
node = m->buckets[i]; | ||
while (node) { | ||
next = node->next; | ||
free(node); | ||
node = next; | ||
} | ||
} | ||
free(m->buckets); | ||
} | ||
|
||
|
||
void *map_get_(map_base_t *m, const char *key) { | ||
map_node_t **next = map_getref(m, key); | ||
return next ? (*next)->value : NULL; | ||
} | ||
|
||
|
||
int map_set_(map_base_t *m, const char *key, void *value, int vsize) { | ||
int n, err; | ||
map_node_t **next, *node; | ||
/* Find & replace existing node */ | ||
next = map_getref(m, key); | ||
if (next) { | ||
memcpy((*next)->value, value, vsize); | ||
return 0; | ||
} | ||
/* Add new node */ | ||
node = map_newnode(key, value, vsize); | ||
if (node == NULL) goto fail; | ||
if (m->nnodes >= m->nbuckets) { | ||
n = (m->nbuckets > 0) ? (m->nbuckets << 1) : 1; | ||
err = map_resize(m, n); | ||
if (err) goto fail; | ||
} | ||
map_addnode(m, node); | ||
m->nnodes++; | ||
return 0; | ||
fail: | ||
if (node) free(node); | ||
return -1; | ||
} | ||
|
||
|
||
void map_remove_(map_base_t *m, const char *key) { | ||
map_node_t *node; | ||
map_node_t **next = map_getref(m, key); | ||
if (next) { | ||
node = *next; | ||
*next = (*next)->next; | ||
free(node); | ||
m->nnodes--; | ||
} | ||
} | ||
|
||
|
||
map_iter_t map_iter_(void) { | ||
map_iter_t iter; | ||
iter.bucketidx = -1; | ||
iter.node = NULL; | ||
return iter; | ||
} | ||
|
||
|
||
const char *map_next_(map_base_t *m, map_iter_t *iter) { | ||
if (iter->node) { | ||
iter->node = iter->node->next; | ||
if (iter->node == NULL) goto nextBucket; | ||
} else { | ||
nextBucket: | ||
do { | ||
if (++iter->bucketidx >= m->nbuckets) { | ||
return NULL; | ||
} | ||
iter->node = m->buckets[iter->bucketidx]; | ||
} while (iter->node == NULL); | ||
} | ||
return iter->node->key; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
#ifndef MAP_H | ||
#define MAP_H | ||
|
||
#include <string.h> | ||
|
||
#define MAP_VERSION "0.0.0" | ||
|
||
struct map_node_t; | ||
typedef struct map_node_t map_node_t; | ||
|
||
struct map_node_t { | ||
unsigned hash; | ||
void *value; | ||
map_node_t *next; | ||
char key[1]; | ||
}; | ||
|
||
typedef struct { | ||
map_node_t **buckets; | ||
unsigned nbuckets, nnodes; | ||
} map_base_t; | ||
|
||
typedef struct { | ||
unsigned bucketidx; | ||
map_node_t *node; | ||
} map_iter_t; | ||
|
||
|
||
#define map_t(T)\ | ||
struct { map_base_t base; T *ref; T tmp; } | ||
|
||
|
||
#define map_init(m)\ | ||
memset(m, 0, sizeof(*m)) | ||
|
||
|
||
#define map_deinit(m)\ | ||
map_deinit_(&(m)->base) | ||
|
||
|
||
#define map_get(m, key)\ | ||
( (m)->ref = map_get_(&(m)->base, key) ) | ||
|
||
|
||
#define map_set(m, key, value)\ | ||
( (m)->tmp = (value),\ | ||
map_set_(&(m)->base, key, &(m)->tmp, sizeof((m)->tmp)) ) | ||
|
||
|
||
#define map_remove(m, key)\ | ||
map_remove_(&(m)->base, key) | ||
|
||
|
||
#define map_iter(m)\ | ||
map_iter_() | ||
|
||
|
||
#define map_next(m, iter)\ | ||
map_next_(&(m)->base, iter) | ||
|
||
|
||
void map_deinit_(map_base_t *m); | ||
void *map_get_(map_base_t *m, const char *key); | ||
int map_set_(map_base_t *m, const char *key, void *value, int vsize); | ||
void map_remove_(map_base_t *m, const char *key); | ||
map_iter_t map_iter_(void); | ||
const char *map_next_(map_base_t *m, map_iter_t *iter); | ||
|
||
|
||
typedef map_t(void*) map_void_t; | ||
typedef map_t(char*) map_str_t; | ||
typedef map_t(int) map_int_t; | ||
typedef map_t(char) map_char_t; | ||
typedef map_t(float) map_float_t; | ||
typedef map_t(double) map_double_t; | ||
|
||
#endif |