+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.
#include "bitrotate.h"
#include "xalloc-oversized.h"
+#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
{
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 *
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;
}
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)
/* Free the remainder of the hash table structure. */
free (table->bucket);
free (table);
+
+ errno = err;
}
/* Insertion and deletion. */
/* 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)
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;
}
* 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))
/* 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
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,
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,
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