#include #include #include #include /* * Implementation of a typed, undirected list of integers for the * "Datastructures and algorithms" courses at the Department of * Computing Science, Umea University. The implementation uses linked * 2-cells. * * Authors: Niclas Borlin (niclas@cs.umu.se) * * Version information: * v1.0 2018-01-28: First public version. * v1.01 2018-03-26: Bugfix: Corrected const declaration in remove. * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. * v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. * v2.0 2024-05-10: Added print_internal. */ // ===========INTERNAL DATA TYPES============ /* * The list elements are implemented as two-cells with forward and * backward links and place to store one integer. The list uses two * border cells at the start and end of the list. */ typedef struct cell { struct cell *next; struct cell *prev; int val; } cell; struct list { cell *head; cell *tail; }; // ===========INTERNAL FUNCTION IMPLEMENTATIONS============ /** * list_empty() - Create an empty list. * * Returns: A pointer to the new list. */ list *list_empty(void) { // Allocate memory for the list head. list *l = calloc(1, sizeof(list)); // Allocate memory for the border cells. l->head = calloc(1, sizeof(cell)); l->tail = calloc(1, sizeof(cell)); // Set consistent links between border elements. l->head->next = l->tail; l->tail->prev = l->head; return l; } /** * list_is_empty() - Check if a list is empty. * @l: List to check. * * Returns: True if the list is empty, otherwise false. */ bool list_is_empty(const list * l) { // List is empty if there are no cells between head and tail. return (l->head->next == l->tail); } /** * list_first() - Return the first position of a list, i.e. the * position of the first element in the list. * @l: List to inspect. * * Returns: The first position in the given list. */ list_pos list_first(const list * l) { // First position is position of first element. return l->head->next; } /** * list_end() - Return the last position of a list, i.e. the position * after the last element in the list. * @l: List to inspect. * * Returns: The last position in the given list. */ list_pos list_end(const list * l) { // Last position is position *after* last element. return l->tail; } /** * list_next() - Return the next position in a list. * @l: List to inspect. * @p: Any valid position except the last in the list. * * Returns: The position in the list after the given position. * NOTE: The return value is undefined for the last position. */ list_pos list_next(const list * l, const list_pos p) { if (list_pos_is_equal(l, p, list_end(l))) { // This should really throw an error. fprintf(stderr,"list_next: Warning: Trying to navigate past end of list!"); } return p->next; } /** * list_prev() - Return the previous position in a list. * @l: List to inspect. * @p: Any valid position except the first in the list. * * Returns: The position in the list before the given position. * NOTE: The return value is undefined for the first position. */ list_pos list_prev(const list * l, const list_pos p) { if (list_pos_is_equal(l, p, list_first(l))) { // This should really throw an error. fprintf(stderr,"list_prev: Warning: Trying to navigate " "past beginning of list!\n"); } return p->prev; } /** * list_inspect() - Return the value of the element at a given * position in a list. * @l: List to inspect. * @p: Any valid position in the list, except the last. * * Returns: The integer value stored in the element at postiion pos. * NOTE: The return value is undefined for the last position. */ int list_inspect(const list * l, const list_pos p) { if (list_pos_is_equal(l, p, list_end(l))) { // This should really throw an error. fprintf(stderr,"list_inspect: Warning: Trying to inspect " "position at end of list!\n"); } return p->val; } /** * list_insert() - Insert a new element with a given value into a list. * @l: List to manipulate. * @v: Integer value to be inserted into the list. * @p: Position in the list before which the value should be inserted. * * Creates a new element and inserts it into the list before p. * Stores data in the new element. * * Returns: The position of the newly created element. */ list_pos list_insert(list * l, int v, const list_pos p) { // Allocate memory for a new cell. list_pos e = malloc(sizeof(cell)); // Store the value. e->val = v; // Add links to/from the new cell. e->next = p; e->prev = p->prev; p->prev = e; e->prev->next = e; // Return the position of the new cell. return e; } /** * list_remove() - Remove an element from a list. * @l: List to manipulate. * @p: Position in the list of the element to remove. * * Removes the element at position p from the list. * * Returns: The position after the removed element. */ list_pos list_remove(list *l, const list_pos p) { // Remember return position. list_pos next_pos = p->next; // Link past this element. p->prev->next = p->next; p->next->prev = p->prev; // Free the memory allocated to the cell itself. free(p); // Return the position of the next element. return next_pos; } /** * list_kill() - Destroy a given list. * @l: List to destroy. * * Returns all dynamic memory used by the list and its elements. * * Returns: Nothing. */ void list_kill(list * l) { // Use public functions to traverse the list. // Start with the first element (will be defined even for an // empty list). list_pos p = list_first(l); // Remove first element until list is empty. while (!list_is_empty(l)) { p = list_remove(l, p); } // Free border elements and the list head. free(l->head); free(l->tail); free(l); } /** * list_print() - Iterate over the list element and print their values. * @l: List to inspect. * * Iterates over the list and print each stored integer. * * Returns: Nothing. */ void list_print(const list * l) { // Start at the beginning of the list. list_pos p = list_first(l); while (!list_pos_is_equal(l, p, list_end(l))) { // Call print_func with the element value at the // current position. printf("[%d]\n", list_inspect(l, p)); p = list_next(l, p); } } /** * list_pos_is_equal() - Return true if two positions in a list are equal. * @l: List to inspect. * @p1: First position to compare. * @p2: Second position to compare. * * Returns: True if p1 and p2 refer to the same position in l, otherwise False. * NOTE: The result is defined only if p1 and p2 are valid positions in l. */ bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2) { // Since we don't need to check whether p1 or p2 are valid, we // only need to compare them directly. return p1 == p2; } /** * list_pos_is_valid() - Return true for a valid position in a list * @l: List to inspect. * @p: Any position. * * Returns: True if p is a valid position in the list, otherwise false. */ bool list_pos_is_valid(const list *l, const list_pos p) { // Iterate over all positions in l. list_pos q = list_first(l); while (!list_pos_is_equal(l, q, list_end(l))) { if (list_pos_is_equal(l, p, q)) { // We found the position in the list. return true; } // Advance to the next valid position, q = list_next(l, q); } // p was not among valid positions in l. return false; } // ===========INTERNAL FUNCTIONS USED BY int_list_print_internal ============ // The functions below output code in the dot language, used by // GraphViz. For documention of the dot language, see graphviz.org. /** * indent() - Output indentation string. * @n: Indentation level. * * Print n tab characters. * * Returns: Nothing. */ static void indent(int n) { for (int i=0; i ", PTR2ADDR(from), port); } else { printf("m%04lx -> ", PTR2ADDR(from)); } if (to == NULL) { printf("NULL"); } else { printf("m%04lx", PTR2ADDR(to)); } printf(" ["); if (options != NULL) { printf("%s", options); } if (label != NULL) { printf(" label=\"%s\"",label); } printf("]\n"); } /** * print_head_node() - Print a node corresponding to the list head struct. * @indent_level: Indentation level. * @l: List to inspect. * * Returns: Nothing. */ static void print_head_node(int indent_level, const list *l) { iprintf(indent_level, "m%04lx [shape=record " "label=\"head\\n%04lx|tail\\n%04lx\" xlabel=\"%04lx\"]\n", PTR2ADDR(l), PTR2ADDR(l->head), PTR2ADDR(l->tail), PTR2ADDR(l)); } // Print edges from the list head to the head and tail cells. static void print_head_edges(int indent_level, const list *l) { print_edge(indent_level, l, l->head, "h", "head", NULL); print_edge(indent_level, l, l->tail, "t", "tail", NULL); } // Print a node corresponding to the cell at position p. static void print_elem_node(int indent_level, const list_pos p) { iprintf(indent_level, "m%04lx [shape=record " "label=\"val\\n%d|next\\n%04lx|

