diff --git a/python/obitools3/dms/dms.cfiles b/python/obitools3/dms/dms.cfiles index f541dda..1d280fb 100644 --- a/python/obitools3/dms/dms.cfiles +++ b/python/obitools3/dms/dms.cfiles @@ -4,6 +4,7 @@ ../../../src/dna_seq_indexer.c ../../../src/encode.c ../../../src/hashtable.c +../../../src/linked_list.c ../../../src/murmurhash2.c ../../../src/obi_align.c ../../../src/obiavl.c diff --git a/src/linked_list.c b/src/linked_list.c new file mode 100644 index 0000000..17a2d51 --- /dev/null +++ b/src/linked_list.c @@ -0,0 +1,167 @@ +/**************************************************************************** + * Linked list source file * + ****************************************************************************/ + +/** + * @file linked_list.c + * @author Celine Mercier + * @date February 22th 2017 + * @brief Source file for linked list functions. + */ + + +#include +#include +#include + +#include "linked_list.h" + + +/** + * @brief Creates a new node. + * + * @warning The returned pointer has to be freed by the caller. + * + * @returns A pointer on the new node. + * @retval NULL if an error occurred. + * + * @since February 2017 + * @author Celine Mercier (celine.mercier@metabarcoding.org) + */ +static Linked_list_node_p create_node(); + + +static Linked_list_node_p create_node() +{ + Linked_list_node_p node; + + node = malloc(sizeof(Linked_list_node_t)); + if (node == NULL) + return NULL; + + node->value = NULL; + node->previous = NULL; + node->next = NULL; + + return node; +} + + +// Add a value at the end of a linked list +Linked_list_node_p ll_add(Linked_list_node_p head, void* value) +{ + Linked_list_node_p node = head; + Linked_list_node_p new_node = NULL; + + // First node + if (head == NULL) + { + head = create_node(); + if (head == NULL) + return NULL; + head->value = value; + } + + else + { + while (node->next != NULL) + node = node->next; + + new_node = create_node(); + if (new_node == NULL) + return NULL; + node->next = new_node; + new_node->previous = node; + new_node->value = value; + } + + return head; +} + + +// Set a value at a given index of the list +int ll_set(Linked_list_node_p head, int idx, void* value) +{ + int i = 0; + Linked_list_node_p node = head; + + while ((node != NULL) && (i < idx)) + { + node = node->next; + i++; + } + + if (node == NULL) // End of list reached before index + return -1; + + node->value = value; + + return 0; +} + + +// Get a node with its index +Linked_list_node_p ll_get(Linked_list_node_p head, int idx) +{ + int i = 0; + Linked_list_node_p node = head; + + while ((node != NULL) && (i < idx)) + { + node = node->next; + i++; + } + + return node; +} + + +// Delete a node +Linked_list_node_p ll_delete(Linked_list_node_p head, int idx) // TODO or with value? +{ + int i = 0; + Linked_list_node_p node = head; + + while ((node != NULL) && (i < idx)) + { + node = node->next; + i++; + } + + if (node == NULL) // Node didn't exist + return NULL; + + if (node->previous != NULL) + (node->previous)->next = node->next; + else // deleting head node: head changes + head = node->next; + + if (node->next != NULL) + (node->next)->previous = node->previous; + + free(node); + + return head; +} + + +// Free the linked list +void ll_free(Linked_list_node_p head) +{ + Linked_list_node_p node = head; + Linked_list_node_p previous = head; + + while (node != NULL) + { + previous = node; + node = node->next; + free(previous); + } +} + + +// Get the index of a node from its value (TODO useless?) -- kinda assumes unique values +//int ll_get_index(Linked_list_node_p head, void* value) +//{ +// +//} diff --git a/src/linked_list.h b/src/linked_list.h new file mode 100644 index 0000000..c7c39b8 --- /dev/null +++ b/src/linked_list.h @@ -0,0 +1,113 @@ +/**************************************************************************** + * Linked list header file * + ****************************************************************************/ + +/** + * @file linked_list.h + * @author Celine Mercier + * @date February 22th 2017 + * @brief Header file for linked list functions. + */ + + +#ifndef LINKED_LIST_H_ +#define LINKED_LIST_H_ + + +#include +#include +#include + + +/** + * @brief Structure for a node in a double linked chain. + */ +typedef struct Linked_list_node { + void* value; /**< A pointer (the value kept). + */ + struct Linked_list_node* next; /**< A pointer on the next node. + */ + struct Linked_list_node* previous; /**< A pointer on the previous node. + */ +} Linked_list_node_t, *Linked_list_node_p; + + +/** + * @brief Adds a new node at the end of a linked list. + * + * Works even if it is the first node. + * + * @param head A pointer on the first node of the linked list, or NULL if the list is empty. + * @param value The value to associate with the node. + * + * @returns A pointer on the new head node of the linked list. + * @retval NULL if an error occurred. + * + * @since February 2017 + * @author Celine Mercier (celine.mercier@metabarcoding.org) + */ +Linked_list_node_p ll_add(Linked_list_node_p head, void* value); + + +/** + * @brief Sets a value at a given index of the list. + * + * @param head A pointer on the first node of the linked list, or NULL if the list is empty. + * @param idx The index of the node at which the value should be changed. + * @param value The new value to associate with the node. + * + * @returns A value indicating the success of the operation. + * @retval 0 if the operation was successfully completed. + * @retval -1 if an error occurred. + * + * @since February 2017 + * @author Celine Mercier (celine.mercier@metabarcoding.org) + */ +int ll_set(Linked_list_node_p head, int idx, void* value); + + +/** + * @brief Gets a node from its index. + * + * @warning The pointer returned is a pointer on the node and not on the value. + * + * @param head A pointer on the first node of the linked list, or NULL if the list is empty. + * @param idx The index of the node to retrieve. + * + * @returns A pointer on the retrieved node. + * @retval NULL if an error occurred. + * + * @since February 2017 + * @author Celine Mercier (celine.mercier@metabarcoding.org) + */ +Linked_list_node_p ll_get(Linked_list_node_p head, int idx); + + +/** + * @brief Deletes a node. + * + * @param head A pointer on the first node of the linked list. + * @param idx The index of the node to delete. + * + * @returns A pointer on the new head node of the linked list. + * @retval NULL if an error occurred. + * + * @since February 2017 + * @author Celine Mercier (celine.mercier@metabarcoding.org) + */ +Linked_list_node_p ll_delete(Linked_list_node_p head, int idx); + + +/** + * @brief Frees all the nodes of a linked list. + * + * @param head A pointer on the first node of the linked list. + * + * @since February 2017 + * @author Celine Mercier (celine.mercier@metabarcoding.org) + */ +void ll_free(Linked_list_node_p head); + + +#endif /* LINKED_LIST_H_ */ + diff --git a/src/obiview.c b/src/obiview.c index a28252a..ac22a93 100644 --- a/src/obiview.c +++ b/src/obiview.c @@ -32,6 +32,7 @@ #include "obidebug.h" #include "obilittlebigman.h" #include "hashtable.h" +#include "linked_list.h" #include "utils.h" #include "obiblob.h" @@ -169,10 +170,13 @@ static int create_obiview_file(OBIDMS_p dms, const char* view_name); * * @param view A pointer on the view. * + * @retval 0 if the operation was successfully completed. + * @retval -1 if an error occurred. + * * @since June 2016 * @author Celine Mercier (celine.mercier@metabarcoding.org) */ -static void update_column_refs(Obiview_p view); +static int update_column_refs(Obiview_p view); /** @@ -763,21 +767,32 @@ static int create_obiview_file(OBIDMS_p dms, const char* view_name) } -static void update_column_refs(Obiview_p view) +static int update_column_refs(Obiview_p view) { int i; + OBIDMS_column_p column; for (i=0; i < (view->infos)->column_count; i++) { - strcpy(((((view->infos)->column_references)[i]).column_refs).column_name, (((view->columns)[i])->header)->name); - ((((view->infos)->column_references)[i]).column_refs).version = (((view->columns)[i])->header)->version; + column = *((OBIDMS_column_p*)ll_get(view->columns, i)); + if (column == NULL) + { + obi_set_errno(OBIVIEW_ERROR); + obidebug(1, "\nError getting a column from the linked list of column pointers of a view"); + return -1; + } + + strcpy(((((view->infos)->column_references)[i]).column_refs).column_name, (column->header)->name); + ((((view->infos)->column_references)[i]).column_refs).version = (column->header)->version; } + return 0; } static int create_column_dict(Obiview_p view) { int i; + OBIDMS_column_p* column_pp; view->column_dict = ht_create(MAX_NB_OPENED_COLUMNS); if (view->column_dict == NULL) @@ -798,7 +813,15 @@ static int create_column_dict(Obiview_p view) return -1; } - if (ht_set(view->column_dict, (((view->infos)->column_references)[i]).alias, (view->columns)+i) < 0) + column_pp = (OBIDMS_column_p*) ll_get(view->columns, i); + if (column_pp == NULL) + { + obi_set_errno(OBIVIEW_ERROR); + obidebug(1, "\nError getting a column from the linked list of column pointers of a view when creating a column dictionary"); + return -1; + } + + if (ht_set(view->column_dict, (((view->infos)->column_references)[i]).alias, column_pp) < 0) { obi_set_errno(OBIVIEW_ERROR); obidebug(1, "\nError adding a column in a column dictionary"); @@ -824,14 +847,16 @@ static int update_column_dict(Obiview_p view) static int update_column_refs_and_dict(Obiview_p view) { - update_column_refs(view); + if (update_column_refs(view) < 0) + return -1; return update_column_dict(view); } static int update_lines(Obiview_p view, index_t line_count) { - int i; + int i; + OBIDMS_column_p column; // Check that the view is not read-only if (view->read_only) @@ -843,8 +868,16 @@ static int update_lines(Obiview_p view, index_t line_count) for (i=0; i<((view->infos)->column_count); i++) { + column = *((OBIDMS_column_p*)ll_get(view->columns, i)); + if (column == NULL) + { + obi_set_errno(OBIVIEW_ERROR); + obidebug(1, "\nError getting a column from the linked list of column pointers of a view when updating view lines"); + return -1; + } + // Clone the column first if needed - if (!(((view->columns)[i])->writable)) + if (!(column->writable)) { if (clone_column_in_view(view, (((view->infos)->column_references)[i]).alias) < 0) { @@ -853,13 +886,13 @@ static int update_lines(Obiview_p view, index_t line_count) } } // Enlarge the column if needed - while (line_count > (((view->columns)[i])->header)->line_count) + while (line_count > (column->header)->line_count) { - if (obi_enlarge_column(((view->columns)[i])) < 0) + if (obi_enlarge_column(column) < 0) return -1; } // Set the number of lines used to the new view line count - (((view->columns)[i])->header)->lines_used = line_count; + (column->header)->lines_used = line_count; } (view->infos)->line_count = line_count; @@ -872,6 +905,7 @@ static OBIDMS_column_p clone_column_in_view(Obiview_p view, const char* column_n { int i; OBIDMS_column_p column = NULL; + OBIDMS_column_p new_column = NULL; OBIDMS_column_p column_buffer; bool found; @@ -890,24 +924,37 @@ static OBIDMS_column_p clone_column_in_view(Obiview_p view, const char* column_n { // Clone with the right line selection and replace (for all columns if there is a line selection) // Save pointer to close column after cloning - column_buffer = (view->columns)[i]; + column_buffer = *((OBIDMS_column_p*)ll_get(view->columns, i)); + if (column_buffer == NULL) + { + obi_set_errno(OBIVIEW_ERROR); + obidebug(1, "\nError getting a column to clone from the linked list of column pointers of a view"); + return NULL; + } // Clone and replace the column in the view - (view->columns)[i] = obi_clone_column(view->dms, view->line_selection, (((view->columns)[i])->header)->name, (((view->columns)[i])->header)->version, 1); - if ((view->columns)[i] == NULL) + column = obi_clone_column(view->dms, view->line_selection, (column_buffer->header)->name, (column_buffer->header)->version, true); + if (column == NULL) { obi_set_errno(OBIVIEW_ERROR); obidebug(1, "\nError cloning a column to replace in a view"); return NULL; } + // Change the pointer in the linked list of column pointers + if (ll_set(view->columns, i, column) < 0) + { + obi_set_errno(OBIVIEW_ERROR); + obidebug(1, "\nError changing the column pointer of a cloned column in the linked list of column pointers of a view"); + return NULL; + } + // Close old cloned column obi_close_column(column_buffer); if (!strcmp((((view->infos)->column_references)[i]).alias, column_name)) - { // Found the column to return - column = (view->columns)[i]; - } + // Found the column to return + new_column = column; } } @@ -922,9 +969,13 @@ static OBIDMS_column_p clone_column_in_view(Obiview_p view, const char* column_n } // Update column refs and dict - update_column_refs_and_dict(view); + if (update_column_refs_and_dict(view) < 0) + { + obidebug(1, "\nError updating columns references and dictionary after cloning a column in a view"); + return NULL; + } - return column; + return new_column; } @@ -952,7 +1003,11 @@ static int save_view(Obiview_p view) (view->infos)->all_lines = true; } - update_column_refs(view); + if (update_column_refs(view) < 0) + { + obidebug(1, "\nError updating column references when saving a view"); + return -1; + } return 0; } @@ -1044,12 +1099,20 @@ static int close_view(Obiview_p view) { int i; int ret_value; + OBIDMS_column_p column; ret_value = 0; for (i=0; i < ((view->infos)->column_count); i++) { - if (obi_close_column((view->columns)[i]) < 0) + column = *((OBIDMS_column_p*)ll_get(view->columns, i)); + if (column == NULL) + { + obi_set_errno(OBIVIEW_ERROR); + obidebug(1, "\nError getting a column to close from the linked list of column pointers of a view"); + return -1; + } + if (obi_close_column(column) < 0) { obidebug(1, "\nError closing a column while closing a view"); ret_value = -1; @@ -1066,6 +1129,9 @@ static int close_view(Obiview_p view) } } + // Free the linked list of column pointers + ll_free(view->columns); + // Free the column dictionary ht_free(view->column_dict); @@ -1251,6 +1317,7 @@ static char* view_check_qual_match_seqs(Obiview_p view) int qual_len; const uint8_t* qual; char* seq; + OBIDMS_column_p column; OBIDMS_column_p qual_column; OBIDMS_column_p seq_column; char* predicate; @@ -1260,12 +1327,20 @@ static char* view_check_qual_match_seqs(Obiview_p view) at_least_one_qual_col = false; for (i=0; i < ((view->infos)->column_count); i++) { + column = *((OBIDMS_column_p*)ll_get(view->columns, i)); + if (column == NULL) + { + obi_set_errno(OBIVIEW_ERROR); + obidebug(1, "\nError getting a column to clone from the linked list of column pointers of a view"); + return NULL; + } + // Check if it's a quality column - if ((((view->columns)[i])->header)->returned_data_type == OBI_QUAL) + if ((column->header)->returned_data_type == OBI_QUAL) { at_least_one_qual_col = true; // Check that the quality arrays match the sequences of the associated column - qual_column = (view->columns)[i]; + qual_column = column; seq_column = obi_open_column(view->dms, ((qual_column->header)->associated_column).column_name, ((qual_column->header)->associated_column).version); if (seq_column == NULL) { @@ -1281,7 +1356,7 @@ static char* view_check_qual_match_seqs(Obiview_p view) return NULL; } - // Check each sequence and its sequence + // Check each sequence and its quality for (j=0; j < (view->infos)->line_count; j++) { for (k=0; k < nb_elements_per_line; k++) @@ -1434,6 +1509,7 @@ Obiview_p obi_new_view(OBIDMS_p dms, const char* view_name, Obiview_p view_to_cl int i; index_t line_nb; char* clone_comment; + OBIDMS_column_p column; // Check that the DMS is a valid pointer if (dms == NULL) @@ -1600,10 +1676,10 @@ Obiview_p obi_new_view(OBIDMS_p dms, const char* view_name, Obiview_p view_to_cl (view->infos)->column_count = 0; (view->infos)->line_count = 0; (view->infos)->all_lines = true; - view->line_selection = NULL; ((view->infos)->created_from)[0] = '\0'; ((view->infos)->view_type)[0] = '\0'; - //view->columns = NULL; // TODO + view->line_selection = NULL; + view->columns = NULL; } // Fill last informations @@ -1641,6 +1717,9 @@ Obiview_p obi_new_view(OBIDMS_p dms, const char* view_name, Obiview_p view_to_cl ((view->infos)->line_selection).version = ((view->line_selection)->header)->version; } + // Initialize linked list of column pointers + view->columns = NULL; + // Create the column dictionary (hash table) associating column names (or aliases) to column pointers if (create_column_dict(view) < 0) { @@ -1654,9 +1733,16 @@ Obiview_p obi_new_view(OBIDMS_p dms, const char* view_name, Obiview_p view_to_cl (view->infos)->column_count = 0; for (i=0; i<((view_to_clone->infos)->column_count); i++) { + column = *((OBIDMS_column_p*)ll_get(view_to_clone->columns, i)); + if (column == NULL) + { + obi_set_errno(OBIVIEW_ERROR); + obidebug(1, "\nError getting a column from the linked list of column pointers of a view"); + return NULL; + } if (obi_view_add_column(view, - (((view_to_clone->columns)[i])->header)->name, - (((view_to_clone->columns)[i])->header)->version, + (column->header)->name, + (column->header)->version, (((view_to_clone->infos)->column_references)[i]).alias, 0, 0, @@ -1964,6 +2050,13 @@ Obiview_p obi_open_view(OBIDMS_p dms, const char* view_name) return NULL; } + // Initialize view informations + view->dms = dms; + view->read_only = true; + view->nb_predicates = 0; + view->predicate_functions = NULL; + view->columns = NULL; + // Map view file view->infos = obi_view_map_file(dms, view_name, true); if ((view->infos) == NULL) @@ -2000,15 +2093,15 @@ Obiview_p obi_open_view(OBIDMS_p dms, const char* view_name) close_view(view); return NULL; } - (view->columns)[i] = column_pointer; - + view->columns = ll_add(view->columns, column_pointer); + if (view->columns == NULL) + { + obidebug(1, "\nError adding a column in the column linked list of a view: column %d: %s, version %d", i, column_name, column_version); + close_view(view); + return NULL; + } } - view->dms = dms; - view->read_only = true; - view->nb_predicates = 0; - view->predicate_functions = NULL; - // Create the column dictionary associating each column alias with its pointer if (create_column_dict(view) < 0) { @@ -2054,16 +2147,31 @@ int obi_view_add_column(Obiview_p view, { { // Clone with the right line selection and replace for all columns // Save pointer to close column after cloning - column_buffer = (view->columns)[i]; + column_buffer = *((OBIDMS_column_p*)ll_get(view->columns, i)); + if (column_buffer == NULL) + { + obi_set_errno(OBIVIEW_ERROR); + obidebug(1, "\nError getting a column to clone from the linked list of column pointers of a view"); + return -1; + } // Clone and replace the column in the view - (view->columns)[i] = obi_clone_column(view->dms, view->line_selection, (((view->columns)[i])->header)->name, (((view->columns)[i])->header)->version, 1); - if ((view->columns)[i] == NULL) + column = obi_clone_column(view->dms, view->line_selection, (column_buffer->header)->name, (column_buffer->header)->version, 1); + if (column == NULL) { obi_set_errno(OBIVIEW_ERROR); obidebug(1, "\nError cloning a column to replace in a view"); return -1; } + + // Change the pointer in the linked list of column pointers + if (ll_set(view->columns, i, column) < 0) + { + obi_set_errno(OBIVIEW_ERROR); + obidebug(1, "\nError changing the column pointer of a cloned column in the linked list of column pointers of a view"); + return -1; + } + // Close old cloned column obi_close_column(column_buffer); } @@ -2120,7 +2228,14 @@ int obi_view_add_column(Obiview_p view, } // Store column pointer in the view structure - (view->columns)[(view->infos)->column_count] = column; + view->columns = ll_add(view->columns, column); + if (view->columns == NULL) + { + obi_set_errno(OBIVIEW_ERROR); + obidebug(1, "\nError adding a column in the linked list of column pointers of a view: column %s, version %d", column_name, version_number); + return -1; + } + // If an alias is not defined, it's the original name of the column. // TODO discuss if (alias == NULL) @@ -2133,7 +2248,11 @@ int obi_view_add_column(Obiview_p view, (view->infos)->column_count++; // Update column references and dictionary - update_column_refs_and_dict(view); + if (update_column_refs_and_dict(view) < 0) + { + obidebug(1, "\nError updating column references and dictionary after adding a column to a view"); + return -1; + } // // Print dict // for (i=0; i<((view->infos)->column_count); i++) @@ -2150,6 +2269,7 @@ int obi_view_delete_column(Obiview_p view, const char* column_name) { int i; bool found; + OBIDMS_column_p column; // Check that the view is not read-only if (view->read_only) @@ -2162,22 +2282,40 @@ int obi_view_delete_column(Obiview_p view, const char* column_name) found = false; for (i=0; i<((view->infos)->column_count); i++) { + column = *((OBIDMS_column_p*)ll_get(view->columns, i)); + if (column == NULL) + { + obi_set_errno(OBIVIEW_ERROR); + obidebug(1, "\nError getting a column from the linked list of column pointers of a view when deleting a column from a view"); + return -1; + } + if ((!found) && (!strcmp((((view->infos)->column_references)[i]).alias, column_name))) { - obi_close_column((view->columns)[i]); + obi_close_column(column); + view->columns = ll_delete(view->columns, i); + if (view->columns != NULL) + { + obi_set_errno(OBIVIEW_ERROR); + obidebug(1, "\nError trying to delete a column: column not found in linked list of column pointers"); + return -1; + } found = true; } if (found) { if (i != (((view->infos)->column_count) - 1)) // not the last one - { // Shift the pointer and the references - (view->columns)[i] = (view->columns)[i+1]; + { // Shift the references strcpy((((view->infos)->column_references)[i]).alias, (((view->infos)->column_references)[i+1]).alias); strcpy(((((view->infos)->column_references)[i]).column_refs).column_name, ((((view->infos)->column_references)[i+1]).column_refs).column_name); ((((view->infos)->column_references)[i]).column_refs).version = ((((view->infos)->column_references)[i+1]).column_refs).version; } else // Last column - (view->columns)[i] = NULL; + { + strcpy((((view->infos)->column_references)[i]).alias, ""); + strcpy(((((view->infos)->column_references)[i]).column_refs).column_name, ""); + ((((view->infos)->column_references)[i]).column_refs).version = -1; + } } } diff --git a/src/obiview.h b/src/obiview.h index 3d90c2d..1a129df 100644 --- a/src/obiview.h +++ b/src/obiview.h @@ -26,6 +26,7 @@ #include "obidmscolumn.h" #include "obierrno.h" #include "hashtable.h" +#include "linked_list.h" #include "obiblob.h" @@ -105,25 +106,25 @@ typedef struct Obiview_infos { * @brief Structure for an opened view. */ typedef struct Obiview { - Obiview_infos_p infos; /**< A pointer on the mapped view informations. + Obiview_infos_p infos; /**< A pointer on the mapped view informations. */ - OBIDMS_p dms; /**< A pointer on the DMS to which the view belongs. + OBIDMS_p dms; /**< A pointer on the DMS to which the view belongs. */ - bool read_only; /**< Whether the view is read-only or can be modified. + bool read_only; /**< Whether the view is read-only or can be modified. */ - OBIDMS_column_p line_selection; /**< A pointer on the column containing the line selection + OBIDMS_column_p line_selection; /**< A pointer on the column containing the line selection * associated with the view if there is one. * This line selection is read-only, and when a line from the view is read, * it is this line selection that is used. */ - OBIDMS_column_p columns[MAX_NB_OPENED_COLUMNS]; /**< Array of pointers on all the columns of the view. + Linked_list_node_p columns; /**< Double linked chain containing the pointers on all the columns of the view. */ - hashtable_p column_dict; /**< Hash table storing the pairs of column names or aliases with the associated + hashtable_p column_dict; /**< Hash table storing the pairs of column names or aliases with the associated * pointers on column pointers (OBIDMS_column_p*). */ - int nb_predicates; /**< Number of predicates to test when closing the view. - */ - char* (**predicate_functions)(struct Obiview* view); /**< Array of pointers on all predicate functions to test when closing the view. + int nb_predicates; /**< Number of predicates to test when closing the view. + */ + char* (**predicate_functions)(struct Obiview* view); /**< Array of pointers on all predicate functions to test when closing the view. */ } Obiview_t, *Obiview_p;