diff --git a/gds-parser/gds-tree-checker.c b/gds-parser/gds-tree-checker.c index 426ff3b..3d067fb 100644 --- a/gds-parser/gds-tree-checker.c +++ b/gds-parser/gds-tree-checker.c @@ -84,31 +84,117 @@ int gds_tree_check_cell_references(struct gds_library *lib) } /** - * @brief This function sets the marker element in the check structure of each cell to zero. - * @param lib Library to work with - * @return 0 if successful; negative if a fault (null pointer, ...) occured. + * @brief Check if list contains a cell + * @param list GList to check. May be a null pointer + * @param cell Cell to check for + * @return 0 if cell is not in list. 1 if cell is in list */ -static int gds_tree_check_clear_cell_check_marker(struct gds_library *lib) +static int gds_tree_check_list_contains_cell(GList *list, struct gds_cell *cell) { + GList *iter; + + for (iter = list; iter != NULL; iter = g_list_next(iter)) { + if ((struct gds_cell *)iter->data == cell) + return 1; + } + + return 0; +} + +/** + * @brief This function follows down the reference list of a cell and marks each visited subcell and detects loops + * @param cell_to_check The cell to check for reference loops + * @param visited_cells Pointer to list head. May be zero. + * @return 0 if no loops exist; error in processing: <0; loop found: >0 + */ +static int gds_tree_check_iterate_ref_and_check(struct gds_cell *cell_to_check, GList **visited_cells) { + GList *ref_iter; + struct gds_cell_instance *ref; + struct gds_cell *sub_cell; + int res; + + if (!cell_to_check) + return -1; + + /* Check if this cell is already contained in visited cells. This indicates a loop */ + if (gds_tree_check_list_contains_cell(*visited_cells, cell_to_check)) + return 1; + + /* Add cell to visited cell list */ + *visited_cells = g_list_append(*visited_cells, (gpointer)cell_to_check); + + /* Mark references and process sub cells */ + for (ref_iter = cell_to_check->child_cells; ref_iter != NULL; ref_iter = g_list_next(ref_iter)) { + ref = (struct gds_cell_instance *)ref_iter->data; + + if (!ref) + return -1; + + sub_cell = ref->cell_ref; + + /* If cell is not resolved, ignore. No harm there */ + if (!sub_cell) + continue; + + res = gds_tree_check_iterate_ref_and_check(sub_cell, visited_cells); + if (res < 0) { + /* Error. return. */ + return -3; + } else if (res > 0) { + /* Loop in subcell found. Propagate to top */ + return 1; + } + } + + /* Remove cell from visted cells */ + *visited_cells = g_list_remove(*visited_cells, cell_to_check); + + /* No error found in this chain */ + return 0; +} + +int gds_tree_check_reference_loops(struct gds_library *lib) +{ + int res; + int loop_count = 0; GList *cell_iter; - struct gds_cell *cell; + struct gds_cell *cell_to_check; + GList *visited_cells = NULL; + if (!lib) return -1; for (cell_iter = lib->cells; cell_iter != NULL; cell_iter = g_list_next(cell_iter)) { - cell = (struct gds_cell *)cell_iter->data; + cell_to_check = (struct gds_cell *)cell_iter->data; - if (!cell) + /* A broken cell reference will be counted fatal in this case */ + if (!cell_to_check) return -2; - cell->checks._internal.marker = 0; - } -} + /* iterate through references and check if loop exists */ + res = gds_tree_check_iterate_ref_and_check(cell_to_check, &visited_cells); -int gds_tree_check_reference_loops(struct gds_library *lib) -{ - return 0; + if (res < 0) { + /* Error */ + return res; + } else if (res > 0) { + /* Loop found: increment loop count and flag cell */ + cell_to_check->checks.affected_by_reference_loop = 1; + loop_count++; + } else if (res == 0) { + /* No error found for this cell */ + cell_to_check->checks.affected_by_reference_loop = 0; + } + + } + + if (visited_cells) { + fprintf(stderr, "Visited cell list should be empty. This is a bug. Please report this.\n"); + g_list_free(visited_cells); + } + + return loop_count; } /** @} */