Generic Unordered Set Library (similar to C++ STL unordered_set).

From: Amit
Date: Sun Feb 19 2023 - 02:54:31 EST


Generic Unordered Set Library (similar to C++ STL unordered_set).

The code is below:

-------------------------------------------
generic_unordered_set_library.c
-------------------------------------------

/*
* License: This file has been released under APACHE LICENSE, VERSION 2.0.
* The license details can be found here:
* https://www.apache.org/licenses/LICENSE-2.0
*/

#include "generic_unordered_set_library.h"

#include <stdlib.h>
#include <string.h>
#include <stddef.h>

static void us_get_matching_and_prev_elements(
struct generic_unordered_set_container *gusc_ptr,
void *data, long data_size,
struct element **pmatch, struct element **pprev)
{

struct element *temp = NULL;
struct element *prev = NULL;

*pmatch = NULL;
if (pprev) {
*pprev = NULL;
}

for (prev = NULL, temp = gusc_ptr->first;
temp != NULL;
prev = temp, temp = temp->next) {

if (temp->data_size != data_size) {
continue;
}

if (memcmp(temp->data, data, (size_t)(data_size)) == 0) { //
element matched
*pmatch = temp;
if (pprev) {
*pprev = prev;
}
break;
}

} // end of for loop

return;

} // end of us_get_matching_and_prev_elements

struct generic_unordered_set_container *us_init_generic_unordered_set_container(
call_function_before_deleting_data cfbdd_callback_function)
{

struct generic_unordered_set_container *gusc_ptr = NULL;

gusc_ptr = malloc(sizeof(*gusc_ptr));

if (!gusc_ptr) {
return NULL;
}

gusc_ptr->first = NULL;
gusc_ptr->fi = NULL;
gusc_ptr->total_number_of_elements = 0;
gusc_ptr->cfbdd_callback_function = cfbdd_callback_function;

return gusc_ptr;

} // end of us_init_generic_unordered_set_container

long us_get_total_number_of_elements(
struct generic_unordered_set_container *gusc_ptr)
{

long num_elems = 0;

num_elems = gusc_ptr->total_number_of_elements;

return num_elems;

} // end of us_get_total_number_of_elements

int us_add_new_element(struct generic_unordered_set_container *gusc_ptr,
void *data, long data_size)
{

struct element *matched_elem = NULL;
struct element *prev_elem = NULL;
struct element *new_elem = NULL;

if (!data) {
return GUSL_AT_LEAST_ONE_ARG_IS_INVALID;
}

if (data_size <= 0) {
return GUSL_AT_LEAST_ONE_ARG_IS_INVALID;
}

us_get_matching_and_prev_elements(gusc_ptr, data, data_size, &matched_elem,
&prev_elem);

if (matched_elem) {
return GUSL_ELEMENT_EXISTS;
}

new_elem = malloc(sizeof(*new_elem));
if (!new_elem) {
return GUSL_NO_MEMORY;
}

new_elem->data = malloc((size_t)(data_size));
if (!new_elem->data) {
free(new_elem);
return GUSL_NO_MEMORY;
}

new_elem->data_size = data_size;
memmove(new_elem->data, data, (size_t)(new_elem->data_size));

new_elem->next = NULL;

if (!prev_elem) { // no elements in the set
gusc_ptr->first = new_elem;
} else {
prev_elem->next = new_elem;
}

gusc_ptr->total_number_of_elements = gusc_ptr->total_number_of_elements + 1;

return GUSL_SUCCESS;

} // end of us_add_new_element

// The user is responsible for freeing the data pointer that is returned in
// *pdata.
int us_get_data_from_front_element_and_delete_front_element(
struct generic_unordered_set_container *gusc_ptr,
void **pdata, long *pdata_size)
{

struct element *temp = NULL;

if ((!pdata) || (!pdata_size)) {
return GUSL_AT_LEAST_ONE_ARG_IS_INVALID;
}

*pdata = NULL;
*pdata_size = 0;

if (gusc_ptr->total_number_of_elements == 0) {
return GUSL_SET_IS_EMPTY;
}

*pdata = gusc_ptr->first->data;
*pdata_size = gusc_ptr->first->data_size;

temp = gusc_ptr->first;
gusc_ptr->first = gusc_ptr->first->next;

temp->next = NULL;
free(temp);

gusc_ptr->total_number_of_elements = gusc_ptr->total_number_of_elements - 1;

return GUSL_SUCCESS;

} // end of us_get_data_from_front_element_and_delete_front_element

