4. Add functionality for both node and data handling
Let the list of node and data handling functions grow.
This makes it easier to see how to continue from here,
how to logically split up the source code in separate files into a (sort-of) organized library.
Spoiler:
Due to the added functionality, the source code example in this section has grown considerably compared to the previous examples,
and many of the functions have been renamed, so the example in this section has little in common with the previous ones.
4.1. Data size
Working with generic node data, the programmer must provide the library with the data size in one way or another.
There are basically three ways:
- Explicitly set the data size as an argument to
node_add_size(), which handles errors, allocates memory, and copies node data. - Set the data size in a "size-only" user-defined callback, which should do nothing but return the data size.
Pass the callback tonode_add(), which handles errors, allocates memory, and copies data. - Use a "complete" user-defined function as callback to
node_add().
The user-defined function should handle both memory allocation memory and copying data, and return 0 (or -1 in case of error). In this case,node_add()only handles errors.
1. Example using explicit size:
int data1 = 42;
node_add_size(&head, &data1, sizeof(data1));
2. Example using a callback to a "size-only" function, return data size:
/* User-defined function, only returns data size */
size_t add_int_size_only(void **node_data, void *data)
{
(void)node_data; (void)data; return sizeof(int);
}
/* Add a node, allocate and copy data in add_int() */
int data1 = 42;
node_add(&head, &data1, add_int_size_only);
3. Example using a callback to a "complete" function, return 0:
/* User-defined function, takes care of both allocating memory and copying data */
size_t add_int(void **node_data, void *data)
{
size_t alloc_size = sizeof(*(int *)data);
if ((*node_data = calloc(1, alloc_size)) == NULL) return -1;
memcpy(*node_data, data, sizeof(int));
return 0;
}
/* Add a node, allocate and copy data in add_int() */
int data1 = 42;
node_add(&head, &data1, add_int);
Here is how node_add() internally handles cases 2 and 3:
int rc = 0;
/* Call the user-defined function */
/* If the 'add_func' callback is a "complete" function, we're done */
if ((rc = add_func(&(new_node->data), data)) == ERROR)
{
/* Handle errors */
free(new_node);
return ERROR;
}
else if (rc > 0)
{
/* If the 'add_func' callback only returns a size, handle data of that size here. */
size_t data_size = (size_t)rc;
new_node->data = data_init(data_size);
memmove(new_node->data, data, data_size);
}
4.2. Added node/list functionality
Node function list has grown, and list functions have also been added.
Generally, functions with prefix node_ handles one single node (well, node_swap() actually affects two nodes).
Functions with prefix list_ deals with either an entire list, or a range of nodes.
See the API for the final list of functions.
Node functions:
node_add() - add new node with value
node_add_size() - add new node with value of given data size
node_get() - get a pointer to a node. No need to free().
node_copy() - get a deep copy of a node. Has to be free():d.
node_move() - move a node from one position to another, or from one list to another
node_swap() - swap positions for two nodes, within the same list, or between two lists
node_set() - set node value
node_set_size() - set node value with given data size
node_print() - print node data
node_del() - delete a node
node_index() - validate an index given as argument
node_appendp() - append an existing node to the end of a list. The node should be obtained by either get() or copy().
node_prependp() - prepend an existing node at the beginning of a list
node_insertp() - insert an existing node at a given index of a list
List functions:
list_count() - number of nodes in a list
list_copy() - get a deep copy of an existing list as a new list
list_copy_range() - get a deep copy of a range of nodes as a new list
list_copy_matching() - get a deep copy of nodes matching a condition as a new list
list_set_range() - modify a range of nodes in an existing list
list_set_matching() - modify matching nodes in an existing list
list_del_range() - delete a range of nodes in a list
list_del_matching() - delete matching nodes in a list
list_del_dup() - delete duplicated nodes in a list
list_sort() - sort a list using a callback for the sort algorithm
list_rev() - reverse a list
list_split() - split a list at a given index
list_join() - join two lists
list_map() - generate a result list applying a callback on each element of an existing list
list_print() - print data for all nodes in a list
Aliases:
node_prepend() -> node_add(0)
node_append() -> node_add(-1)
node_get_head() -> node_get(0)
node_get_tail() -> node_get(-1)
node_set_head() -> node_get(0)
node_set_tail() -> node_get(-1)
node_del_head() -> node_del(0)
node_del_tail() -> node_del(-1)
list_set() -> list_set_range(0, -1) modify all nodes in an existing list
list_del() -> list_del_range(0, -1) delete all nodes in a list
list_del_from(pos) -> list_del_range(pos, -1)
list_del_to(pos) -> list_del_range(0, pos)
4.3. About this example
This example adds lots of functions for handling lists, nodes, and node data, and also test code for those functions.
This example is much more extensive than the previous one, with little code in common.