/*---------------------------------------------------------- * HTBLA-Leonding / Class: 2IHIF * --------------------------------------------------------- * Exercise Number: S01 * Title: Simple Singly Linked List implementation * Author: Marc Tismonar * ---------------------------------------------------------- * Description: * Implementation of a simple singly linked list. * ---------------------------------------------------------- */ /* Implementation notes: 1) The 'ListData' struct of this linked list SHALL have a pointer to the head node of the list as only member! 2) List and list node allocation: Use functions `mem_alloc(…)` and `mem_free(…)` declared in `allocator.h`. DO NOT use `malloc(…)` and `free(…)` directly as unit tests will fail. 3) Use 'limits.h' to get maximum and minimum values for numeric types, if needed. 4) Avoid code duplication wherever (reasonably) possible. This is valid for implementation of similar functions as well as for reoccurring code patterns, such as list iteration. Nevertheless, aim for efficiency, e.g. `remove_all` shall traverse the list only once and not use `remove` as defined, because the later would start at the beginning of the list for each iteration. */ #include "simple_singly_linked_list.h" #include "allocator.h" /** The type of list nodes */ typedef struct IntListNodeData* IntListNode; /** The implementation of list node data */ struct IntListNodeData { int data; IntListNode next; }; /** The implementation of list data */ struct IntListData { IntListNode head; }; /* ===================================================================== */ /* private list functions */ /* abstract away and generalize also memory allocation for list nodes */ static IntListNode list_obtain_node(int data) { IntListNode node = (IntListNode)alloc_mem(sizeof(struct IntListNodeData)); node->data = data; node->next = 0; return node; } static void list_release_node(IntListNode node) { free_mem(node); } /* optional: implement a function for printing the content of the list - may be useful for debugging */ void list_dump(char* prefix, IntList list) { return; } /* ===================================================================== */ /** * Obtains ('creates') and provides a 'new' list instance. * Any list obtained via this function MUST be released using * function `release_list()`. * * Note: This function does not make any assumptions * about how list components, esp. nodes, are allocated. * * @return IntList The list instance or 0, if no list could by instantiated. */ IntList list_obtain() { IntList intList = (IntList)alloc_mem(sizeof(struct IntListData)); if (intList != 0) { intList->head = 0; } return intList; } /** * Releases a list that was obtained earlier via function `obtain_list`. * Released lists MUST NOT be used anymore. * * Note: The implementation of this function does not make any assumptions * about the allocation method of list elements, but MUST match the implementation * of function `obtain_list` as its inverse function. * * @param p_list The pointer to the list to release. The value of the pointer * is set to 0, if the list was successfully released, otherwise it is left untouched. */ void list_release(IntList* p_list) { if (p_list == 0 || *p_list == 0) { return; } IntList list = *p_list; IntListNode current = list->head; while (current != 0) { IntListNode next = current->next; list_release_node(current); current = next; } free_mem(list); *p_list = 0; } /** * Determines whether or not the given list is valid. * * @param list The list to evaluate. * @return `True` if the list is valid, false otherwise. */ bool list_is_valid(IntList list) { return list != 0; } /** * Determines whether or not the list contains at least one item. * * @param list The list to evaluate. * @return `False` if the list contains one or more items, `true` otherwise. */ bool list_is_empty(IntList list) { if (!list_is_valid(list)) { return true; } return list->head == 0; } /** * Provides the number of values stored in the list. * * @param list The list to evaluate. * @return The number of values the list contains. */ int list_get_size(IntList list) { if (!list_is_valid(list)) { return 0; } int size = 0; IntListNode current = list->head; while (current != 0) { size++; current = current->next; } return size; } /** * Determines whether or not the list given list contains the queried value * at least once. * * @param list The list to query. * @param value The value. * @return `True` if the list contains at least one instance of the value, * `false ` otherwise. */ bool list_contains(IntList list, int value) { if (!list_is_valid(list)) { return false; } IntListNode current = list->head; while (current != 0) { if (current->data == value) { return true; } current = current->next; } return false; } /** * Provides the value stored in the list at the given position. * * @param list The list from which the value shall be retrieved. * @param index The zero-based position index of the value to retrieve. * @return The value stored at the given position or 0, if the position * is not available. */ int list_get_at(IntList list, unsigned int index) { if (!list_is_valid(list)) { return 0; } unsigned int i = 0; IntListNode current = list->head; while (current != 0) { if (i == index) { return current->data; } current = current->next; i++; } return 0; } /** * Inserts the given value at the end of the given list. * * @param list The list to which the value shall be appended. * @param value The value to append to the list. */ void list_insert(IntList list, int value) { if (!list_is_valid(list)) { return; } IntListNode newNode = list_obtain_node(value); if (list_is_empty(list)) { list->head = newNode; return; } IntListNode current = list->head; while (current->next != 0) { current = current->next; } current->next = newNode; } /** * Inserts the given value at the indexed position in a way the * the inserted value is on that position. The index is * - similar to arrays - zero-based. If the the list is shorter * than the indexed position, the value is inserted at the end * of the list. * * @param list The list into which the value shall be appended. * @param index The position index of the value to insert. * @param value The value to insert. */ void list_insert_at(IntList list, unsigned int index, int value) { if (!list_is_valid(list)) { return; } IntListNode newNode = list_obtain_node(value); if (list_is_empty(list)) { list->head = newNode; return; } if (index == 0) { newNode->next = list->head; list->head = newNode; return; } unsigned int i = 0; IntListNode current = list->head; IntListNode previous = 0; while (current != 0 && i < index) { previous = current; current = current->next; i++; } previous->next = newNode; newNode->next = current; } /** * Appends the `list_to_append` at the end of the given `list`. * The appended list is empty afterwards, because all nodes of that list * have been transferred to `list`. */ void list_append(IntList list, IntList list_to_append) { if (!list_is_valid(list) || !list_is_valid(list_to_append) || list_is_empty(list_to_append)) { return; } if (list_is_empty(list)) { list->head = list_to_append->head; } else { IntListNode current = list->head; while (current->next != 0) { current = current->next; } current->next = list_to_append->head; } list_to_append->head = 0; } /** * Removes the first occurrance of `value` from the given list. */ void list_remove(IntList list, int value) { if (!list_is_valid(list) || list_is_empty(list)) { return; } if (list->head->data == value) { IntListNode old_head = list->head; list->head = list->head->next; list_release_node(old_head); return; } IntListNode current = list->head; while (current->next != 0) { if (current->next->data == value) { IntListNode to_remove = current->next; current->next = to_remove->next; list_release_node(to_remove); return; } current = current->next; } } /** * Removes all occurrances of `value` from the list. */ void list_remove_all(IntList list, int value) { if (!list_is_valid(list) || list_is_empty(list)) { return; } while (list->head != 0 && list->head->data == value) { IntListNode old_head = list->head; list->head = list->head->next; list_release_node(old_head); } if (list_is_empty(list)) { return; } IntListNode current = list->head; while (current->next != 0) { if (current->next->data == value) { IntListNode to_remove = current->next; current->next = to_remove->next; list_release_node(to_remove); } else { current = current->next; } } } /** * Removes the value at the indexed position from the given list * and provides that value. */ int list_remove_at(IntList list, unsigned int index) { if (!list_is_valid(list) || list_is_empty(list)) { return 0; } if (index == 0) { int value = list->head->data; IntListNode old_head = list->head; list->head = list->head->next; list_release_node(old_head); return value; } unsigned int i = 0; IntListNode current = list->head; IntListNode previous = 0; while (current != 0 && i < index) { previous = current; current = current->next; i++; } if (current == 0) { return 0; } int value = current->data; previous->next = current->next; list_release_node(current); return value; } /** * Clears the given list by removing all values from the list. */ void list_clear(IntList list) { if (!list_is_valid(list) || list_is_empty(list)) { return; } IntListNode current = list->head; while (current != 0) { IntListNode next = current->next; list_release_node(current); current = next; } list->head = 0; }