/* * C_lib Copyright (C) 2021 Wael Karram. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* This file contains the function implementations for a double-linked-list. */ /* Include guard + includes. */ #include #ifndef DOUBLE_LINKED_LIST_C #define DOUBLE_LINKED_LIST_C #include "double_linked_list.h" #endif /* DOUBLE_LINKED_LIST_C */ /* Function implementations. */ /* This function initializes a new double-linked list, returns NULL on failure. */ double_linked_list_t* initialize_double_linked_list() { /* Local variables. */ double_linked_list_t* list; /* Attempt to allocate. */ list = (double_linked_list_t*) malloc(sizeof(double_linked_list_t)); /* Check if the allocation failed. */ if (list == NULL) return NULL; /* Initialize the internal fields. */ list->length = 0; list->head = NULL; list->tail = NULL; /* return the result. */ return list; } /* This function appends a node to the given linked list. */ /* Returns SUCCESS_CODE_DOUBLE_LINKED_LIST_C on success, and FAILURE_CODE_DOUBLE_LINKED_LIST_C on failure. */ int append_node_double_linked_list(double_linked_list_t *list, double_node_t *node){ /* Sanity check - make sure input is not NULL and list state is defined! */ if (list == NULL || node == NULL || (list->head != NULL && list->tail == NULL) || (list->head == NULL && list->tail != NULL)) return FAILURE_CODE_DOUBLE_LINKED_LIST_C; /* Set the next node to NULL. */ node->next = NULL; /* The data is in a defined state, append. */ /* Split cases according to if this is the first node or not. */ if (list->head == NULL) { /* Set the previous node to NULL. */ node->previous = NULL; /* Attach. */ list->head = node; list->tail = node; list->length = 1; return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* Connect the nodes and update the tail. */ list->tail->next = node; node->previous = list->tail; list->tail = node; list->length = list->length + 1; return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* This function prepends a node the the given linked list. */ /* Returns SUCCESS_CODE_DOUBLE_LINKED_LIST_C on success, and FAILURE_CODE_DOUBLE_LINKED_LIST_C on failure. */ int prepend_node_double_linked_list(double_linked_list_t *list, double_node_t *node){ /* Sanity check - make sure input is not NULL and list state is defined! */ if (list == NULL || node == NULL || (list->head != NULL && list->tail == NULL) || (list->head == NULL && list->tail != NULL)) return FAILURE_CODE_DOUBLE_LINKED_LIST_C; /* Set the previous node to NULL. */ node->previous = NULL; /* The data is in a defined state, prepend. */ /* Split cases according to if this is the first node or not. */ if (list->head == NULL) { /* Set the next node to NULL. */ node->next = NULL; /* Attach. */ list->head = node; list->tail = node; list->length = 1; return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* Connect the nodes and update the head. */ list->head->previous = node; node->next = list->head; list->tail = node; list->length = list->length + 1; return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* This function creates and appends a node to the given linked list. */ /* Returns SUCCESS_CODE_DOUBLE_LINKED_LIST_C on success, and FAILURE_CODE_DOUBLE_LINKED_LIST_C on failure. */ int append_node_data_double_linked_list(double_linked_list_t *list, void *data){ /* Sanity check - make sure input is not NULL and list state is defined! */ if (list == NULL || (list->head != NULL && list->tail == NULL) || (list->head == NULL && list->tail != NULL)) return FAILURE_CODE_DOUBLE_LINKED_LIST_C; /* Local variables. */ double_node_t* node; /* Attempt to allocate the node. */ node = (double_node_t*) malloc(sizeof(double_node_t)); /* Check for allocation failure. */ if (node == NULL) return FAILURE_CODE_DOUBLE_LINKED_LIST_C; /* Store the value. */ node->data = data; /* Delegate. */ return append_node_double_linked_list(list, node); } /* This function creates and prepends a node to the given linked list. */ /* Returns SUCCESS_CODE_DOUBLE_LINKED_LIST_C on success, and FAILURE_CODE_DOUBLE_LINKED_LIST_C on failure. */ int prepend_node_data_double_linked_list(double_linked_list_t *list, void *data){ /* Sanity check - make sure input is not NULL and list state is defined! */ if (list == NULL || (list->head != NULL && list->tail == NULL) || (list->head == NULL && list->tail != NULL)) return FAILURE_CODE_DOUBLE_LINKED_LIST_C; /* Local variables. */ double_node_t* node; /* Attempt to allocate the node. */ node = (double_node_t*) malloc(sizeof(double_node_t)); /* Check for allocation failure. */ if (node == NULL) return FAILURE_CODE_DOUBLE_LINKED_LIST_C; /* Store the value. */ node->data = data; /* Delegate. */ return prepend_node_double_linked_list(list, node); } /* This function inserts a node at the ith place. */ /* Returns SUCCESS_CODE_DOUBLE_LINKED_LIST_C on success, and FAILURE_CODE_DOUBLE_LINKED_LIST_C on failure. */ int add_node_i_double_linked_list(double_linked_list_t *list, double_node_t *node, int i) { /* Sanity check - make sure input is not NULL, list state is defined and index is valid. */ if (list == NULL || node == NULL || (list->head != NULL && list->tail == NULL) || (list->head == NULL && list->tail != NULL) || i < 0 || i > list->length) return FAILURE_CODE_DOUBLE_LINKED_LIST_C; /* Deal with special "easy" cases. */ if (i == 0) return prepend_node_double_linked_list(list, node); if (i == list->length) return append_node_double_linked_list(list, node); /* Local variables. */ int index; double_node_t* current; /* Loop until we get to the desired node. */ current = list->head; for (index = 0; index < i; index++) current = current->next; /* Insert before current. */ node->previous = current->previous; node->next = current; current->previous->next = node; current->previous = node; /* Increase the length. */ list->length = list->length + 1; /* Inserted. */ return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* This function creates and inserts a node at the ith place. */ /* Returns SUCCESS_CODE_DOUBLE_LINKED_LIST_C on success, and FAILURE_CODE_DOUBLE_LINKED_LIST_C on failure. */ int add_node_data_i_double_linked_list(double_linked_list_t *list, void *data, int i) { /* Sanity check - make sure list state is defined and index is valid. */ if (list == NULL || (list->head != NULL && list->tail == NULL) || (list->head == NULL && list->tail != NULL) || i < 0 || i > list->length) return FAILURE_CODE_DOUBLE_LINKED_LIST_C; /* Local variables. */ double_node_t* node; /* Attempt to allocate the node. */ node = (double_node_t*) malloc(sizeof(double_node_t)); /* Check for allocation failure. */ if (node == NULL) return FAILURE_CODE_DOUBLE_LINKED_LIST_C; /* Store the data. */ node->data = data; /* Delegate. */ return add_node_i_double_linked_list(list, node, i); } /* This function reads the data at the ith node. */ /* Returns NULL on failure! (Even if NULL can be a legitimate value). */ /* Sets error to SUCCESS_CODE_DOUBLE_LINKED_LIST_C on success, and FAILURE_CODE_DOUBLE_LINKED_LIST_C on failure. */ void* read_node_i_double_linked_list(double_linked_list_t *list, int i, int *error) { /* Sanity check - make sure list state is defined and index is valid. */ if (error == NULL) return NULL; if (list == NULL || (list->head != NULL && list->tail == NULL) || (list->head == NULL && list->tail != NULL) || i < 0 || i > list->length) { *error = FAILURE_CODE_DOUBLE_LINKED_LIST_C; return NULL; } /* Local variables. */ int index; double_node_t* current; /* Loop until we get to the desired node. */ current = list->head; for (index = 0; index < i; index++) current = current->next; /* Set the error code. */ *error = SUCCESS_CODE_DOUBLE_LINKED_LIST_C; /* Return the data. */ return current->data; } /* This function retrieves a pointer to the ith node. */ /* Returns NULL on failure! */ /* Sets error to SUCCESS_CODE_DOUBLE_LINKED_LIST_C on success, and FAILURE_CODE_DOUBLE_LINKED_LIST_C on failure. */ double_node_t* get_node_i_double_linked_list(double_linked_list_t *list, int i, int *error) { /* Sanity check - make sure list state is defined and index is valid. */ if (error == NULL) return NULL; if (list == NULL || (list->head != NULL && list->tail == NULL) || (list->head == NULL && list->tail != NULL) || i < 0 || i > list->length) { *error = FAILURE_CODE_DOUBLE_LINKED_LIST_C; return NULL; } /* Local variables. */ int index; double_node_t* current; /* Loop till we get to the desired node. */ current = list->head; for (index = 0; index < i; index++) current = current->next; /* Set the error code. */ *error = SUCCESS_CODE_DOUBLE_LINKED_LIST_C; /* Return the node pointer. */ return current; } /* This function attempts to find the index of a given node, by pointer. */ /* Returns INDEX_NOT_FOUND_CODE_DOUBLE_LINKED_LIST_C on failure. */ int get_node_index_pointer_double_linked_list(double_linked_list_t *list, double_node_t *node) { /* Sanity check. */ if (list == NULL || (list->head != NULL && list->tail == NULL) || (list->head == NULL && list->tail != NULL) || node == NULL) return INDEX_NOT_FOUND_CODE_DOUBLE_LINKED_LIST_C; /* Local variables. */ int index = 0; double_node_t* current = list->head; /* Loop and look for it. */ while (current != node && current != NULL) { current = current->next; index++; } /* Return according to if found or not. */ return (current != NULL) ? index : INDEX_NOT_FOUND_CODE_DOUBLE_LINKED_LIST_C; } /* This function deletes the ith node, leaves the data AS IS! */ /* Returns SUCCESS_CODE_DOUBLE_LINKED_LIST_C on success, and FAILURE_CODE_DOUBLE_LINKED_LIST_C on failure. */ int remove_node_i_double_linked_list(double_linked_list_t *list, int i) { /* Sanity check - make sure list state is defined and index is valid. */ if (list == NULL || (list->head != NULL && list->tail == NULL) || (list->head == NULL && list->tail != NULL) || i < 0 || i > list->length) return FAILURE_CODE_DOUBLE_LINKED_LIST_C; /* Local variables. */ double_node_t* current; int index; /* Special case of an empty list. */ if (list->length == 0) { /* Done nothing. */ return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* Special case of a list with a single node. */ if (list->length == 1) { /* Clear the node. */ free(list->head); /* "Relink" */ list->head = NULL; list->tail = NULL; list->length = 0; /* Done. */ return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* Loop till we get to the desired node. */ while (index < i) { /* Increment. */ index++; current = current->next; } /* Try to remove the node. */ /* Check if head or tail. */ if (current->previous == NULL) { /* Update head. */ list->head = current->next; list->head->previous = NULL; /* Free node struct. */ free(current); current = NULL; /* Update length. */ list->length = list->length - 1; /* Done. */ return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } if (current->next == NULL) { /* Update tail. */ list->tail = current->previous; current->previous->next = NULL; /* Free node struct. */ free(current); current = NULL; /* Update length. */ list->length = list->length - 1; /* Done. */ return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* General (middle) case. */ /* Update preceding and following nodes. */ current->previous->next = current->next; current->next->previous = current->previous; /* Free node struct. */ free(current); current = NULL; /* Update length. */ list->length = list->length - 1; /* Done. */ return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* This function deletes the ith node and the data stored within. */ /* Returns SUCCESS_CODE_DOUBLE_LINKED_LIST_C on success, and FAILURE_CODE_DOUBLE_LINKED_LIST_C on failure. */ /* If free_function is NULL, will use the stdlib's free(). */ int remove_node_i_data_double_linked_list(double_linked_list_t *list, int i, void (*free_function)(const void*)) { /* Sanity check - make sure list state is defined and index is valid. */ if (list == NULL || (list->head != NULL && list->tail == NULL) || (list->head == NULL && list->tail != NULL) || i < 0 || i > list->length) return FAILURE_CODE_DOUBLE_LINKED_LIST_C; /* Local variables. */ double_node_t* current; int index; /* Special case of an empty list. */ if (list->length == 0) { /* Done nothing. */ return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* Special case of a list with a single node. */ if (list->length == 1) { /* Free the data within the node. */ if (free_function == NULL) free(list->head->data); else free_function(list->head->data); /* Clear the node. */ free(list->head); /* "Relink" */ list->head = NULL; list->tail = NULL; list->length = 0; /* Done. */ return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* Loop till we get to the desired node. */ while (index < i) { /* Increment. */ index++; current = current->next; } /* Try to remove the node. */ /* Check if head or tail. */ if (current->previous == NULL) { /* Update head. */ list->head = current->next; current->next->previous = NULL; /* Free node struct and the data within it. */ if (free_function != NULL) free_function(current->data); else free(current->data); free(current); current = NULL; /* Update length. */ list->length = list->length - 1; /* Done. */ return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } if (current->next == NULL) { /* Update tail. */ list->tail = current->previous; current->previous->next = NULL; /* Free node struct and the data within it. */ if (free_function != NULL) free_function(current->data); else free(current->data); free(current); current = NULL; /* Update length. */ list->length = list->length - 1; /* Done. */ return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* General (middle) case. */ /* Update preceding and following nodes. */ current->previous->next = current->next; current->next->previous = current->previous; /* Free node struct and the data within it. */ if (free_function != NULL) free_function(current->data); else free(current->data); free(current); current = NULL; /* Update length. */ list->length = list->length - 1; /* Done. */ return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* This function reads the data at the first node. */ /* Returns NULL on error or empty list. */ void* read_node_head_double_linked_list(double_linked_list_t *list) { /* Sanity check. */ if (list == NULL || list->length == 0 || list->head == NULL) return NULL; /* Return the data. */ return list->head->data; } /* This function retrieves the first node. */ /* Returns NULL on error or empty list. */ double_node_t* get_node_head_double_linked_list(double_linked_list_t *list) { /* Sanity check. */ if (list == NULL || list->length == 0) return NULL; /* Return the data. */ return list->head; } /* This function deletes the first node. */ /* Returns SUCCESS_CODE_DOUBLE_LINKED_LIST_C on success, and FAILURE_CODE_DOUBLE_LINKED_LIST_C on failure. */ int remove_node_head_double_linked_list(double_linked_list_t *list) { /* Sanity check. */ if (list == NULL || (list->head != NULL && list->tail == NULL) || (list->head == NULL && list->tail != NULL)) return FAILURE_CODE_DOUBLE_LINKED_LIST_C; /* Check if we have to do anything at all. */ if (list->head == NULL) return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; /* Split into cases. */ if (list->length == 1) { /* Clear the only node. */ free(list->head); /* Set the list to be empty again. */ list->head = NULL; list->tail = NULL; list->length = 0; /* Done. */ return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* Length over one. */ /* Local variables. */ double_node_t* tmp = list->head; /* Reconnect. */ list->head = list->head->next; list->head->previous = NULL; /* Free the node. */ free(tmp); /* Change the length. */ list->length = list->length - 1; /* Done. */ return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* This function deletes the first node and the data thereof. */ /* Returns SUCCESS_CODE_DOUBLE_LINKED_LIST_C on success, and FAILURE_CODE_DOUBLE_LINKED_LIST_C on failure. */ /* If free_function is NULL, will use the stdlib's free(). */ int remove_node_head_data_double_linked_list(double_linked_list_t *list, void (*free_function)(const void*)) { /* Sanity check. */ if (list == NULL || (list->head != NULL && list->tail == NULL) || (list->head == NULL && list->tail != NULL)) return FAILURE_CODE_DOUBLE_LINKED_LIST_C; /* Check if we have to do anything at all. */ if (list->head == NULL) return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; /* Split into cases. */ if (list->length == 1) { /* Clear the only node and the data therof. */ if (free_function == NULL) free(list->head->data); else free_function(list->head->data); free(list->head); /* Set the list to be empty again. */ list->head = NULL; list->tail = NULL; list->length = 0; /* Done. */ return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* Length over one. */ /* Local variables. */ double_node_t* tmp = list->head; /* Reconnect. */ list->head = list->head->next; list->head->previous = NULL; /* Free the node and data thereof. */ if (free_function == NULL) free(list->head->data); else free_function(list->head->data); free(tmp); /* Change the length. */ list->length = list->length - 1; /* Done. */ return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* This function reads the data at the last node. */ /* Returns NULL on error or empty list. */ void* read_node_tail_double_linked_list(double_linked_list_t *list) { /* Sanity check. */ if (list == NULL || list->length == 0 || list->tail == NULL) return NULL; /* Return the data. */ return list->tail->data; } /* This function retrieves the last node. */ /* Returns NULL on error or empty list. */ double_node_t* get_node_tail_double_linked_list(double_linked_list_t *list) { /* Sanity check. */ if (list == NULL || list->length == 0) return NULL; /* Return the data. */ return list->tail; } /* This function deletes the last node. */ /* Returns SUCCESS_CODE_DOUBLE_LINKED_LIST_C on success, and FAILURE_CODE_DOUBLE_LINKED_LIST_C on failure. */ /* Failure is also when the list state isn't properly defined! */ int remove_node_tail_double_linked_list(double_linked_list_t *list) { /* Sanity check. */ if (list == NULL || list->length == 0 || (list->head != NULL && list->tail == NULL) || (list->head == NULL && list->tail != NULL)) return FAILURE_CODE_DOUBLE_LINKED_LIST_C; /* Local variables. */ double_node_t *current; /* Split cases. */ /* Single node case. */ if (list->length == 1) { free(list->tail); list->head = NULL; list->tail = NULL; list->length = 0; return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* General case. */ /* Save the pointer to the node to be deleted. */ current = list->tail; /* Relink the tail. */ list->tail = current->previous; free(current); list->length = list->length - 1; /* Done. */ return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* This function deletes the last node and the data thereof. */ /* Returns SUCCESS_CODE_DOUBLE_LINKED_LIST_C on success, and FAILURE_CODE_DOUBLE_LINKED_LIST_C on failure. */ /* If free_function is NULL, will use the stdlib's free(). */ int remove_node_tail_data_double_linked_list(double_linked_list_t *list, void (*free_function)(const void*)) { /* Sanity check. */ if (list == NULL || list->length == 0 || (list->head != NULL && list->tail == NULL) || (list->head == NULL && list->tail != NULL)) return FAILURE_CODE_DOUBLE_LINKED_LIST_C; /* Local variables. */ double_node_t *current; /* Split cases. */ /* Single node case. */ if (list->length == 1) { /* Free the data. */ if (free_function == NULL) free(list->tail->data); else free_function(list->tail->data); /* Free the node and clean up. */ free(list->tail); list->head = NULL; list->tail = NULL; list->length = 0; return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* General case. */ /* Save the pointer to the node to be deleted. */ current = list->tail; /* Free the data. */ if (free_function == NULL) free(list->tail->data); else free_function(list->tail->data); /* Free the node and clean up. */ /* Relink the tail. */ list->tail = current->previous; free(current); list->length = list->length - 1; /* Done. */ return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* This function deletes all the nodes of the given list and the list itself. */ /* Returns SUCCESS_CODE_DOUBLE_LINKED_LIST_C on success, and FAILURE_CODE_DOUBLE_LINKED_LIST_C on failure. */ int clear_list_double_linked_list(double_linked_list_t **list) { /* Sanity check. */ if (list == NULL || *list == NULL) return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; /* Invalid state. */ if (((*list)->head != NULL && (*list)->tail == NULL) || ((*list)->head == NULL && (*list)->tail != NULL)) return FAILURE_CODE_DOUBLE_LINKED_LIST_C; /* Variables. */ double_node_t *current, *next; /* Special case. */ if ((*list)->length == 0) { /* Free the list, and set the pointer. */ free(*list); *list = NULL; return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* General case, free all nodes then the list. */ current = (*list)->head; while (current != NULL) { /* Save the next pointer. */ next = current->next; /* Free the current node. */ free(current); /* Advance by one node. */ current = next; } /* Free the list, and set the pointer. */ free(*list); *list = NULL; return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* This function deletes all the nodes and data stored within of the given list and the list itself. */ /* Returns SUCCESS_CODE_DOUBLE_LINKED_LIST_C on success, and FAILURE_CODE_DOUBLE_LINKED_LIST_C on failure. */ /* If free_function is NULL, will use the stdlib's free(). */ int clear_list_data_double_linked_list(double_linked_list_t **list, void (*free_function)(const void*)) { /* Sanity check. */ if (list == NULL || *list == NULL) return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; /* Invalid state. */ if (((*list)->head != NULL && (*list)->tail == NULL) || ((*list)->head == NULL && (*list)->tail != NULL)) return FAILURE_CODE_DOUBLE_LINKED_LIST_C; /* Variables. */ double_node_t *current, *next; /* Special case. */ if ((*list)->length == 0) { /* Free the list, and set the pointer. */ free(*list); *list = NULL; return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* General case, free all nodes then the list. */ current = (*list)->head; while (current != NULL) { /* Save the next pointer. */ next = current->next; /* Free the current node's data. */ if (free_function == NULL) free(current->data); else free_function(current->data); /* Free the current node. */ free(current); /* Advance by one node. */ current = next; } /* Free the list, and set the pointer. */ free(*list); *list = NULL; return SUCCESS_CODE_DOUBLE_LINKED_LIST_C; } /* This function attempts to serialize a doubly-linked list into an array. */ /* The function will allocate and return a pointer to said array on success, NULL on failure. */ /* The length of the array is sent through the length pointer, and set to -1 on failure. */ void **serialize_double_linked_list(double_linked_list_t *list, int *length) { /* Local variables. */ void **array; double_node_t *current; int i; /* Sanity check. */ if (list == NULL || list->head == NULL || length == NULL || list->length <= 0) return NULL; /* Set the length. */ *length = list->length; /* Attempt to allocate the array. */ array = (void**) (malloc(list->length * sizeof(void*))); if (array == NULL) { *length = -1; return NULL; } /* Loop and add the elements. */ current = list->head; for (i = 0; i < *length; i++) { array[i] = current->data; current = current->next; } /* Return the filled array. */ return array; }