bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 1/2] hash: set errno on failure


From: Paul Eggert
Subject: [PATCH 1/2] hash: set errno on failure
Date: Sun, 13 Aug 2023 10:15:20 -0700

* lib/hash.c: Include errno.h.
(compute_bucket_size, hash_initialize, hash_rehash)
(hash_insert_if_absent): Set errno reliably on failure.
(hash_free): Preserve errno, like plain 'free'.
* modules/hash (Depends-on): Depend on calloc-posix,
free-posix, malloc-posix, so that errno is set reliably.
---
 ChangeLog    | 10 ++++++++++
 lib/hash.c   | 24 +++++++++++++++++++-----
 lib/hash.h   | 21 ++++++++++++---------
 modules/hash |  3 +++
 4 files changed, 44 insertions(+), 14 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 4fec15de51..3a03afaea1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2023-08-13  Paul Eggert  <eggert@cs.ucla.edu>
+
+       hash: set errno on failure
+       * lib/hash.c: Include errno.h.
+       (compute_bucket_size, hash_initialize, hash_rehash)
+       (hash_insert_if_absent): Set errno reliably on failure.
+       (hash_free): Preserve errno, like plain 'free'.
+       * modules/hash (Depends-on): Depend on calloc-posix,
+       free-posix, malloc-posix, so that errno is set reliably.
+
 2023-08-13  Bruno Haible  <bruno@clisp.org>
 
        readutmp, boot-time: Fix compilation error on old Android.
diff --git a/lib/hash.c b/lib/hash.c
index 918aa0d1c3..a004885e28 100644
--- a/lib/hash.c
+++ b/lib/hash.c
@@ -29,6 +29,7 @@
 #include "bitrotate.h"
 #include "xalloc-oversized.h"
 
+#include <errno.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -499,13 +500,17 @@ compute_bucket_size (size_t candidate, const Hash_tuning 
*tuning)
     {
       float new_candidate = candidate / tuning->growth_threshold;
       if ((float) SIZE_MAX <= new_candidate)
-        return 0;
+        goto nomem;
       candidate = new_candidate;
     }
   candidate = next_prime (candidate);
   if (xalloc_oversized (candidate, sizeof (struct hash_entry *)))
-    return 0;
+    goto nomem;
   return candidate;
+
+ nomem:
+  errno = ENOMEM;
+  return 0;
 }
 
 Hash_table *
@@ -534,6 +539,7 @@ hash_initialize (size_t candidate, const Hash_tuning 
*tuning,
          if the user provides invalid tuning options, we silently revert to
          using the defaults, and ignore further request to change the tuning
          options.  */
+      errno = EINVAL;
       goto fail;
     }
 
@@ -607,6 +613,7 @@ hash_free (Hash_table *table)
   struct hash_entry *bucket;
   struct hash_entry *cursor;
   struct hash_entry *next;
+  int err = errno;
 
   /* Call the user data_freer function.  */
   if (table->data_freer && table->n_entries)
@@ -649,6 +656,8 @@ hash_free (Hash_table *table)
   /* Free the remainder of the hash table structure.  */
   free (table->bucket);
   free (table);
+
+  errno = err;
 }
 
 /* Insertion and deletion.  */
@@ -762,8 +771,8 @@ hash_find_entry (Hash_table *table, const void *entry,
 /* Internal helper, to move entries from SRC to DST.  Both tables must
    share the same free entry list.  If SAFE, only move overflow
    entries, saving bucket heads for later, so that no allocations will
-   occur.  Return false if the free entry list is exhausted and an
-   allocation fails.  */
+   occur.  Return false (setting errno) if the free entry list is
+   exhausted and an allocation fails.  */
 
 static bool
 transfer_entries (Hash_table *dst, Hash_table *src, bool safe)
@@ -910,12 +919,14 @@ hash_rehash (Hash_table *table, size_t candidate)
      passes.  Two passes give worse cache performance and takes
      longer, but at this point, we're already out of memory, so slow
      and safe is better than failure.  */
+  int err = errno;
   table->free_entry_list = new_table->free_entry_list;
   if (! (transfer_entries (table, new_table, true)
          && transfer_entries (table, new_table, false)))
     abort ();
   /* table->n_entries already holds its value.  */
   free (new_table->bucket);
+  errno = err;
   return false;
 }
 
