From 2a4ea9b24e5d28598b627fca39fb42a9baedfaa3 Mon Sep 17 00:00:00 2001
From: Dana Robinson <derobins@hdfgroup.org>
Date: Sun, 8 Mar 2015 19:41:54 -0500
Subject: [svn-r26392] Reinstates r26327-8, which had been reverted due to
 failures on Solaris and OS X.

Added public API functions that expose the C library's memory allocator
for use in filter functions that need to allocate or resize buffers.

Intended for use with filter plugins, particularly on Windows, where C
runtime (CRT) issues can cause problems.

Fixes: HDFFV-9100

Tested on: h5committest + OS X (quail) + Solaris (emu)
---
 src/H5.c          |  89 +++++++++++++++++++++++++++++++++++++--
 src/H5MM.c        | 121 +++++++++++++++++++++++++++++++-----------------------
 src/H5MMprivate.h |   6 ---
 src/H5public.h    |   2 +
 test/testhdf5.h   |  12 ++++++
 test/tmisc.c      |  68 ++++++++++++++++++++++++++++++
 6 files changed, 237 insertions(+), 61 deletions(-)

diff --git a/src/H5.c b/src/H5.c
index 52dc566..68b339a 100644
--- a/src/H5.c
+++ b/src/H5.c
@@ -860,27 +860,109 @@ H5close(void)
 
 
 /*-------------------------------------------------------------------------
+ * Function:	H5allocate_memory
+ *
+ * Purpose:	    Allocate a memory buffer with the semantics of malloc().
+ *
+ *              NOTE: This function is intended for use with filter
+ *              plugins so that all allocation and free operations
+ *              use the same memory allocator. It is not intended for
+ *              use as a general memory allocator in applications.
+ *
+ * Parameters:
+ *
+ *      size:   The size of the buffer.
+ *
+ *      clear:  Whether or not to memset the buffer to 0.
+ *
+ * Return:
+ *
+ *      Success:    A pointer to the allocated buffer.
+ *  
+ *      Failure:    NULL
+ *
+ *-------------------------------------------------------------------------
+ */
+void *
+H5allocate_memory(size_t size, hbool_t clear)
+{
+    void *ret_value = NULL;
+
+    FUNC_ENTER_API_NOINIT;
+    H5TRACE2("*x", "zb", size, clear);
+
+    if(clear)
+        ret_value = H5MM_calloc(size);
+    else
+        ret_value = H5MM_malloc(size);
+
+    FUNC_LEAVE_API(ret_value)
+
+} /* end H5allocate_memory() */
+
+
+/*-------------------------------------------------------------------------
+ * Function:	H5resize_memory
+ *
+ * Purpose:	    Resize a memory buffer with the semantics of realloc().
+ *
+ *              NOTE: This function is intended for use with filter
+ *              plugins so that all allocation and free operations
+ *              use the same memory allocator. It is not intended for
+ *              use as a general memory allocator in applications.
+ *
+ * Parameters:
+ *
+ *      mem:    The buffer to be resized.
+ *
+ *      size:   The size of the buffer.
+ *
+ * Return:
+ *
+ *      Success:    A pointer to the resized buffer.
+ *  
+ *      Failure:    NULL (the input buffer will be unchanged)
+ *
+ *-------------------------------------------------------------------------
+ */
+void *
+H5resize_memory(void *mem, size_t size)
+{
+    void *ret_value = NULL;
+
+    FUNC_ENTER_API_NOINIT;
+    H5TRACE2("*x", "*xz", mem, size);
+
+    ret_value = H5MM_realloc(mem, size);
+
+    FUNC_LEAVE_API(ret_value)
+
+} /* end H5resize_memory() */
+
+
+/*-------------------------------------------------------------------------
  * Function:	H5free_memory
  *
- * Purpose:	Frees memory allocated by the library that it is the user's
+ * Purpose:	    Frees memory allocated by the library that it is the user's
  *              responsibility to free.  Ensures that the same library
  *              that was used to allocate the memory frees it.  Passing
  *              NULL pointers is allowed.
  *
- * Return:	SUCCEED/FAIL
+ * Return:	    SUCCEED/FAIL
  *
  *-------------------------------------------------------------------------
  */
 herr_t
 H5free_memory(void *mem)
 {
-    FUNC_ENTER_API_NOINIT
+    FUNC_ENTER_API_NOINIT;
     H5TRACE1("e", "*x", mem);
 
     /* At this time, it is impossible for this to fail. */
     HDfree(mem);
 
     FUNC_LEAVE_API(SUCCEED)
+
 } /* end H5free_memory() */
 
 
