libctf, next, hash: add dynhash and dynset _next iteration
authorNick Alcock <nick.alcock@oracle.com>
Wed, 3 Jun 2020 15:36:18 +0000 (16:36 +0100)
committerNick Alcock <nick.alcock@oracle.com>
Wed, 22 Jul 2020 16:57:51 +0000 (17:57 +0100)
This lets you iterate over dynhashes and dynsets using the _next API.
dynhashes can be iterated over in sorted order, which works by
populating an array of key/value pairs using ctf_dynhash_next itself,
then sorting it with qsort.

Convenience inline functions named ctf_dyn{hash,set}_cnext are also
provided that take (-> return) const keys and values.

libctf/
* ctf-impl.h (ctf_next_hkv_t): New, kv-pairs passed to
sorting functions.
(ctf_next_t) <u.ctn_sorted_hkv>: New, sorted kv-pairs for
ctf_dynhash_next_sorted.
<cu.ctn_h>: New, pointer to the dynhash under iteration.
<cu.ctn_s>: New, pointer to the dynset under iteration.
(ctf_hash_sort_f): Sorting function passed to...
(ctf_dynhash_next_sorted): ... this new function.
(ctf_dynhash_next): New.
(ctf_dynset_next): New.
* ctf-inlines.h (ctf_dynhash_cnext_sorted): New.
(ctf_dynhash_cnext): New.
(ctf_dynset_cnext): New.
* ctf-hash.c (ctf_dynhash_next_sorted): New.
(ctf_dynhash_next): New.
(ctf_dynset_next): New.
* ctf-util.c (ctf_next_destroy): Free the u.ctn_sorted_hkv if
needed.
(ctf_next_copy): Alloc-and-copy the u.ctn_sorted_hkv if needed.

libctf/ChangeLog
libctf/ctf-hash.c
libctf/ctf-impl.h
libctf/ctf-inlines.h
libctf/ctf-util.c

index 3009f4ed9e7687e0708c2833b8fae9703683b588..779f7b1cc873cc35e4a5b4cc26f38e574ba19312 100644 (file)
@@ -1,3 +1,25 @@
+2020-07-22  Nick Alcock  <nick.alcock@oracle.com>
+
+       * ctf-impl.h (ctf_next_hkv_t): New, kv-pairs passed to
+       sorting functions.
+       (ctf_next_t) <u.ctn_sorted_hkv>: New, sorted kv-pairs for
+       ctf_dynhash_next_sorted.
+       <cu.ctn_h>: New, pointer to the dynhash under iteration.
+       <cu.ctn_s>: New, pointer to the dynset under iteration.
+       (ctf_hash_sort_f): Sorting function passed to...
+       (ctf_dynhash_next_sorted): ... this new function.
+       (ctf_dynhash_next): New.
+       (ctf_dynset_next): New.
+       * ctf-inlines.h (ctf_dynhash_cnext_sorted): New.
+       (ctf_dynhash_cnext): New.
+       (ctf_dynset_cnext): New.
+       * ctf-hash.c (ctf_dynhash_next_sorted): New.
+       (ctf_dynhash_next): New.
+       (ctf_dynset_next): New.
+       * ctf-util.c (ctf_next_destroy): Free the u.ctn_sorted_hkv if
+       needed.
+       (ctf_next_copy): Alloc-and-copy the u.ctn_sorted_hkv if needed.
+
 2020-07-22  Nick Alcock  <nick.alcock@oracle.com>
 
        * ctf-impl.h (ctf_next): New.
index 7eba494a51d5af7ce7e3201cf97da7b4fe004595..a026ef225da8b6779ca4a40c1742466d57920ec5 100644 (file)
@@ -378,6 +378,163 @@ ctf_dynhash_iter_remove (ctf_dynhash_t *hp, ctf_hash_iter_remove_f fun,
   htab_traverse (hp->htab, ctf_hashtab_traverse_remove, &arg);
 }
 
