/* * Copyright (C) 2001 Maciej Stachowiak * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program 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 * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, see . * * Author: Maciej Stachowiak */ #include #include "nautilus-hash-queue.h" #include /** * `NautilusHashQueue` is a `GQueue` with 2 special features: * 1) It can find and remove items in constant time * 2) It doesn't allow duplicates. */ struct NautilusHashQueue { GQueue parent; GHashTable *item_to_link_map; GHashTable *link_to_item_map; GDestroyNotify key_destroy_func; GDestroyNotify value_destroy_func; }; /** * nautilus_hash_queue_new: * @hash_func: a function to create a hash value from a key * @key_equal_func: a function to check two keys for equality * @key_destroy_func: (nullable): a function to free the memory allocated for * the key used when removing the entry from the #GHashTable, or `NULL` if * you don't want to supply such a function. * @value_destroy_func: (nullable): a function to free the memory allocated for * the value used when removing the entry from the #GHashTable, or `NULL` * if you don't want to supply such a function. * * Creates a new #NautilusHashQueue. * * Returns: (transfer full): a new #NautilusHashQueue */ NautilusHashQueue * nautilus_hash_queue_new (GHashFunc hash_func, GEqualFunc equal_func, GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func) { NautilusHashQueue *queue; queue = g_new0 (NautilusHashQueue, 1); g_queue_init ((GQueue *) queue); queue->item_to_link_map = g_hash_table_new_full (hash_func, equal_func, key_destroy_func, NULL); queue->link_to_item_map = g_hash_table_new (NULL, NULL); queue->key_destroy_func = key_destroy_func; queue->value_destroy_func = value_destroy_func; return queue; } void nautilus_hash_queue_destroy (NautilusHashQueue *queue) { g_hash_table_destroy (queue->item_to_link_map); g_hash_table_destroy (queue->link_to_item_map); if (queue->value_destroy_func != NULL) { g_queue_clear_full ((GQueue *) queue, queue->value_destroy_func); } else { g_queue_clear ((GQueue *) queue); } g_free (queue); } static gboolean nautilus_hash_queue_enqueue_internal (NautilusHashQueue *queue, gpointer key, gpointer value, gboolean reenqueue) { GList *link = g_hash_table_lookup (queue->item_to_link_map, key); if (link != NULL) { /* It's already on the queue. */ if (queue->key_destroy_func != NULL) { queue->key_destroy_func (key); } if (queue->value_destroy_func != NULL) { queue->value_destroy_func (value); } if (reenqueue) { g_queue_unlink ((GQueue *) queue, link); g_queue_push_tail_link ((GQueue *) queue, link); } return FALSE; } g_queue_push_tail ((GQueue *) queue, value); g_hash_table_insert (queue->item_to_link_map, key, queue->parent.tail); g_hash_table_insert (queue->link_to_item_map, queue->parent.tail, key); return TRUE; } /** Add an item to the tail of the queue, unless it's already in the queue. */ gboolean nautilus_hash_queue_enqueue (NautilusHashQueue *queue, gpointer key, gpointer value) { return nautilus_hash_queue_enqueue_internal (queue, key, value, FALSE); } gboolean nautilus_hash_queue_reenqueue (NautilusHashQueue *queue, gpointer key, gpointer value) { return nautilus_hash_queue_enqueue_internal (queue, key, value, TRUE); } /** * Remove the item responding to the provided key from the queue in constant time. */ void nautilus_hash_queue_remove (NautilusHashQueue *queue, gconstpointer key) { gpointer map_key; GList *link; if (g_hash_table_steal_extended (queue->item_to_link_map, key, &map_key, (gpointer *) &link)) { if (queue->value_destroy_func != NULL) { queue->value_destroy_func (link->data); } g_hash_table_remove (queue->link_to_item_map, link); g_queue_delete_link ((GQueue *) queue, link); if (queue->key_destroy_func != NULL) { queue->key_destroy_func (map_key); } } } void nautilus_hash_queue_remove_head (NautilusHashQueue *queue) { GList *link = g_queue_peek_head_link ((GQueue *) (queue)); if (link != NULL) { gpointer map_key = g_hash_table_lookup (queue->link_to_item_map, link); nautilus_hash_queue_remove (queue, map_key); } } gpointer nautilus_hash_queue_find_item (NautilusHashQueue *queue, gconstpointer key) { GList *link = g_hash_table_lookup (queue->item_to_link_map, key); if (link == NULL) { return NULL; } return link->data; } void nautilus_hash_queue_move_existing_to_head (NautilusHashQueue *queue, gconstpointer key) { GList *link = g_hash_table_lookup (queue->item_to_link_map, key); if (link == NULL) { return; } g_queue_unlink ((GQueue *) queue, link); g_queue_push_head_link ((GQueue *) queue, link); } void nautilus_hash_queue_move_existing_to_tail (NautilusHashQueue *queue, gconstpointer key) { GList *link = g_hash_table_lookup (queue->item_to_link_map, key); if (link == NULL) { return; } g_queue_unlink ((GQueue *) queue, link); g_queue_push_tail_link ((GQueue *) queue, link); }