1 /*
2  * handle-set.c - a set which refs a handle when inserted
3  *
4  * Copyright (C) 2005,2006,2007 Collabora Ltd.
5  * Copyright (C) 2005,2006,2007 Nokia Corporation
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * as published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA
21  *
22  */
23 
24 #include "config.h"
25 
26 /* there is no handle-set.h - handle set and handle repo have a circular
27  * dependency, so they share a header */
28 #include <telepathy-glib/handle-repo.h>
29 
30 #include <glib.h>
31 
32 #include <telepathy-glib/intset.h>
33 #define DEBUG_FLAG TP_DEBUG_HANDLES
34 #include "debug-internal.h"
35 
36 /**
37  * TpHandleSet:
38  *
39  * A set of handles. This is similar to a #TpIntset (and implemented using
40  * one), but adding a handle to the set also references it.
41  */
42 struct _TpHandleSet
43 {
44   TpHandleRepoIface *repo;
45   TpIntset *intset;
46 };
47 
48 /**
49  * TP_TYPE_HANDLE_SET: (skip)
50  *
51  * The boxed type of a #TpHandleSet.
52  *
53  * Since: 0.11.6
54  */
55 
G_DEFINE_BOXED_TYPE(TpHandleSet,tp_handle_set,tp_handle_set_copy,tp_handle_set_destroy)56 G_DEFINE_BOXED_TYPE (TpHandleSet, tp_handle_set, tp_handle_set_copy,
57     tp_handle_set_destroy)
58 
59 /**
60  * tp_handle_set_new: (skip)
61  * @repo: #TpHandleRepoIface that holds the handles to be reffed by this set
62  *
63  * Creates a new #TpHandleSet
64  *
65  * Returns: (transfer full): A new #TpHandleSet
66  */
67 TpHandleSet *
68 tp_handle_set_new (TpHandleRepoIface *repo)
69 {
70   TpHandleSet *set;
71   g_assert (repo != NULL);
72 
73   set = g_slice_new0 (TpHandleSet);
74   set->intset = tp_intset_new ();
75   set->repo = repo;
76 
77   return set;
78 }
79 
80 /**
81  * tp_handle_set_new_from_array: (skip)
82  * @repo: #TpHandleRepoIface that holds the handles to be reffed by this set
83  * @array: (element-type uint): array of handles to be referenced by this set
84  *
85  * Creates a new #TpHandleSet
86  *
87  * Returns: (transfer full): A new #TpHandleSet
88  *
89  * Since: 0.11.7
90  */
91 TpHandleSet *
tp_handle_set_new_from_array(TpHandleRepoIface * repo,const GArray * array)92 tp_handle_set_new_from_array (TpHandleRepoIface *repo,
93     const GArray *array)
94 {
95   TpHandleSet *set = tp_handle_set_new (repo);
96   TpIntset *tmp = tp_intset_from_array (array);
97 
98   tp_intset_destroy (tp_handle_set_update (set, tmp));
99   tp_intset_destroy (tmp);
100   return set;
101 }
102 
103 static void
freer(TpHandleSet * set,TpHandle handle,gpointer userdata)104 freer (TpHandleSet *set, TpHandle handle, gpointer userdata)
105 {
106   tp_handle_set_remove (set, handle);
107 }
108 
109 /**
110  * tp_handle_set_destroy: (skip)
111  * @set:#TpHandleSet to destroy
112  *
113  * Delete a #TpHandleSet and unreference any handles that it holds
114  */
115 void
tp_handle_set_destroy(TpHandleSet * set)116 tp_handle_set_destroy (TpHandleSet *set)
117 {
118   tp_handle_set_foreach (set, freer, NULL);
119   tp_intset_destroy (set->intset);
120   g_slice_free (TpHandleSet, set);
121 }
122 
123 /**
124  * tp_handle_set_clear: (skip)
125  * @set:#TpHandleSet to clear
126  *
127  * Remove every handle from @set, releasing the references it holds.
128  *
129  * Since: 0.11.6
130  */
131 void
tp_handle_set_clear(TpHandleSet * set)132 tp_handle_set_clear (TpHandleSet *set)
133 {
134   tp_handle_set_foreach (set, freer, NULL);
135   g_assert (tp_handle_set_is_empty (set));
136 }
137 
138 /**
139  * tp_handle_set_is_empty: (skip)
140  * @set:#TpHandleSet to check
141  *
142  * Return the same thing as <code>(tp_handle_set_size (set) == 0)</code>,
143  * but calculated more efficiently.
144  *
145  * Returns: %TRUE if the set has no members
146  *
147  * Since: 0.11.6
148  */
149 gboolean
tp_handle_set_is_empty(const TpHandleSet * set)150 tp_handle_set_is_empty (const TpHandleSet *set)
151 {
152   return tp_intset_is_empty (set->intset);
153 }
154 
155 /**
156  * tp_handle_set_peek: (skip)
157  * @set:#TpHandleSet to peek at
158  *
159  * <!--Returns: says it all, this comment is just to keep gtkdoc happy-->
160  *
161  * Returns: (transfer none): the underlying #TpIntset used by this #TpHandleSet
162  */
163 TpIntset *
tp_handle_set_peek(TpHandleSet * set)164 tp_handle_set_peek (TpHandleSet *set)
165 {
166   return set->intset;
167 }
168 
169 /**
170  * tp_handle_set_add: (skip)
171  * @set: #TpHandleSet to add this handle to
172  * @handle: handle to add
173  *
174  * Add a handle to a #TpHandleSet, and reference it in the attached
175  * #TpHandleRepoIface
176  *
177  */
178 void
tp_handle_set_add(TpHandleSet * set,TpHandle handle)179 tp_handle_set_add (TpHandleSet *set, TpHandle handle)
180 {
181   g_return_if_fail (set != NULL);
182   g_return_if_fail (handle != 0);
183 
184   tp_intset_add (set->intset, handle);
185 }
186 
187 /**
188  * tp_handle_set_remove: (skip)
189  * @set: #TpHandleSet to remove this handle from
190  * @handle: handle to remove
191  *
192  * Remove a handle from a #TpHandleSet, and unreference it in the attached
193  * #TpHandleRepoIface
194  *
195  * Returns: FALSE if the handle was invalid, or was not in this set
196  */
197 
198 gboolean
tp_handle_set_remove(TpHandleSet * set,TpHandle handle)199 tp_handle_set_remove (TpHandleSet *set, TpHandle handle)
200 {
201   g_return_val_if_fail (set != NULL, FALSE);
202   g_return_val_if_fail (handle != 0, FALSE);
203 
204   return tp_intset_remove (set->intset, handle);
205 }
206 
207 /**
208  * tp_handle_set_is_member: (skip)
209  * @set: A #TpHandleSet
210  * @handle: handle to check
211  *
212  * Check if the handle is in this set
213  *
214  * Returns: TRUE if the handle is in this set
215  *
216  */
217 gboolean
tp_handle_set_is_member(const TpHandleSet * set,TpHandle handle)218 tp_handle_set_is_member (const TpHandleSet *set,
219     TpHandle handle)
220 {
221   return tp_intset_is_member (set->intset, handle);
222 }
223 
224 typedef struct __foreach_data
225 {
226   TpHandleSet *set;
227   TpHandleSetMemberFunc func;
228   gpointer userdata;
229 } _foreach_data;
230 
231 static void
foreach_helper(guint i,gpointer userdata)232 foreach_helper (guint i, gpointer userdata)
233 {
234   _foreach_data *data = userdata;
235 
236   data->func (data->set, i, data->userdata);
237 }
238 
239 /**
240  * TpHandleSetMemberFunc: (skip)
241  * @set: The set of handles on which tp_handle_set_foreach() was called
242  * @handle: A handle in the set
243  * @userdata: Arbitrary user data as supplied to tp_handle_set_foreach()
244  *
245  * Signature of the callback used to iterate over the handle set in
246  * tp_handle_set_foreach().
247  */
248 
249 /**
250  * tp_handle_set_foreach: (skip)
251  * @set: A set of handles
252  * @func: (scope call): A callback
253  * @user_data: Arbitrary data to pass to @func
254  *
255  * Call @func(@set, @handle, @userdata) for each handle in @set.
256  */
257 void
tp_handle_set_foreach(TpHandleSet * set,TpHandleSetMemberFunc func,gpointer user_data)258 tp_handle_set_foreach (TpHandleSet *set, TpHandleSetMemberFunc func,
259     gpointer user_data)
260 {
261   _foreach_data data = {set, func, user_data};
262   tp_intset_foreach (set->intset, foreach_helper, &data);
263 }
264 
265 /**
266  * tp_handle_set_size: (skip)
267  * @set: A set of handles
268  *
269  * <!--no further documentation needed-->
270  *
271  * Returns: the number of handles in this set
272  */
273 int
tp_handle_set_size(const TpHandleSet * set)274 tp_handle_set_size (const TpHandleSet *set)
275 {
276   return tp_intset_size (set->intset);
277 }
278 
279 /**
280  * tp_handle_set_to_array: (skip)
281  * @set: A handle set
282  *
283  * <!--Returns: says it all, this comment is just to keep gtkdoc happy-->
284  *
285  * Returns: (element-type uint): a newly-allocated GArray of guint representing
286  * the handles in the set
287  */
288 GArray *
tp_handle_set_to_array(const TpHandleSet * set)289 tp_handle_set_to_array (const TpHandleSet *set)
290 {
291   g_return_val_if_fail (set != NULL, NULL);
292 
293   return tp_intset_to_array (set->intset);
294 }
295 
296 /**
297  * tp_handle_set_to_identifier_map:
298  * @self: a handle set
299  *
300  * Returns a dictionary mapping each handle in @self to the corresponding
301  * identifier, as if retrieved by calling tp_handle_inspect() on each handle.
302  * The type of the returned value is described as
303  * <code>Handle_Identifier_Map</code> in the Telepathy specification.
304  *
305  * Returns: (transfer full) (element-type TpHandle utf8): a map from the
306  *  handles in @self to the corresponding identifier.
307  */
308 GHashTable *
tp_handle_set_to_identifier_map(TpHandleSet * self)309 tp_handle_set_to_identifier_map (
310     TpHandleSet *self)
311 {
312   /* We don't bother dupping the strings: they remain valid as long as the
313    * connection's alive and hence the repo exists.
314    */
315   GHashTable *map = g_hash_table_new (NULL, NULL);
316   TpIntsetFastIter iter;
317   TpHandle handle;
318 
319   g_return_val_if_fail (self != NULL, map);
320 
321   tp_intset_fast_iter_init (&iter, self->intset);
322   while (tp_intset_fast_iter_next (&iter, &handle))
323     {
324       if (handle == 0 || !tp_handle_is_valid (self->repo, handle, NULL))
325         {
326           WARNING ("handle set %p contains invalid handle #%u", self, handle);
327         }
328       else
329         {
330           g_hash_table_insert (map, GUINT_TO_POINTER (handle),
331               (gchar *) tp_handle_inspect (self->repo, handle));
332         }
333     }
334 
335   return map;
336 }
337 
338 /**
339  * tp_handle_set_copy: (skip)
340  * @other: another handle set
341  *
342  * Creates a new #TpHandleSet with the same contents as @other.
343  *
344  * Returns: a new set
345  *
346  * Since: 0.11.6
347  */
348 TpHandleSet *
tp_handle_set_copy(const TpHandleSet * other)349 tp_handle_set_copy (const TpHandleSet *other)
350 {
351   g_return_val_if_fail (other != NULL, NULL);
352 
353   return tp_handle_set_new_from_intset (other->repo, other->intset);
354 }
355 
356 /**
357  * tp_handle_set_new_containing: (skip)
358  * @repo: #TpHandleRepoIface that holds the handles to be reffed by this set
359  * @handle: a valid handle
360  *
361  * Creates a new #TpHandleSet from a specified handle repository and single
362  * handle.
363  *
364  * Returns: (transfer full): A new #TpHandleSet
365  *
366  * Since: 0.13.0
367  */
368 TpHandleSet *
tp_handle_set_new_containing(TpHandleRepoIface * repo,TpHandle handle)369 tp_handle_set_new_containing (TpHandleRepoIface *repo,
370     TpHandle handle)
371 {
372   TpHandleSet *set = tp_handle_set_new (repo);
373 
374   tp_handle_set_add (set, handle);
375   return set;
376 }
377 
378 /**
379  * tp_handle_set_new_from_intset: (skip)
380  * @repo: #TpHandleRepoIface that holds the handles to be reffed by this set
381  * @intset: a set of handles, which must all be valid
382  *
383  * Creates a new #TpHandleSet from a specified handle repository and
384  * set of handles.
385  *
386  * Returns: (transfer full): A new #TpHandleSet
387  *
388  * Since: 0.13.0
389  */
390 TpHandleSet *
tp_handle_set_new_from_intset(TpHandleRepoIface * repo,const TpIntset * intset)391 tp_handle_set_new_from_intset (TpHandleRepoIface *repo,
392     const TpIntset *intset)
393 {
394   TpHandleSet *set;
395 
396   g_return_val_if_fail (repo != NULL, NULL);
397   g_return_val_if_fail (intset != NULL, NULL);
398 
399   set = g_slice_new0 (TpHandleSet);
400   set->repo = repo;
401   set->intset = tp_intset_copy (intset);
402   return set;
403 }
404 
405 /**
406  * tp_handle_set_update: (skip)
407  * @set: a #TpHandleSet to update
408  * @add: a #TpIntset of handles to add
409  *
410  * Add a set of handles to a handle set, referencing those which are not
411  * already members. The TpIntset returned must be freed with tp_intset_destroy.
412  *
413  * Returns: the handles which were added (some subset of @add)
414  */
415 TpIntset *
tp_handle_set_update(TpHandleSet * set,const TpIntset * add)416 tp_handle_set_update (TpHandleSet *set, const TpIntset *add)
417 {
418   TpIntset *ret, *tmp;
419 
420   g_return_val_if_fail (set != NULL, NULL);
421   g_return_val_if_fail (add != NULL, NULL);
422 
423   /* reference each of ADD - CURRENT */
424   ret = tp_intset_difference (add, set->intset);
425 
426   /* update CURRENT to be the union of CURRENT and ADD */
427   tmp = tp_intset_union (add, set->intset);
428   tp_intset_destroy (set->intset);
429   set->intset = tmp;
430 
431   return ret;
432 }
433 
434 /**
435  * tp_handle_set_difference_update: (skip)
436  * @set: a #TpHandleSet to update
437  * @remove: a #TpIntset of handles to remove
438  *
439  * Remove a set of handles from a handle set, dereferencing those which are
440  * members. The TpIntset returned must be freed with tp_intset_destroy.
441  *
442  * If you want to be able to inspect the handles in the set returned,
443  * you must ensure that this function does not cause their refcount to drop
444  * to zero, for instance by temporarily taking a reference to all the
445  * handles in @remove, calling this function, doing something with the
446  * result and discarding the temporary references.
447  *
448  * Returns: the handles which were dereferenced and removed (some subset
449  *  of @remove).
450  */
451 TpIntset *
tp_handle_set_difference_update(TpHandleSet * set,const TpIntset * remove)452 tp_handle_set_difference_update (TpHandleSet *set, const TpIntset *remove)
453 {
454   TpIntset *ret, *tmp;
455 
456   g_return_val_if_fail (set != NULL, NULL);
457   g_return_val_if_fail (remove != NULL, NULL);
458 
459   /* dereference each of REMOVE n CURRENT */
460   ret = tp_intset_intersection (remove, set->intset);
461 
462   /* update CURRENT to be CURRENT - REMOVE */
463   tmp = tp_intset_difference (set->intset, remove);
464   tp_intset_destroy (set->intset);
465   set->intset = tmp;
466 
467   return ret;
468 }
469 
470 /**
471  * tp_handle_set_dump:
472  * @self: a handle set
473  *
474  * Format a #TpHandleSet for debug output.
475  *
476  * Returns: (transfer full) (type utf8): a string representation of the
477  *  handle set suitable for debug output
478  */
479 gchar *
tp_handle_set_dump(const TpHandleSet * self)480 tp_handle_set_dump (const TpHandleSet *self)
481 {
482   TpIntsetFastIter iter;
483   guint handle;
484   GString *string = g_string_new ("{ ");
485 
486   tp_intset_fast_iter_init (&iter, self->intset);
487 
488   while (tp_intset_fast_iter_next (&iter, &handle))
489     {
490       if (handle == 0 || !tp_handle_is_valid (self->repo, handle, NULL))
491         {
492           g_string_append_printf (string, "#%u <invalid>, ", handle);
493         }
494       else
495         {
496           g_string_append_printf (string, "#%u '%s', ", handle,
497               tp_handle_inspect (self->repo, handle));
498         }
499     }
500 
501   g_string_append_c (string, '}');
502 
503   return g_string_free (string, FALSE);
504 }
505