// ---------------------------------------------------------------------------
// - Stack.cpp                                                               -
// - standard object library - object stack class implementation             -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - 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.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2008 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "Stack.hpp"
#include "Integer.hpp"
#include "Exception.hpp"
#include "cmem.hpp"

namespace afnix {
  
  // -------------------------------------------------------------------------
  // - class section                                                         -
  // -------------------------------------------------------------------------

  // create a default stack
  
  Stack::Stack (void) {
    d_size = 8 * c_pagesize ();
    p_base = (Object**) c_mmap (d_size * sizeof (Object*));
    p_top  = p_base + d_size - 1;
    p_sp   = p_base;
    p_fp   = p_base;
  }
  
  // destroy this stack

  Stack::~Stack (void) {
    unwind ();
    c_munmap (p_base, d_size * sizeof (Object*));
  }

  // return the class name

  String Stack::repr (void) const {
    return "Stack";
  }

  // push an object on top of the stack

  void Stack::push (Object* object) {
    wrlock ();
    try {
      // check for stack overflow
      if (p_sp >= p_top) resize (d_size * 2);
      // push the object
      *p_sp++ = Object::iref (object);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // pop an object from the stack

  Object* Stack::pop (void) {
    wrlock ();
    try {
      if (p_sp == p_base) {
	throw Exception ("stack-error","out of bound stack pop");
      }
      Object* result = *--p_sp;
      if (p_fp > p_sp) p_fp = p_sp;
      Object::tref (result);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return true if the stack is empty

  bool Stack::empty (void) const {
    rdlock ();
    bool result = (p_sp == p_base);
    unlock ();
    return result;
  }

  // return the current stack pointer

  Object** Stack::getsp (void) const {
    rdlock ();
    Object** result = p_sp;
    unlock ();
    return result;
  }
  
  // return the current frame pointer

  Object** Stack::getfp (void) const {
    rdlock ();
    Object** result = p_fp;
    unlock ();
    return result;
  }
  
  // set the current frame pointer
 
  void Stack::setfp (Object** fp) {
    wrlock ();
    try {
      if ((p_sp < p_base) || (fp > p_sp)) {
	throw Exception ("stack-error", "out of bound frame pointer");
      }
      p_fp = fp;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // unwind the stack to it base pointer
 
  void Stack::unwind (void) {
    wrlock ();
    try {
      while (p_sp != p_base) Object::cref (pop ());
      p_fp = p_base;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }
 
  // unwind the stack by matching the fp
  
  void Stack::unwind (Object** sp, Object** fp) {
    wrlock ();
    try {
      while (p_sp != sp) Object::cref (pop ());
      p_fp = fp;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return an argument by index in reference to the frame pointer
 
  Object* Stack::get (const long index) const {
    rdlock ();
    try {
      Object** ptr = p_fp + index;
      if (ptr >= p_sp) {
	throw Exception ("stack-exception", "out of bound stack access");
      }
      Object* result = *ptr;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    } 
  }
 
  // set a stack value by index
  
  void Stack::set (const long index, Object* object) {
    wrlock ();
    try {
      Object** ptr = p_fp + index;
      if (ptr >= p_sp) {
	throw Exception ("stack-exception", "out of bound stack access");
      }
      Object::iref (object);
      Object::dref (*ptr);
      *ptr = object;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // resize the stack by a certain amount

  void Stack::resize (const long size) {
    wrlock ();
    try {
      long spoff = (long) (p_sp - p_base);
      long fpoff = (long) (p_fp - p_base);
      p_base = (Object**) c_mremap (p_base, d_size * sizeof (Object*), 
				    size * sizeof (Object*));
      p_top  = p_base + size - 1;
      d_size = size;
      p_sp   = p_base + spoff;
      p_fp   = p_base + fpoff;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }
}
