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