@@ -944,3 +1026,4 @@ DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved)
     return fOkay;
 }
 #endif /* H5_HAVE_WIN32_API && H5_BUILT_AS_DYNAMIC_LIB && H5_HAVE_WIN_THREADS && H5_HAVE_THREADSAFE*/
+
diff --git a/src/H5MM.c b/src/H5MM.c
index caeac37..534526f 100644
--- a/src/H5MM.c
+++ b/src/H5MM.c
@@ -31,93 +31,107 @@
 #include "H5Eprivate.h"
 #include "H5MMprivate.h"
 
-#ifndef NDEBUG
 
 /*-------------------------------------------------------------------------
- * Function:	H5MM_malloc
+ * Function:    H5MM_malloc
  *
- * Purpose:	Just like the POSIX version of malloc(3). This routine
- *		specifically checks for allocations of 0 bytes and fails
- *              in that case.  This routine is not called when NDEBUG is
- *		defined.
+ * Purpose:     Similar to the C89 version of malloc().
  *
- * Return:	Success:	Ptr to new memory
+ *              On size of 0, we return a NULL pointer instead of the
+ *              standard-allowed 'special' pointer since that's more
+ *              difficult to check as a return value. This is still
+ *              considered an error condition since allocations of zero
+ *              bytes usually indicate problems.
+ *  
+ * Return:  Success:    Pointer new memory
  *
- *		Failure:	NULL
+ *          Failure:	NULL
  *
- * Programmer:	Quincey Koziol
- *		koziol@ncsa.uiuc.edu
- *		Nov  8 2003
- *
- * Modifications:
+ * Programmer:  Quincey Koziol
+ *              Nov  8 2003
  *
  *-------------------------------------------------------------------------
  */
 void *
 H5MM_malloc(size_t size)
 {
+    void *ret_value;
+
+    HDassert(size);
+
     /* Use FUNC_ENTER_NOAPI_NOINIT_NOERR here to avoid performance issues */
     FUNC_ENTER_NOAPI_NOINIT_NOERR
 
-    HDassert(size);
+    if(size)
+        ret_value = HDmalloc(size);
+    else
+        ret_value = NULL;
 
-    FUNC_LEAVE_NOAPI(HDmalloc(size));
+    FUNC_LEAVE_NOAPI(ret_value);
 } /* end H5MM_malloc() */
 
 
 /*-------------------------------------------------------------------------
- * Function:	H5MM_calloc
+ * Function:    H5MM_calloc
  *
- * Purpose:	Similar to the POSIX version of calloc(3), except this routine
- *              just takes a 'size' parameter. This routine
- *		specifically checks for allocations of 0 bytes and fails
- *              in that case.  This routine is not called when NDEBUG is
- *		defined.
+ * Purpose:     Similar to the C89 version of calloc(), except this
+ *              routine just takes a 'size' parameter.
  *
- * Return:	Success:	Ptr to new memory
+ *              On size of 0, we return a NULL pointer instead of the
+ *              standard-allowed 'special' pointer since that's more
+ *              difficult to check as a return value. This is still
+ *              considered an error condition since allocations of zero
+ *              bytes usually indicate problems.
  *
- *		Failure:	NULL
  *
- * Programmer:	Quincey Koziol
- *		koziol@ncsa.uiuc.edu
- *		Nov  8 2003
+ * Return:  Success:    Pointer new memory
  *
- * Modifications:
+ *          Failure:	NULL
+ *
+ * Programmer:	Quincey Koziol
+ *              Nov  8 2003
  *
  *-------------------------------------------------------------------------
  */
 void *
 H5MM_calloc(size_t size)
 {
+    void *ret_value;
+
+    HDassert(size);
+
     /* Use FUNC_ENTER_NOAPI_NOINIT_NOERR here to avoid performance issues */
     FUNC_ENTER_NOAPI_NOINIT_NOERR
 
-    HDassert(size);
+    if(size)
+        ret_value = HDcalloc((size_t)1, size);
+    else
+        ret_value = NULL;
 
-    FUNC_LEAVE_NOAPI(HDcalloc(1,size));
+    FUNC_LEAVE_NOAPI(ret_value);
 } /* end H5MM_calloc() */
