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

    File:    DeskMem.h
    Author:  Copyright  1995 Julian Smith, based on idea from Ijith 
                              Ponnampalavanar
    Version: 1.20 (18 Nov 1995)
    Purpose: Standard error-handling functions for allocating immovable bits
             of memory.
             Facilities for changing the raw allocation functions (they are
             initially ANSI C's malloc, realloc, calloc and free).
             Some functions for simple buffers.
    History: 1.00 (07 Sep 1995)
             1.10 (29 Sep 1995) JS Added Desk_DeskMem_Buffer* functions.
             1.11 (10 Nov 1995) JS Split things into separate .c files etc.
             1.12 (14 Nov 1995) JS Added support for debug DeskLib.
             1.20 (18 Nov 1995) JS Added support for changing the raw
                                   allocation functions - 
                                   Desk_DeskMem_SetAllocFunctions. Also added
                                   macros for accessing the raw functions
                                   without Desk_DeskMem_Malloc's error-checking.
             1.21 (13 Jun 1996) JS Desk_DeskMem_BufferInit sets buffer pointer
                                   to NULL rather than initialising to increment.
*/


#ifndef __Desk_DeskMem_h
#define __Desk_DeskMem_h

#ifdef __cplusplus
	extern "C" {
#endif

#include <stdlib.h>

#include <stddef.h>

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


void	*Desk_DeskMem_Malloc( size_t size);
/*
A safe replacement for 'malloc'. Calls Desk_Error2_Handle if it can't
allocate 'size' bytes of memory, so never returns NULL.
 */

void	*Desk_DeskMem_Calloc( size_t num, size_t size);
/*
A safe replacement for 'calloc'. Calls Desk_Error2_Handle if it can't
allocate memory, so never returns NULL.
 */

void	*Desk_DeskMem_Realloc( void *ptr, size_t size);
/* 
A safe replacement for 'realloc'. Calls Desk_Error2_Handle if it can't
reallocate 'ptr' to point to 'size' bytes of memory, so never returns
NULL.
 */

void	Desk_DeskMem_Free( void *ptr);
/*
Use like 'free()'.
 */



#define Desk_DeskMem_MallocType( type)	(type *) Desk_DeskMem_Malloc( sizeof( type))
/*
Use like:
bigtypedef*	object = Desk_DeskMem_MallocType( bigtypedef);

Instead of:
bigtypedef*	object = (bigtypedef *) Desk_DeskMem_Malloc( sizeof( bigtypedef));
 */

#define	Desk_DeskMem_CallocType( n, type)	((type *) Desk_DeskMem_Calloc( n, sizeof( type)))
/*
Use like:
bigtypedef	*array = Desk_DeskMem_CallocType( 30, bigtypedef);
 */



typedef struct Desk_deskmem_errorblock	{
	size_t	size;
	void*	ptr;
	}
	Desk_deskmem_errorblock;
/*
This structure is used to contain info on DeskMem errors, using the
Error2 library's error-handling system.
 */

#ifdef Desk__using_SDLS
	extern	Desk_deskmem_errorblock*	Desk_DeskMem__Ref_errorblock( void);
#endif


#if defined( Desk__using_SDLS) && !defined( Desk__making_DeskMem)
	#define	Desk_deskmem_globalerror	(*Desk_DeskMem__Ref_errorblock())
#else
	extern Desk_deskmem_errorblock	Desk_deskmem_globalerror;
/*
This is a global structure which contains details of any error from the
last call to a DeskMem function.
 */
#endif





#ifdef Desk_DeskLib_DEBUG
	extern int	Desk_deskmem_debuglevel;
/*
In the debug version of DeskLib, this is the DeskMem library's own
version of Desk_debug_level. It is initially 0; any program can set it to
different values to turn on/off different debug ouputs in the DeskMem
library.
 */
#endif








/*
Here are some DeskMem things relating to changing the memory-allocation
functions etc.
 */

typedef void*	(*Desk_deskmem_mallocfn)	( size_t);		/* The type of malloc()	*/
typedef void*	(*Desk_deskmem_reallocfn)	( void *, size_t);	/* The type of realloc()*/
typedef void*	(*Desk_deskmem_callocfn)	( size_t, size_t);	/* The type of calloc()	*/
typedef void	(*Desk_deskmem_freefn)	( void *);		/* The type of free()	*/


typedef struct	{
	Desk_deskmem_mallocfn	malloc;
	Desk_deskmem_reallocfn	realloc;
	Desk_deskmem_callocfn	calloc;
	Desk_deskmem_freefn		free;
	}
	Desk_deskmem_functionsblock;	


void	Desk_DeskMem_SetAllocFunctions( 
		Desk_deskmem_mallocfn	m,
		Desk_deskmem_reallocfn	r,
		Desk_deskmem_callocfn	c,
		Desk_deskmem_freefn		f,
		Desk_deskmem_functionsblock*	oldfunctions
		);
/*
This sets the functions DeskMem will use for all future static memory
allocation. 

If 'oldfunctions' isn't NULL, it also returns the functions that DeskMem
was using prior to this call, by filling in the Desk_deskmem_functionsblock
pointed to by 'oldfunctions'.

This is so that the new allocation functions can store the old
functions. This could be useful if the new 'free' is called for a ptr
that isn't recognised - the new 'free()' function could try calling the
old function, or if the new functions are simple wrapper-functions.
 */


#ifdef Desk__using_SDLS
	extern	Desk_deskmem_functionsblock*	Desk_DeskMem__Ref_functionsblock( void);
#endif


#if defined( Desk__using_SDLS) && !defined( Desk__making_DeskMem)
	#define	Desk_deskmem__functions	(*Desk_DeskMem__Ref_functionsblock())
#else
	extern	Desk_deskmem_functionsblock	Desk_deskmem__functions;
/*
You shouldn't need to use this directly. It is used by all
Desk_DeskMem_(X)M/C/Realloc functions. It is initially set to contain the
standard ANSI C memory allocation functions: malloc, realloc, calloc and
free.
 */
#endif



/*
Note that if you are using the DeskMem DLL, the Desk_DeskMem_Raw* macros will
have to make a function-call to get the address of 'Desk_deskmem__functions'.
This will happen every time they are used.

If you are using Desk_DeskMem_RawMalloc etc and speed is important, you
should cache this address at the start of you program, and write your
own macros.

For example:

. |* main.c *|
. Desk_deskmem_functionsblock*	Desk_client_deskmem__functions;
. 
. #define RawMalloc( size)	(Desk_client_deskmem__functions->malloc( size))
. #define RawCalloc( num, size)	(Desk_client_deskmem__functions->calloc( num, size))
.  etc etc
. 
. int	main( void)
. {
. Desk_client_deskmem__functions = &Desk_deskmem__functions;
. ...
. }

 */

#define	Desk_DeskMem_RawMalloc( size)	(Desk_deskmem__functions.malloc( size))
/*
This gives direct access to whatever malloc function DeskMem is using.
 */
 
#define	Desk_DeskMem_RawRealloc( ptr, size)	(Desk_deskmem__functions.realloc( ptr, size))
/*
This gives direct access to whatever realloc function DeskMem is using.
 */
 
#define	Desk_DeskMem_RawCalloc( num, size)	(Desk_deskmem__functions.calloc( num, size))
/*
This gives direct access to whatever calloc function DeskMem is using.
 */
 
#define	Desk_DeskMem_RawFree( ptr)		(Desk_deskmem__functions.free( ptr))
/*
This gives direct access to whatever free function DeskMem is using.
 */
 







typedef struct	{

	void*	data;
	
	int	size;
	int	datasize;
	int	increment;
	}
	Desk_deskmem_buffer;
/*
This structure holds info on a buffer. Only access 'data' directly - use
Desk_DeskMem_Buffer* functions for everything else.

A Desk_deskmem_buffer is useful for allocating some memory which might need
to increase/decrease in size. Memory is only ever allocated in multiples
of 'increment', which is set when the buffer is initialised with
Desk_DeskMem_BufferInit. 

This allows you to use a Desk_desk_buffer to hold varying-size data whilst
keeping the heap reasonably unfragmented.
 */


void	Desk_DeskMem_BufferInit( Desk_deskmem_buffer* buffer, int increment);
/*
Initialises 'buffer'. Calls Desk_DeskMem_BufferEnsure( buffer, increment) to
set the initial size of the buffer to 0, so you don't have to bother
freeing the buffer if you don't use it.
 */

void	Desk_DeskMem_BufferFree( Desk_deskmem_buffer* buffer);
/*
Releases the memory associated with 'buffer'. Sets buffer->data to NULL.

Note that it is easy to call Desk_DeskMem_Free( &buffer) by mistake
(which will crash the application), and CC won't warn about this -
Desk_DeskMem_Free accepts any pointer.
 */

void	Desk_DeskMem_BufferEnsure( Desk_deskmem_buffer* buffer, int min);
/*
Increases/decreases the size of buffer so it points to at least 'min'
bytes of memory. This is done with Desk_DeskMem_Realloc, so it preserves the
data pointed to by buffer->data.
 */

void	Desk_DeskMem_BufferEnsureExtra( Desk_deskmem_buffer *buffer, int extra);
/*
Prepares the buffer so that it will hold at least 'extra' bytes more
than is currently used.
 */

#define	Desk_DeskMem_BufferGetSize( buffer)	( (buffer)->size)
/*
This returns the amount of memory in the buffer, which will always be
>= than the amount last passed to Desk_DeskMem_BufferEnsure.

In general, you will probably want to know the size of the data in the
buffer, for which you should use Desk_DeskMem_BufferGetDataSize.
 */

#define	Desk_DeskMem_BufferGetDataSize( buffer)	( (buffer)->datasize)
/*
This returns the amount of used-memory in the buffer (ie from the last
call to Desk_DeskMem_BufferEnsure).

Also see Desk_DeskMem_BufferGetSize.
 */

#define Desk_DeskMem_BufferGetString( buffer)	( (char*) (buffer)->data)
/*
Returns the data cast to a (char*)
 */

#define Desk_DeskMem_BufferGetIntArray( buffer)	( (int*) (buffer)->data)
/*
Returns the data cast to a (int*)
 */

#define Desk_DeskMem_BufferGetDoubleArray( buffer)	( (double*) (buffer)->data)
/*
Returns the data cast to a (double*)
 */

void	Desk_DeskMem_BufferStrCat( Desk_deskmem_buffer* buffer, const char* extra);
/*
Treat like strcat - appends 'extra', ensuring there is enough space.
 */

void	Desk_DeskMem_BufferStrNCat( Desk_deskmem_buffer* buffer, const char* extra, int extralen);
/*
Treat like strncat - appends the first 'extralen' characters of 'extra',
ensuring there is enough space.
 */

void	Desk_DeskMem_BufferStrCpy( Desk_deskmem_buffer* buffer, const char* s);
/*
Treat like strcpy - copies 's', ensuring there is enough space.
 */




#ifdef __cplusplus
}
#endif


#endif
