diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2010-05-24 18:10:46 +0100 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2010-05-24 18:10:46 +0100 |
commit | 76908456689ab08e444aa25c5791362d4ad445f4 (patch) | |
tree | bfc33cfa58e4a8ce16c16916d9f83a47059fa456 | |
parent | 2222868effd164390e6c2b9c54a4ebf3fa89fc3a (diff) |
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).
-rw-r--r-- | telepathy-glib/intset.c | 32 |
1 files changed, 31 insertions, 1 deletions
diff --git a/telepathy-glib/intset.c b/telepathy-glib/intset.c index 7ffa0e24..30d186d1 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 |