# NAME

libslack(link) - linked list module

# SYNOPSIS

        #include <slack/std.h>
        #include <slack/link.h>

        typedef struct slink_t slink_t;
        typedef struct dlink_t dlink_t;

        struct slink_t
        {
            void *next;
        };

        struct dlink_t
        {
            void *next;
            void *prev;
        };

        int slink_has_next(void *link);
        void *slink_next(void *link);
        int dlink_has_next(void *link);
        void *dlink_next(void *link);
        int dlink_has_prev(void *link);
        void *dlink_prev(void *link);
        void *slink_insert(void *link, void *item);
        void *dlink_insert(void *link, void *item);
        void *slink_remove(void *link);
        void *dlink_remove(void *link);
        void *slink_freelist_init(void *freelist, size_t nelem, size_t size);
        void *dlink_freelist_init(void *freelist, size_t nelem, size_t size);
        void *slink_freelist_attach(void *freelist1, void *freelist2);
        void *dlink_freelist_attach(void *freelist1, void *freelist2);
        void *slink_alloc(void **freelist);
        void *dlink_alloc(void **freelist);
        void *slink_free(void **freelist, void *item);
        void *dlink_free(void **freelist, void *item);

# DESCRIPTION

This module provides functions for manipulating singly- and doubly-linked lists. Two abstract types are defined: slink_t, containing a pointer to the next item, and dlink_t, containing pointers to the next and previous items. These functions work with any struct whose first element is an slink_t or a dlink_t struct. There is support for optional growable free lists so items may be dynamically allocated individually, or allocated from a free list. Free lists can be arrays of structs or dynamically allocated. When a free list is exhausted, further memory may be attached to the free list to extend it.

`int slink_has_next(void *link)`

  Returns `1` if `link`'s `next` pointer is not `null`. Otherwise, returns `0`. On error, returns `-1` with `errno` set appropriately.

`void *slink_next(void *link)`

  Returns `link`'s `next` pointer. On error, returns `null` with `errno` set appropriately.

`int dlink_has_next(void *link)`

  Returns `1` if `link`'s `next` pointer is not `null`. Otherwise, returns `0`. On error, returns `-1` with `errno` set appropriately.

`void *dlink_next(void *link)`

  Returns `link`'s `next` pointer. On error, returns `null` with `errno` set appropriately.

`int dlink_has_prev(void *link)`

  Returns `1` if `link`'s `prev` pointer is not `null`. Otherwise, returns `0`. On error, returns `-1` with `errno` set appropriately.

`void *dlink_prev(void *link)`

  Returns `link`'s `prev` pointer. On error, returns `null` with `errno` set appropriately.

`void *slink_insert(void *link, void *item)`

  Inserts `item` before `link`. Returns `item`. On error, returns `null` with `errno` set appropriately. Items may only be inserted at the beginning of a singly-linked list.

`void *dlink_insert(void *link, void *item)`

  Inserts `item` before `link`. Returns `item`. On error, returns `null` with `errno` set appropriately. Items may be inserted anywhere in a doubly-linked list.

`void *slink_remove(void *link)`

  Removes the first item from the list beginning with `link`. On success, returns `link`'s `next` pointer. On error, returns `null` with `errno` set appropriately.

`void *dlink_remove(void *link)`

  Removes `link` from the list of which it is part. On success, returns `link`'s `next` pointer. On error, returns `null` with `errno` set appropriately.

`void *slink_freelist_init(void *freelist, size_t nelem, size_t size)`

  Initialises an array of `nelem` elements each `size` bytes for use as a singly-linked free list. On success, returns `freelist`. On error, returns `null` with `errno` set appropriately.

`void *dlink_freelist_init(void *freelist, size_t nelem, size_t size)`

  Initialises an array of `nelem` elements each `size` bytes for use as a doubly-linked free list. On success, returns `freelist`. On error, returns `null` with `errno` set appropriately.

`void *slink_freelist_attach(void *freelist1, void *freelist2)`

  Attaches `freelist2` to the end of `freelist1`. Both free lists must have already been initialised with slink_freelist_init(3). Note that it will not be possible to separate these free lists. On success, returns a pointer to the beginning of the combined freelist. On error, returns `null` with `errno` set appropriately.

`void *dlink_freelist_attach(void *freelist1, void *freelist2)`

  Attaches `freelist2` to the end of `freelist1`. Both free lists must have already been initialised with dlink_freelist_init(3). Note that it will not be possible to separate these free lists. On success, returns a pointer to the beginning of the combined freelist. On error, returns `null` with `errno` set appropriately.

