/*
* 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);
}