-#endif /* NDEBUG */
 
 
 /*-------------------------------------------------------------------------
- * Function:	H5MM_realloc
+ * Function:    H5MM_realloc
  *
- * Purpose:	Just like the POSIX version of realloc(3). Specifically, the
- *		following calls are equivalent
+ * Purpose:     Similar semantics as C89's realloc(). Specifically, the
+ *              following calls are equivalent:
  *
- *		H5MM_realloc (NULL, size) <==> H5MM_malloc (size)
- *		H5MM_realloc (ptr, 0)	  <==> H5MM_xfree (ptr)
- *		H5MM_realloc (NULL, 0)	  <==> NULL
+ *              H5MM_realloc(NULL, size)    <==> H5MM_malloc(size)
+ *              H5MM_realloc(ptr, 0)        <==> H5MM_xfree(ptr)
+ *              H5MM_realloc(NULL, 0)       <==> NULL
  *
- * Return:	Success:	Ptr to new memory or NULL if the memory
- *				was freed or HDrealloc couldn't allocate
- *				memory.
+ *              Note that the (NULL, 0) combination is undefined behavior
+ *              in the C standard.
  *
- *		Failure:	NULL
+ * Return:  Success:    Ptr to new memory if size > 0
+ *                      NULL if size is zero
  *
- * Programmer:	Robb Matzke
- *		matzke@llnl.gov
- *		Jul 10 1997
+ *          Failure:    NULL (input buffer is unchanged on failure)
+ *
+ * Programmer:  Robb Matzke
+ *              Jul 10 1997
  *
  *-------------------------------------------------------------------------
  */
@@ -129,16 +143,19 @@ H5MM_realloc(void *mem, size_t size)
     /* Use FUNC_ENTER_NOAPI_NOINIT_NOERR here to avoid performance issues */
     FUNC_ENTER_NOAPI_NOINIT_NOERR
 
-    if(NULL == mem) {
-	if(0 == size)
+    HDassert(mem || size);
+
+    if(NULL == mem && 0 == size) {  
+        /* Not defined in the standard, return NULL */
+        ret_value = NULL;
+    }
+    else {
+        ret_value = HDrealloc(mem, size);
+
+        /* Some platforms do not return NULL if size is zero. */
+        if(0 == size)
             ret_value = NULL;
-        else
-            ret_value = H5MM_malloc(size);
-    } /* end if */
-    else if(0 == size)
-	ret_value = H5MM_xfree(mem);
-    else
-	ret_value = HDrealloc(mem, size);
+    }
 
     FUNC_LEAVE_NOAPI(ret_value)
 } /* end H5MM_realloc() */
diff --git a/src/H5MMprivate.h b/src/H5MMprivate.h
index a3c39f0..0d608b2 100644
--- a/src/H5MMprivate.h
+++ b/src/H5MMprivate.h
@@ -33,19 +33,13 @@
 /* Private headers needed by this file */
 #include "H5private.h"
 
-#ifdef NDEBUG
-#define H5MM_malloc(Z)	HDmalloc(Z)
-#define H5MM_calloc(Z)	HDcalloc((size_t)1,Z)
-#endif /* NDEBUG */
 #define H5MM_free(Z)	HDfree(Z)
 
 /*
  * Library prototypes...
  */
-#ifndef NDEBUG
 H5_DLL void *H5MM_malloc(size_t size);
 H5_DLL void *H5MM_calloc(size_t size);
-#endif /* NDEBUG */
 H5_DLL void *H5MM_realloc(void *mem, size_t size);
 H5_DLL char *H5MM_xstrdup(const char *s);
 H5_DLL char *H5MM_strdup(const char *s);
diff --git a/src/H5public.h b/src/H5public.h
index 673aa6f..0e866be 100644
--- a/src/H5public.h
+++ b/src/H5public.h
@@ -332,6 +332,8 @@ H5_DLL herr_t H5get_libversion(unsigned *majnum, unsigned *minnum,
 H5_DLL herr_t H5check_version(unsigned majnum, unsigned minnum,
 			       unsigned relnum);
 H5_DLL herr_t H5free_memory(void *mem);
+H5_DLL void *H5allocate_memory(size_t size, hbool_t clear);
+H5_DLL void *H5resize_memory(void *mem, size_t size);
 
 #ifdef __cplusplus
 }
