summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--firmware/SOURCES1
-rw-r--r--firmware/common/linked_list.c312
-rw-r--r--firmware/include/linked_list.h114
3 files changed, 427 insertions, 0 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index a9f9ce5780..a68c4921ba 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -172,6 +172,7 @@ common/dircache.c
#endif /* HAVE_DIRCACHE */
common/filefuncs.c
common/format.c
+common/linked_list.c
#ifdef APPLICATION
common/rbpaths.c
#endif
diff --git a/firmware/common/linked_list.c b/firmware/common/linked_list.c
new file mode 100644
index 0000000000..006caacc91
--- /dev/null
+++ b/firmware/common/linked_list.c
@@ -0,0 +1,312 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2014 by Michael Sevakis
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include <stddef.h>
+#include "linked_list.h"
+
+
+/** (L)inked (L)ist **/
+
+/**
+ * Helper to find the node previous to 'find'
+ *
+ * returns: NULL if 'find' is the first node
+ * last node if the 'find' isn't found in the list
+ */
+static struct ll_node * ll_search_prev(struct ll_head *list,
+ struct ll_node *find)
+{
+ struct ll_node *prev = NULL;
+ struct ll_node *node = list->head;
+
+ while (node != NULL && node != find)
+ {
+ prev = node;
+ node = node->next;
+ }
+
+ return prev;
+}
+
+/**
+ * Initializes the singly-linked list
+ */
+void ll_init(struct ll_head *list)
+{
+ list->head = NULL;
+ list->tail = NULL;
+}
+
+/**
+ * Adds a node to s singly-linked list using "insert next"
+ */
+void ll_insert_next(struct ll_head *list, struct ll_node *node,
+ struct ll_node *newnode)
+{
+ if (node == NULL)
+ {
+ node = list->head;
+
+ newnode->next = node;
+ list->head = newnode;
+
+ if (node == NULL)
+ list->tail = node;
+ }
+ else
+ {
+ struct ll_node *next = node->next;
+
+ newnode->next = next;
+ node->next = newnode;
+
+ if (next == NULL)
+ list->tail = newnode;
+ }
+}
+
+/**
+ * Adds a node to a singly-linked list using "insert first"
+ */
+void ll_insert_first(struct ll_head *list, struct ll_node *node)
+{
+ struct ll_node *head = list->head;
+
+ node->next = head;
+ list->head = node;
+
+ if (head == NULL)
+ list->tail = node;
+}
+
+/**
+ * Adds a node to a singly-linked list using "insert last"
+ */
+void ll_insert_last(struct ll_head *list, struct ll_node *node)
+{
+ struct ll_node *tail = list->tail;
+
+ node->next = NULL;
+ list->tail = node;
+
+ if (tail == NULL)
+ list->head = node;
+ else
+ tail->next = node;
+}
+
+/**
+ * Removes the node after "node"; if there is none, nothing is changed
+ */
+void ll_remove_next(struct ll_head *list, struct ll_node *node)
+{
+ if (node == NULL)
+ {
+ node = list->head->next;
+
+ list->head = node;
+
+ if (node == NULL)
+ list->tail = NULL;
+ }
+ else
+ {
+ struct ll_node *next = node->next;
+
+ if (next != NULL)
+ {
+ next = next->next;
+ node->next = next;
+
+ if (!next)
+ list->tail = node;
+ }
+ }
+}
+
+/**
+ * Removes the first node in the list
+ */
+void ll_remove_first(struct ll_head *list)
+{
+ struct ll_node *node = list->head->next;
+
+ list->head = node;
+
+ if (node == NULL)
+ list->tail = NULL;
+}
+
+/**
+ * Removes the node from the list
+ */
+void ll_remove(struct ll_head *list, struct ll_node *node)
+{
+ ll_remove_next(list, ll_search_prev(list, node));
+}
+
+
+/** (L)inked (L)ist (D)ouble **/
+
+/**
+ * Initializes the doubly-linked list
+ */
+void lld_init(struct lld_head *list)
+{
+ list->head = NULL;
+ list->tail = NULL;
+
+ /* tail could be stored in first item's prev pointer but this simplifies
+ the routines and maintains the non-circularity */
+}
+
+/**
+ * Adds a node to a doubly-linked list using "insert first"
+ */
+void lld_insert_first(struct lld_head *list, struct lld_node *node)
+{
+ struct lld_node *head = list->head;
+
+ list->head = node;
+
+ if (head == NULL)
+ list->tail = node;
+ else
+ head->prev = node;
+
+ node->next = head;
+ node->prev = NULL;
+}
+
+/**
+ * Adds a node to a doubly-linked list using "insert last"
+ */
+void lld_insert_last(struct lld_head *list, struct lld_node *node)
+{
+ struct lld_node *tail = list->tail;
+
+ list->tail = node;
+
+ if (tail == NULL)
+ list->head = node;
+ else
+ tail->next = node;
+
+ node->next = NULL;
+ node->prev = tail;
+}
+
+/**
+ * Removes a node from a doubly-linked list
+ */
+void lld_remove(struct lld_head *list, struct lld_node *node)
+{
+ struct lld_node *next = node->next;
+ struct lld_node *prev = node->prev;
+
+ if (node == list->head)
+ list->head = next;
+
+ if (node == list->tail)
+ list->tail = prev;
+
+ if (prev != NULL)
+ prev->next = next;
+
+ if (next != NULL)
+ next->prev = prev;
+}
+
+
+/** (L)inked (L)ist (D)ouble (C)ircular **/
+
+/**
+ * Helper that inserts a node into a doubly-link circular list; does not
+ * affect list->head, just returns its state
+ */
+static inline struct lldc_node * lldc_insert(struct lldc_head *list,
+ struct lldc_node *node)
+{
+ struct lldc_node *head = list->head;
+
+ if (head == NULL)
+ {
+ node->prev = node;
+ node->next = node;
+ }
+ else
+ {
+ node->prev = head->prev;
+ node->next = head;
+ head->prev->next = node;
+ head->prev = node;
+ }
+
+ return head;
+}
+
+/**
+ * Initializes the doubly-linked circular list
+ */
+void lldc_init(struct lldc_head *list)
+{
+ list->head = NULL;
+}
+
+/**
+ * Adds a node to a doubly-linked circular list using "insert first"
+ */
+void lldc_insert_first(struct lldc_head *list, struct lldc_node *node)
+{
+ lldc_insert(list, node);
+ list->head = node;
+}
+
+/**
+ * Adds a node to a doubly-linked circular list using "insert last"
+ */
+void lldc_insert_last(struct lldc_head *list, struct lldc_node *node)
+{
+ if (lldc_insert(list, node) == NULL)
+ list->head = node;
+}
+
+/**
+ * Removes a node from a doubly-linked circular list
+ */
+void lldc_remove(struct lldc_head *list, struct lldc_node *node)
+{
+ struct lldc_node *next = node->next;
+
+ if (node == list->head)
+ {
+ if (node == next)
+ {
+ list->head = NULL;
+ return;
+ }
+
+ list->head = next;
+ }
+
+ struct lldc_node *prev = node->prev;
+ next->prev = prev;
+ prev->next = next;
+}
diff --git a/firmware/include/linked_list.h b/firmware/include/linked_list.h
new file mode 100644
index 0000000000..c678cfa7eb
--- /dev/null
+++ b/firmware/include/linked_list.h
@@ -0,0 +1,114 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2014 by Michael Sevakis
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef LINKED_LIST_H
+#define LINKED_LIST_H
+
+/***
+ ** NOTES:
+ ** Node field order chosen so that one type can alias the other for forward
+ ** list traveral by the same code.
+ **
+ ** Structures are separate, even if logically equivalent, to perform compile-
+ ** time type checking.
+ **
+ */
+
+
+/* (L)inked (L)ist
+ *
+ * Format:
+ *
+ * XX->YY->ZZ->0
+ * head--^ ^
+ * tail----------+
+ */
+struct ll_head
+{
+ struct ll_node *head; /* First list item */
+ struct ll_node *tail; /* Last list item (to make insert_last O(1)) */
+};
+
+struct ll_node
+{
+ struct ll_node *next; /* Next list item */
+};
+
+void ll_init(struct ll_head *list);
+void ll_insert_next(struct ll_head *list, struct ll_node *node,
+ struct ll_node *newnode);
+void ll_insert_last(struct ll_head *list, struct ll_node *node);
+void ll_remove_next(struct ll_head *list, struct ll_node *node);
+void ll_remove(struct ll_head *list, struct ll_node *node);
+void ll_insert_first(struct ll_head *list, struct ll_node *node);
+void ll_remove_first(struct ll_head *list);
+
+
+/* (L)inked (L)ist (D)double
+ *
+ * Format:
+ *
+ * 0<-XX<->YY<->ZZ->0
+ * head----^ ^
+ * tail--------------+
+ */
+struct lld_head
+{
+ struct lld_node *head; /* First list item */
+ struct lld_node *tail; /* Last list item (to make insert_last O(1)) */
+};
+
+struct lld_node
+{
+ struct lld_node *next; /* Next list item */
+ struct lld_node *prev; /* Previous list item */
+};
+
+void lld_init(struct lld_head *list);
+void lld_insert_first(struct lld_head *list, struct lld_node *node);
+void lld_insert_last(struct lld_head *list, struct lld_node *node);
+void lld_remove(struct lld_head *list, struct lld_node *node);
+
+
+/* (L)inked (L)ist (D)ouble (C)ircular
+ *
+ * Format:
+ * +----------------+
+ * | |
+ * +->XX<->YY<->ZZ<-+
+ * head------^
+ */
+struct lldc_head
+{
+ struct lldc_node *head; /* First list item */
+};
+
+struct lldc_node
+{
+ struct lldc_node *next; /* Next list item */
+ struct lldc_node *prev; /* Previous list item */
+};
+
+void lldc_init(struct lldc_head *list);
+void lldc_insert_first(struct lldc_head *list, struct lldc_node *node);
+void lldc_insert_last(struct lldc_head *list, struct lldc_node *node);
+void lldc_remove(struct lldc_head *list, struct lldc_node *node);
+
+#endif /* LINKED_LIST_H */