/* eurephia_nullsafe.c
 *
 *  standard C string functions, which is made NULL safe by checking
 *  if input value is NULL before performing the action.
 *
 *  GPLv2 only - Copyright (C) 2009 - 2012
 *               David Sommerseth <dazo@users.sourceforge.net>
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; version 2
 *  of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 */

/**
 * @file   eurephia_nullsafe.c
 * @author David Sommerseth <dazo@users.sourceforge.net>
 * @date   2009-09-07
 *
 * @brief standard C string functions, which is made NULL safe by checking
 *        if input value is NULL before performing the action.
 *
 */

#include <stdio.h>
#include <stdlib.h>

#include <eurephia_context.h>
#include <eurephia_log.h>

#if __GNUC__ >= 3
#define __malloc__ __attribute__((malloc))
#else /* If not GCC 3 or newer, disable optimisations */
#define __malloc__
#endif

/**
 * Internal function, should be called via the malloc_nullsafe() macro.
 * This replaces the use of malloc() and memset().  This function uses calloc
 * internally, which results in the memory region being zero'd by the kernel
 * on memory allocation.
 *
 * @param ctx   eurephiaCTX, used for logging
 * @param sz    size of the memory region being allocated
 * @param file  debug info, which file is doing this call
 * @param line  debug info, which line in the file
 *
 * @return Returns a void pointer to the memory region on success, otherwise NULL
 */
__malloc__ void *_malloc_nullsafe(eurephiaCTX *ctx, size_t sz, const char *file, int line) {
        void *buf = NULL;

        buf = calloc(1, sz);    /* Using calloc, also gives a zero'd memory region */
        if( !buf ) {
                if( ctx ) {
                        eurephia_log(ctx, LOG_FATAL, 40,
                                     "Could not allocate memory region for %ld bytes (File %s, line %i)",
                                     sz, file, line);
                } else {
                        fprintf(stderr, "** FATAL ERROR ** "
                                     "Could not allocate memory region for %ld bytes (File %s, line %i)",
				(unsigned long int) sz, file, line);
                }
        }
#ifdef DEBUG
        else {
                // Don't use DEBUG macro, to catch the right file and line number for the log
                _eurephia_log_func(ctx, LOG_DEBUG, 40, file, line,
                                   "Allocated %ld bytes of memory on address %p",
                                   sz, buf);
        }
#endif
        return buf;
}


/**
 * Internal function for freeing memory.  Also does some logging of the freeing if debug level
 * enabled and log level high enough.  Should be called via the free_nullsafe() macro.
 *
 * @param ctx    eurephiaCTX (for logging only)
 * @param ptr    Pointer to the memory region to be freed
 * @param file  debug info, which file is doing this call
 * @param line  debug info, which line in the file
 *
 */
void _free_nullsafe(eurephiaCTX *ctx, void *ptr, const char *file, int line) {
        if( ptr == NULL ) {
                return;
        }
#ifdef DEBUG
        // Don't use DEBUG macro, to catch the right file and line number for the log
        _eurephia_log_func(ctx, LOG_DEBUG, 40, file, line, "Freeing memory on address %p", ptr);
#endif
        free(ptr);
}