+/* Traverse a dynhash in arbitrary order, in _next iterator form.
+
+   Mutating the dynhash while iterating is not supported (just as it isn't for
+   htab_traverse).
+
+   Note: unusually, this returns zero on success and a *positive* value on
+   error, because it does not take an fp, taking an error pointer would be
+   incredibly clunky, and nearly all error-handling ends up stuffing the result
+   of this into some sort of errno or ctf_errno, which is invariably
+   positive.  So doing this simplifies essentially all callers.  */
+int
+ctf_dynhash_next (ctf_dynhash_t *h, ctf_next_t **it, void **key, void **value)
+{
+  ctf_next_t *i = *it;
+  ctf_helem_t *slot;
+
+  if (!i)
+    {
+      size_t size = htab_size (h->htab);
+
+      /* If the table has too many entries to fit in an ssize_t, just give up.
+        This might be spurious, but if any type-related hashtable has ever been
+        nearly as large as that then something very odd is going on.  */
+      if (((ssize_t) size) < 0)
+       return EDOM;
+
+      if ((i = ctf_next_create ()) == NULL)
+       return ENOMEM;
+
+      i->u.ctn_hash_slot = h->htab->entries;
+      i->cu.ctn_h = h;
+      i->ctn_n = 0;
+      i->ctn_size = (ssize_t) size;
+      i->ctn_iter_fun = (void (*) (void)) ctf_dynhash_next;
+      *it = i;
+    }
+
+  if ((void (*) (void)) ctf_dynhash_next != i->ctn_iter_fun)
+    return ECTF_NEXT_WRONGFUN;
+
+  if (h != i->cu.ctn_h)
+    return ECTF_NEXT_WRONGFP;
+
+  if ((ssize_t) i->ctn_n == i->ctn_size)
+    goto hash_end;
+
+  while ((ssize_t) i->ctn_n < i->ctn_size
+        && (*i->u.ctn_hash_slot == HTAB_EMPTY_ENTRY
+            || *i->u.ctn_hash_slot == HTAB_DELETED_ENTRY))
+    {
+      i->u.ctn_hash_slot++;
+      i->ctn_n++;
+    }
+
+  if ((ssize_t) i->ctn_n == i->ctn_size)
+    goto hash_end;
+
+  slot = *i->u.ctn_hash_slot;
+
+  if (key)
+    *key = slot->key;
+  if (value)
+    *value = slot->value;
+
+  i->u.ctn_hash_slot++;
+  i->ctn_n++;
+
+  return 0;
+
+ hash_end:
+  ctf_next_destroy (i);
+  *it = NULL;
+  return ECTF_NEXT_END;
+}
+
+/* Traverse a sorted dynhash, in _next iterator form.
+
+   See ctf_dynhash_next for notes on error returns, etc.
+
+   Sort keys before iterating over them using the SORT_FUN and SORT_ARG.
+
+   If SORT_FUN is null, thunks to ctf_dynhash_next.  */
+int
+ctf_dynhash_next_sorted (ctf_dynhash_t *h, ctf_next_t **it, void **key,
+                        void **value, ctf_hash_sort_f sort_fun, void *sort_arg)
+{
+  ctf_next_t *i = *it;
+
+  if (sort_fun == NULL)
+    return ctf_dynhash_next (h, it, key, value);
+
+  if (!i)
+    {
+      size_t els = ctf_dynhash_elements (h);
+      ctf_next_t *accum_i = NULL;
+      void *key, *value;
+      int err;
+      ctf_next_hkv_t *walk;
+
+      if (((ssize_t) els) < 0)
+       return EDOM;
+
+      if ((i = ctf_next_create ()) == NULL)
+       return ENOMEM;
+
+      if ((i->u.ctn_sorted_hkv = calloc (els, sizeof (ctf_next_hkv_t))) == NULL)
+       {
+         ctf_next_destroy (i);
+         return ENOMEM;
+       }
+      walk = i->u.ctn_sorted_hkv;
+
+      i->cu.ctn_h = h;
+
+      while ((err = ctf_dynhash_next (h, &accum_i, &key, &value)) == 0)
+       {
+         walk->hkv_key = key;
+         walk->hkv_value = value;
+         walk++;
+       }
+      if (err != ECTF_NEXT_END)
+       {
+         ctf_next_destroy (i);
+         return err;
+       }
+
+      if (sort_fun)
+         ctf_qsort_r (i->u.ctn_sorted_hkv, els, sizeof (ctf_next_hkv_t),
+                      (int (*) (const void *, const void *, void *)) sort_fun,
+                      sort_arg);
+      i->ctn_n = 0;
+      i->ctn_size = (ssize_t) els;
+      i->ctn_iter_fun = (void (*) (void)) ctf_dynhash_next_sorted;
+      *it = i;
+    }
+
+  if ((void (*) (void)) ctf_dynhash_next_sorted != i->ctn_iter_fun)
+    return ECTF_NEXT_WRONGFUN;
+
+  if (h != i->cu.ctn_h)
+    return ECTF_NEXT_WRONGFP;
+
+  if ((ssize_t) i->ctn_n == i->ctn_size)
+    {
+      ctf_next_destroy (i);
+      *it = NULL;
+      return ECTF_NEXT_END;
+    }
+
+  if (key)
+    *key = i->u.ctn_sorted_hkv[i->ctn_n].hkv_key;
+  if (value)
+    *value = i->u.ctn_sorted_hkv[i->ctn_n].hkv_value;
+  i->ctn_n++;
+  return 0;
+}
+
 void
 ctf_dynhash_destroy (ctf_dynhash_t *hp)
 {
@@ -515,6 +672,74 @@ ctf_dynset_lookup_any (ctf_dynset_t *hp)
   return NULL;
 }
 
