%% options copyright owner = Dirk Krause copyright year = 2015-xxxx license = bsd %% header /** @file Memory allocation and deallocation; copy, reset and compare memory regions. The functions provided by the C runtime libraries are used by default. On Windows you can decide to use Windows API functions instead, define DK4_WIN_AVOID_CRT or DK4_WIN_DENY_CRT to 1. When using Windows API functions dynamic memory allocations use the global heap. Define DK4_USE_WINDOWS_LOCAL_ALLOC to 1 to use the local heap instead. */ #ifndef DK4CONF_H_INCLUDED #include "dk4conf.h" #endif #ifndef DK4TYPES_H_INCLUDED #include "dk4types.h" #endif #ifndef DK4ERROR_H_INCLUDED #include "dk4error.h" #endif #if DK4_HAVE_STRING_H #ifndef STRING_H_INCLUDED #include #define STRING_H_INCLUDED 1 #endif #endif #if DK4_HAVE_STRINGS_H #ifndef STRINGS_H_INCLUDED #include #define STRINGS_H_INCLUDED 1 #endif #endif #ifdef __cplusplus extern "C" { #endif /** Fallback function to use if no suitable memory copy function is found. This function does not do any error checking. The caller is responsible to provide valid arguments. @param dst Destination buffer address. @param src Source buffer address. @param sz Number of bytes to copy. */ void dk4mem_fallback_cpy(void *dst, const void *src, size_t sz); /** Fallback function to use if no suitable memory reseet function is found. This function does not do any error checking. The caller is responsible to provide valid arguments. @param bptr Address of buffer to reset. @param sz Number of bytes to reset. */ void dk4mem_fallback_reset(void *bptr, size_t sz); /** Fallback function to use if no suitable memory comparison function is found. This function does not do any error checking. The caller is responsible to provide valid arguments. @param s1 Address of left buffer in comparison. @param s2 Address of right buffer in comparison. @param sz Buffer size (number of bytes). @return Positive value if s1>s2, 0 for equal buffers, -1 if s1 if bptr is NULL or sz is 0. */ void dk4mem_reset(void *bptr, size_t sz, dk4_er_t *erp); /** Set memory range to specified value. CRT on Windows: Optional. @param bptr Pointer to start of buffer. @param sz Buffer size (number of bytes). @param uc Byte value to write. @param erp Error report for diagnostics, may be NULL. Error codes: - DK4_E_INVALID_ARGUMENTS
if bptr is NULL or sz is 0. */ void dk4mem_set(void *bptr, size_t sz, unsigned char uc, dk4_er_t *erp); /** Copy memory range. CRT on Windows: Optional. @param dst Destination address. @param src Source address. @param sz Range size (number of bytes). @param erp Error report, may be NULL. Error codes: - DK4_E_INVALID_ARGUMENTS
if dst or src is NULL or sz is 0. */ void dk4mem_cpy(void *dst, void const *src, size_t sz, dk4_er_t *erp); /** Compare two memory ranges. CRT on Windows: Optional, disabling CRT degrades performance. @param s1 Left memory range. @param s2 Right memory range. @param sz Number of bytes to compare. @param erp Error report, may be NULL. @return 1 if s1>s2, -1 if s1 if s1 or s2 is NULL or sz is 0. */ int dk4mem_cmp(const void *s1, const void *s2, size_t sz, dk4_er_t *erp); #if (DK4_ON_WINDOWS) || (DK4_HAVE_MALLOC && DK4_HAVE_FREE) /** Dynamically allocate memory. The memory range is not initialized. CRT on Windows: Optional. @param bytes Number of bytes to allocate. @param erp Error report, may be NULL. @return Pointer to memory on success, NULL on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if bytes is 0, - DK4_E_MATH_OVERFLOW
if the specified size is too large, - DK4_E_MEMORY_ALLOCATION_FAILED
with mem.elsize and mem.nelem set if there is not enough memory available. */ void * dk4mem_malloc_bytes(size_t bytes, dk4_er_t *erp); /** Dynamically allocate memory. The memory range is not initialized. CRT on Windows: Optional. @param elsize Size of each element. @param nelem Number of elements. @param erp Error report, may be NULL. @return Pointer to memory on success, NULL on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if elsize or nelem is 0, - DK4_E_MATH_OVERFLOW
on numeric overflow when calculating the product of elsize and nelem, - DK4_E_MEMORY_ALLOCATION_FAILED
with mem.elsize and mem.nelem set if there is not enough memory available. */ void * dk4mem_malloc(size_t elsize, size_t nelem, dk4_er_t *erp); /** Dynamically allocate memory and initialize memory range. CRT on Windows: Optional. @param elsize Size of each element. @param nelem Number of elements. @param erp Error report, may be NULL. @return Pointer to memory on success, NULL on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if elsize or nelem is 0, - DK4_E_MATH_OVERFLOW
on numeric overflow when calculating the product of elsize and nelem, - DK4_E_MEMORY_ALLOCATION_FAILED
with mem.elsize and mem.nelem set if there is not enough memory available. */ void * dk4mem_calloc(size_t elsize, size_t nelem, dk4_er_t *erp); /** Release memory allocated by dk4mem_malloc() or dk4mem_calloc(). The function checks whether the pointer is NULL and frees the memory only for non-NULL pointers. CRT on Windows: Optional. @param ptr Pointer to start address of memory to release. */ void dk4mem_free(void *ptr); #endif /* if defined(_WIN32) ... */ #ifdef __cplusplus } #endif /** Allocate and initialize memory. @param tp Type to allocate. @param ne Number of elements to allocate. @param er Error report, may be NULL. @return Pointer to a new tp on success, NULL on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if elsize or nelem is 0, - DK4_E_MATH_OVERFLOW
on numeric overflow when calculating the product of elsize and nelem, - DK4_E_MEMORY_ALLOCATION_FAILED
with mem.elsize and mem.nelem set if there is not enough memory available. */ #define dk4mem_new(tp,ne,er) (tp *)dk4mem_calloc(sizeof(tp),ne,er) /** Free memory previously allocated. @param ptr Start address of memory range to free. */ #define dk4mem_release(ptr) \ do { if (NULL != ptr) { dk4mem_free(ptr); } ptr = NULL; } while (0) /** Find number of elements in array. @param v Variable. @param t Type. @return The size of the variable divided by the size of the type. */ #define DK4_SIZEOF(v,t) (sizeof(v)/sizeof(t)) /** Increase byte number to alignment size. @param sz Original byte number. @param al Alignment size, typically 16. @return Size corrected for alignment. */ #define DK4_MEM_ALIGN(sz,al) ((0 == (sz % al)) ? (sz) : (al * (1 + (sz/al)))) #if DK4_HAVE_INLINE /* +++ DK4_HAVE_INLINE */ #if DK4_ON_WINDOWS /* +++ DK4_HAVE_INLINE, Windows */ #if DK4_WIN_AVOID_CRT || DK4_WIN_DENY_CRT /* +++ DK4_HAVE_INLINE, Windows, ! crt */ /** Pure memory copy operation. All arguments must be checked by the caller before calling this function. @param dst Destination buffer address. @param src Source buffer address. @param sz Number of bytes to copy. */ static inline void DK4_MEMCPY(void *dst, const void *src, size_t sz) { CopyMemory(dst, src, sz); } /** Pure memory reset. All arguments must be checked by the caller before calling this function. @param dst Start address of buffer to reset. @param sz Number of bytes to reset. */ static inline void DK4_MEMRES(void *dst, size_t sz) { ZeroMemory(dst, sz); } /** Pure memory comparison. All arguments must be checked by the caller before calling this function. @param s1 Address of left buffer in comparison. @param s2 Address of right buffer in comparison. @param sz Number of bytes to compare. @return 1 if s1>s2, 0 for equal buffers, -1 if s1s2, 0 for equal buffers, -1 if s1s2, 0 for equal buffers, -1 if s1s2, 0 for equal buffers, -1 if s1s2, 0 for equal buffers, -1 if s1s2, 0 for equal buffers, -1 if s1s2, 0 for equal buffers, -1 if s1s2, 0 for equal buffers, -1 if s1s2, 0 for equal buffers, -1 if s1s2, 0 for equal buffers, -1 if s1 #define WINDOWS_H_INCLUDED 1 #endif #endif #include "dk4types.h" #if DK4_HAVE_STRING_H #ifndef STRING_H_INCLUDED #include #define STRING_H_INCLUDED 1 #endif #endif #if DK4_HAVE_STRINGS_H #ifndef STRINGS_H_INCLUDED #include #define STRINGS_H_INCLUDED 1 #endif #endif #include "dk4mem.h" #include "dk4numco.h" $!trace-include static void dk4mem_fallback_i_reset(unsigned char *bptr, size_t sz) { while (0 < sz--) { *(bptr++) = '\0'; } } void dk4mem_fallback_reset(void *bptr, size_t sz) { dk4mem_fallback_i_reset((unsigned char *)bptr, sz); } void dk4mem_reset(void *bptr, size_t sz, dk4_er_t *erp) { $? "+ dk4mem_reset" if ((NULL != bptr) && (0 < sz)) { DK4_MEMRES(bptr, sz); } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } $? "- dk4mem_reset" } void dk4mem_set(void *bptr, size_t sz, unsigned char uc, dk4_er_t *erp) { #if !DK4_HAVE_MEMSET unsigned char *ucptr; #endif if ((NULL != bptr) && (0 < sz)) { #if DK4_HAVE_MEMSET memset(bptr, (int)uc, sz); #else ucptr = (unsigned char *)bptr; while (0 < sz--) { *(ucptr++) = uc; } #endif } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } } static void dk4mem_fallback_i_cpy(unsigned char *dst, const unsigned char *src, size_t sz) { while(0 < sz--) { *(dst++) = *(src++); } } void dk4mem_fallback_cpy(void *dst, const void *src, size_t sz) { dk4mem_fallback_i_cpy( (unsigned char *)dst, (const unsigned char *)src, sz ); } void dk4mem_cpy(void *dst, void const *src, size_t sz, dk4_er_t *erp) { if ((NULL != dst) && (NULL != src) && (0 < sz)) { DK4_MEMCPY(dst, src, sz); } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } } static int dk4mem_fallback_i_cmp( const unsigned char *s1, const unsigned char *s2, size_t sz ) { int back = 0; while ((0 < sz--) && (0 == back)) { if (*s1 > *s2) { back = 1; } else { if (*s1 < *s2) { back = -1; } } s1++; s2++; } return back; } int dk4mem_fallback_cmp(const void *s1, const void *s2, size_t sz) { return( dk4mem_fallback_i_cmp( (const unsigned char *)s1, (const unsigned char *)s2, sz ) ); } int dk4mem_cmp(const void *s1, const void *s2, size_t sz, dk4_er_t *erp) { int back = 0; if ((NULL != s1) && (NULL != s2) && (0 < sz)) { back = DK4_MEMCMP(s1, s2, sz); } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } return back; } #if (DK4_ON_WINDOWS) || (DK4_HAVE_MALLOC && DK4_HAVE_FREE) void * dk4mem_malloc_bytes(size_t bytes, dk4_er_t *erp) { void *back = NULL; /* Function result. */ if (0 < bytes) { #if DK4_ON_WINDOWS /* +++ Windows */ #if DK4_WIN_AVOID_CRT || DK4_WIN_DENY_CRT if ((dk4_um_t)bytes <= (dk4_um_t)0xFFFFFFFFUL) { #if DK4_USE_WINDOWS_LOCAL_ALLOC back = (void*)LocalAlloc(LMEM_FIXED, (DWORD)bytes); #else back = (void*)GlobalAlloc(GMEM_FIXED, (DWORD)bytes); #endif if (NULL == back) { dk4error_set_elsize_nelem(erp,DK4_E_MEMORY_ALLOCATION_FAILED,1,bytes); } } else { dk4error_set_simple_error_code(erp, DK4_E_MATH_OVERFLOW); } #else back = malloc(bytes); if (NULL == back) { dk4error_set_elsize_nelem(erp, DK4_E_MEMORY_ALLOCATION_FAILED, 1, bytes); } #endif /* --- Windows */ #else /* +++ non-Windows */ back = malloc(bytes); if (NULL == back) { dk4error_set_elsize_nelem(erp, DK4_E_MEMORY_ALLOCATION_FAILED, 1, bytes); } /* --- non-Windows */ #endif } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } return back; } void * dk4mem_malloc(size_t elsize, size_t nelem, dk4_er_t *erp) { void *back = NULL; size_t bytes; if ((0 < elsize) && (0 < nelem)) { if ((SIZE_MAX / elsize) >= nelem) { bytes = elsize * nelem; back = dk4mem_malloc_bytes(bytes, erp); } else { dk4error_set_simple_error_code(erp, DK4_E_MATH_OVERFLOW); } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } return back; } void * dk4mem_calloc(size_t elsize, size_t nelem, dk4_er_t *erp) { void *back = NULL; if ((0 < elsize) && (0 < nelem)) { if ((SIZE_MAX / elsize) >= nelem) { #if DK4_ON_WINDOWS /* +++ Windows */ #if DK4_WIN_AVOID_CRT || DK4_WIN_DENY_CRT size_t bytes; bytes = elsize * nelem; if ((dk4_um_t)bytes <= (dk4_um_t)0xFFFFFFFFUL) { #if DK4_USE_WINDOWS_LOCAL_ALLOC back = (void *)LocalAlloc((LMEM_FIXED | LMEM_ZEROINIT), (DWORD)bytes); #else back = (void *)GlobalAlloc((GMEM_FIXED | GMEM_ZEROINIT), (DWORD)bytes); #endif if (NULL == back) { dk4error_set_elsize_nelem( erp, DK4_E_MEMORY_ALLOCATION_FAILED, elsize, nelem ); } } else { dk4error_set_simple_error_code(erp, DK4_E_MATH_OVERFLOW); } #else back = calloc(nelem, elsize); if (NULL == back) { dk4error_set_elsize_nelem( erp, DK4_E_MEMORY_ALLOCATION_FAILED, elsize, nelem ); } #endif /* --- Windows */ #else /* +++ non-Windows */ #if DK4_HAVE_CALLOC back = calloc(nelem, elsize); if (NULL == back) { dk4error_set_elsize_nelem( erp, DK4_E_MEMORY_ALLOCATION_FAILED, elsize, nelem ); } #else size_t bytes; bytes = elsize * nelem; back = malloc(bytes); if (NULL != back) { dk4mem_reset(back, bytes); } else { dk4error_set_elsize_nelem( erp, DK4_E_MEMORY_ALLOCATION_FAILED, elsize, nelem ); } #endif /* --- non-Windows */ #endif } else { dk4error_set_simple_error_code(erp, DK4_E_MATH_OVERFLOW); } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } return back; } void dk4mem_free(void *ptr) { if (NULL != ptr) { #if DK4_ON_WINDOWS /* +++ Windows */ #if DK4_WIN_AVOID_CRT || DK4_WIN_DENY_CRT #if DK4_USE_WINDOWS_LOCAL_ALLOC LocalFree((HLOCAL)ptr); #else GlobalFree((HLOCAL)ptr); #endif #else free(ptr); #endif /* --- Windows */ #else /* +++ non-Windows */ free(ptr); /* --- non-Windows */ #endif } } #endif /* if defined(_WIN32) ... */