Added hashtable
This commit is contained in:
parent
ac136d4ffb
commit
2a436f1ea5
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.0 )
|
cmake_minimum_required(VERSION 3.10 )
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
project(wlgol
|
project(wlgol
|
||||||
VERSION 0.0.1
|
VERSION 0.0.1
|
||||||
@ -22,7 +22,6 @@ add_custom_command(
|
|||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
wlgol.c
|
|
||||||
tkeyboard.c
|
tkeyboard.c
|
||||||
tpointer.c
|
tpointer.c
|
||||||
hashtable.c
|
hashtable.c
|
||||||
@ -33,7 +32,7 @@ set(GENHEADERS
|
|||||||
${CMAKE_CURRENT_BINARY_DIR}/include/xdg-shell.h
|
${CMAKE_CURRENT_BINARY_DIR}/include/xdg-shell.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(wlgol ${SOURCES} ${GENHEADERS})
|
add_executable(wlgol wlgol.c ${SOURCES} ${GENHEADERS})
|
||||||
|
|
||||||
target_include_directories(wlgol
|
target_include_directories(wlgol
|
||||||
PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include
|
PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include
|
||||||
@ -44,3 +43,20 @@ target_link_libraries(wlgol
|
|||||||
wayland-client
|
wayland-client
|
||||||
wayland-cursor
|
wayland-cursor
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
add_executable(test_hashtable tests/test_hashtable.c
|
||||||
|
${SOURCES}
|
||||||
|
)
|
||||||
|
target_link_libraries(test_hashtable
|
||||||
|
wayland-client
|
||||||
|
wayland-cursor
|
||||||
|
)
|
||||||
|
target_include_directories(test_hashtable
|
||||||
|
PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include
|
||||||
|
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
|
)
|
||||||
|
|
||||||
|
enable_testing()
|
||||||
|
add_test(NAME Hashtable COMMAND test_hashtable)
|
||||||
|
245
hashtable.c
245
hashtable.c
@ -7,124 +7,193 @@
|
|||||||
#include "hashtable.h"
|
#include "hashtable.h"
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Hash function for hashtable
|
// entry in the hashtable
|
||||||
*
|
struct entry
|
||||||
* This function takes a string key and calculates a hash value.
|
{
|
||||||
* The hash value is used to determine the index in the hashtable
|
const char *key;
|
||||||
* where the key-value pair should be stored.
|
void *value;
|
||||||
*
|
struct entry *next;
|
||||||
* @param key The string key to hash
|
};
|
||||||
* @return The calculated hash value
|
|
||||||
*/
|
struct htobj
|
||||||
|
{
|
||||||
|
int size;
|
||||||
|
struct entry **table;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct item {
|
||||||
|
struct entry *prev, *found;
|
||||||
|
int index;
|
||||||
|
};
|
||||||
|
|
||||||
static unsigned int hash(const char *key)
|
static unsigned int hash(const char *key)
|
||||||
{
|
{
|
||||||
unsigned int hash = 0; // Initialize hash value to 0
|
unsigned int hash = 0;
|
||||||
|
|
||||||
// Iterate over each character in the key string
|
|
||||||
while (*key)
|
while (*key)
|
||||||
{
|
{
|
||||||
// Calculate the hash value by multiplying the current hash value
|
|
||||||
// with 32 (2^5) and subtracting the current hash value. Then add the
|
|
||||||
// ASCII value of the current character.
|
|
||||||
hash = (hash << 5) - hash + *key++;
|
hash = (hash << 5) - hash + *key++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the calculated hash value
|
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
hashtable hashtable_new(int size)
|
||||||
* @brief Create a new hashtable
|
|
||||||
*
|
|
||||||
* This function creates a new hashtable with the specified size.
|
|
||||||
* It initializes the table with NULL entries.
|
|
||||||
*
|
|
||||||
* @param size The size of the hashtable
|
|
||||||
* @return A pointer to the newly created hashtable
|
|
||||||
*/
|
|
||||||
hashtable *hashtable_new(int size)
|
|
||||||
{
|
{
|
||||||
// Allocate memory for the hashtable
|
// Allocate memory for the hashtable
|
||||||
hashtable *h = malloc(sizeof(hashtable));
|
hashtable h = malloc(sizeof(struct htobj));
|
||||||
|
|
||||||
// Set the size of the hashtable
|
// Set the size of the hashtable
|
||||||
h->size = size;
|
h->size = size;
|
||||||
|
|
||||||
// Allocate memory for the entry pointers
|
// Allocate memory for the entry pointers
|
||||||
h->table = malloc(sizeof(entry *) * size);
|
h->table = calloc(size, sizeof(struct entry *));
|
||||||
|
|
||||||
// Initialize all entries to NULL
|
|
||||||
for (int i = 0; i < size; i++)
|
|
||||||
{
|
|
||||||
h->table[i] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the newly created hashtable
|
// Return the newly created hashtable
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// hashtable_insert
|
struct item hashtable_lookup_item(hashtable h, const char *key)
|
||||||
void hashtable_insert(hashtable *h, const char *key, void *value) {
|
{
|
||||||
|
struct item result = {
|
||||||
|
.found = NULL,
|
||||||
|
.index = -1,
|
||||||
|
};
|
||||||
|
|
||||||
// Calculate the index using the hash function
|
|
||||||
int index = hash(key) % h->size;
|
int index = hash(key) % h->size;
|
||||||
|
struct entry *e = h->table[index];
|
||||||
|
struct entry *prev = NULL;
|
||||||
|
|
||||||
// Create a new entry
|
|
||||||
entry *e = malloc(sizeof(entry));
|
|
||||||
e->key = key;
|
|
||||||
e->value = value;
|
|
||||||
e->next = NULL;
|
|
||||||
|
|
||||||
// Insert the entry at the beginning of the linked list
|
|
||||||
e->next = h->table[index];
|
|
||||||
h->table[index] = e;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// hashtable_lookup
|
|
||||||
void *hashtable_lookup(hashtable *h, const char *key) {
|
|
||||||
|
|
||||||
// Calculate the index using the hash function
|
|
||||||
int index = hash(key) % h->size;
|
|
||||||
|
|
||||||
// Traverse the linked list at the index
|
|
||||||
entry *e = h->table[index];
|
|
||||||
while (e != NULL) {
|
while (e != NULL) {
|
||||||
if (strcmp(e->key, key) == 0) {
|
if (strcmp(e->key, key) == 0) {
|
||||||
return e->value;
|
result.index = index;
|
||||||
}
|
result.found = e;
|
||||||
e = e->next;
|
result.prev = prev;
|
||||||
}
|
return result;
|
||||||
|
|
||||||
// Return NULL if the key is not found
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// hashtable_remove
|
|
||||||
void hashtable_remove(hashtable *h, const char *key) {
|
|
||||||
|
|
||||||
// Calculate the index using the hash function
|
|
||||||
int index = hash(key) % h->size;
|
|
||||||
|
|
||||||
// Traverse the linked list at the index
|
|
||||||
entry *e = h->table[index];
|
|
||||||
entry *prev = NULL;
|
|
||||||
while (e != NULL) {
|
|
||||||
if (strcmp(e->key, key) == 0) {
|
|
||||||
if (prev == NULL) {
|
|
||||||
h->table[index] = e->next;
|
|
||||||
} else {
|
|
||||||
prev->next = e->next;
|
|
||||||
}
|
|
||||||
free(e);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
prev = e;
|
prev = e;
|
||||||
e = e->next;
|
e = e->next;
|
||||||
}
|
}
|
||||||
|
return result; // empty in this case
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hashtable_set(hashtable h, const char *key, void *value)
|
||||||
|
{
|
||||||
|
|
||||||
|
struct item item = hashtable_lookup_item(h, key);
|
||||||
|
// not found - insert new
|
||||||
|
if (item.found == NULL) {
|
||||||
|
int index = hash(key) % h->size;
|
||||||
|
|
||||||
|
// Create a new entry
|
||||||
|
struct entry *e = malloc(sizeof(struct entry));
|
||||||
|
e->key = strdup(key); // key is the copy
|
||||||
|
e->value = value;
|
||||||
|
e->next = NULL;
|
||||||
|
|
||||||
|
// Insert the entry at the beginning of the linked list
|
||||||
|
e->next = h->table[index];
|
||||||
|
h->table[index] = e;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// found replace the value
|
||||||
|
void *old_value = item.found->value;
|
||||||
|
item.found-> value = value;
|
||||||
|
|
||||||
|
return old_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hashtable_lookup(hashtable h, const char *key)
|
||||||
|
{
|
||||||
|
struct item item = hashtable_lookup_item(h, key);
|
||||||
|
if (item.found != NULL) {
|
||||||
|
return item.found->value;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hashtable_remove(hashtable h, const char *key)
|
||||||
|
{
|
||||||
|
struct item item = hashtable_lookup_item(h, key);
|
||||||
|
if (item.found == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// found, so detete it
|
||||||
|
if (item.prev != NULL) {
|
||||||
|
item.prev->next = item.found->next;
|
||||||
|
} else {
|
||||||
|
h->table[item.index] = item.found->next;
|
||||||
|
}
|
||||||
|
// free unused memory
|
||||||
|
void *value = item.found->value; // remember value
|
||||||
|
free((void*) item.found->key); // free key
|
||||||
|
free((void*) item.found); // free entry
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashtable_destroy_bucket(hashtable h, int i)
|
||||||
|
{
|
||||||
|
struct entry *e = h->table[i];
|
||||||
|
while (e != NULL) {
|
||||||
|
free((void*)e->key);
|
||||||
|
e = e->next;
|
||||||
|
free(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashtable_destroy(hashtable h)
|
||||||
|
{
|
||||||
|
if (h==NULL) return;
|
||||||
|
for (int i = 0; i < h->size; i++) {
|
||||||
|
hashtable_destroy_bucket(h, i);
|
||||||
|
}
|
||||||
|
free (h->table);
|
||||||
|
free(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
ht_iterator hashtable_iter(hashtable h)
|
||||||
|
{
|
||||||
|
ht_iterator iter = {
|
||||||
|
.h = h,
|
||||||
|
.index = -1,
|
||||||
|
.entry = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ht_iter_next(ht_iterator *iter)
|
||||||
|
{
|
||||||
|
if (iter == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hashtable h = iter->h;
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
if (iter->index >= h->size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (iter->entry == NULL) {
|
||||||
|
iter->index++;
|
||||||
|
iter->entry = h->table[iter->index];
|
||||||
|
} else {
|
||||||
|
iter->entry = iter->entry->next;
|
||||||
|
}
|
||||||
|
if (iter->entry != NULL) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *ht_iter_key(ht_iterator *iter)
|
||||||
|
{
|
||||||
|
if (iter == NULL || iter->entry == NULL) return NULL;
|
||||||
|
return iter->entry->key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *ht_iter_value(ht_iterator *iter)
|
||||||
|
{
|
||||||
|
if (iter == NULL || iter->entry == NULL) return NULL;
|
||||||
|
return iter->entry->value;
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,16 @@
|
|||||||
#ifndef HASHTABLE_H
|
#ifndef HASHTABLE_H
|
||||||
#define HASHTABLE_H
|
#define HASHTABLE_H
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
// entry in the hashtable
|
struct htobj;
|
||||||
typedef struct entry
|
typedef struct htobj *hashtable;
|
||||||
{
|
|
||||||
const char *key;
|
|
||||||
void *value;
|
|
||||||
struct entry *next;
|
|
||||||
} entry;
|
|
||||||
|
|
||||||
// hashtable
|
typedef struct {
|
||||||
typedef struct
|
struct htobj *h;
|
||||||
{
|
struct entry *entry;
|
||||||
int size;
|
int index;
|
||||||
entry **table;
|
} ht_iterator;
|
||||||
} hashtable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a new hashtable
|
* @brief Create a new hashtable
|
||||||
@ -28,7 +21,7 @@ typedef struct
|
|||||||
* @param size The size of the hashtable
|
* @param size The size of the hashtable
|
||||||
* @return A pointer to the newly created hashtable
|
* @return A pointer to the newly created hashtable
|
||||||
*/
|
*/
|
||||||
hashtable *hashtable_new(int size);
|
hashtable hashtable_new(int size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Insert a new key-value pair into the hashtable
|
* @brief Insert a new key-value pair into the hashtable
|
||||||
@ -40,8 +33,9 @@ hashtable *hashtable_new(int size);
|
|||||||
* @param h Pointer to the hashtable
|
* @param h Pointer to the hashtable
|
||||||
* @param key Pointer to the key
|
* @param key Pointer to the key
|
||||||
* @param value Pointer to the value
|
* @param value Pointer to the value
|
||||||
|
* @return null or pointer to the previous value of the key
|
||||||
*/
|
*/
|
||||||
void hashtable_insert(hashtable *h, const char *key, void *value);
|
void *hashtable_set(hashtable h, const char *key, void *value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the value associated with a key from the hashtable
|
* @brief Get the value associated with a key from the hashtable
|
||||||
@ -54,7 +48,7 @@ void hashtable_insert(hashtable *h, const char *key, void *value);
|
|||||||
* @param key Pointer to the key
|
* @param key Pointer to the key
|
||||||
* @return Pointer to the value associated with the key, or NULL if not found
|
* @return Pointer to the value associated with the key, or NULL if not found
|
||||||
*/
|
*/
|
||||||
void *hashtable_lookup(hashtable *h, const char *key);
|
void *hashtable_lookup(hashtable h, const char *key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Remove a key-value pair from the hashtable
|
* @brief Remove a key-value pair from the hashtable
|
||||||
@ -66,6 +60,57 @@ void *hashtable_lookup(hashtable *h, const char *key);
|
|||||||
*
|
*
|
||||||
* @param h Pointer to the hashtable
|
* @param h Pointer to the hashtable
|
||||||
* @param key Pointer to the key to remove
|
* @param key Pointer to the key to remove
|
||||||
|
* @return null or value associated with the removed key.
|
||||||
*/
|
*/
|
||||||
void hashtable_remove(hashtable *h, const char *key);
|
void *hashtable_remove(hashtable h, const char *key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destroy hashtable.
|
||||||
|
*
|
||||||
|
* Values must be destroyed by the caller earlier.
|
||||||
|
*
|
||||||
|
* @param h Reference to the hastable.
|
||||||
|
*/
|
||||||
|
void hashtable_destroy(hashtable h);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the iterator of key-value pairs in the hashtable
|
||||||
|
*
|
||||||
|
* This function returns the iterator of key-value pairs in the hashtable.
|
||||||
|
*
|
||||||
|
* @param h Pointer to the hashtable
|
||||||
|
* @return The iterator of key-value pairs in the hashtable
|
||||||
|
*/
|
||||||
|
ht_iterator hashtable_iter(hashtable h);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the next key-value pair from the iterator
|
||||||
|
*
|
||||||
|
* This function returns the next key-value pair from the iterator.
|
||||||
|
*
|
||||||
|
* @param iter Pointer to the iterator
|
||||||
|
* @return true if there is a next key-value pair, false otherwise
|
||||||
|
*/
|
||||||
|
bool ht_iter_next(ht_iterator *iter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the key from the iterator
|
||||||
|
*
|
||||||
|
* This function returns the key from the iterator.
|
||||||
|
*
|
||||||
|
* @param iter Pointer to the iterator
|
||||||
|
* @return The key from the iterator
|
||||||
|
*/
|
||||||
|
const char *ht_iter_key(ht_iterator *iter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the value from the iterator
|
||||||
|
*
|
||||||
|
* This function returns the value from the iterator.
|
||||||
|
*
|
||||||
|
* @param iter Pointer to the iterator
|
||||||
|
* @return The value from the iterator
|
||||||
|
*/
|
||||||
|
void *ht_iter_value(ht_iterator *iter);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
63
tests/test_hashtable.c
Normal file
63
tests/test_hashtable.c
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <hashtable.h>
|
||||||
|
|
||||||
|
void test_hashtable(int size) {
|
||||||
|
hashtable h = hashtable_new(size);
|
||||||
|
void *value1 = "to jest jakaś tam wartość";
|
||||||
|
void *value2 = "I inna";
|
||||||
|
void *value3 = "trzecia";
|
||||||
|
void *value4 = "Czwarta";
|
||||||
|
void *value5 = "Piąta";
|
||||||
|
|
||||||
|
void *retval = hashtable_set(h, "jeden", value1);
|
||||||
|
assert(retval == NULL);
|
||||||
|
|
||||||
|
void *found = hashtable_lookup(h, "jeden");
|
||||||
|
assert(found != NULL && !strcmp(value1, found));
|
||||||
|
|
||||||
|
retval = hashtable_set(h, "dwa", value2);
|
||||||
|
assert(retval == NULL);
|
||||||
|
|
||||||
|
retval = hashtable_set(h, "trzy", value3);
|
||||||
|
assert(retval == NULL);
|
||||||
|
|
||||||
|
retval = hashtable_set(h, "cztery", value4);
|
||||||
|
assert(retval == NULL);
|
||||||
|
|
||||||
|
retval = hashtable_set(h, "jeden", value5);
|
||||||
|
assert(retval != NULL);
|
||||||
|
assert (retval == value1);
|
||||||
|
|
||||||
|
retval = hashtable_lookup(h, "niema");
|
||||||
|
assert(retval == NULL);
|
||||||
|
|
||||||
|
retval = hashtable_lookup(h, "jeden");
|
||||||
|
assert(retval == value5);
|
||||||
|
|
||||||
|
ht_iterator iter = hashtable_iter(h);
|
||||||
|
|
||||||
|
while (ht_iter_next(&iter)) {
|
||||||
|
printf("Key %s, value: %s\n", ht_iter_key(&iter), (const char*) ht_iter_value(&iter));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
retval = hashtable_remove(h, "cztery");
|
||||||
|
assert (retval == value4);
|
||||||
|
retval = hashtable_remove(h, "trzy");
|
||||||
|
assert(retval == value3);
|
||||||
|
retval = hashtable_remove(h, "dwa");
|
||||||
|
assert(retval == value2);
|
||||||
|
retval = hashtable_remove(h, "jeden");
|
||||||
|
assert(retval == value5);
|
||||||
|
|
||||||
|
hashtable_destroy(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
test_hashtable(2);
|
||||||
|
test_hashtable(10);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user