summaryrefslogtreecommitdiff
path: root/telepathy-glib/intset.c
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2010-05-24 18:10:46 +0100
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2010-05-24 18:10:46 +0100
commit76908456689ab08e444aa25c5791362d4ad445f4 (patch)
treebfc33cfa58e4a8ce16c16916d9f83a47059fa456 /telepathy-glib/intset.c
parent2222868effd164390e6c2b9c54a4ebf3fa89fc3a (diff)
downloadtelepathy-glib-76908456689ab08e444aa25c5791362d4ad445f4.tar.gz
tp_intset_copy etc.: set #TpIntSet.largest_ever correctly on the copy
This fixes a regression in which TpIntSetIter terminated too early (but TpIntSetFastIter was already correct, because it doesn't iterate in order).
Diffstat (limited to 'telepathy-glib/intset.c')
-rw-r--r--telepathy-glib/intset.c32
1 files changed, 31 insertions, 1 deletions
diff --git a/telepathy-glib/intset.c b/telepathy-glib/intset.c
index 7ffa0e246..30d186d12 100644
--- a/telepathy-glib/intset.c
+++ b/telepathy-glib/intset.c
@@ -160,6 +160,25 @@ struct _TpIntSet
guint largest_ever;
};
+/*
+ * Update @set's largest_ever member to be at least as large as everything
+ * that could be encoded in the hash table key @key.
+ *
+ * We could use g_bit_nth_msf (value, BITFIELD_BITS) instead of LOW_MASK if we
+ * wanted to get largest_ever exactly right, but we just need something
+ * reasonable to make TpIntSetIter terminate early, and carrying on for up to
+ * BITFIELD_BITS extra iterations isn't a problem.
+ */
+static inline void
+intset_update_largest_ever (TpIntSet *set,
+ gpointer key)
+{
+ guint upper_bound = GPOINTER_TO_UINT (key) | LOW_MASK;
+
+ if (set->largest_ever < upper_bound)
+ set->largest_ever = upper_bound;
+}
+
/**
* tp_intset_sized_new:
* @size: ignored (it was previously 1 more than the largest integer you
@@ -548,6 +567,7 @@ tp_intset_copy (const TpIntSet *orig)
while (g_hash_table_iter_next (&iter, &key, &value))
{
+ intset_update_largest_ever (ret, key);
g_hash_table_insert (ret->table, key, value);
}
@@ -581,10 +601,14 @@ tp_intset_intersection (const TpIntSet *left, const TpIntSet *right)
while (g_hash_table_iter_next (&iter, &key, &value))
{
gsize v = GPOINTER_TO_SIZE (value);
+
v &= GPOINTER_TO_SIZE (g_hash_table_lookup (right->table, key));
if (v != 0)
- g_hash_table_insert (ret->table, key, GSIZE_TO_POINTER (v));
+ {
+ intset_update_largest_ever (ret, key);
+ g_hash_table_insert (ret->table, key, GSIZE_TO_POINTER (v));
+ }
}
return ret;
@@ -617,6 +641,8 @@ tp_intset_union (const TpIntSet *left, const TpIntSet *right)
while (g_hash_table_iter_next (&iter, &key, &value))
{
gsize v = GPOINTER_TO_SIZE (value);
+
+ intset_update_largest_ever (ret, key);
v |= GPOINTER_TO_SIZE (g_hash_table_lookup (ret->table, key));
g_hash_table_insert (ret->table, key, GSIZE_TO_POINTER (v));
}
@@ -656,6 +682,8 @@ tp_intset_difference (const TpIntSet *left, const TpIntSet *right)
gsize v = GPOINTER_TO_SIZE (value);
v = (GPOINTER_TO_SIZE (g_hash_table_lookup (ret->table, key))) & ~v;
+ /* No need to update largest_ever here - we're only deleting members. */
+
if (v == 0)
g_hash_table_remove (ret->table, key);
else
@@ -697,6 +725,8 @@ tp_intset_symmetric_difference (const TpIntSet *left, const TpIntSet *right)
gsize v = GPOINTER_TO_SIZE (value);
v = v ^ GPOINTER_TO_SIZE (g_hash_table_lookup (ret->table, key));
+ /* No need to update largest_ever here - we're only deleting members. */
+
if (v == 0)
g_hash_table_remove (ret->table, key);
else