|  | /* | 
|  | * | 
|  | *  BlueZ - Bluetooth protocol stack for Linux | 
|  | * | 
|  | *  Copyright (C) 2012-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 "src/shared/util.h" | 
|  | #include "src/shared/queue.h" | 
|  |  | 
|  | struct queue { | 
|  | int ref_count; | 
|  | struct queue_entry *head; | 
|  | struct queue_entry *tail; | 
|  | unsigned int entries; | 
|  | }; | 
|  |  | 
|  | static struct queue *queue_ref(struct queue *queue) | 
|  | { | 
|  | if (!queue) | 
|  | return NULL; | 
|  |  | 
|  | __sync_fetch_and_add(&queue->ref_count, 1); | 
|  |  | 
|  | return queue; | 
|  | } | 
|  |  | 
|  | static void queue_unref(struct queue *queue) | 
|  | { | 
|  | if (__sync_sub_and_fetch(&queue->ref_count, 1)) | 
|  | return; | 
|  |  | 
|  | free(queue); | 
|  | } | 
|  |  | 
|  | struct queue *queue_new(void) | 
|  | { | 
|  | struct queue *queue; | 
|  |  | 
|  | queue = new0(struct queue, 1); | 
|  | queue->head = NULL; | 
|  | queue->tail = NULL; | 
|  | queue->entries = 0; | 
|  |  | 
|  | return queue_ref(queue); | 
|  | } | 
|  |  | 
|  | void queue_destroy(struct queue *queue, queue_destroy_func_t destroy) | 
|  | { | 
|  | if (!queue) | 
|  | return; | 
|  |  | 
|  | queue_remove_all(queue, NULL, NULL, destroy); | 
|  |  | 
|  | queue_unref(queue); | 
|  | } | 
|  |  | 
|  | static struct queue_entry *queue_entry_new(void *data) | 
|  | { | 
|  | struct queue_entry *entry; | 
|  |  | 
|  | entry = new0(struct queue_entry, 1); | 
|  | entry->data = data; | 
|  |  | 
|  | return entry; | 
|  | } | 
|  |  | 
|  | bool queue_push_tail(struct queue *queue, void *data) | 
|  | { | 
|  | struct queue_entry *entry; | 
|  |  | 
|  | if (!queue) | 
|  | return false; | 
|  |  | 
|  | entry = queue_entry_new(data); | 
|  |  | 
|  | if (queue->tail) | 
|  | queue->tail->next = entry; | 
|  |  | 
|  | queue->tail = entry; | 
|  |  | 
|  | if (!queue->head) | 
|  | queue->head = entry; | 
|  |  | 
|  | queue->entries++; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool queue_push_head(struct queue *queue, void *data) | 
|  | { | 
|  | struct queue_entry *entry; | 
|  |  | 
|  | if (!queue) | 
|  | return false; | 
|  |  | 
|  | entry = queue_entry_new(data); | 
|  |  | 
|  | entry->next = queue->head; | 
|  |  | 
|  | queue->head = entry; | 
|  |  | 
|  | if (!queue->tail) | 
|  | queue->tail = entry; | 
|  |  | 
|  | queue->entries++; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool queue_push_after(struct queue *queue, void *entry, void *data) | 
|  | { | 
|  | struct queue_entry *qentry, *tmp, *new_entry; | 
|  |  | 
|  | qentry = NULL; | 
|  |  | 
|  | if (!queue) | 
|  | return false; | 
|  |  | 
|  | for (tmp = queue->head; tmp; tmp = tmp->next) { | 
|  | if (tmp->data == entry) { | 
|  | qentry = tmp; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!qentry) | 
|  | return false; | 
|  |  | 
|  | new_entry = queue_entry_new(data); | 
|  |  | 
|  | new_entry->next = qentry->next; | 
|  |  | 
|  | if (!qentry->next) | 
|  | queue->tail = new_entry; | 
|  |  | 
|  | qentry->next = new_entry; | 
|  | queue->entries++; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void *queue_pop_head(struct queue *queue) | 
|  | { | 
|  | struct queue_entry *entry; | 
|  | void *data; | 
|  |  | 
|  | if (!queue || !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; | 
|  |  | 
|  | free(entry); | 
|  | queue->entries--; | 
|  |  | 
|  | return data; | 
|  | } | 
|  |  | 
|  | void *queue_peek_head(struct queue *queue) | 
|  | { | 
|  | if (!queue || !queue->head) | 
|  | return NULL; | 
|  |  | 
|  | return queue->head->data; | 
|  | } | 
|  |  | 
|  | void *queue_peek_tail(struct queue *queue) | 
|  | { | 
|  | if (!queue || !queue->tail) | 
|  | return NULL; | 
|  |  | 
|  | return queue->tail->data; | 
|  | } | 
|  |  | 
|  | void queue_foreach(struct queue *queue, queue_foreach_func_t function, | 
|  | void *user_data) | 
|  | { | 
|  | struct queue_entry *entry; | 
|  |  | 
|  | if (!queue || !function) | 
|  | return; | 
|  |  | 
|  | entry = queue->head; | 
|  | if (!entry) | 
|  | return; | 
|  |  | 
|  | queue_ref(queue); | 
|  | while (entry && queue->head && queue->ref_count > 1) { | 
|  | struct queue_entry *next; | 
|  |  | 
|  | next = entry->next; | 
|  | function(entry->data, user_data); | 
|  | entry = next; | 
|  | } | 
|  | queue_unref(queue); | 
|  | } | 
|  |  | 
|  | static bool direct_match(const void *a, const void *b) | 
|  | { | 
|  | return a == b; | 
|  | } | 
|  |  | 
|  | void *queue_find(struct queue *queue, queue_match_func_t function, | 
|  | const void *match_data) | 
|  | { | 
|  | struct queue_entry *entry; | 
|  |  | 
|  | if (!queue) | 
|  | return NULL; | 
|  |  | 
|  | if (!function) | 
|  | function = direct_match; | 
|  |  | 
|  | for (entry = queue->head; entry; entry = entry->next) | 
|  | if (function(entry->data, match_data)) | 
|  | return entry->data; | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | bool queue_remove(struct queue *queue, void *data) | 
|  | { | 
|  | struct queue_entry *entry, *prev; | 
|  |  | 
|  | if (!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; | 
|  |  | 
|  | free(entry); | 
|  | queue->entries--; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void *queue_remove_if(struct queue *queue, queue_match_func_t function, | 
|  | void *user_data) | 
|  | { | 
|  | struct queue_entry *entry, *prev = NULL; | 
|  |  | 
|  | if (!queue || !function) | 
|  | return NULL; | 
|  |  | 
|  | entry = queue->head; | 
|  |  | 
|  | while (entry) { | 
|  | if (function(entry->data, user_data)) { | 
|  | void *data; | 
|  |  | 
|  | if (prev) | 
|  | prev->next = entry->next; | 
|  | else | 
|  | queue->head = entry->next; | 
|  |  | 
|  | if (!entry->next) | 
|  | queue->tail = prev; | 
|  |  | 
|  | data = entry->data; | 
|  |  | 
|  | free(entry); | 
|  | queue->entries--; | 
|  |  | 
|  | return data; | 
|  | } else { | 
|  | prev = entry; | 
|  | entry = entry->next; | 
|  | } | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | unsigned int queue_remove_all(struct queue *queue, queue_match_func_t function, | 
|  | void *user_data, queue_destroy_func_t destroy) | 
|  | { | 
|  | struct queue_entry *entry; | 
|  | unsigned int count = 0; | 
|  |  | 
|  | if (!queue) | 
|  | return 0; | 
|  |  | 
|  | entry = queue->head; | 
|  |  | 
|  | if (function) { | 
|  | while (entry) { | 
|  | void *data; | 
|  | unsigned int entries = queue->entries; | 
|  |  | 
|  | data = queue_remove_if(queue, function, user_data); | 
|  | if (entries == queue->entries) | 
|  | break; | 
|  |  | 
|  | if (destroy) | 
|  | destroy(data); | 
|  |  | 
|  | count++; | 
|  | } | 
|  | } else { | 
|  | queue->head = NULL; | 
|  | queue->tail = NULL; | 
|  | queue->entries = 0; | 
|  |  | 
|  | while (entry) { | 
|  | struct queue_entry *tmp = entry; | 
|  |  | 
|  | entry = entry->next; | 
|  |  | 
|  | if (destroy) | 
|  | destroy(tmp->data); | 
|  |  | 
|  | free(tmp); | 
|  | count++; | 
|  | } | 
|  | } | 
|  |  | 
|  | return count; | 
|  | } | 
|  |  | 
|  | const struct queue_entry *queue_get_entries(struct queue *queue) | 
|  | { | 
|  | if (!queue) | 
|  | return NULL; | 
|  |  | 
|  | return queue->head; | 
|  | } | 
|  |  | 
|  | unsigned int queue_length(struct queue *queue) | 
|  | { | 
|  | if (!queue) | 
|  | return 0; | 
|  |  | 
|  | return queue->entries; | 
|  | } | 
|  |  | 
|  | bool queue_isempty(struct queue *queue) | 
|  | { | 
|  | if (!queue) | 
|  | return true; | 
|  |  | 
|  | return queue->entries == 0; | 
|  | } |