diff --git a/doubly_linked_list_with_iterator.c b/doubly_linked_list_with_iterator.c index 0b2640c..6ab7756 100644 --- a/doubly_linked_list_with_iterator.c +++ b/doubly_linked_list_with_iterator.c @@ -1,9 +1,9 @@ /*---------------------------------------------------------- - * HTBLA-Leonding / Class: + * HTBLA-Leonding / Class: 2IHIF * --------------------------------------------------------- * Exercise Number: S02 * Title: Doubly Linked List implementation - * Author: */;/* + * Author: Marc Tismonar * ---------------------------------------------------------- * Description: * Implementation of a doubly linked list. @@ -37,24 +37,46 @@ */ #include "doubly_linked_list_with_iterator.h" +#include "limits.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; + IntListNode prev; +}; /** The implementation of list data */ +struct IntListData { + IntListNode head; + IntListNode tail; + unsigned int size; +}; /** The implementation of list iterator data */ +struct IntListIteratorData { + IntListNode cur; +}; /* ===================================================================== */ /* private list functions */ /* abstract away and generalize also memory allocation for list nodes */ -static list_obtain_node() { +static IntListNode list_obtain_node(int data) { + IntListNode node = (IntListNode)alloc_mem(sizeof(struct IntListNodeData)); + node->data = data; + node->next = 0; + node->prev = 0; + return node; } -static void list_release_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 */ @@ -62,3 +84,508 @@ void list_dump(char* prefix, IntList list) { } /* ===================================================================== */ + +/** + * 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 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; + intList->tail = 0; + intList->size = 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) { // no need to use the tail & size as its going to free everything anyways, TODO: check here later + 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 && list->tail == 0 && list->size == 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_empty(list)) { + return 0; + } + + return list->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) { // TODO: rewrite + 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) { // TODO: rewrite? + 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 (newNode != 0) { + if (list_is_empty(list)) { + list->head = newNode; + } + } else { + list->tail->next = newNode; + } + + list->tail = newNode; + list->size++; +} + +/** + * 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) { // TODO: rewrite + 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`. + * + * @param list The list that receives the other list. + * @param list_to_append The list that is appended 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)) { // TODO: rewrite + 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. + * If the list does not contain that value, the list shall not + * be modified. + * + * @param list The list from which the given value shall be removed. + * @param value The value to remove from the list. + */ +void list_remove(IntList list, int value) { // TODO: rewrite + 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. + * If the list does not contain that value, the list shall not + * be modified. + * + * @param list The list from which all occurrances of `value` shall be removed. + * @param value The `value` to remove throughout the list. + */ +void list_remove_all(IntList list, int value) { // TODO: rewrite + 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. If the list does not have a value + * at that position, the list remains unmodified. + * + * @param list The list from which the value at the given index shall be returned. + * @param index The zero-based index of the value to return. + * @return The removed value or 0 in case of errors. + */ +int list_remove_at(IntList list, unsigned int index) { // TODO: rewrite + 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. + * + * @param list The list to clear. + */ +void list_clear(IntList list) { // TODO: rewrite? + 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; +} + +/* ===================================================================== */ + +/** + * Obtains ('creates') and provides a 'new' list iterator instance for the given list. + * The provided iterator initially points to the head node of the list. + * + * Any iterator obtained via this function MUST be released using + * function `release_iterator()`. + * + * Note: This function does not make any assumptions + * about how list components, esp. nodes, are allocated. + * + * @param list The list for which the iterator is obtained. + * @return The list iterator instance or 0, if no list iterator could by instantiated. + */ +IntListIterator list_it_obtain(IntList list) { + if (!list_is_valid (list)) { + return 0; + } + + IntListIterator iterator = (IntListIterator)alloc_mem(sizeof(struct IntListIteratorData)); + iterator->cur = list->head; + + return iterator; +} + +/** + * Releases a list iterator that was obtained earlier via function `list_it_obtain`. + * Released list iterators MUST NOT be used anymore. + * + * Note: The implementation of this function does not make any assumptions + * about the allocation method of list iterator elements, but MUST match the implementation + * of function `list_it_obtain` as its inverse function. + * + * @param p_it The pointer to the list iterator to release. The value of the pointer + * is set to 0, if the list iterator was successfully released, otherwise it is left untouched. + */ +void list_it_release(IntListIterator* p_it) { + free_mem(p_it); + p_it = 0; +} + +/** + * Determines whether or not the given list iterator is valid. + * + * @param it The list iterator to evaluate. + * @return `True` if the list iterator is valid, false otherwise. + */ +bool list_it_is_valid(IntListIterator it) { + return it != 0; +} + +/** + * Proceeds the list iterator to the next list element, if possible. + * + * @param it The list iterator to evaluate. + * @return `True` if the list iterator could proceed to the next list node, `false` otherwise. + */ +bool list_it_next(IntListIterator it) { + if (it->cur->next == 0) { + return false; + } + + it->cur = it->cur->next; + return true; +} + +/** + * Proceeds the list iterator to the previous list element, if possible. + * + * @param it The list iterator to evaluate. + * @return `True` if the list iterator could proceed to the previous list node, `false` otherwise. + */ +bool list_it_previous(IntListIterator it) { + if (it->cur->prev == 0) { + return false; + } + + it->cur = it->cur->prev; + return true; +} + +/** + * Provides the value of the node the list iterator currently points to. + * + * @param it The list iterator to evaluate. + * @return The value of the current list node under the iterator. + */ +int list_it_get(IntListIterator it) { + if (!list_it_is_valid(it)) { + return 0; + } + + return it->cur->data; +} + +/** + * Applies the given value to the node the list iterator currently points to. + * + * @param it The list iterator to evaluate. + * @param value The value to set to the current list node under the iterator. + */ +void list_it_set(IntListIterator it, int value) { + if (!list_it_is_valid(it)) { + return; + } + + it->cur->data = value; +} + +/** + * Inserts the given value after the node under the iterator and proceeds the iterator to the + * inserted node. + * + * @param it The list iterator to evaluate. + * @param value The value to insert. + */ +void list_it_insert(IntListIterator it, int value) { + if (!list_it_is_valid(it)) { + return; + } + + IntListNode newNode = list_obtain_node(value); + if (newNode != 0) { + it->cur->next = newNode; + } +} + +/** + * Removes the node under the iterator and proceeds the iterator to the 'next' node. + * + * @param it The list iterator to evaluate. + */ +void list_it_remove(IntListIterator it) { + if (!list_it_is_valid(it)) { + return; + } + + IntListNode nextNode = it->cur->next; + list_release_node(it->cur); + it->cur = nextNode; +} \ No newline at end of file diff --git a/doubly_linked_list_with_iterator.h b/doubly_linked_list_with_iterator.h index 6ea4f5e..130940b 100644 --- a/doubly_linked_list_with_iterator.h +++ b/doubly_linked_list_with_iterator.h @@ -1,9 +1,9 @@ /*---------------------------------------------------------- - * HTBLA-Leonding / Class: + * HTBLA-Leonding / Class: 2IHIF * --------------------------------------------------------- * Exercise Number: S02 * Title: Doubly Linked List - * Author: */