1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimpidtable.c
5  * Copyright (C) 2011 Martin Nordholts <martinn@src.gnome.org>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include <gio/gio.h>
24 #include <gegl.h>
25 
26 #include "core-types.h"
27 
28 #include "gimp-memsize.h"
29 #include "gimpidtable.h"
30 
31 
32 #define GIMP_ID_TABLE_START_ID 1
33 #define GIMP_ID_TABLE_END_ID   G_MAXINT
34 
35 
36 struct _GimpIdTablePrivate
37 {
38   GHashTable *id_table;
39   gint        next_id;
40 };
41 
42 
43 static void    gimp_id_table_finalize    (GObject    *object);
44 static gint64  gimp_id_table_get_memsize (GimpObject *object,
45                                           gint64     *gui_size);
46 
47 
G_DEFINE_TYPE_WITH_PRIVATE(GimpIdTable,gimp_id_table,GIMP_TYPE_OBJECT)48 G_DEFINE_TYPE_WITH_PRIVATE (GimpIdTable, gimp_id_table, GIMP_TYPE_OBJECT)
49 
50 #define parent_class gimp_id_table_parent_class
51 
52 
53 static void
54 gimp_id_table_class_init (GimpIdTableClass *klass)
55 {
56   GObjectClass    *object_class      = G_OBJECT_CLASS (klass);
57   GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
58 
59   object_class->finalize         = gimp_id_table_finalize;
60 
61   gimp_object_class->get_memsize = gimp_id_table_get_memsize;
62 }
63 
64 static void
gimp_id_table_init(GimpIdTable * id_table)65 gimp_id_table_init (GimpIdTable *id_table)
66 {
67   id_table->priv = gimp_id_table_get_instance_private (id_table);
68 
69   id_table->priv->id_table = g_hash_table_new (g_direct_hash, NULL);
70   id_table->priv->next_id  = GIMP_ID_TABLE_START_ID;
71 }
72 
73 static void
gimp_id_table_finalize(GObject * object)74 gimp_id_table_finalize (GObject *object)
75 {
76   GimpIdTable *id_table = GIMP_ID_TABLE (object);
77 
78   g_clear_pointer (&id_table->priv->id_table, g_hash_table_unref);
79 
80   G_OBJECT_CLASS (parent_class)->finalize (object);
81 }
82 
83 static gint64
gimp_id_table_get_memsize(GimpObject * object,gint64 * gui_size)84 gimp_id_table_get_memsize (GimpObject *object,
85                            gint64     *gui_size)
86 {
87   GimpIdTable *id_table = GIMP_ID_TABLE (object);
88   gint64       memsize  = 0;
89 
90   memsize += gimp_g_hash_table_get_memsize (id_table->priv->id_table, 0);
91 
92   return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
93                                                                   gui_size);
94 }
95 
96 /**
97  * gimp_id_table_new:
98  *
99  * Returns: A new #GimpIdTable.
100  **/
101 GimpIdTable *
gimp_id_table_new(void)102 gimp_id_table_new (void)
103 {
104   return g_object_new (GIMP_TYPE_ID_TABLE, NULL);
105 }
106 
107 /**
108  * gimp_id_table_insert:
109  * @id_table: A #GimpIdTable
110  * @data: Data to insert and assign an id to
111  *
112  * Insert data in the id table. The data will get an, in this table,
113  * unused ID assigned to it that can be used to later lookup the data.
114  *
115  * Returns: The assigned ID.
116  **/
117 gint
gimp_id_table_insert(GimpIdTable * id_table,gpointer data)118 gimp_id_table_insert (GimpIdTable *id_table, gpointer data)
119 {
120   gint new_id;
121   gint start_id;
122 
123   g_return_val_if_fail (GIMP_IS_ID_TABLE (id_table), 0);
124 
125   start_id = id_table->priv->next_id;
126 
127   do
128     {
129       new_id = id_table->priv->next_id++;
130 
131       if (id_table->priv->next_id == GIMP_ID_TABLE_END_ID)
132         id_table->priv->next_id = GIMP_ID_TABLE_START_ID;
133 
134       if (start_id == id_table->priv->next_id)
135         {
136           /* We looped once over all used ids. Very unlikely to happen.
137              And if it does, there is probably not much to be done.
138              It is just good design not to allow a theoretical infinite loop. */
139           g_error ("%s: out of ids!", G_STRFUNC);
140           break;
141         }
142     }
143   while (gimp_id_table_lookup (id_table, new_id));
144 
145   return gimp_id_table_insert_with_id (id_table, new_id, data);
146 }
147 
148 /**
149  * gimp_id_table_insert_with_id:
150  * @id_table: An #GimpIdTable
151  * @id: The ID to use. Must be greater than 0.
152  * @data: The data to associate with the id
153  *
154  * Insert data in the id table with a specific ID. If data already
155  * exsts with the given ID, this function fails.
156  *
157  * Returns: The used ID if successful, -1 if it was already in use.
158  **/
159 gint
gimp_id_table_insert_with_id(GimpIdTable * id_table,gint id,gpointer data)160 gimp_id_table_insert_with_id (GimpIdTable *id_table, gint id, gpointer data)
161 {
162   g_return_val_if_fail (GIMP_IS_ID_TABLE (id_table), 0);
163   g_return_val_if_fail (id > 0, 0);
164 
165   if (gimp_id_table_lookup (id_table, id))
166     return -1;
167 
168   g_hash_table_insert (id_table->priv->id_table, GINT_TO_POINTER (id), data);
169 
170   return id;
171 }
172 
173 /**
174  * gimp_id_table_replace:
175  * @id_table: An #GimpIdTable
176  * @id: The ID to use. Must be greater than 0.
177  * @data: The data to insert/replace
178  *
179  * Replaces (if an item with the given ID exists) or inserts a new
180  * entry in the id table.
181  **/
182 void
gimp_id_table_replace(GimpIdTable * id_table,gint id,gpointer data)183 gimp_id_table_replace (GimpIdTable *id_table, gint id, gpointer data)
184 {
185   g_return_if_fail (GIMP_IS_ID_TABLE (id_table));
186   g_return_if_fail (id > 0);
187 
188   g_hash_table_replace (id_table->priv->id_table, GINT_TO_POINTER (id), data);
189 }
190 
191 /**
192  * gimp_id_table_lookup:
193  * @id_table: An #GimpIdTable
194  * @id: The ID of the data to lookup
195  *
196  * Lookup data based on ID.
197  *
198  * Returns: The data, or NULL if no data with the given ID was found.
199  **/
200 gpointer
gimp_id_table_lookup(GimpIdTable * id_table,gint id)201 gimp_id_table_lookup (GimpIdTable *id_table, gint id)
202 {
203   g_return_val_if_fail (GIMP_IS_ID_TABLE (id_table), NULL);
204 
205   return g_hash_table_lookup (id_table->priv->id_table, GINT_TO_POINTER (id));
206 }
207 
208 
209 /**
210  * gimp_id_table_remove:
211  * @id_table: An #GimpIdTable
212  * @id: The ID of the data to remove.
213  *
214  * Remove the data from the table with the given ID.
215  *
216  * Returns: %TRUE if data with the ID existed and was successfully
217  *          removed, %FALSE otherwise.
218  **/
219 gboolean
gimp_id_table_remove(GimpIdTable * id_table,gint id)220 gimp_id_table_remove (GimpIdTable *id_table, gint id)
221 {
222   g_return_val_if_fail (GIMP_IS_ID_TABLE (id_table), FALSE);
223 
224   g_return_val_if_fail (id_table != NULL, FALSE);
225 
226   return g_hash_table_remove (id_table->priv->id_table, GINT_TO_POINTER (id));
227 }
228