/*
    Copyright 2001 (c) by Boris Lutz,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.
 
    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.
 
    For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
    This software is provided under the terms of the GNU General Public 
    Licence. A full copy of the GNU GPL is provided in the file COPYING 
    found in the development root of Topsy.
*/
/*
	
    File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/User/Attic/HeapFreeTree.c,v $
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 2922 $
    Last Date of Change:   $Date: 2007-03-19 17:48:52 +0100 (Mon, 19 Mär 2007) $  by: $Author: baderma $
 
*/


#include "Support.h"
#include "Topsy.h"
#include "Lock.h"
#include "HeapAllocator.h"
#include "HeapFreeTree.h"



MemChunkHeadPtr_t popFreeTree(struct HeapAllocator_ctxt* ctxt, size_t min_size, short get_frag)
{
  /* start at the root and search for the smalles element which ist *
   * larger than or equal to min_size, remove it from the tree and  *
   * return it's address                                            */
  Error								err;
  MemChunkHeadPtr_t		ThisNode,
  LargerNode;		// this node must match unless the root node
  // is smaller than size in this case LargerNode
  // is NULL and we return NULL (nothing found)


  // get excl TREE-LOCK
  lock(&ctxt->TreeLock);
  // Find the matching node!
  ThisNode = ctxt->FreeTree;
  LargerNode = NULL;
  while (ThisNode && ThisNode->tree_fsize != min_size)
  {
    if (ThisNode->tree_fsize > min_size)
    {
#ifdef FRAG_FIRST_MATCH
      if (get_frag == GET_FRAGMENT && (ThisNode->flags & IS_FRAGMENTED_M) )
      {
        // when looking for a fragment we take the first match
        printf("popFreeTree: taking first fragment match\n");
        break;
      }
#endif
      if (get_frag == GET_CONTINOUS && (ThisNode->flags & IS_FRAGMENTED_M) )
      {
        printf("popFreeTree: size would fit in fragment but requested continous => go back to larger\n");
        ThisNode = LargerNode;
        break;
      }
      LargerNode = ThisNode;
      ThisNode = ThisNode->tree_smaller;
    }
    else
    {
      ThisNode = ThisNode->tree_larger;
    }
  }
  if (ThisNode == NULL)
  {
    ThisNode = LargerNode;
  }

  if (get_frag == GET_FRAGMENT && ThisNode && !(ThisNode->flags & IS_FRAGMENTED_M) )
  {
    // we need a fragment chunk! return NULL to have MM allocate one
    // release excl TREE-LOCK
    unlock(&ctxt->TreeLock);
    return NULL;
  }

  // remove node from tree
  err = removeFromFreeTree(ctxt, ThisNode, NO_LOCK);
  // release excl TREE-LOCK
  unlock(&ctxt->TreeLock);
  if (err == HA_OK)
  {
    return ThisNode;
  }
  else
  {
    printf("popFreeTree: remove failed\n");
    return NULL;
  }
}


