Initial commit
This commit is contained in:
6
Makefile
Normal file
6
Makefile
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
test: main.c hm.h
|
||||||
|
cc -Wall -Wextra -ggdb -o test main.c
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf test
|
||||||
544
hm.h
Normal file
544
hm.h
Normal file
@@ -0,0 +1,544 @@
|
|||||||
|
// hm.h - v1.0.0 - MIT License
|
||||||
|
// chained hash table implementation as a single header library.
|
||||||
|
|
||||||
|
#ifndef _HM_H_
|
||||||
|
#define _HM_H_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef HM_INITIAL_CAPACITY
|
||||||
|
# define HM_INITIAL_CAPACITY 256
|
||||||
|
#endif // HM_INITIAL_CAPACITY
|
||||||
|
|
||||||
|
#ifndef HM_MAX_LOAD_FACTOR
|
||||||
|
# define HM_MAX_LOAD_FACTOR 0.75f
|
||||||
|
#endif // HM_MAX_LOAD_FACTOR
|
||||||
|
|
||||||
|
#ifndef HM_GROWTH_FACTOR
|
||||||
|
# define HM_GROWTH_FACTOR 2
|
||||||
|
#endif // HM_GROWTH_FACTOR
|
||||||
|
|
||||||
|
#ifndef HM_NO_ASSERT
|
||||||
|
# ifndef HM_ASSERT
|
||||||
|
# include <assert.h>
|
||||||
|
# define HM_ASSERT assert
|
||||||
|
# endif // HM_ASSERT
|
||||||
|
#else
|
||||||
|
# define HM_ASSERT(...) ((void)0)
|
||||||
|
#endif // HM_NO_ASSERT
|
||||||
|
|
||||||
|
#ifndef HM_REALLOC
|
||||||
|
# include <stdlib.h>
|
||||||
|
# define HM_REALLOC realloc
|
||||||
|
#endif // HM_REALLOC
|
||||||
|
|
||||||
|
#ifndef HM_FREE
|
||||||
|
# include <stdlib.h>
|
||||||
|
# define HM_FREE free
|
||||||
|
#endif // HM_FREE
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" { // Prevent name mangling
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
typedef struct Hm__Bucket {
|
||||||
|
struct Hm__Bucket *next;
|
||||||
|
struct Hm__Bucket *prev;
|
||||||
|
void *key;
|
||||||
|
void *value;
|
||||||
|
size_t value_size;
|
||||||
|
} Hm__Bucket;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Hm__Bucket **map;
|
||||||
|
size_t count;
|
||||||
|
size_t capacity;
|
||||||
|
size_t key_size;
|
||||||
|
size_t value_size;
|
||||||
|
} HashMap;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const HashMap *hm;
|
||||||
|
Hm__Bucket *bucket;
|
||||||
|
size_t index;
|
||||||
|
bool end;
|
||||||
|
} HashMapIterator;
|
||||||
|
|
||||||
|
// Create and free
|
||||||
|
HashMap hm_create(size_t key_size, size_t value_size);
|
||||||
|
void hm_free(HashMap *hm);
|
||||||
|
|
||||||
|
// Modify, access and remove
|
||||||
|
bool hm_put(HashMap *hm, const void *key, const void *value);
|
||||||
|
void *hm_get(const HashMap *hm, const void *key);
|
||||||
|
bool hm_remove(HashMap *hm, const void *key);
|
||||||
|
|
||||||
|
// Iterator functions
|
||||||
|
HashMapIterator hm_iterate(const HashMap *hm);
|
||||||
|
const void *hm_key(const HashMapIterator *it);
|
||||||
|
void *hm_value(const HashMapIterator *it);
|
||||||
|
void *hm_next(HashMapIterator *it);
|
||||||
|
|
||||||
|
// Private functions
|
||||||
|
uint64_t hm__fnv1a(const void *buffer, size_t size);
|
||||||
|
size_t hm__key_size(const HashMap *hm, const void *key);
|
||||||
|
size_t hm__value_size(const HashMap *hm, const void *value);
|
||||||
|
bool hm__keycmp(const HashMap *hm, const void *key1, const void *key2);
|
||||||
|
bool hm__rehash(HashMap *hm);
|
||||||
|
Hm__Bucket *hm__bucket_create(const void *key, size_t key_size, const void *value, size_t value_size);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#endif // _HM_H_
|
||||||
|
|
||||||
|
#ifdef HM_IMPLEMENTATION
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" { // Prevent name mangling
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
HashMap hm_create(size_t key_size, size_t value_size)
|
||||||
|
{
|
||||||
|
HashMap hm;
|
||||||
|
memset(&hm, 0, sizeof(hm));
|
||||||
|
hm.capacity = HM_INITIAL_CAPACITY;
|
||||||
|
hm.key_size = key_size;
|
||||||
|
hm.value_size = value_size;
|
||||||
|
return hm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hm_free(HashMap *hm)
|
||||||
|
{
|
||||||
|
if (hm == NULL || hm->map == NULL) {
|
||||||
|
memset(hm, 0, sizeof(*hm));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < hm->capacity; ++i) {
|
||||||
|
Hm__Bucket *cur = hm->map[i];
|
||||||
|
if (cur == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (cur != NULL) {
|
||||||
|
Hm__Bucket *next = cur->next;
|
||||||
|
HM_FREE(cur->key);
|
||||||
|
HM_FREE(cur->value);
|
||||||
|
HM_FREE(cur);
|
||||||
|
cur = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HM_FREE(hm->map);
|
||||||
|
memset(hm, 0, sizeof(*hm));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hm_put(HashMap *hm, const void *key, const void *value)
|
||||||
|
{
|
||||||
|
if (hm == NULL || key == NULL || value == NULL) {
|
||||||
|
HM_ASSERT(false && "Invalid parameters");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that HashMap is initialised
|
||||||
|
if (hm->map == NULL) {
|
||||||
|
hm->map = (Hm__Bucket**)HM_REALLOC(NULL, sizeof(*hm->map) * HM_INITIAL_CAPACITY);
|
||||||
|
if (hm->map == NULL) {
|
||||||
|
HM_ASSERT(false && "Reallocation failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hm->capacity = HM_INITIAL_CAPACITY;
|
||||||
|
memset(hm->map, 0, sizeof(*hm->map) * hm->capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rehash if necessary
|
||||||
|
if ((float)hm->count + 1 > (float)hm->capacity * HM_MAX_LOAD_FACTOR) {
|
||||||
|
if (!hm__rehash(hm)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key-value sizes
|
||||||
|
size_t key_size = hm__key_size(hm, key);
|
||||||
|
size_t value_size = hm__value_size(hm, value);
|
||||||
|
|
||||||
|
// Get the head bucket
|
||||||
|
size_t idx = hm__fnv1a(key, key_size) % hm->capacity;
|
||||||
|
Hm__Bucket *head = hm->map[idx];
|
||||||
|
|
||||||
|
// Check if key already exists
|
||||||
|
for (Hm__Bucket *cur = head; cur != NULL; cur = cur->next) {
|
||||||
|
if (!hm__keycmp(hm, key, cur->key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fixed or equal value_size
|
||||||
|
if (hm->value_size != 0 || value_size == cur->value_size) {
|
||||||
|
memcpy(cur->value, value, value_size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary buffer in case of overlapping pointers
|
||||||
|
uint8_t *tmp = (uint8_t*)HM_REALLOC(NULL, value_size);
|
||||||
|
if (tmp == NULL) {
|
||||||
|
HM_ASSERT(false && "Reallocation failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(tmp, value, value_size);
|
||||||
|
|
||||||
|
// Realloc and copy
|
||||||
|
uint8_t *new_value = (uint8_t*)HM_REALLOC(cur->value, value_size);
|
||||||
|
if (new_value == NULL) {
|
||||||
|
HM_ASSERT(false && "Reallocation failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cur->value = new_value;
|
||||||
|
memcpy(cur->value, tmp, value_size);
|
||||||
|
|
||||||
|
HM_FREE(tmp);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the new value
|
||||||
|
Hm__Bucket *new_bucket = hm__bucket_create(key, key_size, value, value_size);
|
||||||
|
if (new_bucket == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_bucket->next = head;
|
||||||
|
if (head != NULL) {
|
||||||
|
head->prev = new_bucket;
|
||||||
|
}
|
||||||
|
hm->map[idx] = new_bucket;
|
||||||
|
++hm->count;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hm_get(const HashMap *hm, const void *key)
|
||||||
|
{
|
||||||
|
if (hm == NULL) {
|
||||||
|
HM_ASSERT(false && "Invalid parameters");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hm->map == NULL || key == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t key_size = hm__key_size(hm, key);
|
||||||
|
size_t idx = hm__fnv1a(key, key_size) % hm->capacity;
|
||||||
|
|
||||||
|
Hm__Bucket *head = hm->map[idx];
|
||||||
|
if (head == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Hm__Bucket *cur = head; cur != NULL; cur = cur->next) {
|
||||||
|
if (hm__keycmp(hm, key, cur->key)) {
|
||||||
|
return cur->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hm_remove(HashMap *hm, const void *key)
|
||||||
|
{
|
||||||
|
if (hm == NULL || hm->map == NULL || key == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t key_size = hm__key_size(hm, key);
|
||||||
|
size_t idx = hm__fnv1a(key, key_size) % hm->capacity;
|
||||||
|
|
||||||
|
Hm__Bucket *head = hm->map[idx];
|
||||||
|
if (head == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Hm__Bucket *cur = head; cur != NULL; cur = cur->next) {
|
||||||
|
if (hm__keycmp(hm, key, cur->key)) {
|
||||||
|
if (cur == head) {
|
||||||
|
hm->map[idx] = cur->next;
|
||||||
|
} else {
|
||||||
|
cur->prev->next = cur->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur->next != NULL) {
|
||||||
|
cur->next->prev = cur->prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
HM_FREE(cur->key);
|
||||||
|
HM_FREE(cur->value);
|
||||||
|
HM_FREE(cur);
|
||||||
|
--hm->count;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMapIterator hm_iterate(const HashMap *hm)
|
||||||
|
{
|
||||||
|
HashMapIterator it;
|
||||||
|
memset(&it, 0, sizeof(it));
|
||||||
|
|
||||||
|
if (hm == NULL) {
|
||||||
|
HM_ASSERT(false && "Invalid parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
it.hm = hm;
|
||||||
|
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void *hm_key(const HashMapIterator *it)
|
||||||
|
{
|
||||||
|
if (it == NULL || it->hm == NULL) {
|
||||||
|
HM_ASSERT(false && "Invalid parameters");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reached the end
|
||||||
|
if (it->end) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->bucket->key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hm_value(const HashMapIterator *it)
|
||||||
|
{
|
||||||
|
if (it == NULL || it->hm == NULL) {
|
||||||
|
HM_ASSERT(false && "Invalid parameters");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reached the end
|
||||||
|
if (it->end) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->bucket->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hm_next(HashMapIterator *it)
|
||||||
|
{
|
||||||
|
if (it == NULL || it->hm == NULL) {
|
||||||
|
HM_ASSERT(false && "Invalid parameters");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reached the end
|
||||||
|
if (it->end) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it->bucket == NULL) {
|
||||||
|
// Find first list of buckets
|
||||||
|
while ((it->bucket = it->hm->map[it->index++]) == NULL);
|
||||||
|
return it->bucket->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next bucket
|
||||||
|
if (it->bucket->next != NULL) {
|
||||||
|
it->bucket = it->bucket->next;
|
||||||
|
return it->bucket->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find next list of buckets
|
||||||
|
while ((it->bucket = it->hm->map[++it->index]) == NULL);
|
||||||
|
|
||||||
|
// Reached the end
|
||||||
|
if (it->index >= it->hm->capacity) {
|
||||||
|
it->end = true;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
it->bucket = it->hm->map[it->index];
|
||||||
|
return it->bucket->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t hm__fnv1a(const void *buffer, size_t size)
|
||||||
|
{
|
||||||
|
if (buffer == NULL) {
|
||||||
|
HM_ASSERT(false && "Invalid parameters");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t *b = (const uint8_t*)buffer;
|
||||||
|
uint64_t hash = 14695981039346656037ULL;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < size; ++i) {
|
||||||
|
hash ^= b[i];
|
||||||
|
hash *= 1099511628211ULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t hm__key_size(const HashMap *hm, const void *key)
|
||||||
|
{
|
||||||
|
if (hm == NULL || key == NULL) {
|
||||||
|
HM_ASSERT(false && "Invalid parameters");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (hm->key_size == 0 ? strlen((const char*)key) + 1 : hm->key_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t hm__value_size(const HashMap *hm, const void *value)
|
||||||
|
{
|
||||||
|
if (hm == NULL || value == NULL) {
|
||||||
|
HM_ASSERT(false && "Invalid parameters");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (hm->value_size == 0 ? strlen((const char*)value) + 1 : hm->value_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hm__keycmp(const HashMap *hm, const void *key1, const void *key2)
|
||||||
|
{
|
||||||
|
if (hm == NULL || key1 == NULL || key2 == NULL) {
|
||||||
|
HM_ASSERT(false && "Invalid parameters");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hm->key_size == 0) {
|
||||||
|
return strcmp((const char*)key1, (const char*)key2) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return memcmp(key1, key2, hm->key_size) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hm__rehash(HashMap *hm)
|
||||||
|
{
|
||||||
|
if (hm == NULL || hm->map == NULL) {
|
||||||
|
HM_ASSERT(false && "Invalid parameters");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate new table
|
||||||
|
size_t new_capacity = hm->capacity * HM_GROWTH_FACTOR;
|
||||||
|
Hm__Bucket **new_map = (Hm__Bucket**)HM_REALLOC(NULL, sizeof(*new_map) * new_capacity);
|
||||||
|
if (new_map == NULL) {
|
||||||
|
HM_ASSERT(false && "Reallocation failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memset(new_map, 0, sizeof(*new_map) * new_capacity);
|
||||||
|
|
||||||
|
// Iterate old table
|
||||||
|
for (size_t i = 0; i < hm->capacity; ++i) {
|
||||||
|
Hm__Bucket *cur = hm->map[i];
|
||||||
|
while (cur != NULL) {
|
||||||
|
Hm__Bucket *next = cur->next;
|
||||||
|
|
||||||
|
size_t key_size = hm__key_size(hm, cur->key);
|
||||||
|
uint64_t idx = hm__fnv1a(cur->key, key_size) % new_capacity;
|
||||||
|
|
||||||
|
// Insert new bucket
|
||||||
|
cur->prev = NULL;
|
||||||
|
cur->next = new_map[idx];
|
||||||
|
if (new_map[idx] != NULL) {
|
||||||
|
new_map[idx]->prev = cur;
|
||||||
|
}
|
||||||
|
new_map[idx] = cur;
|
||||||
|
|
||||||
|
cur = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HM_FREE(hm->map);
|
||||||
|
hm->map = new_map;
|
||||||
|
hm->capacity = new_capacity;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hm__Bucket *hm__bucket_create(const void *key, size_t key_size, const void *value, size_t value_size)
|
||||||
|
{
|
||||||
|
if (key == NULL || value == NULL) {
|
||||||
|
HM_ASSERT(false && "Invalid parameters");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate bucket
|
||||||
|
Hm__Bucket *bucket = (Hm__Bucket*)HM_REALLOC(NULL, sizeof(*bucket));
|
||||||
|
if (bucket == NULL) {
|
||||||
|
HM_ASSERT(false && "Reallocation failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memset(bucket, 0, sizeof(*bucket));
|
||||||
|
|
||||||
|
// Allocate key
|
||||||
|
bucket->key = HM_REALLOC(NULL, key_size);
|
||||||
|
if (bucket->key == NULL) {
|
||||||
|
HM_ASSERT(false && "Reallocation failed");
|
||||||
|
HM_FREE(bucket);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memset(bucket->key, 0, key_size);
|
||||||
|
|
||||||
|
// Allocate value
|
||||||
|
bucket->value = HM_REALLOC(NULL, value_size);
|
||||||
|
if (bucket->value == NULL) {
|
||||||
|
HM_ASSERT(false && "Reallocation failed");
|
||||||
|
HM_FREE(bucket->key);
|
||||||
|
HM_FREE(bucket);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memset(bucket->value, 0, value_size);
|
||||||
|
|
||||||
|
// Copy Key-value data
|
||||||
|
memcpy(bucket->key, key, key_size);
|
||||||
|
memcpy(bucket->value, value, value_size);
|
||||||
|
bucket->value_size = value_size;
|
||||||
|
|
||||||
|
return bucket;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#endif // HM_IMPLEMENTATION
|
||||||
|
|
||||||
|
// TODO: Find a way to reduce calls to malloc
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Revision history:
|
||||||
|
*
|
||||||
|
* 1.0.0 (2025-11-18) Initial release
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 seajee
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
58
main.c
Normal file
58
main.c
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define HM_IMPLEMENTATION
|
||||||
|
#define HM_INITIAL_CAPACITY 1
|
||||||
|
#include "hm.h"
|
||||||
|
|
||||||
|
void print_hm(const HashMap *hm)
|
||||||
|
{
|
||||||
|
HashMapIterator it = hm_iterate(hm);
|
||||||
|
while (hm_next(&it) != NULL) {
|
||||||
|
printf("%-15s: ", (const char*)hm_key(&it));
|
||||||
|
printf("%d\n", *(int*)hm_value(&it));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *words[49] = {
|
||||||
|
"apple", "banana", "cherry", "date", "elderberry", "fig", "grape",
|
||||||
|
"honeydew", "apple", "banana", "kiwi", "lemon", "mango", "nectarine",
|
||||||
|
"orange", "papaya", "quince", "raspberry", "strawberry", "tangerine",
|
||||||
|
"ugli", "voavanga", "watermelon", "xigua", "yellowfruit", "zucchini",
|
||||||
|
"apple", "banana", "cherry", "mango", "kiwi", "lemon", "lemon", "orange",
|
||||||
|
"papaya", "papaya", "grape", "grape", "grape", "strawberry", "strawberry",
|
||||||
|
"date", "date", "date", "fig", "fig", "elderberry", "nectarine", "plum"
|
||||||
|
};
|
||||||
|
const size_t N = sizeof(words)/sizeof(words[0]);
|
||||||
|
const int ONE = 1;
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
HashMap freq = hm_create(0, sizeof(int));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < N; ++i) {
|
||||||
|
const char *word = words[i];
|
||||||
|
|
||||||
|
int *f = hm_get(&freq, word);
|
||||||
|
if (f == NULL) {
|
||||||
|
hm_put(&freq, word, &ONE);
|
||||||
|
} else {
|
||||||
|
*f += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print(&freq);
|
||||||
|
printf("count = %zu\n", freq.count);
|
||||||
|
|
||||||
|
HashMapIterator it = hm_iterate(&freq);
|
||||||
|
hm_next(&it);
|
||||||
|
while (hm_value(&it) != NULL) {
|
||||||
|
const void *key = hm_key(&it);
|
||||||
|
hm_next(&it);
|
||||||
|
hm_remove(&freq, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("count = %zu\n", freq.count);
|
||||||
|
hm_free(&freq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user