
//
// AT_OBJECTS.H -- ref-counted VM objects, works in conjunction with AT_OBJPTR.H
//
// These templates provide a convenient encapsulation of reference-counted,
// (if necessary) VM objects and arrays.
//
// For an object of type T, construct an Obj<T>; to get access to the object,
// construct a Ptr<T>, which can be used like an auto_ptr.  Constructors take
// a dim parameter; set >1 for arrays, 0 for null objects.  You may also
// construct or pass a ConstObj, which may not be altered.
//
// Objects are reference counted with copy-on-write.
//
// Any object whose copy or delete semantics are not bitwise must be derived
// from ObjBase.
//
// Until someone implements member templates, we must implement special
// constructors for Obj casting.
//

#ifndef AT_OBJECTS_H
#define AT_OBJECTS_H

#include <new>

#include <cstdio>

#ifndef NDEBUG
#include <typeinfo>
#endif

#include "at_defs.h"

// ================================== OBJECTS =================

class ObjBase
{
public :
  virtual ~ObjBase
    ()
  = 0;

  // placement clone - required for non-bitwise copy
  virtual void placeClone
    (void *pNew)      // clone this to pNew
    const
  = 0;
};

inline ObjBase::~ObjBase
  ()
{};

class SEARCH_DECL refBuf;

template <class T> class PtrConst;
template <class T> class ConstObj
{
  friend PtrConst<T>;

public :
  ConstObj
    (uint32 dim = 1);

  ConstObj
    (const ConstObj<T>& rhs);

  ConstObj& operator=
    (const ConstObj<T>& rhs);

  bool operator!
    ()
    const;

  ~ConstObj
    ();

  bool operator==
    (const ConstObj& rhs)
    const;

  uint getDim
    ()
    const;

  // object cast - until we have member templates
  #define CAST_OBJ(T, o) Obj<T>(((o).getRB()), 0)
  refBuf* getRB
    () const
  { return rb; };
  ConstObj
    (refBuf* r,
     int);
  // end object cast

protected :
  void release
    ();

  refBuf *rb;
};

template <class T> class Obj : public ConstObj<T>
{
public :
  Obj
    (uint32 dim = 1);

  Obj
    (const Obj<T>& rhs);
  /*
  Obj<T>& operator=
    (const Obj<T>& rhs);
  */
  // cast
  Obj
    (refBuf* r,
     int) :
      ConstObj<T>(r, 0)
  {}
};

// ============================================================
// ================================== INLINES =================

// move these to at_objects.cpp as soon as
// Borland supports template export

// ================================== CONST OBJ ===============


template <class T> inline
ConstObj<T>::ConstObj
  (uint32 dim) :
    rb(dim ? (new (sizeof(T), dim) refBuf()) : 0)
{
}

template <class T> inline
ConstObj<T>::ConstObj
  (const ConstObj<T>& rhs) :
    rb(rhs.rb)
{
  if (rb)
    rb->ref();
}

template <class T> inline
void ConstObj<T>::release
  ()
{
  T* t;
  if (rb && (t = reinterpret_cast<T*>(rb->unref())))
  {
    if (rb->isTouched())
    {
      t->~T();
//    #ifndef NDEBUG
//    memset(t, '*', rb->getTSize());
//    #endif
    }
    delete rb;
  }
}

template <class T> inline
ConstObj<T>& ConstObj<T>::operator=
  (const ConstObj<T>& rhs)
{
  if (rb != rhs.rb)
  {
    release();
    if (rhs.rb)
      rhs.rb->ref();
    rb = rhs.rb;
  }
  return *this;
}

template <class T> inline
ConstObj<T>::~ConstObj
  ()
{
  release();
}

template <class T> inline
bool ConstObj<T>::operator!
  ()
  const
{
  return !rb;
}

template <class T> inline
bool ConstObj<T>::operator==
  (const ConstObj<T>& rhs)
  const
{
  return rb == rhs.rb;
}

template <class T> inline
uint ConstObj<T>::getDim
  ()
  const
{
  return rb ? rb->getDim() : 0;
}

// cast
template <class T> inline
ConstObj<T>::ConstObj
  (refBuf* r,
   int) :
    rb(r)
{
  if (rb)
    rb->ref();
}

// ================================== OBJ =====================

template <class T> inline
Obj<T>::Obj
  (uint32 dim) :
    ConstObj<T>(dim)
{
}


template <class T> inline
Obj<T>::Obj
  (const Obj<T>& rhs) :
    ConstObj<T>(rhs)
{
}
/*
template <class T> inline
Obj<T>& Obj<T>::operator=
  (const Obj<T>& rhs)
{
  if (rb != rhs.rb)
  {
    if (rhs.rb)
      rhs.rb->ref();
    if (rb)
      rb->unref();
    rb = rhs.rb;
  }

  return *this;
}
*/
// ============================================================
// ================================== REF BUF =================

// this would also be a template if we had template export

class SEARCH_DECL refBuf
{
public :
  refBuf
    ();

  void* operator new
    (size_t thisSize,
     uint TSize,                      // size of object (T)
     uint dim);
  void* operator new
    (size_t thisSize,
     const refBuf *const sameSize);   // doesn't copy; just uses size

  void operator delete
    (void *p);

  uint getDim
    ()
    const
  { return dim; };

  uint getTSize
    ()
    const
  { return t_size; };

  bool isTouched
    ()
    const
  { return touched; };

  bool shared
    ()
    const;

  void ref
    ()
    const;

  void* lock
    ();
  void* unlock
    ();

  // !=0 -> obj should be deleted
  /* T* */ void *unref
    ();

private :
  // can't do these
  void* operator new
    (size_t)
  { return 0; };
  refBuf
    (const refBuf& rhs,
     uint32 = 0) :
      dim(0),
      t_size(0)
  {};

  const uint dim;
  const uint t_size;

  mutable uint refs;
  mutable bool touched;

  byte buf[];
};

#endif

