Add "str_map_unset_iter"

This commit is contained in:
Přemysl Eric Janouch 2015-07-14 22:17:01 +02:00
parent 7fa873fb96
commit fb50290341
2 changed files with 115 additions and 36 deletions

127
liberty.c
View File

@ -682,16 +682,8 @@ struct str_map
/// Callback that transforms all key values for storage and comparison;
/// has to behave exactly like strxfrm().
size_t (*key_xfrm) (char *dest, const char *src, size_t n);
};
// As long as you don't remove the current entry, you can modify the map.
// Use `link' directly to access the data.
struct str_map_iter
{
struct str_map *map; ///< The map we're iterating
size_t next_index; ///< Next table index to search
struct str_map_link *link; ///< Current link
bool shrink_lock; ///< Lock against autoshrinking
};
#define STR_MAP_MIN_ALLOC 16
@ -706,6 +698,7 @@ str_map_init (struct str_map *self)
self->free = NULL;
self->key_xfrm = NULL;
self->map = xcalloc (self->alloc, sizeof *self->map);
self->shrink_lock = false;
}
static void
@ -735,29 +728,6 @@ str_map_free (struct str_map *self)
self->map = NULL;
}
static void
str_map_iter_init (struct str_map_iter *self, struct str_map *map)
{
self->map = map;
self->next_index = 0;
self->link = NULL;
}
static void *
str_map_iter_next (struct str_map_iter *self)
{
struct str_map *map = self->map;
if (self->link)
self->link = self->link->next;
while (!self->link)
{
if (self->next_index >= map->alloc)
return NULL;
self->link = map->map[self->next_index++];
}
return self->link->data;
}
static uint64_t
str_map_hash (const char *s, size_t len)
{
@ -805,6 +775,21 @@ str_map_resize (struct str_map *self, size_t new_size)
free (old_map);
}
static void
str_map_shrink (struct str_map *self)
{
if (self->shrink_lock)
return;
// The array should be at least 1/4 full
size_t new_alloc = self->alloc;
while (self->len < (new_alloc >> 2)
&& new_alloc >= (STR_MAP_MIN_ALLOC << 1))
new_alloc >>= 1;
if (new_alloc != self->alloc)
str_map_resize (self, new_alloc);
}
static void
str_map_set_real (struct str_map *self, const char *key, void *value)
{
@ -829,10 +814,7 @@ str_map_set_real (struct str_map *self, const char *key, void *value)
free (iter);
self->len--;
// The array should be at least 1/4 full
if (self->alloc >= (STR_MAP_MIN_ALLOC << 2)
&& self->len < (self->alloc >> 2))
str_map_resize (self, self->alloc >> 2);
str_map_shrink (self);
return;
}
@ -890,6 +872,79 @@ str_map_find (struct str_map *self, const char *key)
return str_map_find_real (self, tmp);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// This iterator is intended for accessing and eventually adding links.
// Use `link' directly to access the data.
struct str_map_iter
{
struct str_map *map; ///< The map we're iterating
size_t next_index; ///< Next table index to search
struct str_map_link *link; ///< Current link
};
static void
str_map_iter_init (struct str_map_iter *self, struct str_map *map)
{
self->map = map;
self->next_index = 0;
self->link = NULL;
}
static void *
str_map_iter_next (struct str_map_iter *self)
{
struct str_map *map = self->map;
if (self->link)
self->link = self->link->next;
while (!self->link)
{
if (self->next_index >= map->alloc)
return NULL;
self->link = map->map[self->next_index++];
}
return self->link->data;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// This iterator is intended for accessing and eventually removing links.
// Use `link' directly to access the data.
struct str_map_unset_iter
{
struct str_map_iter iter; ///< Regular iterator
struct str_map_link *link; ///< Current link
struct str_map_link *next; ///< Next link
};
static void
str_map_unset_iter_init (struct str_map_unset_iter *self, struct str_map *map)
{
str_map_iter_init (&self->iter, map);
self->iter.map->shrink_lock = true;
(void) str_map_iter_next (&self->iter);
self->next = self->iter.link;
}
static void *
str_map_unset_iter_next (struct str_map_unset_iter *self)
{
if (!(self->link = self->next))
return NULL;
(void) str_map_iter_next (&self->iter);
self->next = self->iter.link;
return self->link->data;
}
static void
str_map_unset_iter_free (struct str_map_unset_iter *self)
{
self->iter.map->shrink_lock = false;
str_map_shrink (self->iter.map);
}
// --- File descriptor utilities -----------------------------------------------
static void

View File

@ -306,6 +306,30 @@ test_str_map (void)
soft_assert (*b == 1);
free_counter (a);
free_counter (b);
// Iterator test with a high number of items
str_map_init (&m);
m.free = free;
for (size_t i = 0; i < 100 * 100; i++)
{
char *x = xstrdup_printf ("%zu", i);
str_map_set (&m, x, x);
}
struct str_map_unset_iter unset_iter;
str_map_unset_iter_init (&unset_iter, &m);
while ((str_map_unset_iter_next (&unset_iter)))
{
unsigned long x;
hard_assert (xstrtoul (&x, unset_iter.link->key, 10));
if (x >= 100)
str_map_set (&m, unset_iter.link->key, NULL);
}
str_map_unset_iter_free (&unset_iter);
soft_assert (m.len == 100);
str_map_free (&m);
}
static void