Created
August 31, 2020 15:44
-
-
Save SteffenBauer/92cfa5cf03acb5d479346c7c0a13a02d to your computer and use it in GitHub Desktop.
Basic key/value in C, including simple unit test system
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
/* A key/value dict system in C */ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <assert.h> | |
#define TEST TRUE /* Do the unit tests */ | |
typedef struct dict_t_struct { | |
char *key; | |
char *value; | |
struct dict_t_struct *next; | |
} dict_t; | |
static dict_t *config = NULL; | |
int configLen() { | |
int count = 0; | |
dict_t *ptr; | |
for (ptr = config; ptr != NULL; ptr = ptr->next) | |
count++; | |
return count; | |
} | |
void configAddItem(char *key, char *value) { | |
dict_t *ptr; | |
dict_t *new; | |
#ifdef DEBUG | |
printf("Adding key '%s' value '%s'\n", key, value); | |
#endif | |
for (ptr = config; ptr != NULL; ptr = ptr->next) { | |
if (strcmp(ptr->key, key) == 0) { | |
ptr->value = realloc(ptr->value, strlen(value)+1); | |
strcpy(ptr->value, value); | |
return; | |
} | |
} | |
new = malloc(sizeof(struct dict_t_struct)); | |
new->key = malloc(strlen(key)+1); | |
new->value = malloc(strlen(value)+1); | |
strcpy(new->key, key); | |
strcpy(new->value, value); | |
new->next = config; | |
config = new; | |
return; | |
} | |
void configPrint() { | |
dict_t *ptr; | |
int count = 1; | |
if (config == NULL) printf("config is empty\n"); | |
else printf("config entries:\n"); | |
for (ptr = config; ptr != NULL; ptr = ptr->next) { | |
printf("-- Entry %d Key '%s' -> Value '%s' / Next '%s'\n", count++, ptr->key, ptr->value, ptr->next == NULL ? "NO":"YES"); | |
} | |
} | |
void configDel() { | |
dict_t *ptr; | |
while (config != NULL) { | |
ptr = config; | |
config = config->next; | |
#ifdef DEBUG | |
printf("Cleanup entry key '%s' value '%s'\n", ptr->key, ptr->value); | |
#endif | |
free(ptr->key); | |
free(ptr->value); | |
free(ptr); | |
} | |
} | |
char *configGetItem(char *key) { | |
dict_t *ptr; | |
for (ptr = config; ptr != NULL; ptr = ptr->next) { | |
if (strcmp(ptr->key, key) == 0) { | |
return ptr->value; | |
} | |
} | |
return NULL; | |
} | |
void configDelItem(char *key) { | |
dict_t *ptr, *prev; | |
ptr = config; | |
prev = NULL; | |
while(ptr != NULL) { | |
if(strcmp(ptr->key, key) == 0) { | |
if (prev == NULL && ptr->next != NULL) | |
config = ptr->next; | |
else if (prev != NULL && ptr->next != NULL) | |
prev->next = ptr->next; | |
else if (prev == NULL && ptr->next == NULL) | |
config = NULL; | |
else | |
prev->next = NULL; | |
#ifdef DEBUG | |
printf("Remove key '%s' value '%s'\n", ptr->key, ptr->value); | |
#endif | |
free(ptr->key); | |
free(ptr->value); | |
free(ptr); | |
return; | |
} | |
prev = ptr; | |
ptr = ptr->next; | |
} | |
printf("Del: Key '%s' not in config\n", key); | |
} | |
#ifdef TEST | |
void test_setup() { | |
config = NULL; | |
} | |
void test_teardown() { | |
configDel(); | |
} | |
void test_empty_len() { | |
assert(configLen() == 0); | |
} | |
void test_add_foo() { | |
configAddItem("foo", "bar"); | |
assert(strcmp(configGetItem("foo"), "bar") == 0); | |
assert(configGetItem("bar") == NULL); | |
assert(configLen() == 1); | |
} | |
void test_add_foo_bar() { | |
configAddItem("foo", "bar"); | |
configAddItem("bar", "foo"); | |
assert(strcmp(configGetItem("foo"), "bar") == 0); | |
assert(strcmp(configGetItem("bar"), "foo") == 0); | |
assert(configLen() == 2); | |
} | |
void test_remove_top() { | |
configAddItem("foo", "bar"); | |
configAddItem("bar", "foo"); | |
configAddItem("barbar", "foo2"); | |
assert(configLen() == 3); | |
configDelItem("barbar"); | |
assert(configLen() == 2); | |
assert(strcmp(configGetItem("foo"), "bar") == 0); | |
assert(strcmp(configGetItem("bar"), "foo") == 0); | |
assert(configGetItem("barbar") == NULL); | |
} | |
void test_remove_mid() { | |
configAddItem("foo", "bar"); | |
configAddItem("bar", "foo"); | |
configAddItem("barbar", "foo2"); | |
assert(configLen() == 3); | |
configDelItem("bar"); | |
assert(configLen() == 2); | |
assert(strcmp(configGetItem("foo"), "bar") == 0); | |
assert(configGetItem("bar") == NULL); | |
assert(strcmp(configGetItem("barbar"), "foo2") == 0); | |
} | |
void test_remove_last() { | |
configAddItem("foo", "bar"); | |
configAddItem("bar", "foo"); | |
configAddItem("barbar", "foo2"); | |
assert(configLen() == 3); | |
configDelItem("foo"); | |
assert(configLen() == 2); | |
assert(configGetItem("foo") == NULL); | |
assert(strcmp(configGetItem("bar"), "foo") == 0); | |
assert(strcmp(configGetItem("barbar"), "foo2") == 0); | |
} | |
struct test_def { | |
void (*test)(); | |
char *testname; | |
}; | |
struct test_def tests[] = { | |
{test_empty_len, "Test empty dict length "}, | |
{test_add_foo, "Test add one entry "}, | |
{test_add_foo_bar, "Test add two entries "}, | |
{test_remove_top, "Test remove from top of dict"}, | |
{test_remove_mid, "Test remove from mid of dict"}, | |
{test_remove_last, "Test remove last entry "}, | |
{NULL, ""} | |
}; | |
int main(int argc, char **argv) { | |
int test_count = 0; | |
while(tests[test_count].test != NULL) { | |
test_setup(); | |
printf("Do test '%s' - ", tests[test_count].testname); | |
tests[test_count].test(); | |
printf("[OK]\n"); | |
test_teardown(); | |
test_count++; | |
} | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment