/*
 * nasd_mem.h
 *
 * Generic memory allocation/deallocation
 *
 * Authors: Daniel Stodolsky, Mark Holland, Jim Zelenka, Sean Levy
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1995,1996,1997,1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#ifndef _NASD_MEM_H_
#define _NASD_MEM_H_

#include <nasd/nasd_options.h>
#include <nasd/nasd_types.h>

#ifdef DEC_OSF
#include <nasd/dux/nasd_dux_mem.h>
#endif /* DEC_OSF */

#ifdef LINUX
#include <nasd/linux/nasd_linux_mem.h>
#ifndef KERNEL
#include <malloc.h>
#endif /* KERNEL */
#endif /* LINUX */

#if defined(FREEBSD) || defined(SOLARIS) || defined(NETBSD)
#include <stdlib.h>
#endif /* FREEBSD || SOLARIS || NETBSD */

/* This should be correct on all platforms, but just in case it isn't,
   you can override it from the platform-specific nasd_blah_mem.h
   file. */
#ifndef NASD_MEM_ALIGN
#define NASD_MEM_ALIGN sizeof(long)
#endif /* NASD_MEM_ALIGN */

/*
 * Usually will already be defined for kernel env, but not user
 */
#ifndef NASD_SYS_MALLOC
#define NASD_SYS_MALLOC(_p_,_cast_,_size_) _p_ = _cast_  malloc(_size_)
#endif /* !NASD_SYS_MALLOC */

/*
 * For page-aligned allocations. This doesn't exist on all platforms;
 * if it isn't supported, it should fall back to a standard malloc
 */
#ifndef NASD_SYS_VALLOC
# if defined(LINUX) && !defined(KERNEL)
#  define NASD_SYS_VALLOC(_p_,_cast_,_size_) _p_ = _cast_ valloc(_size_)
# else /* LINUX && !KERNEL */
#  define NASD_SYS_VALLOC(_p_,_cast_,_size_) _p_ = _cast_ malloc(_size_)
# endif /* LINUX && !KERNEL */
#endif /* !NASD_SYS_VALLOC */

/*
 * Usually will already be defined for kernel env, but not user
 */
#ifndef NASD_SYS_FREE
#define NASD_SYS_FREE(_p_,_size_) free(_p_)
#endif /* !NASD_SYS_FREE */


#define NASD_MEM_FENCEPOST_PATTERN nasd_uint64cast(0xDEADBEEFDEADBEEF)
#define NASD_MEM_FENCEPOST_BYTE 0xDE

#if NASD_TRACK_ALLOCATIONS > 0
#define NASD_TRACK_ALLOC(_addr_,_sz_)   nasd_mem_track_alloc(_addr_,_sz_,__FILE__,__LINE__)
#define NASD_TRACK_DEALLOC(_addr_,_sz_) nasd_mem_track_dealloc(_addr_,_sz_,__FILE__,__LINE__)
#else /* NASD_TRACK_ALLOCATIONS > 0 */
#define NASD_TRACK_ALLOC(_addr_,_sz_)
#define NASD_TRACK_DEALLOC(_addr_,_sz_)
#endif /* NASD_TRACK_ALLOCATIONS > 0 */

#if NASD_MEM_FILL == 0
#define NASD_MEM_DO_FILL(_addr_,_nbytes_) { /* noop */ }
#else /* NASD_MEM_FILL == 0 */
extern nasd_uint32 nasd_mem_filler;
extern void nasd_mem_do_fill(void *addr, int nbytes);
#define NASD_MEM_DO_FILL(_addr_,_nbytes_) nasd_mem_do_fill(_addr_,_nbytes_)
#endif /* NASD_MEM_FILL == 0 */