int us_is_element_present(struct generic_unordered_set_container *gusc_ptr,
void *data, long data_size)
{

struct element *matched_elem = NULL;

if (!data) {
return GUSL_AT_LEAST_ONE_ARG_IS_INVALID;
}

if (data_size <= 0) {
return GUSL_AT_LEAST_ONE_ARG_IS_INVALID;
}

if (gusc_ptr->total_number_of_elements == 0) {
return GUSL_FALSE;
}

us_get_matching_and_prev_elements(gusc_ptr, data, data_size, &matched_elem,
NULL);

if (matched_elem) {
return GUSL_TRUE;
}

return GUSL_FALSE;

} // end of us_is_element_present

int us_replace_data_and_size_in_matching_element(
struct generic_unordered_set_container *gusc_ptr,
void *old_data, long old_data_size,
void *new_data, long new_data_size)
{

struct element *matched_elem = NULL;
void *data = NULL;

if ((!old_data) || (!new_data)) {
return GUSL_AT_LEAST_ONE_ARG_IS_INVALID;
}

if ((old_data_size <= 0) || (new_data_size <= 0)) {
return GUSL_AT_LEAST_ONE_ARG_IS_INVALID;
}

if (gusc_ptr->total_number_of_elements == 0) {
return GUSL_SET_IS_EMPTY;
}

us_get_matching_and_prev_elements(gusc_ptr, old_data, old_data_size,
&matched_elem, NULL);

if (!matched_elem) {
return GUSL_ELEMENT_NOT_FOUND;
}

// We will allocate memory for contents of new_data and copy contents of
// new_data into this newly allocated memory. Then we will free old_data
// and populate the data member of the matched_elem with the address of the
// newly allocated memory. We will also populate the data_size member of
// the matched_elem with the value in new_data_size.
data = malloc((size_t)(new_data_size));
if (!data) {
return GUSL_NO_MEMORY;
}

memmove(data, new_data, (size_t)(new_data_size));

if (gusc_ptr->cfbdd_callback_function) {
gusc_ptr->cfbdd_callback_function(gusc_ptr, matched_elem->data);
}

free(matched_elem->data);

matched_elem->data = data;
matched_elem->data_size = new_data_size;

return GUSL_SUCCESS;

} // end of us_replace_data_and_size_in_matching_element

int us_delete_matching_element(
struct generic_unordered_set_container *gusc_ptr,
void *data, long data_size)
{

struct element *matched_elem = NULL;
struct element *prev_elem = NULL;

if (!data) {
return GUSL_AT_LEAST_ONE_ARG_IS_INVALID;
}

if (data_size <= 0) {
return GUSL_AT_LEAST_ONE_ARG_IS_INVALID;
}

if (gusc_ptr->total_number_of_elements == 0) {
return GUSL_SET_IS_EMPTY;
}

us_get_matching_and_prev_elements(gusc_ptr, data, data_size, &matched_elem,
&prev_elem);

if (!matched_elem) {
return GUSL_ELEMENT_NOT_FOUND;
}

if (!prev_elem) { // first element matched
gusc_ptr->first = gusc_ptr->first->next;
} else {
prev_elem->next = matched_elem->next;
}

matched_elem->next = NULL;

if (gusc_ptr->cfbdd_callback_function) {
gusc_ptr->cfbdd_callback_function(gusc_ptr, matched_elem->data);
}

gusc_ptr->total_number_of_elements = gusc_ptr->total_number_of_elements - 1;

free(matched_elem->data);
free(matched_elem);

return GUSL_SUCCESS;

} // end of us_delete_matching_element

void us_init_fi(struct generic_unordered_set_container *gusc_ptr)
{

gusc_ptr->fi = gusc_ptr->first;

return;

} // end of us_init_fi

int us_fi_has_next_element(struct generic_unordered_set_container *gusc_ptr)
{

int has_next = GUSL_FALSE;

if (gusc_ptr->fi) {
has_next = GUSL_TRUE;
}

return has_next;

} // end of us_fi_has_next_element