`void *slink_alloc(void **freelist)`

  Allocates an item from `*freelist` and updates `*freelist` to point to the next free item. `*freelist` must be a singly-linked freelist initialised with slink_freelist_init(3). On success, returns the allocated item. On error, returns `null` with `errno` set appropriately.

`void *dlink_alloc(void **freelist)`

  Allocates an item from `*freelist` and updates `*freelist` to point to the next free item. `*freelist` must be a doubly-linked freelist initialised with dlink_freelist_init(3). On success, returns the allocated item. On error, returns `null` with `errno` set appropriately.

`void *slink_free(void **freelist, void *item)`

  Inserts `item` into `*freelist` and updates `*freelist` to point to item. `*freelist` must be a singly-linked freelist initialised with slink_freelist_init(3). On success, returns the resulting free list. On error, returns `null` with `errno` set appropriately.

`void *dlink_free(void **freelist, void *item)`

  Inserts `item` into `*freelist` and updates `*freelist` to point to `item`. `*freelist` must be a doubly-linked freelist initialised with dlink_freelist_init(3). On success, returns the resulting free list. On error, returns `null` with `errno` set appropriately.

# ERRORS

On error, these functions set `errno` to the following:

`EINVAL`

  When `null` pointers are incorrectly passed as arguments to most functions.

`ENOSPC`

  When slink_alloc(3) or dlink_alloc(3) is called and the free list is exhausted.

# MT-Level

Unsafe

This module declares abstract types. They must be used as part of larger data structures. It is assumed that the surrounding data structure and its functions will provide any locking that is required.

# EXAMPLES

A singly-linked example that reads pairs of numbers from `stdin` (attaching more space to the list as necessary), iterates over the items and then deletes them:

        #include <slack/std.h>
        #include <slack/link.h>

        typedef struct spoint_t spoint_t;

        struct spoint_t
        {
            slink_t link;
            int x;
            int y;
        };

        #define SLIST_SIZE 10
        #define MAX_ADDITIONS 100
        spoint_t sfreespace[SLIST_SIZE];
        spoint_t *sfreelist = sfreespace;
        spoint_t *spoints = NULL;
        spoint_t *additional[MAX_ADDITIONS];
        int added = 0;

        int main(int ac, char **av)
        {
            spoint_t *item, *morespace;
            int x, y, i;

            // Initialize the singly-linked list of points

            if (slink_freelist_init(sfreespace, SLIST_SIZE, sizeof(spoint_t)) != sfreespace)
                return EXIT_FAILURE;

            // Read coordinates from stdin and populate the list

            while (scanf("%d %d", &x, &y) == 2)
            {
                // Add more space to the list when it runs out

                if (!(item = slink_alloc((void **)&sfreelist)))
                {
                    if (added == MAX_ADDITIONS)
                        return EXIT_FAILURE; // or extend additional

                    if (!(morespace = malloc(SLIST_SIZE * sizeof(spoint_t))))
                        return EXIT_FAILURE;

                    additional[added++] = morespace; // remember to free this

                    if (slink_freelist_init(morespace, SLIST_SIZE, sizeof(spoint_t)) != morespace)
                        return EXIT_FAILURE;

                    if (!(sfreelist = slink_freelist_attach(sfreelist, morespace)))
                        return EXIT_FAILURE;

                    if (!(item = slink_alloc((void **)&sfreelist)))
                        return EXIT_FAILURE;
                }

                // Initialize the item

                item->x = x;
                item->y = y;

                // Insert it into the list

                if (!(spoints = slink_insert(spoints, item)))
                    return EXIT_FAILURE;
            }

            // Iterate over the list with slink_next()

            for (item = spoints; item; item = slink_next(item))
                printf("%d %d\n", item->x, item->y);

            // Iterate over the list with slink_has_next()

            for (item = spoints; slink_has_next(item) == 1; item = slink_next(item))
            {
                spoint_t *next = slink_next(item);
                printf("%d %d -> %d %d\n", item->x, item->y, next->x, next->y);
            }

            if (item)
                printf("%d %d !\n", item->x, item->y);

            // Remove the items (printing them out)

            while (spoints)
            {
                spoints = slink_remove(item = spoints);

                printf("%d %d\n", item->x, item->y);

                slink_free((void **)&sfreelist, item);
            }

            // Deallocate any attached freelists

            for (i = 0; i < added; ++i)
                free(additional[i]);

            return EXIT_SUCCESS;
        }

