| /* |
| * |
| * Embedded Linux library |
| * |
| * Copyright (C) 2011-2014 Intel Corporation. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include "queue.h" |
| #include "private.h" |
| #include "useful.h" |
| |
| /** |
| * SECTION:queue |
| * @short_description: Queue support |
| * |
| * Queue support |
| */ |
| |
| /** |
| * l_queue: |
| * |
| * Opague object representing the queue. |
| */ |
| struct l_queue { |
| struct l_queue_entry *head; |
| struct l_queue_entry *tail; |
| unsigned int entries; |
| }; |
| |
| /** |
| * l_queue_new: |
| * |
| * Create a new queue. |
| * |
| * No error handling is needed since. In case of real memory allocation |
| * problems abort() will be called. |
| * |
| * Returns: a newly allocated #l_queue object |
| **/ |
| LIB_EXPORT struct l_queue *l_queue_new(void) |
| { |
| struct l_queue *queue; |
| |
| queue = l_new(struct l_queue, 1); |
| |
| queue->head = NULL; |
| queue->tail = NULL; |
| queue->entries = 0; |
| |
| return queue; |
| } |
| |
| /** |
| * l_queue_destroy: |
| * @queue: queue object |
| * @destroy: destroy function |
| * |
| * Free queue and call @destory on all remaining entries. |
| **/ |
| LIB_EXPORT void l_queue_destroy(struct l_queue *queue, |
| l_queue_destroy_func_t destroy) |
| { |
| l_queue_clear(queue, destroy); |
| l_free(queue); |
| } |
| |
| /** |
| * l_queue_clear: |
| * @queue: queue object |
| * @destroy: destroy function |
| * |
| * Clear queue and call @destory on all remaining entries. |
| **/ |
| LIB_EXPORT void l_queue_clear(struct l_queue *queue, |
| l_queue_destroy_func_t destroy) |
| { |
| struct l_queue_entry *entry; |
| |
| if (unlikely(!queue)) |
| return; |
| |
| entry = queue->head; |
| |
| while (entry) { |
| struct l_queue_entry *tmp = entry; |
| |
| if (destroy) |
| destroy(entry->data); |
| |
| entry = entry->next; |
| |
| l_free(tmp); |
| } |
| |
| queue->head = NULL; |
| queue->tail = NULL; |
| queue->entries = 0; |
| } |
| |
| /** |
| * l_queue_push_tail: |
| * @queue: queue object |
| * @data: pointer to data |
| * |
| * Adds @data pointer at the end of the queue. |
| * |
| * Returns: #true when data has been added and #false in case an invalid |
| * @queue object has been provided |
| **/ |
| LIB_EXPORT bool l_queue_push_tail(struct l_queue *queue, void *data) |
| { |
| struct l_queue_entry *entry; |
| |
| if (unlikely(!queue)) |
| return false; |
| |
| entry = l_new(struct l_queue_entry, 1); |
| |
| entry->data = data; |
| entry->next = NULL; |
| |
| if (queue->tail) |
| queue->tail->next = entry; |
| |
| queue->tail = entry; |
| |
| if (!queue->head) |
| queue->head = entry; |
| |
| queue->entries++; |
| |
| return true; |
| } |
| |
| /** |
| * l_queue_push_head: |
| * @queue: queue object |
| * @data: pointer to data |
| * |
| * Adds @data pointer at the start of the queue. |
| * |
| * Returns: #true when data has been added and #false in case an invalid |
| * @queue object has been provided |
| **/ |
| LIB_EXPORT bool l_queue_push_head(struct l_queue *queue, void *data) |
| { |
| struct l_queue_entry *entry; |
| |
| if (unlikely(!queue)) |
| return false; |
| |
| entry = l_new(struct l_queue_entry, 1); |
| |
| entry->data = data; |
| entry->next = queue->head; |
| |
| queue->head = entry; |
| |
| if (!queue->tail) |
| queue->tail = entry; |
| |
| queue->entries++; |
| |
| return true; |
| } |
| |
| /** |
| * l_queue_pop_head: |
| * @queue: queue object |
| * |
| * Removes the first element of the queue an returns it. |
| * |
| * Returns: data pointer to first element or #NULL in case an empty queue |
| **/ |
| LIB_EXPORT void *l_queue_pop_head(struct l_queue *queue) |
| { |
| struct l_queue_entry *entry; |
| void *data; |
| |
| if (unlikely(!queue)) |
| return NULL; |
| |
| if (!queue->head) |
| return NULL; |
| |
| entry = queue->head; |
| |
| if (!queue->head->next) { |
| queue->head = NULL; |
| queue->tail = NULL; |
| } else |
| queue->head = queue->head->next; |
| |
| data = entry->data; |
| |
| l_free(entry); |
| |
| queue->entries--; |
| |
| return data; |
| } |
| |
| /** |
| * l_queue_peek_head: |
| * @queue: queue object |
| * |
| * Peeks at the first element of the queue an returns it. |
| * |
| * Returns: data pointer to first element or #NULL in case an empty queue |
| **/ |
| LIB_EXPORT void *l_queue_peek_head(struct l_queue *queue) |
| { |
| struct l_queue_entry *entry; |
| |
| if (unlikely(!queue)) |
| return NULL; |
| |
| if (!queue->head) |
| return NULL; |
| |
| entry = queue->head; |
| return entry->data; |
| } |
| |
| /** |
| * l_queue_peek_tail: |
| * @queue: queue object |
| * |
| * Peeks at the last element of the queue an returns it. |
| * |
| * Returns: data pointer to first element or #NULL in case an empty queue |
| **/ |
| LIB_EXPORT void *l_queue_peek_tail(struct l_queue *queue) |
| { |
| struct l_queue_entry *entry; |
| |
| if (unlikely(!queue)) |
| return NULL; |
| |
| if (!queue->tail) |
| return NULL; |
| |
| entry = queue->tail; |
| return entry->data; |
| } |
| |
| /** |
| * l_queue_insert: |
| * @queue: queue object |
| * @data: pointer to data |
| * @function: compare function |
| * @user_data: user data given to compare function |
| * |
| * Inserts @data pointer at a position in the queue determined by the |
| * compare @function. @function should return >= 0 if the @data (first |
| * parameter) should be inserted after the current entry (second parameter) |
| * and should return < 0 if before it. |
| * |
| * Returns: #true when data has been added and #false in case of failure |
| **/ |
| LIB_EXPORT bool l_queue_insert(struct l_queue *queue, void *data, |
| l_queue_compare_func_t function, void *user_data) |
| { |
| struct l_queue_entry *entry, *prev, *cur; |
| int cmp; |
| |
| if (unlikely(!queue || !function)) |
| return false; |
| |
| entry = l_new(struct l_queue_entry, 1); |
| |
| entry->data = data; |
| entry->next = NULL; |
| |
| if (!queue->head) { |
| queue->head = entry; |
| queue->tail = entry; |
| goto done; |
| } |
| |
| for (prev = NULL, cur = queue->head; cur; prev = cur, cur = cur->next) { |
| cmp = function(entry->data, cur->data, user_data); |
| |
| if (cmp >= 0) |
| continue; |
| |
| if (prev == NULL) { |
| entry->next = queue->head; |
| queue->head = entry; |
| goto done; |
| } |
| |
| entry->next = cur; |
| prev->next = entry; |
| |
| goto done; |
| } |
| |
| queue->tail->next = entry; |
| queue->tail = entry; |
| |
| done: |
| queue->entries++; |
| |
| return true; |
| } |
| |
| /** |
| * l_queue_find: |
| * @queue: queue object |
| * @function: match function |
| * @user_data: user data given to compare function |
| * |
| * Finds an entry in the queue by running the match @function |
| * |
| * Returns: Matching entry or NULL if no entry can be found |
| **/ |
| LIB_EXPORT void *l_queue_find(struct l_queue *queue, |
| l_queue_match_func_t function, |
| const void *user_data) |
| { |
| struct l_queue_entry *entry; |
| |
| if (unlikely(!queue || !function)) |
| return NULL; |
| |
| for (entry = queue->head; entry; entry = entry->next) |
| if (function(entry->data, user_data)) |
| return entry->data; |
| |
| return NULL; |
| } |
| |
| /** |
| * l_queue_remove: |
| * @queue: queue object |
| * @data: pointer to data |
| * |
| * Remove given @data from the queue. |
| * |
| * Returns: #true when data has been removed and #false when data could not |
| * be found or an invalid @queue object has been provided |
| **/ |
| LIB_EXPORT bool l_queue_remove(struct l_queue *queue, void *data) |
| { |
| struct l_queue_entry *entry, *prev; |
| |
| if (unlikely(!queue)) |
| return false; |
| |
| for (entry = queue->head, prev = NULL; entry; |
| prev = entry, entry = entry->next) { |
| if (entry->data != data) |
| continue; |
| |
| if (prev) |
| prev->next = entry->next; |
| else |
| queue->head = entry->next; |
| |
| if (!entry->next) |
| queue->tail = prev; |
| |
| l_free(entry); |
| |
| queue->entries--; |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * l_queue_reverse: |
| * @queue: queue object |
| * |
| * Reverse entries in the queue. |
| * |
| * Returns: #true on success and #false on failure |
| **/ |
| LIB_EXPORT bool l_queue_reverse(struct l_queue *queue) |
| { |
| struct l_queue_entry *entry, *prev = NULL; |
| |
| if (unlikely(!queue)) |
| return false; |
| |
| entry = queue->head; |
| |
| while (entry) { |
| struct l_queue_entry *next = entry->next; |
| |
| entry->next = prev; |
| |
| prev = entry; |
| entry = next; |
| } |
| |
| queue->tail = queue->head; |
| queue->head = prev; |
| |
| return true; |
| } |
| |
| /** |
| * l_queue_foreach: |
| * @queue: queue object |
| * @function: callback function |
| * @user_data: user data given to callback function |
| * |
| * Call @function for every given data in @queue. |
| **/ |
| LIB_EXPORT void l_queue_foreach(struct l_queue *queue, |
| l_queue_foreach_func_t function, void *user_data) |
| { |
| struct l_queue_entry *entry; |
| |
| if (unlikely(!queue || !function)) |
| return; |
| |
| for (entry = queue->head; entry; entry = entry->next) |
| function(entry->data, user_data); |
| } |
| |
| /** |
| * l_queue_foreach_remove: |
| * @queue: queue object |
| * @function: callback function |
| * @user_data: user data given to callback function |
| * |
| * Remove all entries in the @queue where @function returns #true. |
| * |
| * Returns: number of removed entries |
| **/ |
| LIB_EXPORT unsigned int l_queue_foreach_remove(struct l_queue *queue, |
| l_queue_remove_func_t function, void *user_data) |
| { |
| struct l_queue_entry *entry, *prev = NULL; |
| unsigned int count = 0; |
| |
| if (unlikely(!queue || !function)) |
| return 0; |
| |
| entry = queue->head; |
| |
| while (entry) { |
| if (function(entry->data, user_data)) { |
| struct l_queue_entry *tmp = entry; |
| |
| if (prev) |
| prev->next = entry->next; |
| else |
| queue->head = entry->next; |
| |
| if (!entry->next) |
| queue->tail = prev; |
| |
| entry = entry->next; |
| |
| l_free(tmp); |
| |
| count++; |
| } else { |
| prev = entry; |
| entry = entry->next; |
| } |
| } |
| |
| queue->entries -= count; |
| |
| return count; |
| } |
| |
| /** |
| * l_queue_remove_if |
| * @queue: queue object |
| * @function: callback function |
| * @user_data: user data given to callback function |
| * |
| * Remove the first entry in the @queue where the function returns #true. |
| * |
| * Returns: NULL if no entry was found, or the entry data if removal was |
| * successful. |
| **/ |
| LIB_EXPORT void *l_queue_remove_if(struct l_queue *queue, |
| l_queue_match_func_t function, |
| const void *user_data) |
| { |
| struct l_queue_entry *entry, *prev = NULL; |
| |
| if (unlikely(!queue || !function)) |
| return NULL; |
| |
| entry = queue->head; |
| |
| while (entry) { |
| if (function(entry->data, user_data)) { |
| struct l_queue_entry *tmp = entry; |
| void *data; |
| |
| if (prev) |
| prev->next = entry->next; |
| else |
| queue->head = entry->next; |
| |
| if (!entry->next) |
| queue->tail = prev; |
| |
| entry = entry->next; |
| |
| data = tmp->data; |
| |
| l_free(tmp); |
| queue->entries--; |
| |
| return data; |
| } |
| |
| prev = entry; |
| entry = entry->next; |
| } |
| |
| return NULL; |
| } |
| |
| /** |
| * l_queue_length: |
| * @queue: queue object |
| * |
| * Returns: entries of the queue |
| **/ |
| LIB_EXPORT unsigned int l_queue_length(struct l_queue *queue) |
| { |
| if (unlikely(!queue)) |
| return 0; |
| |
| return queue->entries; |
| } |
| |
| /** |
| * l_queue_isempty: |
| * @queue: queue object |
| * |
| * Returns: #true if @queue is empty and #false is not |
| **/ |
| LIB_EXPORT bool l_queue_isempty(struct l_queue *queue) |
| { |
| if (unlikely(!queue)) |
| return true; |
| |
| return queue->entries == 0; |
| } |
| |
| /** |
| * l_queue_get_entries: |
| * @queue: queue object |
| * |
| * This function gives direct, read-only access to the internal list structure |
| * of the queue. This can be used to efficiently traverse the elements. |
| * |
| * Returns: A pointer to the head of the queue. |
| **/ |
| LIB_EXPORT const struct l_queue_entry *l_queue_get_entries( |
| const struct l_queue *queue) |
| { |
| if (unlikely(!queue)) |
| return NULL; |
| |
| return queue->head; |
| } |