// The user should not free the data pointer that is returned in *pdata because
// the element that contains the data is still part of the set. However, the
// user can modify the data and then modify data_size if the size of data has
// changed.
int us_fi_get_data_from_next_element(
struct generic_unordered_set_container *gusc_ptr,
void **pdata, long **ppdata_size)
{

if ((!pdata) || (!ppdata_size)) {
return GUSL_AT_LEAST_ONE_ARG_IS_INVALID;
}

*pdata = NULL;
*ppdata_size = 0;

*pdata = gusc_ptr->fi->data;
*ppdata_size = &(gusc_ptr->fi->data_size);

gusc_ptr->fi = gusc_ptr->fi->next;

return GUSL_SUCCESS;

} // end of us_fi_get_data_from_next_element

void us_delete_all_elements(struct generic_unordered_set_container *gusc_ptr)
{

struct element *temp = NULL;

while ((temp = gusc_ptr->first)) {

gusc_ptr->first = gusc_ptr->first->next;
temp->next = NULL;

if (gusc_ptr->cfbdd_callback_function) {
gusc_ptr->cfbdd_callback_function(gusc_ptr, temp->data);
}

free(temp->data);
free(temp);

gusc_ptr->total_number_of_elements =
gusc_ptr->total_number_of_elements - 1;
}

// crash the program if total_number_of_elements is not zero
if (gusc_ptr->total_number_of_elements != 0) {
*((unsigned long *)(-1)) = 123;
}

return;

} // end of us_delete_all_elements

void us_delete_container(struct generic_unordered_set_container *gusc_ptr)
{

us_delete_all_elements(gusc_ptr);

free(gusc_ptr);

return;

} // end of us_delete_container

-------------------------------------------
generic_unordered_set_library.h
-------------------------------------------

/*
* License: This file has been released under APACHE LICENSE, VERSION 2.0.
* The license details can be found here:
* https://www.apache.org/licenses/LICENSE-2.0
*/

#ifndef _GENERIC_UNORDERED_SET_LIBRARY_H_
#define _GENERIC_UNORDERED_SET_LIBRARY_H_

#define GUSL_SUCCESS 2 // everything happened successfully
#define GUSL_TRUE 1 // true
#define GUSL_FALSE 0 // false
#define GUSL_AT_LEAST_ONE_ARG_IS_INVALID -1 // at least one argument is invalid
#define GUSL_NO_MEMORY -2 // no memory available
#define GUSL_ELEMENT_EXISTS -3 // element already exists in the set
#define GUSL_SET_IS_EMPTY -4 // there are no elements in the set
#define GUSL_ELEMENT_NOT_FOUND -5 // element not found in the set

struct generic_unordered_set_container;

struct element
{
void *data;
long data_size;
struct element *next;
};

// gusc_ptr needs to be sent because in case the user has created more than
// one container and the user stores different 'data' in each container, then
// the user will be able to identify that the 'data' belongs to which container.
// Otherwise, the user won't be able to identify that the 'data' belongs to
// which container and then things won't happen correctly.
typedef void (*call_function_before_deleting_data)(
struct generic_unordered_set_container *gusc_ptr,
void *data);

struct generic_unordered_set_container
{
struct element *first;
struct element *fi; // fi stands for forward iterator
long total_number_of_elements;
// callback function
call_function_before_deleting_data cfbdd_callback_function;
};

/*
* Names of functions start with the prefix us_. us_ stands for unordered set.
*
* In this software, delete and free mean the same thing.
*/

struct generic_unordered_set_container *us_init_generic_unordered_set_container(
call_function_before_deleting_data cfbdd_callback_function);

long us_get_total_number_of_elements(
struct generic_unordered_set_container *gusc_ptr);

int us_add_new_element(struct generic_unordered_set_container *gusc_ptr,
void *data, long data_size);

int us_get_data_from_front_element_and_delete_front_element(
struct generic_unordered_set_container *gusc_ptr,
void **pdata, long *pdata_size);

int us_is_element_present(struct generic_unordered_set_container *gusc_ptr,
void *data, long data_size);

int us_replace_data_and_size_in_matching_element(
struct generic_unordered_set_container *gusc_ptr,
void *old_data, long old_data_size,
void *new_data, long new_data_size);

int us_delete_matching_element(
struct generic_unordered_set_container *gusc_ptr,
void *data, long data_size);

void us_init_fi(struct generic_unordered_set_container *gusc_ptr);

int us_fi_has_next_element(struct generic_unordered_set_container *gusc_ptr);

int us_fi_get_data_from_next_element(
struct generic_unordered_set_container *gusc_ptr,
void **pdata, long **ppdata_size);

void us_delete_all_elements(struct generic_unordered_set_container *gusc_ptr);

void us_delete_container(struct generic_unordered_set_container *gusc_ptr);

#endif