blob: 8d7495849dfbe75e363ec77b71adba22209d0d6c [file] [log] [blame] [view] [raw]
---
title: facil.io - Linked Lists
sidebar: 0.6.x/_sidebar.md
---
# A Simple Linked List
Linked lists come in two main flavors:
1. Generic (or independent) Linked Lists:
Where the list's node structure has a pointer that points to the object being added to the list. This requires objects to be allocated separately from list node, but allows a single object to belong to many lists (one-to-many).
2. Embedded Linked Lists:
Where the list's node structure needs to be embedded within the object, thereby minimizing memory allocation and improving memory locality, while creating a one-to-one restriction (one object can belong to one list).
The single file library `fio_llist.h` offers both flavors in a simple to use package.
The API is practically the same for both, the only difference is in the prefix (`fio_ls_X` vs. `fio_ls_embd_X`).
## Data Structure and Initialization.
The container for a list is the same as a node, however, it must be initialized so that it links to itself (see `FIO_LS_INIT`). i.e.:
```c
fio_ls_s my_list = FIO_LS_INIT(my_list);
```
The linked list container is a simple data structure shown here... however, it is best to **access the data using the API and avoid accessing the data directly**. This will both protect the program from future changes to the data structures and minimize possible errors.
```c
/** an embeded linked list. */
typedef struct fio_ls_embd_s {
struct fio_ls_embd_s *prev;
struct fio_ls_embd_s *next;
} fio_ls_embd_s;
/** an independent linked list. */
typedef struct fio_ls_s {
struct fio_ls_s *prev;
struct fio_ls_s *next;
const void *obj;
} fio_ls_s;
#define FIO_LS_INIT(name) { .next = &(name), .prev = &(name) }
```
The container can be dynamically allocated, placed of the stack or embedded in a dynamic object.
## Generic Linked List API
Note that the API is comprised of **inline static functions that act like macros**, so there is no performance to be gained by accessing the data structure directly (only integrity to be lost).
### `fio_ls_push`
```c
void fio_ls_push(fio_ls_s *pos, const void *obj)
```
Adds an object to the list's head.
### `fio_ls_unshift`
```c
void fio_ls_unshift(fio_ls_s *pos, const void *obj)
```
Adds an object to the list's tail.
### `fio_ls_pop`
```c
void *fio_ls_pop(fio_ls_s *list)
```
Removes an object from the list's head.
### `fio_ls_shift`
```c
void *fio_ls_shift(fio_ls_s *list)
```
Removes an object from the list's tail.
### `fio_ls_remove`
```c
void *fio_ls_remove(fio_ls_s *node)
```
Removes a node from the list, returning the contained object.
### `fio_ls_is_empty`
```c
int fio_ls_is_empty(fio_ls_s *list)
```
Tests if the list is empty.
### `fio_ls_any`
```c
int fio_ls_any(fio_ls_s *list)
```
Tests if the list is NOT empty (contains any nodes).
### `FIO_LS_FOR`
```c
FIO_LS_FOR(list, pos)
```
Iterates through the list using a `for` loop.
Access the data with `pos->obj` (`pos` can be named however you please).
## Embedded List API
### `fio_ls_embd_push`
```c
void fio_ls_embd_push(fio_ls_embd_s *dest, fio_ls_embd_s *node)
```
Adds a node to the list's head.
### `fio_ls_embd_unshift`
```c
fio_ls_embd_unshift(fio_ls_embd_s *dest, fio_ls_embd_s *node)
```
Adds a node to the list's tail.
### `fio_ls_embd_pop`
```c
fio_ls_embd_s *fio_ls_embd_pop(fio_ls_embd_s *list)
```
Removes a node from the list's head.
### `fio_ls_embd_shift`
```c
fio_ls_embd_s *fio_ls_embd_shift(fio_ls_embd_s *list)
```
Removes a node from the list's tail.
### `fio_ls_embd_remove`
```c
fio_ls_embd_s *fio_ls_embd_remove(fio_ls_embd_s *node)
```
Removes a node from it's container list.
### `fio_ls_embd_is_empty`
```c
fio_ls_embd_is_empty(fio_ls_embd_s *list)
```
Tests if the list is empty.
### `fio_ls_embd_any`
```c
int fio_ls_embd_any(fio_ls_embd_s *list)
```
Tests if the list is NOT empty (contains any nodes).
### `FIO_LS_EMBD_FOR`
```c
FIO_LS_EMBD_FOR(list, node)
```
Iterates through the list using a `for` loop.
Access the data with `pos->obj` (`pos` can be named however you pleas..
### `FIO_LS_EMBD_OBJ`
```c
FIO_LS_EMBD_OBJ(type, member, plist) \
((type *)((uintptr_t)(plist) - (uintptr_t)(&(((type *)0)->member))))
```
Takes a list pointer `plist` and returns a pointer to it's container.
This uses pointer offset calculations and can be used to calculate any struct's pointer (not just list containers) as an offset from a pointer of one of it's members.
This is a very useful macro to have around.