Error addToFreeTree(struct HeapAllocator_ctxt* ctxt, MemChunkHeadPtr_t elem, size_t free_size)
{
  /* start at the root and step down untill we arrive at a node which is *
   * either equal in size or which on the smaller side but too small or  *
   * on the larger side but too large. Then insert the ne element in the *
   * tree and return HA_OK                                                */
  MemChunkHeadPtr_t		ThisNode;
  int									ElemInserted = 0;


  // get excl TREE-LOCK
  lock(&ctxt->TreeLock);
  if (ctxt->FreeTree == NULL)
  {
    // tree was empty
    ctxt->FreeTree = elem;
    elem->tree_smaller = NULL;
    elem->tree_larger = NULL;
    elem->tree_prev = NULL;		// root must not have a previous element
    elem->tree_fsize = free_size;
    // release excl TREE-LOCK
    unlock(&ctxt->TreeLock);
    return HA_OK;
  }
  else
  {
    // Find a place to add the node
    ThisNode = ctxt->FreeTree;
    while (ThisNode && ThisNode->tree_fsize != free_size)
    {
      if (ThisNode->tree_fsize > free_size)
      {
        if (ThisNode->tree_smaller)
        {
          // go further
          ThisNode = ThisNode->tree_smaller;
        }
        else
        {
          // insert in smaller subtree
          ThisNode->tree_smaller = elem;
          elem->tree_smaller = NULL;
          elem->tree_larger = NULL;
          elem->tree_prev = ThisNode;
          elem->tree_fsize = free_size;
          ElemInserted = 1;
          break;
        }
      }
      else
      {
        if (ThisNode->tree_larger)
        {
          // go further
          ThisNode = ThisNode->tree_larger;
        }
        else
        {
          // insert in the larger subtree
          ThisNode->tree_larger = elem;
          elem->tree_smaller = NULL;
          elem->tree_larger = NULL;
          elem->tree_prev = ThisNode;
          elem->tree_fsize = free_size;
          ElemInserted = 1;
          break;
        }
      }
    }
    if (ElemInserted == 0)
    {
      // we have an equivalent element so add it in the larger
      // subtree (convention)
      elem->tree_larger = ThisNode->tree_larger;
      if (ThisNode->tree_larger)
      {
        ((MemChunkHeadPtr_t)(ThisNode->tree_larger))->tree_prev = elem;
      }
      ThisNode->tree_larger = elem;
      elem->tree_smaller = NULL;
      elem->tree_prev = ThisNode;
      elem->tree_fsize = free_size;
    }
    // release excl TREE-LOCK
    unlock(&ctxt->TreeLock);
    return HA_OK;
  }
}