diff --git a/test/testhdf5.h b/test/testhdf5.h
index 907fce9..62dadde 100644
--- a/test/testhdf5.h
+++ b/test/testhdf5.h
@@ -68,6 +68,18 @@
    }									      \
 }
 
+#define CHECK_PTR_NULL(ret,where) {						      \
+   if (VERBOSE_HI) {					      \
+      print_func("   Call to routine: %15s at line %4d in %s returned %p\n",  \
+                 (where), (int)__LINE__, __FILE__, (ret));		      \
+   }									      \
+   if (ret) {							      \
+      TestErrPrintf ("*** UNEXPECTED RETURN from %s is not NULL line %4d in %s\n", \
+                  (where), (int)__LINE__, __FILE__);			      \
+      H5Eprint2(H5E_DEFAULT, stdout);				      \
+   }									      \
+}
+
 /* Used to make certain a return value _is_ a value */
 #define VERIFY(_x, _val, where) do {					      \
     long __x = (long)_x, __val = (long)_val;				      \
diff --git a/test/tmisc.c b/test/tmisc.c
index cffb3df..68a30eb 100644
--- a/test/tmisc.c
+++ b/test/tmisc.c
@@ -5304,6 +5304,73 @@ test_misc31(void)
 
 
 /****************************************************************
+ *
+ *  test_misc32(): Simple test of filter memory allocation
+ *                 functions.
+ *
+ ***************************************************************/
+static void
+test_misc32(void)
+{
+    void *buffer;
+    void *resized;
+    size_t size;
+
+    /* Output message about test being performed */
+    MESSAGE(5, ("Edge case test of filter memory allocation functions\n"));
+
+    /* Test that the filter memory allocation functions behave correctly
+     * at edge cases.
+     */
+
+    /* FREE */
+
+    /* Test freeing a NULL pointer.
+     * No real confirmation check here, but Valgrind will confirm no
+     * shenanigans.
+     */
+    buffer = NULL;
+    H5free_memory(buffer);
+
+    /* ALLOCATE */
+
+    /* Size zero returns NULL.
+     * Also checks that a size of zero and setting the buffer clear flag
+     * to TRUE can be used together.
+     *
+     * Note that we have asserts in the code, so only check when NDEBUG
+     * is defined.
+     */
+#ifdef NDEBUG
+    buffer = H5allocate_memory(0, FALSE);
+    CHECK_PTR_NULL(buffer, "H5allocate_memory"); /*BAD*/
+    buffer = H5allocate_memory(0, TRUE);
+    CHECK_PTR_NULL(buffer, "H5allocate_memory"); /*BAD*/
+#endif /* NDEBUG */
+
+    /* RESIZE */
+
+    /* Size zero returns NULL. Valgrind will confirm buffer is freed. */
+    size = 1024;
+    buffer = H5allocate_memory(size, TRUE);
+    resized = H5resize_memory(buffer, 0);
+    CHECK_PTR_NULL(resized, "H5resize_memory");
+
+    /* NULL input pointer returns new buffer */
+    resized = H5resize_memory(NULL, 1024);
+    CHECK_PTR(resized, "H5resize_memory");
+    H5free_memory(resized);
+
+    /* NULL input pointer and size zero returns NULL */
+#ifdef NDEBUG
+    resized = H5resize_memory(NULL, 0);
+    CHECK_PTR_NULL(resized, "H5resize_memory"); /*BAD*/
+#endif /* NDEBUG */
+    
+} /* end test_misc32() */
+
+
+/****************************************************************
 **
 **  test_misc(): Main misc. test routine.
 **
@@ -5349,6 +5416,7 @@ test_misc(void)
     test_misc29();      /* Test that speculative metadata reads are handled correctly */
     test_misc30();      /* Exercise local heap loading bug where free lists were getting dropped */
     test_misc31();      /* Test Reentering library through deprecated routines after H5close() */
+    test_misc32();      /* Test filter memory allocation functions */
 
 } /* test_misc() */
 
-- 
cgit v0.12