#if NASD_MEM_FENCEPOST > 0
#define NASD_Fencepost(_p_,_sz_,_n_) { \
  nasd_uint64 *_ll_; \
  void *_pp_, *_lpp_; \
  nasd_byte_t *_c_; \
  int _i, _pad; \
  _pad = _sz_ % NASD_MEM_ALIGN; \
  if(_pad) \
    _pad = NASD_MEM_ALIGN - _pad; \
  _pp_ = (void *)(((nasd_byte_t *)_p_) - ((unsigned long)(NASD_MEM_FENCEPOST * sizeof(nasd_uint64)))); \
  _lpp_ = (void *)(((nasd_byte_t *)_p_) + _sz_ + _pad); \
  _ll_ = _pp_; \
  if (_ll_[0] != _sz_) { \
    nasd_printf("%s:%d _ll_=%lx _ll_[0]=%lu _sz_=%lu n=%d\n", \
      __FILE__, __LINE__, _ll_, _ll_[0], _sz_, _n_); \
    NASD_PANIC(); \
  } \
  for(_i=1;_i<NASD_MEM_FENCEPOST;_i++) { \
    if (_ll_[_i] != NASD_MEM_FENCEPOST_PATTERN) { \
      nasd_printf("1-%d _ll_=0x%" NASD_64x_FMT " _ll_[%d] = 0x%" NASD_64x_FMT " %s:%d n=%d\n", _i, _ll_, _i, _ll_[_i], \
        __FILE__, __LINE__, _n_); \
      NASD_PANIC(); \
    } \
  } \
  if(_pad) { \
    _c_ = ((nasd_byte_t *)_p_) + _sz_; \
    for(_i=0;_i<_pad;_i++) { \
      if(_c_[_i] != NASD_MEM_FENCEPOST_BYTE) { \
        nasd_printf("2-%d _c_=0x%" NASD_64x_FMT " _c_[%d] = 0x%x %s:%d n=%d\n", _i, _c_, _i, (nasd_byte_t) _c_[_i], __FILE__, __LINE__, _n_); \
        NASD_PANIC(); \
      } \
    } \
  } \
  _ll_ = _lpp_; \
  for(_i=0;_i<NASD_MEM_FENCEPOST;_i++) { \
    if (_ll_[_i] != NASD_MEM_FENCEPOST_PATTERN) { \
      nasd_printf("3-%d _ll_=0x%" NASD_64x_FMT " _ll_[%d] = 0x%" NASD_64x_FMT " %s:%d n=%d\n", _i, _ll_, _i, _ll_[_i], \
        __FILE__, __LINE__, _n_); \
      NASD_PANIC(); \
    } \
  } \
}
#else /* NASD_MEM_FENCEPOST > 0 */
#define NASD_MEM_Fencepost(_p_, _sz_, _n_) { /* noop */ }
#endif /* NASD_MEM_FENCEPOST > 0 */

#if NASD_MEM_COUNT_ALLOC > 0

extern nasd_uint64 nasd_mem_allocated;
#define NASD_MEMALLOC_FMT NASD_64u_FMT

#define NASD_MEM_ALLOCATED(_addr_,_nbytes_) { \
  NASD_ATOMIC_ADD64(&nasd_mem_allocated, _nbytes_); \
  NASD_TRACK_ALLOC(_addr_,_nbytes_); \
  NASD_MEM_DO_FILL(_addr_,_nbytes_); \
}

#define NASD_MEM_DEALLOCATED(_addr_,_nbytes_) { \
  NASD_ATOMIC_SUB64(&nasd_mem_allocated, _nbytes_); \
  NASD_TRACK_DEALLOC(_addr_,_nbytes_); \
}

#else /* NASD_MEM_COUNT_ALLOC > 0 */

#define NASD_MEM_ALLOCATED(_addr_,_nbytes_) { \
  NASD_MEM_DO_FILL(_addr_,_nbytes_); \
}
#define NASD_MEM_DEALLOCATED(_addr_,_nbytes_) { /* noop */ }

#endif /* NASD_MEM_COUNT_ALLOC > 0 */

#if NASD_MEM_FENCEPOST > 0

