diff --git a/.travis.yml b/.travis.yml index 9094189c8..7591dd772 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,3 +31,4 @@ env: - TEST_NAME='compile-nxp-ports' - TEST_NAME='doxygen' - TEST_NAME='compile-tools' + - TEST_NAME='native-runs' diff --git a/examples/libs/data-structures/Makefile b/examples/libs/data-structures/Makefile new file mode 100644 index 000000000..1f4d6b765 --- /dev/null +++ b/examples/libs/data-structures/Makefile @@ -0,0 +1,7 @@ +CONTIKI_PROJECT = data-structures + +all: $(CONTIKI_PROJECT) + +CONTIKI = ../../.. + +include $(CONTIKI)/Makefile.include diff --git a/examples/libs/data-structures/data-structures.c b/examples/libs/data-structures/data-structures.c new file mode 100644 index 000000000..21752762a --- /dev/null +++ b/examples/libs/data-structures/data-structures.c @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2017, George Oikonomou - http://www.spd.gr + * Copyright (c) 2017, James Pope + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "lib/stack.h" +#include "lib/queue.h" +#include "lib/circular-list.h" +#include "lib/dbl-list.h" +#include "lib/dbl-circ-list.h" +#include "lib/random.h" + +#include +#include +#include +#include +/*---------------------------------------------------------------------------*/ +PROCESS(data_structure_process, "Data structure process"); +AUTOSTART_PROCESSES(&data_structure_process); +/*---------------------------------------------------------------------------*/ +STACK(demo_stack); +QUEUE(demo_queue); +CIRCULAR_LIST(demo_cll); +DBL_LIST(demo_dbl); +DBL_CIRC_LIST(demo_dblcl); +/*---------------------------------------------------------------------------*/ +typedef struct demo_struct_s { + struct demo_struct_s *next; + struct demo_struct_s *previous; + unsigned short value; +} demo_struct_t; +/*---------------------------------------------------------------------------*/ +#define DATA_STRUCTURE_DEMO_ELEMENT_COUNT 4 +static demo_struct_t elements[DATA_STRUCTURE_DEMO_ELEMENT_COUNT]; +/*---------------------------------------------------------------------------*/ +static void +dbl_circ_list_print(dbl_circ_list_t dblcl) +{ + demo_struct_t *this = *dblcl; + + if(*dblcl == NULL) { + printf("Length=0\n"); + return; + } + + do { + printf("<--(0x%04x)--0x%04x--(0x%04x)-->", this->previous->value, + this->value, this->next->value); + this = this->next; + } while(this != *dblcl); + + printf(" (Length=%lu)\n", dbl_circ_list_length(dblcl)); +} +/*---------------------------------------------------------------------------*/ +static void +demonstrate_dbl_circ_list(void) +{ + int i; + demo_struct_t *this; + + dbl_circ_list_init(demo_dblcl); + printf("============================\n"); + printf("Circular, doubly-linked list\n"); + + for(i = 0; i < DATA_STRUCTURE_DEMO_ELEMENT_COUNT; i++) { + elements[i].next = NULL; + elements[i].previous = NULL; + } + + /* Add elements */ + dbl_circ_list_add_tail(demo_dblcl, &elements[0]); + printf("Add tail : 0x%04x | ", elements[0].value); + dbl_circ_list_print(demo_dblcl); + + dbl_circ_list_add_after(demo_dblcl, &elements[0], &elements[1]); + printf("Add after : 0x%04x | ", elements[1].value); + dbl_circ_list_print(demo_dblcl); + + dbl_circ_list_add_head(demo_dblcl, &elements[2]); + printf("Add head : 0x%04x | ", elements[2].value); + dbl_circ_list_print(demo_dblcl); + + dbl_circ_list_add_before(demo_dblcl, &elements[2], &elements[3]); + printf("Add before: 0x%04x | ", elements[3].value); + dbl_circ_list_print(demo_dblcl); + + /* Remove head */ + this = dbl_circ_list_head(demo_dblcl); + printf("Rm head: (0x%04x) | ", this->value); + dbl_circ_list_remove(demo_dblcl, this); + dbl_circ_list_print(demo_dblcl); + + /* Remove currently second element */ + this = ((demo_struct_t *)dbl_circ_list_head(demo_dblcl))->next; + printf("Rm 2nd : (0x%04x) | ", this->value); + dbl_circ_list_remove(demo_dblcl, this); + dbl_circ_list_print(demo_dblcl); + + /* Remove tail */ + this = dbl_circ_list_tail(demo_dblcl); + printf("Rm tail: (0x%04x) | ", this->value); + dbl_circ_list_remove(demo_dblcl, this); + dbl_circ_list_print(demo_dblcl); + + /* Remove last remaining element */ + this = dbl_circ_list_tail(demo_dblcl); + printf("Rm last: (0x%04x) | ", this->value); + dbl_circ_list_remove(demo_dblcl, this); + dbl_circ_list_print(demo_dblcl); + + printf("Circular, doubly-linked list is%s empty\n", + dbl_circ_list_is_empty(demo_dblcl) ? "" : " not"); +} +/*---------------------------------------------------------------------------*/ +static void +dbl_list_print(dbl_list_t dll) +{ + demo_struct_t *this; + + for(this = *dll; this != NULL; this = this->next) { + printf("<--("); + if(this->previous == NULL) { + printf(" null "); + } else { + printf("0x%04x", this->previous->value); + } + + printf(")--0x%04x--(", this->value); + + if(this->next == NULL) { + printf(" null "); + } else { + printf("0x%04x", this->next->value); + } + printf(")-->"); + } + + printf(" (Length=%lu)\n", dbl_list_length(dll)); +} +/*---------------------------------------------------------------------------*/ +static void +demonstrate_dbl_list(void) +{ + int i; + demo_struct_t *this; + + dbl_list_init(demo_dbl); + printf("==================\n"); + printf("Doubly-linked list\n"); + + for(i = 0; i < DATA_STRUCTURE_DEMO_ELEMENT_COUNT; i++) { + elements[i].next = NULL; + elements[i].previous = NULL; + } + + /* Add elements */ + dbl_list_add_tail(demo_dbl, &elements[0]); + printf("Add tail : 0x%04x | ", elements[0].value); + dbl_list_print(demo_dbl); + + dbl_list_add_after(demo_dbl, &elements[0], &elements[1]); + printf("Add after : 0x%04x | ", elements[1].value); + dbl_list_print(demo_dbl); + + dbl_list_add_head(demo_dbl, &elements[2]); + printf("Add head : 0x%04x | ", elements[2].value); + dbl_list_print(demo_dbl); + + dbl_list_add_before(demo_dbl, &elements[2], &elements[3]); + printf("Add before: 0x%04x | ", elements[3].value); + dbl_list_print(demo_dbl); + + /* Remove head */ + this = dbl_list_head(demo_dbl); + printf("Rm head: (0x%04x) | ", this->value); + dbl_list_remove(demo_dbl, this); + dbl_list_print(demo_dbl); + + /* Remove currently second element */ + this = ((demo_struct_t *)dbl_list_head(demo_dbl))->next; + printf("Rm 2nd : (0x%04x) | ", this->value); + dbl_list_remove(demo_dbl, this); + dbl_list_print(demo_dbl); + + /* Remove tail */ + this = dbl_list_tail(demo_dbl); + printf("Rm tail: (0x%04x) | ", this->value); + dbl_list_remove(demo_dbl, this); + dbl_list_print(demo_dbl); + + /* Remove last remaining element */ + this = dbl_list_tail(demo_dbl); + printf("Rm last: (0x%04x) | ", this->value); + dbl_list_remove(demo_dbl, this); + dbl_list_print(demo_dbl); + + printf("Doubly-linked list is%s empty\n", + dbl_list_is_empty(demo_dbl) ? "" : " not"); +} +/*---------------------------------------------------------------------------*/ +static void +circular_list_print(circular_list_t cl) +{ + demo_struct_t *this = *cl; + + if(*cl == NULL) { + printf("Length=0\n"); + return; + } + + do { + printf("0x%04x-->", this->value); + this = this->next; + } while(this != *cl); + + printf("0x%04x (Length=%lu)\n", this->value, circular_list_length(cl)); +} +/*---------------------------------------------------------------------------*/ +static void +demonstrate_circular_list(void) +{ + int i; + + circular_list_init(demo_cll); + printf("============================\n"); + printf("Circular, singly-linked list\n"); + + /* Add elements */ + for(i = 0; i < DATA_STRUCTURE_DEMO_ELEMENT_COUNT; i++) { + elements[i].next = NULL; + circular_list_add(demo_cll, &elements[i]); + + printf("Add: 0x%04x | ", elements[i].value); + circular_list_print(demo_cll); + } + + /* Remove head */ + circular_list_remove(demo_cll, circular_list_head(demo_cll)); + printf("Remove head | "); + circular_list_print(demo_cll); + + /* Remove currently second element */ + circular_list_remove(demo_cll, + ((demo_struct_t *)circular_list_head(demo_cll))->next); + printf("Remove 2nd | "); + circular_list_print(demo_cll); + + /* Remove tail */ + circular_list_remove(demo_cll, circular_list_tail(demo_cll)); + printf("Remove tail | "); + circular_list_print(demo_cll); + + /* Remove last remaining element */ + circular_list_remove(demo_cll, circular_list_tail(demo_cll)); + printf("Remove last | "); + circular_list_print(demo_cll); + + printf("Circular list is%s empty\n", + circular_list_is_empty(demo_cll) ? "" : " not"); +} +/*---------------------------------------------------------------------------*/ +static void +demonstrate_stack(void) +{ + int i; + demo_struct_t *this; + + printf("=====\n"); + printf("Stack\n"); + + stack_init(demo_stack); + + /* Add elements */ + for(i = 0; i < DATA_STRUCTURE_DEMO_ELEMENT_COUNT; i++) { + elements[i].next = NULL; + stack_push(demo_stack, &elements[i]); + printf("Push: 0x%04x\n", elements[i].value); + } + + printf("Peek: 0x%04x\n", + ((demo_struct_t *)stack_peek(demo_stack))->value); + + for(i = 0; i <= DATA_STRUCTURE_DEMO_ELEMENT_COUNT; i++) { + this = stack_pop(demo_stack); + printf("Pop: "); + if(this == NULL) { + printf("(stack underflow)\n"); + } else { + printf("0x%04x\n", this->value); + } + } + printf("Stack is%s empty\n", + stack_is_empty(demo_stack) ? "" : " not"); +} +/*---------------------------------------------------------------------------*/ +static void +demonstrate_queue(void) +{ + int i; + demo_struct_t *this; + + printf("=====\n"); + printf("Queue\n"); + + queue_init(demo_queue); + + /* Add elements */ + for(i = 0; i < DATA_STRUCTURE_DEMO_ELEMENT_COUNT; i++) { + elements[i].next = NULL; + queue_enqueue(demo_queue, &elements[i]); + printf("Enqueue: 0x%04x\n", elements[i].value); + } + + printf("Peek: 0x%04x\n", + ((demo_struct_t *)queue_peek(demo_queue))->value); + + for(i = 0; i <= DATA_STRUCTURE_DEMO_ELEMENT_COUNT; i++) { + this = queue_dequeue(demo_queue); + printf("Dequeue: "); + if(this == NULL) { + printf("(queue underflow)\n"); + } else { + printf("0x%04lx\n", (unsigned long)this->value); + } + } + + printf("Queue is%s empty\n", + queue_is_empty(demo_queue) ? "" : " not"); +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(data_structure_process, ev, data) +{ + int i; + + PROCESS_BEGIN(); + + /* Generate some elements */ + printf("Elements: ["); + + for(i = 0; i < DATA_STRUCTURE_DEMO_ELEMENT_COUNT; i++) { + elements[i].next = NULL; + elements[i].value = random_rand(); + printf(" 0x%04x", elements[i].value); + } + printf(" ]\n"); + + demonstrate_stack(); + demonstrate_queue(); + demonstrate_circular_list(); + demonstrate_dbl_list(); + demonstrate_dbl_circ_list(); + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/os/lib/circular-list.c b/os/lib/circular-list.c new file mode 100644 index 000000000..2c04d8418 --- /dev/null +++ b/os/lib/circular-list.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2017, George Oikonomou - http://www.spd.gr + * Copyright (c) 2017, James Pope + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup circular-singly-linked-list + * @{ + * + * \file + * Implementation of circular singly linked lists + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "lib/circular-list.h" + +#include +#include +#include +/*---------------------------------------------------------------------------*/ +struct cl { + struct cl *next; +}; +/*---------------------------------------------------------------------------*/ +void +circular_list_init(circular_list_t cl) +{ + *cl = NULL; +} +/*---------------------------------------------------------------------------*/ +void * +circular_list_head(circular_list_t cl) +{ + return *cl; +} +/*---------------------------------------------------------------------------*/ +void * +circular_list_tail(circular_list_t cl) +{ + struct cl *this; + + if(*cl == NULL) { + return NULL; + } + + for(this = *cl; this->next != *cl; this = this->next); + + return this; +} +/*---------------------------------------------------------------------------*/ +void +circular_list_remove(circular_list_t cl, void *element) +{ + struct cl *this, *previous; + + if(*cl == NULL) { + return; + } + + /* + * We start traversing from the second element. + * The head will be visited last. We always update the list's head after + * removal, just in case we have just removed the head. + */ + previous = *cl; + this = previous->next; + + do { + if(this == element) { + previous->next = this->next; + *cl = this->next == this ? NULL : previous; + return; + } + previous = this; + this = this->next; + } while(this != ((struct cl *)*cl)->next); +} +/*---------------------------------------------------------------------------*/ +void +circular_list_add(circular_list_t cl, void *element) +{ + struct cl *head; + + if(element == NULL) { + return; + } + + /* Don't add twice */ + circular_list_remove(cl, element); + + head = *cl; + + if(head == NULL) { + /* If the list was empty, we update the new element to point to itself */ + ((struct cl *)element)->next = element; + } else { + /* If the list exists, we add the new element between the current head and + * the previously second element. */ + ((struct cl *)element)->next = head->next; + head->next = element; + } + + /* In all cases, the new element becomes the list's new head */ + *cl = element; +} +/*---------------------------------------------------------------------------*/ +unsigned long +circular_list_length(circular_list_t cl) +{ + unsigned long len = 1; + struct cl *this; + + if(circular_list_is_empty(cl)) { + return 0; + } + + for(this = *cl; this->next != *cl; this = this->next) { + len++; + } + + return len; +} +/*---------------------------------------------------------------------------*/ +bool +circular_list_is_empty(circular_list_t cl) +{ + return *cl == NULL ? true : false; +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/os/lib/circular-list.h b/os/lib/circular-list.h new file mode 100644 index 000000000..2edcaf808 --- /dev/null +++ b/os/lib/circular-list.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2017, George Oikonomou - http://www.spd.gr + * Copyright (c) 2017, James Pope + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/*---------------------------------------------------------------------------*/ +/** \addtogroup data + * @{ + * + * \defgroup circular-singly-linked-list Circular, singly-linked list + * + * This library provides functions for the creation and manipulation of + * circular, singly-linked lists. + * + * A circular, singly-linked list is declared using the CIRCULAR_LIST macro. + * Elements must be allocated by the calling code and must be of a C struct + * datatype. In this struct, the first field must be a pointer called \e next. + * This field will be used by the library to maintain the list. Application + * code must not modify this field directly. + * + * Functions that modify the list (add / remove) will, in the general case, + * update the list's head and item order. If you call one of these functions + * as part of a list traversal, it is advised to stop / restart traversing + * after the respective function returns. + * @{ + */ +/*---------------------------------------------------------------------------*/ +#ifndef CIRCULAR_LIST_H_ +#define CIRCULAR_LIST_H_ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" + +#include +#include +#include +/*---------------------------------------------------------------------------*/ +/** + * \brief Define a circular, singly-linked list. + * + * This macro defines a circular, singly-linked list. + * + * The datatype for elements must be a C struct. The struct's first member must + * be a pointer called \e next. This is used internally by the library to + * maintain data structure integrity and must not be modified directly by + * application code. + * + * \param name The name of the circular, singly-linked list. + */ +#define CIRCULAR_LIST(name) \ + static void *name##_circular_list = NULL; \ + static circular_list_t name = (circular_list_t)&name##_circular_list +/*---------------------------------------------------------------------------*/ +/** + * \brief The circular, singly-linked list datatype + */ +typedef void **circular_list_t; +/*---------------------------------------------------------------------------*/ +/** + * \brief Initialise a circular, singly-linked list. + * \param cl The circular, singly-linked list. + */ +void circular_list_init(circular_list_t cl); + +/** + * \brief Return the tail of a circular, singly-linked list. + * \param cl The circular, singly-linked list. + * \return A pointer to the list's head, or NULL if the list is empty + */ +void *circular_list_head(circular_list_t cl); + +/** + * \brief Return the tail of a circular, singly-linked list. + * \param cl The circular, singly-linked list. + * \return A pointer to the list's tail, or NULL if the list is empty + */ +void *circular_list_tail(circular_list_t cl); + +/** + * \brief Add an element to a circular, singly-linked list. + * \param cl The circular, singly-linked list. + * \param element A pointer to the element to be added. + * + * The caller should make no assumptions as to the position in the list of the + * new element. + * + * After this function returns, the list's head is not guaranteed to be the + * same as it was before the addition. + * + * Calling this function will update the list's head and item order. If you + * call this function as part of a list traversal, it is advised to stop + * traversing after this function returns. + */ +void circular_list_add(circular_list_t cl, void *element); + +/** + * \brief Remove an element from a circular, singly-linked list. + * \param cl The circular, singly-linked list. + * \param element A pointer to the element to be removed. + * + * After this function returns, the list's head is not guaranteed to be the + * same as it was before the addition. + * + * Calling this function will update the list's head and item order. If you + * call this function as part of a list traversal, it is advised to stop + * traversing after this function returns. + */ +void circular_list_remove(circular_list_t cl, void *element); + +/** + * \brief Get the length of a circular, singly-linked list. + * \param cl The circular, singly-linked list. + * \return The number of elements in the list + */ +unsigned long circular_list_length(circular_list_t cl); + +/** + * \brief Determine whether a circular, singly-linked list is empty. + * \param cl The circular, singly-linked list. + * \retval true The list is empty + * \retval false The list is not empty + */ +bool circular_list_is_empty(circular_list_t cl); +/*---------------------------------------------------------------------------*/ +#endif /* CIRCULAR_LIST_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/os/lib/dbl-circ-list.c b/os/lib/dbl-circ-list.c new file mode 100644 index 000000000..35b33a801 --- /dev/null +++ b/os/lib/dbl-circ-list.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2017, George Oikonomou - http://www.spd.gr + * Copyright (c) 2017, James Pope + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup doubly-linked-circular-list + * @{ + * + * \file + * Implementation of circular, doubly-linked lists + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "lib/dbl-circ-list.h" + +#include +#include +#include +/*---------------------------------------------------------------------------*/ +struct dblcl { + struct dblcl *next; + struct dblcl *previous; +}; +/*---------------------------------------------------------------------------*/ +void +dbl_circ_list_init(dbl_circ_list_t dblcl) +{ + *dblcl = NULL; +} +/*---------------------------------------------------------------------------*/ +void * +dbl_circ_list_head(dbl_circ_list_t dblcl) +{ + return *dblcl; +} +/*---------------------------------------------------------------------------*/ +void * +dbl_circ_list_tail(dbl_circ_list_t dblcl) +{ + struct dblcl *this; + + if(*dblcl == NULL) { + return NULL; + } + + for(this = *dblcl; this->next != *dblcl; this = this->next); + + return this; +} +/*---------------------------------------------------------------------------*/ +void +dbl_circ_list_remove(dbl_circ_list_t dblcl, void *element) +{ + struct dblcl *this; + + if(*dblcl == NULL || element == NULL) { + return; + } + + this = *dblcl; + + do { + if(this == element) { + this->previous->next = this->next; + this->next->previous = this->previous; + + /* We need to update the head of the list if we removed the head */ + if(*dblcl == element) { + *dblcl = this->next == this ? NULL : this->next; + } + + this->next = NULL; + this->previous = NULL; + + return; + } + + this = this->next; + } while(this != *dblcl); +} +/*---------------------------------------------------------------------------*/ +void +dbl_circ_list_add_head(dbl_circ_list_t dblcl, void *element) +{ + struct dblcl *head; + + if(element == NULL) { + return; + } + + /* Don't add twice */ + dbl_circ_list_remove(dblcl, element); + + head = dbl_circ_list_head(dblcl); + + if(head == NULL) { + /* If the list was empty */ + ((struct dblcl *)element)->next = element; + ((struct dblcl *)element)->previous = element; + } else { + /* If the list was not empty */ + ((struct dblcl *)element)->next = head; + ((struct dblcl *)element)->previous = head->previous; + head->previous->next = element; + head->previous = element; + } + + *dblcl = element; +} +/*---------------------------------------------------------------------------*/ +void +dbl_circ_list_add_tail(dbl_circ_list_t dblcl, void *element) +{ + struct dblcl *tail; + + if(element == NULL) { + return; + } + + /* Don't add twice */ + dbl_circ_list_remove(dblcl, element); + + tail = dbl_circ_list_tail(dblcl); + + if(tail == NULL) { + /* If the list was empty */ + ((struct dblcl *)element)->next = element; + ((struct dblcl *)element)->previous = element; + *dblcl = element; + } else { + /* If the list was not empty */ + ((struct dblcl *)element)->next = *dblcl; + ((struct dblcl *)element)->previous = tail; + tail->next->previous = element; + tail->next = element; + } +} +/*---------------------------------------------------------------------------*/ +void +dbl_circ_list_add_after(dbl_circ_list_t dblcl, void *existing, void *element) +{ + if(element == NULL || existing == NULL) { + return; + } + + /* Don't add twice */ + dbl_circ_list_remove(dblcl, element); + + ((struct dblcl *)element)->next = ((struct dblcl *)existing)->next; + ((struct dblcl *)element)->previous = existing; + ((struct dblcl *)existing)->next->previous = element; + ((struct dblcl *)existing)->next = element; +} +/*---------------------------------------------------------------------------*/ +void +dbl_circ_list_add_before(dbl_circ_list_t dblcl, void *existing, void *element) +{ + if(element == NULL || existing == NULL) { + return; + } + + /* Don't add twice */ + dbl_circ_list_remove(dblcl, element); + + ((struct dblcl *)element)->next = existing; + ((struct dblcl *)element)->previous = ((struct dblcl *)existing)->previous; + ((struct dblcl *)existing)->previous->next = element; + ((struct dblcl *)existing)->previous = element; + + /* If we added before the list's head, we must update the head */ + if(*dblcl == existing) { + *dblcl = element; + } +} +/*---------------------------------------------------------------------------*/ +unsigned long +dbl_circ_list_length(dbl_circ_list_t dblcl) +{ + unsigned long len = 1; + struct dblcl *this; + + if(*dblcl == NULL) { + return 0; + } + + for(this = *dblcl; this->next != *dblcl; this = this->next) { + len++; + } + + return len; +} +/*---------------------------------------------------------------------------*/ +bool +dbl_circ_list_is_empty(dbl_circ_list_t dblcl) +{ + return *dblcl == NULL ? true : false; +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/os/lib/dbl-circ-list.h b/os/lib/dbl-circ-list.h new file mode 100644 index 000000000..08eba8188 --- /dev/null +++ b/os/lib/dbl-circ-list.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2017, George Oikonomou - http://www.spd.gr + * Copyright (c) 2017, James Pope + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/*---------------------------------------------------------------------------*/ +/** \addtogroup data + * @{ + * + * \defgroup doubly-linked-circular-list Circular, doubly-linked list + * + * This library provides functions for the creation and manipulation of + * circular, doubly-linked lists. + * + * A circular, doubly-linked list is declared using the DBL_CIRC_LIST macro. + * Elements must be allocated by the calling code and must be of a C struct + * datatype. In this struct, the first field must be a pointer called \e next. + * The second field must be a pointer called \e previous. + * These fields will be used by the library to maintain the list. Application + * code must not modify these fields directly. + * + * Functions that modify the list (add / remove) will, in the general case, + * update the list's head and item order. If you call one of these functions + * as part of a list traversal, it is advised to stop / restart traversing + * after the respective function returns. + * @{ + */ +/*---------------------------------------------------------------------------*/ +#ifndef DBL_CIRC_LIST_H_ +#define DBL_CIRC_LIST_H_ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" + +#include +#include +#include +/*---------------------------------------------------------------------------*/ +/** + * \brief Define a circular, doubly-linked list. + * + * This macro defines a circular, doubly-linked list. + * + * The datatype for elements must be a C struct. + * The struct's first member must be a pointer called \e next. + * The second field must be a pointer called \e previous. + * These fields will be used by the library to maintain the list. Application + * code must not modify these fields directly. + * + * \param name The name of the circular, doubly-linked list. + */ +#define DBL_CIRC_LIST(name) \ + static void *name##_dbl_circ_list = NULL; \ + static dbl_list_t name = (dbl_circ_list_t)&name##_dbl_circ_list +/*---------------------------------------------------------------------------*/ +/** + * \brief The doubly-linked list datatype + */ +typedef void **dbl_circ_list_t; +/*---------------------------------------------------------------------------*/ +/** + * \brief Initialise a circular, doubly-linked list. + * \param dblcl The circular, doubly-linked list. + */ +void dbl_circ_list_init(dbl_circ_list_t dblcl); + +/** + * \brief Return the tail of a circular, doubly-linked list. + * \param dblcl The circular, doubly-linked list. + * \return A pointer to the list's head, or NULL if the list is empty + */ +void *dbl_circ_list_head(dbl_circ_list_t dblcl); + +/** + * \brief Return the tail of a circular, doubly-linked list. + * \param dblcl The circular, doubly-linked list. + * \return A pointer to the list's tail, or NULL if the list is empty + */ +void *dbl_circ_list_tail(dbl_circ_list_t dblcl); + +/** + * \brief Add an element to the head of a circular, doubly-linked list. + * \param dblcl The circular, doubly-linked list. + * \param element A pointer to the element to be added. + * + * Calling this function will update the list's head and item order. If you + * call this function as part of a list traversal, it is advised to stop + * traversing after this function returns. + */ +void dbl_circ_list_add_head(dbl_circ_list_t dblcl, void *element); + +/** + * \brief Add an element to the tail of a circular, doubly-linked list. + * \param dblcl The circular, doubly-linked list. + * \param element A pointer to the element to be added. + * + * Calling this function will update the list's head and item order. If you + * call this function as part of a list traversal, it is advised to stop + * traversing after this function returns. + */ +void dbl_circ_list_add_tail(dbl_circ_list_t dblcl, void *element); + +/** + * \brief Add an element to a circular, doubly linked list after an existing element. + * \param dblcl The circular, doubly-linked list. + * \param existing A pointer to the existing element. + * \param element A pointer to the element to be added. + * + * This function will add \e element after \e existing + * + * The function will not verify that \e existing is already part of the list. + * + * Calling this function will update the list's head and item order. If you + * call this function as part of a list traversal, it is advised to stop + * traversing after this function returns. + */ +void dbl_circ_list_add_after(dbl_circ_list_t dblcl, void *existing, + void *element); + +/** + * \brief Add an element to a circular, doubly linked list before an existing element. + * \param dblcl The circular, doubly-linked list. + * \param existing A pointer to the existing element. + * \param element A pointer to the element to be added. + * + * This function will add \e element before \e existing + * + * The function will not verify that \e existing is already part of the list. + * + * Calling this function will update the list's head and item order. If you + * call this function as part of a list traversal, it is advised to stop + * traversing after this function returns. + */ +void dbl_circ_list_add_before(dbl_circ_list_t dblcl, void *existing, + void *element); + +/** + * \brief Remove an element from a circular, doubly-linked list. + * \param dblcl The circular, doubly-linked list. + * \param element A pointer to the element to be removed. + * + * Calling this function will update the list's head and item order. If you + * call this function as part of a list traversal, it is advised to stop + * traversing after this function returns. + */ +void dbl_circ_list_remove(dbl_circ_list_t dblcl, void *element); + +/** + * \brief Get the length of a circular, doubly-linked list. + * \param dblcl The circular, doubly-linked list. + * \return The number of elements in the list + */ +unsigned long dbl_circ_list_length(dbl_circ_list_t dblcl); + +/** + * \brief Determine whether a circular, doubly-linked list is empty. + * \param dblcl The circular, doubly-linked list. + * \retval true The list is empty + * \retval false The list is not empty + */ +bool dbl_circ_list_is_empty(dbl_circ_list_t dblcl); +/*---------------------------------------------------------------------------*/ +#endif /* DBL_CIRC_LIST_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/os/lib/dbl-list.c b/os/lib/dbl-list.c new file mode 100644 index 000000000..7f2f4b587 --- /dev/null +++ b/os/lib/dbl-list.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2017, George Oikonomou - http://www.spd.gr + * Copyright (c) 2017, James Pope + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup doubly-linked-list + * @{ + * + * \file + * Implementation of doubly-linked lists + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "lib/dbl-list.h" + +#include +#include +#include +/*---------------------------------------------------------------------------*/ +struct dll { + struct dll *next; + struct dll *previous; +}; +/*---------------------------------------------------------------------------*/ +void +dbl_list_init(dbl_list_t dll) +{ + *dll = NULL; +} +/*---------------------------------------------------------------------------*/ +void * +dbl_list_head(dbl_list_t dll) +{ + return *dll; +} +/*---------------------------------------------------------------------------*/ +void * +dbl_list_tail(dbl_list_t dll) +{ + struct dll *this; + + if(*dll == NULL) { + return NULL; + } + + for(this = *dll; this->next != NULL; this = this->next); + + return this; +} +/*---------------------------------------------------------------------------*/ +void +dbl_list_remove(dbl_list_t dll, void *element) +{ + struct dll *this, *previous, *next; + + if(*dll == NULL || element == NULL) { + return; + } + + for(this = *dll; this != NULL; this = this->next) { + if(this == element) { + previous = this->previous; + next = this->next; + + if(previous) { + previous->next = this->next; + } + + if(next) { + next->previous = this->previous; + } + + if(*dll == this) { + *dll = next; + } + + return; + } + } +} +/*---------------------------------------------------------------------------*/ +void +dbl_list_add_head(dbl_list_t dll, void *element) +{ + struct dll *head; + + if(element == NULL) { + return; + } + + /* Don't add twice */ + dbl_list_remove(dll, element); + + head = dbl_list_head(dll); + + ((struct dll *)element)->previous = NULL; + ((struct dll *)element)->next = head; + + if(head) { + /* If the list was not empty, update ->previous on the old head */ + head->previous = element; + } + + *dll = element; +} +/*---------------------------------------------------------------------------*/ +void +dbl_list_add_tail(dbl_list_t dll, void *element) +{ + struct dll *tail; + + if(element == NULL) { + return; + } + + /* Don't add twice */ + dbl_list_remove(dll, element); + + tail = dbl_list_tail(dll); + + if(tail == NULL) { + /* The list was empty */ + *dll = element; + } else { + tail->next = element; + } + + ((struct dll *)element)->previous = tail; + ((struct dll *)element)->next = NULL; +} +/*---------------------------------------------------------------------------*/ +void +dbl_list_add_after(dbl_list_t dll, void *existing, void *element) +{ + if(element == NULL || existing == NULL) { + return; + } + + /* Don't add twice */ + dbl_list_remove(dll, element); + + ((struct dll *)element)->next = ((struct dll *)existing)->next; + ((struct dll *)element)->previous = existing; + + if(((struct dll *)existing)->next) { + ((struct dll *)existing)->next->previous = element; + } + ((struct dll *)existing)->next = element; +} +/*---------------------------------------------------------------------------*/ +void +dbl_list_add_before(dbl_list_t dll, void *existing, void *element) +{ + if(element == NULL || existing == NULL) { + return; + } + + /* Don't add twice */ + dbl_list_remove(dll, element); + + ((struct dll *)element)->next = existing; + ((struct dll *)element)->previous = ((struct dll *)existing)->previous; + + if(((struct dll *)existing)->previous) { + ((struct dll *)existing)->previous->next = element; + } + ((struct dll *)existing)->previous = element; + + /* If we added before the list's head, we must update the head */ + if(*dll == existing) { + *dll = element; + } +} +/*---------------------------------------------------------------------------*/ +unsigned long +dbl_list_length(dbl_list_t dll) +{ + unsigned long len = 0; + struct dll *this; + + if(*dll == NULL) { + return 0; + } + + for(this = *dll; this != NULL; this = this->next) { + len++; + } + + return len; +} +/*---------------------------------------------------------------------------*/ +bool +dbl_list_is_empty(dbl_list_t dll) +{ + return *dll == NULL ? true : false; +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/os/lib/dbl-list.h b/os/lib/dbl-list.h new file mode 100644 index 000000000..88caf30ce --- /dev/null +++ b/os/lib/dbl-list.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2017, George Oikonomou - http://www.spd.gr + * Copyright (c) 2017, James Pope + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/*---------------------------------------------------------------------------*/ +/** \addtogroup data + * @{ + * + * \defgroup doubly-linked-list Doubly-linked list + * + * This library provides functions for the creation and manipulation of + * doubly-linked lists. + * + * A doubly-linked list is declared using the DBL_LIST macro. + * Elements must be allocated by the calling code and must be of a C struct + * datatype. In this struct, the first field must be a pointer called \e next. + * The second field must be a pointer called \e previous. + * These fields will be used by the library to maintain the list. Application + * code must not modify these fields directly. + * + * Functions that modify the list (add / remove) will, in the general case, + * update the list's head and item order. If you call one of these functions + * as part of a list traversal, it is advised to stop / restart traversing + * after the respective function returns. + * @{ + */ +/*---------------------------------------------------------------------------*/ +#ifndef DBL_LIST_H_ +#define DBL_LIST_H_ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" + +#include +#include +#include +/*---------------------------------------------------------------------------*/ +/** + * \brief Define a doubly-linked list. + * + * This macro defines a doubly-linked list. + * + * The datatype for elements must be a C struct. + * The struct's first member must be a pointer called \e next. + * The second field must be a pointer called \e previous. + * These fields will be used by the library to maintain the list. Application + * code must not modify these fields directly. + * + * \param name The name of the doubly-linked list. + */ +#define DBL_LIST(name) \ + static void *name##_dbl_list = NULL; \ + static dbl_list_t name = (dbl_list_t)&name##_dbl_list +/*---------------------------------------------------------------------------*/ +/** + * \brief The doubly-linked list datatype + */ +typedef void **dbl_list_t; +/*---------------------------------------------------------------------------*/ +/** + * \brief Initialise a doubly-linked list. + * \param dll The doubly-linked list. + */ +void dbl_list_init(dbl_list_t dll); + +/** + * \brief Return the tail of a doubly-linked list. + * \param dll The doubly-linked list. + * \return A pointer to the list's head, or NULL if the list is empty + */ +void *dbl_list_head(dbl_list_t dll); + +/** + * \brief Return the tail of a doubly-linked list. + * \param dll The doubly-linked list. + * \return A pointer to the list's tail, or NULL if the list is empty + */ +void *dbl_list_tail(dbl_list_t dll); + +/** + * \brief Add an element to the head of a doubly-linked list. + * \param dll The doubly-linked list. + * \param element A pointer to the element to be added. + * + * Calling this function will update the list's head and item order. If you + * call this function as part of a list traversal, it is advised to stop + * traversing after this function returns. + */ +void dbl_list_add_head(dbl_list_t dll, void *element); + +/** + * \brief Add an element to the tail of a doubly-linked list. + * \param dll The doubly-linked list. + * \param element A pointer to the element to be added. + * + * Calling this function will update the list's head and item order. If you + * call this function as part of a list traversal, it is advised to stop + * traversing after this function returns. + */ +void dbl_list_add_tail(dbl_list_t dll, void *element); + +/** + * \brief Add an element to a doubly linked list after an existing element. + * \param dll The doubly-linked list. + * \param existing A pointer to the existing element. + * \param element A pointer to the element to be added. + * + * This function will add \e element after \e existing + * + * The function will not verify that \e existing is already part of the list. + * + * Calling this function will update the list's head and item order. If you + * call this function as part of a list traversal, it is advised to stop + * traversing after this function returns. + */ +void dbl_list_add_after(dbl_list_t dll, void *existing, void *element); + +/** + * \brief Add an element to a doubly linked list before an existing element. + * \param dll The doubly-linked list. + * \param existing A pointer to the existing element. + * \param element A pointer to the element to be added. + * + * This function will add \e element before \e existing + * + * The function will not verify that \e existing is already part of the list. + * + * Calling this function will update the list's head and item order. If you + * call this function as part of a list traversal, it is advised to stop + * traversing after this function returns. + */ +void dbl_list_add_before(dbl_list_t dll, void *existing, void *element); + +/** + * \brief Remove an element from a doubly-linked list. + * \param dll The doubly-linked list. + * \param element A pointer to the element to be removed. + * + * Calling this function will update the list's head and item order. If you + * call this function as part of a list traversal, it is advised to stop + * traversing after this function returns. + */ +void dbl_list_remove(dbl_list_t dll, void *element); + +/** + * \brief Get the length of a doubly-linked list. + * \param dll The doubly-linked list. + * \return The number of elements in the list + */ +unsigned long dbl_list_length(dbl_list_t dll); + +/** + * \brief Determine whether a doubly-linked list is empty. + * \param dll The doubly-linked list. + * \retval true The list is empty + * \retval false The list is not empty + */ +bool dbl_list_is_empty(dbl_list_t dll); +/*---------------------------------------------------------------------------*/ +#endif /* DBL_LIST_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/os/lib/queue.h b/os/lib/queue.h new file mode 100644 index 000000000..9d65fec9e --- /dev/null +++ b/os/lib/queue.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2017, George Oikonomou - http://www.spd.gr + * Copyright (c) 2017, James Pope + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup data + * @{ + * + * \defgroup queue Queue library + * + * This library provides functions for the creation and manipulation of + * queues. The library is implemented as a wrapper around the list library. + * + * A queue is declared using the QUEUE macro. Queue elements must be + * allocated by the calling code and must be of a C struct datatype. In this + * struct, the first field must be a pointer called \e next. This field will + * be used by the library to maintain the queue. Application code must not + * modify this field directly. + * @{ + */ +/*---------------------------------------------------------------------------*/ +#ifndef QUEUE_H_ +#define QUEUE_H_ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "lib/list.h" + +#include +/*---------------------------------------------------------------------------*/ +/** + * \brief The queue data type + */ +typedef list_t queue_t; +/*---------------------------------------------------------------------------*/ +/** + * \brief Define a queue. + * + * This macro defines a queue. + * + * The datatype for elements must be a C struct. The struct's first member must + * be a pointer called \e next. This is used internally by the library to + * maintain data structure integrity and must not be modified directly by + * application code. + * + * \param name The name of the queue. + */ +#define QUEUE(name) LIST(name) +/*---------------------------------------------------------------------------*/ +struct queue { + struct queue *next; +}; +/*---------------------------------------------------------------------------*/ +/** + * \brief Initialise a queue + * \param queue The queue + */ +static inline void +queue_init(queue_t queue) +{ + list_init(queue); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Adds an element to the tail of the queue + * \param queue The queue + * \param element A pointer to the element to be added + */ +static inline void +queue_enqueue(queue_t queue, void *element) +{ + list_add(queue, element); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Removes the element at the front of the queue + * \param queue The queue + * \return A pointer to the element removed + * + * If this function returns NULL if the queue was empty (queue underflow) + */ +static inline void * +queue_dequeue(queue_t queue) +{ + return list_pop(queue); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns the front element of the queue, without removing it + * \param queue The queue + * \return A pointer to the element at the front of the queue + */ +static inline void * +queue_peek(queue_t queue) +{ + return list_head(queue); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Check if a queue is empty + * \param queue The queue + * \retval true The queue is empty + * \retval false The queue has at least one element + */ +static inline bool +queue_is_empty(queue_t queue) +{ + return *queue == NULL ? true : false; +} +/*---------------------------------------------------------------------------*/ +#endif /* QUEUE_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/os/lib/stack.h b/os/lib/stack.h new file mode 100644 index 000000000..8d56e46ef --- /dev/null +++ b/os/lib/stack.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2017, George Oikonomou - http://www.spd.gr + * Copyright (c) 2017, James Pope + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup data + * @{ + * + * \defgroup stack-data-structure Stack library + * + * This library provides functions for the creation and manipulation of + * stacks. The library is implemented as a wrapper around the list library. + * + * A stack is declared using the STACK macro. Stack elements must be + * allocated by the calling code and must be of a C struct datatype. In this + * struct, the first field must be a pointer called \e next. This field will + * be used by the library to maintain the stack. Application code must not + * modify this field directly. + * @{ + */ +/*---------------------------------------------------------------------------*/ +#ifndef STACK_H_ +#define STACK_H_ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "lib/list.h" + +#include +/*---------------------------------------------------------------------------*/ +/** + * \brief The stack data type + */ +typedef list_t stack_t; +/*---------------------------------------------------------------------------*/ +/** + * \brief Define a stack. + * + * This macro defines a stack. + * + * The datatype for elements must be a C struct. The struct's first member must + * be a pointer called \e next. This is used internally by the library to + * maintain data structure integrity and must not be modified directly by + * application code. + * + * \param name The name of the stack. + */ +#define STACK(name) LIST(name) +/*---------------------------------------------------------------------------*/ +struct stack { + struct stack *next; +}; +/*---------------------------------------------------------------------------*/ +/** + * \brief Initialise a stack + * \param stack The stack + */ +static inline void +stack_init(stack_t stack) +{ + list_init(stack); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Adds an element to the top of the stack + * \param stack The stack + * \param element A pointer to the element to be added + */ +static inline void +stack_push(stack_t stack, void *element) +{ + list_push(stack, element); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Removes the top element from the stack + * \param stack The stack + * \return A pointer to the element popped + * + * If this function returns NULL if the stack was empty (stack underflow) + */ +static inline void * +stack_pop(stack_t stack) +{ + return list_pop(stack); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns the top element of the stack, without popping it + * \param stack The stack + * \return A pointer to the element at the top of the stack + */ +static inline void * +stack_peek(stack_t stack) +{ + return list_head(stack); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Check if a stack is empty + * \param stack The stack + * \retval true The stack is empty + * \retval false The stack has at least one element + */ +static inline bool +stack_is_empty(stack_t stack) +{ + return *stack == NULL ? true : false; +} +/*---------------------------------------------------------------------------*/ +#endif /* STACK_H_ */ +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/tests/01-compile-base/Makefile b/tests/01-compile-base/Makefile index dee315dfd..78efc6411 100644 --- a/tests/01-compile-base/Makefile +++ b/tests/01-compile-base/Makefile @@ -10,6 +10,8 @@ multicast/sky \ libs/logging/native \ libs/energest/native \ libs/energest/sky \ +libs/data-structures/native \ +libs/data-structures/sky \ rpl-udp/sky \ rpl-border-router/native \ rpl-border-router/sky \ diff --git a/tests/02-compile-arm-ports-01/Makefile b/tests/02-compile-arm-ports-01/Makefile index 7b83165cf..b54acc303 100644 --- a/tests/02-compile-arm-ports-01/Makefile +++ b/tests/02-compile-arm-ports-01/Makefile @@ -34,6 +34,7 @@ platform-specific/nrf52dk/coap-demo/nrf52dk:coap-client:SERVER_IPV6_ADDR=ffff \ platform-specific/nrf52dk/mqtt-demo/nrf52dk \ platform-specific/nrf52dk/blink-hello/nrf52dk \ platform-specific/nrf52dk/timer-test/nrf52dk \ +libs/data-structures/nrf52dk \ libs/logging/nrf52dk TOOLS= diff --git a/tests/03-compile-arm-ports-02/Makefile b/tests/03-compile-arm-ports-02/Makefile index 62b0ad084..25345e14c 100644 --- a/tests/03-compile-arm-ports-02/Makefile +++ b/tests/03-compile-arm-ports-02/Makefile @@ -26,6 +26,7 @@ http-socket/zoul \ libs/timers/zoul \ libs/energest/zoul \ libs/trickle-library/zoul \ +libs/data-structures/zoul \ nullnet/zoul \ slip-radio/zoul \ storage/cfs-coffee/openmote-cc2538 \ diff --git a/tests/07-simulation-base/31-data-structures-sky.csc b/tests/07-simulation-base/31-data-structures-sky.csc new file mode 100644 index 000000000..cd0e35ee3 --- /dev/null +++ b/tests/07-simulation-base/31-data-structures-sky.csc @@ -0,0 +1,98 @@ + + + [APPS_DIR]/mrm + [APPS_DIR]/mspsim + [APPS_DIR]/avrora + [APPS_DIR]/serial_socket + [APPS_DIR]/collect-view + [APPS_DIR]/powertracker + + data-structures-sky + 123456 + 1000000 + + org.contikios.cooja.radiomediums.UDGM + 50.0 + 100.0 + 1.0 + 1.0 + + + 40000 + + + org.contikios.cooja.mspmote.SkyMoteType + sky1 + Sky Mote Type #sky1 + [CONTIKI_DIR]/tests/07-simulation-base/code-data-structures/test-data-structures.c + make test-data-structures.sky TARGET=sky + [CONTIKI_DIR]/tests/07-simulation-base/code-data-structures/test-data-structures.sky + org.contikios.cooja.interfaces.Position + org.contikios.cooja.interfaces.RimeAddress + org.contikios.cooja.interfaces.IPAddress + org.contikios.cooja.interfaces.Mote2MoteRelations + org.contikios.cooja.interfaces.MoteAttributes + org.contikios.cooja.mspmote.interfaces.MspClock + org.contikios.cooja.mspmote.interfaces.MspMoteID + org.contikios.cooja.mspmote.interfaces.SkyButton + org.contikios.cooja.mspmote.interfaces.SkyFlash + org.contikios.cooja.mspmote.interfaces.SkyCoffeeFilesystem + org.contikios.cooja.mspmote.interfaces.Msp802154Radio + org.contikios.cooja.mspmote.interfaces.MspSerial + org.contikios.cooja.mspmote.interfaces.SkyLED + org.contikios.cooja.mspmote.interfaces.MspDebugOutput + org.contikios.cooja.mspmote.interfaces.SkyTemperature + + + + + org.contikios.cooja.interfaces.Position + 3.086692968239446 + 5.726233183606267 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 1 + + sky1 + + + + org.contikios.cooja.plugins.SimControl + 280 + 2 + 160 + 400 + 0 + + + org.contikios.cooja.plugins.LogListener + + + + + + 586 + 1 + 666 + 400 + 160 + + + org.contikios.cooja.plugins.ScriptRunner + + [CONFIG_DIR]/js/data-structures.js + true + + 600 + 0 + 700 + 5 + 1 + + diff --git a/tests/07-simulation-base/code-data-structures/Makefile b/tests/07-simulation-base/code-data-structures/Makefile new file mode 100644 index 000000000..d88177bbe --- /dev/null +++ b/tests/07-simulation-base/code-data-structures/Makefile @@ -0,0 +1,9 @@ +all: test-data-structures + +MODULES += os/services/unit-test + +MAKE_MAC = MAKE_MAC_NULLMAC +MAKE_NET = MAKE_NET_NULLNET + +CONTIKI = ../../.. +include $(CONTIKI)/Makefile.include diff --git a/tests/07-simulation-base/code-data-structures/project-conf.h b/tests/07-simulation-base/code-data-structures/project-conf.h new file mode 100644 index 000000000..1e6d48809 --- /dev/null +++ b/tests/07-simulation-base/code-data-structures/project-conf.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017, Yasuyuki Tanaka + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PROJECT_CONF_H_ +#define PROJECT_CONF_H_ + +#define UNIT_TEST_PRINT_FUNCTION print_test_report + +#endif /* PROJECT_CONF_H_ */ diff --git a/tests/07-simulation-base/code-data-structures/test-data-structures.c b/tests/07-simulation-base/code-data-structures/test-data-structures.c new file mode 100644 index 000000000..e682e43da --- /dev/null +++ b/tests/07-simulation-base/code-data-structures/test-data-structures.c @@ -0,0 +1,897 @@ +/* + * Copyright (c) 2017, George Oikonomou - http://www.spd.gr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "lib/stack.h" +#include "lib/queue.h" +#include "lib/circular-list.h" +#include "lib/dbl-list.h" +#include "lib/dbl-circ-list.h" +#include "lib/random.h" +#include "services/unit-test/unit-test.h" + +#include +#include +#include +#include +/*---------------------------------------------------------------------------*/ +PROCESS(data_structure_test_process, "Data structure process"); +AUTOSTART_PROCESSES(&data_structure_test_process); +/*---------------------------------------------------------------------------*/ +typedef struct demo_struct_s { + struct demo_struct_s *next; + struct demo_struct_s *previous; +} demo_struct_t; +/*---------------------------------------------------------------------------*/ +#define ELEMENT_COUNT 10 +static demo_struct_t elements[ELEMENT_COUNT]; +/*---------------------------------------------------------------------------*/ +void +print_test_report(const unit_test_t *utp) +{ + printf("=check-me= "); + if(utp->result == unit_test_failure) { + printf("FAILED - %s: exit at L%u\n", utp->descr, utp->exit_line); + } else { + printf("SUCCEEDED - %s\n", utp->descr); + } +} +/*---------------------------------------------------------------------------*/ +UNIT_TEST_REGISTER(test_stack, "Stack Push/Pop"); +UNIT_TEST(test_stack) +{ + STACK(stack); + + UNIT_TEST_BEGIN(); + + memset(elements, 0, sizeof(elements)); + stack_init(stack); + + /* Starts from empty */ + UNIT_TEST_ASSERT(stack_is_empty(stack) == true); + UNIT_TEST_ASSERT(stack_peek(stack) == NULL); + UNIT_TEST_ASSERT(stack_pop(stack) == NULL); + + /* + * Push two elements. Peek and pop should be the last one. Stack should be + * non-empty after the pop + */ + stack_push(stack, &elements[0]); + stack_push(stack, &elements[1]); + + UNIT_TEST_ASSERT(stack_peek(stack) == &elements[1]); + UNIT_TEST_ASSERT(stack_pop(stack) == &elements[1]); + UNIT_TEST_ASSERT(stack_peek(stack) == &elements[0]); + UNIT_TEST_ASSERT(stack_is_empty(stack) == false); + UNIT_TEST_ASSERT(stack_pop(stack) == &elements[0]); + + /* Ends empty */ + UNIT_TEST_ASSERT(stack_is_empty(stack) == true); + UNIT_TEST_ASSERT(stack_peek(stack) == NULL); + UNIT_TEST_ASSERT(stack_pop(stack) == NULL); + + UNIT_TEST_END(); +} +/*---------------------------------------------------------------------------*/ +UNIT_TEST_REGISTER(test_queue, "Queue Enqueue/Dequeue"); +UNIT_TEST(test_queue) +{ + QUEUE(queue); + + UNIT_TEST_BEGIN(); + + memset(elements, 0, sizeof(elements)); + queue_init(queue); + + /* Starts from empty */ + UNIT_TEST_ASSERT(queue_is_empty(queue) == true); + UNIT_TEST_ASSERT(queue_peek(queue) == NULL); + UNIT_TEST_ASSERT(queue_dequeue(queue) == NULL); + + /* Enqueue three elements. They should come out in the same order */ + queue_enqueue(queue, &elements[0]); + queue_enqueue(queue, &elements[1]); + queue_enqueue(queue, &elements[2]); + + UNIT_TEST_ASSERT(queue_dequeue(queue) == &elements[0]); + UNIT_TEST_ASSERT(queue_dequeue(queue) == &elements[1]); + UNIT_TEST_ASSERT(queue_dequeue(queue) == &elements[2]); + + /* Should be empty */ + UNIT_TEST_ASSERT(queue_is_empty(queue) == true); + UNIT_TEST_ASSERT(queue_peek(queue) == NULL); + UNIT_TEST_ASSERT(queue_dequeue(queue) == NULL); + + UNIT_TEST_END(); +} +/*---------------------------------------------------------------------------*/ +UNIT_TEST_REGISTER(test_csll, "Circular, singly-linked list"); +UNIT_TEST(test_csll) +{ + demo_struct_t *head, *tail; + + CIRCULAR_LIST(csll); + + UNIT_TEST_BEGIN(); + + memset(elements, 0, sizeof(elements)); + circular_list_init(csll); + + /* Starts from empty */ + UNIT_TEST_ASSERT(circular_list_is_empty(csll) == true); + UNIT_TEST_ASSERT(circular_list_length(csll) == 0); + UNIT_TEST_ASSERT(circular_list_head(csll) == NULL); + UNIT_TEST_ASSERT(circular_list_tail(csll) == NULL); + + /* Add one element. Should point to itself and act as head and tail */ + circular_list_add(csll, &elements[0]); + + UNIT_TEST_ASSERT(circular_list_is_empty(csll) == false); + UNIT_TEST_ASSERT(circular_list_length(csll) == 1); + UNIT_TEST_ASSERT(circular_list_head(csll) == &elements[0]); + UNIT_TEST_ASSERT(circular_list_tail(csll) == &elements[0]); + UNIT_TEST_ASSERT(elements[0].next == &elements[0]); + + /* Add a second element. The two should point to each-other */ + circular_list_add(csll, &elements[1]); + UNIT_TEST_ASSERT(elements[0].next == &elements[1]); + UNIT_TEST_ASSERT(elements[1].next == &elements[0]); + + /* + * Add a third element and check that head->next->next points to tail. + * Check that tail->next points to the head + */ + circular_list_add(csll, &elements[2]); + head = circular_list_head(csll); + tail = circular_list_tail(csll); + + UNIT_TEST_ASSERT(head->next->next == circular_list_tail(csll)); + UNIT_TEST_ASSERT(tail->next == circular_list_head(csll)); + + /* Re-add an existing element. Check the list's integrity */ + circular_list_add(csll, &elements[1]); + head = circular_list_head(csll); + tail = circular_list_tail(csll); + + UNIT_TEST_ASSERT(circular_list_is_empty(csll) == false); + UNIT_TEST_ASSERT(circular_list_length(csll) == 3); + UNIT_TEST_ASSERT(head->next->next->next == circular_list_head(csll)); + UNIT_TEST_ASSERT(head->next->next == circular_list_tail(csll)); + UNIT_TEST_ASSERT(tail->next == circular_list_head(csll)); + + /* Add another two elements, then start testing removal */ + circular_list_add(csll, &elements[3]); + circular_list_add(csll, &elements[4]); + + /* Remove an item in the middle and test list integrity */ + head = circular_list_head(csll); + circular_list_remove(csll, head->next->next); + head = circular_list_head(csll); + tail = circular_list_tail(csll); + + UNIT_TEST_ASSERT(circular_list_length(csll) == 4); + UNIT_TEST_ASSERT(head->next->next->next->next == circular_list_head(csll)); + UNIT_TEST_ASSERT(head->next->next->next == circular_list_tail(csll)); + UNIT_TEST_ASSERT(tail->next == circular_list_head(csll)); + + /* Remove the head and test list integrity */ + circular_list_remove(csll, circular_list_head(csll)); + head = circular_list_head(csll); + tail = circular_list_tail(csll); + + UNIT_TEST_ASSERT(circular_list_length(csll) == 3); + UNIT_TEST_ASSERT(head->next->next->next == circular_list_head(csll)); + UNIT_TEST_ASSERT(head->next->next == circular_list_tail(csll)); + UNIT_TEST_ASSERT(tail->next == circular_list_head(csll)); + + /* Remove the tail and test list integrity */ + circular_list_remove(csll, circular_list_tail(csll)); + head = circular_list_head(csll); + tail = circular_list_tail(csll); + + UNIT_TEST_ASSERT(circular_list_length(csll) == 2); + UNIT_TEST_ASSERT(head->next->next == circular_list_head(csll)); + UNIT_TEST_ASSERT(head->next == circular_list_tail(csll)); + UNIT_TEST_ASSERT(tail->next == circular_list_head(csll)); + + /* + * Remove the tail + * Only one item left: Make sure the head and tail are the same and point to + * each other + */ + circular_list_remove(csll, circular_list_tail(csll)); + head = circular_list_head(csll); + tail = circular_list_tail(csll); + + UNIT_TEST_ASSERT(circular_list_length(csll) == 1); + UNIT_TEST_ASSERT(head == tail); + UNIT_TEST_ASSERT(head->next->next == circular_list_head(csll)); + UNIT_TEST_ASSERT(head->next == circular_list_head(csll)); + UNIT_TEST_ASSERT(head->next == circular_list_tail(csll)); + UNIT_TEST_ASSERT(tail->next == circular_list_head(csll)); + UNIT_TEST_ASSERT(tail->next == circular_list_tail(csll)); + + /* Remove the last element by removing the head */ + circular_list_remove(csll, circular_list_head(csll)); + UNIT_TEST_ASSERT(circular_list_is_empty(csll) == true); + UNIT_TEST_ASSERT(circular_list_length(csll) == 0); + UNIT_TEST_ASSERT(circular_list_head(csll) == NULL); + UNIT_TEST_ASSERT(circular_list_tail(csll) == NULL); + + /* Remove the last element by removing the tail */ + circular_list_add(csll, &elements[0]); + circular_list_remove(csll, circular_list_tail(csll)); + UNIT_TEST_ASSERT(circular_list_is_empty(csll) == true); + UNIT_TEST_ASSERT(circular_list_length(csll) == 0); + UNIT_TEST_ASSERT(circular_list_head(csll) == NULL); + UNIT_TEST_ASSERT(circular_list_tail(csll) == NULL); + + UNIT_TEST_END(); +} +/*---------------------------------------------------------------------------*/ +UNIT_TEST_REGISTER(test_dll, "Doubly-linked list"); +UNIT_TEST(test_dll) +{ + demo_struct_t *head, *tail; + + CIRCULAR_LIST(dll); + + UNIT_TEST_BEGIN(); + + memset(elements, 0, sizeof(elements)); + + /* Starts from empty */ + dbl_list_init(dll); + UNIT_TEST_ASSERT(dbl_list_is_empty(dll) == true); + UNIT_TEST_ASSERT(dbl_list_length(dll) == 0); + UNIT_TEST_ASSERT(dbl_list_head(dll) == NULL); + UNIT_TEST_ASSERT(dbl_list_tail(dll) == NULL); + + /* + * Add an item by adding to the head. + * Head and tail should point to NULL in both directions + */ + dbl_list_add_head(dll, &elements[0]); + head = dbl_list_head(dll); + tail = dbl_list_tail(dll); + + UNIT_TEST_ASSERT(dbl_list_is_empty(dll) == false); + UNIT_TEST_ASSERT(dbl_list_length(dll) == 1); + UNIT_TEST_ASSERT(head == &elements[0]); + UNIT_TEST_ASSERT(tail == &elements[0]); + UNIT_TEST_ASSERT(head->previous == NULL); + UNIT_TEST_ASSERT(head->next == NULL); + UNIT_TEST_ASSERT(tail->previous == NULL); + UNIT_TEST_ASSERT(tail->next == NULL); + + /* + * Add an item by adding to the tail. + * Head and tail should point to NULL in both directions + */ + dbl_list_remove(dll, dbl_list_head(dll)); + dbl_list_add_tail(dll, &elements[1]); + head = dbl_list_head(dll); + tail = dbl_list_tail(dll); + + UNIT_TEST_ASSERT(dbl_list_is_empty(dll) == false); + UNIT_TEST_ASSERT(dbl_list_length(dll) == 1); + UNIT_TEST_ASSERT(head == &elements[1]); + UNIT_TEST_ASSERT(tail == &elements[1]); + UNIT_TEST_ASSERT(head->previous == NULL); + UNIT_TEST_ASSERT(head->next == NULL); + UNIT_TEST_ASSERT(tail->previous == NULL); + UNIT_TEST_ASSERT(tail->next == NULL); + + /* + * Add a second item to head. Head points forward to tail. + * Tail points backwards to head. + */ + dbl_list_add_head(dll, &elements[2]); + head = dbl_list_head(dll); + tail = dbl_list_tail(dll); + + UNIT_TEST_ASSERT(dbl_list_is_empty(dll) == false); + UNIT_TEST_ASSERT(dbl_list_length(dll) == 2); + UNIT_TEST_ASSERT(head == &elements[2]); + UNIT_TEST_ASSERT(tail == &elements[1]); + UNIT_TEST_ASSERT(head->previous == NULL); + UNIT_TEST_ASSERT(head->next == tail); + UNIT_TEST_ASSERT(tail->previous == head); + UNIT_TEST_ASSERT(tail->next == NULL); + + /* + * Add before head. + * NULL <-- 3 --> 2 --> 1 --> NULL + */ + dbl_list_add_before(dll, dbl_list_head(dll), &elements[3]); + head = dbl_list_head(dll); + tail = dbl_list_tail(dll); + + UNIT_TEST_ASSERT(dbl_list_is_empty(dll) == false); + UNIT_TEST_ASSERT(dbl_list_length(dll) == 3); + UNIT_TEST_ASSERT(head == &elements[3]); + UNIT_TEST_ASSERT(tail == &elements[1]); + UNIT_TEST_ASSERT(head->previous == NULL); + UNIT_TEST_ASSERT(head->next == &elements[2]); + UNIT_TEST_ASSERT(head->next->next == tail); + UNIT_TEST_ASSERT(head->next->next->next == NULL); + UNIT_TEST_ASSERT(tail->previous == &elements[2]); + UNIT_TEST_ASSERT(tail->previous->previous == &elements[3]); + UNIT_TEST_ASSERT(tail->previous->previous->previous == NULL); + UNIT_TEST_ASSERT(tail->next == NULL); + + /* + * Add after head. + * NULL <-- 3 --> 4 --> 2 --> 1 --> NULL + */ + dbl_list_add_after(dll, dbl_list_head(dll), &elements[4]); + head = dbl_list_head(dll); + tail = dbl_list_tail(dll); + + UNIT_TEST_ASSERT(dbl_list_is_empty(dll) == false); + UNIT_TEST_ASSERT(dbl_list_length(dll) == 4); + UNIT_TEST_ASSERT(head == &elements[3]); + UNIT_TEST_ASSERT(tail == &elements[1]); + UNIT_TEST_ASSERT(head->previous == NULL); + UNIT_TEST_ASSERT(head->next == &elements[4]); + UNIT_TEST_ASSERT(head->next->next == &elements[2]); + UNIT_TEST_ASSERT(head->next->next->next == tail); + UNIT_TEST_ASSERT(head->next->next->next->next == NULL); + UNIT_TEST_ASSERT(tail->previous == &elements[2]); + UNIT_TEST_ASSERT(tail->previous->previous == &elements[4]); + UNIT_TEST_ASSERT(tail->previous->previous->previous == &elements[3]); + UNIT_TEST_ASSERT(tail->previous->previous->previous->previous == NULL); + UNIT_TEST_ASSERT(tail->next == NULL); + + /* + * Add at 3rd position by adding after 2nd + * NULL <-- 3 --> 4 --> 5 --> 2 --> 1 --> NULL + */ + dbl_list_add_after(dll, &elements[4], &elements[5]); + head = dbl_list_head(dll); + tail = dbl_list_tail(dll); + + UNIT_TEST_ASSERT(dbl_list_is_empty(dll) == false); + UNIT_TEST_ASSERT(dbl_list_length(dll) == 5); + UNIT_TEST_ASSERT(head == &elements[3]); + UNIT_TEST_ASSERT(tail == &elements[1]); + UNIT_TEST_ASSERT(head->previous == NULL); + UNIT_TEST_ASSERT(head->next == &elements[4]); + UNIT_TEST_ASSERT(head->next->next == &elements[5]); + UNIT_TEST_ASSERT(tail->previous->previous == &elements[5]); + UNIT_TEST_ASSERT(tail->previous == &elements[2]); + UNIT_TEST_ASSERT(elements[5].next == &elements[2]); + UNIT_TEST_ASSERT(elements[5].previous == &elements[4]); + UNIT_TEST_ASSERT(tail->next == NULL); + + /* + * Add at 3rd position by adding before 3rd + * NULL <-- 3 --> 4 --> 6 --> 5 --> 2 --> 1 --> NULL + */ + dbl_list_add_before(dll, &elements[5], &elements[6]); + head = dbl_list_head(dll); + tail = dbl_list_tail(dll); + + UNIT_TEST_ASSERT(dbl_list_is_empty(dll) == false); + UNIT_TEST_ASSERT(dbl_list_length(dll) == 6); + UNIT_TEST_ASSERT(head == &elements[3]); + UNIT_TEST_ASSERT(tail == &elements[1]); + UNIT_TEST_ASSERT(head->previous == NULL); + UNIT_TEST_ASSERT(head->next == &elements[4]); + UNIT_TEST_ASSERT(head->next->next == &elements[6]); + UNIT_TEST_ASSERT(tail->previous->previous == &elements[5]); + UNIT_TEST_ASSERT(elements[6].next == &elements[5]); + UNIT_TEST_ASSERT(elements[6].previous == &elements[4]); + UNIT_TEST_ASSERT(tail->next == NULL); + + /* + * Add before tail + * NULL <-- 3 --> 4 --> 6 --> 5 --> 2 --> 7 --> 1 --> NULL + */ + dbl_list_add_before(dll, dbl_list_tail(dll), &elements[7]); + head = dbl_list_head(dll); + tail = dbl_list_tail(dll); + + UNIT_TEST_ASSERT(dbl_list_is_empty(dll) == false); + UNIT_TEST_ASSERT(dbl_list_length(dll) == 7); + UNIT_TEST_ASSERT(head == &elements[3]); + UNIT_TEST_ASSERT(tail == &elements[1]); + UNIT_TEST_ASSERT(head->previous == NULL); + UNIT_TEST_ASSERT(tail->previous == &elements[7]); + UNIT_TEST_ASSERT(elements[7].next == &elements[1]); + UNIT_TEST_ASSERT(elements[7].previous == &elements[2]); + UNIT_TEST_ASSERT(tail->next == NULL); + + /* + * Add after tail + * NULL <-- 3 --> 4 --> 6 --> 5 --> 2 --> 7 --> 1 --> 8 --> NULL + */ + dbl_list_add_after(dll, dbl_list_tail(dll), &elements[8]); + head = dbl_list_head(dll); + tail = dbl_list_tail(dll); + + UNIT_TEST_ASSERT(dbl_list_is_empty(dll) == false); + UNIT_TEST_ASSERT(dbl_list_length(dll) == 8); + UNIT_TEST_ASSERT(head == &elements[3]); + UNIT_TEST_ASSERT(tail == &elements[8]); + UNIT_TEST_ASSERT(head->previous == NULL); + UNIT_TEST_ASSERT(tail->previous == &elements[1]); + UNIT_TEST_ASSERT(elements[8].next == NULL); + UNIT_TEST_ASSERT(elements[8].previous == &elements[1]); + UNIT_TEST_ASSERT(tail->next == NULL); + + /* + * Find and remove element 5 + * NULL <-- 3 --> 4 --> 6 --> 2 --> 7 --> 1 --> 8 --> NULL + */ + dbl_list_remove(dll, &elements[5]); + head = dbl_list_head(dll); + tail = dbl_list_tail(dll); + + UNIT_TEST_ASSERT(dbl_list_is_empty(dll) == false); + UNIT_TEST_ASSERT(dbl_list_length(dll) == 7); + UNIT_TEST_ASSERT(head == &elements[3]); + UNIT_TEST_ASSERT(tail == &elements[8]); + UNIT_TEST_ASSERT(head->previous == NULL); + UNIT_TEST_ASSERT(elements[6].next == &elements[2]); + UNIT_TEST_ASSERT(elements[2].previous == &elements[6]); + UNIT_TEST_ASSERT(tail->next == NULL); + + /* + * Remove before tail + * NULL <-- 3 --> 4 --> 6 --> 2 --> 7 --> 8 --> NULL + */ + dbl_list_remove(dll, ((demo_struct_t *)dbl_list_tail(dll))->previous); + head = dbl_list_head(dll); + tail = dbl_list_tail(dll); + + UNIT_TEST_ASSERT(dbl_list_is_empty(dll) == false); + UNIT_TEST_ASSERT(dbl_list_length(dll) == 6); + UNIT_TEST_ASSERT(head == &elements[3]); + UNIT_TEST_ASSERT(tail == &elements[8]); + UNIT_TEST_ASSERT(head->previous == NULL); + UNIT_TEST_ASSERT(elements[7].next == tail); + UNIT_TEST_ASSERT(tail->previous == &elements[7]); + UNIT_TEST_ASSERT(tail->next == NULL); + + /* + * Remove after head + * NULL <-- 3 --> 6 --> 2 --> 7 --> 8 --> NULL + */ + dbl_list_remove(dll, ((demo_struct_t *)dbl_list_head(dll))->next); + head = dbl_list_head(dll); + tail = dbl_list_tail(dll); + + UNIT_TEST_ASSERT(dbl_list_is_empty(dll) == false); + UNIT_TEST_ASSERT(dbl_list_length(dll) == 5); + UNIT_TEST_ASSERT(head == &elements[3]); + UNIT_TEST_ASSERT(tail == &elements[8]); + UNIT_TEST_ASSERT(head->previous == NULL); + UNIT_TEST_ASSERT(head->next == &elements[6]); + UNIT_TEST_ASSERT(elements[6].previous == head); + UNIT_TEST_ASSERT(tail->next == NULL); + + /* + * Find element 2 and remove whatever is after it + * NULL <-- 3 --> 6 --> 2 --> 8 --> NULL + */ + dbl_list_remove(dll, elements[2].next); + head = dbl_list_head(dll); + tail = dbl_list_tail(dll); + + UNIT_TEST_ASSERT(dbl_list_is_empty(dll) == false); + UNIT_TEST_ASSERT(dbl_list_length(dll) == 4); + UNIT_TEST_ASSERT(head == &elements[3]); + UNIT_TEST_ASSERT(tail == &elements[8]); + UNIT_TEST_ASSERT(head->previous == NULL); + UNIT_TEST_ASSERT(elements[2].next == tail); + UNIT_TEST_ASSERT(tail->previous == &elements[2]); + UNIT_TEST_ASSERT(tail->next == NULL); + + /* + * Find element 2 and remove whatever is before it + * NULL <-- 3 --> 2 --> 8 --> NULL + */ + dbl_list_remove(dll, elements[2].previous); + head = dbl_list_head(dll); + tail = dbl_list_tail(dll); + + UNIT_TEST_ASSERT(dbl_list_is_empty(dll) == false); + UNIT_TEST_ASSERT(dbl_list_length(dll) == 3); + UNIT_TEST_ASSERT(head == &elements[3]); + UNIT_TEST_ASSERT(tail == &elements[8]); + UNIT_TEST_ASSERT(head->previous == NULL); + UNIT_TEST_ASSERT(head->next == &elements[2]); + UNIT_TEST_ASSERT(elements[2].previous == head); + UNIT_TEST_ASSERT(tail->next == NULL); + + /* + * Remove head + * NULL <-- 2 --> 8 --> NULL + */ + dbl_list_remove(dll, dbl_list_head(dll)); + head = dbl_list_head(dll); + tail = dbl_list_tail(dll); + + UNIT_TEST_ASSERT(dbl_list_is_empty(dll) == false); + UNIT_TEST_ASSERT(dbl_list_length(dll) == 2); + UNIT_TEST_ASSERT(head == &elements[2]); + UNIT_TEST_ASSERT(tail == &elements[8]); + UNIT_TEST_ASSERT(head->previous == NULL); + UNIT_TEST_ASSERT(head->next == tail); + UNIT_TEST_ASSERT(tail->previous == head); + UNIT_TEST_ASSERT(tail->next == NULL); + + /* + * Remove tail + * NULL <-- 8 --> NULL + */ + dbl_list_remove(dll, dbl_list_head(dll)); + head = dbl_list_head(dll); + tail = dbl_list_tail(dll); + + UNIT_TEST_ASSERT(dbl_list_is_empty(dll) == false); + UNIT_TEST_ASSERT(dbl_list_length(dll) == 1); + UNIT_TEST_ASSERT(head == &elements[8]); + UNIT_TEST_ASSERT(tail == &elements[8]); + UNIT_TEST_ASSERT(head->previous == NULL); + UNIT_TEST_ASSERT(head->next == NULL); + UNIT_TEST_ASSERT(tail->previous == NULL); + UNIT_TEST_ASSERT(tail->next == NULL); + + /* Remove the last element */ + dbl_list_remove(dll, dbl_list_head(dll)); + UNIT_TEST_ASSERT(dbl_list_is_empty(dll) == true); + UNIT_TEST_ASSERT(dbl_list_length(dll) == 0); + UNIT_TEST_ASSERT(dbl_list_head(dll) == NULL); + UNIT_TEST_ASSERT(dbl_list_tail(dll) == NULL); + + UNIT_TEST_END(); +} +/*---------------------------------------------------------------------------*/ +UNIT_TEST_REGISTER(test_cdll, "Circular, doubly-linked list"); +UNIT_TEST(test_cdll) +{ + demo_struct_t *head, *tail; + + CIRCULAR_LIST(cdll); + + UNIT_TEST_BEGIN(); + + memset(elements, 0, sizeof(elements)); + + /* Starts from empty */ + dbl_circ_list_init(cdll); + UNIT_TEST_ASSERT(dbl_circ_list_is_empty(cdll) == true); + UNIT_TEST_ASSERT(dbl_circ_list_length(cdll) == 0); + UNIT_TEST_ASSERT(dbl_circ_list_head(cdll) == NULL); + UNIT_TEST_ASSERT(dbl_circ_list_tail(cdll) == NULL); + + /* + * Add an item by adding to the head. + * Head and tail should be the same element and should point to itself in + * both directions + */ + dbl_circ_list_add_head(cdll, &elements[0]); + head = dbl_circ_list_head(cdll); + tail = dbl_circ_list_tail(cdll); + + UNIT_TEST_ASSERT(dbl_circ_list_is_empty(cdll) == false); + UNIT_TEST_ASSERT(dbl_circ_list_length(cdll) == 1); + UNIT_TEST_ASSERT(head == &elements[0]); + UNIT_TEST_ASSERT(tail == &elements[0]); + UNIT_TEST_ASSERT(head->previous == head); + UNIT_TEST_ASSERT(head->next == head); + UNIT_TEST_ASSERT(tail->previous == tail); + UNIT_TEST_ASSERT(tail->next == tail); + + /* + * Add an item by adding to the tail. + * (tail) <--> 0 <--> 1 <--> (head) + * Head should point to tail in both directions + */ + dbl_circ_list_add_tail(cdll, &elements[1]); + head = dbl_circ_list_head(cdll); + tail = dbl_circ_list_tail(cdll); + + UNIT_TEST_ASSERT(dbl_circ_list_is_empty(cdll) == false); + UNIT_TEST_ASSERT(dbl_circ_list_length(cdll) == 2); + UNIT_TEST_ASSERT(head == &elements[0]); + UNIT_TEST_ASSERT(tail == &elements[1]); + UNIT_TEST_ASSERT(head->next == tail); + UNIT_TEST_ASSERT(tail->previous == head); + UNIT_TEST_ASSERT(head->previous == tail); + UNIT_TEST_ASSERT(tail->next == head); + + /* + * Add before head. + * (tail) <--> 2 <--> 0 <--> 1 <--> (head) + */ + dbl_circ_list_add_before(cdll, dbl_circ_list_head(cdll), &elements[2]); + head = dbl_circ_list_head(cdll); + tail = dbl_circ_list_tail(cdll); + + UNIT_TEST_ASSERT(dbl_circ_list_is_empty(cdll) == false); + UNIT_TEST_ASSERT(dbl_circ_list_length(cdll) == 3); + UNIT_TEST_ASSERT(head == &elements[2]); + UNIT_TEST_ASSERT(tail == &elements[1]); + UNIT_TEST_ASSERT(head->previous == tail); + UNIT_TEST_ASSERT(tail->next == head); + UNIT_TEST_ASSERT(elements[2].previous == tail); + UNIT_TEST_ASSERT(elements[2].next == &elements[0]); + UNIT_TEST_ASSERT(elements[0].previous == head); + + /* + * Add after head. + * (tail) <--> 2 <--> 3 <--> 0 <--> 1 <--> (head) + */ + dbl_circ_list_add_after(cdll, dbl_circ_list_head(cdll), &elements[3]); + head = dbl_circ_list_head(cdll); + tail = dbl_circ_list_tail(cdll); + + UNIT_TEST_ASSERT(dbl_circ_list_is_empty(cdll) == false); + UNIT_TEST_ASSERT(dbl_circ_list_length(cdll) == 4); + UNIT_TEST_ASSERT(head == &elements[2]); + UNIT_TEST_ASSERT(tail == &elements[1]); + UNIT_TEST_ASSERT(head->previous == tail); + UNIT_TEST_ASSERT(tail->next == head); + UNIT_TEST_ASSERT(elements[3].previous == head); + UNIT_TEST_ASSERT(elements[3].next == &elements[0]); + UNIT_TEST_ASSERT(elements[0].previous == &elements[3]); + + /* + * Add at 3rd position by adding after 2nd + * (tail) <--> 2 <--> 3 <--> 4 <--> 0 <--> 1 <--> (head) + */ + dbl_circ_list_add_after(cdll, &elements[3], &elements[4]); + head = dbl_circ_list_head(cdll); + tail = dbl_circ_list_tail(cdll); + + UNIT_TEST_ASSERT(dbl_circ_list_is_empty(cdll) == false); + UNIT_TEST_ASSERT(dbl_circ_list_length(cdll) == 5); + UNIT_TEST_ASSERT(head == &elements[2]); + UNIT_TEST_ASSERT(tail == &elements[1]); + UNIT_TEST_ASSERT(head->previous == tail); + UNIT_TEST_ASSERT(tail->next == head); + UNIT_TEST_ASSERT(elements[3].next == &elements[4]); + UNIT_TEST_ASSERT(elements[4].previous == &elements[3]); + UNIT_TEST_ASSERT(elements[4].next == &elements[0]); + UNIT_TEST_ASSERT(elements[0].previous == &elements[4]); + + /* + * Add at 3rd position by adding before 3rd + * (tail) <--> 2 <--> 3 <--> 5 <--> 4 <--> 0 <--> 1 <--> (head) + */ + dbl_circ_list_add_before(cdll, &elements[4], &elements[5]); + head = dbl_circ_list_head(cdll); + tail = dbl_circ_list_tail(cdll); + + UNIT_TEST_ASSERT(dbl_circ_list_is_empty(cdll) == false); + UNIT_TEST_ASSERT(dbl_circ_list_length(cdll) == 6); + UNIT_TEST_ASSERT(head == &elements[2]); + UNIT_TEST_ASSERT(tail == &elements[1]); + UNIT_TEST_ASSERT(head->previous == tail); + UNIT_TEST_ASSERT(tail->next == head); + UNIT_TEST_ASSERT(elements[3].next == &elements[5]); + UNIT_TEST_ASSERT(elements[5].previous == &elements[3]); + UNIT_TEST_ASSERT(elements[5].next == &elements[4]); + UNIT_TEST_ASSERT(elements[4].previous == &elements[5]); + + /* + * Add before tail + * (tail) <--> 2 <--> 3 <--> 5 <--> 4 <--> 0 <--> 6 <--> 1 <--> (head) + */ + dbl_circ_list_add_before(cdll, dbl_circ_list_tail(cdll), &elements[6]); + head = dbl_circ_list_head(cdll); + tail = dbl_circ_list_tail(cdll); + + UNIT_TEST_ASSERT(dbl_circ_list_is_empty(cdll) == false); + UNIT_TEST_ASSERT(dbl_circ_list_length(cdll) == 7); + UNIT_TEST_ASSERT(head == &elements[2]); + UNIT_TEST_ASSERT(tail == &elements[1]); + UNIT_TEST_ASSERT(head->previous == tail); + UNIT_TEST_ASSERT(tail->next == head); + UNIT_TEST_ASSERT(elements[0].next == &elements[6]); + UNIT_TEST_ASSERT(elements[6].previous == &elements[0]); + UNIT_TEST_ASSERT(elements[6].next == &elements[1]); + UNIT_TEST_ASSERT(elements[1].previous == &elements[6]); + + /* + * Add after tail + * (tail) <--> 2 <--> 3 <--> 5 <--> 4 <--> 0 <--> 6 <--> 1 <--> 7 <--> (head) + */ + dbl_circ_list_add_after(cdll, dbl_circ_list_tail(cdll), &elements[7]); + head = dbl_circ_list_head(cdll); + tail = dbl_circ_list_tail(cdll); + + UNIT_TEST_ASSERT(dbl_circ_list_is_empty(cdll) == false); + UNIT_TEST_ASSERT(dbl_circ_list_length(cdll) == 8); + UNIT_TEST_ASSERT(head == &elements[2]); + UNIT_TEST_ASSERT(tail == &elements[7]); + UNIT_TEST_ASSERT(head->previous == tail); + UNIT_TEST_ASSERT(tail->next == head); + UNIT_TEST_ASSERT(elements[1].next == &elements[7]); + UNIT_TEST_ASSERT(elements[7].previous == &elements[1]); + UNIT_TEST_ASSERT(elements[7].next == &elements[2]); + UNIT_TEST_ASSERT(elements[2].previous == &elements[7]); + + /* + * Find and remove element 5 + * (tail) <--> 2 <--> 3 <--> 4 <--> 0 <--> 6 <--> 1 <--> 7 <--> (head) + */ + dbl_circ_list_remove(cdll, &elements[5]); + head = dbl_circ_list_head(cdll); + tail = dbl_circ_list_tail(cdll); + + UNIT_TEST_ASSERT(dbl_circ_list_is_empty(cdll) == false); + UNIT_TEST_ASSERT(dbl_circ_list_length(cdll) == 7); + UNIT_TEST_ASSERT(head == &elements[2]); + UNIT_TEST_ASSERT(tail == &elements[7]); + UNIT_TEST_ASSERT(head->previous == tail); + UNIT_TEST_ASSERT(tail->next == head); + UNIT_TEST_ASSERT(elements[3].next == &elements[4]); + UNIT_TEST_ASSERT(elements[4].previous == &elements[3]); + + /* + * Find element 4 and remove what's after it + * (tail) <--> 2 <--> 3 <--> 4 <--> 6 <--> 1 <--> 7 <--> (head) + */ + dbl_circ_list_remove(cdll, elements[4].next); + head = dbl_circ_list_head(cdll); + tail = dbl_circ_list_tail(cdll); + + UNIT_TEST_ASSERT(dbl_circ_list_is_empty(cdll) == false); + UNIT_TEST_ASSERT(dbl_circ_list_length(cdll) == 6); + UNIT_TEST_ASSERT(head == &elements[2]); + UNIT_TEST_ASSERT(tail == &elements[7]); + UNIT_TEST_ASSERT(head->previous == tail); + UNIT_TEST_ASSERT(tail->next == head); + UNIT_TEST_ASSERT(elements[4].next == &elements[6]); + UNIT_TEST_ASSERT(elements[6].previous == &elements[4]); + + /* + * Find element 4 and remove what's before it + * (tail) <--> 2 <--> 4 <--> 6 <--> 1 <--> 7 <--> (head) + */ + dbl_circ_list_remove(cdll, elements[4].previous); + head = dbl_circ_list_head(cdll); + tail = dbl_circ_list_tail(cdll); + + UNIT_TEST_ASSERT(dbl_circ_list_is_empty(cdll) == false); + UNIT_TEST_ASSERT(dbl_circ_list_length(cdll) == 5); + UNIT_TEST_ASSERT(head == &elements[2]); + UNIT_TEST_ASSERT(tail == &elements[7]); + UNIT_TEST_ASSERT(head->previous == tail); + UNIT_TEST_ASSERT(tail->next == head); + UNIT_TEST_ASSERT(elements[2].next == &elements[4]); + UNIT_TEST_ASSERT(elements[4].previous == &elements[2]); + + /* + * Remove before tail + * (tail) <--> 2 <--> 4 <--> 6 <--> 7 <--> (head) + */ + dbl_circ_list_remove(cdll, + ((demo_struct_t *)dbl_circ_list_tail(cdll))->previous); + head = dbl_circ_list_head(cdll); + tail = dbl_circ_list_tail(cdll); + + UNIT_TEST_ASSERT(dbl_circ_list_is_empty(cdll) == false); + UNIT_TEST_ASSERT(dbl_circ_list_length(cdll) == 4); + UNIT_TEST_ASSERT(head == &elements[2]); + UNIT_TEST_ASSERT(tail == &elements[7]); + UNIT_TEST_ASSERT(head->previous == tail); + UNIT_TEST_ASSERT(tail->next == head); + UNIT_TEST_ASSERT(elements[6].next == &elements[7]); + UNIT_TEST_ASSERT(elements[7].previous == &elements[6]); + + /* + * Remove after tail + * (tail) <--> 4 <--> 6 <--> 7 <--> (head) + */ + dbl_circ_list_remove(cdll, + ((demo_struct_t *)dbl_circ_list_tail(cdll))->next); + head = dbl_circ_list_head(cdll); + tail = dbl_circ_list_tail(cdll); + + UNIT_TEST_ASSERT(dbl_circ_list_is_empty(cdll) == false); + UNIT_TEST_ASSERT(dbl_circ_list_length(cdll) == 3); + UNIT_TEST_ASSERT(head == &elements[4]); + UNIT_TEST_ASSERT(tail == &elements[7]); + UNIT_TEST_ASSERT(head->previous == tail); + UNIT_TEST_ASSERT(tail->next == head); + UNIT_TEST_ASSERT(elements[7].next == &elements[4]); + UNIT_TEST_ASSERT(elements[4].previous == &elements[7]); + + /* + * Remove after head + * (tail) <--> 4 <--> 7 <--> (head) + */ + dbl_circ_list_remove(cdll, + ((demo_struct_t *)dbl_circ_list_head(cdll))->next); + head = dbl_circ_list_head(cdll); + tail = dbl_circ_list_tail(cdll); + + UNIT_TEST_ASSERT(dbl_circ_list_is_empty(cdll) == false); + UNIT_TEST_ASSERT(dbl_circ_list_length(cdll) == 2); + UNIT_TEST_ASSERT(head == &elements[4]); + UNIT_TEST_ASSERT(tail == &elements[7]); + UNIT_TEST_ASSERT(head->previous == tail); + UNIT_TEST_ASSERT(head->next == tail); + UNIT_TEST_ASSERT(tail->previous == head); + UNIT_TEST_ASSERT(tail->next == head); + + /* + * Remove before head + * (tail) <--> 4 <--> (head) + */ + dbl_circ_list_remove(cdll, + ((demo_struct_t *)dbl_circ_list_head(cdll))->previous); + head = dbl_circ_list_head(cdll); + tail = dbl_circ_list_tail(cdll); + + UNIT_TEST_ASSERT(dbl_circ_list_is_empty(cdll) == false); + UNIT_TEST_ASSERT(dbl_circ_list_length(cdll) == 1); + UNIT_TEST_ASSERT(head == &elements[4]); + UNIT_TEST_ASSERT(tail == &elements[4]); + UNIT_TEST_ASSERT(head->previous == tail); + UNIT_TEST_ASSERT(head->next == tail); + + /* Remove head */ + dbl_circ_list_remove(cdll, dbl_circ_list_head(cdll)); + dbl_circ_list_remove(cdll, dbl_circ_list_head(cdll)); + UNIT_TEST_ASSERT(dbl_circ_list_is_empty(cdll) == true); + UNIT_TEST_ASSERT(dbl_circ_list_length(cdll) == 0); + UNIT_TEST_ASSERT(dbl_circ_list_head(cdll) == NULL); + UNIT_TEST_ASSERT(dbl_circ_list_tail(cdll) == NULL); + + UNIT_TEST_END(); +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(data_structure_test_process, ev, data) +{ + PROCESS_BEGIN(); + + printf("Run unit-test\n"); + printf("---\n"); + + memset(elements, 0, sizeof(elements)); + + UNIT_TEST_RUN(test_stack); + UNIT_TEST_RUN(test_queue); + UNIT_TEST_RUN(test_csll); + UNIT_TEST_RUN(test_dll); + UNIT_TEST_RUN(test_cdll); + + printf("=check-me= DONE\n"); + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/tests/07-simulation-base/js/data-structures.js b/tests/07-simulation-base/js/data-structures.js new file mode 100644 index 000000000..5acb27f11 --- /dev/null +++ b/tests/07-simulation-base/js/data-structures.js @@ -0,0 +1,25 @@ +TIMEOUT(10000, log.testFailed()); + +var failed = false; + +while(true) { + YIELD(); + + log.log(time + " " + "node-" + id + " "+ msg + "\n"); + + if(msg.contains("=check-me=") == false) { + continue; + } + + if(msg.contains("FAILED")) { + failed = true; + } + + if(msg.contains("DONE")) { + break; + } +} +if(failed) { + log.testFailed(); +} +log.testOK(); diff --git a/tests/08-native-runs/01-test-data-structures.sh b/tests/08-native-runs/01-test-data-structures.sh new file mode 100755 index 000000000..790235638 --- /dev/null +++ b/tests/08-native-runs/01-test-data-structures.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# Contiki directory +CONTIKI=$1 + +# Example code directory +CODE_DIR=$CONTIKI/tests/07-simulation-base/code-data-structures/ +CODE=test-data-structures + +# Starting Contiki-NG native node +echo "Starting native node" +make -C $CODE_DIR TARGET=native > make.log 2> make.err +$CODE_DIR/$CODE.native > $CODE.log 2> $CODE.err & +CPID=$! +sleep 2 + +echo "Closing native node" +sleep 2 +pgrep $CODE | xargs kill -9 + +if grep -q "=check-me= FAILED" $CODE.log ; then + echo "==== make.log ====" ; cat make.log; + echo "==== make.err ====" ; cat make.err; + echo "==== $CODE.log ====" ; cat $CODE.log; + echo "==== $CODE.err ====" ; cat $CODE.err; + + printf "%-32s TEST FAIL\n" "$CODE" | tee $CODE.testlog; +else + cp $CODE.log $CODE.testlog + printf "%-32s TEST OK\n" "$CODE" | tee $CODE.testlog; +fi + +rm make.log +rm make.err +rm $CODE.log +rm $CODE.err + +# We do not want Make to stop -> Return 0 +# The Makefile will check if a log contains FAIL at the end +exit 0 diff --git a/tests/08-native-runs/Makefile b/tests/08-native-runs/Makefile new file mode 100644 index 000000000..c46e5271d --- /dev/null +++ b/tests/08-native-runs/Makefile @@ -0,0 +1 @@ +include ../Makefile.script-test