/* ********************************************** * generic linked list in plain c * author: Freddy Hurkmans * date: dec 20, 2014 * **********************************************/ /* ****************************************************************************** * This list is designed using object oriented ideas, but is implemented in c. * * The idea is: you construct a list for a certain data structure (say: mystruct): * list_admin* mylist = construct_list(sizeof(mystruct)) * mylist contains private data for this list implementation. You need not worry * about it, only give it as first parameter to each list method. * * This means you can have multiple lists at the same time, just make sure you * give the right list_admin structure to the list function. * When you're done with your list, just call the destructor: destruct_list(&mylist) * The destructor automatically deletes remaining list elements and the list administration. * * As this list implementation keeps its own administration in list_admin, you need * not worry about next pointers and such. Your structure doesn't even need pointers * to itself, you only need to worry about data. A valid struct could thus be: * typedef struct * { * int nr; * char text[100]; * } mystruct; * * Adding data to the list is done using list_add_* methods, such as list_add_tail, * which adds data to the end of the list: * mystruct data = {10, "hello world!"}; * int result = list_add_tail(mylist, &data); * You don't have to provide the size of your struct, you've already done that in * the destructor. If result < 0 list_add_* has failed (either a parameter problem * or malloc didn't give memory). * * You can get a pointer to your data using list_get_*, e.g. get the 2nd element: * mystruct* dataptr = list_get_element(admin, 1); * or get the last element: * mystruct* dataptr = list_get_last(admin); * If dataptr is not NULL it points to the correct data, else the operation failed. * * Searching for data in your list can be done with list_index_of*. list_index_of * compares the data in each list item to the given data. If they are the same, it * returns the index. If you want to search for an element with a certain value for * nr or text (see mystruct) you can implement your own compare function and use * list_index_of_special (check memcmp for required return values): * int mycompare(void* p1, void* p2, size_t size) * { * // this example doesn't need size! * mystruct* org = (mystruct*)p1; * int* nr = (int*)p2; * return org->nr - nr; // return 0 if they are the same * } * Say you want to search for an element that has nr 10: * int nr = 10; * int index = list_index_of_special(mylist, &nr, mycompare); * As noted earlier: mycompare must have the same prototype as memcmp. In fact: * list_index_of is a shortcut for list_index_of_special(mylist, data, memcmp). * * Finally you can delete items with list_delete_*. They should work rather * straight forward. If they succeed they return 0. You don't have to delete * all items before destructing your list. * * ******************************************************************************/ #include /* for memcmp and memcpy */ #include "list.h" static size_t data_size(const list_admin* admin); static list_head* get_guaranteed_list_head_of_element_n(list_admin* admin, size_t index); static list_head* get_list_head_of_element_n(list_admin* admin, size_t index); static void remove_first_element(list_admin* admin); static int remove_non_first_element(list_admin* admin, size_t index); /* ********************************************** * Constructor / destructor * **********************************************/ list_admin* construct_list(size_t element_size) { list_admin* admin = malloc(sizeof(*admin)); if (admin != NULL) { admin->head = NULL; admin->element_size = element_size; admin->nr_elements = 0; } return admin; } void destruct_list(list_admin** admin) { if ((admin != NULL) && (*admin != NULL)) { while ((*admin)->head != NULL) { list_head* temp = (*admin)->head; (*admin)->head = (*admin)->head->next; free(temp); temp = NULL; } free(*admin); *admin = NULL; } } /* ********************************************** * Public functions * **********************************************/ int list_get_nr_elements(list_admin* admin) { int nr_elements = -1; if (admin != NULL) { nr_elements = admin->nr_elements; } return nr_elements; } int list_add_tail(list_admin* admin, const void* data) { int result = -1; if ((admin != NULL) && (data != NULL)) { list_head* new = malloc(data_size(admin)); if (new != NULL) { result = 0; new->next = NULL; memcpy(new+1, data, admin->element_size); if (admin->head == NULL) { admin->head = new; } else { list_head* temp = get_guaranteed_list_head_of_element_n(admin, admin->nr_elements-1); temp->next = new; } admin->nr_elements++; } } return result; } void* list_get_element(list_admin* admin, size_t index) { list_head* item = get_list_head_of_element_n(admin, index); if(item != NULL) { item++; // user data is just beyond list_head, thus we must increase the pointer } return item; } void* list_get_last(list_admin* admin) { list_head* item = NULL; if (admin != NULL) { item = list_get_element(admin, admin->nr_elements-1); } return item; } int list_index_of(list_admin* admin, const void* data) { return list_index_of_special(admin, data, memcmp); } int list_index_of_special(list_admin* admin, const void* data, CompareFuncPtr compare) { int index = -1; if ((admin != NULL) && (data != NULL) && (compare != NULL)) { list_head* temp = admin->head; index = 0; while ((temp != NULL) && (compare(temp+1, data, admin->element_size) != 0)) { temp = temp->next; index++; } if (temp == NULL) { index = -1; } } return index; } int list_delete_item(list_admin* admin, size_t index) { int result = -1; if ((admin != NULL) && (admin->head != NULL) && (index < admin->nr_elements)) { if (index == 0) { result = 0; remove_first_element(admin); } else { result = remove_non_first_element(admin, index); } } return result; } int list_delete_last(list_admin* admin) { int result = -1; if (admin != NULL) { result = list_delete_item(admin, admin->nr_elements - 1); } return result; } /* ********************************************** * Private functions * **********************************************/ static size_t data_size(const list_admin* admin) { return admin->element_size + sizeof(list_head); } static list_head* get_guaranteed_list_head_of_element_n(list_admin* admin, size_t index) { list_head* item = admin->head; for (size_t i = 0; (i < index) && (item != NULL); i++) { item = item->next; } return item; } static list_head* get_list_head_of_element_n(list_admin* admin, size_t index) { list_head* item = NULL; if ((admin != NULL) && (index < admin->nr_elements)) { item = get_guaranteed_list_head_of_element_n(admin, index); } return item; } static void remove_first_element(list_admin* admin) { list_head* temp = admin->head; admin->head = admin->head->next; free(temp); temp = NULL; admin->nr_elements--; } static int remove_non_first_element(list_admin* admin, size_t index) { int result = -1; if (index > 0) { list_head* temp = get_list_head_of_element_n(admin, index-1); if ((temp != NULL) && (temp->next != NULL)) // to remove element n, element n-1 and n must exist { result = 0; list_head* to_delete = temp->next; temp->next = to_delete->next; free(to_delete); to_delete = NULL; admin->nr_elements--; } } return result; }