#define NASD_Malloc(_p_, _rsize_, _cast_) { \
  void *_ppp_, *_pp_, *_lpp_; \
  nasd_uint64 *_ll_; \
  nasd_byte_t *_c_; \
  int _size_, _i, _pad; \
  _pad = _rsize_ % NASD_MEM_ALIGN; \
  if(_pad) \
    _pad = NASD_MEM_ALIGN - _pad; \
  _size_ = (_rsize_ + _pad + (2*NASD_MEM_FENCEPOST * sizeof(nasd_uint64))); \
  NASD_SYS_MALLOC(_pp_,_cast_,_size_); \
  if (_pp_) { \
    _ppp_ = (void *)(((nasd_byte_t *)_pp_) + ((unsigned long)(NASD_MEM_FENCEPOST * sizeof(nasd_uint64)))); \
    _p_ = _ppp_; \
    NASD_MEM_ALLOCATED(_p_,_rsize_); \
    _lpp_ = (void *)(((nasd_byte_t *)_p_) + (_rsize_) + _pad); \
    _ll_ = _pp_; \
    _ll_[0] = _rsize_; \
    for(_i=1;_i<NASD_MEM_FENCEPOST;_i++) \
      _ll_[_i] = NASD_MEM_FENCEPOST_PATTERN; \
    _ll_ = _lpp_; \
    for(_i=0;_i<NASD_MEM_FENCEPOST;_i++) \
      _ll_[_i] = NASD_MEM_FENCEPOST_PATTERN; \
    if(_pad) { \
      _c_ = (nasd_byte_t *)(((nasd_byte_t *)_p_) + (_rsize_)); \
      for(_i=0;_i<_pad;_i++) \
        _c_[_i] = NASD_MEM_FENCEPOST_BYTE; \
    } \
    NASD_Fencepost(_p_, _rsize_, -1); \
  } \
}

/* we don't play the fencepost game with vallocs. it's too painful to
 * think about right now.
 */
#define NASD_Valloc(_p_, _size_, _cast_) { \
  NASD_SYS_VALLOC(_p_,_cast_,_size_); \
  if ((_p_) != NULL) { \
    NASD_MEM_ALLOCATED(_p_,_size_); \
  } \
  /* bzero((char *)_p_, _size_); */ \
}  

#define NASD_Free(_p_, _sz_) { \
  void *_pp_; \
  int _size_, _pad; \
  _pad = _sz_ % NASD_MEM_ALIGN; \
  if(_pad) \
    _pad = NASD_MEM_ALIGN - _pad; \
  _size_ = (_sz_ + _pad + (2*NASD_MEM_FENCEPOST * sizeof(nasd_uint64))); \
  _pp_ = (void *)(((unsigned long)_p_) - (NASD_MEM_FENCEPOST * sizeof(nasd_uint64))); \
  NASD_Fencepost(_p_, _sz_, -3); \
  NASD_SYS_FREE(_pp_,_size_); \
  NASD_MEM_DEALLOCATED(_p_,_sz_); \
}

#else /* NASD_MEM_FENCEPOST > 0 */

#define NASD_Malloc(_p_, _size_, _cast_) { \
  NASD_SYS_MALLOC(_p_,_cast_,_size_); \
  if ((_p_) != NULL) { \
    NASD_MEM_ALLOCATED(_p_,_size_); \
  } \
  /* bzero((char *)_p_, _size_); */ \
}

#define NASD_Valloc(_p_, _size_, _cast_) { \
  NASD_SYS_VALLOC(_p_,_cast_,_size_); \
  if ((_p_) != NULL) { \
    NASD_MEM_ALLOCATED(_p_,_size_); \
  } \
  /* bzero((char *)_p_, _size_); */ \
}  

#define NASD_Free(_p_, _sz_) { \
  NASD_SYS_FREE(_p_,_sz_); \
  NASD_MEM_DEALLOCATED(_p_,_sz_); \
}

#endif /* NASD_MEM_FENCEPOST > 0 */

/*
 * Must directly invoke alloc/dealloc operations
 * for this list to avoid recursion.
 */
typedef struct nasd_mem_track_s nasd_mem_track_t;

struct nasd_mem_track_s {
  void              *addr;
  int                size;
  char              *file;
  int                line;
  nasd_mem_track_t  *next;
};

/*
 * Memory subsystem funcs
 */
extern nasd_status_t nasd_mem_dump_unfreed(void);
extern nasd_status_t nasd_mem_real_init(void);
extern void nasd_mem_real_shutdown(void);
extern void nasd_memsys_init(void);
extern nasd_status_t nasd_mem_init(void);
extern void nasd_mem_shutdown(void);
extern void nasd_mem_track_alloc(void *addr, int size,
  char *file, int line);
extern void nasd_mem_track_dealloc(void *addr, int size,
  char *file, int line);
extern nasd_mem_track_t *nasd_mem_gettrack(void);
extern void nasd_mem_freetrack(nasd_mem_track_t *t);

#endif /* !_NASD_MEM_H_ */

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