A doubly-linked example that reads pairs of numbers from `stdin` (attaching more space to the list as necessary), iterates over the items and then deletes them:

        #include <slack/std.h>
        #include <slack/link.h>

        typedef struct dpoint_t dpoint_t;

        struct dpoint_t
        {
            dlink_t link;
            int x;
            int y;
        };

        #define DLIST_SIZE 10
        #define MAX_ADDITIONS 100
        dpoint_t dfreespace[DLIST_SIZE];
        dpoint_t *dfreelist = dfreespace;
        dpoint_t *dpoints = NULL;
        dpoint_t *additional[MAX_ADDITIONS];
        int added = 0;

        int main(int ac, char **av)
        {
            dpoint_t *item, *morespace;
            dpoint_t *last;
            int x, y, i;

            // Initialize the doubly-linked list of points

            if (dlink_freelist_init(dfreespace, DLIST_SIZE, sizeof(dpoint_t)) != dfreespace)
                return EXIT_FAILURE;

            // Read coordinates from stdin and populate the list

            while (scanf("%d %d", &x, &y) == 2)
            {
                // Add more space to the list when it runs out

                if (!(item = dlink_alloc((void **)&dfreelist)))
                {
                    if (added == MAX_ADDITIONS)
                        return EXIT_FAILURE; // or extend additional

                    if (!(morespace = malloc(DLIST_SIZE * sizeof(dpoint_t))))
                        return EXIT_FAILURE;

                    additional[added++] = morespace; // remember to free this

                    if (dlink_freelist_init(morespace, DLIST_SIZE, sizeof(dpoint_t)) != morespace)
                        return EXIT_FAILURE;

                    if (!(dfreelist = dlink_freelist_attach(dfreelist, morespace)))
                        return EXIT_FAILURE;

                    if (!(item = dlink_alloc((void **)&dfreelist)))
                        return EXIT_FAILURE;
                }

                // Initialize the item

                item->x = x;
                item->y = y;

                // Insert it into the list

                if (!(dpoints = dlink_insert(dpoints, item)))
                    return EXIT_FAILURE;
            }

            // Iterate over the list with dlink_next()

            for (item = dpoints; item; item = dlink_next(item))
            {
                dpoint_t *prev = dlink_prev(item);
                dpoint_t *next = dlink_next(item);

                if (prev && next)
                    printf("%d %d -> %d %d -> %d %d\n", prev->x, prev->y, item->x, item->y, next->x, next->y);
                else if (prev)
                    printf("%d %d -> %d %d -> end\n", prev->x, prev->y, item->x, item->y);
                else if (next)
                    printf("start -> %d %d -> %d %d\n", item->x, item->y, next->x, next->y);
            }

            // Iterate backwards with dlink_has_next() and dlink_prev()

            for (item = dpoints; dlink_has_next(item) == 1; item = dlink_next(item))
            {}

            for (; item; item = dlink_prev(item))
            {
                dpoint_t *prev = dlink_prev(item);
                dpoint_t *next = dlink_next(item);

                if (prev && next)
                    printf("%d %d -> %d %d -> %d %d\n", prev->x, prev->y, item->x, item->y, next->x, next->y);
                else if (prev)
                    printf("%d %d -> %d %d -> end\n", prev->x, prev->y, item->x, item->y);
                else if (next)
                    printf("start -> %d %d -> %d %d\n", item->x, item->y, next->x, next->y);
            }

            // Remove the items (printing them out)

            while (dpoints)
            {
                dpoints = dlink_remove(item = dpoints);

                printf("%d %d\n", item->x, item->y);

                dlink_free((void **)&dfreelist, item);
            }

            // Deallocate any attached freelists

            for (i = 0; i < added; ++i)
                free(additional[i]);

            return EXIT_SUCCESS;
        }

# BUGS

These functions only work on structs where the `next` and `prev` pointers at the first elements. To fix this would require adding an `offset` parameter to each function to tell it where the `next` and `prev` pointers were within the item. It's probably not worth it.

Attached free lists can't be detached. To change this would require more code and more metadata. Again, it's probably not worth it.

# SEE ALSO

libslack(3), list(3), map(3), mem(3), locker(3)

# AUTHOR

20210220 raf <raf@raf.org>