+/* Traverse a dynset in arbitrary order, in _next iterator form.
+
+   Otherwise, just like ctf_dynhash_next.  */
+int
+ctf_dynset_next (ctf_dynset_t *hp, ctf_next_t **it, void **key)
+{
+  struct htab *htab = (struct htab *) hp;
+  ctf_next_t *i = *it;
+  void *slot;
+
+  if (!i)
+    {
+      size_t size = htab_size (htab);
+
+      /* If the table has too many entries to fit in an ssize_t, just give up.
+        This might be spurious, but if any type-related hashtable has ever been
+        nearly as large as that then somthing very odd is going on.  */
+
+      if (((ssize_t) size) < 0)
+       return EDOM;
+
+      if ((i = ctf_next_create ()) == NULL)
+       return ENOMEM;
+
+      i->u.ctn_hash_slot = htab->entries;
+      i->cu.ctn_s = hp;
+      i->ctn_n = 0;
+      i->ctn_size = (ssize_t) size;
+      i->ctn_iter_fun = (void (*) (void)) ctf_dynset_next;
+      *it = i;
+    }
+
+  if ((void (*) (void)) ctf_dynset_next != i->ctn_iter_fun)
+    return ECTF_NEXT_WRONGFUN;
+
+  if (hp != i->cu.ctn_s)
+    return ECTF_NEXT_WRONGFP;
+
+  if ((ssize_t) i->ctn_n == i->ctn_size)
+    goto set_end;
+
+  while ((ssize_t) i->ctn_n < i->ctn_size
+        && (*i->u.ctn_hash_slot == HTAB_EMPTY_ENTRY
+            || *i->u.ctn_hash_slot == HTAB_DELETED_ENTRY))
+    {
+      i->u.ctn_hash_slot++;
+      i->ctn_n++;
+    }
+
+  if ((ssize_t) i->ctn_n == i->ctn_size)
+    goto set_end;
+
+  slot = *i->u.ctn_hash_slot;
+
+  if (key)
+    *key = internal_to_key (slot);
+
+  i->u.ctn_hash_slot++;
+  i->ctn_n++;
+
+  return 0;
+
+ set_end:
+  ctf_next_destroy (i);
+  *it = NULL;
+  return ECTF_NEXT_END;
+}
+
 /* ctf_hash, used for fixed-size maps from const char * -> ctf_id_t without
    removal.  This is a straight cast of a hashtab.  */
 
index b18ba9465244e4aa83a2a12d2fc9e4b4d77550a2..eea5204c51dd6d9ebdca02ded6302bec897180d4 100644 (file)
@@ -328,6 +328,13 @@ struct ctf_archive_internal
 
 /* Iterator state for the *_next() functions.  */
 
+/* A single hash key/value pair.  */
+typedef struct ctf_next_hkv
+{
+  void *hkv_key;
+  void *hkv_value;
+} ctf_next_hkv_t;
+
 struct ctf_next
 {
   void (*ctn_iter_fun) (void);
@@ -346,13 +353,17 @@ struct ctf_next
     const ctf_dmdef_t *ctn_dmd;
     const ctf_enum_t *ctn_en;
     const ctf_dvdef_t *ctn_dvd;
+    ctf_next_hkv_t *ctn_sorted_hkv;
+    void **ctn_hash_slot;
   } u;
   /* This union is of various sorts of container we can iterate over:
-     currently dictionaries and archives.  */
+     currently dictionaries and archives, dynhashes, and dynsets.  */
   union
   {
     const ctf_file_t *ctn_fp;
     const ctf_archive_t *ctn_arc;
+    const ctf_dynhash_t *ctn_h;
+    const ctf_dynset_t *ctn_s;
   } cu;
 };
 
@@ -411,6 +422,8 @@ typedef void (*ctf_hash_free_fun) (void *);
 typedef void (*ctf_hash_iter_f) (void *key, void *value, void *arg);
 typedef int (*ctf_hash_iter_remove_f) (void *key, void *value, void *arg);
 typedef int (*ctf_hash_iter_find_f) (void *key, void *value, void *arg);
