/**
 * These functions define the way that the VM will allocate
 * deallocate, and access memory
 * @author James Liang
 * @author Christian Grothoff
 **/

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

#include "mem.h"

#if MEMORY_TRACKING
struct memtracking_block {
  void * start;
  void * end;
  struct memtracking_block * next;
};

static struct memtracking_block * first = NULL;
#endif

/**
 * Allocate a block of memory from the OS.
 * @param size size of the block in bytes
 * @return a reference to the memory
 **/
void * getmem(int size) {
  void * retVal = malloc(size);
  if (retVal == NULL) {
      fprintf(stderr, "failed to allocate memory\n");
      abort();
  }
  registerMem(retVal,size);
  return retVal;
}

/**
 * Return a block of memory to the OS
 * @param ptr the reference to the block as obtained from getmem
 **/
void freemem(void * ptr) {
  unregisterMem(ptr);
  free(ptr);
}

/**
 * Lets the memory object know about a block of memory that
 * is considered accessible by the interpreted code.
 *
 * Used mostly for the mapped bootimage.  
 * Most other forms of memory accessible by the
 * interpreted code with be retrieved by getmem() which
 * calles registerMem.
 **/ 
void registerMem(void * begin, int length) {
#if MEMORY_TRACKING
  struct memtracking_block * block = (struct memtracking_block *)
    malloc(sizeof(struct memtracking_block));
  block->start = begin;
  block->end = begin+length;
  block->next = first;
  first = block;
#endif
}

/**
 * Unregister a block of memory at a certain beginning position.
 * If memory registration is active and the block was never
 * registered, an error message is printed.
 **/ 
void unregisterMem(void * begin) {
#if MEMORY_TRACKING
  struct memtracking_block * deadBlock=first;
  if (begin==NULL) 
    return;
  if (deadBlock->start==begin) {
    first = first->next;
    free(deadBlock);
    return;
  }
  while (deadBlock->next != NULL) {  
    if (deadBlock->next->start==begin) {
      deadBlock->next=deadBlock->next->next;
      free(deadBlock->next);
      return;
    }
    deadBlock=deadBlock->next;
  }
  fprintf(stderr,
	  "ERROR: cannot unregister block of memory that was never registered in the first place\n");
#endif
}

/**
 * Verify that pointer is a valid address in memory, 
 * prints a warning if the address is invalid. Can do 
 * nothing if debugging memory is turned off.
 *
 * @param pointer the address to verify
 * @return 0 on error, 1 on ok
 **/
int checkPointer(void * pointer) {
#if MEMORY_TRACKING
  struct memtracking_block * current=first;
  while (current!=NULL) {
    if (current->start<=pointer && current->end >=pointer)
      return 1;
    current = current->next;
  }
  return 0;
#else
  return 1;
#endif
}
