/*
    ####             #    #     # #
    #   #            #    #       #          The FreeWare C library for 
    #   #  ##   ###  #  # #     # ###             RISC OS machines
    #   # #  # #     # #  #     # #  #   ___________________________________
    #   # ####  ###  ##   #     # #  #                                      
    #   # #        # # #  #     # #  #    Please refer to the accompanying
    ####   ### ####  #  # ##### # ###    documentation for conditions of use
    ________________________________________________________________________

    File:    Mem.h
    Author:  Copyright  1993, 1994, 1995 Jason Williams and Jason Howat
    Version: 2.00 (18 Jun 1995)
    Purpose: Dynamic memory manager
*/

#ifndef __Desk_Mem_h
#define __Desk_Mem_h

#ifdef __cplusplus
	extern "C" {
#endif


/* "Flex" is the RISC OS Lib 'flexible' malloc for desktop tasks
 * "Mem" is DeskLib's equivalent thereof.
 *
 *  Mem memory chunks are held in a 'heap' of memory above the area grabbed
 *  by the SharedCLibrary for mallocs and stackspace, extending up to the end
 *  of the WimpSlot, which is adjusted up & down as necessary.
 *
 *  The major feature that externally distinguishes Mem from flex() is that
 *  the heap is NOT compacted automatically (flex keeps the entire heap
 *  compacted at all times). This has several advantages:
 *    - You can rely on pointers remaining constant at all times between
 *      calls to Desk_Mem_Compact() (and other Desk_Mem_ calls if you allow it)
 *    - everything is a lot faster if you are allocating and/or deallocating
 *      many chunks in one go.
 *
 *  The idea behind this is that you simply call Desk_Mem_Compact before calling
 *  Desk_Wimp_Poll - this returns free memory to the Wimp as effectively as the
 *  flex system did, but saves us from having to waste time on multiple
 *  compactions between Desk_Wimp_Polls.
 *
 *  Automatic compaction (a la flex) can be turned on (or off) at any time,
 *  so the Mem system can be made to emulate Desk_flex_ if you really want it. To
 *  do this, set Desk_mem_autocompact to the values described below.
 *  NOTE that by default, Mem will only autocompact if it has no choice, but
 *  *may* autocompact during any Desk_Mem_ call.
 */


#ifndef __Desk_Core_h
#include "Desk.Core.h"
#endif


typedef void *Desk_mem_anchor;


/*  Compaction control
 *  Apart from compacting when you call Desk_Mem_Compact(), Mem will also compact
 *  at the following times, depending on the value of Desk_mem_autocompact
 *
 *    Desk_mem_NOCOMPACT:    Auto compaction never occurs. This may be useful if
 *                      you wish to guarantee that chunks ONLY move when you
 *                      call Desk_Mem_Compact().
 *
 *    Desk_mem_PARTCOMPACT:  Auto compaction occurs whenever compaction might make
 *                      an otherwise impossible memory claim possible.  This
 *                      is the recommended (and default) setting.
 *
 *    Desk_mem_FULLCOMPACT:  Auto compaction occurs every time a chunk is freed
 *                      (the way that RISC OS Lib flex() operates). This is
 *                      not reccommended, as it can be very inefficient.
 *
 *  NOTE that the value of Desk_mem_autocompact may be changed at any time and
 *  will take immediate effect. Therefore, if you wish to ensure that
 *  pointers into Mem chunks remain 'safe' for a short period of time, you
 *  can set Desk_mem_autocompact = Desk_mem_NOCOMPACT while procssing, and return it to
 *  its former value when finished.
 */

  extern int Desk_mem_autocompact;

typedef enum
{
  Desk_mem_NOCOMPACT   = 0,             /* ONLY compacts if Desk_Mem_Compact called  */
  Desk_mem_FASTCOMPACT = 1,             /* Compacts only if necessary           */
  Desk_mem_PARTCOMPACT = 1,
  Desk_mem_FULLCOMPACT = 2              /* Compacts on every heap change        */
} Desk_mem_compaction;



  /*  Desk_Mem_Initialise()
   *  Initialises the Mem system ready for use.
   *  Note that this locks down the malloc and stack memory area to the
   *  current WimpSlot size at the point of calling, and builds a Mem heap
   *  above this. Malloc and stack allocation will not be able to get more
   *  memory than is originally available in your WimpSlot, so get it right!
   *  Note that Desk_Mem_Initialise() provides a function to
   *  _kernel_register_slotextend() to stop the SharedCLibrary being able to
   *  overwrite the Mem heap with the stack/malloc chunks.
   */
extern Desk_bool Desk_Mem_Initialise(void);


  /*  Desk_Mem_Alloc()
   *  Attempts to allocate the given amount of memory (in bytes) in the Mem
   *  heap. Updates the anchor you pass in to point to this block, or to
   *  contain NULL if it is unable to allocate the requested memory. The
   *  returned block of memory starts at a word-aligned address.
   *  Returns Desk_bool_TRUE if it succeeded
   *
   *  If permitted to by the setting of Desk_mem_autocompact, this call MAY
   *  relocate other Mem chunks.
   */
extern Desk_bool Desk_Mem_Alloc(Desk_mem_anchor *anchor, int numbytes);


  /*  Desk_Mem_MidExtend()
   *  Attempts to alter the size of a Mem chunk.
   *  'at' is a byte-offset within the data chunk
   *  'by' is the number of bytes to extend by (negative to reduce the chunk)
   *  If 'by' is positive,
   *    'by' bytes of indeterminate value will be inserted at 'at',
   *    shifting the rest of the data up to make room
   *  else
   *    'by' bytes of data BELOW 'at' will be deleted by moving the
   *    data from 'at' onwards down by 'by' bytes.
   *
   *  Returns Desk_bool_TRUE if the extension was successful.
   *  The allocated memory starts at a word-aligned address
   *
   *  If permitted to by the setting of Desk_mem_autocompact, this call MAY
   *  relocate other Mem chunks.
   */
extern Desk_bool Desk_Mem_MidExtend(Desk_mem_anchor *anchor, int at, int by);


  /*  Desk_Mem_MoveAnchor
   *  Allows you to move an anchor from one variable to another.
   *  This allows you to allocate a chunk with a temporary anchor and then
   *  move the anchor into permanent storage at a later time (without having
   *  to allocate a new chunk and copy, as is necessary under flex)
   *
   *  Use with care - i.e. remember to check anchors to see if they are NULL
   *  (in which case you're using the wrong anchor!)
   *
   *  If all goes well (i.e. 'from' was a valid chunk), *from will be set to
   *  NULL and *to will now point at the chunk. From this point on, the
   *  anchor 'to' will be adjusted whenever the chunk is moved.
   *
   *  Otherwise, from is left as it was, and to will be set to NULL
   *
   *  example:
   *  {
   *    char *old, *new;
   *    Desk_Mem_Alloc((Desk_mem_anchor *) &old, 1024);        |* Get some memory *|
   *    if (old != NULL)
   *    {
   *      old[5] = 'a';
   *      Desk_Mem_MoveAnchor((Desk_mem_anchor *) &old, (Desk_mem_anchor *) &new);
   *          |* if 'old' was valid, 'new' now points to where old was, *|
   *          |* and old is now NULL                                    *|
   *
   *      if (new == NULL)    printf("Error - old was invalid! \n");
   *      if (new[5] != 'a')  printf("Error - Jason's code is buggy! \n");
   */
extern void Desk_Mem_MoveAnchor(Desk_mem_anchor *from, Desk_mem_anchor *to);


  /*  Desk_Mem_Free()
   *  Releases a chunk of memory back to the free pool.
   *  The contents of the anchor will be set to NULL to indicate it is no
   *  longer a valid pointer.
   *  If permitted to by the contents of Desk_mem_autocompact, this call MAY
   *  relocate other Mem chunks.
   */
extern void Desk_Mem_Free(Desk_mem_anchor *anchor);


  /*  Desk_Mem_Compact()
   *  This call compacts the Mem heap, moving all free space to the end of
   *  the chunk, and giving back as much memory as possible to the Wimp. This
   *  may result in some Mem chunks moving, so you cannot rely on anchors
   *  remaining the same across this call.
   *  Ideally, you should call this just before calling Desk_Wimp_Poll, to ensure
   *  the Wimp has all possible available memory (and also keep the heap
   *  tidy).
   *  Note that this may be automatically called by other Desk_Mem_ functions IF
   *  Desk_mem_autocompact allows it. However, it will NEVER be called if
   *  Desk_mem_autocompact == Desk_mem_NOCOMPACT
   */
extern void Desk_Mem_Compact(void);


  /*  Desk_Mem_Size()
   *  Returns the current size (in bytes) of a Mem chunk
   */
extern int  Desk_Mem_Size(Desk_mem_anchor *anchor);


  /*  Desk_Mem_CheckHeap()
   *  Returns Desk_bool_TRUE if the heap data structure is valid (i.e. the links are
   *  all intact and anchors are consistent)
   */
extern Desk_bool Desk_Mem_CheckHeap(void);





#ifdef Desk_DeskLib_DEBUG
	#ifdef Desk__making_Mem
		#include "Debug.h"
		#define Desk_debug_level Desk_mem_debuglevel
	#endif
	
	extern int	Desk_mem_debuglevel;
/*
In the debug version of DeskLib, this is the Mem library's own version
of Desk_debug_level. It is initially 0; a program can set it to different
values to turn on different debug ouputs in the Mem library.
 */
#endif


#ifdef __cplusplus
}
#endif
   

#endif
