// Filename: deletedChain.h
// Created by:  drose (01Apr06)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

#ifndef DELETEDCHAIN_H
#define DELETEDCHAIN_H

#include "dtoolbase.h"
#include "deletedBufferChain.h"
#include <assert.h>

////////////////////////////////////////////////////////////////////
//       Class : DeletedChain
// Description : This template class can be used to provide faster
//               allocation/deallocation for many Panda objects.  It
//               works by maintaining a linked list of deleted objects
//               that are all of the same type; when a new object is
//               allocated that matches that type, the same space is
//               just reused.
//
//               This class is actually a layer on top of
//               DeletedBufferChain, which handles the actual
//               allocation.  This class just provides the
//               typecasting.
//
//               Of course, this trick of maintaining the deleted
//               object chain won't work in the presence of
//               polymorphism, where you might have many classes that
//               derive from a base class, and all of them have a
//               different size--unless you instantiate a DeletedChain
//               for *every* kind of derived class.  The
//               ALLOC_DELETED_CHAIN macro, below, is designed to make
//               this easy.
////////////////////////////////////////////////////////////////////
template<class Type>
class DeletedChain {
public:
  INLINE Type *allocate(size_t size, TypeHandle type_handle);
  INLINE void deallocate(Type *ptr, TypeHandle type_handle);

  INLINE bool validate(const Type *ptr);

  static INLINE ReferenceCount *make_ref_ptr(void *ptr);
  static INLINE ReferenceCount *make_ref_ptr(ReferenceCount *ptr);

private:
  INLINE void init_deleted_chain();

  DeletedBufferChain *_chain;
};

////////////////////////////////////////////////////////////////////
//       Class : StaticDeletedChain
// Description : This template class is used to conveniently
//               declare a single instance of the DeletedChain
//               template object, above, for a particular type.
//
//               It relies on the fact that the compiler and linker
//               should unify all references to this static pointer
//               for a given type, as per the C++ spec. However, this
//               sometimes fails; and if the compiler fails to do
//               this, it mostly won't be a big deal; it just means
//               there will be multiple unrelated chains of deleted
//               objects for a particular type.  This is only a
//               problem if the code structure causes objects to be
//               allocated from one chain and freed to another, which
//               can lead to leaks.
////////////////////////////////////////////////////////////////////
template<class Type>
class StaticDeletedChain {
public:
  INLINE static Type *allocate(size_t size, TypeHandle type_handle);
  INLINE static void deallocate(Type *ptr, TypeHandle type_handle);

  INLINE static bool validate(const Type *ptr);

  static DeletedChain<Type> _chain;
};

#ifdef USE_DELETED_CHAIN
// Place this macro within a class definition to define appropriate
// operator new and delete methods that take advantage of
// DeletedChain.
#define ALLOC_DELETED_CHAIN(Type)                            \
  inline void *operator new(size_t size) {                   \
    return (void *)StaticDeletedChain< Type >::allocate(size, get_type_handle(Type)); \
  }                                                          \
  inline void *operator new(size_t size, void *ptr) {        \
    return ptr;                                              \
  }                                                          \
  inline void operator delete(void *ptr) {                   \
    StaticDeletedChain< Type >::deallocate((Type *)ptr, get_type_handle(Type)); \
  }                                                          \
  inline void operator delete(void *, void *) {              \
  }                                                          \
  inline static bool validate_ptr(const void *ptr) {         \
    return StaticDeletedChain< Type >::validate((const Type *)ptr); \
  }

// Use this variant of the above macro in cases in which the compiler
// fails to unify the static template pointers properly, to prevent
// leaks.
#define ALLOC_DELETED_CHAIN_DECL(Type)                       \
  inline void *operator new(size_t size) {                   \
    return (void *)_deleted_chain.allocate(size, get_type_handle(Type)); \
  }                                                          \
  inline void *operator new(size_t size, void *ptr) {        \
    return ptr;                                              \
  }                                                          \
  inline void operator delete(void *ptr) {                   \
    _deleted_chain.deallocate((Type *)ptr, get_type_handle(Type)); \
  }                                                          \
  inline void operator delete(void *, void *) {              \
  }                                                          \
  inline static bool validate_ptr(const void *ptr) {         \
    return _deleted_chain.validate((const Type *)ptr);       \
  }                                                          \
  static DeletedChain< Type > _deleted_chain;

// When you use ALLOC_DELETED_CHAIN_DECL in a class body, you must
// also put this line in the .cxx file defining that class body.
#define ALLOC_DELETED_CHAIN_DEF(Type)                        \
  DeletedChain< Type > Type::_deleted_chain;

#else  // USE_DELETED_CHAIN

#define ALLOC_DELETED_CHAIN(Type)                            \
  inline static bool validate_ptr(const void *ptr) {         \
    return (ptr != NULL);                                    \
  }
#define ALLOC_DELETED_CHAIN_DECL(Type)                       \
  inline static bool validate_ptr(const void *ptr) {         \
    return (ptr != NULL);                                    \
  }
#define ALLOC_DELETED_CHAIN_DEF(Type)

#endif  // USE_DELETED_CHAIN

#include "deletedChain.T"

#endif