prev\\n%04lx\" xlabel=\"%04lx\"]\n", PTR2ADDR(p), p->val, PTR2ADDR(p->next), PTR2ADDR(p->prev), PTR2ADDR(p)); } // Print edges from the cell at position p to the next and previous // cells and the value. The value pointer is red, the others are // black. If the list owns the memory, the edge is solid, otherwise // dashed. static void print_elem_edges(int indent_level, const list *l, const list_pos p) { print_edge(indent_level, p, p->next, "n", "next", NULL); print_edge(indent_level, p, p->prev, "p", "prev", NULL); } // Create an escaped version of the input string. The most common // control characters - newline, horizontal tab, backslash, and double // quote - are replaced by their escape sequence. The returned pointer // must be deallocated by the caller. static char *escape_chars(const char *s) { int i, j; int escaped = 0; // The number of chars that must be escaped. // Count how many chars need to be escaped, i.e. how much longer // the output string will be. for (i = escaped = 0; s[i] != '\0'; i++) { if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { escaped++; } } // Allocate space for the escaped string. The variable i holds the input // length, escaped how much the string will grow. char *t = malloc(i + escaped + 1); // Copy-and-escape loop for (i = j = 0; s[i] != '\0'; i++) { // Convert each control character by its escape sequence. // Non-control characters are copied as-is. switch (s[i]) { case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; default: t[i+j] = s[i]; break; } } // Terminal the output string t[i+j] = '\0'; return t; } /** * list_print_internal() - Print the lists internal structure in dot format. * @l: List to inspect. * @desc: String with a description/state of the list, or NULL for no description. * @indent_level: Indentation level, 0 for outermost * * Iterates over the list and outputs dot code that shows the internal * structure of the list. The dot code can be visualized by * Graphviz. * * On linux system, the output can be parsed by the dot program, e.g. * * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg * * where is the name of the executable * * The output may also be possible to visualize online on * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz * online". * * For documention of the dot language, see graphviz.org. * * Returns: Nothing. */ void list_print_internal(const list *l, const char *desc, int indent_level) { static int graph_number = 0; graph_number++; int il = indent_level; if (indent_level == 0) { // If this is the outermost datatype, start a graph... printf("digraph LIST_%d {\n", graph_number); // Specify default shape and fontname il++; iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); iprintf(il, "ranksep=0.01\n"); } if (desc != NULL) { // Escape the string before printout char *escaped = escape_chars(desc); // Use different names on inner description nodes if (indent_level == 0) { iprintf(il, "description [label=\"%s\"]\n", escaped); } else { iprintf(il, "\tcluster_int_list_%d_description [label=\"%s\"]\n", graph_number, escaped); } // Return the memory used by the escaped string free(escaped); } if (indent_level == 0) { // Use a single "pointer" edge as a starting point for the // outermost datatype iprintf(il, "l [label=\"%04lx\" xlabel=\"l\"]\n", PTR2ADDR(l)); iprintf(il, "l -> m%04lx\n", PTR2ADDR(l)); } // Print the subgraph to surround the DList content iprintf(il, "subgraph cluster_int_list_%d { label=\"List\"\n", graph_number); il++; // Output the head node print_head_node(il, l); // Output the element nodes list_pos p = l->head; while (p != NULL) { print_elem_node(il, p); p = p->next; } // Close the subgraph il--; iprintf(il, "}\n"); // Output the edges from the head print_head_edges(il, l); // Output the edges from each element p = l->head; while (p != NULL) { print_elem_edges(il, l, p); p = p->next; } if (indent_level == 0) { // Termination of graph printf("}\n"); } }