+typedef int (*ctf_hash_sort_f) (const ctf_next_hkv_t *, const ctf_next_hkv_t *,
+                               void *arg);
 
 extern ctf_hash_t *ctf_hash_create (unsigned long, ctf_hash_fun, ctf_hash_eq_fun);
 extern int ctf_hash_insert_type (ctf_hash_t *, ctf_file_t *, uint32_t, uint32_t);
@@ -434,6 +447,11 @@ extern void ctf_dynhash_iter_remove (ctf_dynhash_t *, ctf_hash_iter_remove_f,
                                     void *);
 extern void *ctf_dynhash_iter_find (ctf_dynhash_t *, ctf_hash_iter_find_f,
                                    void *);
+extern int ctf_dynhash_next (ctf_dynhash_t *, ctf_next_t **,
+                            void **key, void **value);
+extern int ctf_dynhash_next_sorted (ctf_dynhash_t *, ctf_next_t **,
+                                   void **key, void **value, ctf_hash_sort_f,
+                                   void *);
 
 extern ctf_dynset_t *ctf_dynset_create (htab_hash, htab_eq, ctf_hash_free_fun);
 extern int ctf_dynset_insert (ctf_dynset_t *, void *);
@@ -442,6 +460,7 @@ extern void ctf_dynset_destroy (ctf_dynset_t *);
 extern void *ctf_dynset_lookup (ctf_dynset_t *, const void *);
 extern int ctf_dynset_exists (ctf_dynset_t *, const void *key,
                              const void **orig_key);
+extern int ctf_dynset_next (ctf_dynset_t *, ctf_next_t **, void **key);
 extern void *ctf_dynset_lookup_any (ctf_dynset_t *);
 
 #define        ctf_list_prev(elem)     ((void *)(((ctf_list_t *)(elem))->l_prev))
index 3b912bd9a25f7a11181cb3971e97e8dda1f98ab8..a35b6cd5a77f2c60970eb083344eb9a3a7e37ca1 100644 (file)
@@ -46,6 +46,21 @@ ctf_forwardable_kind (int kind)
   return (kind == CTF_K_STRUCT || kind == CTF_K_UNION || kind == CTF_K_ENUM);
 }
 
+static inline int
+ctf_dynhash_cnext_sorted (ctf_dynhash_t *h, ctf_next_t **i, const void **key,
+                         const void **value, ctf_hash_sort_f sort_fun,
+                         void *sort_arg)
+{
+  return ctf_dynhash_next_sorted (h, i, (void **) key, (void **) value,
+                                 sort_fun, sort_arg);
+}
+
+static inline int
+ctf_dynhash_cnext (ctf_dynhash_t *h, ctf_next_t **it,
+                 const void **key, const void **value)
+{
+  return ctf_dynhash_next (h, it, (void **) key, (void **) value);
+}
 
 static inline int
 ctf_dynhash_cinsert (ctf_dynhash_t *h, const void *k, const void *v)
@@ -53,6 +68,12 @@ ctf_dynhash_cinsert (ctf_dynhash_t *h, const void *k, const void *v)
   return ctf_dynhash_insert (h, (void *) k, (void *) v);
 }
 
+static inline int
+ctf_dynset_cnext (ctf_dynset_t *h, ctf_next_t **it, const void **key)
+{
+  return ctf_dynset_next (h, it, (void **) key);
+}
+
 static inline int
 ctf_dynset_cinsert (ctf_dynset_t *h, const void *k)
 {
index 5abea6a70bb126f45712f11ff06be9e093f5e8b5..c113dfc559640436b2b95cb001ff1235317456ea 100644 (file)
@@ -187,6 +187,11 @@ ctf_next_create (void)
 void
 ctf_next_destroy (ctf_next_t *i)
 {
+  if (i == NULL)
+    return;
+
+  if (i->ctn_iter_fun == (void (*) (void)) ctf_dynhash_next_sorted)
+    free (i->u.ctn_sorted_hkv);
   free (i);
 }
 
@@ -200,5 +205,17 @@ ctf_next_copy (ctf_next_t *i)
   if ((i2 = ctf_next_create()) == NULL)
     return NULL;
   memcpy (i2, i, sizeof (struct ctf_next));
+
+  if (i2->ctn_iter_fun == (void (*) (void)) ctf_dynhash_next_sorted)
+    {
+      size_t els = ctf_dynhash_elements ((ctf_dynhash_t *) i->cu.ctn_h);
+      if ((i2->u.ctn_sorted_hkv = calloc (els, sizeof (ctf_next_hkv_t))) == NULL)
+       {
+         free (i2);
+         return NULL;
+       }
+      memcpy (i2->u.ctn_sorted_hkv, i->u.ctn_sorted_hkv,
+             els * sizeof (ctf_next_hkv_t));
+    }
   return i2;
 }