diff --git a/Makefile.common b/Makefile.common index 25133c0e14..2c555ad17a 100644 --- a/Makefile.common +++ b/Makefile.common @@ -275,6 +275,10 @@ OBJ += \ $(LIBRETRO_COMM_DIR)/memmap/memalign.o \ $(LIBRETRO_COMM_DIR)/file/nbio/nbio_stdio.o +OBJ += \ + $(LIBRETRO_COMM_DIR)/lists/linked_list.o \ + $(LIBRETRO_COMM_DIR)/queues/generic_queue.o + ifneq ($(findstring Linux,$(OS)),) OBJ += $(LIBRETRO_COMM_DIR)/file/nbio/nbio_linux.o endif diff --git a/config.features.h b/config.features.h index 9f9bec8df0..a5b605d7d0 100644 --- a/config.features.h +++ b/config.features.h @@ -350,6 +350,12 @@ #define SUPPORTS_CORETEXT false #endif +#ifdef HAVE_CHECK +#define SUPPORTS_CHECK true +#else +#define SUPPORTS_CHECK false +#endif + #if !defined(_WIN32) && !defined(GLOBAL_CONFIG_DIR) #if defined(__HAIKU__) #define GLOBAL_CONFIG_DIR "/system/settings" diff --git a/libretro-common/Makefile.test b/libretro-common/Makefile.test new file mode 100644 index 0000000000..04b87db8bb --- /dev/null +++ b/libretro-common/Makefile.test @@ -0,0 +1,35 @@ +include ../config.mk + +OBJDIR = ../obj-unix + +TEST_UNIT_CFLAGS = $(CFLAGS) -Iinclude $(LDFLAGS) $(LIBCHECK_CFLAGS) -Werror -Wdeclaration-after-statement -fsanitize=address -fsanitize=undefined + +TEST_GENERIC_QUEUE = test_generic_queue +TEST_GENERIC_QUEUE_SRC = test/queues/test_generic_queue.c queues/generic_queue.c +TEST_GENERIC_QUEUE_FLAGS = $(CFLAGS) $(LDFLAGS) $(LIBCHECK_CFLAGS) +TEST_GENERIC_QUEUE_LIBS = $(LIBCHECK_LIBS) + +$(TEST_GENERIC_QUEUE): $(TEST_GENERIC_QUEUE_SRC) + $(CC) $(TEST_UNIT_CFLAGS) -o $(OBJDIR)/test/$@ $(TEST_GENERIC_QUEUE_SRC) $(TEST_GENERIC_QUEUE_LIBS) + +TEST_LINKED_LIST = test_linked_list +TEST_LINKED_LIST_SRC = test/lists/test_linked_list.c lists/linked_list.c +TEST_LINKED_LIST_LIBS = $(LIBCHECK_LIBS) + +$(TEST_LINKED_LIST): $(TEST_LINKED_LIST_SRC) + $(CC) $(TEST_UNIT_CFLAGS) -o $(OBJDIR)/test/$@ $(TEST_LINKED_LIST_SRC) $(TEST_LINKED_LIST_LIBS) + +TESTS = $(TEST_GENERIC_QUEUE) $(TEST_LINKED_LIST) + +ifeq ($(HAVE_LIBCHECK),1) +test-dir: + @mkdir -p $(OBJDIR)/test + +test: test-dir $(TESTS) + @for t in $(TESTS); do \ + LD_LIBRARY_PATH='$(LD_PATH)' $(OBJDIR)/test/$$t; \ + done +else +test: + @echo "Cannot run unit tests. libcheck was not found." +endif diff --git a/libretro-common/include/lists/linked_list.h b/libretro-common/include/lists/linked_list.h new file mode 100644 index 0000000000..fc93ba5f77 --- /dev/null +++ b/libretro-common/include/lists/linked_list.h @@ -0,0 +1,298 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (linked_list.h). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __LIBRETRO_SDK_LINKED_LIST_H +#define __LIBRETRO_SDK_LINKED_LIST_H + +#include + +#include +#include + +RETRO_BEGIN_DECLS + +/** + * Represents a linked list. Contains any number of elements. + */ +typedef struct linked_list linked_list_t; + +/** + * Represents an iterator for iterating over a linked list. The iterator can + * go through the linked list forwards or backwards. + */ +typedef struct linked_list_iterator linked_list_iterator_t; + +/** + * Creates a new linked list with no elements. + * + * @return New linked list + */ +linked_list_t *linked_list_new(void); + +/** + * @brief frees the memory used by the linked list + * + * Frees all of the memory used by this linked list. The values of all + * remaining elements are freed using the "free_value" function. Does + * nothing if "list" is NULL. + * + * @param list linked list to free + * @param free_value function to use to free remaining values + */ +void linked_list_free(linked_list_t *list, void (*free_value)(void *value)); + +/** + * @brief adds an element to the linked list + * + * Add a new element to the end of this linked list. Does nothing if + * "list" is NULL. + * + * @param list list to add the element to + * @param value new value to add to the linked list + */ +void linked_list_add(linked_list_t *list, void *value); + +/** + * @brief inserts a value into the linked list + * + * Inserts a value into the linked list at the specified index. Does + * nothing if "list" is NULL. + * + * @param list list to insert the value into + * @param index index where the value should be inserted at (can be equal to list size) + * @param value value to insert into the linked list + */ +void linked_list_insert(linked_list_t *list, size_t index, void *value); + +/** + * @brief Get the value in the linked list at the provided index. + * + * Return the value vstored in the linked list at the provided index. Does + * nothing if "list" is NULL. + * + * @param list list to get the value from + * @param index index of the value to return + * @return value in the list at the provided index + */ +void *linked_list_get(linked_list_t *list, size_t index); + +/** + * @brief Get the first value that is matched by the provided function + * + * Return the first value that the function matches. The matches function + * parameters are value from the linked list and the provided state. + * + * @param list list to get the value from + * @param matches function to test the values with + * @param usrptr user data to pass to the matches function + * @return first value that matches otherwise NULL + */ +void *linked_list_get_first_matching(linked_list_t *list, bool (*matches)(void *item, void *usrptr), void *usrptr); + +/** + * @brief Get the last value that is matched by the provided function + * + * Return the last value that the function matches. The matches function + * parameters are value from the linked list and the provided state. + * + * @param list list to get the value from + * @param matches function to test the values with + * @param usrptr user data to pass to the matches function + * @return last value that matches otherwise NULL + */ +void *linked_list_get_last_matching(linked_list_t *list, bool (*matches)(void *item, void *usrptr), void *usrptr); + +/** + * @brief Remove the element at the provided index + * + * Removes the element of the linked list at the provided index. + * + * @param list linked list to remove the element from + * @param index index of the element to remove + * @return value of the element that was removed, NULL if list is NULL or + * index is invalid + */ +void *linked_list_remove_at(linked_list_t *list, size_t index); + +/** + * @brief Remove the first element with the provided value + * + * Removes the first element with a value equal to the provided value. + * Does nothing if "list" is NULL. + * + * @param list linked list to remove the element from + * @param value value of the element to remove + * @return value if a matching element was removed + */ +void *linked_list_remove_first(linked_list_t *list, void *value); + +/** + * @brief Remove the last element with the provided value + * + * Removes the last element with a value equal to the provided value. + * Does nothing if "list" is NULL. + * + * @param list linked list to remove the element from + * @param value value of the element to remove + * @return value if a matching element was removed + */ +void *linked_list_remove_last(linked_list_t *list, void *value); + +/** + * @brief Remove all elements with the provided value + * + * Removes all elements with a value equal to the provided value. + * Does nothing if "list" is NULL. + * + * @param list linked list to remove the elements from + * @param value value of the elements to remove + * @return value if any matching element(s) where removed + */ +void *linked_list_remove_all(linked_list_t *list, void *value); + +/** + * @brief Remove the first matching element + * + * Removes the first matching element from the linked list. The "matches" function + * is used to test for matching element values. Does nothing if "list" is NULL. + * + * @param list linked list to remove the element from + * @param matches function to use for testing element values for a match + * @return value if a matching element was removed + */ +void *linked_list_remove_first_matching(linked_list_t *list, bool (*matches)(void *value)); + +/** + * @brief Remove the last matching element + * + * Removes the last matching element from the linked list. The "matches" function + * is used to test for matching element values. + * + * @param list linked list to remove the element from + * @param matches function to use for testing element value for a match + * @return value if a matching element was removed + */ +void *linked_list_remove_last_matching(linked_list_t *list, bool (*matches)(void *value)); + +/** + * @brief Remove all matching elements + * + * Removes all matching elements from the linked list. The "matches" function + * is used to test for matching element values. Does nothing if "list" is NULL. + * + * @param list linked list to remove the elements from + * @param matches function to use for testing element values for a match + */ +void linked_list_remove_all_matching(linked_list_t *list, bool (*matches)(void *value)); + +/** + * @brief Replace the value of the element at the provided index + * + * Replaces the value of the element at the provided index. The linked list must + * contain an element at the index. + * + * @param list linked list to replace the value in + * @param index index of the element to replace the value of + * @param value new value for the selected element + * @return whether an element was updated + */ +bool linked_list_set_at(linked_list_t *list, size_t index, void *value); + +/** + * @brief Get the size of the linked list + * + * Returns the number of elements in the linked list. + * + * @param linked list to get the size of + * @return number of elements in the linked list, 0 if linked list is NULL + */ +size_t linked_list_size(linked_list_t *list); + +/** + * @brief Get an iterator for the linked list + * + * Returns a new iterator for the linked list. Can be either a forward or backward + * iterator. + * + * @param list linked list to iterate over + * @param forward true for a forward iterator, false for backwards + * @return new iterator for the linked list in the specified direction, NULL if + * "list" is NULL + */ +linked_list_iterator_t *linked_list_iterator(linked_list_t *list, bool forward); + +/** + * @brief Move to the next element in the linked list + * + * Moves the iterator to the next element in the linked list. The direction is + * specified when retrieving a new iterator. + * + * @param iterator iterator for the current element + * @return iterator for the next element, NULL if iterator is NULL or "iterator" + * is at the last element + */ +linked_list_iterator_t *linked_list_iterator_next(linked_list_iterator_t *iterator); + +/** + * @brief Get the value of the element for the iterator + * + * Returns the value of the element that the iterator is at. + * + * @param iterator iterator for the current element + * @return value of the element for the iterator + */ +void *linked_list_iterator_value(linked_list_iterator_t *iterator); + +/** + * @brief Remove the element that the iterator is at + * + * Removes the element that the iterator is at. The iterator is updated to the + * next element. + * + * @param iterator iterator for the current element + * @return updated iterator or NULL if the last element was removed + */ +linked_list_iterator_t *linked_list_iterator_remove(linked_list_iterator_t *iterator); + +/** + * @brief Release the memory for the iterator + * + * Frees the memory for the provided iterator. Does nothing if "iterator" is NULL. + * + * @param iterator iterator to free + */ +void linked_list_iterator_free(linked_list_iterator_t *iterator); + +/** + * @brief Apply the provided function to all values in the linked list + * + * Apply the provied function to all values in the linked list. The values are applied + * in the forward direction. Does nothing if "list" is NULL. + * + * @param list linked list to apply the function to + * @param fn function to apply to all elements + */ +void linked_list_foreach(linked_list_t *list, void (*fn)(size_t index, void *value)); + +RETRO_END_DECLS + +#endif diff --git a/libretro-common/include/queues/generic_queue.h b/libretro-common/include/queues/generic_queue.h new file mode 100644 index 0000000000..698d7b65ac --- /dev/null +++ b/libretro-common/include/queues/generic_queue.h @@ -0,0 +1,208 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (generic_queue.h). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __LIBRETRO_SDK_GENERIC_QUEUE_H +#define __LIBRETRO_SDK_GENERIC_QUEUE_H + +#include + +#include +#include + +RETRO_BEGIN_DECLS + +/** + * Represents a generic queue. Can contain any number of elements. Each element contains + * a value of type "void *". Can be used as a FIFO or LIFO queue. + */ +typedef struct generic_queue generic_queue_t; + +/** + * Represents an iterator for iterating over a queue. + */ +typedef struct generic_queue_iterator generic_queue_iterator_t; + +/** + * Creates a new queue with no elements. + * + * @return New queue + */ +generic_queue_t *generic_queue_new(void); + +/** + * @brief frees the memory used by the queue + * + * Frees all of the memory used by this queue. The values of all + * remaining elements are freed using the "free_value" function. Does + * nothing if "queue" is NULL. + * + * @param queue queue to free + * @param free_value function to use to free remaining values + */ +void generic_queue_free(generic_queue_t *queue, void (*free_value)(void *value)); + +/** + * @brief Push a new value onto the queue + * + * Pushes a new value onto the end of the queue. Does nothing if "queue" + * is NULL. + * + * @param queue queue to the push the value onto + * @param value value to push onto the queue + */ +void generic_queue_push(generic_queue_t *queue, void *value); + +/** + * @brief Remove the last value from the queue + * + * Removes the last element from the queue. Does nothing if the queue is + * NULL. + * + * @param queue queue to get the value from + * @return value of the last element, NULL if queue is empty or NULL + */ +void *generic_queue_pop(generic_queue_t *queue); + +/** + * @brief Get the last value from the queue + * + * Returns the value of the last element in the queue. Returns NULL if the + * queue is NULL or empty. + * + * @param queue queue to get the last value from + * @return value of the last element or NULL + */ +void *generic_queue_peek(generic_queue_t *queue); + +/** + * @brief Get the first value from the queue + * + * Returns the value of the first element in the queue. Returns NULL if the + * queue is NULL or empty. + * + * @param queue queue to get the first value from + * @return value of the first element or NULL + */ +void *generic_queue_peek_first(generic_queue_t *queue); + +/** + * @brief Push a new value onto the front of the queue + * + * Pushes a new value onto the front of the queue. Does nothing if "queue" + * is NULL. + * + * @param queue queue to the push the value onto + * @param value value to push onto the queue + */ +void generic_queue_shift(generic_queue_t *queue, void *value); + +/** + * @brief Remove the first value from the queue + * + * Removes the first element from the queue. Does nothing if the queue is + * NULL. + * + * @param queue queue to get the value from + * @return value of the last element, NULL if queue is empty or NULL + */ +void *generic_queue_unshift(generic_queue_t *queue); + +/** + * @brief Get the size of the queue + * + * Returns the number of elements in the queue. + * + * @param queue queue to get the size of + * @return number of elements in the queue, 0 if queue is NULL + */ +size_t generic_queue_length(generic_queue_t *queue); + +/** + * @brief Remove the first element in the queue with the provided value + * + * Removes the first element with a value matching the provided value. Does + * nothing if queue is NULL. + * + * @param queue queue to remove the element from + * @param value value to look for in the queue + * @return the value of the element removed, NULL if no element was removed + */ +void *generic_queue_remove(generic_queue_t *queue, void *value); + +/** + * @brief Get an iterator for the queue + * + * Returns a new iterator for the queue. Can be either a forward or backward + * iterator. + * + * @param queue queue to iterate over + * @param forward true for a forward iterator, false for backwards + * @return new iterator for the queue in the specified direction, NULL if + * "queue" is NULL + */ +generic_queue_iterator_t *generic_queue_iterator(generic_queue_t *queue, bool forward); + +/** + * @brief Move to the next element in the queue + * + * Moves the iterator to the next element in the queue. The direction is + * specified when retrieving a new iterator. + * + * @param iterator iterator for the current element + * @return iterator for the next element, NULL if iterator is NULL or "iterator" + * is at the last element + */ +generic_queue_iterator_t *generic_queue_iterator_next(generic_queue_iterator_t *iterator); + +/** + * @brief Get the value of the element for the iterator + * + * Returns the value of the element that the iterator is at. + * + * @param iterator iterator for the current element + * @return value of the element for the iterator + */ +void *generic_queue_iterator_value(generic_queue_iterator_t *iterator); + +/** + * @brief Remove the element that the iterator is at + * + * Removes the element that the iterator is at. The iterator is updated to the + * next element. + * + * @param iterator iterator for the current element + * @return updated iterator or NULL if the last element was removed + */ +generic_queue_iterator_t *generic_queue_iterator_remove(generic_queue_iterator_t *iterator); + +/** + * @brief Release the memory for the iterator + * + * Frees the memory for the provided iterator. Does nothing if "iterator" is NULL. + * + * @param iterator iterator to free + */ +void generic_queue_iterator_free(generic_queue_iterator_t *iterator); + +RETRO_END_DECLS + +#endif diff --git a/libretro-common/lists/linked_list.c b/libretro-common/lists/linked_list.c new file mode 100644 index 0000000000..fce754d5eb --- /dev/null +++ b/libretro-common/lists/linked_list.c @@ -0,0 +1,479 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (linked_list.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include + +struct linked_list_item_t +{ + void *value; + struct linked_list_item_t *previous; + struct linked_list_item_t *next; +}; + +struct linked_list +{ + struct linked_list_item_t *first_item; + struct linked_list_item_t *last_item; + size_t length; +}; + +struct linked_list_iterator +{ + linked_list_t *list; + struct linked_list_item_t *item; + bool forward; +}; + +linked_list_t *linked_list_new(void) +{ + linked_list_t *list; + + list = (linked_list_t *)calloc(sizeof(linked_list_t), 1); + return list; +} + +void linked_list_free(linked_list_t *list, void (*free_value)(void *value)) +{ + if (!list) + { + return; + } + + while (list->first_item) + { + struct linked_list_item_t *next; + + next = list->first_item->next; + if (free_value) + free_value(list->first_item->value); + free(list->first_item); + + list->first_item = next; + } + + free (list); +} + +void linked_list_add(linked_list_t *list, void *value) +{ + struct linked_list_item_t *new_item; + + if (!list) + return; + + new_item = (struct linked_list_item_t *)malloc(sizeof(struct linked_list_item_t)); + new_item->value = value; + new_item->previous = list->last_item; + new_item->next = NULL; + + if (list->length == 0) + list->first_item = new_item; + else + list->last_item->next = new_item; + + list->last_item = new_item; + list->length++; +} + +void linked_list_insert(linked_list_t *list, size_t index, void *value) +{ + size_t i; + struct linked_list_item_t *previous_item; + struct linked_list_item_t *next_item; + struct linked_list_item_t *new_item; + + if (!list || index > list->length) + return; + + previous_item = NULL; + next_item = list->first_item; + for (i = 1; i <= index; i++) + { + previous_item = next_item; + next_item = next_item->next; + } + + new_item = (struct linked_list_item_t *)malloc(sizeof(struct linked_list_item_t)); + new_item->value = value; + + if (previous_item) + previous_item->next = new_item; + else + list->first_item = new_item; + new_item->previous = previous_item; + + if (next_item) + next_item->previous = new_item; + else + list->last_item = new_item; + new_item->next = next_item; + + list->length++; +} + +void *linked_list_get(linked_list_t *list, size_t index) +{ + size_t i; + struct linked_list_item_t *item; + + if (!list) + return NULL; + + if (index >= list->length) + return NULL; + + item = list->first_item; + for (i = 1; i <= index; i++) + item = item->next; + + return item->value; +} + +void *linked_list_get_first_matching(linked_list_t *list, bool (*matches)(void *item, void *usrptr), void *usrptr) +{ + struct linked_list_item_t *item; + + if (!list || !matches) + return NULL; + + for (item = list->first_item; item; item = item->next) + { + if (matches(item->value, usrptr)) + return item->value; + } + + return NULL; +} + +void *linked_list_get_last_matching(linked_list_t *list, bool (*matches)(void *item, void *usrptr), void *usrptr) +{ + struct linked_list_item_t *item; + + if (!list || !matches) + return NULL; + + for (item = list->last_item; item; item = item->previous) + { + if (matches(item->value, usrptr)) + return item->value; + } + + return NULL; +} + +static void _linked_list_remove_item(linked_list_t *list, struct linked_list_item_t *item) +{ + struct linked_list_item_t *previous_item; + struct linked_list_item_t *next_item; + + previous_item = item->previous; + next_item = item->next; + free(item); + list->length--; + + if (previous_item) + previous_item->next = next_item; + else + list->first_item = next_item; + + if (next_item) + next_item->previous = previous_item; + else + list->last_item = previous_item; +} + +void *linked_list_remove_at(linked_list_t *list, size_t index) +{ + size_t i = 0; + struct linked_list_item_t *item; + void *value; + + if (!list || list->length == 0 || index >= list->length) + return NULL; + + item = list->first_item; + for (i = 1; i <= index; i++) + item = item->next; + + value = item->value; + _linked_list_remove_item(list, item); + return value; +} + +void *linked_list_remove_first(linked_list_t *list, void *value) +{ + struct linked_list_item_t *item; + + if (!list) + return NULL; + + for (item = list->first_item; item; item = item->next) + { + if (item->value == value) + break; + } + + if (item) + { + _linked_list_remove_item(list, item); + return value; + } + + return NULL; +} + +void *linked_list_remove_last(linked_list_t *list, void *value) +{ + struct linked_list_item_t *item; + + if (!list) + return NULL; + + for (item = list->last_item; item; item = item->previous) + { + if (item->value == value) + break; + } + + if (item) + { + _linked_list_remove_item(list, item); + return value; + } + + return NULL; +} + +void *linked_list_remove_all(linked_list_t *list, void *value) +{ + struct linked_list_item_t *item; + bool found = false; + + if (!list) + return NULL; + + for (item = list->first_item; item;) + { + if (item->value == value) + { + struct linked_list_item_t *next_item; + + next_item = item->next; + _linked_list_remove_item(list, item); + found = true; + item = next_item; + } else + { + item = item->next; + } + } + + return found ? value : NULL; +} + +void *linked_list_remove_first_matching(linked_list_t *list, bool (*matches)(void *value)) +{ + struct linked_list_item_t *item; + + if (!list) + return NULL; + + for (item = list->first_item; item; item = item->next) + { + if (matches(item->value)) + break; + } + + if (item) + { + void *value; + + value = item->value; + _linked_list_remove_item(list, item); + return value; + } + + return NULL; +} + +void *linked_list_remove_last_matching(linked_list_t *list, bool (*matches)(void *value)) +{ + struct linked_list_item_t *item; + + if (!list) + return NULL; + + for (item = list->last_item; item; item = item->previous) + { + if (matches(item->value)) + break; + } + + if (item) + { + void *value; + + value = item->value; + _linked_list_remove_item(list, item); + return value; + } + + return NULL; +} + +void linked_list_remove_all_matching(linked_list_t *list, bool (*matches)(void *value)) +{ + struct linked_list_item_t *item; + + if (!list) + return; + + for (item = list->first_item; item;) + { + if (matches(item->value)) + { + struct linked_list_item_t *next_item; + + next_item = item->next; + _linked_list_remove_item(list, item); + item = next_item; + } else + { + item = item->next; + } + } +} + +bool linked_list_set_at(linked_list_t *list, size_t index, void *value) +{ + struct linked_list_item_t *item; + size_t i; + + if (!list || list->length == 0 || index >= list->length) + return false; + + item = list->first_item; + for (i = 1; i <= index; i++) + item = item->next; + + if (item) + { + item->value = value; + return true; + } + + return false; +} + +size_t linked_list_size(linked_list_t *list) +{ + if (list) + return list->length; + + return 0; +} + +linked_list_iterator_t *linked_list_iterator(linked_list_t *list, bool forward) +{ + linked_list_iterator_t *iterator; + + if (!list || !list->first_item) + return NULL; + + iterator = (linked_list_iterator_t *)malloc(sizeof(linked_list_iterator_t)); + iterator->list = list; + iterator->item = forward ? list->first_item : list->last_item; + iterator->forward = forward; + + return iterator; +} + +linked_list_iterator_t *linked_list_iterator_next(linked_list_iterator_t *iterator) +{ + struct linked_list_item_t *item; + + if (!iterator) + return NULL; + + item = iterator->forward ? iterator->item->next : iterator->item->previous; + if (item) + { + iterator->item = item; + return iterator; + } else + { + free(iterator); + return NULL; + } +} + +void *linked_list_iterator_value(linked_list_iterator_t *iterator) +{ + if (iterator) + return iterator->item->value; + + return NULL; +} + +linked_list_iterator_t *linked_list_iterator_remove(linked_list_iterator_t *iterator) +{ + struct linked_list_item_t *next_item; + + if (!iterator) + return NULL; + + next_item = iterator->forward ? iterator->item->next : iterator->item->previous; + _linked_list_remove_item(iterator->list, iterator->item); + + if (next_item) + { + iterator->item = next_item; + return iterator; + } else + { + free(iterator); + return NULL; + } +} + +void linked_list_iterator_free(linked_list_iterator_t *iterator) +{ + if (iterator) + free(iterator); +} + +void linked_list_foreach(linked_list_t *list, void (*fn)(size_t index, void *value)) +{ + size_t i; + struct linked_list_item_t *item; + + if (!list || !fn) + return; + + i = 0; + for (item = list->first_item; item; item = item->next) + fn(i++, item->value); +} diff --git a/libretro-common/queues/generic_queue.c b/libretro-common/queues/generic_queue.c new file mode 100644 index 0000000000..f8e5c3d39f --- /dev/null +++ b/libretro-common/queues/generic_queue.c @@ -0,0 +1,303 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (generic_queue.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include + +struct generic_queue_item_t +{ + void *value; + struct generic_queue_item_t *previous; + struct generic_queue_item_t *next; +}; + +struct generic_queue +{ + struct generic_queue_item_t *first_item; + struct generic_queue_item_t *last_item; + size_t length; +}; + +struct generic_queue_iterator +{ + generic_queue_t *queue; + struct generic_queue_item_t *item; + bool forward; +}; + +generic_queue_t *generic_queue_new(void) +{ + return (generic_queue_t *)calloc(1, sizeof(generic_queue_t)); +} + +void generic_queue_free(generic_queue_t *queue, void (*free_value)(void *value)) +{ + struct generic_queue_item_t *next_item; + + if (!queue) + return; + + while (queue->first_item) + { + if (free_value) + free_value(queue->first_item->value); + + next_item = queue->first_item->next; + free(queue->first_item); + queue->first_item = next_item; + } + + free(queue); +} + +void generic_queue_push(generic_queue_t *queue, void *value) +{ + struct generic_queue_item_t *new_item; + + new_item = (struct generic_queue_item_t *)calloc(1, sizeof(struct generic_queue_item_t)); + new_item->value = value; + new_item->previous = queue->last_item; + new_item->next = NULL; + + queue->last_item = new_item; + queue->length++; + + if (!queue->first_item) + queue->first_item = new_item; + else + new_item->previous->next = new_item; +} + +void *generic_queue_pop(generic_queue_t *queue) +{ + void *value; + struct generic_queue_item_t *item; + + if (!queue || !queue->last_item) + return NULL; + + item = queue->last_item; + queue->last_item = queue->last_item->previous; + queue->length--; + + if (queue->length == 0) + queue->first_item = NULL; + + value = item->value; + free(item); + + return value; +} + +void *generic_queue_peek(generic_queue_t *queue) +{ + if (!queue || !queue->last_item) + return NULL; + + return queue->last_item->value; +} + +void *generic_queue_peek_first(generic_queue_t *queue) +{ + if (!queue || !queue->first_item) + return NULL; + + return queue->first_item->value; +} + +void generic_queue_shift(generic_queue_t *queue, void *value) +{ + struct generic_queue_item_t *new_item; + + if (!queue) + return; + + new_item = (struct generic_queue_item_t *)calloc(1, sizeof(struct generic_queue_item_t)); + new_item->value = value; + new_item->previous = NULL; + new_item->next = queue->first_item; + + queue->first_item = new_item; + queue->length++; + + if (!queue->last_item) + queue->last_item = new_item; + else + new_item->next->previous = new_item; +} + +void *generic_queue_unshift(generic_queue_t *queue) +{ + void *value; + struct generic_queue_item_t *item; + + if (!queue || !queue->first_item) + return NULL; + + item = queue->first_item; + queue->first_item = queue->first_item->next; + queue->length--; + + if (queue->length == 0) + queue->last_item = NULL; + + value = item->value; + free(item); + + return value; +} + +size_t generic_queue_length(generic_queue_t *queue) +{ + if (queue) + return queue->length; + + return 0; +} + +void *generic_queue_remove(generic_queue_t *queue, void *value) +{ + struct generic_queue_item_t *item; + + if (!queue) + return NULL; + + for (item = queue->first_item; item; item = item->next) + { + if (item->value == value) + { + if (item->previous) + item->previous->next = item->next; + else + queue->first_item = item->next; + + if (item->next) + item->next->previous = item->previous; + else + queue->last_item = item->previous; + + free(item); + queue->length--; + + return value; + } + } + + return NULL; +} + +generic_queue_iterator_t *generic_queue_iterator(generic_queue_t *queue, bool forward) +{ + if (queue && queue->first_item) + { + generic_queue_iterator_t *iterator; + + iterator = (generic_queue_iterator_t *)malloc(sizeof(generic_queue_iterator_t)); + iterator->queue = queue; + iterator->item = forward ? queue->first_item : queue->last_item; + iterator->forward = forward; + + return iterator; + } + + return NULL; +} + +generic_queue_iterator_t *generic_queue_iterator_next(generic_queue_iterator_t *iterator) +{ + if (iterator) + { + struct generic_queue_item_t *item; + + item = iterator->forward ? iterator->item->next : iterator->item->previous; + if (item) + { + iterator->item = item; + return iterator; + } else + { + free(iterator); + return NULL; + } + } + + return NULL; +} + +void *generic_queue_iterator_value(generic_queue_iterator_t *iterator) +{ + if (iterator) + return iterator->item->value; + + return NULL; +} + +generic_queue_iterator_t *generic_queue_iterator_remove(generic_queue_iterator_t *iterator) +{ + struct generic_queue_item_t *item; + + if (!iterator) + return NULL; + + item = iterator->forward ? iterator->queue->first_item : iterator->queue->last_item; + while (item) + { + if (iterator->item == item) + { + if (iterator->queue->first_item == item) + iterator->queue->first_item = item->next; + else + item->previous->next = item->next; + + if (iterator->queue->last_item == item) + iterator->queue->last_item = item->previous; + else + item->next->previous = item->previous; + + iterator->queue->length--; + + iterator->item = iterator->forward ? item->next : item->previous; + free(item); + if (iterator->item) + { + return iterator; + } else + { + free(iterator); + return NULL; + } + } + + item = iterator->forward ? item->next : item->previous; + } + + return iterator; +} + +void generic_queue_iterator_free(generic_queue_iterator_t *iterator) +{ + if (iterator) + free(iterator); +} diff --git a/libretro-common/test/lists/test_linked_list.c b/libretro-common/test/lists/test_linked_list.c new file mode 100644 index 0000000000..7f86654821 --- /dev/null +++ b/libretro-common/test/lists/test_linked_list.c @@ -0,0 +1,1299 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (test_linked_list.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include + +#define SUITE_NAME "Linked List" + +static char *_value_1 = "value1"; +static char *_value_2 = "value2"; +static char *_value_3 = "value3"; + +START_TEST (test_linked_list_create) +{ + linked_list_t *list = linked_list_new(); + ck_assert_ptr_nonnull(list); + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_free) +{ + linked_list_t *queue = linked_list_new(); + linked_list_free(queue, NULL); + linked_list_free(NULL, NULL); +} +END_TEST + +static int _free_alloced_value_count; +static void _free_alloced_value(void *value) +{ + _free_alloced_value_count++; + free(value); +} + +START_TEST (test_linked_list_free_with_fn) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, malloc(1)); + linked_list_add(list, malloc(1)); + linked_list_add(list, malloc(1)); + + _free_alloced_value_count = 0; + linked_list_free(list, &_free_alloced_value); + + ck_assert_int_eq(3, _free_alloced_value_count); +} +END_TEST + +static void _verify_list(linked_list_t *list, int size, ...) +{ + va_list values_list; + void **values; + int i; + linked_list_iterator_t *iterator; + + values = (void **)malloc(size * sizeof(void *)); + + ck_assert_int_eq(linked_list_size(list), size); + + va_start(values_list, size); + for (i = 0; i < size; i++) + { + values[i] = va_arg(values_list, void *); + ck_assert_ptr_eq(values[i], linked_list_get(list, i)); + } + va_end(values_list); + + iterator = linked_list_iterator(list, true); + for (i = 0; i < size; i++) + { + ck_assert_ptr_nonnull(iterator); + ck_assert_ptr_eq(values[i], linked_list_iterator_value(iterator)); + iterator = linked_list_iterator_next(iterator); + } + ck_assert_ptr_null(iterator); + + iterator = linked_list_iterator(list, false); + for (i = size - 1; i >= 0; i--) + { + ck_assert_ptr_nonnull(iterator); + ck_assert_ptr_eq(values[i], linked_list_iterator_value(iterator)); + iterator = linked_list_iterator_next(iterator); + } + ck_assert_ptr_null(iterator); + + free(values); +} + +START_TEST (test_linked_list_add) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + _verify_list(list, 3, _value_1, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_insert_empty) +{ + linked_list_t *list = linked_list_new(); + linked_list_insert(list, 0, _value_1); + + ck_assert_int_eq(linked_list_size(list), 1); + ck_assert_ptr_eq(linked_list_get(list, 0), _value_1); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_insert_first) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + linked_list_insert(list, 0, _value_1); + + _verify_list(list, 3, _value_1, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_insert_middle) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_3); + linked_list_insert(list, 1, _value_2); + + _verify_list(list, 3, _value_1, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_insert_last) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_insert(list, 2, _value_3); + + _verify_list(list, 3, _value_1, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_insert_invalid) +{ + linked_list_t *list = linked_list_new(); + linked_list_insert(list, 2, _value_1); + + ck_assert_int_eq(linked_list_size(list), 0); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_insert_null) +{ + linked_list_insert(NULL, 0, _value_1); +} +END_TEST + +START_TEST (test_linked_list_get_invalid) +{ + linked_list_t *list = linked_list_new(); + ck_assert_ptr_null(linked_list_get(list, 2)); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_get_null) +{ + ck_assert_ptr_null(linked_list_get(NULL, 0)); +} +END_TEST + +START_TEST (test_linked_list_get_first_matching_null) +{ + ck_assert_ptr_null(linked_list_get_first_matching(NULL, NULL, NULL)); +} +END_TEST + +START_TEST (test_linked_list_get_first_matching_function_null) +{ + linked_list_t *list = linked_list_new(); + ck_assert_ptr_null(linked_list_get_first_matching(list, NULL, NULL)); + + linked_list_free(list, NULL); +} + +bool _matches_function(void *value, void *state) +{ + ck_assert_ptr_eq(_value_1, state); + return value == _value_2; +} + +START_TEST (test_linked_list_get_first_matching_no_match) +{ + linked_list_t *list = linked_list_new(); + ck_assert_ptr_null(linked_list_get_first_matching(list, &_matches_function, _value_1)); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_get_first_matching_with_match) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_ptr_eq(_value_2, linked_list_get_first_matching(list, &_matches_function, _value_1)); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_get_last_matching_null) +{ + ck_assert_ptr_null(linked_list_get_last_matching(NULL, NULL, NULL)); +} +END_TEST + +START_TEST (test_linked_list_get_last_matching_function_null) +{ + linked_list_t *list = linked_list_new(); + ck_assert_ptr_null(linked_list_get_last_matching(list, NULL, NULL)); + + linked_list_free(list, NULL); +} + +START_TEST (test_linked_list_get_last_matching_no_match) +{ + linked_list_t *list = linked_list_new(); + ck_assert_ptr_null(linked_list_get_last_matching(list, &_matches_function, _value_1)); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_get_last_matching_with_match) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_ptr_eq(_value_2, linked_list_get_last_matching(list, &_matches_function, _value_1)); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_at_null) +{ + ck_assert_ptr_null(linked_list_remove_at(NULL, 0)); +} +END_TEST + +START_TEST (test_linked_list_remove_at_empty) +{ + linked_list_t *list = linked_list_new(); + ck_assert_ptr_null(linked_list_remove_at(list, 0)); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_at_invalid) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + linked_list_remove_at(list, 3); + + _verify_list(list, 3, _value_1, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_at_first) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + linked_list_remove_at(list, 0); + + _verify_list(list, 2, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_at_middle) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + linked_list_remove_at(list, 1); + + _verify_list(list, 2, _value_1, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_at_last) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + linked_list_remove_at(list, 2); + + _verify_list(list, 2, _value_1, _value_2); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_at_only) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + + linked_list_remove_at(list, 0); + + _verify_list(list, 0); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_first_null) +{ + ck_assert_ptr_null(linked_list_remove_first(NULL, _value_1)); +} +END_TEST + +START_TEST (test_linked_list_remove_first_empty) +{ + linked_list_t *list = linked_list_new(); + ck_assert_ptr_null(linked_list_remove_first(list, _value_1)); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_first_not_found) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_ptr_null(linked_list_remove_first(list, "foo")); + + _verify_list(list, 3, _value_1, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_first_first) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_ptr_eq(linked_list_remove_first(list, _value_1), _value_1); + + _verify_list(list, 2, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_first_middle) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_ptr_eq(linked_list_remove_first(list, _value_2), _value_2); + + _verify_list(list, 2, _value_1, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_first_last) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_ptr_eq(linked_list_remove_first(list, _value_3), _value_3); + + _verify_list(list, 2, _value_1, _value_2); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_first_only) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + + ck_assert_ptr_eq(linked_list_remove_first(list, _value_1), _value_1); + + _verify_list(list, 0); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_first_multiple) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_1); + + ck_assert_ptr_eq(linked_list_remove_first(list, _value_1), _value_1); + + _verify_list(list, 2, _value_2, _value_1); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_last_null) +{ + ck_assert_ptr_null(linked_list_remove_last(NULL, _value_1)); +} +END_TEST + +START_TEST (test_linked_list_remove_last_empty) +{ + linked_list_t *list = linked_list_new(); + ck_assert_ptr_null(linked_list_remove_last(list, _value_1)); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_last_not_found) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_ptr_null(linked_list_remove_last(list, "foo")); + + _verify_list(list, 3, _value_1, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_last_first) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_ptr_eq(linked_list_remove_last(list, _value_1), _value_1); + + _verify_list(list, 2, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_last_middle) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_ptr_eq(linked_list_remove_last(list, _value_2), _value_2); + + _verify_list(list, 2, _value_1, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_last_last) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_ptr_eq(linked_list_remove_last(list, _value_3), _value_3); + + _verify_list(list, 2, _value_1, _value_2); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_last_only) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + + ck_assert_ptr_eq(linked_list_remove_last(list, _value_1), _value_1); + + _verify_list(list, 0); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_last_multiple) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_1); + + ck_assert_ptr_eq(linked_list_remove_last(list, _value_1), _value_1); + + _verify_list(list, 2, _value_1, _value_2); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_all_null) +{ + ck_assert_ptr_null(linked_list_remove_all(NULL, _value_1)); +} +END_TEST + +START_TEST (test_linked_list_remove_all_empty) +{ + linked_list_t *list = linked_list_new(); + ck_assert_ptr_null(linked_list_remove_all(list, _value_1)); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_all_not_found) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_ptr_null(linked_list_remove_all(list, "foo")); + + _verify_list(list, 3, _value_1, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_all_first) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_ptr_eq(linked_list_remove_all(list, _value_1), _value_1); + + _verify_list(list, 2, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_all_middle) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_ptr_eq(linked_list_remove_all(list, _value_2), _value_2); + + _verify_list(list, 2, _value_1, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_all_last) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_ptr_eq(linked_list_remove_all(list, _value_3), _value_3); + + _verify_list(list, 2, _value_1, _value_2); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_all_only) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + + ck_assert_ptr_eq(linked_list_remove_all(list, _value_1), _value_1); + + _verify_list(list, 0); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_all_multiple) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_1); + + ck_assert_ptr_eq(linked_list_remove_all(list, _value_1), _value_1); + + _verify_list(list, 1, _value_2); + + linked_list_free(list, NULL); +} +END_TEST + +bool _match_value_1(void *value) +{ + return _value_1 == value; +} + +bool _no_match(void *value) +{ + return false; +} + +START_TEST (test_linked_list_remove_first_matching_null) +{ + ck_assert_ptr_null(linked_list_remove_first_matching(NULL, &_match_value_1)); +} +END_TEST + +START_TEST (test_linked_list_remove_first_matching_empty) +{ + linked_list_t *list = linked_list_new(); + ck_assert_ptr_null(linked_list_remove_first_matching(list, &_match_value_1)); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_first_matching_not_found) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_ptr_null(linked_list_remove_first_matching(list, &_no_match)); + + _verify_list(list, 3, _value_1, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_first_matching_first) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_ptr_eq(linked_list_remove_first_matching(list, &_match_value_1), _value_1); + + _verify_list(list, 2, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_first_matching_middle) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_2); + linked_list_add(list, _value_1); + linked_list_add(list, _value_3); + + ck_assert_ptr_eq(linked_list_remove_first_matching(list, &_match_value_1), _value_1); + + _verify_list(list, 2, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_first_matching_last) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + linked_list_add(list, _value_1); + + ck_assert_ptr_eq(linked_list_remove_first_matching(list, &_match_value_1), _value_1); + + _verify_list(list, 2, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_first_matching_only) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + + ck_assert_ptr_eq(linked_list_remove_first_matching(list, &_match_value_1), _value_1); + + _verify_list(list, 0); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_first_matching_multiple) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_1); + + ck_assert_ptr_eq(linked_list_remove_first_matching(list, &_match_value_1), _value_1); + + _verify_list(list, 2, _value_2, _value_1); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_last_matching_null) +{ + ck_assert_ptr_null(linked_list_remove_last_matching(NULL, &_match_value_1)); +} +END_TEST + +START_TEST (test_linked_list_remove_last_matching_empty) +{ + linked_list_t *list = linked_list_new(); + ck_assert_ptr_null(linked_list_remove_last_matching(list, &_match_value_1)); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_last_matching_not_found) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_ptr_null(linked_list_remove_last_matching(list, &_no_match)); + + _verify_list(list, 3, _value_1, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_last_matching_first) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_ptr_eq(linked_list_remove_last_matching(list, &_match_value_1), _value_1); + + _verify_list(list, 2, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_last_matching_middle) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_2); + linked_list_add(list, _value_1); + linked_list_add(list, _value_3); + + ck_assert_ptr_eq(linked_list_remove_last_matching(list, &_match_value_1), _value_1); + + _verify_list(list, 2, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_last_matching_last) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + linked_list_add(list, _value_1); + + ck_assert_ptr_eq(linked_list_remove_last_matching(list, &_match_value_1), _value_1); + + _verify_list(list, 2, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_last_matching_only) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + + ck_assert_ptr_eq(linked_list_remove_last_matching(list, &_match_value_1), _value_1); + + _verify_list(list, 0); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_last_matching_multiple) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_1); + + ck_assert_ptr_eq(linked_list_remove_last_matching(list, &_match_value_1), _value_1); + + _verify_list(list, 2, _value_1, _value_2); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_all_matching_null) +{ + linked_list_remove_all_matching(NULL, &_match_value_1); +} +END_TEST + +START_TEST (test_linked_list_remove_all_matching_empty) +{ + linked_list_t *list = linked_list_new(); + linked_list_remove_all_matching(list, &_match_value_1); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_all_matching_not_found) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + linked_list_remove_all_matching(list, &_no_match); + + _verify_list(list, 3, _value_1, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_all_matching_first) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + linked_list_remove_all_matching(list, &_match_value_1); + + _verify_list(list, 2, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_all_matching_middle) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_2); + linked_list_add(list, _value_1); + linked_list_add(list, _value_3); + + linked_list_remove_all_matching(list, &_match_value_1); + + _verify_list(list, 2, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_all_matching_last) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + linked_list_add(list, _value_1); + + linked_list_remove_all_matching(list, &_match_value_1); + + _verify_list(list, 2, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_all_matching_only) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + + linked_list_remove_all_matching(list, &_match_value_1); + + _verify_list(list, 0); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_remove_all_matching_multiple) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_1); + + linked_list_remove_all_matching(list, &_match_value_1); + + _verify_list(list, 1, _value_2); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_set_at_null) +{ + ck_assert_int_eq(linked_list_set_at(NULL, 0, _value_1) == true, 0); +} +END_TEST + +START_TEST (test_linked_list_set_at_empty) +{ + linked_list_t *list = linked_list_new(); + ck_assert_int_eq(linked_list_set_at(list, 0, _value_1) == true, 0); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_set_at_invalid) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + ck_assert_int_eq(linked_list_set_at(list, 1, _value_2) == true, 0); + + linked_list_free(list, NULL); +} +END_TEST + +static char *_replacement_value = "foo"; + +START_TEST (test_linked_list_set_at_first) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_int_eq(linked_list_set_at(list, 0, _replacement_value) == false, 0); + + _verify_list(list, 3, _replacement_value, _value_2, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_set_at_middle) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_int_eq(linked_list_set_at(list, 1, _replacement_value) == false, 0); + + _verify_list(list, 3, _value_1, _replacement_value, _value_3); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_set_at_last) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + ck_assert_int_eq(linked_list_set_at(list, 2, _replacement_value) == false, 0); + + _verify_list(list, 3, _value_1, _value_2, _replacement_value); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_iterator_remove_null) +{ + ck_assert_ptr_null(linked_list_iterator_remove(NULL)); +} +END_TEST + +START_TEST (test_linked_list_iterator_remove_first) +{ + linked_list_t *list; + linked_list_iterator_t *iterator; + + list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + iterator = linked_list_iterator(list, true); + iterator = linked_list_iterator_remove(iterator); + + ck_assert_ptr_nonnull(iterator); + ck_assert_ptr_eq(linked_list_iterator_value(iterator), _value_2); + _verify_list(list, 2, _value_2, _value_3); + + linked_list_iterator_free(iterator); + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_iterator_remove_middle) +{ + linked_list_t *list; + linked_list_iterator_t *iterator; + + list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + iterator = linked_list_iterator(list, true); + iterator = linked_list_iterator_next(iterator); + iterator = linked_list_iterator_remove(iterator); + + ck_assert_ptr_nonnull(iterator); + ck_assert_ptr_eq(linked_list_iterator_value(iterator), _value_3); + _verify_list(list, 2, _value_1, _value_3); + + linked_list_iterator_free(iterator); + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_iterator_remove_last) +{ + linked_list_t *list; + linked_list_iterator_t *iterator; + + list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + iterator = linked_list_iterator(list, true); + iterator = linked_list_iterator_next(iterator); + iterator = linked_list_iterator_next(iterator); + iterator = linked_list_iterator_remove(iterator); + + ck_assert_ptr_null(iterator); + _verify_list(list, 2, _value_1, _value_2); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_iterator_free_null) +{ + linked_list_iterator_free(NULL); +} +END_TEST + +static size_t _foreach_count; +static void _foreach_fn(size_t index, void *value) +{ + _foreach_count++; +} + +START_TEST (test_linked_list_foreach_null_list) +{ + linked_list_foreach(NULL, _foreach_fn); +} +END_TEST + +START_TEST (test_linked_list_foreach_null_fn) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + linked_list_foreach(list, NULL); + + linked_list_free(list, NULL); +} +END_TEST + +START_TEST (test_linked_list_foreach_valid) +{ + linked_list_t *list = linked_list_new(); + linked_list_add(list, _value_1); + linked_list_add(list, _value_2); + linked_list_add(list, _value_3); + + _foreach_count = 0; + linked_list_foreach(list, &_foreach_fn); + ck_assert_uint_eq(3, _foreach_count); + + linked_list_free(list, NULL); +} + +Suite *create_suite(void) +{ + Suite *s = suite_create(SUITE_NAME); + + TCase *tc_core = tcase_create("Core"); + tcase_add_test(tc_core, test_linked_list_create); + tcase_add_test(tc_core, test_linked_list_free); + tcase_add_test(tc_core, test_linked_list_free_with_fn); + tcase_add_test(tc_core, test_linked_list_add); + tcase_add_test(tc_core, test_linked_list_insert_empty); + tcase_add_test(tc_core, test_linked_list_insert_first); + tcase_add_test(tc_core, test_linked_list_insert_middle); + tcase_add_test(tc_core, test_linked_list_insert_last); + tcase_add_test(tc_core, test_linked_list_insert_invalid); + tcase_add_test(tc_core, test_linked_list_insert_null); + tcase_add_test(tc_core, test_linked_list_get_invalid); + tcase_add_test(tc_core, test_linked_list_get_null); + tcase_add_test(tc_core, test_linked_list_get_first_matching_null); + tcase_add_test(tc_core, test_linked_list_get_first_matching_function_null); + tcase_add_test(tc_core, test_linked_list_get_first_matching_no_match); + tcase_add_test(tc_core, test_linked_list_get_first_matching_with_match); + tcase_add_test(tc_core, test_linked_list_get_last_matching_null); + tcase_add_test(tc_core, test_linked_list_get_last_matching_function_null); + tcase_add_test(tc_core, test_linked_list_get_last_matching_no_match); + tcase_add_test(tc_core, test_linked_list_get_last_matching_with_match); + tcase_add_test(tc_core, test_linked_list_remove_at_null); + tcase_add_test(tc_core, test_linked_list_remove_at_empty); + tcase_add_test(tc_core, test_linked_list_remove_at_invalid); + tcase_add_test(tc_core, test_linked_list_remove_at_first); + tcase_add_test(tc_core, test_linked_list_remove_at_middle); + tcase_add_test(tc_core, test_linked_list_remove_at_last); + tcase_add_test(tc_core, test_linked_list_remove_at_only); + tcase_add_test(tc_core, test_linked_list_remove_first_null); + tcase_add_test(tc_core, test_linked_list_remove_first_empty); + tcase_add_test(tc_core, test_linked_list_remove_first_not_found); + tcase_add_test(tc_core, test_linked_list_remove_first_first); + tcase_add_test(tc_core, test_linked_list_remove_first_middle); + tcase_add_test(tc_core, test_linked_list_remove_first_last); + tcase_add_test(tc_core, test_linked_list_remove_first_only); + tcase_add_test(tc_core, test_linked_list_remove_first_multiple); + tcase_add_test(tc_core, test_linked_list_remove_last_null); + tcase_add_test(tc_core, test_linked_list_remove_last_empty); + tcase_add_test(tc_core, test_linked_list_remove_last_not_found); + tcase_add_test(tc_core, test_linked_list_remove_last_first); + tcase_add_test(tc_core, test_linked_list_remove_last_middle); + tcase_add_test(tc_core, test_linked_list_remove_last_last); + tcase_add_test(tc_core, test_linked_list_remove_last_only); + tcase_add_test(tc_core, test_linked_list_remove_last_multiple); + tcase_add_test(tc_core, test_linked_list_remove_all_null); + tcase_add_test(tc_core, test_linked_list_remove_all_empty); + tcase_add_test(tc_core, test_linked_list_remove_all_not_found); + tcase_add_test(tc_core, test_linked_list_remove_all_first); + tcase_add_test(tc_core, test_linked_list_remove_all_middle); + tcase_add_test(tc_core, test_linked_list_remove_all_last); + tcase_add_test(tc_core, test_linked_list_remove_all_only); + tcase_add_test(tc_core, test_linked_list_remove_all_multiple); + tcase_add_test(tc_core, test_linked_list_remove_first_matching_null); + tcase_add_test(tc_core, test_linked_list_remove_first_matching_empty); + tcase_add_test(tc_core, test_linked_list_remove_first_matching_not_found); + tcase_add_test(tc_core, test_linked_list_remove_first_matching_first); + tcase_add_test(tc_core, test_linked_list_remove_first_matching_middle); + tcase_add_test(tc_core, test_linked_list_remove_first_matching_last); + tcase_add_test(tc_core, test_linked_list_remove_first_matching_only); + tcase_add_test(tc_core, test_linked_list_remove_first_matching_multiple); + tcase_add_test(tc_core, test_linked_list_remove_last_matching_null); + tcase_add_test(tc_core, test_linked_list_remove_last_matching_empty); + tcase_add_test(tc_core, test_linked_list_remove_last_matching_not_found); + tcase_add_test(tc_core, test_linked_list_remove_last_matching_first); + tcase_add_test(tc_core, test_linked_list_remove_last_matching_middle); + tcase_add_test(tc_core, test_linked_list_remove_last_matching_last); + tcase_add_test(tc_core, test_linked_list_remove_last_matching_only); + tcase_add_test(tc_core, test_linked_list_remove_last_matching_multiple); + tcase_add_test(tc_core, test_linked_list_remove_all_matching_null); + tcase_add_test(tc_core, test_linked_list_remove_all_matching_empty); + tcase_add_test(tc_core, test_linked_list_remove_all_matching_not_found); + tcase_add_test(tc_core, test_linked_list_remove_all_matching_first); + tcase_add_test(tc_core, test_linked_list_remove_all_matching_middle); + tcase_add_test(tc_core, test_linked_list_remove_all_matching_last); + tcase_add_test(tc_core, test_linked_list_remove_all_matching_only); + tcase_add_test(tc_core, test_linked_list_remove_all_matching_multiple); + tcase_add_test(tc_core, test_linked_list_set_at_null); + tcase_add_test(tc_core, test_linked_list_set_at_empty); + tcase_add_test(tc_core, test_linked_list_set_at_invalid); + tcase_add_test(tc_core, test_linked_list_set_at_first); + tcase_add_test(tc_core, test_linked_list_set_at_middle); + tcase_add_test(tc_core, test_linked_list_set_at_last); + tcase_add_test(tc_core, test_linked_list_iterator_remove_first); + tcase_add_test(tc_core, test_linked_list_iterator_remove_middle); + tcase_add_test(tc_core, test_linked_list_iterator_remove_last); + tcase_add_test(tc_core, test_linked_list_iterator_free_null); + tcase_add_test(tc_core, test_linked_list_foreach_null_list); + tcase_add_test(tc_core, test_linked_list_foreach_null_fn); + tcase_add_test(tc_core, test_linked_list_foreach_valid); + suite_add_tcase(s, tc_core); + + return s; +} + +int main(void) +{ + int num_fail; + Suite *s = create_suite(); + SRunner *sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + num_fail = srunner_ntests_failed(sr); + srunner_free(sr); + return (num_fail == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/libretro-common/test/queues/test_generic_queue.c b/libretro-common/test/queues/test_generic_queue.c new file mode 100644 index 0000000000..3fe5f0c938 --- /dev/null +++ b/libretro-common/test/queues/test_generic_queue.c @@ -0,0 +1,410 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (test_generic_queue.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#include +#include + +#include + +#define SUITE_NAME "Generic Queue" + +static char *_value_1 = "value1"; +static char *_value_2 = "value2"; +static char *_value_3 = "value3"; + +START_TEST (test_generic_queue_create) +{ + generic_queue_t *queue = generic_queue_new(); + ck_assert_ptr_nonnull(queue); + generic_queue_free(queue, NULL); +} +END_TEST + +START_TEST (test_generic_queue_free) +{ + generic_queue_t *queue = generic_queue_new(); + generic_queue_free(queue, NULL); + generic_queue_free(NULL, NULL); +} +END_TEST + +START_TEST (test_generic_queue_push_pop) +{ + generic_queue_t *queue; + char *value; + + queue = generic_queue_new(); + generic_queue_push(queue, _value_1); + ck_assert_int_eq(generic_queue_length(queue), 1); + value = (char *) generic_queue_pop(queue); + ck_assert_ptr_eq(value, _value_1); + ck_assert_int_eq(generic_queue_length(queue), 0); + + generic_queue_push(queue, _value_2); + ck_assert_int_eq(generic_queue_length(queue), 1); + generic_queue_push(queue, _value_3); + ck_assert_int_eq(generic_queue_length(queue), 2); + value = (char *) generic_queue_pop(queue); + ck_assert_ptr_eq(value, _value_3); + ck_assert_int_eq(generic_queue_length(queue), 1); + value = (char *) generic_queue_pop(queue); + ck_assert_ptr_eq(value, _value_2); + ck_assert_int_eq(generic_queue_length(queue), 0); + + generic_queue_free(queue, NULL); +} +END_TEST + +START_TEST (test_generic_queue_peek) +{ + generic_queue_t *queue; + + queue = generic_queue_new(); + ck_assert_ptr_null(generic_queue_peek(queue)); + ck_assert_ptr_null(generic_queue_peek_first(queue)); + + generic_queue_push(queue, _value_1); + ck_assert_ptr_eq(_value_1, generic_queue_peek(queue)); + ck_assert_ptr_eq(_value_1, generic_queue_peek_first(queue)); + + generic_queue_push(queue, _value_2); + ck_assert_ptr_eq(_value_2, generic_queue_peek(queue)); + ck_assert_ptr_eq(_value_1, generic_queue_peek_first(queue)); + + generic_queue_push(queue, _value_3); + ck_assert_ptr_eq(_value_3, generic_queue_peek(queue)); + ck_assert_ptr_eq(_value_1, generic_queue_peek_first(queue)); + + generic_queue_free(queue, NULL); +} +END_TEST + +START_TEST (test_generic_queue_shift_unshift) +{ + generic_queue_t *queue; + char *value; + + queue = generic_queue_new(); + generic_queue_shift(queue, _value_1); + ck_assert_int_eq(generic_queue_length(queue), 1); + value = (char *) generic_queue_unshift(queue); + ck_assert_ptr_eq(value, _value_1); + ck_assert_int_eq(generic_queue_length(queue), 0); + + generic_queue_shift(queue, _value_2); + ck_assert_int_eq(generic_queue_length(queue), 1); + generic_queue_shift(queue, _value_3); + ck_assert_int_eq(generic_queue_length(queue), 2); + value = (char *) generic_queue_unshift(queue); + ck_assert_ptr_eq(value, _value_3); + ck_assert_int_eq(generic_queue_length(queue), 1); + value = (char *) generic_queue_unshift(queue); + ck_assert_ptr_eq(value, _value_2); + ck_assert_int_eq(generic_queue_length(queue), 0); + + generic_queue_free(queue, NULL); +} +END_TEST + +START_TEST (test_generic_queue_empty) +{ + generic_queue_t *queue; + + queue = generic_queue_new(); + ck_assert_ptr_null(generic_queue_pop(queue)); + ck_assert_ptr_null(generic_queue_unshift(queue)); + generic_queue_free(queue, NULL); +} +END_TEST + +void _free_value(void *value) +{ + return; +} + +START_TEST (test_generic_queue_iterator) +{ + generic_queue_t *queue; + generic_queue_iterator_t *iterator; + + queue = generic_queue_new(); + generic_queue_push(queue, _value_1); + generic_queue_push(queue, _value_2); + generic_queue_push(queue, _value_3); + + iterator = generic_queue_iterator(queue, true); + ck_assert_ptr_nonnull(iterator); + ck_assert_ptr_eq(generic_queue_iterator_value(iterator), _value_1); + iterator = generic_queue_iterator_next(iterator); + ck_assert_ptr_nonnull(iterator); + ck_assert_ptr_eq(generic_queue_iterator_value(iterator), _value_2); + iterator = generic_queue_iterator_next(iterator); + ck_assert_ptr_nonnull(iterator); + ck_assert_ptr_eq(generic_queue_iterator_value(iterator), _value_3); + iterator = generic_queue_iterator_next(iterator); + ck_assert_ptr_null(iterator); + + iterator = generic_queue_iterator(queue, false); + ck_assert_ptr_nonnull(iterator); + ck_assert_ptr_eq(generic_queue_iterator_value(iterator), _value_3); + iterator = generic_queue_iterator_next(iterator); + ck_assert_ptr_nonnull(iterator); + ck_assert_ptr_eq(generic_queue_iterator_value(iterator), _value_2); + iterator = generic_queue_iterator_next(iterator); + ck_assert_ptr_nonnull(iterator); + ck_assert_ptr_eq(generic_queue_iterator_value(iterator), _value_1); + iterator = generic_queue_iterator_next(iterator); + ck_assert_ptr_null(iterator); + + generic_queue_free(queue, &_free_value); +} +END_TEST + +START_TEST (test_generic_queue_shift_free) +{ + generic_queue_t *queue; + + queue = generic_queue_new(); + + generic_queue_shift(queue, _value_1); + generic_queue_shift(queue, _value_2); + generic_queue_shift(queue, _value_3); + + generic_queue_free(queue, &_free_value); +} +END_TEST + +START_TEST (test_generic_queue_remove_one) +{ + generic_queue_t *queue; + generic_queue_iterator_t *iterator; + + queue = generic_queue_new(); + generic_queue_push(queue, _value_1); + + iterator = generic_queue_iterator(queue, true); + iterator = generic_queue_iterator_remove(iterator); + ck_assert_ptr_null(iterator); + ck_assert_int_eq(generic_queue_length(queue), 0); + + generic_queue_free(queue, NULL); +} +END_TEST + +static void _verify_queue_values(generic_queue_t *queue, int count, ...) +{ + va_list values_list; + void **values; + int i; + generic_queue_iterator_t *iterator; + + values = (void **)malloc(count * sizeof(void *)); + + ck_assert_int_eq(count, generic_queue_length(queue)); + + va_start(values_list, count); + for (i = 0; i < count; i++) + values[i] = va_arg(values_list, void *); + va_end(values_list); + + iterator = generic_queue_iterator(queue, true); + for (i = 0; i < count; i++) + { + ck_assert_ptr_nonnull(iterator); + ck_assert_ptr_eq(values[i], generic_queue_iterator_value(iterator)); + iterator = generic_queue_iterator_next(iterator); + } + ck_assert_ptr_null(iterator); + + iterator = generic_queue_iterator(queue, false); + for (i = count - 1; i >= 0; i--) + { + ck_assert_ptr_nonnull(iterator); + ck_assert_ptr_eq(values[i], generic_queue_iterator_value(iterator)); + iterator = generic_queue_iterator_next(iterator); + } + ck_assert_ptr_null(iterator); + + free(values); +} + +START_TEST (test_generic_queue_iterator_remove_first) +{ + generic_queue_t *queue; + generic_queue_iterator_t *iterator; + + queue = generic_queue_new(); + generic_queue_push(queue, _value_1); + generic_queue_push(queue, _value_2); + generic_queue_push(queue, _value_3); + + iterator = generic_queue_iterator(queue, true); + iterator = generic_queue_iterator_remove(iterator); + generic_queue_iterator_free(iterator); + + _verify_queue_values(queue, 2, _value_2, _value_3); + + generic_queue_free(queue, &_free_value); +} +END_TEST + +START_TEST (test_generic_queue_iterator_remove_middle) +{ + generic_queue_t *queue; + generic_queue_iterator_t *iterator; + + queue = generic_queue_new(); + generic_queue_push(queue, _value_1); + generic_queue_push(queue, _value_2); + generic_queue_push(queue, _value_3); + + iterator = generic_queue_iterator(queue, true); + iterator = generic_queue_iterator_next(iterator); + iterator = generic_queue_iterator_remove(iterator); + generic_queue_iterator_free(iterator); + + _verify_queue_values(queue, 2, _value_1, _value_3); + + generic_queue_free(queue, &_free_value); +} +END_TEST + +START_TEST (test_generic_queue_iterator_remove_last) +{ + generic_queue_t *queue; + generic_queue_iterator_t *iterator; + + queue = generic_queue_new(); + generic_queue_push(queue, _value_1); + generic_queue_push(queue, _value_2); + generic_queue_push(queue, _value_3); + + iterator = generic_queue_iterator(queue, false); + iterator = generic_queue_iterator_remove(iterator); + generic_queue_iterator_free(iterator); + + _verify_queue_values(queue, 2, _value_1, _value_2); + + generic_queue_free(queue, &_free_value); +} +END_TEST + +START_TEST (test_generic_queue_remove_first) +{ + generic_queue_t *queue; + + queue = generic_queue_new(); + generic_queue_push(queue, _value_1); + generic_queue_push(queue, _value_2); + generic_queue_push(queue, _value_3); + + ck_assert_ptr_eq(generic_queue_remove(queue, _value_1), _value_1); + + _verify_queue_values(queue, 2, _value_2, _value_3); + + generic_queue_free(queue, &_free_value); +} + +START_TEST (test_generic_queue_remove_middle) +{ + generic_queue_t *queue; + + queue = generic_queue_new(); + generic_queue_push(queue, _value_1); + generic_queue_push(queue, _value_2); + generic_queue_push(queue, _value_3); + + ck_assert_ptr_eq(generic_queue_remove(queue, _value_2), _value_2); + + _verify_queue_values(queue, 2, _value_1, _value_3); + + generic_queue_free(queue, &_free_value); +} + +START_TEST (test_generic_queue_remove_last) +{ + generic_queue_t *queue; + + queue = generic_queue_new(); + generic_queue_push(queue, _value_1); + generic_queue_push(queue, _value_2); + generic_queue_push(queue, _value_3); + + ck_assert_ptr_eq(generic_queue_remove(queue, _value_3), _value_3); + + _verify_queue_values(queue, 2, _value_1, _value_2); + + generic_queue_free(queue, &_free_value); +} + +START_TEST (test_generic_queue_iterator_free) +{ + generic_queue_t *queue; + generic_queue_iterator_t *iterator; + + queue = generic_queue_new(); + generic_queue_push(queue, _value_1); + iterator = generic_queue_iterator(queue, true); + + generic_queue_iterator_free(iterator); + generic_queue_iterator_free(NULL); + + generic_queue_free(queue, _free_value); +} +END_TEST + +Suite *create_suite(void) +{ + Suite *s = suite_create(SUITE_NAME); + + TCase *tc_core = tcase_create("Core"); + tcase_add_test(tc_core, test_generic_queue_create); + tcase_add_test(tc_core, test_generic_queue_free); + tcase_add_test(tc_core, test_generic_queue_push_pop); + tcase_add_test(tc_core, test_generic_queue_peek); + tcase_add_test(tc_core, test_generic_queue_shift_unshift); + tcase_add_test(tc_core, test_generic_queue_empty); + tcase_add_test(tc_core, test_generic_queue_iterator); + tcase_add_test(tc_core, test_generic_queue_shift_free); + tcase_add_test(tc_core, test_generic_queue_remove_one); + tcase_add_test(tc_core, test_generic_queue_iterator_remove_first); + tcase_add_test(tc_core, test_generic_queue_iterator_remove_middle); + tcase_add_test(tc_core, test_generic_queue_iterator_remove_last); + tcase_add_test(tc_core, test_generic_queue_remove_first); + tcase_add_test(tc_core, test_generic_queue_remove_middle); + tcase_add_test(tc_core, test_generic_queue_remove_last); + tcase_add_test(tc_core, test_generic_queue_iterator_free); + suite_add_tcase(s, tc_core); + + return s; +} + +int main(void) +{ + int num_fail; + Suite *s = create_suite(); + SRunner *sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + num_fail = srunner_ntests_failed(sr); + srunner_free(sr); + return (num_fail == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/qb/config.libs.sh b/qb/config.libs.sh index c9aa28eb1a..361766246b 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -164,6 +164,8 @@ check_lib '' THREADS "$PTHREADLIB" pthread_create check_enabled THREADS THREAD_STORAGE 'Thread Local Storage' 'Threads are' false check_lib '' THREAD_STORAGE "$PTHREADLIB" pthread_key_create +check_pkgconf LIBCHECK check 0.15 + if [ "$OS" = 'Linux' ]; then check_header '' CDROM sys/ioctl.h scsi/sg.h fi diff --git a/qb/config.params.sh b/qb/config.params.sh index fdb7dd1569..3330449616 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -193,3 +193,4 @@ HAVE_NETWORK_VIDEO=no HAVE_STEAM=no # Enable Steam build HAVE_ODROIDGO2=no # ODROID-GO Advance rotation support (requires librga) HAVE_LIBSHAKE=no # libShake haptic feedback support +HAVE_CHECK=no # check support for unit tests \ No newline at end of file