@@ -962,7 +973,10 @@ hash_insert_if_absent (Hash_table *table, void const 
*entry,
                 * tuning->growth_threshold));
 
           if ((float) SIZE_MAX <= candidate)
-            return -1;
+            {
+              errno = ENOMEM;
+              return -1;
+            }
 
           /* If the rehash fails, arrange to return NULL.  */
           if (!hash_rehash (table, candidate))
diff --git a/lib/hash.h b/lib/hash.h
index 54d3f22f69..af07242ff4 100644
--- a/lib/hash.h
+++ b/lib/hash.h
@@ -148,7 +148,7 @@ typedef void (*Hash_data_freer) (void *entry);
 /* Reclaim all storage associated with a hash table.  If a data_freer
    function has been supplied by the user when the hash table was created,
    this function applies it to the data of each entry before freeing that
-   entry.  */
+   entry.  This function preserves errno, like 'free'.  */
 extern void hash_free (Hash_table *table);
 
 /* Allocate and return a new hash table, or NULL upon failure.  The initial
@@ -183,7 +183,9 @@ extern void hash_free (Hash_table *table);
    You should specify this function only if you want these functions to free
    all of your 'data' data.  This is typically the case when your data is
    simply an auxiliary struct that you have malloc'd to aggregate several
-   values.  */
+   values.
+
+   Set errno on failure; otherwise errno is unspecified.  */
 _GL_ATTRIBUTE_NODISCARD
 extern Hash_table *hash_initialize (size_t candidate,
                                     const Hash_tuning *tuning,
@@ -192,7 +194,7 @@ extern Hash_table *hash_initialize (size_t candidate,
                                     Hash_data_freer data_freer)
   _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC (hash_free, 1);
 
-/* Same as hash_initialize, but invokes xalloc_die on memory exhaustion.  */
+/* Like hash_initialize, but invokes xalloc_die instead of returning NULL.  */
 /* This function is defined by module 'xhash'.  */
 _GL_ATTRIBUTE_NODISCARD
 extern Hash_table *hash_xinitialize (size_t candidate,
@@ -218,25 +220,26 @@ extern void hash_clear (Hash_table *table);
    the table may receive at least CANDIDATE different user entries, including
    those already in the table, before any other growth of the hash table size
    occurs.  If TUNING->IS_N_BUCKETS is true, then CANDIDATE specifies the
-   exact number of buckets desired.  Return true iff the rehash succeeded.  */
+   exact number of buckets desired.  Return true iff the rehash succeeded,
+   false (setting errno) otherwise.  */
 _GL_ATTRIBUTE_NODISCARD
 extern bool hash_rehash (Hash_table *table, size_t candidate);
 
 /* If ENTRY matches an entry already in the hash table, return the pointer
    to the entry from the table.  Otherwise, insert ENTRY and return ENTRY.
-   Return NULL if the storage required for insertion cannot be allocated.
-   This implementation does not support duplicate entries or insertion of
-   NULL.  */
+   Return NULL (setting errno) if the storage required for insertion
+   cannot be allocated.  This implementation does not support
+   duplicate entries or insertion of NULL.  */
 _GL_ATTRIBUTE_NODISCARD
 extern void *hash_insert (Hash_table *table, const void *entry);
 
-/* Same as hash_insert, but invokes xalloc_die on memory exhaustion.  */
+/* Same as hash_insert, but invokes xalloc_die instead of returning NULL.  */
 /* This function is defined by module 'xhash'.  */
 extern void *hash_xinsert (Hash_table *table, const void *entry);
 
 /* Insert ENTRY into hash TABLE if there is not already a matching entry.
 
-   Return -1 upon memory allocation failure.
+   Return -1 (setting errno) upon memory allocation failure.
    Return 1 if insertion succeeded.
    Return 0 if there is already a matching entry in the table,
    and in that case, if MATCHED_ENT is non-NULL, set *MATCHED_ENT
diff --git a/modules/hash b/modules/hash
index 42502e749e..e837317d9f 100644
--- a/modules/hash
+++ b/modules/hash
@@ -7,6 +7,9 @@ lib/hash.h
 
 Depends-on:
 bitrotate
+calloc-posix
+free-posix
+malloc-posix
 stdbool
 stdint
 xalloc-oversized
-- 
2.39.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]