Error removeFromFreeTree(struct HeapAllocator_ctxt* ctxt, MemChunkHeadPtr_t rm_node, short use_lock)
{
  MemChunkHeadPtr_t		TempNode;


  // remove node and relink tree
  if (use_lock != NO_LOCK)
  {
    // get excl TREE-LOCK
    lock(&ctxt->TreeLock);
  }
  if (rm_node && rm_node->tree_fsize == 0)
  {
    // either somebody was faster or I got the wrong element :(
    // release excl TREE-LOCK
    unlock(&ctxt->TreeLock);
    return HA_FAILED;
  }
  //-BL	printf("rm %p  larger: %p  smaller: %p  pre: %p  (root = %p)\n", rm_node,
  //-BL			rm_node->tree_smaller, rm_node->tree_larger, rm_node->tree_prev,
  //-BL			ctxt->FreeTree);
  if (rm_node && rm_node->tree_prev)
  {
    // check which branch we are on
    if ( ((MemChunkHeadPtr_t)(rm_node->tree_prev))->tree_larger == rm_node)
    {
      /*** larger bracnch ***/
      // check how many and which branches we have as descendants
      if (rm_node->tree_larger == NULL)
      {
        if (rm_node->tree_smaller)
        {
          ((MemChunkHeadPtr_t)(rm_node->tree_smaller))->tree_prev = rm_node->tree_prev;
        }
        ((MemChunkHeadPtr_t)(rm_node->tree_prev))->tree_larger = rm_node->tree_smaller;
      }
      else if (rm_node->tree_smaller == NULL)
      {
        // rm_node->tree_larger exists if not the upper check would have been triggered
        ((MemChunkHeadPtr_t)(rm_node->tree_larger))->tree_prev = rm_node->tree_prev;
        ((MemChunkHeadPtr_t)(rm_node->tree_prev))->tree_larger = rm_node->tree_larger;
      }
      else
      {
        // we have two subtrees, so we add the smaller subtree after
        // the smallest element of the larger subtree
        ((MemChunkHeadPtr_t)(rm_node->tree_larger))->tree_prev = rm_node->tree_prev;
        ((MemChunkHeadPtr_t)(rm_node->tree_prev))->tree_larger = rm_node->tree_larger;
        TempNode = rm_node->tree_larger;
        while (TempNode->tree_smaller != NULL)
        {
          TempNode = TempNode->tree_smaller;
        }
        TempNode->tree_smaller = rm_node->tree_smaller;
        ((MemChunkHeadPtr_t)(rm_node->tree_smaller))->tree_prev = TempNode;
      }
    }
    else
    {
      /*** smaller branch ***/
      // check how many and which branches we have as descendants
      if (rm_node->tree_larger == NULL)
      {
        if (rm_node->tree_smaller)
        {
          ((MemChunkHeadPtr_t)(rm_node->tree_smaller))->tree_prev = rm_node->tree_prev;
        }
        ((MemChunkHeadPtr_t)(rm_node->tree_prev))->tree_smaller = rm_node->tree_smaller;
      }
      else if (rm_node->tree_smaller == NULL)
      {
        // rm_node->tree_larger exists if not the upper check would have been triggered
        ((MemChunkHeadPtr_t)(rm_node->tree_larger))->tree_prev = rm_node->tree_prev;
        ((MemChunkHeadPtr_t)(rm_node->tree_prev))->tree_smaller = rm_node->tree_larger;
      }
      else
      {
        // we have two subtrees, so we add the smaller subtree after
        // the smallest element of the larger subtree
        ((MemChunkHeadPtr_t)(rm_node->tree_larger))->tree_prev = rm_node->tree_prev;
        ((MemChunkHeadPtr_t)(rm_node->tree_prev))->tree_smaller = rm_node->tree_larger;
        TempNode = rm_node->tree_larger;
        while (TempNode->tree_smaller != NULL)
        {
          TempNode = TempNode->tree_smaller;
        }
        TempNode->tree_smaller = rm_node->tree_smaller;
        ((MemChunkHeadPtr_t)(rm_node->tree_smaller))->tree_prev = TempNode;
      }
    }
  }
  // clear the entries to make sure no bad links will exist. Nice for
  // debugging too since we will run into SIGSEGV if we follow the pointer
  if (rm_node)
  {
    if (rm_node == ctxt->FreeTree)
    {
      // the tree is either empty now or needs a new root node
      if (rm_node->tree_smaller == NULL)
      {
        ctxt->FreeTree = rm_node->tree_larger;
      }
      else if (rm_node->tree_larger == NULL)
      {
        ctxt->FreeTree = rm_node->tree_smaller;
      }
      else
      {
        // we have two subtrees, so we add the smaller subtree after
        // the smallest element of the larger subtree
        TempNode = (ctxt->FreeTree = rm_node->tree_larger);
        while (TempNode->tree_smaller != NULL)
        {
          TempNode = TempNode->tree_smaller;
        }
        TempNode->tree_smaller = rm_node->tree_smaller;
        ((MemChunkHeadPtr_t)(rm_node->tree_smaller))->tree_prev = TempNode;
      }
      if (ctxt->FreeTree)
      {
        (ctxt->FreeTree)->tree_prev = NULL;
      }
    }
    rm_node->tree_smaller = NULL;
    rm_node->tree_larger = NULL;
    rm_node->tree_prev = NULL;
  }
  if (use_lock != NO_LOCK)
  {
    // release excl TREE-LOCK
    unlock(&ctxt->TreeLock);
  }
  return HA_OK;
}


void dumpFreeTree(MemChunkHeadPtr_t rootNode)
{
  if (rootNode == NULL)
    return;
  dumpFreeTree(rootNode->tree_smaller);
  printf("Tree node %p contains a %s block with %ld bytes of free space\n",
         rootNode, (rootNode->flags & IS_FRAGMENTED_M) ? "fragmented" : "continous",
         rootNode->tree_fsize);
  dumpFreeTree(rootNode->tree_larger);
}
