/* Copyright 1996 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Title:    heap.c
 * Purpose:  provide malloc-style heap allocation in a flex block
 * Author:   NR, IDJ
 * Status:   under development
 * History:  20-Mar-89  IDJ created
 *           13-Apr-89  IDJ modified to loop if you can't allocate enough
 *                          space in a single increment to flex block.
 *           14-Apr-89  IDJ applications which repeatedly ask for large
 *                      chunks of memory (without doing much in between) will
 *                      delay events for other applications.
 *            5-May-89  IDJ made slot shrink when doing heap_free()
 *           21-Jun-89  IDJ made shrinking of slot conditional on flag to
 *                      heap_init().
 *           21-Jun-89  IDJ removed different error codes for failures to
 *                      do a heap_alloc.
 *            7-Dec-89  IDJ No need for this stuff anymore. New malloc does
 *                      it all
 *
 */

#include "werr.h"
#include "heap.h"
#include "os.h"
#include "flex.h"
#include "msgs.h"
#include <stdlib.h>
#include "h.verintern.messages"

#if FALSE
static int *Heap;
static int heap__initialised=0;
static BOOL heap__shrinkflex = TRUE;

#define heap__BLOCKSIZE 4096
#define OS_Heap 0x1D

void heap__check(void)
{
  if(heap__initialised == 0)
    werr(TRUE, msgs_lookup(MSGS_Heap5));
}
#endif

void heap_init(BOOL heap_shrink)
{

#if FALSE
  os_regset r;
  os_error *e;

  /* is the flex block to be shrunk if poss. on heap free */
  heap__shrinkflex = heap_shrink;

  /* create flex block to be used as heap */
  if (!flex_alloc((flex_ptr)&Heap, heap__BLOCKSIZE))
    werr (TRUE, msgs_lookup(MSGS_heap1));

  /* initialise heap as being in the flex block we just created */
  r.r[0] = 0;
  r.r[1] = (int) Heap;
  r.r[3] = flex_size((flex_ptr)&Heap);
  if ((e = os_swix(OS_Heap, &r)) != 0)
    werr (TRUE, e->errmess);

  heap__initialised = 1;
#endif
}



void *heap_alloc(unsigned int size)
{
    return (malloc(size));
}



/* old implementation of heap stuff */
#if FALSE
  os_regset r;
  os_error *e;

  heap__check();
  /* see if we can satisfy request in heap as it is */
  r.r[0] = 2;   /* claim block from heap manager */
  r.r[1] = (int) Heap;
  r.r[3] = (int)size;


  while((e = os_swix(OS_Heap, &r)) != 0)
  {
    /* see if failure to allocate block */

    if (e->errnum == 0x0184 /* HeapFail_Alloc */)
    {
      int *oldHeap = Heap;
      if (flex_extend((flex_ptr)&Heap, flex_size((flex_ptr)&Heap)
                                     + heap__BLOCKSIZE))
      {
        if (Heap == oldHeap)    /* heap hasn't moved */
        {
          os_error *ext;

          r.r[0] = 5;
          r.r[1] = (int)Heap;
          r.r[3] = (int)heap__BLOCKSIZE;
          if ((ext = os_swix(OS_Heap, &r)) != 0)
            werr(TRUE, msgs_lookup(MSGS_heap2), ext->errmess);

          /*Heap[3] += heap__BLOCKSIZE; Removed - bug fixed in RISCOS */

          r.r[0] = 2;                   /* claim block again */
          r.r[1] = (int) Heap;
          r.r[3] = (int) size;
          /*e = os_swix(OS_Heap, &r);*/
        }
        else
          return (void *)0;  /* flex has moved the heap */
      }
      else
        return (void *)0;  /* the flex area is full (ie. out of memory) */
    }
    else
      werr(1, msgs_lookup(MSGS_heap3));

  }

  if (e == 0)
    return (void *)r.r[2];
  else
    return (void *)0;
}

#endif



void heap_free(void *heapptr)
{
    free(heapptr);
}



/* old implementation of heap stuff */
#if FALSE
  os_regset r;
  os_error *e;
  static int free_count = 0;
  int change_flex;

  heap__check();
  if(!heap__shrinkflex)
  {
     r.r[0] = 3;   /* release block */
     r.r[1] = (int) Heap;
     r.r[2] = (int) heapptr;
     if((e = os_swix(OS_Heap, &r)) != 0)
       werr(FALSE, msgs_lookup(MSGS_heap4), e->errmess);
  }
  else
  {
    /* examine the size of the block being freed */
    r.r[0] = 6;
    r.r[1] = (int)Heap;
    r.r[2] = (int)heapptr;
    if((e = os_swix(OS_Heap, &r)) != 0)
      werr(FALSE, msgs_lookup(MSGS_heap4), e->errmess);

    /* keep a count of max free space */
    free_count += r.r[3];

    r.r[0] = 3;   /* release block */
    r.r[1] = (int) Heap;
    r.r[2] = (int) heapptr;
    if((e = os_swix(OS_Heap, &r)) != 0)
      werr(FALSE, msgs_lookup(MSGS_heap4), e->errmess);

    r.r[0] = 5;   /* extend heap (with neg. increment to shrink) */
    r.r[1] = (int)Heap;
    r.r[3] = -free_count;
    if((e = os_swix(OS_Heap, &r)) != 0)
      change_flex = r.r[3]; /* a positive value if failed */
    else
      change_flex = -r.r[3]; /* a negative (preserved) value if ok */
    free_count -= change_flex; /* amend count accordingly */
    flex_extend((flex_ptr)&Heap, flex_size((flex_ptr)&Heap)
                                       - change_flex);
  }
}

#endif